@ran-sh/dsh-crew 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +16 -16
  2. package/.claude-plugin/plugin.json +14 -14
  3. package/LICENSE +21 -21
  4. package/README.de.md +359 -359
  5. package/README.es.md +359 -359
  6. package/README.fr.md +359 -359
  7. package/README.hi.md +359 -359
  8. package/README.id.md +359 -359
  9. package/README.ja.md +359 -359
  10. package/README.ko.md +359 -359
  11. package/README.md +126 -126
  12. package/README.pt.md +359 -359
  13. package/README.ru.md +359 -359
  14. package/README.th.md +359 -359
  15. package/README.tr.md +359 -359
  16. package/README.vi.md +359 -359
  17. package/README.zh-TW.md +359 -359
  18. package/README.zh.md +116 -116
  19. package/agents/ds-flash.md +24 -24
  20. package/agents/ds-pro.md +24 -24
  21. package/agents/ds-reviewer.md +23 -23
  22. package/agents/ds-worker.md +23 -23
  23. package/bin/dsh-crew.mjs +16 -16
  24. package/codex/agents/ds-flash.toml +34 -34
  25. package/codex/agents/ds-pro.toml +34 -34
  26. package/codex/agents/ds-reviewer.toml +32 -32
  27. package/codex/agents/ds-worker.toml +32 -32
  28. package/codex/prompts/dsh-config.md +19 -19
  29. package/codex/prompts/dsh-status.md +5 -5
  30. package/commands/config.md +23 -23
  31. package/commands/off.md +5 -5
  32. package/commands/on.md +5 -5
  33. package/commands/status.md +9 -9
  34. package/cordis.patch.yml +4 -4
  35. package/docs/gpt-relay-extension.md +103 -103
  36. package/docs/installation.md +138 -138
  37. package/docs/job-contracts.md +103 -103
  38. package/docs/readiness-matrix.md +85 -85
  39. package/docs/ui-surfaces.md +107 -107
  40. package/official-web-bridge/cordis.patch.yml +4 -4
  41. package/official-web-bridge/entry.mjs +1 -1
  42. package/official-web-bridge/overlay-entry.mjs +59 -59
  43. package/official-web-bridge/package.json +25 -25
  44. package/package.json +1 -1
  45. package/scripts/build-client.mjs +49 -49
  46. package/scripts/live-crew-smoke.mjs +39 -39
  47. package/scripts/live-policy-matrix.mjs +177 -177
  48. package/scripts/policy-probe.mjs +101 -101
  49. package/scripts/remove-legacy-official-bridge.ps1 +89 -89
  50. package/scripts/setup.mjs +393 -393
  51. package/scripts/smoke-real.mjs +110 -110
  52. package/scripts/smoke.mjs +78 -78
  53. package/scripts/verify-crew-ui-polish.mjs +145 -145
  54. package/scripts/verify-history-ui.mjs +97 -97
  55. package/scripts/verify-installer-fix.mjs +26 -26
  56. package/scripts/verify-npm-install.mjs +311 -311
  57. package/scripts/verify-official-bridge-e2e.mjs +192 -192
  58. package/src/adaptive-routing.mjs +260 -260
  59. package/src/client/activation-summary.tsx +64 -64
  60. package/src/client/collapsible-sections.mjs +55 -55
  61. package/src/client/history-panel.tsx +108 -108
  62. package/src/client/host-readiness.mjs +71 -71
  63. package/src/client/index.tsx +1711 -1711
  64. package/src/client/model-callability-view.mjs +17 -17
  65. package/src/client/panel-chrome.tsx +40 -40
  66. package/src/client/quick-entry.tsx +10 -10
  67. package/src/client/quick-panel.tsx +275 -275
  68. package/src/client/readiness-envelope.mjs +34 -34
  69. package/src/client/surface-detection.mjs +43 -43
  70. package/src/config-readiness.mjs +226 -226
  71. package/src/credential-reference.mjs +38 -38
  72. package/src/delivery.mjs +205 -205
  73. package/src/dsh-cli-runtime.mjs +1021 -1021
  74. package/src/dsh-cohort.mjs +20 -20
  75. package/src/extension-contract.mjs +104 -104
  76. package/src/failure-classification.mjs +201 -201
  77. package/src/history/admission-gate.mjs +67 -67
  78. package/src/history/archive-store.mjs +272 -272
  79. package/src/history/cleanup-plan.mjs +89 -89
  80. package/src/history/http.mjs +32 -32
  81. package/src/history/operation.mjs +86 -86
  82. package/src/history/runner-detach.mjs +34 -34
  83. package/src/history/runner.mjs +52 -52
  84. package/src/history/runtime.mjs +36 -36
  85. package/src/history/service.mjs +177 -177
  86. package/src/history/state.mjs +27 -27
  87. package/src/hub/entry.mjs +104 -104
  88. package/src/hub/index.mjs +2694 -2694
  89. package/src/hub-client.mjs +154 -154
  90. package/src/hub-compatibility.mjs +42 -42
  91. package/src/i18n.mjs +19 -19
  92. package/src/information-flow.mjs +67 -67
  93. package/src/install/cli.mjs +28 -28
  94. package/src/install/install-legacy.mjs +711 -711
  95. package/src/install/install.mjs +483 -483
  96. package/src/install/npx-lifecycle.mjs +3456 -3456
  97. package/src/install/official-frontend-assets.mjs +78 -78
  98. package/src/install/official-web.mjs +95 -95
  99. package/src/install/payload-content.mjs +88 -88
  100. package/src/install/windows-startup.mjs +236 -236
  101. package/src/install/windows-supervisor-adapter.mjs +443 -443
  102. package/src/install/windows-supervisor-lifecycle.mjs +782 -782
  103. package/src/install/zcode.mjs +397 -397
  104. package/src/job-contracts.mjs +255 -255
  105. package/src/local-request-guard.mjs +60 -60
  106. package/src/mcp-runtime.mjs +340 -340
  107. package/src/model-callability-contract.mjs +79 -79
  108. package/src/model-catalog.mjs +180 -180
  109. package/src/model-routing.mjs +586 -586
  110. package/src/model-schedule.mjs +207 -207
  111. package/src/official-web-bridge.mjs +447 -447
  112. package/src/policy.mjs +235 -235
  113. package/src/provider-delete-adapters.mjs +1934 -1934
  114. package/src/provider-health.mjs +130 -130
  115. package/src/provider-inventory.mjs +182 -182
  116. package/src/provider-layer-migration-adapters.mjs +759 -759
  117. package/src/provider-layer-migration.mjs +198 -198
  118. package/src/provider-lifecycle-state.mjs +103 -103
  119. package/src/provider-lifecycle.mjs +252 -252
  120. package/src/provider-profile-store.mjs +390 -390
  121. package/src/provider-settings-store.mjs +633 -633
  122. package/src/provider-store-lock.mjs +67 -67
  123. package/src/readiness-matrix.mjs +181 -181
  124. package/src/removable-waiter.mjs +29 -29
  125. package/src/role-profiles.mjs +107 -107
  126. package/src/runtime-controls.mjs +84 -84
  127. package/src/runtime-identity-contract.mjs +34 -34
  128. package/src/runtime-identity.mjs +235 -235
  129. package/src/runtime-readiness-snapshot.mjs +285 -285
  130. package/src/server.mjs +581 -581
  131. package/src/session-origins.mjs +60 -60
  132. package/src/standalone-sdk.mjs +23 -23
  133. package/src/status-shard.mjs +63 -63
  134. package/src/structured-error-code.mjs +38 -38
  135. package/src/supervisor/restart-request.mjs +256 -256
  136. package/src/workflow-runtime.mjs +739 -739
  137. package/src/workflow.mjs +155 -155
  138. package/src/workspace-audit.mjs +231 -231
  139. package/src/workspace-context.mjs +146 -146
  140. package/src/workspace-isolation.mjs +455 -455
  141. package/src/workspace-readiness.mjs +32 -32
  142. package/statusline/statusline.sh +14 -14
  143. package/statusline/worker-segment.sh +35 -35
  144. package/windows/start-dsh-crew.cmd +57 -57
  145. package/windows/start-dsh-crew.ps1 +1302 -1302
  146. package/windows/supervisor-control.ps1 +467 -467
  147. package/worker.cordis.yml +67 -67
  148. package/zcode/agents/ds-reviewer.md +31 -31
  149. package/zcode/agents/ds-worker.md +31 -31
  150. package/zcode/commands/dsh-config.md +17 -17
  151. package/zcode/commands/dsh-status.md +5 -5
