@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.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +16 -16
  2. package/.claude-plugin/plugin.json +14 -14
  3. package/LICENSE +21 -21
  4. package/README.de.md +359 -359
  5. package/README.es.md +359 -359
  6. package/README.fr.md +359 -359
  7. package/README.hi.md +359 -359
  8. package/README.id.md +359 -359
  9. package/README.ja.md +359 -359
  10. package/README.ko.md +359 -359
  11. package/README.md +126 -126
  12. package/README.pt.md +359 -359
  13. package/README.ru.md +359 -359
  14. package/README.th.md +359 -359
  15. package/README.tr.md +359 -359
  16. package/README.vi.md +359 -359
  17. package/README.zh-TW.md +359 -359
  18. package/README.zh.md +116 -116
  19. package/agents/ds-flash.md +24 -24
  20. package/agents/ds-pro.md +24 -24
  21. package/agents/ds-reviewer.md +23 -23
  22. package/agents/ds-worker.md +23 -23
  23. package/bin/dsh-crew.mjs +16 -16
  24. package/codex/agents/ds-flash.toml +34 -34
  25. package/codex/agents/ds-pro.toml +34 -34
  26. package/codex/agents/ds-reviewer.toml +32 -32
  27. package/codex/agents/ds-worker.toml +32 -32
  28. package/codex/prompts/dsh-config.md +19 -19
  29. package/codex/prompts/dsh-status.md +5 -5
  30. package/commands/config.md +23 -23
  31. package/commands/off.md +5 -5
  32. package/commands/on.md +5 -5
  33. package/commands/status.md +9 -9
  34. package/cordis.patch.yml +4 -4
  35. package/docs/gpt-relay-extension.md +103 -103
  36. package/docs/installation.md +138 -138
  37. package/docs/job-contracts.md +103 -103
  38. package/docs/readiness-matrix.md +85 -85
  39. package/docs/ui-surfaces.md +107 -107
  40. package/official-web-bridge/cordis.patch.yml +4 -4
  41. package/official-web-bridge/entry.mjs +1 -1
  42. package/official-web-bridge/overlay-entry.mjs +59 -59
  43. package/official-web-bridge/package.json +25 -25
  44. package/package.json +1 -1
  45. package/scripts/build-client.mjs +49 -49
  46. package/scripts/live-crew-smoke.mjs +39 -39
  47. package/scripts/live-policy-matrix.mjs +177 -177
  48. package/scripts/policy-probe.mjs +101 -101
  49. package/scripts/remove-legacy-official-bridge.ps1 +89 -89
  50. package/scripts/setup.mjs +393 -393
  51. package/scripts/smoke-real.mjs +110 -110
  52. package/scripts/smoke.mjs +78 -78
  53. package/scripts/verify-crew-ui-polish.mjs +145 -145
  54. package/scripts/verify-history-ui.mjs +97 -97
  55. package/scripts/verify-installer-fix.mjs +26 -26
  56. package/scripts/verify-npm-install.mjs +311 -311
  57. package/scripts/verify-official-bridge-e2e.mjs +192 -192
  58. package/src/adaptive-routing.mjs +260 -260
  59. package/src/client/activation-summary.tsx +64 -64
  60. package/src/client/collapsible-sections.mjs +55 -55
  61. package/src/client/history-panel.tsx +108 -108
  62. package/src/client/host-readiness.mjs +71 -71
  63. package/src/client/index.tsx +1711 -1711
  64. package/src/client/model-callability-view.mjs +17 -17
  65. package/src/client/panel-chrome.tsx +40 -40
  66. package/src/client/quick-entry.tsx +10 -10
  67. package/src/client/quick-panel.tsx +275 -275
  68. package/src/client/readiness-envelope.mjs +34 -34
  69. package/src/client/surface-detection.mjs +43 -43
  70. package/src/config-readiness.mjs +226 -226
  71. package/src/credential-reference.mjs +38 -38
  72. package/src/delivery.mjs +205 -205
  73. package/src/dsh-cli-runtime.mjs +1021 -1021
  74. package/src/dsh-cohort.mjs +20 -20
  75. package/src/extension-contract.mjs +104 -104
  76. package/src/failure-classification.mjs +201 -201
  77. package/src/history/admission-gate.mjs +67 -67
  78. package/src/history/archive-store.mjs +272 -272
  79. package/src/history/cleanup-plan.mjs +89 -89
  80. package/src/history/http.mjs +32 -32
  81. package/src/history/operation.mjs +86 -86
  82. package/src/history/runner-detach.mjs +34 -34
  83. package/src/history/runner.mjs +52 -52
  84. package/src/history/runtime.mjs +36 -36
  85. package/src/history/service.mjs +177 -177
  86. package/src/history/state.mjs +27 -27
  87. package/src/hub/entry.mjs +104 -104
  88. package/src/hub/index.mjs +2694 -2694
  89. package/src/hub-client.mjs +154 -154
  90. package/src/hub-compatibility.mjs +42 -42
  91. package/src/i18n.mjs +19 -19
  92. package/src/information-flow.mjs +67 -67
  93. package/src/install/cli.mjs +28 -28
  94. package/src/install/install-legacy.mjs +711 -711
  95. package/src/install/install.mjs +483 -483
  96. package/src/install/npx-lifecycle.mjs +3456 -3456
  97. package/src/install/official-frontend-assets.mjs +78 -78
  98. package/src/install/official-web.mjs +95 -95
  99. package/src/install/payload-content.mjs +88 -88
  100. package/src/install/windows-startup.mjs +236 -236
  101. package/src/install/windows-supervisor-adapter.mjs +443 -443
  102. package/src/install/windows-supervisor-lifecycle.mjs +782 -782
  103. package/src/install/zcode.mjs +397 -397
  104. package/src/job-contracts.mjs +255 -255
  105. package/src/local-request-guard.mjs +60 -60
  106. package/src/mcp-runtime.mjs +340 -332
  107. package/src/model-callability-contract.mjs +79 -79
  108. package/src/model-catalog.mjs +180 -180
  109. package/src/model-routing.mjs +586 -586
  110. package/src/model-schedule.mjs +207 -207
  111. package/src/official-web-bridge.mjs +447 -447
  112. package/src/policy.mjs +235 -235
  113. package/src/provider-delete-adapters.mjs +1934 -1934
  114. package/src/provider-health.mjs +130 -130
  115. package/src/provider-inventory.mjs +182 -182
  116. package/src/provider-layer-migration-adapters.mjs +759 -759
  117. package/src/provider-layer-migration.mjs +198 -198
  118. package/src/provider-lifecycle-state.mjs +103 -103
  119. package/src/provider-lifecycle.mjs +252 -252
  120. package/src/provider-profile-store.mjs +390 -390
  121. package/src/provider-settings-store.mjs +633 -633
  122. package/src/provider-store-lock.mjs +67 -67
  123. package/src/readiness-matrix.mjs +181 -181
  124. package/src/removable-waiter.mjs +29 -29
  125. package/src/role-profiles.mjs +107 -107
  126. package/src/runtime-controls.mjs +84 -84
  127. package/src/runtime-identity-contract.mjs +34 -34
  128. package/src/runtime-identity.mjs +235 -235
  129. package/src/runtime-readiness-snapshot.mjs +285 -285
  130. package/src/server.mjs +581 -581
  131. package/src/session-origins.mjs +60 -60
  132. package/src/standalone-sdk.mjs +23 -23
  133. package/src/status-shard.mjs +63 -63
  134. package/src/structured-error-code.mjs +38 -38
  135. package/src/supervisor/restart-request.mjs +256 -256
  136. package/src/workflow-runtime.mjs +739 -739
  137. package/src/workflow.mjs +155 -155
  138. package/src/workspace-audit.mjs +231 -231
  139. package/src/workspace-context.mjs +146 -146
  140. package/src/workspace-isolation.mjs +455 -401
  141. package/src/workspace-readiness.mjs +32 -32
  142. package/statusline/statusline.sh +14 -14
  143. package/statusline/worker-segment.sh +35 -35
  144. package/windows/start-dsh-crew.cmd +57 -57
  145. package/windows/start-dsh-crew.ps1 +1302 -1302
  146. package/windows/supervisor-control.ps1 +467 -467
  147. package/worker.cordis.yml +67 -67
  148. package/zcode/agents/ds-reviewer.md +31 -31
  149. package/zcode/agents/ds-worker.md +31 -31
  150. package/zcode/commands/dsh-config.md +17 -17
  151. package/zcode/commands/dsh-status.md +5 -5
@@ -1,110 +1,110 @@
1
- // Real DSH smoke for the v0.3 runtime path. Spawns this branch's MCP server
2
- // (src/server.mjs) over stdio and drives it with the MCP SDK.
3
- // run|spawn : against a LIVE, Crew-owned DSH hub. The hub URL comes from the
4
- // DSH_CREW_HUB environment variable and defaults to the dedicated
5
- // Crew port 3210; the official ~/.dsh home / web profile is never
6
- // read or touched.
7
- // standalone: standalone worker path (jobs.mjs) using ONLY explicitly
8
- // supplied process-environment credentials
9
- // (DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL). This script never reads
10
- // ~/.dsh, any official credential/settings store, or key files.
11
- // If the credential environment is absent it exits BLOCKED-style.
12
- // Uses a throwaway temp git repo; never touches the web profile or user repos.
13
- // Usage: node scripts/smoke-real.mjs <tempGitRepo> [run|spawn|standalone]
14
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
15
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
16
-
17
- /** Session descriptor for the MCP child. NEVER logs the key. */
18
- function serverTransport(extraEnv = {}) {
19
- return new StdioClientTransport({
20
- command: 'node',
21
- args: ['src/server.mjs'],
22
- cwd: process.cwd(),
23
- env: {
24
- ...process.env,
25
- // Crew-owned hub default; an explicit DSH_CREW_HUB overrides it.
26
- DSH_CREW_HUB: process.env.DSH_CREW_HUB ?? 'http://127.0.0.1:3210',
27
- ...extraEnv,
28
- },
29
- stderr: process.stderr,
30
- });
31
- }
32
-
33
- const repo = process.argv[2];
34
- const mode = process.argv[3] ?? 'run';
35
- const effort = process.argv[4] ?? 'max';
36
- if (!repo) { console.error('usage: node scripts/smoke-real.mjs <tempGitRepo> [run|spawn|standalone] [effort]'); process.exit(2); }
37
-
38
- const TASK = [
39
- 'In this git repository, create the file src/answer.mjs with exactly the content: `export const answer = 40 + 2;`',
40
- 'Then run the single-line check: node -e \'const a = require("./src/answer.mjs"); if (a.answer !== 42) process.exit(1); console.log("answer ok");\' (ESM note: use import("./src/answer.mjs") or write .cjs — pick whatever works and run it).',
41
- 'End your final message with the required Delivery Report:',
42
- '## Diff',
43
- '- src/answer.mjs — added answer',
44
- '## Tests',
45
- 'PASS — node check — answer is 42',
46
- '## Risks',
47
- 'none',
48
- ].join('\n');
49
-
50
- // For standalone mode, use ONLY explicitly supplied process-environment
51
- // credentials (DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL). This script never reads
52
- // ~/.dsh or any official credential/settings store. Absent credentials are a
53
- // BLOCKED outcome, not an excuse to invent or read credentials.
54
- const standaloneEnv = {};
55
- if (mode === 'standalone') {
56
- const apiKey = process.env.DEEPSEEK_API_KEY;
57
- if (!apiKey) {
58
- console.error('[smoke] BLOCKED: standalone requires DEEPSEEK_API_KEY in the process environment; credential: ABSENT (no official store will be read)');
59
- process.exit(2);
60
- }
61
- standaloneEnv.DEEPSEEK_API_KEY = apiKey;
62
- if (process.env.DEEPSEEK_BASE_URL) standaloneEnv.DEEPSEEK_BASE_URL = process.env.DEEPSEEK_BASE_URL;
63
- standaloneEnv.DSH_SANDBOX_MODE = 'workspace-write';
64
- console.log('[smoke] standalone: using process-environment credentials (not shown)');
65
- }
66
- const transport = serverTransport(standaloneEnv);
67
- const client = new Client({ name: 'dsh-crew-smoke', version: '1.0.0', requestTimeout: 600 * 1000 });
68
-
69
- async function call(name, args, timeoutMs) {
70
- const res = await client.callTool({ name, arguments: args }, undefined, timeoutMs ? { timeout: timeoutMs } : undefined);
71
- return JSON.parse(res.content.find((c) => c.type === 'text').text);
72
- }
73
-
74
- try {
75
- await client.connect(transport);
76
- console.log(`[smoke] connected; mode=${mode} cwd=${repo}`);
77
- if (mode === 'standalone') {
78
- // Force the standalone execution path (session mode) so jobs.mjs handles it.
79
- await call('dsh_worker_config', { mode: 'standalone' });
80
- }
81
-
82
- if (mode === 'spawn') {
83
- const spawnView = await call('dsh_spawn_worker', { task: TASK, role: 'worker', cwd: repo });
84
- console.log('[smoke] spawn view:', JSON.stringify(spawnView, null, 2));
85
- const wfId = spawnView.workflow_id ?? spawnView.id;
86
- // poll a few times for the final result (workflow continues in background)
87
- for (let i = 0; i < 24; i++) {
88
- await new Promise((r) => setTimeout(r, 5000));
89
- const v = await call('dsh_worker_result', { job_id: wfId, wait_seconds: 0 });
90
- if (v.status !== 'running') {
91
- console.log('[smoke] final (spawn):', JSON.stringify({ id: v.id, role: v.role, phase: v.phase, status: v.status, isolation: v.isolation, attempt: v.attempt, review: v.review?.verdict ?? null, candidate: v.candidate?.changed_files ?? null, error: v.error }, null, 2));
92
- await client.close();
93
- process.exit(v.status === 'done' ? 0 : 1);
94
- }
95
- if (i === 23) console.log('[smoke] still running after poll window: ', JSON.stringify(v));
96
- }
97
- await client.close();
98
- process.exit(2);
99
- }
100
-
101
- const view = await call('dsh_run_worker', { task: TASK, role: 'worker', cwd: repo, effort, timeout_seconds: 540 }, 560 * 1000);
102
- const k = (o) => ({ id: o.id, role: o.role, phase: o.phase, status: o.status, isolation: o.isolation, execution_cwd: o.execution_cwd, base_revision: o.base_revision, attempt: o.attempt, decision: o.decision, review: o.review?.verdict ?? null, candidate_available: o.candidate_available, candidate_changed: o.candidate?.changed_files ?? null, outcome_task: o.outcome?.task_status ?? null, tests: o.outcome?.tests_status ?? null, error: o.error, error_code: o.error_code, cleanup_warning: o.cleanup_warning, primary_dirty_hint: o.primary_workspace_dirty });
103
- console.log('[smoke] final (run):', JSON.stringify(k(view), null, 2));
104
- await client.close();
105
- process.exit(view.status === 'done' ? 0 : 1);
106
- } catch (err) {
107
- console.error('[smoke] ERROR:', err?.message ?? err);
108
- try { await client.close(); } catch {}
109
- process.exit(3);
110
- }
1
+ // Real DSH smoke for the v0.3 runtime path. Spawns this branch's MCP server
2
+ // (src/server.mjs) over stdio and drives it with the MCP SDK.
3
+ // run|spawn : against a LIVE, Crew-owned DSH hub. The hub URL comes from the
4
+ // DSH_CREW_HUB environment variable and defaults to the dedicated
5
+ // Crew port 3210; the official ~/.dsh home / web profile is never
6
+ // read or touched.
7
+ // standalone: standalone worker path (jobs.mjs) using ONLY explicitly
8
+ // supplied process-environment credentials
9
+ // (DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL). This script never reads
10
+ // ~/.dsh, any official credential/settings store, or key files.
11
+ // If the credential environment is absent it exits BLOCKED-style.
12
+ // Uses a throwaway temp git repo; never touches the web profile or user repos.
13
+ // Usage: node scripts/smoke-real.mjs <tempGitRepo> [run|spawn|standalone]
14
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
15
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
16
+
17
+ /** Session descriptor for the MCP child. NEVER logs the key. */
18
+ function serverTransport(extraEnv = {}) {
19
+ return new StdioClientTransport({
20
+ command: 'node',
21
+ args: ['src/server.mjs'],
22
+ cwd: process.cwd(),
23
+ env: {
24
+ ...process.env,
25
+ // Crew-owned hub default; an explicit DSH_CREW_HUB overrides it.
26
+ DSH_CREW_HUB: process.env.DSH_CREW_HUB ?? 'http://127.0.0.1:3210',
27
+ ...extraEnv,
28
+ },
29
+ stderr: process.stderr,
30
+ });
31
+ }
32
+
33
+ const repo = process.argv[2];
34
+ const mode = process.argv[3] ?? 'run';
35
+ const effort = process.argv[4] ?? 'max';
36
+ if (!repo) { console.error('usage: node scripts/smoke-real.mjs <tempGitRepo> [run|spawn|standalone] [effort]'); process.exit(2); }
37
+
38
+ const TASK = [
39
+ 'In this git repository, create the file src/answer.mjs with exactly the content: `export const answer = 40 + 2;`',
40
+ 'Then run the single-line check: node -e \'const a = require("./src/answer.mjs"); if (a.answer !== 42) process.exit(1); console.log("answer ok");\' (ESM note: use import("./src/answer.mjs") or write .cjs — pick whatever works and run it).',
41
+ 'End your final message with the required Delivery Report:',
42
+ '## Diff',
43
+ '- src/answer.mjs — added answer',
44
+ '## Tests',
45
+ 'PASS — node check — answer is 42',
46
+ '## Risks',
47
+ 'none',
48
+ ].join('\n');
49
+
50
+ // For standalone mode, use ONLY explicitly supplied process-environment
51
+ // credentials (DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL). This script never reads
52
+ // ~/.dsh or any official credential/settings store. Absent credentials are a
53
+ // BLOCKED outcome, not an excuse to invent or read credentials.
54
+ const standaloneEnv = {};
55
+ if (mode === 'standalone') {
56
+ const apiKey = process.env.DEEPSEEK_API_KEY;
57
+ if (!apiKey) {
58
+ console.error('[smoke] BLOCKED: standalone requires DEEPSEEK_API_KEY in the process environment; credential: ABSENT (no official store will be read)');
59
+ process.exit(2);
60
+ }
61
+ standaloneEnv.DEEPSEEK_API_KEY = apiKey;
62
+ if (process.env.DEEPSEEK_BASE_URL) standaloneEnv.DEEPSEEK_BASE_URL = process.env.DEEPSEEK_BASE_URL;
63
+ standaloneEnv.DSH_SANDBOX_MODE = 'workspace-write';
64
+ console.log('[smoke] standalone: using process-environment credentials (not shown)');
65
+ }
66
+ const transport = serverTransport(standaloneEnv);
67
+ const client = new Client({ name: 'dsh-crew-smoke', version: '1.0.0', requestTimeout: 600 * 1000 });
68
+
69
+ async function call(name, args, timeoutMs) {
70
+ const res = await client.callTool({ name, arguments: args }, undefined, timeoutMs ? { timeout: timeoutMs } : undefined);
71
+ return JSON.parse(res.content.find((c) => c.type === 'text').text);
72
+ }
73
+
74
+ try {
75
+ await client.connect(transport);
76
+ console.log(`[smoke] connected; mode=${mode} cwd=${repo}`);
77
+ if (mode === 'standalone') {
78
+ // Force the standalone execution path (session mode) so jobs.mjs handles it.
79
+ await call('dsh_worker_config', { mode: 'standalone' });
80
+ }
81
+
82
+ if (mode === 'spawn') {
83
+ const spawnView = await call('dsh_spawn_worker', { task: TASK, role: 'worker', cwd: repo });
84
+ console.log('[smoke] spawn view:', JSON.stringify(spawnView, null, 2));
85
+ const wfId = spawnView.workflow_id ?? spawnView.id;
86
+ // poll a few times for the final result (workflow continues in background)
87
+ for (let i = 0; i < 24; i++) {
88
+ await new Promise((r) => setTimeout(r, 5000));
89
+ const v = await call('dsh_worker_result', { job_id: wfId, wait_seconds: 0 });
90
+ if (v.status !== 'running') {
91
+ console.log('[smoke] final (spawn):', JSON.stringify({ id: v.id, role: v.role, phase: v.phase, status: v.status, isolation: v.isolation, attempt: v.attempt, review: v.review?.verdict ?? null, candidate: v.candidate?.changed_files ?? null, error: v.error }, null, 2));
92
+ await client.close();
93
+ process.exit(v.status === 'done' ? 0 : 1);
94
+ }
95
+ if (i === 23) console.log('[smoke] still running after poll window: ', JSON.stringify(v));
96
+ }
97
+ await client.close();
98
+ process.exit(2);
99
+ }
100
+
101
+ const view = await call('dsh_run_worker', { task: TASK, role: 'worker', cwd: repo, effort, timeout_seconds: 540 }, 560 * 1000);
102
+ const k = (o) => ({ id: o.id, role: o.role, phase: o.phase, status: o.status, isolation: o.isolation, execution_cwd: o.execution_cwd, base_revision: o.base_revision, attempt: o.attempt, decision: o.decision, review: o.review?.verdict ?? null, candidate_available: o.candidate_available, candidate_changed: o.candidate?.changed_files ?? null, outcome_task: o.outcome?.task_status ?? null, tests: o.outcome?.tests_status ?? null, error: o.error, error_code: o.error_code, cleanup_warning: o.cleanup_warning, primary_dirty_hint: o.primary_workspace_dirty });
103
+ console.log('[smoke] final (run):', JSON.stringify(k(view), null, 2));
104
+ await client.close();
105
+ process.exit(view.status === 'done' ? 0 : 1);
106
+ } catch (err) {
107
+ console.error('[smoke] ERROR:', err?.message ?? err);
108
+ try { await client.close(); } catch {}
109
+ process.exit(3);
110
+ }
package/scripts/smoke.mjs CHANGED
@@ -1,78 +1,78 @@
1
- // Smoke test: dispatch one cheap flash job to verify the installation.
2
- // Probes the hub first (if DSH web instance is running); falls back to
3
- // standalone mode. Always exercises the path the user actually installed.
4
-
5
- import { hubAvailable, hub } from '../src/hub-client.mjs';
6
- import { readGlobalConfig } from '../src/install/install.mjs';
7
-
8
- try {
9
- const hubUrl = process.env.DSH_CREW_HUB ?? readGlobalConfig().hub_url;
10
- const isHubUp = await hubAvailable();
11
-
12
- if (isHubUp) {
13
- // Dispatch through the hub
14
- console.log(`hub at ${hubUrl} is reachable, using hub mode`);
15
- const job = await hub.spawn({
16
- task: 'Reply with exactly the word: ok',
17
- tier: 'flash',
18
- effort: 'off',
19
- cwd: process.cwd(),
20
- source: 'smoke',
21
- });
22
- console.log(`spawned ${job.id} ...`);
23
- const result = await hub.get(job.id, 120);
24
- if (result.status === 'done') {
25
- console.log(`worker replied: ${result.result}`);
26
- console.log('smoke test passed — configuration OK');
27
- } else if (result.status === 'running') {
28
- // The wait returns early once the job settles, so this is a real timeout.
29
- console.error(`smoke test failed: job ${job.id} still running after 120s`);
30
- console.error('Hint: it is still live in the DSH instance — check its session for what is stuck');
31
- process.exit(1);
32
- } else {
33
- console.error(`smoke test failed: status=${result.status} error=${result.error}`);
34
- console.error('Hint: check the DSH instance logs and verify DeepSeek provider credentials');
35
- process.exit(1);
36
- }
37
- } else {
38
- // Fall back to standalone mode
39
- console.log(`no hub at ${hubUrl}, falling back to standalone mode`);
40
-
41
- try {
42
- const { startJob, waitJob, jobView } = await import('../src/jobs.mjs');
43
- const job = startJob({
44
- task: 'Reply with exactly the word: ok',
45
- tier: 'flash',
46
- effort: 'off',
47
- cwd: process.cwd(),
48
- });
49
- console.log(`spawned ${job.id} (deepseek-v4-flash) ...`);
50
- await waitJob(job.id, 120_000);
51
- const v = jobView(job, { withResult: true });
52
- if (v.status === 'done') {
53
- console.log(`worker replied: ${v.result}`);
54
- console.log('smoke test passed — configuration OK');
55
- } else {
56
- console.error(`smoke test failed: status=${v.status} error=${v.error}`);
57
- console.error('Hint: DEEPSEEK_API_KEY missing or invalid in env or ~/.config/dsh-crew/.env, or runtime not installed');
58
- process.exit(1);
59
- }
60
- } catch (err) {
61
- // The standalone runtime lives in this package's own node_modules; the
62
- // @deepseek-ai/* packages are peerDependencies, so an installed copy of
63
- // the plugin has none of them and the dynamic import above is what tells
64
- // us so. Anything else here is a dispatch failure, not a missing runtime.
65
- if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND') {
66
- console.error('smoke test failed: the standalone runtime is not installed in this copy');
67
- console.error('Hint: run "pnpm install" from a source checkout, or start a DSH instance to use hub mode');
68
- } else {
69
- console.error(`smoke test failed: ${err.message}`);
70
- console.error('Hint: DEEPSEEK_API_KEY missing or invalid in env or ~/.config/dsh-crew/.env');
71
- }
72
- process.exit(1);
73
- }
74
- }
75
- } catch (err) {
76
- console.error(`smoke test failed: ${err.message}`);
77
- process.exit(1);
78
- }
1
+ // Smoke test: dispatch one cheap flash job to verify the installation.
2
+ // Probes the hub first (if DSH web instance is running); falls back to
3
+ // standalone mode. Always exercises the path the user actually installed.
4
+
5
+ import { hubAvailable, hub } from '../src/hub-client.mjs';
6
+ import { readGlobalConfig } from '../src/install/install.mjs';
7
+
8
+ try {
9
+ const hubUrl = process.env.DSH_CREW_HUB ?? readGlobalConfig().hub_url;
10
+ const isHubUp = await hubAvailable();
11
+
12
+ if (isHubUp) {
13
+ // Dispatch through the hub
14
+ console.log(`hub at ${hubUrl} is reachable, using hub mode`);
15
+ const job = await hub.spawn({
16
+ task: 'Reply with exactly the word: ok',
17
+ tier: 'flash',
18
+ effort: 'off',
19
+ cwd: process.cwd(),
20
+ source: 'smoke',
21
+ });
22
+ console.log(`spawned ${job.id} ...`);
23
+ const result = await hub.get(job.id, 120);
24
+ if (result.status === 'done') {
25
+ console.log(`worker replied: ${result.result}`);
26
+ console.log('smoke test passed — configuration OK');
27
+ } else if (result.status === 'running') {
28
+ // The wait returns early once the job settles, so this is a real timeout.
29
+ console.error(`smoke test failed: job ${job.id} still running after 120s`);
30
+ console.error('Hint: it is still live in the DSH instance — check its session for what is stuck');
31
+ process.exit(1);
32
+ } else {
33
+ console.error(`smoke test failed: status=${result.status} error=${result.error}`);
34
+ console.error('Hint: check the DSH instance logs and verify DeepSeek provider credentials');
35
+ process.exit(1);
36
+ }
37
+ } else {
38
+ // Fall back to standalone mode
39
+ console.log(`no hub at ${hubUrl}, falling back to standalone mode`);
40
+
41
+ try {
42
+ const { startJob, waitJob, jobView } = await import('../src/jobs.mjs');
43
+ const job = startJob({
44
+ task: 'Reply with exactly the word: ok',
45
+ tier: 'flash',
46
+ effort: 'off',
47
+ cwd: process.cwd(),
48
+ });
49
+ console.log(`spawned ${job.id} (deepseek-v4-flash) ...`);
50
+ await waitJob(job.id, 120_000);
51
+ const v = jobView(job, { withResult: true });
52
+ if (v.status === 'done') {
53
+ console.log(`worker replied: ${v.result}`);
54
+ console.log('smoke test passed — configuration OK');
55
+ } else {
56
+ console.error(`smoke test failed: status=${v.status} error=${v.error}`);
57
+ console.error('Hint: DEEPSEEK_API_KEY missing or invalid in env or ~/.config/dsh-crew/.env, or runtime not installed');
58
+ process.exit(1);
59
+ }
60
+ } catch (err) {
61
+ // The standalone runtime lives in this package's own node_modules; the
62
+ // @deepseek-ai/* packages are peerDependencies, so an installed copy of
63
+ // the plugin has none of them and the dynamic import above is what tells
64
+ // us so. Anything else here is a dispatch failure, not a missing runtime.
65
+ if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND') {
66
+ console.error('smoke test failed: the standalone runtime is not installed in this copy');
67
+ console.error('Hint: run "pnpm install" from a source checkout, or start a DSH instance to use hub mode');
68
+ } else {
69
+ console.error(`smoke test failed: ${err.message}`);
70
+ console.error('Hint: DEEPSEEK_API_KEY missing or invalid in env or ~/.config/dsh-crew/.env');
71
+ }
72
+ process.exit(1);
73
+ }
74
+ }
75
+ } catch (err) {
76
+ console.error(`smoke test failed: ${err.message}`);
77
+ process.exit(1);
78
+ }