necokeine/dsh-calendar0

@necokeine/dsh-calendar

Session-interaction calendar plugin for DeepSeek Harness: a root-scope calendar surface aggregating every session's interaction events across the harness

包名
@necokeine/dsh-calendar
版本
0.1.0
许可证
MIT
最近更新
2026年8月21日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:necokeine/dsh-calendar

dsh-calendar

Session-interaction calendar plugin for DeepSeek Harness: a root-scope calendar surface that aggregates every session's interaction events across the whole harness. A sidebar-foot action opens a full-viewport month grid; each day cell carries a badge with that day's interaction count, and selecting a day lists one row per active session — the session's title, its day-scoped duration, and its event count — expandable to the raw event list. Live appends stream into the open calendar in real time.

English | 中文

Features

  • Whole-harness view — one calendar for every session (user messages, assistant replies, tool calls, turn boundaries), not just the current one.
  • Real-time — host-side SSE channel pushes new events into the open calendar the moment they are appended.
  • Historical backfill — seeds already-live sessions at startup and best-effort reads the persisted log through sessionPersistence.
  • Merge-extensible — unknown (plugin-merged) event types fall back to their type name; no per-type registry to maintain.
  • Bounded memory — rolling 90-day window, capped at 2000 events per day.

Installation

The plugin targets the DeepSeek Harness web surface (dsh web).

Install it into a profile with dsh plugin (which forwards to pnpm inside the profile directory). The package declares dsh.bundle, so dsh appends it to the profile's dsh.profile.bundles list automatically:

dsh plugin --profile web add @necokeine/dsh-calendar

Restart dsh web and refresh the page — a Calendar entry appears at the bottom of the sidebar, replacing the in-box ui-calendar row that @deepseek-ai/dsh-web-app mounts (this bundle disables that row and inserts its own, so only one calendar runs).

Remove it again with:

dsh plugin --profile web remove @necokeine/dsh-calendar

Other install forms also work:

  • Local checkoutdsh plugin --profile web add ./path/to/dsh-calendar (or link:./path/to/dsh-calendar).
  • Tarballpnpm pack then dsh plugin --profile web add ./dsh-calendar-0.1.0.tgz; prebuilt lib/, no build permission needed.
  • Gitdsh plugin --profile web add github:necokeine/dsh-calendar#; this fetches sources (no lib/), so pnpm ≥10 blocks the build until you allowlist the package under allowBuilds in the profile's pnpm-workspace.yaml, and the checkout must produce lib/ itself.

Development

pnpm install
pnpm test        # vitest: node half + browser-half data layer + component specs
pnpm build       # tsc -b && tsdown → lib/ (node half + browser client bundle)
pnpm lint        # oxlint (src + tests)
pnpm pack        # build (via prepack) + tarball for `dsh plugin add ./….tgz`

The footer-action integration specs (slot registration over the real SlotRegistry and locale plugins) need a DeepSeek Harness checkout: the published @deepseek-ai/dsh-client-runtime/dsh-client-locale npm artifacts are browser-format bundles that cannot load under plain node/vitest. The standalone specs here cover the same code through a local createSnapshotStore stub (tests/stubs/runtime-client.ts); run the integration specs inside the harness monorepo, where workspace tsconfig paths resolve those packages to source.

The package is a split plugin like the harness's own dsh-client-hmr:

  • Node half (src/index.ts) — a host plugin injecting sessions + webServer. It subscribes to the session/event firehose, summarizes every event into a short wire record { sessionId, seq, time, type, summary } (Chinese product copy, host-authored), folds each session's title from its session/title events (foldSessionTitle), and aggregates everything per-session-per-day — each day bucket lists one CalendarSessionWire per active session with its title, day-scoped time span, event count, and (for the expanded view) its raw event list. It registers two routes:
    • GET /calendar/events?from=YYYY-MM-DD&to=YYYY-MM-DD — JSON per-session day buckets for a local-day range.
    • GET /calendar/stream — SSE channel: a backlog frame on connect, then one event frame per live append (the browser folds them into the matching session rows).
  • Browser half (src/client/index.ts) — registers the sidebar.footer.action entry plus the calendar locale namespace. CalendarController owns the view store (bound to useCalendar), the per-month /calendar/events fetch, and the /calendar/stream EventSource lifecycle (open with the panel, close with it). The month grid shows per-day interaction counts; the day-detail area lists session rows (title + duration + event count) that expand to the raw event list — expanded state is component-local.

Repository layout

src/
  index.ts              # node half: aggregator, index, routes, SSE
  events.ts             # shared wire protocol (both halves)
  invariant.ts          # package invariant companion
  client/
    index.ts            # browser half entry (footer action registration)
    calendar-controller.ts  # store + wire calls + SSE lifecycle
    CalendarAction.tsx  # footer trigger + month grid + day detail
    calendar-action.module.css
    locales.ts          # zh/en dictionaries
tests/                  # vitest specs (node half, controller, component)
cordis.patch.yml        # bundle patch (`dsh.bundle.patch`): disables the in-box
                        # ui-calendar row and inserts this package under its own id
tsdown.config.ts        # independent build (CSS modules inline + loader banner)

License

MIT