@ran-sh/dsh-crew 1.8.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.
- package/.claude-plugin/marketplace.json +16 -16
- package/.claude-plugin/plugin.json +14 -14
- package/LICENSE +21 -21
- package/README.de.md +359 -359
- package/README.es.md +359 -359
- package/README.fr.md +359 -359
- package/README.hi.md +359 -359
- package/README.id.md +359 -359
- package/README.ja.md +359 -359
- package/README.ko.md +359 -359
- package/README.md +126 -126
- package/README.pt.md +359 -359
- package/README.ru.md +359 -359
- package/README.th.md +359 -359
- package/README.tr.md +359 -359
- package/README.vi.md +359 -359
- package/README.zh-TW.md +359 -359
- package/README.zh.md +116 -116
- package/agents/ds-flash.md +24 -24
- package/agents/ds-pro.md +24 -24
- package/agents/ds-reviewer.md +23 -23
- package/agents/ds-worker.md +23 -23
- package/bin/dsh-crew.mjs +16 -16
- package/codex/agents/ds-flash.toml +34 -34
- package/codex/agents/ds-pro.toml +34 -34
- package/codex/agents/ds-reviewer.toml +32 -32
- package/codex/agents/ds-worker.toml +32 -32
- package/codex/prompts/dsh-config.md +19 -19
- package/codex/prompts/dsh-status.md +5 -5
- package/commands/config.md +23 -23
- package/commands/off.md +5 -5
- package/commands/on.md +5 -5
- package/commands/status.md +9 -9
- package/cordis.patch.yml +4 -4
- package/docs/gpt-relay-extension.md +103 -103
- package/docs/installation.md +138 -138
- package/docs/job-contracts.md +103 -103
- package/docs/readiness-matrix.md +85 -85
- package/docs/ui-surfaces.md +107 -107
- package/official-web-bridge/cordis.patch.yml +4 -4
- package/official-web-bridge/entry.mjs +1 -1
- package/official-web-bridge/overlay-entry.mjs +59 -59
- package/official-web-bridge/package.json +25 -25
- package/package.json +1 -1
- package/scripts/build-client.mjs +49 -49
- package/scripts/live-crew-smoke.mjs +39 -39
- package/scripts/live-policy-matrix.mjs +177 -177
- package/scripts/policy-probe.mjs +101 -101
- package/scripts/remove-legacy-official-bridge.ps1 +89 -89
- package/scripts/setup.mjs +393 -393
- package/scripts/smoke-real.mjs +110 -110
- package/scripts/smoke.mjs +78 -78
- package/scripts/verify-crew-ui-polish.mjs +145 -145
- package/scripts/verify-history-ui.mjs +97 -97
- package/scripts/verify-installer-fix.mjs +26 -26
- package/scripts/verify-npm-install.mjs +311 -311
- package/scripts/verify-official-bridge-e2e.mjs +192 -192
- package/src/adaptive-routing.mjs +260 -260
- package/src/client/activation-summary.tsx +64 -64
- package/src/client/collapsible-sections.mjs +55 -55
- package/src/client/history-panel.tsx +108 -108
- package/src/client/host-readiness.mjs +71 -71
- package/src/client/index.tsx +1711 -1711
- package/src/client/model-callability-view.mjs +17 -17
- package/src/client/panel-chrome.tsx +40 -40
- package/src/client/quick-entry.tsx +10 -10
- package/src/client/quick-panel.tsx +275 -275
- package/src/client/readiness-envelope.mjs +34 -34
- package/src/client/surface-detection.mjs +43 -43
- package/src/config-readiness.mjs +226 -226
- package/src/credential-reference.mjs +38 -38
- package/src/delivery.mjs +205 -205
- package/src/dsh-cli-runtime.mjs +1021 -1021
- package/src/dsh-cohort.mjs +20 -20
- package/src/extension-contract.mjs +104 -104
- package/src/failure-classification.mjs +201 -201
- package/src/history/admission-gate.mjs +67 -67
- package/src/history/archive-store.mjs +272 -272
- package/src/history/cleanup-plan.mjs +89 -89
- package/src/history/http.mjs +32 -32
- package/src/history/operation.mjs +86 -86
- package/src/history/runner-detach.mjs +34 -34
- package/src/history/runner.mjs +52 -52
- package/src/history/runtime.mjs +36 -36
- package/src/history/service.mjs +177 -177
- package/src/history/state.mjs +27 -27
- package/src/hub/entry.mjs +104 -104
- package/src/hub/index.mjs +2694 -2694
- package/src/hub-client.mjs +154 -154
- package/src/hub-compatibility.mjs +42 -42
- package/src/i18n.mjs +19 -19
- package/src/information-flow.mjs +67 -67
- package/src/install/cli.mjs +28 -28
- package/src/install/install-legacy.mjs +711 -711
- package/src/install/install.mjs +483 -483
- package/src/install/npx-lifecycle.mjs +3456 -3456
- package/src/install/official-frontend-assets.mjs +78 -78
- package/src/install/official-web.mjs +95 -95
- package/src/install/payload-content.mjs +88 -88
- package/src/install/windows-startup.mjs +236 -236
- package/src/install/windows-supervisor-adapter.mjs +443 -443
- package/src/install/windows-supervisor-lifecycle.mjs +782 -782
- package/src/install/zcode.mjs +397 -397
- package/src/job-contracts.mjs +255 -255
- package/src/local-request-guard.mjs +60 -60
- package/src/mcp-runtime.mjs +340 -332
- package/src/model-callability-contract.mjs +79 -79
- package/src/model-catalog.mjs +180 -180
- package/src/model-routing.mjs +586 -586
- package/src/model-schedule.mjs +207 -207
- package/src/official-web-bridge.mjs +447 -447
- package/src/policy.mjs +235 -235
- package/src/provider-delete-adapters.mjs +1934 -1934
- package/src/provider-health.mjs +130 -130
- package/src/provider-inventory.mjs +182 -182
- package/src/provider-layer-migration-adapters.mjs +759 -759
- package/src/provider-layer-migration.mjs +198 -198
- package/src/provider-lifecycle-state.mjs +103 -103
- package/src/provider-lifecycle.mjs +252 -252
- package/src/provider-profile-store.mjs +390 -390
- package/src/provider-settings-store.mjs +633 -633
- package/src/provider-store-lock.mjs +67 -67
- package/src/readiness-matrix.mjs +181 -181
- package/src/removable-waiter.mjs +29 -29
- package/src/role-profiles.mjs +107 -107
- package/src/runtime-controls.mjs +84 -84
- package/src/runtime-identity-contract.mjs +34 -34
- package/src/runtime-identity.mjs +235 -235
- package/src/runtime-readiness-snapshot.mjs +285 -285
- package/src/server.mjs +581 -581
- package/src/session-origins.mjs +60 -60
- package/src/standalone-sdk.mjs +23 -23
- package/src/status-shard.mjs +63 -63
- package/src/structured-error-code.mjs +38 -38
- package/src/supervisor/restart-request.mjs +256 -256
- package/src/workflow-runtime.mjs +739 -739
- package/src/workflow.mjs +155 -155
- package/src/workspace-audit.mjs +231 -231
- package/src/workspace-context.mjs +146 -146
- package/src/workspace-isolation.mjs +455 -401
- package/src/workspace-readiness.mjs +32 -32
- package/statusline/statusline.sh +14 -14
- package/statusline/worker-segment.sh +35 -35
- package/windows/start-dsh-crew.cmd +57 -57
- package/windows/start-dsh-crew.ps1 +1302 -1302
- package/windows/supervisor-control.ps1 +467 -467
- package/worker.cordis.yml +67 -67
- package/zcode/agents/ds-reviewer.md +31 -31
- package/zcode/agents/ds-worker.md +31 -31
- package/zcode/commands/dsh-config.md +17 -17
- package/zcode/commands/dsh-status.md +5 -5
package/scripts/policy-probe.mjs
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
// Functional probe of the real MCP stdio server (src/server.mjs), no DSH
|
|
2
|
-
// instance and no DeepSeek credentials needed: policy rejections must happen
|
|
3
|
-
// before any worker runtime is touched.
|
|
4
|
-
// Run: node scripts/policy-probe.mjs
|
|
5
|
-
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
|
|
8
|
-
const child = spawn(process.execPath, ['src/server.mjs'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
9
|
-
let buffer = '';
|
|
10
|
-
const pending = new Map();
|
|
11
|
-
let nextId = 1;
|
|
12
|
-
|
|
13
|
-
child.stdout.on('data', (d) => {
|
|
14
|
-
buffer += d.toString();
|
|
15
|
-
let idx;
|
|
16
|
-
while ((idx = buffer.indexOf('\n')) >= 0) {
|
|
17
|
-
const line = buffer.slice(0, idx);
|
|
18
|
-
buffer = buffer.slice(idx + 1);
|
|
19
|
-
if (!line.trim()) continue;
|
|
20
|
-
let msg;
|
|
21
|
-
try { msg = JSON.parse(line); } catch { continue; }
|
|
22
|
-
if (msg.id !== undefined && pending.has(msg.id)) {
|
|
23
|
-
pending.get(msg.id)(msg);
|
|
24
|
-
pending.delete(msg.id);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
function call(method, params) {
|
|
30
|
-
return new Promise((resolve) => {
|
|
31
|
-
const id = nextId++;
|
|
32
|
-
pending.set(id, resolve);
|
|
33
|
-
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const results = [];
|
|
38
|
-
function check(name, cond, detail) {
|
|
39
|
-
results.push({ name, ok: !!cond, detail });
|
|
40
|
-
console.log(`${cond ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
await call('initialize', { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'probe', version: '1' } });
|
|
44
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
45
|
-
|
|
46
|
-
// 1. Read config: must expose the new effective policy fields.
|
|
47
|
-
const cfg = await call('tools/call', { name: 'dsh_worker_config', arguments: {} });
|
|
48
|
-
const cfgText = cfg.result?.content?.[0]?.text;
|
|
49
|
-
let cfgJson = null;
|
|
50
|
-
try { cfgJson = JSON.parse(cfgText); } catch {}
|
|
51
|
-
check('config returns effective policy', !!cfgJson && typeof cfgJson.flash_state === 'string' && typeof cfgJson.pro_state === 'string' && typeof cfgJson.routing_guidance === 'string',
|
|
52
|
-
cfgJson ? `flash=${cfgJson.flash_state} pro=${cfgJson.pro_state} mode=${cfgJson.collaboration_mode}` : cfgText?.slice(0, 120));
|
|
53
|
-
check('config hides API keys', cfgText !== undefined && !/api_key|sk-/.test(cfgText));
|
|
54
|
-
check('config reports effective_policy + roles + legacy_source', !!cfgJson
|
|
55
|
-
&& typeof cfgJson.effective_policy === 'string' && cfgJson.effective_policy.includes('mode=')
|
|
56
|
-
&& Array.isArray(cfgJson.flash_roles) && cfgJson.flash_roles.length > 0
|
|
57
|
-
&& Array.isArray(cfgJson.pro_roles) && cfgJson.pro_roles.length > 0
|
|
58
|
-
&& typeof cfgJson.legacy_source === 'string',
|
|
59
|
-
cfgJson ? cfgJson.effective_policy : undefined);
|
|
60
|
-
check('config reflects main_agent_mode', !!cfgJson && ['direct-allowed', 'coordinator-first', 'dispatcher-only'].includes(cfgJson.main_agent_mode), cfgJson?.main_agent_mode);
|
|
61
|
-
|
|
62
|
-
// 2. Session clamp: flash-only + explicit pro must be refused with TIER_DISABLED.
|
|
63
|
-
const clamp = await call('tools/call', { name: 'dsh_worker_config', arguments: { tier_policy: 'flash-only' } });
|
|
64
|
-
check('session tier_policy clamp set', clamp.result !== undefined && !clamp.result.isError);
|
|
65
|
-
|
|
66
|
-
const refusePro = await call('tools/call', { name: 'dsh_run_worker', arguments: { task: 'probe', tier: 'pro' } });
|
|
67
|
-
const refuseText = refusePro.result?.content?.[0]?.text ?? '';
|
|
68
|
-
check('pro refused under flash-only', /TIER_DISABLED/.test(refuseText), refuseText.slice(0, 120));
|
|
69
|
-
|
|
70
|
-
// 3. subagents master switch via session enabled=false.
|
|
71
|
-
const off = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true, enabled: false } });
|
|
72
|
-
check('session enabled=false set', off.result !== undefined && !off.result.isError);
|
|
73
|
-
const refuseAll = await call('tools/call', { name: 'dsh_run_worker', arguments: { task: 'probe' } });
|
|
74
|
-
const refuseAllText = refuseAll.result?.content?.[0]?.text ?? '';
|
|
75
|
-
check('dispatch refused when disabled', /SUBAGENTS_DISABLED/.test(refuseAllText), refuseAllText.slice(0, 120));
|
|
76
|
-
|
|
77
|
-
// 4. dsh_spawn_worker shares the same policy.
|
|
78
|
-
const refuseSpawn = await call('tools/call', { name: 'dsh_spawn_worker', arguments: { task: 'probe' } });
|
|
79
|
-
const refuseSpawnText = refuseSpawn.result?.content?.[0]?.text ?? '';
|
|
80
|
-
check('spawn refused when disabled', /SUBAGENTS_DISABLED/.test(refuseSpawnText));
|
|
81
|
-
|
|
82
|
-
// 5. Manual semantics: flash manual + pro auto, no explicit tier → pro is chosen, never flash.
|
|
83
|
-
const manual = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true, tier_policy: 'auto' } });
|
|
84
|
-
// global config is balanced here; exercise manual via session state override:
|
|
85
|
-
const manualSet = await call('tools/call', { name: 'dsh_worker_config', arguments: { collaboration_mode: 'custom', flash_state: 'manual', pro_state: 'auto', default_tier: 'flash' } });
|
|
86
|
-
check('manual/custom session set', manualSet.result !== undefined && !manualSet.result.isError);
|
|
87
|
-
const manualCfg = await call('tools/call', { name: 'dsh_worker_config', arguments: {} });
|
|
88
|
-
const manualCfgJson = JSON.parse(manualCfg.result.content[0].text);
|
|
89
|
-
check('manual tier never auto-default (effective_default_tier=pro)',
|
|
90
|
-
manualCfgJson.effective_default_tier === 'pro', `got ${manualCfgJson.effective_default_tier}`);
|
|
91
|
-
check('effective states reported', manualCfgJson.flash_state === 'manual' && manualCfgJson.pro_state === 'auto');
|
|
92
|
-
|
|
93
|
-
// 6. Reset back to global defaults.
|
|
94
|
-
const reset = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true } });
|
|
95
|
-
check('reset restores defaults', reset.result !== undefined && !reset.result.isError);
|
|
96
|
-
|
|
97
|
-
child.kill();
|
|
98
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
99
|
-
const failed = results.filter((r) => !r.ok);
|
|
100
|
-
console.log(`\n${results.length - failed.length}/${results.length} checks passed`);
|
|
101
|
-
process.exit(failed.length > 0 ? 1 : 0);
|
|
1
|
+
// Functional probe of the real MCP stdio server (src/server.mjs), no DSH
|
|
2
|
+
// instance and no DeepSeek credentials needed: policy rejections must happen
|
|
3
|
+
// before any worker runtime is touched.
|
|
4
|
+
// Run: node scripts/policy-probe.mjs
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const child = spawn(process.execPath, ['src/server.mjs'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
9
|
+
let buffer = '';
|
|
10
|
+
const pending = new Map();
|
|
11
|
+
let nextId = 1;
|
|
12
|
+
|
|
13
|
+
child.stdout.on('data', (d) => {
|
|
14
|
+
buffer += d.toString();
|
|
15
|
+
let idx;
|
|
16
|
+
while ((idx = buffer.indexOf('\n')) >= 0) {
|
|
17
|
+
const line = buffer.slice(0, idx);
|
|
18
|
+
buffer = buffer.slice(idx + 1);
|
|
19
|
+
if (!line.trim()) continue;
|
|
20
|
+
let msg;
|
|
21
|
+
try { msg = JSON.parse(line); } catch { continue; }
|
|
22
|
+
if (msg.id !== undefined && pending.has(msg.id)) {
|
|
23
|
+
pending.get(msg.id)(msg);
|
|
24
|
+
pending.delete(msg.id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function call(method, params) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const id = nextId++;
|
|
32
|
+
pending.set(id, resolve);
|
|
33
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const results = [];
|
|
38
|
+
function check(name, cond, detail) {
|
|
39
|
+
results.push({ name, ok: !!cond, detail });
|
|
40
|
+
console.log(`${cond ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await call('initialize', { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'probe', version: '1' } });
|
|
44
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
45
|
+
|
|
46
|
+
// 1. Read config: must expose the new effective policy fields.
|
|
47
|
+
const cfg = await call('tools/call', { name: 'dsh_worker_config', arguments: {} });
|
|
48
|
+
const cfgText = cfg.result?.content?.[0]?.text;
|
|
49
|
+
let cfgJson = null;
|
|
50
|
+
try { cfgJson = JSON.parse(cfgText); } catch {}
|
|
51
|
+
check('config returns effective policy', !!cfgJson && typeof cfgJson.flash_state === 'string' && typeof cfgJson.pro_state === 'string' && typeof cfgJson.routing_guidance === 'string',
|
|
52
|
+
cfgJson ? `flash=${cfgJson.flash_state} pro=${cfgJson.pro_state} mode=${cfgJson.collaboration_mode}` : cfgText?.slice(0, 120));
|
|
53
|
+
check('config hides API keys', cfgText !== undefined && !/api_key|sk-/.test(cfgText));
|
|
54
|
+
check('config reports effective_policy + roles + legacy_source', !!cfgJson
|
|
55
|
+
&& typeof cfgJson.effective_policy === 'string' && cfgJson.effective_policy.includes('mode=')
|
|
56
|
+
&& Array.isArray(cfgJson.flash_roles) && cfgJson.flash_roles.length > 0
|
|
57
|
+
&& Array.isArray(cfgJson.pro_roles) && cfgJson.pro_roles.length > 0
|
|
58
|
+
&& typeof cfgJson.legacy_source === 'string',
|
|
59
|
+
cfgJson ? cfgJson.effective_policy : undefined);
|
|
60
|
+
check('config reflects main_agent_mode', !!cfgJson && ['direct-allowed', 'coordinator-first', 'dispatcher-only'].includes(cfgJson.main_agent_mode), cfgJson?.main_agent_mode);
|
|
61
|
+
|
|
62
|
+
// 2. Session clamp: flash-only + explicit pro must be refused with TIER_DISABLED.
|
|
63
|
+
const clamp = await call('tools/call', { name: 'dsh_worker_config', arguments: { tier_policy: 'flash-only' } });
|
|
64
|
+
check('session tier_policy clamp set', clamp.result !== undefined && !clamp.result.isError);
|
|
65
|
+
|
|
66
|
+
const refusePro = await call('tools/call', { name: 'dsh_run_worker', arguments: { task: 'probe', tier: 'pro' } });
|
|
67
|
+
const refuseText = refusePro.result?.content?.[0]?.text ?? '';
|
|
68
|
+
check('pro refused under flash-only', /TIER_DISABLED/.test(refuseText), refuseText.slice(0, 120));
|
|
69
|
+
|
|
70
|
+
// 3. subagents master switch via session enabled=false.
|
|
71
|
+
const off = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true, enabled: false } });
|
|
72
|
+
check('session enabled=false set', off.result !== undefined && !off.result.isError);
|
|
73
|
+
const refuseAll = await call('tools/call', { name: 'dsh_run_worker', arguments: { task: 'probe' } });
|
|
74
|
+
const refuseAllText = refuseAll.result?.content?.[0]?.text ?? '';
|
|
75
|
+
check('dispatch refused when disabled', /SUBAGENTS_DISABLED/.test(refuseAllText), refuseAllText.slice(0, 120));
|
|
76
|
+
|
|
77
|
+
// 4. dsh_spawn_worker shares the same policy.
|
|
78
|
+
const refuseSpawn = await call('tools/call', { name: 'dsh_spawn_worker', arguments: { task: 'probe' } });
|
|
79
|
+
const refuseSpawnText = refuseSpawn.result?.content?.[0]?.text ?? '';
|
|
80
|
+
check('spawn refused when disabled', /SUBAGENTS_DISABLED/.test(refuseSpawnText));
|
|
81
|
+
|
|
82
|
+
// 5. Manual semantics: flash manual + pro auto, no explicit tier → pro is chosen, never flash.
|
|
83
|
+
const manual = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true, tier_policy: 'auto' } });
|
|
84
|
+
// global config is balanced here; exercise manual via session state override:
|
|
85
|
+
const manualSet = await call('tools/call', { name: 'dsh_worker_config', arguments: { collaboration_mode: 'custom', flash_state: 'manual', pro_state: 'auto', default_tier: 'flash' } });
|
|
86
|
+
check('manual/custom session set', manualSet.result !== undefined && !manualSet.result.isError);
|
|
87
|
+
const manualCfg = await call('tools/call', { name: 'dsh_worker_config', arguments: {} });
|
|
88
|
+
const manualCfgJson = JSON.parse(manualCfg.result.content[0].text);
|
|
89
|
+
check('manual tier never auto-default (effective_default_tier=pro)',
|
|
90
|
+
manualCfgJson.effective_default_tier === 'pro', `got ${manualCfgJson.effective_default_tier}`);
|
|
91
|
+
check('effective states reported', manualCfgJson.flash_state === 'manual' && manualCfgJson.pro_state === 'auto');
|
|
92
|
+
|
|
93
|
+
// 6. Reset back to global defaults.
|
|
94
|
+
const reset = await call('tools/call', { name: 'dsh_worker_config', arguments: { reset: true } });
|
|
95
|
+
check('reset restores defaults', reset.result !== undefined && !reset.result.isError);
|
|
96
|
+
|
|
97
|
+
child.kill();
|
|
98
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
99
|
+
const failed = results.filter((r) => !r.ok);
|
|
100
|
+
console.log(`\n${results.length - failed.length}/${results.length} checks passed`);
|
|
101
|
+
process.exit(failed.length > 0 ? 1 : 0);
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
# One-shot operator maintenance: remove the legacy official-web bridge state
|
|
2
|
-
# left behind BEFORE the read-only policy. This script is NEVER invoked by
|
|
3
|
-
# the dsh-crew installer; the product code never writes to ~/.dsh.
|
|
4
|
-
#
|
|
5
|
-
# Preconditions (fail closed unless ALL hold):
|
|
6
|
-
# 1. Official 3080 is stopped and no longer listening.
|
|
7
|
-
# 2. ~/.dsh/profiles/web/package.json dependencies["@ran-sh/dsh-crew-web-bridge"] exists.
|
|
8
|
-
# 3. dsh.profile.bundles contains "@ran-sh/dsh-crew-web-bridge".
|
|
9
|
-
# 4. node_modules/@ran-sh/dsh-crew-web-bridge is a symlink/junction whose
|
|
10
|
-
# target is an old Crew release official-web-bridge dir.
|
|
11
|
-
# Usage:
|
|
12
|
-
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts/remove-legacy-official-bridge.ps1 [-WhatIf]
|
|
13
|
-
|
|
14
|
-
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
15
|
-
param()
|
|
16
|
-
|
|
17
|
-
Set-StrictMode -Version Latest
|
|
18
|
-
$ErrorActionPreference = 'Stop'
|
|
19
|
-
|
|
20
|
-
$BridgePackage = '@ran-sh/dsh-crew-web-bridge'
|
|
21
|
-
$officialManifest = Join-Path $env:USERPROFILE '.dsh\profiles\web\package.json'
|
|
22
|
-
$bridgeLink = Join-Path $env:USERPROFILE '.dsh\profiles\web\node_modules\@ran-sh\dsh-crew-web-bridge'
|
|
23
|
-
$crewState = Join-Path $env:USERPROFILE '.config\dsh-crew\official-web.json'
|
|
24
|
-
|
|
25
|
-
function Fail($message) {
|
|
26
|
-
Write-Host "FAIL-CLOSED: $message" -ForegroundColor Red
|
|
27
|
-
exit 2
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
# 1. Official 3080 must be stopped.
|
|
31
|
-
try {
|
|
32
|
-
$listeners = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpListeners()
|
|
33
|
-
if (@($listeners | Where-Object Port -eq 3080).Count -gt 0) {
|
|
34
|
-
Fail 'official 3080 is still listening; stop it before cleanup.'
|
|
35
|
-
}
|
|
36
|
-
} catch {
|
|
37
|
-
Fail "could not enumerate TCP listeners: $($_.Exception.Message)"
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
# 2. Read + back up the official manifest (backup goes OUTSIDE .dsh).
|
|
41
|
-
if (-not (Test-Path -LiteralPath $officialManifest -PathType Leaf)) {
|
|
42
|
-
Fail "official manifest not found: $officialManifest"
|
|
43
|
-
}
|
|
44
|
-
$backup = Join-Path ([System.IO.Path]::GetTempPath()) 'dsh-web-package.pre-crew-bridge-removal.json'
|
|
45
|
-
if ($PSCmdlet.ShouldProcess($officialManifest, "backup to $backup")) {
|
|
46
|
-
Copy-Item -LiteralPath $officialManifest -Destination $backup -Force
|
|
47
|
-
Write-Host "Backup: $backup"
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
$manifest = Get-Content -LiteralPath $officialManifest -Raw | ConvertFrom-Json -ErrorAction Stop
|
|
51
|
-
if (-not $manifest.dependencies.$BridgePackage) {
|
|
52
|
-
Fail 'bridge dependency key absent; nothing to clean.'
|
|
53
|
-
}
|
|
54
|
-
if (-not ($manifest.dsh.profile.bundles -contains $BridgePackage)) {
|
|
55
|
-
Fail 'bridge bundle entry absent; nothing to clean.'
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# 3. The link must be a symlink/junction pointing at an old Crew release bridge.
|
|
59
|
-
$link = Get-Item -LiteralPath $bridgeLink -ErrorAction SilentlyContinue
|
|
60
|
-
if (-not $link -or $link.LinkType -notin @('SymbolicLink', 'Junction')) {
|
|
61
|
-
Fail 'bridge link is not a symlink/junction; refusing to touch unknown state.'
|
|
62
|
-
}
|
|
63
|
-
$target = $link.Target
|
|
64
|
-
if (-not $target) {
|
|
65
|
-
$target = (Get-Item -LiteralPath $bridgeLink).FullName
|
|
66
|
-
}
|
|
67
|
-
if ($target -notmatch 'dsh-crew.*official-web-bridge') {
|
|
68
|
-
Fail "bridge link target does not look like a Crew release bridge: $target"
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
# 4. Remove ONLY the dependency key, the bundle entry, and the link.
|
|
72
|
-
if ($PSCmdlet.ShouldProcess($officialManifest, 'remove legacy bridge entries')) {
|
|
73
|
-
$raw = Get-Content -LiteralPath $officialManifest -Raw | ConvertFrom-Json
|
|
74
|
-
$raw.dependencies.PSObject.Properties.Remove($BridgePackage)
|
|
75
|
-
$raw.dsh.profile.bundles = @($raw.dsh.profile.bundles | Where-Object { $_ -ne $BridgePackage })
|
|
76
|
-
$raw | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $officialManifest -Encoding UTF8
|
|
77
|
-
Remove-Item -LiteralPath $bridgeLink -Force
|
|
78
|
-
Write-Host 'Removed bridge dependency key, bundle entry, and link.'
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# 5. Remove Crew-owned legacy intent/state (Crew-owned path, safe to delete).
|
|
82
|
-
if ($PSCmdlet.ShouldProcess($crewState, 'remove legacy Crew bridge state')) {
|
|
83
|
-
Remove-Item -LiteralPath $crewState -Force -ErrorAction SilentlyContinue
|
|
84
|
-
Write-Host 'Removed Crew-owned legacy bridge state (if present).'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
Write-Host 'Done. Restart official 3080 natively; verify:'
|
|
88
|
-
Write-Host ' - 3080 / is 2xx/3xx and /_dsh/dsh-crew/{ping,bridge-status,supervisor/restart} are gone'
|
|
89
|
-
Write-Host ' - 3210 /_dsh/dsh-crew/extension identity is correct (Crew launcher owns 3210 directly)'
|
|
1
|
+
# One-shot operator maintenance: remove the legacy official-web bridge state
|
|
2
|
+
# left behind BEFORE the read-only policy. This script is NEVER invoked by
|
|
3
|
+
# the dsh-crew installer; the product code never writes to ~/.dsh.
|
|
4
|
+
#
|
|
5
|
+
# Preconditions (fail closed unless ALL hold):
|
|
6
|
+
# 1. Official 3080 is stopped and no longer listening.
|
|
7
|
+
# 2. ~/.dsh/profiles/web/package.json dependencies["@ran-sh/dsh-crew-web-bridge"] exists.
|
|
8
|
+
# 3. dsh.profile.bundles contains "@ran-sh/dsh-crew-web-bridge".
|
|
9
|
+
# 4. node_modules/@ran-sh/dsh-crew-web-bridge is a symlink/junction whose
|
|
10
|
+
# target is an old Crew release official-web-bridge dir.
|
|
11
|
+
# Usage:
|
|
12
|
+
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts/remove-legacy-official-bridge.ps1 [-WhatIf]
|
|
13
|
+
|
|
14
|
+
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
15
|
+
param()
|
|
16
|
+
|
|
17
|
+
Set-StrictMode -Version Latest
|
|
18
|
+
$ErrorActionPreference = 'Stop'
|
|
19
|
+
|
|
20
|
+
$BridgePackage = '@ran-sh/dsh-crew-web-bridge'
|
|
21
|
+
$officialManifest = Join-Path $env:USERPROFILE '.dsh\profiles\web\package.json'
|
|
22
|
+
$bridgeLink = Join-Path $env:USERPROFILE '.dsh\profiles\web\node_modules\@ran-sh\dsh-crew-web-bridge'
|
|
23
|
+
$crewState = Join-Path $env:USERPROFILE '.config\dsh-crew\official-web.json'
|
|
24
|
+
|
|
25
|
+
function Fail($message) {
|
|
26
|
+
Write-Host "FAIL-CLOSED: $message" -ForegroundColor Red
|
|
27
|
+
exit 2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# 1. Official 3080 must be stopped.
|
|
31
|
+
try {
|
|
32
|
+
$listeners = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpListeners()
|
|
33
|
+
if (@($listeners | Where-Object Port -eq 3080).Count -gt 0) {
|
|
34
|
+
Fail 'official 3080 is still listening; stop it before cleanup.'
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
Fail "could not enumerate TCP listeners: $($_.Exception.Message)"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# 2. Read + back up the official manifest (backup goes OUTSIDE .dsh).
|
|
41
|
+
if (-not (Test-Path -LiteralPath $officialManifest -PathType Leaf)) {
|
|
42
|
+
Fail "official manifest not found: $officialManifest"
|
|
43
|
+
}
|
|
44
|
+
$backup = Join-Path ([System.IO.Path]::GetTempPath()) 'dsh-web-package.pre-crew-bridge-removal.json'
|
|
45
|
+
if ($PSCmdlet.ShouldProcess($officialManifest, "backup to $backup")) {
|
|
46
|
+
Copy-Item -LiteralPath $officialManifest -Destination $backup -Force
|
|
47
|
+
Write-Host "Backup: $backup"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$manifest = Get-Content -LiteralPath $officialManifest -Raw | ConvertFrom-Json -ErrorAction Stop
|
|
51
|
+
if (-not $manifest.dependencies.$BridgePackage) {
|
|
52
|
+
Fail 'bridge dependency key absent; nothing to clean.'
|
|
53
|
+
}
|
|
54
|
+
if (-not ($manifest.dsh.profile.bundles -contains $BridgePackage)) {
|
|
55
|
+
Fail 'bridge bundle entry absent; nothing to clean.'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# 3. The link must be a symlink/junction pointing at an old Crew release bridge.
|
|
59
|
+
$link = Get-Item -LiteralPath $bridgeLink -ErrorAction SilentlyContinue
|
|
60
|
+
if (-not $link -or $link.LinkType -notin @('SymbolicLink', 'Junction')) {
|
|
61
|
+
Fail 'bridge link is not a symlink/junction; refusing to touch unknown state.'
|
|
62
|
+
}
|
|
63
|
+
$target = $link.Target
|
|
64
|
+
if (-not $target) {
|
|
65
|
+
$target = (Get-Item -LiteralPath $bridgeLink).FullName
|
|
66
|
+
}
|
|
67
|
+
if ($target -notmatch 'dsh-crew.*official-web-bridge') {
|
|
68
|
+
Fail "bridge link target does not look like a Crew release bridge: $target"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# 4. Remove ONLY the dependency key, the bundle entry, and the link.
|
|
72
|
+
if ($PSCmdlet.ShouldProcess($officialManifest, 'remove legacy bridge entries')) {
|
|
73
|
+
$raw = Get-Content -LiteralPath $officialManifest -Raw | ConvertFrom-Json
|
|
74
|
+
$raw.dependencies.PSObject.Properties.Remove($BridgePackage)
|
|
75
|
+
$raw.dsh.profile.bundles = @($raw.dsh.profile.bundles | Where-Object { $_ -ne $BridgePackage })
|
|
76
|
+
$raw | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $officialManifest -Encoding UTF8
|
|
77
|
+
Remove-Item -LiteralPath $bridgeLink -Force
|
|
78
|
+
Write-Host 'Removed bridge dependency key, bundle entry, and link.'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 5. Remove Crew-owned legacy intent/state (Crew-owned path, safe to delete).
|
|
82
|
+
if ($PSCmdlet.ShouldProcess($crewState, 'remove legacy Crew bridge state')) {
|
|
83
|
+
Remove-Item -LiteralPath $crewState -Force -ErrorAction SilentlyContinue
|
|
84
|
+
Write-Host 'Removed Crew-owned legacy bridge state (if present).'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Write-Host 'Done. Restart official 3080 natively; verify:'
|
|
88
|
+
Write-Host ' - 3080 / is 2xx/3xx and /_dsh/dsh-crew/{ping,bridge-status,supervisor/restart} are gone'
|
|
89
|
+
Write-Host ' - 3210 /_dsh/dsh-crew/extension identity is correct (Crew launcher owns 3210 directly)'
|