@ran-sh/dsh-crew 1.0.2 → 1.1.0
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/README.md +4 -0
- package/README.zh.md +4 -0
- package/codex/AGENTS.md +101 -92
- package/docs/ui-surfaces.md +48 -21
- package/lib/client.js +235 -34
- package/official-web-bridge/lib/client.js +368 -4532
- package/official-web-bridge/package.json +1 -2
- package/package.json +130 -67
- package/scripts/build-client.mjs +30 -15
- package/scripts/remove-legacy-official-bridge.ps1 +89 -0
- package/scripts/setup.mjs +152 -140
- package/scripts/verify-npm-install.mjs +311 -310
- package/scripts/verify-official-bridge-e2e.mjs +12 -1
- package/src/client/host-readiness.mjs +62 -59
- package/src/client/index.tsx +117 -24
- package/src/client/quick-entry.tsx +10 -0
- package/src/client/quick-panel.tsx +295 -0
- package/src/client/surface-detection.mjs +43 -30
- package/src/credential-reference.mjs +38 -0
- package/src/dsh-cli-runtime.mjs +500 -40
- package/src/dsh-cohort.mjs +20 -0
- package/src/hub/index.mjs +1602 -1016
- package/src/install/npx-lifecycle.mjs +1983 -468
- package/src/install/official-web.mjs +36 -73
- package/src/jobs.mjs +20 -24
- package/src/model-catalog.mjs +8 -1
- package/src/official-web-bridge.mjs +329 -249
- package/src/provider-delete-adapters.mjs +165 -17
- package/src/provider-inventory.mjs +17 -3
- package/src/provider-layer-migration-adapters.mjs +759 -0
- package/src/provider-layer-migration.mjs +198 -0
- package/src/provider-lifecycle-state.mjs +4 -1
- package/src/provider-profile-store.mjs +193 -4
- package/src/provider-settings-store.mjs +82 -6
- package/src/provider-store-lock.mjs +67 -0
- package/src/runtime-identity.mjs +24 -1
- package/src/supervisor/restart-request.mjs +202 -0
- package/windows/start-dsh-crew.ps1 +783 -370
- package/worker.cordis.yml +32 -46
- package/zcode/AGENTS.md +35 -26
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
copyFileSync,
|
|
3
2
|
existsSync,
|
|
4
3
|
lstatSync,
|
|
5
|
-
mkdirSync,
|
|
6
4
|
readFileSync,
|
|
7
5
|
realpathSync,
|
|
8
|
-
unlinkSync,
|
|
9
|
-
writeFileSync,
|
|
10
6
|
} from 'node:fs';
|
|
11
7
|
import { homedir } from 'node:os';
|
|
12
|
-
import {
|
|
13
|
-
import { ensurePluginRegistration, removeCrewPluginRegistration } from '../dsh-cli-runtime.mjs';
|
|
8
|
+
import { join } from 'node:path';
|
|
14
9
|
|
|
15
10
|
export const OFFICIAL_BRIDGE_PACKAGE = '@ran-sh/dsh-crew-web-bridge';
|
|
16
11
|
const STATE_FILENAME = 'official-web.json';
|
|
@@ -30,12 +25,6 @@ function readState(home) {
|
|
|
30
25
|
} catch { return null; }
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
function writeState(home, value) {
|
|
34
|
-
const file = officialWebIntegrationStateFile({ home });
|
|
35
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
36
|
-
writeFileSync(file, JSON.stringify(value, null, 2) + '\n');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
28
|
function validOfficialManifest(file) {
|
|
40
29
|
if (!existsSync(file)) return { ok: false, code: 'OFFICIAL_WEB_PROFILE_NOT_FOUND' };
|
|
41
30
|
try {
|
|
@@ -50,83 +39,57 @@ function validOfficialManifest(file) {
|
|
|
50
39
|
} catch { return { ok: false, code: 'OFFICIAL_WEB_PROFILE_INVALID' }; }
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
if (previous?.backup_file && existsSync(previous.backup_file)) return previous.backup_file;
|
|
55
|
-
const backupDir = join(home, '.config', 'dsh-crew', 'backups');
|
|
56
|
-
mkdirSync(backupDir, { recursive: true });
|
|
57
|
-
const backupFile = join(backupDir, `official-web-package-${Date.now()}.json`);
|
|
58
|
-
copyFileSync(profileManifest, backupFile);
|
|
59
|
-
return backupFile;
|
|
60
|
-
}
|
|
42
|
+
export const OFFICIAL_WEB_READ_ONLY_CODE = 'OFFICIAL_WEB_PROFILE_READ_ONLY';
|
|
61
43
|
|
|
44
|
+
// The official ~/.dsh/profiles/web tree is a read-only boundary. Crew must
|
|
45
|
+
// never install, update, register, unlink, repair, or mutate anything under
|
|
46
|
+
// ~/.dsh; the isolated 3210 Crew backend is the only runtime Crew owns.
|
|
47
|
+
// ensure/remove therefore fail closed instead of writing the official
|
|
48
|
+
// profile. Status probing stays read-only.
|
|
62
49
|
export function ensureOfficialWebIntegration({ home = homedir(), releaseDir } = {}) {
|
|
63
|
-
|
|
64
|
-
try { resolvedRelease = realpathSync(releaseDir); } catch { return { ok: false, code: 'RELEASE_NOT_FOUND' }; }
|
|
65
|
-
const bridgeRoot = join(resolvedRelease, 'official-web-bridge');
|
|
66
|
-
const profileRoot = officialWebProfileDir({ home });
|
|
67
|
-
const profileManifest = join(profileRoot, 'package.json');
|
|
68
|
-
const profile = validOfficialManifest(profileManifest);
|
|
69
|
-
if (!profile.ok) return profile;
|
|
70
|
-
const previous = readState(home);
|
|
71
|
-
const backupFile = makeBackup({ home, profileManifest, previous });
|
|
72
|
-
const registration = ensurePluginRegistration({
|
|
73
|
-
profileRoot,
|
|
74
|
-
root: bridgeRoot,
|
|
75
|
-
name: OFFICIAL_BRIDGE_PACKAGE,
|
|
76
|
-
createProfile: false,
|
|
77
|
-
});
|
|
78
|
-
if (!registration.ok) {
|
|
79
|
-
return { ok: false, code: registration.code === 'CREW_PROFILE_METADATA_INVALID' ? 'OFFICIAL_WEB_PROFILE_INVALID' : registration.code };
|
|
80
|
-
}
|
|
81
|
-
const stateChanged = previous?.enabled !== true || previous?.release_dir !== resolvedRelease || previous?.backup_file !== backupFile;
|
|
82
|
-
writeState(home, {
|
|
83
|
-
enabled: true,
|
|
84
|
-
release_dir: resolvedRelease,
|
|
85
|
-
backup_file: backupFile,
|
|
86
|
-
package: OFFICIAL_BRIDGE_PACKAGE,
|
|
87
|
-
});
|
|
88
|
-
return { ...registration, changed: registration.changed || stateChanged, backupFile };
|
|
50
|
+
return { ok: false, code: OFFICIAL_WEB_READ_ONLY_CODE };
|
|
89
51
|
}
|
|
90
52
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
53
|
+
// Project the legacy full-bridge state file. The old `enabled: true` shape
|
|
54
|
+
// is DEPRECATED: dsh-crew no longer desires any control of the 3080 official
|
|
55
|
+
// web profile (the native 3210 page is the full control plane, and the 3080
|
|
56
|
+
// quick surface is optional). Status probing stays read-only; the projection
|
|
57
|
+
// makes the legacy record diagnostic instead of an active intent.
|
|
58
|
+
function projectLegacyState(state, home, releaseDir) {
|
|
59
|
+
if (!state || state?.enabled !== true) {
|
|
60
|
+
return { enabled: false, legacy_present: false, desired: false, healthy: false, state };
|
|
61
|
+
}
|
|
94
62
|
const expectedRelease = releaseDir ?? state.release_dir;
|
|
95
63
|
let expectedBridge;
|
|
96
64
|
try { expectedBridge = realpathSync(join(expectedRelease, 'official-web-bridge')); } catch {
|
|
97
|
-
return { enabled: true, healthy: false, code: 'BRIDGE_RELEASE_MISSING', state };
|
|
65
|
+
return { enabled: false, legacy_present: true, desired: false, healthy: false, code: 'BRIDGE_RELEASE_MISSING', state };
|
|
98
66
|
}
|
|
99
67
|
const profileRoot = officialWebProfileDir({ home });
|
|
100
68
|
const profile = validOfficialManifest(join(profileRoot, 'package.json'));
|
|
101
|
-
if (!profile.ok)
|
|
69
|
+
if (!profile.ok) {
|
|
70
|
+
return { enabled: false, legacy_present: true, desired: false, healthy: false, code: profile.code, state };
|
|
71
|
+
}
|
|
102
72
|
const dependency = profile.manifest.dependencies?.[OFFICIAL_BRIDGE_PACKAGE];
|
|
103
73
|
const bundled = profile.manifest.dsh.profile.bundles.includes(OFFICIAL_BRIDGE_PACKAGE);
|
|
104
74
|
const linkPath = join(profileRoot, 'node_modules', ...OFFICIAL_BRIDGE_PACKAGE.split('/'));
|
|
105
75
|
let linked = false;
|
|
106
76
|
try { linked = lstatSync(linkPath).isSymbolicLink() && realpathSync(linkPath) === expectedBridge; } catch {}
|
|
107
|
-
return {
|
|
77
|
+
return {
|
|
78
|
+
enabled: false,
|
|
79
|
+
legacy_present: true,
|
|
80
|
+
desired: false,
|
|
81
|
+
healthy: Boolean(dependency && bundled && linked),
|
|
82
|
+
removal_requires_manual_official_profile_action: true,
|
|
83
|
+
state,
|
|
84
|
+
linkPath,
|
|
85
|
+
expectedBridge,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function officialWebIntegrationStatus({ home = homedir(), releaseDir } = {}) {
|
|
90
|
+
return projectLegacyState(readState(home), home, releaseDir);
|
|
108
91
|
}
|
|
109
92
|
|
|
110
93
|
export function removeOfficialWebIntegration({ home = homedir(), remember = true, preserveIntent = false } = {}) {
|
|
111
|
-
|
|
112
|
-
const profileManifest = join(profileRoot, 'package.json');
|
|
113
|
-
const profile = validOfficialManifest(profileManifest);
|
|
114
|
-
if (!profile.ok && profile.code !== 'OFFICIAL_WEB_PROFILE_NOT_FOUND') return profile;
|
|
115
|
-
let removed = false;
|
|
116
|
-
if (profile.ok) {
|
|
117
|
-
const result = removeCrewPluginRegistration({ home, name: OFFICIAL_BRIDGE_PACKAGE, profileRoot });
|
|
118
|
-
if (!result.ok) return { ok: false, code: result.code === 'CREW_PROFILE_METADATA_INVALID' ? 'OFFICIAL_WEB_PROFILE_INVALID' : result.code };
|
|
119
|
-
removed = result.removed;
|
|
120
|
-
}
|
|
121
|
-
const previous = readState(home);
|
|
122
|
-
if (remember) writeState(home, {
|
|
123
|
-
...(previous ?? {}),
|
|
124
|
-
enabled: preserveIntent ? previous?.enabled === true : false,
|
|
125
|
-
package: OFFICIAL_BRIDGE_PACKAGE,
|
|
126
|
-
});
|
|
127
|
-
else {
|
|
128
|
-
const stateFile = officialWebIntegrationStateFile({ home });
|
|
129
|
-
if (existsSync(stateFile)) unlinkSync(stateFile);
|
|
130
|
-
}
|
|
131
|
-
return { ok: true, removed };
|
|
94
|
+
return { ok: false, code: OFFICIAL_WEB_READ_ONLY_CODE };
|
|
132
95
|
}
|
package/src/jobs.mjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// Job manager: each job runs one dsh-jsonrpc-agent runtime via the DSH SDK.
|
|
2
2
|
// Progress derived from session.event notifications; status mirrored to a
|
|
3
3
|
// JSON file so the Claude Code statusline (and anything else) can render it.
|
|
4
|
+
//
|
|
5
|
+
// alpha.5 SDK surface: DeepSeekHarness takes HarnessClientOptions at the top
|
|
6
|
+
// level (no `launch` wrapper). The SDK resolves the installed same-version
|
|
7
|
+
// dsh CLI itself and enforces the same-version manifest check, so dsh-crew
|
|
8
|
+
// never passes an explicit dshBin that would bypass it. The worker rides the
|
|
9
|
+
// `sdk-minimal` profile with the Crew overlay applied as a --patch.
|
|
4
10
|
|
|
5
11
|
import { DeepSeekHarness } from '@deepseek-ai/dsh-sdk-client';
|
|
6
12
|
import { readFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
@@ -8,7 +14,6 @@ import { createShardWriter } from './status-shard.mjs';
|
|
|
8
14
|
import { fileURLToPath } from 'node:url';
|
|
9
15
|
import { dirname, join, resolve } from 'node:path';
|
|
10
16
|
import { homedir } from 'node:os';
|
|
11
|
-
import { createRequire } from 'node:module';
|
|
12
17
|
import { appendDeliveryInstructions, parseDeliveryReport, formatDeliveryMetadata } from './delivery.mjs';
|
|
13
18
|
import { captureWorkspaceBaseline, captureWorkspaceDiff, NOT_A_GIT_REPOSITORY } from './workspace-audit.mjs';
|
|
14
19
|
import { buildOutcome, JOB_PHASES } from './workflow.mjs';
|
|
@@ -17,19 +22,10 @@ import { buildDirectSelectionTrace } from './model-routing.mjs';
|
|
|
17
22
|
|
|
18
23
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
24
|
const CONFIG_DIR = join(homedir(), '.config', 'dsh-crew');
|
|
25
|
+
const CREW_DSH_HOME = join(homedir(), '.config', 'dsh-crew', 'harness');
|
|
20
26
|
const STATUS_FILE = join(CONFIG_DIR, 'status.json');
|
|
21
27
|
const CORDIS = join(ROOT, 'worker.cordis.yml');
|
|
22
28
|
|
|
23
|
-
// The worker agent entry. The pnpm .bin shim on Windows is a POSIX shell
|
|
24
|
-
// script that Node spawn cannot execute (ENOENT), so we always launch
|
|
25
|
-
// `node <lib/bin.js> <cordis>` via process.execPath — cross-platform.
|
|
26
|
-
const requireAgents = createRequire(import.meta.url);
|
|
27
|
-
let AGENT_JS = null;
|
|
28
|
-
for (const spec of ['@deepseek-ai/dsh-sdk-jsonrpc-demo/bin', '@deepseek-ai/dsh-sdk-jsonrpc-demo/lib/bin.js']) {
|
|
29
|
-
try { AGENT_JS = requireAgents.resolve(spec); break; } catch {}
|
|
30
|
-
}
|
|
31
|
-
if (!AGENT_JS) AGENT_JS = join(ROOT, 'node_modules', '@deepseek-ai', 'dsh-sdk-jsonrpc-demo', 'lib', 'bin.js');
|
|
32
|
-
|
|
33
29
|
export const TIERS = {
|
|
34
30
|
flash: { model: 'deepseek-v4-flash', label: 'V4 Flash' },
|
|
35
31
|
pro: { model: 'deepseek-v4-pro', label: 'V4 Pro' },
|
|
@@ -115,7 +111,7 @@ export function startJob({
|
|
|
115
111
|
if (!tierInfo) throw new Error(`unknown tier "${tier}" (expected: ${Object.keys(TIERS).join(', ')})`);
|
|
116
112
|
if (!ROLES[role]) throw new Error(`unknown role "${role}" (expected: ${Object.keys(ROLES).join(', ')})`);
|
|
117
113
|
if (!['off', 'high', 'max'].includes(effort)) throw new Error(`unknown effort "${effort}" (expected: off, high, max)`);
|
|
118
|
-
if (!existsSync(
|
|
114
|
+
if (!existsSync(CORDIS)) throw new Error(`worker overlay not found at ${CORDIS}`);
|
|
119
115
|
const workspace = resolve(cwd ?? process.cwd());
|
|
120
116
|
// The worker always gets the auditable Delivery Contract appended (unless it
|
|
121
117
|
// already carries one), so its final message follows ## Diff / ## Tests /
|
|
@@ -128,22 +124,22 @@ export function startJob({
|
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
const harness = new DeepSeekHarness({
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
requestTimeoutMs: timeoutMs,
|
|
127
|
+
profile: 'sdk-minimal',
|
|
128
|
+
patches: [CORDIS],
|
|
129
|
+
dshHome: CREW_DSH_HOME,
|
|
130
|
+
processCwd: workspace,
|
|
131
|
+
env: {
|
|
132
|
+
...process.env,
|
|
133
|
+
...dotEnv,
|
|
134
|
+
DSH_CWD: workspace,
|
|
135
|
+
DSH_SESSION_ROOT: join(CONFIG_DIR, 'sessions'),
|
|
136
|
+
DSH_REASONING_EFFORT: effort,
|
|
143
137
|
},
|
|
138
|
+
requestTimeoutMs: timeoutMs,
|
|
144
139
|
cwd: workspace,
|
|
145
140
|
provider: 'deepseek-official',
|
|
146
141
|
model: tierInfo.model,
|
|
142
|
+
reasoningEffort: effort,
|
|
147
143
|
maxTokens,
|
|
148
144
|
});
|
|
149
145
|
|
package/src/model-catalog.mjs
CHANGED
|
@@ -20,7 +20,14 @@ function catalogError() {
|
|
|
20
20
|
function normalizeProvider(raw) {
|
|
21
21
|
if (!raw || typeof raw.id !== 'string' || raw.id.trim() === '') return null;
|
|
22
22
|
const id = raw.id.trim();
|
|
23
|
-
|
|
23
|
+
const adapterOwned = typeof raw.adapter_owned === 'boolean'
|
|
24
|
+
? raw.adapter_owned
|
|
25
|
+
: typeof raw.adapterOwned === 'boolean' ? raw.adapterOwned : undefined;
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
name: typeof raw.name === 'string' && raw.name.trim() ? raw.name.trim() : id,
|
|
29
|
+
...(adapterOwned === undefined ? {} : { adapter_owned: adapterOwned }),
|
|
30
|
+
};
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
function normalizeModels(raw, provider) {
|