@ran-sh/dsh-crew 0.5.0 → 0.5.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/README.md +93 -112
- package/README.zh.md +93 -111
- package/codex/AGENTS.md +92 -0
- package/docs/installation.md +63 -0
- package/docs/ui-surfaces.md +1 -2
- package/lib/client.js +41 -4
- package/official-web-bridge/lib/client.js +41 -4
- package/package.json +162 -133
- package/scripts/setup.mjs +65 -19
- package/scripts/verify-npm-install.mjs +16 -3
- package/src/client/host-readiness.mjs +2 -1
- package/src/client/index.tsx +29 -15
- package/src/hub/index.mjs +8 -6
- package/src/install/install-legacy.mjs +65 -11
- package/src/install/install.mjs +3 -1
- package/src/install/npx-lifecycle.mjs +55 -19
- package/src/install/windows-startup.mjs +98 -0
- package/src/install/zcode.mjs +316 -0
- package/src/runtime-identity.mjs +1 -1
- package/windows/start-dsh-crew.cmd +55 -0
- package/windows/start-dsh-crew.vbs +9 -0
- package/zcode/AGENTS.md +21 -0
- package/zcode/agents/ds-reviewer.md +19 -0
- package/zcode/agents/ds-worker.md +20 -0
- package/zcode/commands/dsh-config.md +6 -0
- package/zcode/commands/dsh-status.md +4 -0
package/scripts/setup.mjs
CHANGED
|
@@ -167,12 +167,36 @@ export async function setupInstall({
|
|
|
167
167
|
mark(log, true, `DSH crew profile linked offline (dedicated Crew DSH_HOME, profile dsh-crew; ${registration.changed ? 'updated' : 'already current'})`);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
if (dryRun) mark(log, true, 'Codex Desktop integration (dry-run)');
|
|
171
|
-
else {
|
|
170
|
+
if (dryRun) mark(log, true, 'Codex Desktop integration (dry-run)');
|
|
171
|
+
else {
|
|
172
172
|
const r = installer.installCodex ? installer.installCodex({ home }) : realInstaller.installCodex({ home });
|
|
173
173
|
mark(log, r.ok !== false, r.ok === false ? `Codex Desktop integration failed: ${(r.actions ?? []).join('; ')}` : 'Codex Desktop integration');
|
|
174
|
-
if (r.ok === false) return { ok: false, error: 'Codex integration failed' };
|
|
175
|
-
}
|
|
174
|
+
if (r.ok === false) return { ok: false, error: 'Codex integration failed' };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (dryRun) mark(log, true, 'ZCode integration (dry-run)');
|
|
178
|
+
else {
|
|
179
|
+
const r = installer.installZCode
|
|
180
|
+
? installer.installZCode({ home, root })
|
|
181
|
+
: realInstaller.installZCode({ home, root });
|
|
182
|
+
if (r.ok === false) {
|
|
183
|
+
mark(log, false, `ZCode integration failed (${r.code ?? 'unknown'})`);
|
|
184
|
+
return { ok: false, error: 'ZCode integration failed' };
|
|
185
|
+
}
|
|
186
|
+
mark(log, true, 'ZCode integration');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (dryRun) mark(log, true, 'Windows login startup (dry-run)');
|
|
190
|
+
else {
|
|
191
|
+
const r = installer.installWindowsStartup
|
|
192
|
+
? installer.installWindowsStartup({ home, root })
|
|
193
|
+
: realInstaller.installWindowsStartup({ home, root });
|
|
194
|
+
if (r.ok === false) {
|
|
195
|
+
mark(log, false, `Windows login startup failed (${r.code ?? 'unknown'})`);
|
|
196
|
+
return { ok: false, error: 'Windows login startup failed' };
|
|
197
|
+
}
|
|
198
|
+
if (r.supported) mark(log, true, 'Windows login startup');
|
|
199
|
+
}
|
|
176
200
|
|
|
177
201
|
if (commandExists('claude')) {
|
|
178
202
|
if (dryRun) mark(log, true, 'Claude Code integration (dry-run)');
|
|
@@ -203,17 +227,31 @@ export async function setupUninstall({
|
|
|
203
227
|
const fail = (name, text) => { mark(log, false, text || `${name} failed`); failures.push(name); };
|
|
204
228
|
|
|
205
229
|
if (dryRun) {
|
|
206
|
-
mark(log, true, 'Codex Desktop integration would be removed');
|
|
207
|
-
mark(log, true, '
|
|
208
|
-
mark(log, true, '
|
|
230
|
+
mark(log, true, 'Codex Desktop integration would be removed');
|
|
231
|
+
mark(log, true, 'ZCode integration would be removed');
|
|
232
|
+
mark(log, true, 'Claude Code integration would be removed');
|
|
233
|
+
mark(log, true, 'Windows login startup would be removed');
|
|
234
|
+
mark(log, true, 'DSH crew profile would be removed');
|
|
209
235
|
} else {
|
|
210
236
|
const cx = installer.uninstallCodex ? installer.uninstallCodex({ home }) : realInstaller.uninstallCodex({ home });
|
|
211
|
-
if (cx.ok !== false) mark(log, true, 'Codex Desktop integration removed');
|
|
212
|
-
else fail('codex', 'Codex Desktop integration removal failed');
|
|
237
|
+
if (cx.ok !== false) mark(log, true, 'Codex Desktop integration removed');
|
|
238
|
+
else fail('codex', 'Codex Desktop integration removal failed');
|
|
239
|
+
|
|
240
|
+
const zc = installer.uninstallZCode
|
|
241
|
+
? installer.uninstallZCode({ home, root })
|
|
242
|
+
: realInstaller.uninstallZCode({ home, root });
|
|
243
|
+
if (zc.ok !== false) mark(log, true, 'ZCode integration removed');
|
|
244
|
+
else fail('zcode', 'ZCode integration removal failed');
|
|
213
245
|
|
|
214
|
-
const cl = installer.uninstallClaudeCode ? installer.uninstallClaudeCode({ home }) : realInstaller.uninstallClaudeCode({ home });
|
|
215
|
-
if (cl.ok !== false) mark(log, true, 'Claude Code integration removed');
|
|
216
|
-
else fail('claude', 'Claude Code integration removal failed');
|
|
246
|
+
const cl = installer.uninstallClaudeCode ? installer.uninstallClaudeCode({ home }) : realInstaller.uninstallClaudeCode({ home });
|
|
247
|
+
if (cl.ok !== false) mark(log, true, 'Claude Code integration removed');
|
|
248
|
+
else fail('claude', 'Claude Code integration removal failed');
|
|
249
|
+
|
|
250
|
+
const startup = installer.uninstallWindowsStartup
|
|
251
|
+
? installer.uninstallWindowsStartup({ home })
|
|
252
|
+
: realInstaller.uninstallWindowsStartup({ home });
|
|
253
|
+
if (startup.ok === false) fail('startup', 'Windows login startup removal failed');
|
|
254
|
+
else if (startup.supported) mark(log, true, 'Windows login startup removed');
|
|
217
255
|
|
|
218
256
|
const name = readPackageName(root);
|
|
219
257
|
if (!name) fail('dsh', 'DSH plugin removal failed: package name missing');
|
|
@@ -243,9 +281,15 @@ export async function setupUninstall({
|
|
|
243
281
|
}
|
|
244
282
|
|
|
245
283
|
export async function setupStatus({ log = console.log, root = ROOT, home = homedir(), installer = realInstaller } = {}) {
|
|
246
|
-
const st = installer.installStatus ? installer.installStatus({ home }) : realInstaller.installStatus({ home });
|
|
247
|
-
const claude = st?.claude?.installed ? 'installed' : 'not installed';
|
|
248
|
-
const codex = st?.codex?.installed ? 'installed' : 'not installed';
|
|
284
|
+
const st = installer.installStatus ? installer.installStatus({ home, root }) : realInstaller.installStatus({ home, root });
|
|
285
|
+
const claude = st?.claude?.installed ? 'installed' : 'not installed';
|
|
286
|
+
const codex = st?.codex?.installed ? 'installed' : 'not installed';
|
|
287
|
+
const zcode = st?.zcode?.installed ? 'installed' : 'not installed';
|
|
288
|
+
const startupState = installer.windowsStartupStatus
|
|
289
|
+
? installer.windowsStartupStatus({ home })
|
|
290
|
+
: realInstaller.windowsStartupStatus({ home });
|
|
291
|
+
const windowsStartup = !startupState.supported ? 'not supported'
|
|
292
|
+
: startupState.ready ? 'installed' : startupState.installed ? 'needs repair' : 'not installed';
|
|
249
293
|
// Crew status reads ONLY the dedicated Crew profile under the Crew DSH_HOME;
|
|
250
294
|
// the official web profile layout under the default DSH home is intentionally
|
|
251
295
|
// never inspected.
|
|
@@ -260,10 +304,12 @@ export async function setupStatus({ log = console.log, root = ROOT, home = homed
|
|
|
260
304
|
} catch { dshPlugin = 'unknown'; }
|
|
261
305
|
}
|
|
262
306
|
log(`DSH plugin: ${dshPlugin} (dedicated dsh-crew profile; official web profile ignored)`);
|
|
263
|
-
log(`Codex Desktop integration: ${codex}`);
|
|
264
|
-
log(`
|
|
265
|
-
|
|
266
|
-
}
|
|
307
|
+
log(`Codex Desktop integration: ${codex}`);
|
|
308
|
+
log(`ZCode integration: ${zcode}`);
|
|
309
|
+
log(`Claude Code integration: ${claude}`);
|
|
310
|
+
log(`Windows login startup: ${windowsStartup}`);
|
|
311
|
+
return { ok: true, dshPlugin, codex, zcode, claude, windowsStartup };
|
|
312
|
+
}
|
|
267
313
|
|
|
268
314
|
export async function runSetupCli({ argv = process.argv.slice(2), run: actions = {}, log = console.log } = {}) {
|
|
269
315
|
const action = argv[0];
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
// or fully resolving the very large official DSH dependency graph.
|
|
10
10
|
|
|
11
11
|
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
12
13
|
import os from 'node:os';
|
|
13
14
|
import path from 'node:path';
|
|
14
15
|
import { spawnSync } from 'node:child_process';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
16
17
|
|
|
17
18
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
18
|
-
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
19
19
|
const registry = process.env.NPM_REGISTRY ?? 'https://registry.npmjs.org/';
|
|
20
20
|
export const supportedDshVersion = '0.1.1-rc.2';
|
|
21
21
|
const verifyOfficialDsh = process.argv.includes('--with-official-dsh');
|
|
@@ -23,6 +23,19 @@ const DSH_PACKAGE = '@deepseek-ai/dsh';
|
|
|
23
23
|
const DSH_PREFIX = '@deepseek-ai/dsh-';
|
|
24
24
|
const MAX_DIRECT_PEERS = 64;
|
|
25
25
|
|
|
26
|
+
export function resolveNpmInvocation({
|
|
27
|
+
platform = process.platform,
|
|
28
|
+
nodePath = process.execPath,
|
|
29
|
+
fileExists = existsSync,
|
|
30
|
+
} = {}) {
|
|
31
|
+
if (platform !== 'win32') return { command: 'npm', argsPrefix: [] };
|
|
32
|
+
const npmCli = path.join(path.dirname(nodePath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
33
|
+
if (!fileExists(npmCli)) throw new Error(`npm CLI was not found beside Node.js: ${npmCli}`);
|
|
34
|
+
return { command: nodePath, argsPrefix: [npmCli] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const npmInvocation = resolveNpmInvocation();
|
|
38
|
+
|
|
26
39
|
/**
|
|
27
40
|
* The candidate version is derived from the candidate package manifest at the
|
|
28
41
|
* checkout root instead of a hard-coded release literal, so the verifier never
|
|
@@ -217,10 +230,10 @@ export async function auditOfficialDshCohort({
|
|
|
217
230
|
}
|
|
218
231
|
|
|
219
232
|
function run(args, cwd = root) {
|
|
220
|
-
const result = spawnSync(
|
|
233
|
+
const result = spawnSync(npmInvocation.command, [...npmInvocation.argsPrefix, ...args], {
|
|
221
234
|
cwd,
|
|
222
235
|
encoding: 'utf8',
|
|
223
|
-
shell:
|
|
236
|
+
shell: false,
|
|
224
237
|
windowsHide: true,
|
|
225
238
|
});
|
|
226
239
|
if (result.error) throw result.error;
|
|
@@ -33,13 +33,14 @@ function bridgeState(surface) {
|
|
|
33
33
|
export function projectHostReadiness({ installStatus, runtime, surface } = {}) {
|
|
34
34
|
const codex = installStatus?.codex;
|
|
35
35
|
const claude = installStatus?.claude;
|
|
36
|
+
const zcode = installStatus?.zcode;
|
|
36
37
|
return [
|
|
37
38
|
{ id: 'codex_mcp', state: componentState(codex, ['mcp'], { aligned: true }) },
|
|
38
39
|
{ id: 'ds_worker', state: componentState(codex, ['worker_role'], { aligned: true }) },
|
|
39
40
|
{ id: 'ds_reviewer', state: componentState(codex, ['reviewer_role'], { aligned: true }) },
|
|
40
41
|
{ id: 'claude_plugin', state: componentState(claude, ['enabled', 'marketplace', 'snapshot', 'permissions']) },
|
|
42
|
+
{ id: 'zcode_mcp', state: componentState(zcode, ['mcp', 'policy', 'worker_agent', 'reviewer_agent', 'config_prompt', 'status_prompt', 'ownership']) },
|
|
41
43
|
{ id: 'crew_harness', state: runtimeState(runtime), detail: runtime?.runtime_version ?? null },
|
|
42
44
|
{ id: 'official_bridge', state: bridgeState(surface) },
|
|
43
45
|
];
|
|
44
46
|
}
|
|
45
|
-
|
package/src/client/index.tsx
CHANGED
|
@@ -52,11 +52,11 @@ const COPY = {
|
|
|
52
52
|
expandAll: '全部展开', collapseAll: '全部折叠',
|
|
53
53
|
modelCount: (count: number) => `${count} 个模型`, providerCount: (count: number) => `${count} 个 Provider`,
|
|
54
54
|
jobCount: (count: number) => `${count} 个任务`, runningCount: (count: number) => `${count} 个运行中`,
|
|
55
|
-
sectionNames: { integrations: 'Codex / Claude 集成状态', workflow: 'Crew 工作流设置', flash: 'Worker / Flash', pro: 'Reviewer / Pro', dispatch: '模型优先级与派发', adaptive: '自适应路由', runtime: '运行 / 生效边界', multimodal: '视觉与生图', providers: '自定义 Provider', jobs: '任务状态' },
|
|
55
|
+
sectionNames: { integrations: 'Codex / Claude / ZCode 集成状态', workflow: 'Crew 工作流设置', flash: 'Worker / Flash', pro: 'Reviewer / Pro', dispatch: '模型优先级与派发', adaptive: '自适应路由', runtime: '运行 / 生效边界', multimodal: '视觉与生图', providers: '自定义 Provider', jobs: '任务状态' },
|
|
56
56
|
openHarness: '打开 3210 Crew Harness →',
|
|
57
57
|
harnessHint: '底层 Provider、Harness Models 与运行时配置',
|
|
58
58
|
hostReadiness: '宿主集成就绪度', hostReadinessHint: '只使用结构化安装与运行时证据;缺少证据不会显示 READY。',
|
|
59
|
-
readinessLabels: { codex_mcp: 'Codex MCP', ds_worker: 'ds-worker', ds_reviewer: 'ds-reviewer', claude_plugin: 'Claude plugin', crew_harness: 'Crew Harness', official_bridge: 'Official bridge' },
|
|
59
|
+
readinessLabels: { codex_mcp: 'Codex MCP', ds_worker: 'ds-worker', ds_reviewer: 'ds-reviewer', claude_plugin: 'Claude plugin', zcode_mcp: 'ZCode MCP', crew_harness: 'Crew Harness', official_bridge: 'Official bridge' },
|
|
60
60
|
readinessStates: { READY: 'READY', DEGRADED: 'DEGRADED', UNAVAILABLE: 'UNAVAILABLE', UNKNOWN: 'UNKNOWN' },
|
|
61
61
|
globalHint: '修改即时保存到 ~/.config/dsh-crew/config.json;CC / Codex 的新会话自动读取为默认值(会话内可用 /dsh-crew:config 临时覆盖)。',
|
|
62
62
|
orchestration: 'Agent 编排',
|
|
@@ -154,11 +154,16 @@ const COPY = {
|
|
|
154
154
|
update: '重跑安装流程:刷新注册与权限、迁移旧格式配置、更新 HUD 接线;幂等,可放心点',
|
|
155
155
|
restore: '从 Claude Code 移除集成:清除 marketplace 注册、插件启用项与权限规则(settings 先备份);不删除插件源码与已跑任务',
|
|
156
156
|
},
|
|
157
|
-
codex: {
|
|
158
|
-
install: '把 ds-flash / ds-pro 角色和 /dsh-config、/dsh-status 命令复制到 ~/.codex/(MCP 路径按本机自动渲染,含 approve 审批与超时配置)',
|
|
159
|
-
update: '重新渲染并覆盖角色文件与命令(插件路径或配置变更后用);原文件先备份',
|
|
160
|
-
restore: '删除 ~/.codex/ 下的 ds-flash / ds-pro 角色与 dsh 命令文件(角色文件先备份)',
|
|
161
|
-
},
|
|
157
|
+
codex: {
|
|
158
|
+
install: '把 ds-flash / ds-pro 角色和 /dsh-config、/dsh-status 命令复制到 ~/.codex/(MCP 路径按本机自动渲染,含 approve 审批与超时配置)',
|
|
159
|
+
update: '重新渲染并覆盖角色文件与命令(插件路径或配置变更后用);原文件先备份',
|
|
160
|
+
restore: '删除 ~/.codex/ 下的 ds-flash / ds-pro 角色与 dsh 命令文件(角色文件先备份)',
|
|
161
|
+
},
|
|
162
|
+
zcode: {
|
|
163
|
+
install: '安装 ZCode 全局规则、ds-worker / ds-reviewer 角色、命令和按来源选择的 MCP 配置到 ~/.zcode/',
|
|
164
|
+
update: '重新渲染 ZCode 角色、命令和 MCP 条目;保留个人文件与其他 MCP 服务',
|
|
165
|
+
restore: '仅移除 dsh-crew 管理的 ZCode 文件和 MCP 条目(保留备份)',
|
|
166
|
+
},
|
|
162
167
|
},
|
|
163
168
|
},
|
|
164
169
|
en: {
|
|
@@ -173,11 +178,11 @@ const COPY = {
|
|
|
173
178
|
expandAll: 'Expand all', collapseAll: 'Collapse all',
|
|
174
179
|
modelCount: (count: number) => `${count} models`, providerCount: (count: number) => `${count} providers`,
|
|
175
180
|
jobCount: (count: number) => `${count} jobs`, runningCount: (count: number) => `${count} running`,
|
|
176
|
-
sectionNames: { integrations: 'Codex / Claude integration status', workflow: 'Crew workflow settings', flash: 'Worker / Flash', pro: 'Reviewer / Pro', dispatch: 'Model priority & dispatch', adaptive: 'Adaptive routing', runtime: 'Runtime / activation boundaries', multimodal: 'Vision & image generation', providers: 'Custom providers', jobs: 'Task status' },
|
|
181
|
+
sectionNames: { integrations: 'Codex / Claude / ZCode integration status', workflow: 'Crew workflow settings', flash: 'Worker / Flash', pro: 'Reviewer / Pro', dispatch: 'Model priority & dispatch', adaptive: 'Adaptive routing', runtime: 'Runtime / activation boundaries', multimodal: 'Vision & image generation', providers: 'Custom providers', jobs: 'Task status' },
|
|
177
182
|
openHarness: 'Open 3210 Crew Harness →',
|
|
178
183
|
harnessHint: 'Low-level providers, Harness Models, and runtime configuration',
|
|
179
184
|
hostReadiness: 'Host integration readiness', hostReadinessHint: 'Uses structured installer and runtime evidence only; missing evidence is never READY.',
|
|
180
|
-
readinessLabels: { codex_mcp: 'Codex MCP', ds_worker: 'ds-worker', ds_reviewer: 'ds-reviewer', claude_plugin: 'Claude plugin', crew_harness: 'Crew Harness', official_bridge: 'Official bridge' },
|
|
185
|
+
readinessLabels: { codex_mcp: 'Codex MCP', ds_worker: 'ds-worker', ds_reviewer: 'ds-reviewer', claude_plugin: 'Claude plugin', zcode_mcp: 'ZCode MCP', crew_harness: 'Crew Harness', official_bridge: 'Official bridge' },
|
|
181
186
|
readinessStates: { READY: 'READY', DEGRADED: 'DEGRADED', UNAVAILABLE: 'UNAVAILABLE', UNKNOWN: 'UNKNOWN' },
|
|
182
187
|
globalHint: 'Changes save instantly to ~/.config/dsh-crew/config.json; new CC / Codex sessions pick them up as defaults (override per session with /dsh-crew:config).',
|
|
183
188
|
orchestration: 'Agent orchestration',
|
|
@@ -275,11 +280,16 @@ const COPY = {
|
|
|
275
280
|
update: 'Re-run the installer: refresh registration & permissions, migrate legacy config, update HUD wiring; idempotent',
|
|
276
281
|
restore: 'Remove the integration from Claude Code: marketplace registration, plugin enablement and permission rules (settings backed up); plugin source and past jobs untouched',
|
|
277
282
|
},
|
|
278
|
-
codex: {
|
|
279
|
-
install: 'Copy ds-flash / ds-pro roles and /dsh-config, /dsh-status prompts into ~/.codex/ (MCP paths rendered for this machine, approve mode + timeout included)',
|
|
280
|
-
update: 'Re-render and overwrite role files and prompts (after path/config changes); originals backed up',
|
|
281
|
-
restore: 'Delete ds-flash / ds-pro roles and dsh prompts from ~/.codex/ (roles backed up first)',
|
|
282
|
-
},
|
|
283
|
+
codex: {
|
|
284
|
+
install: 'Copy ds-flash / ds-pro roles and /dsh-config, /dsh-status prompts into ~/.codex/ (MCP paths rendered for this machine, approve mode + timeout included)',
|
|
285
|
+
update: 'Re-render and overwrite role files and prompts (after path/config changes); originals backed up',
|
|
286
|
+
restore: 'Delete ds-flash / ds-pro roles and dsh prompts from ~/.codex/ (roles backed up first)',
|
|
287
|
+
},
|
|
288
|
+
zcode: {
|
|
289
|
+
install: 'Install the ZCode global policy, ds-worker / ds-reviewer agents, commands and source-aware MCP config under ~/.zcode/',
|
|
290
|
+
update: 'Re-render ZCode agents, commands and MCP entry; user files and unrelated MCP servers are preserved',
|
|
291
|
+
restore: 'Remove only dsh-crew-owned ZCode files and MCP entry (backups kept)',
|
|
292
|
+
},
|
|
283
293
|
},
|
|
284
294
|
},
|
|
285
295
|
};
|
|
@@ -817,6 +827,7 @@ function WorkersPanel({ ctx }: { ctx: any }) {
|
|
|
817
827
|
<div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
|
|
818
828
|
<span style={S.chip(!!status?.codex?.ready)}>Codex {status?.codex?.ready ? 'READY' : 'CHECK'}</span>
|
|
819
829
|
<span style={S.chip(!!status?.claude?.ready)}>Claude {status?.claude?.ready ? 'READY' : 'CHECK'}</span>
|
|
830
|
+
<span style={S.chip(!!status?.zcode?.ready)}>ZCode {status?.zcode?.ready ? 'READY' : 'CHECK'}</span>
|
|
820
831
|
<span style={S.chip(jobs.some((job) => job.status === 'running'))}>{jobs.filter((job) => job.status === 'running').length} running</span>
|
|
821
832
|
</div>
|
|
822
833
|
</div>
|
|
@@ -836,7 +847,7 @@ function WorkersPanel({ ctx }: { ctx: any }) {
|
|
|
836
847
|
</div>
|
|
837
848
|
|
|
838
849
|
<CollapsibleSection sectionId="integrations" title={copy.sectionNames.integrations}
|
|
839
|
-
summary={sectionSummary(status?.claude?.ready ? 'Claude READY' : 'Claude CHECK', status?.codex?.ready ? 'Codex READY' : 'Codex CHECK')}
|
|
850
|
+
summary={sectionSummary(status?.claude?.ready ? 'Claude READY' : 'Claude CHECK', `${status?.codex?.ready ? 'Codex READY' : 'Codex CHECK'} · ${status?.zcode?.ready ? 'ZCode READY' : 'ZCode CHECK'}`)}
|
|
840
851
|
expanded={!!expandedSections.integrations} onToggle={() => toggleSection('integrations')}>
|
|
841
852
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
842
853
|
<div style={S.block}>
|
|
@@ -860,6 +871,9 @@ function WorkersPanel({ ctx }: { ctx: any }) {
|
|
|
860
871
|
{integrationRow('Codex', !!status?.codex?.installed, !!status?.codex?.ready,
|
|
861
872
|
status?.codex?.ready ? null : <span title={(status?.codex?.missing ?? []).join(', ')} style={{ fontSize: 11, opacity: 0.6 }}>{(status?.codex?.missing ?? []).join(' · ')}</span>,
|
|
862
873
|
'codex', 'codex-uninstall', (copy as any).tips.codex)}
|
|
874
|
+
{integrationRow('ZCode', !!status?.zcode?.installed, !!status?.zcode?.ready,
|
|
875
|
+
status?.zcode?.ready ? null : <span title={(status?.zcode?.missing ?? []).join(', ')} style={{ fontSize: 11, opacity: 0.6 }}>{(status?.zcode?.missing ?? []).join(' · ')}</span>,
|
|
876
|
+
'zcode', 'zcode-uninstall', (copy as any).tips.zcode)}
|
|
863
877
|
</div>
|
|
864
878
|
</CollapsibleSection>
|
|
865
879
|
|
package/src/hub/index.mjs
CHANGED
|
@@ -1033,8 +1033,8 @@ export async function apply(ctx) {
|
|
|
1033
1033
|
// Cache-busted import: the installer must always run the code
|
|
1034
1034
|
// currently on disk, not whatever this process first loaded —
|
|
1035
1035
|
// a stale cached copy once re-broke user settings after a fix.
|
|
1036
|
-
const { installClaudeCode, installCodex, installHudSegment, uninstallClaudeCode, uninstallCodex } =
|
|
1037
|
-
await import(`../install/install.mjs?t=${Date.now()}`);
|
|
1036
|
+
const { installClaudeCode, installCodex, installHudSegment, uninstallClaudeCode, uninstallCodex, installZCode, uninstallZCode } =
|
|
1037
|
+
await import(`../install/install.mjs?t=${Date.now()}`);
|
|
1038
1038
|
if (target === 'claude') {
|
|
1039
1039
|
const base = await installClaudeCode({ statusline: !!statusline });
|
|
1040
1040
|
const hud = installHudSegment({});
|
|
@@ -1043,10 +1043,12 @@ export async function apply(ctx) {
|
|
|
1043
1043
|
actions: [...base.actions, ...hud.actions.map((a) => `hud: ${a}`)],
|
|
1044
1044
|
});
|
|
1045
1045
|
}
|
|
1046
|
-
if (target === 'codex') return sendJson(res, 200, installCodex({}));
|
|
1047
|
-
if (target === '
|
|
1048
|
-
if (target === '
|
|
1049
|
-
|
|
1046
|
+
if (target === 'codex') return sendJson(res, 200, installCodex({}));
|
|
1047
|
+
if (target === 'zcode') return sendJson(res, 200, installZCode({}));
|
|
1048
|
+
if (target === 'claude-uninstall') return sendJson(res, 200, uninstallClaudeCode({}));
|
|
1049
|
+
if (target === 'codex-uninstall') return sendJson(res, 200, uninstallCodex({}));
|
|
1050
|
+
if (target === 'zcode-uninstall') return sendJson(res, 200, uninstallZCode({}));
|
|
1051
|
+
return sendJson(res, 400, { ok: false, error: 'target must be claude | codex | zcode | claude-uninstall | codex-uninstall | zcode-uninstall' });
|
|
1050
1052
|
} catch (err) {
|
|
1051
1053
|
return sendJson(res, 500, { ok: false, error: err?.message ?? String(err) });
|
|
1052
1054
|
}
|
|
@@ -6,11 +6,14 @@ import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, readd
|
|
|
6
6
|
import { dirname, join, resolve, relative } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
|
-
import { normalizeModelPriority } from '../model-routing.mjs';
|
|
9
|
+
import { normalizeModelPriority } from '../model-routing.mjs';
|
|
10
|
+
import { zcodeStatus } from './zcode.mjs';
|
|
10
11
|
|
|
11
12
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
12
13
|
const MARKETPLACE_NAME = 'dsh-crew';
|
|
13
|
-
const PLUGIN_KEY = `dsh-crew@${MARKETPLACE_NAME}`;
|
|
14
|
+
const PLUGIN_KEY = `dsh-crew@${MARKETPLACE_NAME}`;
|
|
15
|
+
const POLICY_START = '<!-- DSH CREW MANAGED POLICY:START -->';
|
|
16
|
+
const POLICY_END = '<!-- DSH CREW MANAGED POLICY:END -->';
|
|
14
17
|
// dsh_worker_config is included so the session commands (/dsh-crew:config,
|
|
15
18
|
// /dsh-config) and any orchestrator policy lookup run without an extra
|
|
16
19
|
// authorization prompt.
|
|
@@ -33,6 +36,50 @@ function readText(file) {
|
|
|
33
36
|
try { return readFileSync(file, 'utf8'); } catch { return null; }
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
function managedPolicyBlock(root) {
|
|
40
|
+
const policy = readText(join(root, 'codex', 'AGENTS.md'))?.trim();
|
|
41
|
+
if (!policy) return null;
|
|
42
|
+
return `${POLICY_START}\n${policy}\n${POLICY_END}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function installGlobalCodexPolicy({ home, root }) {
|
|
46
|
+
const file = join(home, '.codex', 'AGENTS.md');
|
|
47
|
+
const block = managedPolicyBlock(root);
|
|
48
|
+
if (!block) return { ok: false, action: 'global policy template missing' };
|
|
49
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
50
|
+
const current = readText(file) ?? '';
|
|
51
|
+
const managed = new RegExp(`${POLICY_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${POLICY_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'm');
|
|
52
|
+
const template = readText(join(root, 'codex', 'AGENTS.md'))?.trim() ?? '';
|
|
53
|
+
let next;
|
|
54
|
+
if (managed.test(current)) next = current.replace(managed, block);
|
|
55
|
+
else if (current.trim() === template) next = `${block}\n`;
|
|
56
|
+
else next = `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}\n`;
|
|
57
|
+
if (next !== current) {
|
|
58
|
+
backup(file);
|
|
59
|
+
writeFileSync(file, next);
|
|
60
|
+
}
|
|
61
|
+
return { ok: true, action: `global policy: ${file}` };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function globalCodexPolicyReady({ home, root = ROOT }) {
|
|
65
|
+
const block = managedPolicyBlock(root);
|
|
66
|
+
const text = readText(join(home, '.codex', 'AGENTS.md'));
|
|
67
|
+
return !!block && typeof text === 'string' && text.includes(block);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function uninstallGlobalCodexPolicy({ home }) {
|
|
71
|
+
const file = join(home, '.codex', 'AGENTS.md');
|
|
72
|
+
const current = readText(file);
|
|
73
|
+
if (typeof current !== 'string') return null;
|
|
74
|
+
const managed = new RegExp(`(?:\\r?\\n){0,2}${POLICY_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${POLICY_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:\\r?\\n)?`, 'm');
|
|
75
|
+
if (!managed.test(current)) return null;
|
|
76
|
+
const next = current.replace(managed, '').trimEnd();
|
|
77
|
+
backup(file);
|
|
78
|
+
if (next.trim()) writeFileSync(file, `${next}\n`);
|
|
79
|
+
else rmSync(file);
|
|
80
|
+
return `codex global policy: removed managed block`;
|
|
81
|
+
}
|
|
82
|
+
|
|
36
83
|
function tomlSection(text, name) {
|
|
37
84
|
if (typeof text !== 'string') return null;
|
|
38
85
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -256,7 +303,7 @@ export function writeGlobalConfig(patch) {
|
|
|
256
303
|
}
|
|
257
304
|
|
|
258
305
|
/** What is currently installed where — drives the settings-page buttons. */
|
|
259
|
-
export function installStatus({ home = homedir() } = {}) {
|
|
306
|
+
export function installStatus({ home = homedir(), root } = {}) {
|
|
260
307
|
const settings = readJson(join(home, '.claude', 'settings.json'), {});
|
|
261
308
|
const enabled = settings.enabledPlugins;
|
|
262
309
|
const claudeInstalled = !!(enabled && !Array.isArray(enabled) && enabled[PLUGIN_KEY]);
|
|
@@ -282,6 +329,7 @@ export function installStatus({ home = homedir() } = {}) {
|
|
|
282
329
|
status_prompt: !!readText(join(codexRoot, 'prompts', 'dsh-status.md'))?.trim(),
|
|
283
330
|
mcp: !!mcpTarget,
|
|
284
331
|
target_alignment: !!workerTarget && workerTarget === reviewerTarget && workerTarget === mcpTarget,
|
|
332
|
+
global_policy: globalCodexPolicyReady({ home }),
|
|
285
333
|
};
|
|
286
334
|
const codexInstalled = Object.values(components).some(Boolean)
|
|
287
335
|
|| existsSync(join(codexRoot, 'agents', 'ds-flash.toml'))
|
|
@@ -296,10 +344,11 @@ export function installStatus({ home = homedir() } = {}) {
|
|
|
296
344
|
missing: claudeMissing,
|
|
297
345
|
},
|
|
298
346
|
codex: { installed: codexInstalled, ready: missing.length === 0, components, missing },
|
|
347
|
+
zcode: zcodeStatus({ home, ...(root ? { root } : {}) }),
|
|
299
348
|
};
|
|
300
349
|
}
|
|
301
350
|
|
|
302
|
-
export function uninstallCodex({ home = homedir() } = {}) {
|
|
351
|
+
export function uninstallCodex({ home = homedir() } = {}) {
|
|
303
352
|
const actions = [];
|
|
304
353
|
// Both the v0.2 roles (ds-worker / ds-reviewer) and the deprecated v0.1
|
|
305
354
|
// aliases (ds-flash / ds-pro) are dsh-crew managed; uninstall removes only
|
|
@@ -308,10 +357,12 @@ export function uninstallCodex({ home = homedir() } = {}) {
|
|
|
308
357
|
const p = join(home, '.codex', 'agents', f);
|
|
309
358
|
if (existsSync(p)) { backup(p); rmSync(p); actions.push(`removed: ${p} (backup kept)`); }
|
|
310
359
|
}
|
|
311
|
-
for (const f of ['dsh-config.md', 'dsh-status.md']) {
|
|
360
|
+
for (const f of ['dsh-config.md', 'dsh-status.md']) {
|
|
312
361
|
const p = join(home, '.codex', 'prompts', f);
|
|
313
362
|
if (existsSync(p)) { rmSync(p); actions.push(`removed: ${p}`); }
|
|
314
|
-
}
|
|
363
|
+
}
|
|
364
|
+
const policyAction = uninstallGlobalCodexPolicy({ home });
|
|
365
|
+
if (policyAction) actions.push(policyAction);
|
|
315
366
|
// Remove only the dsh-crew entry from [mcp_servers], keeping any other
|
|
316
367
|
// MCP servers the user configured.
|
|
317
368
|
const configFile = join(home, '.codex', 'config.toml');
|
|
@@ -428,7 +479,7 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
|
|
|
428
479
|
return { ok: true, actions };
|
|
429
480
|
}
|
|
430
481
|
|
|
431
|
-
export function installCodex({ home = homedir(), scope, root = ROOT } = {}) {
|
|
482
|
+
export function installCodex({ home = homedir(), scope, root = ROOT } = {}) {
|
|
432
483
|
const actions = [];
|
|
433
484
|
const agentsDir = scope === 'project' ? join(process.cwd(), '.codex', 'agents') : join(home, '.codex', 'agents');
|
|
434
485
|
mkdirSync(agentsDir, { recursive: true });
|
|
@@ -455,10 +506,13 @@ export function installCodex({ home = homedir(), scope, root = ROOT } = {}) {
|
|
|
455
506
|
writeFileSync(join(promptsDir, f), readFileSync(join(promptsSrc, f), 'utf8'));
|
|
456
507
|
actions.push(`prompt: ${join(promptsDir, f)}`);
|
|
457
508
|
}
|
|
458
|
-
if (scope !== 'project') {
|
|
459
|
-
const act = writeGlobalCodexMcpServer(home, renderedPath);
|
|
460
|
-
actions.push(...act);
|
|
461
|
-
|
|
509
|
+
if (scope !== 'project') {
|
|
510
|
+
const act = writeGlobalCodexMcpServer(home, renderedPath);
|
|
511
|
+
actions.push(...act);
|
|
512
|
+
const policy = installGlobalCodexPolicy({ home, root });
|
|
513
|
+
if (!policy.ok) return { ok: false, actions: [...actions, policy.action] };
|
|
514
|
+
actions.push(policy.action);
|
|
515
|
+
}
|
|
462
516
|
return { ok: true, actions };
|
|
463
517
|
}
|
|
464
518
|
|
package/src/install/install.mjs
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
// This module keeps all installer exports while replacing only global config
|
|
5
5
|
// read/write semantics with schema-v3 canonical authority.
|
|
6
6
|
|
|
7
|
-
export * from './install-legacy.mjs';
|
|
7
|
+
export * from './install-legacy.mjs';
|
|
8
|
+
export * from './windows-startup.mjs';
|
|
9
|
+
export * from './zcode.mjs';
|
|
8
10
|
|
|
9
11
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
12
|
import { dirname, join } from 'node:path';
|
|
@@ -549,14 +549,30 @@ async function activateRelease({ home, releaseDir, manifest, log, installer }) {
|
|
|
549
549
|
}
|
|
550
550
|
log(`✓ Harness plugin registered (dedicated dsh-crew profile → ${releaseDir})`);
|
|
551
551
|
|
|
552
|
-
const codex = installer.installCodex({ home, root: releaseDir });
|
|
552
|
+
const codex = installer.installCodex({ home, root: releaseDir });
|
|
553
553
|
if (codex.ok === false) {
|
|
554
554
|
log(`✗ Codex Desktop integration failed: ${(codex.actions ?? []).join('; ')}`);
|
|
555
555
|
return false;
|
|
556
556
|
}
|
|
557
|
-
log('✓ Codex Desktop integration');
|
|
558
|
-
|
|
559
|
-
|
|
557
|
+
log('✓ Codex Desktop integration');
|
|
558
|
+
|
|
559
|
+
if (installer.installZCode) {
|
|
560
|
+
const zcode = installer.installZCode({ home, root: releaseDir });
|
|
561
|
+
if (zcode.ok === false) {
|
|
562
|
+
log(`✗ ZCode integration failed (${zcode.code ?? 'unknown'})`);
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
log('✓ ZCode integration');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const startup = installer.installWindowsStartup?.({ home, root: releaseDir });
|
|
569
|
+
if (startup?.ok === false) {
|
|
570
|
+
log(`✗ Windows login startup failed (${startup.code ?? 'unknown'})`);
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
if (startup?.supported) log('✓ Windows login startup');
|
|
574
|
+
|
|
575
|
+
const claude = await installer.installClaudeCode({ home, root: releaseDir });
|
|
560
576
|
if (claude.ok === false) {
|
|
561
577
|
log(`✗ Claude Code integration failed`);
|
|
562
578
|
return false;
|
|
@@ -944,11 +960,17 @@ export function npxStatus({
|
|
|
944
960
|
} catch { dshPlugin = 'unknown'; }
|
|
945
961
|
}
|
|
946
962
|
|
|
947
|
-
const st = installer.installStatus
|
|
948
|
-
|
|
949
|
-
|
|
963
|
+
const st = installer.installStatus
|
|
964
|
+
? installer.installStatus({ home, root: pointer?.path ?? runningPackageRoot() })
|
|
965
|
+
: realInstaller.installStatus({ home, root: pointer?.path ?? runningPackageRoot() });
|
|
966
|
+
const codex = st?.codex?.installed ? 'installed' : 'not installed';
|
|
967
|
+
const zcode = st?.zcode?.installed ? 'installed' : 'not installed';
|
|
968
|
+
const claude = st?.claude?.installed ? 'installed' : 'not installed';
|
|
950
969
|
const official = officialWebIntegrationStatus({ home, releaseDir: pointer?.path });
|
|
951
|
-
const officialWeb = !official.enabled ? 'disabled' : official.healthy ? 'installed' : 'needs repair';
|
|
970
|
+
const officialWeb = !official.enabled ? 'disabled' : official.healthy ? 'installed' : 'needs repair';
|
|
971
|
+
const startupState = installer.windowsStartupStatus?.({ home });
|
|
972
|
+
const windowsStartup = !startupState?.supported ? 'not supported'
|
|
973
|
+
: startupState.ready ? 'installed' : startupState.installed ? 'needs repair' : 'not installed';
|
|
952
974
|
|
|
953
975
|
log(`DSH Crew launcher/candidate: ${candidateVersion ?? 'unknown'}`);
|
|
954
976
|
log(`Installed DSH Crew payload: ${installedLine}`);
|
|
@@ -964,8 +986,10 @@ export function npxStatus({
|
|
|
964
986
|
}
|
|
965
987
|
log(`DSH plugin: ${dshPlugin} (dedicated dsh-crew profile on 3210)`);
|
|
966
988
|
log(`Official 3080 UI bridge: ${officialWeb}`);
|
|
967
|
-
log(`Codex Desktop integration: ${codex}`);
|
|
968
|
-
log(`
|
|
989
|
+
log(`Codex Desktop integration: ${codex}`);
|
|
990
|
+
log(`ZCode integration: ${zcode}`);
|
|
991
|
+
log(`Claude Code integration: ${claude}`);
|
|
992
|
+
log(`Windows login startup: ${windowsStartup}`);
|
|
969
993
|
|
|
970
994
|
return {
|
|
971
995
|
ok: true,
|
|
@@ -974,9 +998,11 @@ export function npxStatus({
|
|
|
974
998
|
installedPath: pointer?.path ?? null,
|
|
975
999
|
dshPlugin,
|
|
976
1000
|
officialWeb,
|
|
977
|
-
codex,
|
|
978
|
-
|
|
979
|
-
|
|
1001
|
+
codex,
|
|
1002
|
+
zcode,
|
|
1003
|
+
claude,
|
|
1004
|
+
windowsStartup,
|
|
1005
|
+
};
|
|
980
1006
|
}
|
|
981
1007
|
|
|
982
1008
|
export async function npxUninstall({
|
|
@@ -992,13 +1018,23 @@ export async function npxUninstall({
|
|
|
992
1018
|
const pointer = readCurrentPointer({ home });
|
|
993
1019
|
const name = pointer?.name ?? readManifest(runningPackageRoot())?.name;
|
|
994
1020
|
|
|
995
|
-
const cx = installer.uninstallCodex({ home });
|
|
1021
|
+
const cx = installer.uninstallCodex({ home });
|
|
996
1022
|
if (cx.ok !== false) log('✓ Codex Desktop integration removed');
|
|
997
|
-
else fail('Codex Desktop integration removal failed');
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1023
|
+
else fail('Codex Desktop integration removal failed');
|
|
1024
|
+
|
|
1025
|
+
if (installer.uninstallZCode) {
|
|
1026
|
+
const zc = installer.uninstallZCode({ home });
|
|
1027
|
+
if (zc.ok !== false) log('✓ ZCode integration removed');
|
|
1028
|
+
else fail('ZCode integration removal failed');
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const cl = installer.uninstallClaudeCode ? await installer.uninstallClaudeCode({ home }) : realInstaller.uninstallClaudeCode({ home });
|
|
1032
|
+
if (cl.ok !== false) log('✓ Claude Code integration removed');
|
|
1033
|
+
else fail('Claude Code integration removal failed');
|
|
1034
|
+
|
|
1035
|
+
const startup = installer.uninstallWindowsStartup?.({ home });
|
|
1036
|
+
if (startup?.ok === false) fail('Windows login startup removal failed');
|
|
1037
|
+
else if (startup?.supported) log('✓ Windows login startup removed');
|
|
1002
1038
|
|
|
1003
1039
|
const official = removeOfficialWebIntegration({ home, preserveIntent: !purge, remember: !purge });
|
|
1004
1040
|
if (!official.ok) fail(`official 3080 bridge removal failed (${official.code ?? 'unknown'})`);
|