@@ -1,85 +1,85 @@
1
- # DSH Crew Readiness Matrix
2
-
3
- The readiness matrix is a conservative diagnostic surface for release and environment confidence. Its primary rule is simple:
4
-
5
- > Missing evidence is never PASS.
6
-
7
- The matrix is emitted by `hubStatus()` and is therefore visible inside the existing `dsh_worker_config` response under `hub_compatibility.readiness_matrix`.
8
-
9
- ## Statuses
10
-
11
- - `PASS` — direct evidence confirms the row.
12
- - `FAIL` — a check actually ran and produced an incompatible or failed result.
13
- - `BLOCKED` — the check could not run because required infrastructure or authorization was unavailable.
14
- - `SKIP` — the row is intentionally not applicable for the active policy/path.
15
- - `NOT_RUN` — no trusted evidence has been supplied for the row.
16
-
17
- `BLOCKED` and `SKIP` are not failures. `NOT_RUN` is not success.
18
-
19
- ## Evidence classes
20
-
21
- The matrix separates three kinds of evidence:
22
-
23
- 1. `live-runtime` — facts the current process can directly observe, such as Hub reachability and protocol compatibility.
24
- 2. `ci` — platform validation such as Linux deterministic, Windows regressions, and future macOS smoke.
25
- 3. `real-execution` — provider/model and workflow behavior that requires a genuine DSH execution.
26
-
27
- Live checks are populated automatically. The config report also consumes the
28
- compatible Hub's bounded job registry, so a completed Worker or Reviewer can
29
- promote the corresponding generic real-execution row. Other CI and execution
30
- rows remain `NOT_RUN` until a trusted higher layer supplies explicit evidence.
31
-
32
- ## Target rows
33
-
34
- - `linux_deterministic`
35
- - `windows_regressions`
36
- - `macos_smoke`
37
- - `hub_compatibility`
38
- - `provider_catalog`
39
- - `provider_health`
40
- - `reviewer_health`
41
- - `model_execution`
42
- - `deepseek_flash`
43
- - `deepseek_pro`
44
- - `opencode_go_mimo_qwen`
45
- - `reviewer_pipeline`
46
- - `cancellation_timeout_escalation`
47
- - `standalone_official`
48
-
49
- `provider_health` and `reviewer_health` describe only the currently resolved
50
- Worker and Reviewer routes. A fresh negative observation overrides historical
51
- success; unrelated providers and stale observations do not. Historical jobs
52
- count only when their provider/model matches the corresponding current route,
53
- with escalation evidence matched against the current escalation route.
54
-
55
- ## Provider/catalog rule
56
-
57
- The Hub client does not know the active worker provider mode, so its embedded matrix leaves `provider_catalog` as `NOT_RUN` with `PROVIDER_MODE_UNKNOWN` rather than guessing.
58
-
59
- When a higher layer knows the provider mode and has actually read the Harness catalog, it may build a more specific matrix:
60
-
61
- - DeepSeek Official strict mode: catalog row may be `SKIP` / `PROVIDER_CATALOG_NOT_REQUIRED`.
62
- - Follow-DSH with successful catalog read: `PASS` / `PROVIDER_CATALOG_RESOLVED`.
63
- - Follow-DSH with an attempted but failed catalog read: `FAIL` / `PROVIDER_CATALOG_UNAVAILABLE`.
64
- - Hub unavailable/incompatible before catalog access: `BLOCKED`, not `FAIL`.
65
-
66
- ## Credential safety
67
-
68
- The matrix never reads or returns credential values, provider configuration payloads, quota data, pricing, cookies, tokens, or raw exception dumps. The standalone row defaults to `NOT_RUN` / `CREDENTIAL_STATUS_NOT_PROBED` unless an authorized real-environment validation explicitly reports evidence.
69
-
70
- ## Trusted evidence
71
-
72
- `buildReadinessMatrix()` accepts an optional evidence map so CI/report aggregation can be added later without changing row semantics. Evidence can set only the existing status vocabulary. Invalid statuses are ignored rather than broadening the contract.
73
-
74
- An evidence record may contain:
75
-
76
- ```json
77
- {
78
- "status": "PASS",
79
- "reason_code": "CI_GREEN",
80
- "evidence_source": "github-actions",
81
- "evidence_ref": "run-123"
82
- }
83
- ```
84
-
85
- The matrix does not fetch or trust arbitrary remote data by itself. Loading and authenticating an evidence source is the responsibility of the higher layer that calls the builder.
1
+ # DSH Crew Readiness Matrix
2
+
3
+ The readiness matrix is a conservative diagnostic surface for release and environment confidence. Its primary rule is simple:
4
+
5
+ > Missing evidence is never PASS.
6
+
7
+ The matrix is emitted by `hubStatus()` and is therefore visible inside the existing `dsh_worker_config` response under `hub_compatibility.readiness_matrix`.
8
+
9
+ ## Statuses
10
+
11
+ - `PASS` — direct evidence confirms the row.
12
+ - `FAIL` — a check actually ran and produced an incompatible or failed result.
13
+ - `BLOCKED` — the check could not run because required infrastructure or authorization was unavailable.
14
+ - `SKIP` — the row is intentionally not applicable for the active policy/path.
15
+ - `NOT_RUN` — no trusted evidence has been supplied for the row.
16
+
17
+ `BLOCKED` and `SKIP` are not failures. `NOT_RUN` is not success.
18
+
19
+ ## Evidence classes
20
+
21
+ The matrix separates three kinds of evidence:
22
+
23
+ 1. `live-runtime` — facts the current process can directly observe, such as Hub reachability and protocol compatibility.
24
+ 2. `ci` — platform validation such as Linux deterministic, Windows regressions, and future macOS smoke.
25
+ 3. `real-execution` — provider/model and workflow behavior that requires a genuine DSH execution.
26
+
27
+ Live checks are populated automatically. The config report also consumes the
28
+ compatible Hub's bounded job registry, so a completed Worker or Reviewer can
29
+ promote the corresponding generic real-execution row. Other CI and execution
30
+ rows remain `NOT_RUN` until a trusted higher layer supplies explicit evidence.
31
+
32
+ ## Target rows
33
+
34
+ - `linux_deterministic`
35
+ - `windows_regressions`
36
+ - `macos_smoke`
37
+ - `hub_compatibility`
38
+ - `provider_catalog`
39
+ - `provider_health`
40
+ - `reviewer_health`
41
+ - `model_execution`
42
+ - `deepseek_flash`
43
+ - `deepseek_pro`
44
+ - `opencode_go_mimo_qwen`
45
+ - `reviewer_pipeline`
46
+ - `cancellation_timeout_escalation`
47
+ - `standalone_official`
48
+
49
+ `provider_health` and `reviewer_health` describe only the currently resolved
50
+ Worker and Reviewer routes. A fresh negative observation overrides historical
51
+ success; unrelated providers and stale observations do not. Historical jobs
52
+ count only when their provider/model matches the corresponding current route,
53
+ with escalation evidence matched against the current escalation route.
54
+
55
+ ## Provider/catalog rule
56
+
57
+ The Hub client does not know the active worker provider mode, so its embedded matrix leaves `provider_catalog` as `NOT_RUN` with `PROVIDER_MODE_UNKNOWN` rather than guessing.
58
+
59
+ When a higher layer knows the provider mode and has actually read the Harness catalog, it may build a more specific matrix:
60
+
61
+ - DeepSeek Official strict mode: catalog row may be `SKIP` / `PROVIDER_CATALOG_NOT_REQUIRED`.
62
+ - Follow-DSH with successful catalog read: `PASS` / `PROVIDER_CATALOG_RESOLVED`.
63
+ - Follow-DSH with an attempted but failed catalog read: `FAIL` / `PROVIDER_CATALOG_UNAVAILABLE`.
64
+ - Hub unavailable/incompatible before catalog access: `BLOCKED`, not `FAIL`.
65
+
66
+ ## Credential safety
67
+
68
+ The matrix never reads or returns credential values, provider configuration payloads, quota data, pricing, cookies, tokens, or raw exception dumps. The standalone row defaults to `NOT_RUN` / `CREDENTIAL_STATUS_NOT_PROBED` unless an authorized real-environment validation explicitly reports evidence.
69
+
70
+ ## Trusted evidence
71
+
72
+ `buildReadinessMatrix()` accepts an optional evidence map so CI/report aggregation can be added later without changing row semantics. Evidence can set only the existing status vocabulary. Invalid statuses are ignored rather than broadening the contract.
73
+
74
+ An evidence record may contain:
75
+
76
+ ```json
77
+ {
78
+ "status": "PASS",
79
+ "reason_code": "CI_GREEN",
80
+ "evidence_source": "github-actions",
81
+ "evidence_ref": "run-123"
82
+ }
83
+ ```
84
+
85
+ The matrix does not fetch or trust arbitrary remote data by itself. Loading and authenticating an evidence source is the responsibility of the higher layer that calls the builder.
@@ -1,107 +1,107 @@
1
- # 3210 and 3080 UI responsibilities
2
-
3
- The desktop entry opens the official frontend with a simple Crew panel on 3080. Crew runs hidden on 3210;
4
- its web page is an explicit advanced-settings entry, not the default browser tab.
5
- The background watcher manages only 3210. Desktop launch may start the official
6
- program but never uses it as authority to restart, update or roll back Crew.
7
- The simple panel is mounted through a `--patch` file under the Crew-owned home,
8
- with immutable frontend revisions kept independently of backend release cleanup.
9
- The 3080 button opens 3210; the 3210 button opens 3080.
10
-
11
- DSH Crew is a plugin attached to the official DeepSeek Harness. Current installs
12
- register it in a dedicated official Harness `dsh-crew` profile whose native 3210
13
- page is the canonical full Crew control and execution surface. An older
14
- installation may still expose a narrow 3080 quick-controls bundle, but that
15
- legacy surface is external to Crew ownership, optional, and deprecated. Crew
16
- treats the official profile as read-only. The legacy surface
17
- never starts, owns, or supervises 3210.
18
-
19
- ## 3210: official Harness profile with the Crew plugin
20
-
21
- The Crew-owned official Harness `dsh-crew` profile on `127.0.0.1:3210` loads the
22
- DSH Crew plugin and owns day-to-day Crew management and model execution:
23
-
24
- - Crew workflow and global enablement
25
- - Worker and Reviewer policy
26
- - model priority, fallback, review, and adaptive routing
27
- - multimodal (vision/imagegen) capability switches
28
- - providers lifecycle (migrate/delete/rollback/quarantine)
29
- - credential references lifecycle
30
- - workspaces, presets, jobs
31
- - installation integrations (Codex, Claude, ZCode)
32
- - task status and bounded model-invocation summaries
33
-
34
- Bundle: `lib/client.js` (module `@ran-sh/dsh-crew`), built from
35
- `src/client/entry.tsx`.
36
-
37
- ## 3080: official frontend; optional legacy Crew controls
38
-
39
- An older official Harness `web` profile on `127.0.0.1:3080` may still host a
40
- NARROW quick-controls card — and nothing else:
41
-
42
- - master switch (`subagents_enabled`)
43
- - Flash / Pro model priority lists (add/remove/reorder only)
44
- - vision / imagegen toggles + providers (restart-pending flow)
45
- - deep link to the 3210 full control plane (`http://127.0.0.1:3210/`)
46
-
47
- Bundle: `official-web-bridge/lib/client.js` (module
48
- `@ran-sh/dsh-crew-web-bridge`), built from `src/client/quick-entry.tsx`.
49
- It physically contains none of the full control-plane code (no credential
50
- purge, no provider delete/migration, no install integration).
51
-
52
- The 3080 bridge proxies ONLY two exact endpoints to 3210:
53
- `quick-config` and `quick-status`. Restart and rollback belong to 3210;
54
- the quick panel links there when needed. Everything else on the Crew namespace is 404 on
55
- 3080, and `/supervisor/restart` returns 410 Gone pointing at 3210.
56
-
57
- The official `~/.dsh` tree is strictly read-only for Crew; the 3080 quick
58
- surface itself is optional — Crew works fully with 3080 closed.
59
-
60
- ## UNKNOWN: diagnostics only
61
-
62
- Unknown surfaces render diagnostics and never gain write authority.
63
-
64
- ## Surface detection and failure behavior
65
-
66
- The client does not infer responsibility from a hard-coded browser port. It
67
- uses two same-origin, structured signals:
68
-
69
- 1. `/_dsh/dsh-crew/bridge-status` identifies the official quick bridge.
70
- 2. `/_dsh/dsh-crew/runtime` identifies the native Crew-owned runtime.
71
-
72
- The bridge signal wins on 3080 because the proxied runtime response correctly
73
- describes the 3210 backend. If neither contract can be verified, the client
74
- fails closed to the diagnostics view. Missing evidence never enables the
75
- full control plane and never becomes `READY`.
76
-
77
- The split changes presentation only. Crew state, credentials, routing policy,
78
- and model execution remain isolated under the Crew-owned home and profile.
79
-
80
- ## Local trust model
81
-
82
- Both surfaces listen on loopback only and share one request guard
83
- (`src/local-request-guard.mjs`). A request is trusted when it proves, in
84
- order: (1) the TCP peer is a loopback address, (2) the `Host` header names a
85
- loopback host, (3) browser fetch-metadata (`Sec-Fetch-Site`) is `same-origin`
86
- or absent/`none`, and (4) when an `Origin` header is present it names a
87
- loopback host — on the 3080 bridge the `Origin` authority must additionally
88
- equal the request authority, while the 3210 hub accepts any loopback origin
89
- because the 3080 panel calls it cross-port.
90
-
91
- There is deliberately no bearer token: every process running as the local
92
- user is inside the trust boundary and may control Crew. The guards keep
93
- off-machine browsers, DNS-rebinding pages, and cross-machine proxies out;
94
- they do not authenticate local users. If a deployment ever needs to separate
95
- local principals, add a per-install random token to state-changing calls and
96
- inject it server-side in the bridge — do not use browser `Origin` as
97
- authentication.
98
-
99
- ## Process ownership
100
-
101
- The Windows launcher supervisor (`windows/start-dsh-crew.ps1` watch mode) is
102
- the ONLY process authority for the Crew-owned 3210 service. The official 3080
103
- surface never starts, owns, or supervises 3210. Restart and maintenance go
104
- through durable request files the hub writes and the launcher executes
105
- (`supervisor/restart-request.mjs`). Ordinary backend startup does not probe the
106
- legacy bridge. Desktop opening uses the official process and root HTTP response,
107
- independently of whether that process has any Crew plugin installed.
1
+ # 3210 and 3080 UI responsibilities
2
+
3
+ The desktop entry opens the official frontend with a simple Crew panel on 3080. Crew runs hidden on 3210;
4
+ its web page is an explicit advanced-settings entry, not the default browser tab.
5
+ The background watcher manages only 3210. Desktop launch may start the official
6
+ program but never uses it as authority to restart, update or roll back Crew.
7
+ The simple panel is mounted through a `--patch` file under the Crew-owned home,
8
+ with immutable frontend revisions kept independently of backend release cleanup.
9
+ The 3080 button opens 3210; the 3210 button opens 3080.
10
+
11
+ DSH Crew is a plugin attached to the official DeepSeek Harness. Current installs
12
+ register it in a dedicated official Harness `dsh-crew` profile whose native 3210
13
+ page is the canonical full Crew control and execution surface. An older
14
+ installation may still expose a narrow 3080 quick-controls bundle, but that
15
+ legacy surface is external to Crew ownership, optional, and deprecated. Crew
16
+ treats the official profile as read-only. The legacy surface
17
+ never starts, owns, or supervises 3210.
18
+
19
+ ## 3210: official Harness profile with the Crew plugin
20
+
21
+ The Crew-owned official Harness `dsh-crew` profile on `127.0.0.1:3210` loads the
22
+ DSH Crew plugin and owns day-to-day Crew management and model execution:
23
+
24
+ - Crew workflow and global enablement
25
+ - Worker and Reviewer policy
26
+ - model priority, fallback, review, and adaptive routing
27
+ - multimodal (vision/imagegen) capability switches
28
+ - providers lifecycle (migrate/delete/rollback/quarantine)
29
+ - credential references lifecycle
30
+ - workspaces, presets, jobs
31
+ - installation integrations (Codex, Claude, ZCode)
32
+ - task status and bounded model-invocation summaries
33
+
34
+ Bundle: `lib/client.js` (module `@ran-sh/dsh-crew`), built from
35
+ `src/client/entry.tsx`.
36
+
37
+ ## 3080: official frontend; optional legacy Crew controls
38
+
39
+ An older official Harness `web` profile on `127.0.0.1:3080` may still host a
40
+ NARROW quick-controls card — and nothing else:
41
+
42
+ - master switch (`subagents_enabled`)
43
+ - Flash / Pro model priority lists (add/remove/reorder only)
44
+ - vision / imagegen toggles + providers (restart-pending flow)
45
+ - deep link to the 3210 full control plane (`http://127.0.0.1:3210/`)
46
+
47
+ Bundle: `official-web-bridge/lib/client.js` (module
48
+ `@ran-sh/dsh-crew-web-bridge`), built from `src/client/quick-entry.tsx`.
49
+ It physically contains none of the full control-plane code (no credential
50
+ purge, no provider delete/migration, no install integration).
51
+
52
+ The 3080 bridge proxies ONLY two exact endpoints to 3210:
53
+ `quick-config` and `quick-status`. Restart and rollback belong to 3210;
54
+ the quick panel links there when needed. Everything else on the Crew namespace is 404 on
55
+ 3080, and `/supervisor/restart` returns 410 Gone pointing at 3210.
56
+
57
+ The official `~/.dsh` tree is strictly read-only for Crew; the 3080 quick
58
+ surface itself is optional — Crew works fully with 3080 closed.
59
+
60
+ ## UNKNOWN: diagnostics only
61
+
62
+ Unknown surfaces render diagnostics and never gain write authority.
63
+
64
+ ## Surface detection and failure behavior
65
+
66
+ The client does not infer responsibility from a hard-coded browser port. It
67
+ uses two same-origin, structured signals:
68
+
69
+ 1. `/_dsh/dsh-crew/bridge-status` identifies the official quick bridge.
70
+ 2. `/_dsh/dsh-crew/runtime` identifies the native Crew-owned runtime.
71
+
72
+ The bridge signal wins on 3080 because the proxied runtime response correctly
73
+ describes the 3210 backend. If neither contract can be verified, the client
74
+ fails closed to the diagnostics view. Missing evidence never enables the
75
+ full control plane and never becomes `READY`.
76
+
77
+ The split changes presentation only. Crew state, credentials, routing policy,
78
+ and model execution remain isolated under the Crew-owned home and profile.
79
+
80
+ ## Local trust model
81
+
82
+ Both surfaces listen on loopback only and share one request guard
83
+ (`src/local-request-guard.mjs`). A request is trusted when it proves, in
84
+ order: (1) the TCP peer is a loopback address, (2) the `Host` header names a
85
+ loopback host, (3) browser fetch-metadata (`Sec-Fetch-Site`) is `same-origin`
86
+ or absent/`none`, and (4) when an `Origin` header is present it names a
87
+ loopback host — on the 3080 bridge the `Origin` authority must additionally
88
+ equal the request authority, while the 3210 hub accepts any loopback origin
89
+ because the 3080 panel calls it cross-port.
90
+
91
+ There is deliberately no bearer token: every process running as the local
92
+ user is inside the trust boundary and may control Crew. The guards keep
93
+ off-machine browsers, DNS-rebinding pages, and cross-machine proxies out;
94
+ they do not authenticate local users. If a deployment ever needs to separate
95
+ local principals, add a per-install random token to state-changing calls and
96
+ inject it server-side in the bridge — do not use browser `Origin` as
97
+ authentication.
98
+
99
+ ## Process ownership
100
+
101
+ The Windows launcher supervisor (`windows/start-dsh-crew.ps1` watch mode) is
102
+ the ONLY process authority for the Crew-owned 3210 service. The official 3080
103
+ surface never starts, owns, or supervises 3210. Restart and maintenance go
104
+ through durable request files the hub writes and the launcher executes
105
+ (`supervisor/restart-request.mjs`). Ordinary backend startup does not probe the
106
+ legacy bridge. Desktop opening uses the official process and root HTTP response,
107
+ independently of whether that process has any Crew plugin installed.
@@ -1,4 +1,4 @@
1
- # Lightweight official-web bridge. The full Crew Hub remains isolated on 3210.
2
- - insert:
3
- - id: dsh-crew-official-web-bridge
4
- name: '@ran-sh/dsh-crew-web-bridge'
1
+ # Lightweight official-web bridge. The full Crew Hub remains isolated on 3210.
2
+ - insert:
3
+ - id: dsh-crew-official-web-bridge
4
+ name: '@ran-sh/dsh-crew-web-bridge'
@@ -1 +1 @@
1
- export { apply, registerOfficialWebBridge } from '../src/official-web-bridge.mjs';
1
+ export { apply, registerOfficialWebBridge } from '../src/official-web-bridge.mjs';
@@ -1,59 +1,59 @@
1
- // UI-only overlay host. No process lifecycle, installer or profile mutations.
2
- import { localRequestCore, originAuthorityMatches } from '../src/local-request-guard.mjs';
3
- import { readFileSync } from 'node:fs';
4
-
5
- const PREFIX = '/_dsh/dsh-crew';
6
- const BACKEND = 'http://127.0.0.1:3210';
7
- const REVISION = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).dshCrewFrontendRevision ?? null;
8
-
9
- function trusted(req) {
10
- return localRequestCore(req) && (req.headers.origin === undefined
11
- || originAuthorityMatches(req.headers.origin, req.headers.host));
12
- }
13
-
14
- function send(res, status, body) {
15
- res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
16
- res.end(JSON.stringify(body));
17
- }
18
-
19
- export function registerFrontend(ctx, { fetchImpl = globalThis.fetch } = {}) {
20
- return ctx.inject(['webServer'], ({ webServer }) => {
21
- const disposers = [];
22
- disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}/bridge-status`, handler(req, res) {
23
- if (!trusted(req)) return send(res, 403, { ok: false, code: 'LOCAL_SAME_ORIGIN_ONLY' });
24
- send(res, 200, { ok: true, surface: 'official-bridge', ui_role: 'quick-controls', frontend_revision: REVISION, full_control_plane_url: `${BACKEND}/` });
25
- } }));
26
- for (const suffix of ['/quick-config', '/quick-status']) {
27
- disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}${suffix}`, async handler(req, res) {
28
- if (!trusted(req)) return send(res, 403, { ok: false, code: 'LOCAL_SAME_ORIGIN_ONLY' });
29
- const method = req.method ?? 'GET';
30
- if (method !== 'GET' && !(suffix === '/quick-config' && method === 'POST')) return send(res, 405, { ok: false, code: 'METHOD_NOT_ALLOWED' });
31
- try {
32
- const identityResponse = await fetchImpl(`${BACKEND}${PREFIX}/runtime`, { signal: AbortSignal.timeout(2500) });
33
- const identity = await identityResponse.json();
34
- if (!identityResponse.ok || identity.ok !== true || identity.service !== 'dsh-crew-hub'
35
- || identity.profile !== 'dsh-crew' || identity.execution_plane !== 'hub-3210'
36
- || identity.listen_port !== 3210 || identity.protocol_version !== 1) throw new Error('incompatible backend');
37
- const chunks = [];
38
- let size = 0;
39
- if (method === 'POST') for await (const chunk of req) {
40
- size += chunk.length;
41
- if (size > 64 * 1024) return send(res, 413, { ok: false, code: 'BODY_TOO_LARGE' });
42
- chunks.push(Buffer.from(chunk));
43
- }
44
- const response = await fetchImpl(`${BACKEND}${PREFIX}${suffix}`, {
45
- method, headers: { 'content-type': 'application/json' },
46
- ...(method === 'POST' ? { body: Buffer.concat(chunks) } : {}),
47
- signal: AbortSignal.timeout(5000),
48
- });
49
- send(res, response.status, await response.json());
50
- } catch {
51
- send(res, 503, { ok: false, ready: false, code: 'CREW_BACKEND_UNAVAILABLE' });
52
- }
53
- } }));
54
- }
55
- return () => { for (const dispose of disposers) dispose?.(); };
56
- });
57
- }
58
-
59
- export function apply(ctx) { registerFrontend(ctx); }
1
+ // UI-only overlay host. No process lifecycle, installer or profile mutations.
2
+ import { localRequestCore, originAuthorityMatches } from '../src/local-request-guard.mjs';
3
+ import { readFileSync } from 'node:fs';
4
+
5
+ const PREFIX = '/_dsh/dsh-crew';
6
+ const BACKEND = 'http://127.0.0.1:3210';
7
+ const REVISION = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).dshCrewFrontendRevision ?? null;
8
+
9
+ function trusted(req) {
10
+ return localRequestCore(req) && (req.headers.origin === undefined
11
+ || originAuthorityMatches(req.headers.origin, req.headers.host));
12
+ }
13
+
14
+ function send(res, status, body) {
15
+ res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
16
+ res.end(JSON.stringify(body));
17
+ }
18
+
19
+ export function registerFrontend(ctx, { fetchImpl = globalThis.fetch } = {}) {
20
+ return ctx.inject(['webServer'], ({ webServer }) => {
21
+ const disposers = [];
22
+ disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}/bridge-status`, handler(req, res) {
23
+ if (!trusted(req)) return send(res, 403, { ok: false, code: 'LOCAL_SAME_ORIGIN_ONLY' });
24
+ send(res, 200, { ok: true, surface: 'official-bridge', ui_role: 'quick-controls', frontend_revision: REVISION, full_control_plane_url: `${BACKEND}/` });
25
+ } }));
26
+ for (const suffix of ['/quick-config', '/quick-status']) {
27
+ disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}${suffix}`, async handler(req, res) {
28
+ if (!trusted(req)) return send(res, 403, { ok: false, code: 'LOCAL_SAME_ORIGIN_ONLY' });
29
+ const method = req.method ?? 'GET';
30
+ if (method !== 'GET' && !(suffix === '/quick-config' && method === 'POST')) return send(res, 405, { ok: false, code: 'METHOD_NOT_ALLOWED' });
31
+ try {
32
+ const identityResponse = await fetchImpl(`${BACKEND}${PREFIX}/runtime`, { signal: AbortSignal.timeout(2500) });
33
+ const identity = await identityResponse.json();
34
+ if (!identityResponse.ok || identity.ok !== true || identity.service !== 'dsh-crew-hub'
35
+ || identity.profile !== 'dsh-crew' || identity.execution_plane !== 'hub-3210'
36
+ || identity.listen_port !== 3210 || identity.protocol_version !== 1) throw new Error('incompatible backend');
37
+ const chunks = [];
38
+ let size = 0;
39
+ if (method === 'POST') for await (const chunk of req) {
40
+ size += chunk.length;
41
+ if (size > 64 * 1024) return send(res, 413, { ok: false, code: 'BODY_TOO_LARGE' });
42
+ chunks.push(Buffer.from(chunk));
43
+ }
44
+ const response = await fetchImpl(`${BACKEND}${PREFIX}${suffix}`, {
45
+ method, headers: { 'content-type': 'application/json' },
46
+ ...(method === 'POST' ? { body: Buffer.concat(chunks) } : {}),
47
+ signal: AbortSignal.timeout(5000),
48
+ });
49
+ send(res, response.status, await response.json());
50
+ } catch {
51
+ send(res, 503, { ok: false, ready: false, code: 'CREW_BACKEND_UNAVAILABLE' });
52
+ }
53
+ } }));
54
+ }
55
+ return () => { for (const dispose of disposers) dispose?.(); };
56
+ });
57
+ }
58
+
59
+ export function apply(ctx) { registerFrontend(ctx); }
@@ -1,25 +1,25 @@
1
- {
2
- "name": "@ran-sh/dsh-crew-web-bridge",
3
- "version": "0.3.8",
4
- "private": true,
5
- "type": "module",
6
- "main": "./entry.mjs",
7
- "exports": {
8
- ".": "./entry.mjs",
9
- "./client": "./lib/client.js",
10
- "./package.json": "./package.json"
11
- },
12
- "dsh": {
13
- "bundle": {
14
- "patch": "./cordis.patch.yml"
15
- },
16
- "client": {
17
- "inject": [
18
- "@deepseek-ai/dsh-client-ui-renderer",
19
- "@deepseek-ai/dsh-client-locale",
20
- "@deepseek-ai/dsh-client-ui-settings"
21
- ],
22
- "platform": "web"
23
- }
24
- }
25
- }
1
+ {
2
+ "name": "@ran-sh/dsh-crew-web-bridge",
3
+ "version": "0.3.8",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "./entry.mjs",
7
+ "exports": {
8
+ ".": "./entry.mjs",
9
+ "./client": "./lib/client.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "dsh": {
13
+ "bundle": {
14
+ "patch": "./cordis.patch.yml"
15
+ },
16
+ "client": {
17
+ "inject": [
18
+ "@deepseek-ai/dsh-client-ui-renderer",
19
+ "@deepseek-ai/dsh-client-locale",
20
+ "@deepseek-ai/dsh-client-ui-settings"
21
+ ],
22
+ "platform": "web"
23
+ }
24
+ }
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ran-sh/dsh-crew",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {