Skip to content

Merge/dsh gateway auth - #396

Open
Fairy-happy wants to merge 30 commits into
mainfrom
merge/dsh-gateway-auth
Open

Merge/dsh gateway auth#396
Fairy-happy wants to merge 30 commits into
mainfrom
merge/dsh-gateway-auth

Conversation

@Fairy-happy

Copy link
Copy Markdown
Collaborator

No description provided.

yaojin3616 and others added 30 commits September 9, 2026 22:38
Upstream now publishes the official CI build to npm, so the 251 vendored
tarballs under packages/harness-0.1.2-rc.1/ (8.1 MB, rebuilt by hand on a
maintainer's machine every upgrade) are replaced by exact version pins plus
lockfile integrity. That also removes the build-machine absolute paths the
CSS patches used to carry.

Upstream breaking changes handled:

- The session-persistence seam is now a handle-ownership model
  (create/open/flush/stat/list). The permanent-deletion patch chain is
  rewritten against it: the JSONL backend's delete() takes the same write
  claim and directory lease a write open takes.
- The replace surfaceOp renamed start/end to startSeq/endSeq; the bundled
  PPT plugin emitted the old shape and is fixed and repacked.
- SystemPrompt's persona option split into personaPrefix/personaSuffix.
- Prerelease peer ranges (^0.1.2-alpha.4) do not match 0.1.5-rc.1, so the
  in-repo plugins' dsh peer ranges are widened.

All 20 patches are migrated. Several previously replaced whole CSS strings
and class maps with a locally rebuilt copy, which silently mismatched after
any upstream rebuild; they now only append desktop-owned rules and keys, and
the tests assert that invariant instead of pinning CSS-module hashes.

Verified: npm ci (20 patches), vitest 749/750, tsc, npm run build,
verify-harness-auth.mjs, and a direct safe-mode profile boot.

test/safe-mode-runtime.test.ts fails: its fault injection uses an ESM loader
hook, and under 0.1.5 registering any loader hook makes boot exit 0 silently.
The real launch path registers none and boots fine. See
docs/harness-0.1.5-rc.1-upgrade.md.
…meta.main

Harness 0.1.5 moved lib/bin.js from top-level execution to

    async function runCli() { ... }
    if (import.meta.main) await runCli();
    export { runCli };

build/harness-node-entry.mjs imports that entry rather than being it, so the
guard is false and the CLI never ran: the process loaded the module, did
nothing, and exited 0 with no diagnostics. The app surfaced only "Harness
stopped unexpectedly (exit code 0 (0x00000000))" after a 40s wait, on both
the web and safe-mode profiles.

Isolated with a clean DSH_HOME and no third-party plugins: running bin.js
directly serves (401), running it through the entry wrapper does not. The
fix calls the exported runCli(); builds that still self-execute on import
keep working.

This was also the root cause of the safe-mode-runtime failure previously
attributed to ESM loader hooks — that diagnosis compared against a direct
bin.js run, which bypassed the wrapper. Hooks were never involved. With the
entry fixed the suite is 751/751.

test/harness-node-entry.test.ts locks both halves of the contract: upstream
still gating on import.meta.main and exporting runCli, and the entry calling
it rather than relying on import side effects.
动态 import 不会触发 import.meta.main,导致 Harness 空载退出;PPT RPC 在未注入 webServer 的 fiber 上注册会启动失败。重打内置 PPT tarball 并同步 lockfile,保证同事 npm ci 后即可启动。

Co-authored-by: Cursor <cursoragent@cursor.com>
Harness 0.1.5 turns an RPC channel into a webServer route owned by the
Context that read `connection`:

    get rpc() { const owner = this.ctx; ... }
    register(owner, ...) { return owner.effect(() => owner.webServer.register(route)) }

The bundled PPT plugin called ctx.connection.rpc.handle() directly, so that
Context had no webServer and the read threw "cannot get property webServer
without inject", failing the whole plugin tree. Each cold start then burned
~30s per attempt: web profile fails, plugin recovery runs, safe mode boots,
and the app restarts into web again — roughly 90s before the user sees
anything, which is the reported slow startup.

Adding "webServer" to the plugin's inject array does not help; the failing
Context is not the plugin's own (verified: fiber.inject and fiber.store both
carry it and the read still throws). Registering from a scoped inject
Context does, and matches registerPreviewAssets in this same package. The
top-level inject stays unchanged so a profile without a web server still
gets the tool half.

