@linzumi/cli 1.0.118 → 1.0.119

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.
@@ -0,0 +1,357 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 14, mcp).
3
+ // Relationship: Builds the TS-side PARITY ORACLE for the mcp cluster of the
4
+ // ReScript commander port. Two artifacts:
5
+ //
6
+ // 1. .differential/mcp-parity-oracle.mjs - the REAL TS mcp modules
7
+ // (mcpConfig.ts, mcpServer.ts, mcpServerEntry.ts, linzumiApiClient.ts,
8
+ // devServiceRegistry.ts, mediaGuidance.ts) behind a one-shot stdin/stdout
9
+ // JSON batch driver. The ReScript parity conformance
10
+ // (packages/linzumi-cli-rescript/src/parity/McpParityConformance.res)
11
+ // drives the SAME inputs through the ported modules and compares
12
+ // serialized results byte for byte. The `toolSurface` op runs the REAL
13
+ // registerLinzumiMcpTools against a capturing fake server + a recording
14
+ // fetch, so tool names/descriptions/schema verdicts/handler request
15
+ // mappings are all differential.
16
+ //
17
+ // 2. .differential/mcp-server-oracle-entry.mjs - a runnable bundle of the
18
+ // REAL src/mcpServerEntry.ts, so the flow suite
19
+ // (McpLawsConformance.res) can spawn the TS MCP server as a stdio child
20
+ // next to the ReScript entry and assert byte-equal JSON-RPC traffic,
21
+ // stdout/stderr, and scripted-backend requests.
22
+ //
23
+ // Like the sibling oracles, both outputs are local test artifacts
24
+ // (.differential/ is gitignored, never published) and stay unminified for
25
+ // debuggability.
26
+ import { mkdir } from 'node:fs/promises';
27
+ import { dirname, join } from 'node:path';
28
+ import { fileURLToPath } from 'node:url';
29
+ import { build } from 'esbuild';
30
+
31
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
32
+
33
+ const driverSource = `
34
+ import {
35
+ claudeCodeMcpConfigJson,
36
+ codexMcpConfigArgs,
37
+ codexMcpConfigToml,
38
+ linzumiMcpCommandForProcess,
39
+ linzumiMcpServerConfig,
40
+ } from './src/mcpConfig';
41
+ import {
42
+ absolutizeAttachmentUrls,
43
+ customEmojiNameSchema,
44
+ devServiceStatusJson,
45
+ devServicesOptionsFromEnv,
46
+ mcpHelpText,
47
+ mcpJsonResult,
48
+ mcpOperatingMode,
49
+ mcpToolScope,
50
+ normalizeCustomEmojiNameForSchema,
51
+ paramsWithDefaultThread,
52
+ registerLinzumiMcpTools,
53
+ resolveLinzumiUploadUrl,
54
+ strictFlagValues,
55
+ targetWithDefaultThread,
56
+ textModeGatedMessagePostingMcpTools,
57
+ uiControlToolResult,
58
+ withUploadChannelRemediation,
59
+ } from './src/mcpServer';
60
+ import { createLinzumiMcpApiClient } from './src/linzumiApiClient';
61
+ import {
62
+ devServiceLogPath,
63
+ devServiceWatchdogProgram,
64
+ devServiceWatchdogSpawn,
65
+ devServicesScopeDir,
66
+ normalizeDevServiceName,
67
+ parseDevServiceRecord,
68
+ } from './src/devServiceRegistry';
69
+ import { linzumiMarkdownMediaGuidance } from './src/mediaGuidance';
70
+ import { z } from 'zod/v3';
71
+
72
+ // mcpServerEntry.ts auto-runs when import.meta.url matches argv[1] - true for
73
+ // this bundle when node runs it directly. Import it DYNAMICALLY behind an
74
+ // argv[1] guard so the real module's direct-invocation gate stays untouched
75
+ // but cannot fire inside the oracle.
76
+ const argv1ForEntryImport = process.argv[1];
77
+ process.argv[1] = '/linzumi-mcp-parity-oracle-guard';
78
+ const { mcpServerEntryArgs } = await import('./src/mcpServerEntry');
79
+ process.argv[1] = argv1ForEntryImport;
80
+
81
+ // Encode a call outcome so undefined stays distinguishable from null after
82
+ // JSON serialization.
83
+ function outcome(run) {
84
+ try {
85
+ const value = run();
86
+ return value === undefined ? { defined: false } : { defined: true, value };
87
+ } catch (error) {
88
+ return { threw: true, message: error instanceof Error ? error.message : String(error) };
89
+ }
90
+ }
91
+
92
+ async function asyncOutcome(run) {
93
+ try {
94
+ const value = await run();
95
+ return value === undefined ? { defined: false } : { defined: true, value };
96
+ } catch (error) {
97
+ return { threw: true, message: error instanceof Error ? error.message : String(error) };
98
+ }
99
+ }
100
+
101
+ function safeParseVerdict(schema, input) {
102
+ const parsed = schema.safeParse(input);
103
+ return parsed.success
104
+ ? { success: true, data: parsed.data === undefined ? null : parsed.data }
105
+ : {
106
+ success: false,
107
+ issues: JSON.parse(JSON.stringify(parsed.error.issues)),
108
+ };
109
+ }
110
+
111
+ // A recording fetch for the api-client seam: every request is captured and
112
+ // answered from the scripted queue (default {ok: true}).
113
+ function recordingFetch(script) {
114
+ const requests = [];
115
+ const responses = [...(script?.responses ?? [])];
116
+ const fetchImpl = async (url, init) => {
117
+ requests.push({
118
+ url: String(url),
119
+ method: init?.method ?? 'GET',
120
+ authorization: init?.headers?.authorization ?? null,
121
+ contentType: init?.headers?.['content-type'] ?? null,
122
+ body: typeof init?.body === 'string' ? init.body : null,
123
+ });
124
+ const next = responses.shift() ?? { status: 200, body: { ok: true } };
125
+ return new Response(JSON.stringify(next.body), {
126
+ status: next.status ?? 200,
127
+ headers: { 'content-type': 'application/json' },
128
+ });
129
+ };
130
+ return { requests, fetchImpl };
131
+ }
132
+
133
+ async function toolSurface(testCase) {
134
+ const captured = [];
135
+ const handlers = new Map();
136
+ const fakeServer = {
137
+ tool(name, description, shape, handler) {
138
+ captured.push({ name, description, schemaKeys: Object.keys(shape) });
139
+ handlers.set(name, { shape, handler });
140
+ },
141
+ };
142
+ const { requests, fetchImpl } = recordingFetch(testCase.script);
143
+ const client = createLinzumiMcpApiClient({
144
+ kandanUrl: testCase.options.apiUrl,
145
+ accessToken: 'synthetic-parity-token',
146
+ authMode: testCase.options.authMode,
147
+ operatingMode: testCase.options.operatingMode,
148
+ fetchImpl,
149
+ });
150
+ registerLinzumiMcpTools(fakeServer, {
151
+ client,
152
+ toolScope: testCase.options.toolScope,
153
+ operatingMode: testCase.options.operatingMode,
154
+ cwd: testCase.options.cwd,
155
+ kandanUrl: testCase.options.apiUrl,
156
+ defaultThreadId: testCase.options.defaultThreadId ?? undefined,
157
+ ownerUsername: testCase.options.ownerUsername ?? undefined,
158
+ agentSession: testCase.options.agentSession === true,
159
+ devServices:
160
+ testCase.options.devServices === undefined || testCase.options.devServices === null
161
+ ? undefined
162
+ : testCase.options.devServices,
163
+ });
164
+
165
+ const probes = [];
166
+ for (const probe of testCase.probes ?? []) {
167
+ const entry = handlers.get(probe.tool);
168
+ probes.push(
169
+ entry === undefined
170
+ ? { tool: probe.tool, missing: true }
171
+ : { tool: probe.tool, verdict: safeParseVerdict(z.object(entry.shape), probe.input) }
172
+ );
173
+ }
174
+
175
+ const calls = [];
176
+ for (const call of testCase.calls ?? []) {
177
+ const entry = handlers.get(call.tool);
178
+ if (entry === undefined) {
179
+ calls.push({ tool: call.tool, missing: true });
180
+ continue;
181
+ }
182
+ const before = requests.length;
183
+ const result = await asyncOutcome(() => entry.handler(call.params));
184
+ calls.push({ tool: call.tool, result, requests: requests.slice(before) });
185
+ }
186
+
187
+ return { tools: captured, probes, calls };
188
+ }
189
+
190
+ async function handle(testCase) {
191
+ switch (testCase.op) {
192
+ case 'serverConfig': {
193
+ const config = outcome(() => linzumiMcpServerConfig(testCase.options));
194
+ if (config.threw === true || config.defined !== true) {
195
+ return { config };
196
+ }
197
+ return {
198
+ config,
199
+ codexArgs: outcome(() => codexMcpConfigArgs(config.value)),
200
+ codexToml: outcome(() => codexMcpConfigToml(config.value)),
201
+ claudeCodeJson: outcome(() => claudeCodeMcpConfigJson(config.value)),
202
+ };
203
+ }
204
+ case 'commandForProcess':
205
+ return outcome(() =>
206
+ linzumiMcpCommandForProcess(
207
+ testCase.processExecPath,
208
+ testCase.scriptPath ?? undefined,
209
+ testCase.execArgv ?? []
210
+ )
211
+ );
212
+ case 'strictFlags':
213
+ return outcome(() => [...strictFlagValues(testCase.args).entries()]);
214
+ case 'helpText':
215
+ return mcpHelpText();
216
+ case 'mediaGuidance':
217
+ return linzumiMarkdownMediaGuidance;
218
+ case 'entryArgs':
219
+ return mcpServerEntryArgs(testCase.argv);
220
+ case 'operatingMode':
221
+ return outcome(() => mcpOperatingMode(testCase.value ?? undefined));
222
+ case 'toolScope':
223
+ return outcome(() => mcpToolScope(testCase.value ?? undefined));
224
+ case 'textModeGatedTools':
225
+ return [...textModeGatedMessagePostingMcpTools];
226
+ case 'mcpJsonResult':
227
+ return mcpJsonResult(testCase.value);
228
+ case 'uiControlResult':
229
+ return await asyncOutcome(() =>
230
+ uiControlToolResult(
231
+ testCase.throwMessage === undefined || testCase.throwMessage === null
232
+ ? Promise.resolve(testCase.value)
233
+ : Promise.reject(new Error(testCase.throwMessage))
234
+ )
235
+ );
236
+ case 'uploadRemediation': {
237
+ const mapped = withUploadChannelRemediation(new Error(testCase.message));
238
+ return {
239
+ message: mapped instanceof Error ? mapped.message : String(mapped),
240
+ causeMessage:
241
+ mapped instanceof Error && mapped.cause instanceof Error
242
+ ? mapped.cause.message
243
+ : null,
244
+ };
245
+ }
246
+ case 'targetWithDefaultThread':
247
+ return targetWithDefaultThread(testCase.target, testCase.defaultThreadId ?? undefined);
248
+ case 'paramsWithDefaultThread':
249
+ return paramsWithDefaultThread(testCase.params, testCase.defaultThreadId ?? undefined);
250
+ case 'resolveUploadUrl':
251
+ return outcome(() => resolveLinzumiUploadUrl(testCase.apiUrl, testCase.uploadUrl));
252
+ case 'absolutize':
253
+ return outcome(() => absolutizeAttachmentUrls(testCase.apiUrl, testCase.result));
254
+ case 'emojiNormalize':
255
+ return normalizeCustomEmojiNameForSchema(testCase.value);
256
+ case 'emojiSchemaVerdict':
257
+ return safeParseVerdict(customEmojiNameSchema, testCase.value);
258
+ case 'devServicesOptionsFromEnv':
259
+ return outcome(() => devServicesOptionsFromEnv(testCase.env));
260
+ case 'devServicesScopeDir':
261
+ return devServicesScopeDir(testCase.scope);
262
+ case 'normalizeDevServiceName':
263
+ return outcome(() => normalizeDevServiceName(testCase.name));
264
+ case 'devServiceLogPath':
265
+ return devServiceLogPath(testCase.scopeDir, testCase.name);
266
+ case 'parseDevServiceRecord':
267
+ return outcome(() => parseDevServiceRecord(testCase.value));
268
+ case 'watchdogProgram':
269
+ return devServiceWatchdogProgram();
270
+ case 'watchdogSpawn':
271
+ return devServiceWatchdogSpawn(testCase.nodeExecPath, testCase.config);
272
+ case 'devServiceStatusJson':
273
+ return devServiceStatusJson({
274
+ record: testCase.status.record,
275
+ liveness: testCase.status.liveness,
276
+ portProbes: testCase.status.portProbes,
277
+ lastExit: testCase.status.lastExit ?? undefined,
278
+ });
279
+ case 'toolSurface':
280
+ return await toolSurface(testCase);
281
+ case 'clientCall': {
282
+ // One api-client method driven directly (the full method->endpoint
283
+ // map, including the runner-internal endpoints no MCP tool calls).
284
+ const { requests, fetchImpl } = recordingFetch(testCase.script);
285
+ const client = createLinzumiMcpApiClient({
286
+ kandanUrl: testCase.apiUrl,
287
+ accessToken: 'synthetic-parity-token',
288
+ authMode: testCase.authMode ?? undefined,
289
+ operatingMode: testCase.operatingMode ?? undefined,
290
+ fetchImpl,
291
+ });
292
+ const method = client[testCase.method];
293
+ if (typeof method !== 'function') {
294
+ return { threw: true, message: 'unknown client method ' + testCase.method };
295
+ }
296
+ const result = await asyncOutcome(() => method(testCase.params));
297
+ return { result, requests };
298
+ }
299
+ default:
300
+ return { threw: true, message: 'unknown op ' + String(testCase.op) };
301
+ }
302
+ }
303
+
304
+ let stdin = '';
305
+ process.stdin.setEncoding('utf8');
306
+ for await (const chunk of process.stdin) {
307
+ stdin += chunk;
308
+ }
309
+ const cases = JSON.parse(stdin);
310
+ const results = [];
311
+ for (const testCase of cases) {
312
+ results.push(await handle(testCase));
313
+ }
314
+ process.stdout.write(JSON.stringify(results));
315
+ `;
316
+
317
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
318
+
319
+ const banner = {
320
+ js: "import { createRequire } from 'node:module';\nconst require = createRequire(import.meta.url);",
321
+ };
322
+
323
+ await build({
324
+ stdin: {
325
+ contents: driverSource,
326
+ resolveDir: packageRoot,
327
+ sourcefile: 'mcp-parity-driver.ts',
328
+ loader: 'ts',
329
+ },
330
+ bundle: true,
331
+ platform: 'node',
332
+ target: 'node20',
333
+ format: 'esm',
334
+ outfile: join(packageRoot, '.differential/mcp-parity-oracle.mjs'),
335
+ minify: false,
336
+ sourcemap: false,
337
+ legalComments: 'none',
338
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
339
+ banner,
340
+ });
341
+
342
+ // The runnable REAL TS MCP server entry, for the flow-parity legs: same code
343
+ // path as `linzumi mcp ...` (mcpServerEntry strips the leading 'mcp' and
344
+ // dispatches through runMcpCommand), bundled unminified like the driver.
345
+ await build({
346
+ entryPoints: [join(packageRoot, 'src/mcpServerEntry.ts')],
347
+ bundle: true,
348
+ platform: 'node',
349
+ target: 'node20',
350
+ format: 'esm',
351
+ outfile: join(packageRoot, '.differential/mcp-server-oracle-entry.mjs'),
352
+ minify: false,
353
+ sourcemap: false,
354
+ legalComments: 'none',
355
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
356
+ banner,
357
+ });
@@ -0,0 +1,65 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 14, mcp-editor-forwarding-vm-sandbox - the explicit
3
+ // mcp-server.mjs selector story).
4
+ // Relationship: Builds the TS-side PARITY ORACLE for the mcp-server.mjs
5
+ // dual-impl selector seam. The oracle bundles the REAL
6
+ // mcpServerBundleCandidates ladder (src/vmSandbox/startVmCodexAppServer.ts,
7
+ // the guest-injection selection-by-co-location seam) behind a one-shot
8
+ // batch driver: a JSON array of cases on stdin, a JSON array of results on
9
+ // stdout. The ReScript package's selector conformance
10
+ // (packages/linzumi-cli-rescript/src/parity/McpSelectorConformance.res)
11
+ // drives the SAME cases through CommanderMcpBundleSelection and asserts
12
+ // the candidate ladders byte-equal - the two impls' selectors cannot
13
+ // drift while both ship. Post-M4.3 the ladder collapses to its bare
14
+ // ['mcp-server.mjs'] tail and this oracle retires with the TS impl.
15
+ //
16
+ // Like the other parity oracles, the output is a local test artifact
17
+ // (.differential/ is gitignored, never published) and stays unminified for
18
+ // debuggability.
19
+ import { mkdir } from 'node:fs/promises';
20
+ import { dirname, join } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ import { build } from 'esbuild';
23
+
24
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
25
+
26
+ const driverSource = `
27
+ import { mcpServerBundleCandidates } from './src/vmSandbox/startVmCodexAppServer';
28
+
29
+ function handle(testCase) {
30
+ switch (testCase.op) {
31
+ case 'candidates':
32
+ return { candidates: mcpServerBundleCandidates(testCase.impl) };
33
+ default:
34
+ return { threw: true, message: 'unknown op ' + String(testCase.op) };
35
+ }
36
+ }
37
+
38
+ let stdin = '';
39
+ process.stdin.setEncoding('utf8');
40
+ for await (const chunk of process.stdin) {
41
+ stdin += chunk;
42
+ }
43
+ const cases = JSON.parse(stdin);
44
+ process.stdout.write(JSON.stringify(cases.map(handle)));
45
+ `;
46
+
47
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
48
+
49
+ await build({
50
+ stdin: {
51
+ contents: driverSource,
52
+ resolveDir: packageRoot,
53
+ sourcefile: 'mcp-selector-parity-driver.ts',
54
+ loader: 'ts',
55
+ },
56
+ bundle: true,
57
+ platform: 'node',
58
+ target: 'node20',
59
+ format: 'esm',
60
+ outfile: join(packageRoot, '.differential/mcp-selector-parity-oracle.mjs'),
61
+ minify: false,
62
+ sourcemap: false,
63
+ legalComments: 'none',
64
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
65
+ });