From 6512e57047db0799f37a29d703c71f1e75c3a5e5 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:56:39 +0800 Subject: [PATCH] feat(gui): dsh-client-hmr reload driver and the dshClient-discovered watch-build script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit client-hmr is a normal plugin package composed into dev graphs only. It listens on /plugins/events and reloads one plugin per rebuilt frame, serialized: invalidate, prefetch (fresh factory registers while the old fiber still serves), registry.delete before touching the fiber, drain disposers, drop owned style tags, entry.refresh(), fiber.await() loud. Dependency cascade costs zero client code — fiber activation epochs re-load dependents through cordis itself. Reload is coarse by design; no rollback in v1; self-reload works with a frame gap the next rebuild heals. scripts/dev-web.ts (pnpm run dev:web) is the convenience watch-build: it discovers its package list by scanning packages/*/*/package.json for dshClient platform "web" at startup — no hardcoded roster — and talks no protocol to the host. Gate bookkeeping rides along: knip entries for the new packages, README model-experience allowlist rows. --- knip.json | 43 +++- package.json | 1 + packages/client/hmr/README.md | 19 ++ packages/client/hmr/package.json | 51 +++++ packages/client/hmr/src/client/index.ts | 191 ++++++++++++++++++ packages/client/hmr/src/index.ts | 9 + packages/client/hmr/src/invariant.ts | 33 +++ packages/client/hmr/tsconfig.json | 30 +++ packages/client/hmr/tsdown.config.ts | 3 + pnpm-lock.yaml | 158 +++++++++------ scripts/dev-web.ts | 85 ++++++++ .../verify-package-readme-model-experience.ts | 2 + 12 files changed, 549 insertions(+), 76 deletions(-) create mode 100644 packages/client/hmr/README.md create mode 100644 packages/client/hmr/package.json create mode 100644 packages/client/hmr/src/client/index.ts create mode 100644 packages/client/hmr/src/index.ts create mode 100644 packages/client/hmr/src/invariant.ts create mode 100644 packages/client/hmr/tsconfig.json create mode 100644 packages/client/hmr/tsdown.config.ts create mode 100644 scripts/dev-web.ts diff --git a/knip.json b/knip.json index 7d84c5b67b..f182867eee 100644 --- a/knip.json +++ b/knip.json @@ -18,7 +18,8 @@ "workspaces": { ".": { "entry": [ - "scripts/**/*.mjs" + "scripts/**/*.mjs", + "scripts/dev-web.ts" ], "project": [ "scripts/**/*.ts", @@ -66,15 +67,11 @@ }, "packages/host/runtime": { "entry": [ - "tests/**/*.spec.ts", - "tests/**/*.e2e.ts" + "tests/**/*.spec.ts" ], "project": [ "src/**/*.ts", "tests/**/*.ts" - ], - "ignoreDependencies": [ - "@deepseek-ai/dsh-client-.+" ] }, "packages/client/web-ui": { @@ -391,8 +388,14 @@ ] }, "packages/examples/agent-spine-demo": { - "entry": ["tests/**/*.spec.ts", "tests/**/*.e2e.ts"], - "project": ["src/**/*.ts", "tests/**/*.ts"] + "entry": [ + "tests/**/*.spec.ts", + "tests/**/*.e2e.ts" + ], + "project": [ + "src/**/*.ts", + "tests/**/*.ts" + ] }, "packages/ui/jsonrpc": { "entry": [ @@ -548,15 +551,15 @@ "tests/**/*.tsx" ], "ignoreDependencies": [ - "@deepseek-ai/dsh-client-ui-theme", - "@deepseek-ai/dsh-client-connection" + "@deepseek-ai/dsh-client-ui-theme" ] }, "apps/web": { "entry": [ "tests/**/*.e2e.ts", "tests/**/*.snapshot.ts", - "tests/support.ts" + "tests/support.ts", + "src/node-module-stub.ts" ], "project": [ "src/**/*.ts", @@ -571,6 +574,24 @@ "react", "react-dom" ] + }, + "apps/cli": { + "project": [ + "src/**/*.ts" + ], + "ignoreDependencies": [ + "@deepseek-ai/dsh-client-.+" + ] + }, + "packages/client/modules": { + "project": [ + "src/**/*.ts" + ] + }, + "packages/client/hmr": { + "project": [ + "src/**/*.ts" + ] } } } diff --git a/package.json b/package.json index 74eee75261..3ff149b80a 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "demo:cordis": "node --import tsx packages/examples/tui-demo/src/bin.ts examples/cordis-agent/cordis.yml", "demo:acp": "node --import tsx packages/examples/acp-demo/src/bin.ts --config examples/acp-agent/cordis.yml", "demo:web": "npm run build && npm run build:web && node --import tsx apps/cli/src/bin.ts web", + "dev:web": "tsx scripts/dev-web.ts --poll", "postinstall": "node scripts/install-lefthook.mjs" }, "devDependencies": { diff --git a/packages/client/hmr/README.md b/packages/client/hmr/README.md new file mode 100644 index 0000000000..f6fcd44dba --- /dev/null +++ b/packages/client/hmr/README.md @@ -0,0 +1,19 @@ +# @deepseek-ai/dsh-client-hmr + +Hot reload for fetch-arrival client plugins. A static-arrival entry composed only into `--dev` graphs (`dsh web --dev`); production graphs omit the row, so the shell-bundled code stays inert. + +The plugin subscribes to the webserver's system SSE channel (`GET /plugins/events`) and reloads one plugin per `rebuilt` frame, serialized through a queue (the bundle handoff slot is single). The sequence per frame — `prefetch` (fetch the new bundle before touching anything), `invalidate`, `registry.delete` (before the fiber: a bare fiber dispose trips the vendored Loader's self-dispose branch, which would mark the entry disabled), drain the old fiber, delete `entry.fiber`, remove owned `