@linzumi/cli 1.0.117 → 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.
- package/README.md +1 -1
- package/dist/index.js +336 -336
- package/package.json +2 -1
- package/scripts/build-editor-parity-oracle.mjs +1523 -0
- package/scripts/build-forwarding-parity-oracle.mjs +1390 -0
- package/scripts/build-mcp-parity-oracle.mjs +357 -0
- package/scripts/build-mcp-selector-parity-oracle.mjs +65 -0
- package/scripts/build-vm-sandbox-parity-oracle.mjs +1266 -0
- package/scripts/build.mjs +41 -0
|
@@ -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
|
+
});
|