@ran-sh/dsh-crew 2.2.0 → 2.2.2
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.
- package/.claude-plugin/plugin.json +1 -1
- package/official-web-bridge/lib/client.js +1 -1
- package/official-web-bridge/overlay-entry.mjs +5 -1
- package/official-web-bridge/package.json +1 -1
- package/package.json +1 -1
- package/scripts/setup.mjs +39 -33
- package/scripts/verify-npm-install.mjs +2 -2
- package/src/adaptive-routing.mjs +2 -2
- package/src/ci-evidence.mjs +1 -1
- package/src/client/quick-panel.tsx +6 -2
- package/src/credential-lifecycle.mjs +0 -3
- package/src/credential-purge-state.mjs +1 -5
- package/src/credential-reference.mjs +0 -8
- package/src/crew-workspaces.mjs +1 -1
- package/src/delivery.mjs +2 -2
- package/src/dsh-cli-runtime.mjs +227 -30
- package/src/extension-contract.mjs +1 -1
- package/src/failure-classification.mjs +0 -10
- package/src/hub/entry.mjs +1 -1
- package/src/hub/index.mjs +57 -7
- package/src/i18n.mjs +0 -2
- package/src/install/crew-paths.mjs +16 -11
- package/src/install/crew-skill.mjs +1 -1
- package/src/install/desktop-profile.mjs +168 -0
- package/src/install/harness-config-import.mjs +204 -0
- package/src/install/install-legacy.mjs +3 -3
- package/src/install/npx-lifecycle.mjs +210 -125
- package/src/install/official-web.mjs +1 -1
- package/src/install/windows-startup.mjs +5 -5
- package/src/install/windows-supervisor-adapter.mjs +3 -2
- package/src/install/windows-supervisor-lifecycle.mjs +2 -2
- package/src/install/zcode.mjs +0 -2
- package/src/job-contracts.mjs +1 -1
- package/src/job-identity.mjs +2 -2
- package/src/jobs.mjs +3 -4
- package/src/model-routing.mjs +3 -4
- package/src/model-schedule.mjs +0 -1
- package/src/official-web-bridge.mjs +3 -1
- package/src/policy-legacy.mjs +10 -12
- package/src/provider-inventory.mjs +1 -1
- package/src/provider-layer-migration.mjs +0 -2
- package/src/provider-settings-store.mjs +19 -1
- package/src/readiness-matrix.mjs +1 -1
- package/src/release-in-use.mjs +1 -1
- package/src/role-profiles.mjs +2 -2
- package/src/runtime-controls.mjs +1 -1
- package/src/runtime-identity.mjs +1 -1
- package/src/status-shard.mjs +1 -1
- package/src/supervisor/restart-request.mjs +4 -4
- package/src/workflow-runtime.mjs +1 -2
- package/src/workspace-context.mjs +2 -2
- package/src/workspace-isolation.mjs +4 -4
- package/src/workspace-lock.mjs +2 -3
- package/scripts/policy-probe.mjs +0 -101
- package/scripts/verify-crew-ui-polish.mjs +0 -145
- package/scripts/verify-history-ui.mjs +0 -98
- package/scripts/verify-installer-fix.mjs +0 -26
|
@@ -61,7 +61,7 @@ function PanelHeader({ title, eyebrow, description, href, linkText, children })
|
|
|
61
61
|
//#endregion
|
|
62
62
|
//#region src/client/quick-panel.tsx
|
|
63
63
|
const API = "/_dsh/dsh-crew";
|
|
64
|
-
const FULL = "http://127.0.0.1:3210/";
|
|
64
|
+
const FULL = "http://127.0.0.1:3210/_dsh/dsh-crew/control-plane";
|
|
65
65
|
const RESTART_KEYS = /* @__PURE__ */ new Set([]);
|
|
66
66
|
const T = {
|
|
67
67
|
zh: {
|
|
@@ -4,6 +4,10 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
|
|
5
5
|
const PREFIX = '/_dsh/dsh-crew';
|
|
6
6
|
const BACKEND = 'http://127.0.0.1:3210';
|
|
7
|
+
// The control plane's session belongs to the hub's own process, so the only usable
|
|
8
|
+
// entry is the hub's redirect: it mints the session and 302s into it. Advertising
|
|
9
|
+
// the bare port here sent operators to "dsh web authentication required".
|
|
10
|
+
const CONTROL_PLANE = `${BACKEND}${PREFIX}/control-plane`;
|
|
7
11
|
const REVISION = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).dshCrewFrontendRevision ?? null;
|
|
8
12
|
|
|
9
13
|
function trusted(req) {
|
|
@@ -21,7 +25,7 @@ export function registerFrontend(ctx, { fetchImpl = globalThis.fetch } = {}) {
|
|
|
21
25
|
const disposers = [];
|
|
22
26
|
disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}/bridge-status`, handler(req, res) {
|
|
23
27
|
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:
|
|
28
|
+
send(res, 200, { ok: true, surface: 'official-bridge', ui_role: 'quick-controls', frontend_revision: REVISION, full_control_plane_url: CONTROL_PLANE });
|
|
25
29
|
} }));
|
|
26
30
|
for (const suffix of ['/quick-config', '/quick-status']) {
|
|
27
31
|
disposers.push(webServer.register({ kind: 'exact', path: `${PREFIX}${suffix}`, async handler(req, res) {
|
package/package.json
CHANGED
package/scripts/setup.mjs
CHANGED
|
@@ -15,12 +15,13 @@ import { crewDshHome, crewProfileDir, CREW_PROFILE_NAME, claudeIntegrationLine }
|
|
|
15
15
|
import {
|
|
16
16
|
ensureCrewDshRuntime,
|
|
17
17
|
ensureCrewPluginRegistration,
|
|
18
|
+
ensureCrewWebProfile,
|
|
18
19
|
resolveDshCli,
|
|
19
|
-
runResolvedDsh,
|
|
20
20
|
describeDshCli,
|
|
21
21
|
removeCrewPluginRegistration,
|
|
22
22
|
TARGET_DSH_VERSION,
|
|
23
23
|
} from '../src/dsh-cli-runtime.mjs';
|
|
24
|
+
import { integrationRoot } from '../src/install/crew-paths.mjs';
|
|
24
25
|
|
|
25
26
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
27
|
|
|
@@ -81,11 +82,12 @@ function run(cmd, args, opts = {}) {
|
|
|
81
82
|
* may use `npx -y` to fetch DSH, while status/uninstall probes never trigger a
|
|
82
83
|
* network download merely by checking availability.
|
|
83
84
|
*
|
|
84
|
-
* The returned descriptor is the CLI base only
|
|
85
|
-
* `
|
|
86
|
-
*
|
|
85
|
+
* The returned descriptor is the CLI base only, reported to the operator as
|
|
86
|
+
* `description`; the dedicated dsh-crew profile and the Crew-owned DSH_HOME
|
|
87
|
+
* are supplied by the install flow. The official ``web`` profile is never a
|
|
88
|
+
* target.
|
|
87
89
|
*/
|
|
88
|
-
|
|
90
|
+
function detectDsh({ allowDownload = false, home = homedir(), includeCompatibility = true } = {}) {
|
|
89
91
|
const resolved = resolveDshCli({
|
|
90
92
|
home,
|
|
91
93
|
allowDownload,
|
|
@@ -95,18 +97,6 @@ export function detectDsh({ allowDownload = false, home = homedir(), includeComp
|
|
|
95
97
|
return { ...resolved, cli: resolved.command, description: describeDshCli(resolved) };
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
/**
|
|
99
|
-
* Run a DSH plugin command against the dedicated Crew profile under the
|
|
100
|
-
* Crew-owned DSH_HOME. `home` is the "user home" base used to derive that
|
|
101
|
-
* isolated home, so tests can point it at a disposable root.
|
|
102
|
-
*/
|
|
103
|
-
export function runDsh(dsh, args, { home = homedir() } = {}) {
|
|
104
|
-
const resolved = dsh?.command
|
|
105
|
-
? dsh
|
|
106
|
-
: { kind: dsh?.kind ?? 'legacy', command: dsh?.cli ?? dsh, args: [] };
|
|
107
|
-
return runResolvedDsh(resolved, ['plugin', '--profile', CREW_PROFILE_NAME, ...args], { home });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
100
|
export function readPackageName(root = ROOT) {
|
|
111
101
|
try { return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).name; } catch { return null; }
|
|
112
102
|
}
|
|
@@ -117,20 +107,11 @@ export function checkRoot(root = ROOT) {
|
|
|
117
107
|
&& existsSync(join(root, 'cordis.patch.yml'));
|
|
118
108
|
}
|
|
119
109
|
|
|
120
|
-
|
|
110
|
+
function depsPresent(root = ROOT) {
|
|
121
111
|
return existsSync(join(root, 'node_modules', '@modelcontextprotocol', 'sdk'))
|
|
122
112
|
&& existsSync(join(root, 'node_modules', '.bin', 'tsdown'));
|
|
123
113
|
}
|
|
124
114
|
|
|
125
|
-
export function profileHasPackage(home, name, profileRoot = crewProfileDir({ home })) {
|
|
126
|
-
const pkgFile = join(profileRoot, 'package.json');
|
|
127
|
-
if (!existsSync(pkgFile)) return false;
|
|
128
|
-
try {
|
|
129
|
-
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'));
|
|
130
|
-
return Boolean(pkg.dependencies?.[name]) || Boolean((pkg.dsh?.profile?.bundles ?? []).includes(name));
|
|
131
|
-
} catch { return false; }
|
|
132
|
-
}
|
|
133
|
-
|
|
134
115
|
function mark(log, ok, text) {
|
|
135
116
|
log(`${ok ? '✓' : '✗'} ${text}`);
|
|
136
117
|
return ok;
|
|
@@ -167,6 +148,8 @@ export async function setupInstall({
|
|
|
167
148
|
home = homedir(),
|
|
168
149
|
installer = realInstaller,
|
|
169
150
|
depRunner = null,
|
|
151
|
+
ensureRuntime = null,
|
|
152
|
+
ensureWebProfile = null,
|
|
170
153
|
} = {}) {
|
|
171
154
|
log('DSH Crew installer');
|
|
172
155
|
if (!checkRoot(root)) return { ok: false, error: `not a dsh-crew checkout: ${root}` };
|
|
@@ -217,8 +200,15 @@ export async function setupInstall({
|
|
|
217
200
|
error: `Crew runtime ${dsh.version} does not match ${TARGET_DSH_VERSION}; use the transactional update path (dsh-crew update) to migrate the cohort`,
|
|
218
201
|
};
|
|
219
202
|
}
|
|
220
|
-
if (!dryRun && (!dsh || dsh.
|
|
221
|
-
|
|
203
|
+
if (!dryRun && (!dsh || !dsh.reusable)) {
|
|
204
|
+
// The Windows launcher only starts a Crew-owned CLI entry: a global
|
|
205
|
+
// official dsh or a transient npx download cannot pass its preflight, so
|
|
206
|
+
// the launcher would report "DSH CLI was not found" on a machine that has
|
|
207
|
+
// a perfectly good global dsh. A machine without a reusable runtime
|
|
208
|
+
// provisions one here — before the launcher ever runs — and the web
|
|
209
|
+
// profile scaffold below completes what that preflight and the supervisor
|
|
210
|
+
// require. Idempotent: a matching reusable runtime is reused as-is.
|
|
211
|
+
const boot = (ensureRuntime ?? ensureCrewDshRuntime)({ home });
|
|
222
212
|
if (boot.ok) dsh = boot.cli;
|
|
223
213
|
else log(`- reusable Crew DSH runtime unavailable (${boot.code ?? 'unknown'}); trying compatibility fallback`);
|
|
224
214
|
}
|
|
@@ -240,11 +230,27 @@ export async function setupInstall({
|
|
|
240
230
|
return { ok: false, error: 'DSH profile link failed' };
|
|
241
231
|
}
|
|
242
232
|
mark(log, true, `DSH crew profile linked offline (dedicated Crew DSH_HOME, profile dsh-crew; ${registration.changed ? 'updated' : 'already current'})`);
|
|
233
|
+
// The launcher preflight and the supervisor both require
|
|
234
|
+
// <crew DSH_HOME>/profiles/web; without it a fresh install deadlocked at
|
|
235
|
+
// the preflight ("The Harness web profile is missing") and the supervisor
|
|
236
|
+
// heartbeat timed out. Same idempotent scaffold the runtime step creates
|
|
237
|
+
// on the npx-managed path.
|
|
238
|
+
const webProfile = (ensureWebProfile ?? ensureCrewWebProfile)({ home });
|
|
239
|
+
if (!webProfile.ok) {
|
|
240
|
+
log(`✗ Crew web profile bootstrap failed (${webProfile.code ?? 'unknown'})`);
|
|
241
|
+
return { ok: false, error: 'Crew web profile bootstrap failed' };
|
|
242
|
+
}
|
|
243
|
+
if (webProfile.created) mark(log, true, 'Crew web profile scaffold created');
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
// The host integrations must be written against the same root readiness
|
|
247
|
+
// derives (integrationRoot) — see activateRelease. After registration the
|
|
248
|
+
// link resolves to this checkout, so this is the loader link path.
|
|
249
|
+
const integrationWriteRoot = integrationRoot({ home, root, name });
|
|
250
|
+
|
|
245
251
|
if (dryRun) mark(log, true, 'Codex Desktop integration (dry-run)');
|
|
246
252
|
else {
|
|
247
|
-
const r = installer.installCodex ? installer.installCodex({ home, root }) : realInstaller.installCodex({ home, root });
|
|
253
|
+
const r = installer.installCodex ? installer.installCodex({ home, root: integrationWriteRoot }) : realInstaller.installCodex({ home, root: integrationWriteRoot });
|
|
248
254
|
mark(log, r.ok !== false, r.ok === false ? `Codex Desktop integration failed: ${(r.actions ?? []).join('; ')}` : 'Codex Desktop integration');
|
|
249
255
|
if (r.ok === false) return { ok: false, error: 'Codex integration failed' };
|
|
250
256
|
}
|
|
@@ -252,8 +258,8 @@ export async function setupInstall({
|
|
|
252
258
|
if (dryRun) mark(log, true, 'ZCode integration (dry-run)');
|
|
253
259
|
else {
|
|
254
260
|
const r = installer.installZCode
|
|
255
|
-
? installer.installZCode({ home, root })
|
|
256
|
-
: realInstaller.installZCode({ home, root });
|
|
261
|
+
? installer.installZCode({ home, root: integrationWriteRoot })
|
|
262
|
+
: realInstaller.installZCode({ home, root: integrationWriteRoot });
|
|
257
263
|
if (r.ok === false) {
|
|
258
264
|
mark(log, false, `ZCode integration failed (${r.code ?? 'unknown'})`);
|
|
259
265
|
return { ok: false, error: 'ZCode integration failed' };
|
|
@@ -274,7 +280,7 @@ export async function setupInstall({
|
|
|
274
280
|
}
|
|
275
281
|
|
|
276
282
|
if (commandExists('claude')) {
|
|
277
|
-
const r = await runClaudeIntegrationStep({ dryRun, log, root, home, installer });
|
|
283
|
+
const r = await runClaudeIntegrationStep({ dryRun, log, root: integrationWriteRoot, home, installer });
|
|
278
284
|
if (!r.ok) return r;
|
|
279
285
|
} else log('- Claude Code not detected, skipped');
|
|
280
286
|
|
|
@@ -24,7 +24,7 @@ const DSH_PACKAGE = '@deepseek-ai/dsh';
|
|
|
24
24
|
const DSH_PREFIX = '@deepseek-ai/dsh-';
|
|
25
25
|
const MAX_DIRECT_PEERS = 64;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
function resolveNpmInvocation({
|
|
28
28
|
platform = process.platform,
|
|
29
29
|
nodePath = process.execPath,
|
|
30
30
|
fileExists = existsSync,
|
|
@@ -144,7 +144,7 @@ function manifestUrl(registryUrl, name, version) {
|
|
|
144
144
|
return `${registryUrl.replace(/\/+$/, '')}/${encodedName}/${encodeURIComponent(version)}`;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
async function fetchPublicManifest(name, version, {
|
|
148
148
|
registryUrl = registry,
|
|
149
149
|
fetchImpl = globalThis.fetch,
|
|
150
150
|
} = {}) {
|
package/src/adaptive-routing.mjs
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// caller may record only outcomes Crew already observed, and routing decisions
|
|
6
6
|
// expose bounded evidence suitable for model-selection trace metadata.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const ADAPTIVE_ROUTING_VERSION = 1;
|
|
9
|
+
const ADAPTIVE_ROUTING_DEFAULTS = Object.freeze({
|
|
10
10
|
enabled: false,
|
|
11
11
|
window_size: 8,
|
|
12
12
|
min_samples: 2,
|
package/src/ci-evidence.mjs
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// one; nothing here reads credentials from disk, and the value is never logged,
|
|
30
30
|
// returned, or included in an evidence record.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const CI_EVIDENCE_REPO = 'Ran-sh/dsh-crew';
|
|
33
33
|
|
|
34
34
|
// The row each CI job validates. A platform with no job in the workflow cannot
|
|
35
35
|
// be evidenced by any run, so `macos_smoke` is deliberately absent rather than
|
|
@@ -9,8 +9,12 @@ import { PanelHeader, PanelStyles } from './panel-chrome';
|
|
|
9
9
|
// (/_dsh/dsh-crew/quick-config, /quick-status). Runtime maintenance stays on 3210.
|
|
10
10
|
|
|
11
11
|
const API = '/_dsh/dsh-crew';
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// The control plane needs its own session, and this panel runs in a DIFFERENT
|
|
13
|
+
// harness process (the desktop app, or the 3080 frontend): its connection service
|
|
14
|
+
// cannot mint a token for 3210, so linking the bare port landed the operator on
|
|
15
|
+
// "dsh web authentication required". The hub's own endpoint mints the session and
|
|
16
|
+
// redirects, so no token is ever published to a page or a file.
|
|
17
|
+
const FULL = 'http://127.0.0.1:3210/_dsh/dsh-crew/control-plane';
|
|
14
18
|
|
|
15
19
|
type ModelEntry = { provider: string; model: string };
|
|
16
20
|
type QuickConfig = {
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import { createHash, randomUUID } from 'node:crypto';
|
|
6
6
|
|
|
7
|
-
export const CREDENTIAL_PURGE_STATES = Object.freeze([
|
|
8
|
-
'PLANNED', 'REFERENCE_RECHECKED', 'PURGE_PENDING_CONFIRMATION', 'PURGED', 'VERIFIED', 'FAILED',
|
|
9
|
-
]);
|
|
10
7
|
const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,191}$/;
|
|
11
8
|
const REVISION_PATTERN = /^[a-f0-9]{64}$/;
|
|
12
9
|
|
|
@@ -36,7 +36,7 @@ export function markCredentialPurged(input, plan) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
function markCredentialPurgeUnverified(input, plan) {
|
|
40
40
|
const state = normalizeCredentialPurgeState(input);
|
|
41
41
|
if (!validId(plan?.reference_id) || !validId(plan?.plan_id)) return state;
|
|
42
42
|
const purged = { ...state.purged };
|
|
@@ -53,7 +53,3 @@ export function recordCredentialPurgeOutcome(input, plan, result) {
|
|
|
53
53
|
if (result?.state === 'PURGED') return markCredentialPurgeUnverified(input, plan);
|
|
54
54
|
return normalizeCredentialPurgeState(input);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
export function isCredentialPurged(input, referenceId) {
|
|
58
|
-
return Object.prototype.hasOwnProperty.call(normalizeCredentialPurgeState(input).purged, referenceId);
|
|
59
|
-
}
|
|
@@ -28,11 +28,3 @@ export function classifyCredentialReference(value, { kind = 'env' } = {}) {
|
|
|
28
28
|
: HANDLE_REFERENCE.test(raw) && !SECRET_LIKE.test(raw) && !EMBEDDED_SECRET.test(raw);
|
|
29
29
|
return { present: true, value: valid ? raw : null, redacted: !valid };
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
export function sanitizeCredentialReference(value, options = {}) {
|
|
33
|
-
return classifyCredentialReference(value, options).value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function hasCredentialReference(value, options = {}) {
|
|
37
|
-
return classifyCredentialReference(value, options).present;
|
|
38
|
-
}
|
package/src/crew-workspaces.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import { acquireWorkspaceLock, DEFAULT_LOCK_WAIT_MS } from './workspace-lock.mjs
|
|
|
32
32
|
|
|
33
33
|
export const CREW_WORKSPACE_WORKER = 'dsh-crew-worker';
|
|
34
34
|
export const CREW_WORKSPACE_REVIEW = 'dsh-crew-review';
|
|
35
|
-
|
|
35
|
+
const CREW_WORKSPACE_NAMES = Object.freeze([CREW_WORKSPACE_WORKER, CREW_WORKSPACE_REVIEW]);
|
|
36
36
|
export const WORKSPACE_CONFLICT = 'WORKSPACE_CONFLICT';
|
|
37
37
|
|
|
38
38
|
/** The workspace a role runs in. Only the reviewer is separated out. */
|
package/src/delivery.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
export const DELIVERY_SECTIONS = ['Diff', 'Tests', 'Risks'];
|
|
13
13
|
export const OPTIONAL_DELIVERY_SECTIONS = ['Unverified'];
|
|
14
|
-
|
|
14
|
+
const ALL_DELIVERY_SECTIONS = [...DELIVERY_SECTIONS, ...OPTIONAL_DELIVERY_SECTIONS];
|
|
15
15
|
|
|
16
16
|
export const REVIEW_SECTIONS = ['Review Findings', 'Evidence', 'Risks', 'Verdict'];
|
|
17
17
|
|
|
@@ -129,7 +129,7 @@ export function appendDeliveryInstructions(task, { tier, isReview } = {}) {
|
|
|
129
129
|
* visible evidence. Two parsers gave two answers about one section; there is now
|
|
130
130
|
* one, and it lives here.
|
|
131
131
|
*/
|
|
132
|
-
|
|
132
|
+
function parseTestRow(line) {
|
|
133
133
|
const normalized = String(line ?? '')
|
|
134
134
|
.replace(/^\s*[-*+]\s+/, '')
|
|
135
135
|
.replace(/^\|/, '')
|