The PPT fixtures fake ctx, so they need `effect` and `webServer.register`
for the scoped callbacks to run and register the channel.

Verified on clean DSH_HOME with the desktop's own launch arguments: the web
profile with PPT is ready in 4-6s with zero inject failures. Suite 751/751.

The same enforcement also hits the third-party dsh-plugin-width-slider,
which is not ours to fix.
The Harness log timestamped only the `starting` line, so a slow launch could
not be split between Harness booting and the desktop probing for readiness —
the only other signal was a progress tick every 10s.

Every file line now carries `+<ms>` since the launch began, the two
boundaries that matter are marked ("Harness announced its endpoint", "Harness
is ready"), and the progress tick drops to 5s. The in-memory log lines stay
verbatim because recovery detection, failure-cause extraction and their tests
match on the text.

Measured with this on a copy of a real profile (9 third-party plugins):
Harness serves in ~4s and the readiness probe answers in 2-19ms, so neither
is the reported slow startup.
The launch clock began at `[desktop] starting`, but launchHarness does its
pre-flight first: splash, stopping the previous Harness, migration recovery,
the pnpm store, generation projection and the LaunchAgent audit. All of that
was unstamped, so the log made a launch look faster than it felt.

beginLaunch() now starts the clock when the launch is requested and start()
keeps a clock the launcher already began. The phases in between are marked so
a slow start can be attributed to one of them.

Measured so far on a copy of a real profile, time until Harness prints its
token: 3.6s with no third-party plugins, 8.8s with the user's nine. Leaving
one out at a time puts the whole 5.2s difference on a single entry — an MCP
client that opens a remote streamable-http session during boot; the other
eight are within noise. Same numbers under the Electron utility process
(3.7s / 4.0s), so the runtime is not the cost.
…al path

Profiling a boot of a real nine-plugin profile put a third of Harness CPU in
@deepseek-ai/dsh-client-modules building the combined client bundle:

- newlineCount walked each multi-megabyte bundle with `for (const char of
  value)`, allocating a string per code point, and identitySectionMap built
  a line-length array only to join it. Native indexOf and String#repeat give
  byte-identical counts and mappings (verified against all 64 real bundles
  plus edge cases) about 18x faster.
- compose() runs on every graph flush and rebuilt every single-record combo
  each time; while a profile boots the graph flushes about nine times, so
  each unchanged bundle was re-concatenated, hashed and encoded ~9x (the
  6.9MB document-preview bundle ~40ms per pass). Records keep their identity
  until their source changes and rebuilt() assigns a new content-hash rev on
  any byte change, so combos are memoised on the record, validated by rev.
  All 64 served bundles still return 200 at the same sizes and parse.

Same machine, same profile, time until Harness prints its token:
upstream 3.9-4.2s -> 1.8-2.6s.

Desktop side, measured from the new launch clock:
- The login-shell capture (310-440ms here) ran with execFileSync on the main
  process right before the spawn. It now starts asynchronously when the app
  starts and the launch reads the result.
- Readiness waited a further 500ms sustain window after the first healthy
  probe. Health already requires the launch token, which Harness prints only
  once its plugin tree is loaded and it is serving, so the first healthy
  probe is the settled state.
Upstream rc.2 is four commits: the feedback dialog, delivered-file card
layout and conversation spacing.

- 217 @deepseek-ai/dsh-* dependencies move to 0.1.5-rc.2 and the 19 dsh
  patch files are renamed; the cordis family versions are unchanged.
- Drop @deepseek-ai/dsh-typert-generator. It is a TypeScript analyzer and
  code generator that only the root project depended on, carried over from
  the old vendored package set; upstream did not publish it for rc.2.
- Of the patched packages only dsh-client-ui-chat, -deliverables and
  -sidebar changed between rc.1 and rc.2. Every function and anchor the
  patches touch is identical in both, and the sidebar CSS-module hash did
  not change, so all 20 patches apply unchanged.
- The in-repo plugins' `^0.1.5-rc.1` peer ranges already admit rc.2, so the
  PPT bundles are not repacked.

Verified: vitest 754/754, tsc, build, verify-harness-auth; the web profile
with PPT boots under both the bundled Node and the Electron utility process
with no inject or entry failures, and a real Chromium load of the first
screen logs no console errors.
Adapt the feature to Harness 0.1.5-rc.2 and validate its independent Desktop composition.
… main 的市场安装与启动修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants