@linzumi/cli 1.0.115 → 1.0.117

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "1.0.115",
3
+ "version": "1.0.117",
4
4
  "description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -86,8 +86,14 @@ for await (const line of rl) {
86
86
  state = { pending: null };
87
87
  threads.set(threadId, state);
88
88
  }
89
- const event = entry.event;
90
- if (event === 'kandan.message_queued') {
89
+ // N2.4-prep (naming-consistency program wave N2): match the CANONICAL
90
+ // event names first with the legacy spellings as the permanent fallback -
91
+ // dual-emit window logs carry the legacy name (plus an event_canonical
92
+ // twin field), post-flip logs carry the canonical name only, and archived
93
+ // logs stay legacy forever (D45). codex.turn_starting is a retained D42
94
+ // true-harness name (not renamed).
95
+ const event = entry.event_canonical ?? entry.event;
96
+ if (event === 'linzumi.message_queued' || event === 'kandan.message_queued') {
91
97
  state.pending = { atMs, seq: entry.seq, blocked: [], spawn: false };
92
98
  } else if (state.pending !== null) {
93
99
  if (event === 'codex.turn_starting') {
@@ -99,7 +105,10 @@ for await (const line of rl) {
99
105
  spawn: state.pending.spawn,
100
106
  });
101
107
  state.pending = null;
102
- } else if (event === 'kandan.queue_drain_blocked') {
108
+ } else if (
109
+ event === 'linzumi.queue_drain_blocked' ||
110
+ event === 'kandan.queue_drain_blocked'
111
+ ) {
103
112
  state.pending.blocked.push(entry.reason);
104
113
  } else if (event === 'runner.thread_process_starting') {
105
114
  state.pending.spawn = true;
@@ -0,0 +1,186 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 13, onboarding - visual evidence).
3
+ // Relationship: Builds the TS TWIN of the onboarding demo entrypoint
4
+ // (packages/linzumi-cli-rescript/src/onboarding/CommanderOnboardingDemoMain.res):
5
+ // the REAL runSignupFlow mounted on the live terminal - real
6
+ // @inquirer/prompts rendering, the real raw-keypress project picker, the
7
+ // REAL createSignupServerClient speaking HTTP to the SAME in-process
8
+ // ScriptedAuthServer (imported from the ReScript package's compiled
9
+ // module - one server implementation, zero drift), replaying the SAME
10
+ // commanderOnboardingDemoScenario.json. scripts/onboarding_evidence_
11
+ // capture.sh drives both entrypoints with the same tmux key schedule and
12
+ // byte-compares the captures.
13
+ //
14
+ // Determinism: the scenario is dummy data; the only wall-clock effects
15
+ // (config-file timestamps) never reach the terminal (the demo runs with
16
+ // debugLaunchPayload off unless --debug-signup is passed for ad-hoc use).
17
+ import { mkdir, readFile } from 'node:fs/promises';
18
+ import { dirname, join } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { build } from 'esbuild';
21
+
22
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
23
+
24
+ const driverSource = `
25
+ import { readFileSync } from 'node:fs';
26
+ import { join } from 'node:path';
27
+ import { runSignupFlow, signupSuggestTasksForProjectForTest, signupPromptCancelMessage } from './src/signupFlow';
28
+ import { createSignupServerClient } from './src/signupServerClient';
29
+ // The ONE scripted-server implementation: the ReScript package's cluster 3
30
+ // double, imported as plain ESM.
31
+ import * as ScriptedAuthServer from '../linzumi-cli-rescript/src/parity/ScriptedAuthServer.res.mjs';
32
+
33
+ const scenarioPath = join(
34
+ '${packageRoot}',
35
+ '..',
36
+ 'linzumi-cli-rescript',
37
+ 'src',
38
+ 'onboarding',
39
+ 'commanderOnboardingDemoScenario.json'
40
+ );
41
+ const scenario = JSON.parse(readFileSync(scenarioPath, 'utf8'));
42
+
43
+ function reviveProjects(projects) {
44
+ return (projects ?? []).map((project) => ({
45
+ path: project.path,
46
+ language: project.language,
47
+ source: project.source,
48
+ ...(project.gitHead === undefined || project.gitHead === null
49
+ ? {}
50
+ : { gitHead: project.gitHead }),
51
+ }));
52
+ }
53
+
54
+ const jsonResponse = (value) =>
55
+ ScriptedAuthServer.jsonResponse(200, JSON.stringify(value ?? {}));
56
+
57
+ async function main() {
58
+ const args = process.argv.slice(2);
59
+ const mock = args.includes('--mock');
60
+ const handle = await ScriptedAuthServer.start({
61
+ routes: [
62
+ {
63
+ method: 'POST',
64
+ path: '/api/v2/signup/email-code/start',
65
+ responses: [jsonResponse(scenario.server.emailCodeStart)],
66
+ },
67
+ {
68
+ method: 'POST',
69
+ path: '/api/v2/signup/email-code/verify',
70
+ responses: [jsonResponse(scenario.server.emailCodeVerify)],
71
+ },
72
+ {
73
+ method: 'POST',
74
+ path: '/api/v2/signup/mission-control/complete',
75
+ responses: [jsonResponse(scenario.server.missionControlComplete)],
76
+ },
77
+ {
78
+ method: 'POST',
79
+ path: '/api/v2/signup/mission-control/start-tasks',
80
+ responses: [jsonResponse(scenario.server.missionControlStartTasks)],
81
+ },
82
+ ],
83
+ });
84
+ const serviceUrl = handle.baseUrl;
85
+ const preflight = scenario.preflight ?? {};
86
+ const tools = preflight.tools ?? {};
87
+ const demoHome = process.env.LINZUMI_ONBOARDING_DEMO_HOME ?? process.env.HOME ?? '/tmp';
88
+ const noopCacheStore = { read: () => undefined, write: () => {} };
89
+ try {
90
+ await runSignupFlow({
91
+ // Real stdio: the prompts default to the REAL inquirer surface and
92
+ // the raw-keypress picker (deps.prompts stays undefined).
93
+ input: process.stdin,
94
+ output: process.stdout,
95
+ preflight: {
96
+ cwd: demoHome,
97
+ homeDir: demoHome,
98
+ probeTool: async (command) => {
99
+ const tool = tools[command];
100
+ if (tool === undefined) {
101
+ return { command, available: false };
102
+ }
103
+ return { command, available: tool.available, version: tool.version ?? undefined };
104
+ },
105
+ readGitEmail: async () => preflight.gitEmail ?? undefined,
106
+ discoverCodeRoots: async () => preflight.codeRoots ?? [],
107
+ checkBrowserHandoff: async () => preflight.browserHandoff !== false,
108
+ },
109
+ projectDiscovery: {
110
+ discoverProjects: async () => reviveProjects(scenario.projects),
111
+ },
112
+ taskSuggestion: {
113
+ suggestTasks: (project, output) =>
114
+ signupSuggestTasksForProjectForTest({
115
+ project,
116
+ output,
117
+ taskCacheStore: noopCacheStore,
118
+ codexTaskRunner: async () => scenario.suggestedTasks ?? [],
119
+ }),
120
+ },
121
+ forceSignup: args.includes('--force-signup'),
122
+ serviceUrl,
123
+ openBrowser: false,
124
+ debugLaunchPayload: args.includes('--debug-signup'),
125
+ signupServerClient: mock ? false : createSignupServerClient({ serviceUrl }),
126
+ commanderLauncher: async () => ({
127
+ status: 'started',
128
+ runnerId: scenario.launch?.runnerId ?? 'demo-commander',
129
+ restartCommand:
130
+ scenario.launch?.restartCommand ?? 'npx -y @linzumi/cli@latest connect',
131
+ instanceId: 'demo-instance',
132
+ }),
133
+ });
134
+ await handle.stop();
135
+ process.exit(0);
136
+ } catch (error) {
137
+ await handle.stop();
138
+ const message = error instanceof Error ? error.message : String(error);
139
+ process.stdout.write((signupPromptCancelMessage(error) ?? message) + '\\n');
140
+ process.exit(1);
141
+ }
142
+ }
143
+
144
+ main();
145
+ `;
146
+
147
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
148
+
149
+ // The build-auth-parity-oracle.mjs banner convention (inquirer's CJS deps
150
+ // require node builtins at module scope).
151
+ const banner = {
152
+ js: "import { createRequire as __onboardingDemoCreateRequire } from 'node:module';\nconst require = __onboardingDemoCreateRequire(import.meta.url);",
153
+ };
154
+
155
+ await build({
156
+ banner,
157
+ stdin: {
158
+ contents: driverSource,
159
+ resolveDir: packageRoot,
160
+ sourcefile: 'onboarding-demo-evidence-driver.ts',
161
+ loader: 'ts',
162
+ },
163
+ bundle: true,
164
+ platform: 'node',
165
+ target: 'node20',
166
+ format: 'esm',
167
+ outfile: join(packageRoot, '.differential/onboarding-demo-evidence.mjs'),
168
+ minify: false,
169
+ sourcemap: false,
170
+ legalComments: 'none',
171
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
172
+ });
173
+
174
+ // Keep the scenario honest: fail the build loudly if the shared file is
175
+ // missing (the twin reads it at RUNTIME from the sibling package).
176
+ await readFile(
177
+ join(
178
+ packageRoot,
179
+ '..',
180
+ 'linzumi-cli-rescript',
181
+ 'src',
182
+ 'onboarding',
183
+ 'commanderOnboardingDemoScenario.json'
184
+ ),
185
+ 'utf8'
186
+ );
@@ -0,0 +1,380 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 13, onboarding - the DISCOVERY STACK slice).
3
+ // Relationship: Builds the TS-side PARITY ORACLE for the onboarding
4
+ // discovery port. The oracle bundles the REAL TS discovery modules -
5
+ // onboardingProjectDiscovery.ts, onboardingConversationDiscovery.ts,
6
+ // onboardingDiscoveryAgent.ts, onboardingDiscoveryChildProcess.ts,
7
+ // onboardingPlaceResponsiveness.ts - plus the agent builder's REAL seam
8
+ // modules (codexHome.ts, mcpConfig.ts, safeWorkingDir.ts, probed so the
9
+ // ReScript port's injected deps carry the SAME real values) behind the
10
+ // cluster 1/2 one-shot BATCH driver (a JSON array of cases on stdin, a
11
+ // JSON array of results on stdout).
12
+ //
13
+ // CHILD ROLE MODE: when LINZUMI_ONBOARDING_DISCOVERY_ROLE is set in the
14
+ // environment the oracle becomes a REAL discovery child running the REAL
15
+ // runOnboardingDiscoveryChildRole over process stdin/stdout - exactly the
16
+ // CLI entry's dispatch (src/index.ts main) - so EITHER impl's parent can
17
+ // spawn this bundle as its discovery child (the cross-impl process legs).
18
+ //
19
+ // Determinism: both impls scan the SAME conformance-built scratch trees
20
+ // (absolute paths ride the cases; nothing is normalized here - the
21
+ // conformance normalizes wall-clock durations on both sides identically).
22
+ //
23
+ // SECRETS: synthetic case payloads only; .differential/ is gitignored.
24
+ import { mkdir } from 'node:fs/promises';
25
+ import { dirname, join } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { build } from 'esbuild';
28
+
29
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
30
+
31
+ const driverSource = `
32
+ import {
33
+ discoverCurrentGitProject,
34
+ defaultLocalProjectSourceRootsForTest,
35
+ } from './src/onboardingProjectDiscovery';
36
+ import {
37
+ cleanConversationTitleForTest,
38
+ discoverLocalConversations,
39
+ readLocalConversationImportMessages,
40
+ } from './src/onboardingConversationDiscovery';
41
+ import {
42
+ conversationDiscoveryPrompt,
43
+ onboardingDiscoveryAgentPlans,
44
+ onboardingDiscoveryAgentProcess,
45
+ onboardingDiscoveryAgentProcessesForTest,
46
+ projectDiscoveryPrompt,
47
+ } from './src/onboardingDiscoveryAgent';
48
+ import {
49
+ discoverCurrentGitProjectInChildProcess,
50
+ discoverLocalConversationsInChildProcess,
51
+ onboardingDiscoveryRoleEnvKey,
52
+ runOnboardingDiscoveryChildRole,
53
+ } from './src/onboardingDiscoveryChildProcess';
54
+ import {
55
+ isPlaceResponsive,
56
+ probeWorkerSourceForTest,
57
+ } from './src/onboardingPlaceResponsiveness';
58
+ import {
59
+ codexCredentialStoreFileArgs,
60
+ ensureIsolatedCodexHome,
61
+ } from './src/codexHome';
62
+ import { codexMcpConfigArgs, linzumiMcpServerConfig } from './src/mcpConfig';
63
+ import { assessDangerousCwd } from './src/safeWorkingDir';
64
+
65
+ function outcome(run) {
66
+ try {
67
+ const value = run();
68
+ return value === undefined ? { defined: false } : { defined: true, value };
69
+ } catch (error) {
70
+ return {
71
+ threw: true,
72
+ message: error instanceof Error ? error.message : String(error),
73
+ };
74
+ }
75
+ }
76
+
77
+ const savedEnv = {};
78
+
79
+ function setCaseEnv(env) {
80
+ for (const [key, value] of Object.entries(env ?? {})) {
81
+ savedEnv[key] = process.env[key];
82
+ if (value === null) {
83
+ delete process.env[key];
84
+ } else {
85
+ process.env[key] = value;
86
+ }
87
+ }
88
+ }
89
+
90
+ function restoreCaseEnv() {
91
+ for (const [key, value] of Object.entries(savedEnv)) {
92
+ if (value === undefined) {
93
+ delete process.env[key];
94
+ } else {
95
+ process.env[key] = value;
96
+ }
97
+ delete savedEnv[key];
98
+ }
99
+ }
100
+
101
+ function scriptedResponsiveness(testCase, probeCalls) {
102
+ if (testCase.unresponsivePlaces === undefined && testCase.probeTimeoutMs === undefined) {
103
+ return undefined;
104
+ }
105
+ const unresponsive = new Set(testCase.unresponsivePlaces ?? []);
106
+ return {
107
+ timeoutMs: testCase.probeTimeoutMs,
108
+ probe: (path, timeoutMs) => {
109
+ probeCalls.push({ path, timeoutMs });
110
+ return !unresponsive.has(path);
111
+ },
112
+ };
113
+ }
114
+
115
+ function handleProjectDiscovery(testCase) {
116
+ const probeCalls = [];
117
+ setCaseEnv(testCase.env);
118
+ try {
119
+ const summary = discoverCurrentGitProject({
120
+ cwd: testCase.args.cwd,
121
+ nowMs: testCase.args.nowMs ?? undefined,
122
+ homeDir: testCase.args.homeDir ?? undefined,
123
+ sourceRoots: testCase.args.sourceRoots ?? undefined,
124
+ selectedScanRoots: testCase.args.selectedScanRoots ?? undefined,
125
+ includeRuntimeCwdHint: testCase.args.includeRuntimeCwdHint ?? undefined,
126
+ sourceRootMaxDepth: testCase.args.sourceRootMaxDepth ?? undefined,
127
+ sourceRootScanBudgetMs: testCase.args.sourceRootScanBudgetMs ?? undefined,
128
+ sourceRootMaxDirectoryVisits:
129
+ testCase.args.sourceRootMaxDirectoryVisits ?? undefined,
130
+ sourceRootMaxProjectEnrichments:
131
+ testCase.args.sourceRootMaxProjectEnrichments ?? undefined,
132
+ placeResponsiveness: scriptedResponsiveness(testCase, probeCalls),
133
+ });
134
+ return { summary, probeCalls };
135
+ } finally {
136
+ restoreCaseEnv();
137
+ }
138
+ }
139
+
140
+ function handleConversationDiscovery(testCase) {
141
+ return {
142
+ summary: discoverLocalConversations({
143
+ homeDir: testCase.homeDir ?? undefined,
144
+ env: testCase.env ?? undefined,
145
+ nowMs: testCase.nowMs ?? undefined,
146
+ candidateLimit: testCase.candidateLimit ?? undefined,
147
+ }),
148
+ };
149
+ }
150
+
151
+ function handleImportMessages(testCase) {
152
+ return outcome(() =>
153
+ readLocalConversationImportMessages({
154
+ source: testCase.source,
155
+ importKey: 'oracle-import-key',
156
+ path: testCase.path,
157
+ displayName: 'oracle-display-name',
158
+ activityAt: undefined,
159
+ metadata: {},
160
+ })
161
+ );
162
+ }
163
+
164
+ // The REAL seam values the ReScript port's injected deps must carry: the
165
+ // credential-store args, the composed MCP config args, and the
166
+ // dangerous-cwd verdict - probed from the same modules the TS builder
167
+ // calls internally.
168
+ function handleAgentSeams(testCase) {
169
+ const assessment = assessDangerousCwd(testCase.cwd);
170
+ return {
171
+ credentialStoreFileArgs: codexCredentialStoreFileArgs(),
172
+ mcpConfigArgs:
173
+ testCase.mcp === undefined
174
+ ? null
175
+ : codexMcpConfigArgs(
176
+ linzumiMcpServerConfig({
177
+ command: testCase.mcp.command,
178
+ argsPrefix: testCase.mcp.argsPrefix,
179
+ kandanUrl: testCase.mcp.serviceUrl,
180
+ authFilePath: testCase.mcp.authFilePath,
181
+ ownerUsername: testCase.mcp.ownerUsername ?? undefined,
182
+ operatingMode: testCase.mcp.operatingMode,
183
+ toolScope: testCase.mcp.toolScope,
184
+ cwd: testCase.mcp.cwd,
185
+ })
186
+ ),
187
+ dangerousReason: assessment.dangerous ? assessment.reason : null,
188
+ };
189
+ }
190
+
191
+ function handleAgentProcess(testCase) {
192
+ return outcome(() =>
193
+ onboardingDiscoveryAgentProcess({
194
+ kind: testCase.kind,
195
+ workerId: testCase.workerId,
196
+ workerLabel: testCase.workerLabel,
197
+ codexBin: testCase.codexBin,
198
+ cwd: testCase.cwd,
199
+ kandanUrl: testCase.serviceUrl,
200
+ runnerId: testCase.commanderId,
201
+ workspaceSlug: testCase.workspaceSlug,
202
+ ownerUsername: testCase.ownerUsername ?? undefined,
203
+ model: testCase.model ?? undefined,
204
+ authFilePath: testCase.authFilePath,
205
+ prompt: testCase.prompt,
206
+ linzumiCommand: testCase.mcpCommand,
207
+ codexHome: testCase.codexHome,
208
+ })
209
+ );
210
+ }
211
+
212
+ function handleProcessesForTest(testCase) {
213
+ setCaseEnv(testCase.env);
214
+ try {
215
+ return outcome(() =>
216
+ onboardingDiscoveryAgentProcessesForTest({
217
+ codexBin: testCase.codexBin,
218
+ cwd: testCase.cwd,
219
+ kandanUrl: testCase.serviceUrl,
220
+ runnerId: testCase.commanderId,
221
+ workspaceSlug: testCase.workspaceSlug,
222
+ ownerUsername: testCase.ownerUsername ?? undefined,
223
+ model: testCase.model ?? undefined,
224
+ })
225
+ );
226
+ } finally {
227
+ restoreCaseEnv();
228
+ }
229
+ }
230
+
231
+ async function handleChildParent(testCase) {
232
+ const logs = [];
233
+ const log = (event, payload) => logs.push({ event, payload });
234
+ setCaseEnv(testCase.env);
235
+ try {
236
+ const deps = {
237
+ log,
238
+ timeoutMs: testCase.timeoutMs ?? undefined,
239
+ entryPath: testCase.entryPath ?? undefined,
240
+ execPath: testCase.execPath ?? undefined,
241
+ };
242
+ const summary =
243
+ testCase.kind === 'projects'
244
+ ? await discoverCurrentGitProjectInChildProcess(testCase.args ?? { cwd: process.cwd() }, deps)
245
+ : await discoverLocalConversationsInChildProcess(deps);
246
+ return { summary, logs };
247
+ } finally {
248
+ restoreCaseEnv();
249
+ }
250
+ }
251
+
252
+ function handleProbeInjected(testCase) {
253
+ const calls = [];
254
+ const result = isPlaceResponsive(testCase.path, {
255
+ timeoutMs: testCase.timeoutMs ?? undefined,
256
+ probe: (path, timeoutMs) => {
257
+ calls.push({ path, timeoutMs });
258
+ return testCase.verdict;
259
+ },
260
+ });
261
+ return { result, calls };
262
+ }
263
+
264
+ async function handle(testCase) {
265
+ switch (testCase.op) {
266
+ case 'projectDiscovery':
267
+ return handleProjectDiscovery(testCase);
268
+ case 'defaultSourceRoots':
269
+ return outcome(() =>
270
+ defaultLocalProjectSourceRootsForTest({
271
+ cwd: testCase.cwd,
272
+ homeDir: testCase.homeDir ?? undefined,
273
+ selectedScanRoots: testCase.selectedScanRoots ?? undefined,
274
+ })
275
+ );
276
+ case 'conversationDiscovery':
277
+ return handleConversationDiscovery(testCase);
278
+ case 'importMessages':
279
+ return handleImportMessages(testCase);
280
+ case 'cleanTitle':
281
+ return outcome(() =>
282
+ cleanConversationTitleForTest(testCase.value ?? undefined)
283
+ );
284
+ case 'agentPlans':
285
+ return outcome(() =>
286
+ onboardingDiscoveryAgentPlans({
287
+ cwd: testCase.cwd,
288
+ runnerId: testCase.commanderId,
289
+ workspaceSlug: testCase.workspaceSlug,
290
+ })
291
+ );
292
+ case 'projectPrompt':
293
+ return outcome(() =>
294
+ projectDiscoveryPrompt({
295
+ runnerId: testCase.commanderId,
296
+ workspaceSlug: testCase.workspaceSlug,
297
+ cwd: testCase.cwd ?? undefined,
298
+ scope: testCase.scope ?? undefined,
299
+ })
300
+ );
301
+ case 'conversationPrompt':
302
+ return outcome(() =>
303
+ conversationDiscoveryPrompt({
304
+ runnerId: testCase.commanderId,
305
+ workspaceSlug: testCase.workspaceSlug,
306
+ source: testCase.source ?? undefined,
307
+ })
308
+ );
309
+ case 'agentSeams':
310
+ return handleAgentSeams(testCase);
311
+ case 'agentProcess':
312
+ return handleAgentProcess(testCase);
313
+ case 'processesForTest':
314
+ return handleProcessesForTest(testCase);
315
+ case 'childParent':
316
+ return handleChildParent(testCase);
317
+ case 'probeSource':
318
+ return {
319
+ source: probeWorkerSourceForTest(),
320
+ roleEnvKey: onboardingDiscoveryRoleEnvKey,
321
+ };
322
+ case 'probeInjected':
323
+ return handleProbeInjected(testCase);
324
+ case 'probeReal':
325
+ return { result: isPlaceResponsive(testCase.path) };
326
+ case 'isolatedCodexHome':
327
+ setCaseEnv(testCase.env);
328
+ try {
329
+ return outcome(() => ensureIsolatedCodexHome());
330
+ } finally {
331
+ restoreCaseEnv();
332
+ }
333
+ default:
334
+ return { threw: true, message: 'unknown op ' + String(testCase.op) };
335
+ }
336
+ }
337
+
338
+ const roleValue = process.env[onboardingDiscoveryRoleEnvKey];
339
+ if (roleValue !== undefined && roleValue !== '') {
340
+ // The CLI entry's dispatch, verbatim: this bundle IS a real discovery
341
+ // child for whichever parent implementation spawned it.
342
+ process.exitCode = await runOnboardingDiscoveryChildRole(
343
+ roleValue,
344
+ process.stdin,
345
+ process.stdout
346
+ );
347
+ } else {
348
+ let stdin = '';
349
+ process.stdin.setEncoding('utf8');
350
+ for await (const chunk of process.stdin) {
351
+ stdin += chunk;
352
+ }
353
+ const cases = JSON.parse(stdin);
354
+ const results = [];
355
+ for (const testCase of cases) {
356
+ results.push(await handle(testCase));
357
+ }
358
+ process.stdout.write(JSON.stringify(results));
359
+ }
360
+ `;
361
+
362
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
363
+
364
+ await build({
365
+ stdin: {
366
+ contents: driverSource,
367
+ resolveDir: packageRoot,
368
+ sourcefile: 'onboarding-discovery-parity-driver.ts',
369
+ loader: 'ts',
370
+ },
371
+ bundle: true,
372
+ platform: 'node',
373
+ target: 'node20',
374
+ format: 'esm',
375
+ outfile: join(packageRoot, '.differential/onboarding-discovery-parity-oracle.mjs'),
376
+ minify: false,
377
+ sourcemap: false,
378
+ legalComments: 'none',
379
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
380
+ });