@linzumi/cli 1.0.275 → 1.0.276
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 +10 -9
- package/dist/{impl-ts/cloud-supervisor.mjs → cloud-supervisor.mjs} +78 -78
- package/dist/hosted-coding-account-worker.js +25 -0
- package/dist/index.js +1609 -3
- package/dist/mcp-server.mjs +174 -168
- package/dist/{impl-res/native → native}/darwin/manifest.json +5 -2
- package/dist/remote-codex-harness-worker.js +757 -0
- package/dist/tcp-tunnel-cli.mjs +25 -69
- package/dist/topics-cli.mjs +137 -13
- package/package.json +9 -16
- package/scripts/build.mjs +33 -356
- package/scripts/bump-cli-version.mjs +35 -9
- package/scripts/check-package-size-budget.mjs +13 -61
- package/dist/impl-res/cloud-supervisor.mjs +0 -281
- package/dist/impl-res/index.js +0 -1546
- package/dist/impl-res/mcp-server.mjs +0 -461
- package/dist/impl-ts/assets/linzumi-logo.svg +0 -1
- package/dist/impl-ts/crossHarnessFailover.mjs +0 -14
- package/dist/impl-ts/index.js +0 -2617
- package/dist/impl-ts/mcp-server.mjs +0 -455
- package/dist/impl-ts/native/darwin/manifest.json +0 -21
- package/dist/impl-ts/native/darwin/preview-process-identity +0 -0
- package/dist/impl-ts/tcp-tunnel-cli.mjs +0 -93
- package/scripts/build-differential-entry-impl-res.mjs +0 -64
- package/scripts/build-differential-entry.mjs +0 -180
- package/scripts/build-onboarding-demo-evidence.mjs +0 -186
- package/scripts/build-tui-demo-evidence.mjs +0 -107
- package/scripts/diagnose-claude-probe.ts +0 -198
- package/scripts/differentialMutations.mjs +0 -114
- package/scripts/f5_live_cross_harness_failover_e2e.mjs +0 -239
- package/scripts/golden-chaos-harness.mjs +0 -1420
- package/scripts/normalize-agent-replay-fixture.ts +0 -281
- package/scripts/promote-gstack-claude-smoke-fixture.ts +0 -891
- package/scripts/record-agent-replay-fixtures.ts +0 -610
- package/scripts/replay-agent-backend-browser.ts +0 -198
- package/scripts/replay-agent-backend.ts +0 -1961
- package/scripts/replay-agent-fixture.ts +0 -326
- package/scripts/typecheck.mjs +0 -42
- /package/dist/{impl-res/assets → assets}/linzumi-logo.svg +0 -0
- /package/dist/{impl-res/crossHarnessFailover.mjs → crossHarnessFailover.mjs} +0 -0
- /package/dist/{impl-res/native → native}/darwin/preview-process-identity +0 -0
package/scripts/build.mjs
CHANGED
|
@@ -114,375 +114,52 @@ if (
|
|
|
114
114
|
)
|
|
115
115
|
throw new Error('preview_identity_packaged_asset_invalid');
|
|
116
116
|
|
|
117
|
+
// Spec: spec/single-commander.md. Required ReScript assets fail the build.
|
|
117
118
|
const { build } = await import('esbuild');
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// is a mandatory ReScript build input, not a fail-soft optional rung.
|
|
121
|
-
const tcpTunnelCliEntry = join(
|
|
122
|
-
packageRoot,
|
|
123
|
-
'../linzumi-cli-rescript/src/forwarding/CommanderTcpTunnelCli.res.mjs'
|
|
124
|
-
);
|
|
125
|
-
await access(tcpTunnelCliEntry);
|
|
126
|
-
|
|
127
|
-
// M3 cluster 15: the ReScript package ships no version literal (repo rule);
|
|
128
|
-
// the CLI version is esbuild-`define`d into the impl-res bundle as
|
|
129
|
-
// process.env.LINZUMI_CLI_VERSION (read via CommanderEntryVersion.res), the
|
|
130
|
-
// same source of truth as src/version.ts linzumiCliVersion.
|
|
131
|
-
const packageVersion = JSON.parse(
|
|
132
|
-
await readFile(join(packageRoot, 'package.json'), 'utf8')
|
|
133
|
-
).version;
|
|
134
|
-
|
|
135
|
-
await rm(join(packageRoot, 'dist'), { recursive: true, force: true });
|
|
136
|
-
for (const implementation of ['impl-ts', 'impl-res']) {
|
|
137
|
-
const destination = join(
|
|
138
|
-
packageRoot,
|
|
139
|
-
'dist',
|
|
140
|
-
implementation,
|
|
141
|
-
'native/darwin'
|
|
142
|
-
);
|
|
143
|
-
await mkdir(destination, { recursive: true });
|
|
144
|
-
await copyFile(identityAsset, join(destination, 'preview-process-identity'));
|
|
145
|
-
await chmod(join(destination, 'preview-process-identity'), 0o755);
|
|
146
|
-
await copyFile(identityManifestPath, join(destination, 'manifest.json'));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Renamed banner import (the build-auth-parity-oracle.mjs convention, made
|
|
150
|
-
// load-bearing by prod RCA 2026-07-20): esbuild injects banners VERBATIM -
|
|
151
|
-
// outside symbol renaming - so a bare `createRequire` here collides with any
|
|
152
|
-
// source module's own top-level `import { createRequire } from 'node:module'`
|
|
153
|
-
// (bundledCodexBin.ts) and the bundle dies at load with "Identifier
|
|
154
|
-
// 'createRequire' has already been declared". These bundles are currently
|
|
155
|
-
// minified (which renames the source-side binding and masks the collision),
|
|
156
|
-
// but the identically-bannered UNMINIFIED nix image bundles shipped exactly
|
|
157
|
-
// that SyntaxError on 2026-07-11 and crash-looped the hosted @linzumi runner
|
|
158
|
-
// for 9 days. Keep the banner symbol collision-proof, never rely on minify.
|
|
119
|
+
const packageVersion = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8')).version;
|
|
120
|
+
const commanderSource = join(packageRoot, '../linzumi-cli-rescript/src');
|
|
159
121
|
const banner = {
|
|
160
122
|
js: "import { createRequire as __linzumiBundleCreateRequire } from 'node:module';\nconst require = __linzumiBundleCreateRequire(import.meta.url);",
|
|
161
123
|
};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
platform: 'node',
|
|
178
|
-
target: 'node20',
|
|
179
|
-
format: 'esm',
|
|
180
|
-
outfile: join(packageRoot, 'dist/index.js'),
|
|
181
|
-
minify: true,
|
|
182
|
-
sourcemap: false,
|
|
183
|
-
legalComments: 'none',
|
|
184
|
-
// The impl bundles are runtime dynamic imports the chooser selects between;
|
|
185
|
-
// they must never be inlined into the chooser bundle.
|
|
186
|
-
external: ['./impl-ts/*', './impl-res/*'],
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
await build({
|
|
190
|
-
entryPoints: [join(packageRoot, 'src/index.ts')],
|
|
191
|
-
bundle: true,
|
|
192
|
-
platform: 'node',
|
|
193
|
-
target: 'node20',
|
|
194
|
-
format: 'esm',
|
|
195
|
-
outfile: join(packageRoot, 'dist/impl-ts/index.js'),
|
|
196
|
-
// Production hardening: minify the published CLI bundle and ship no source
|
|
197
|
-
// map. We already bundle to a single impl bundle (no loose src/); minifying
|
|
198
|
-
// reduces accidental source/IP leakage in the npm artifact. This is NOT a
|
|
199
|
-
// security boundary -- the published package is public by definition, so no
|
|
200
|
-
// secret may live in this code and all authz stays server-side.
|
|
201
|
-
minify: true,
|
|
202
|
-
sourcemap: false,
|
|
203
|
-
legalComments: 'none',
|
|
204
|
-
external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
|
|
205
|
-
banner,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
await build({
|
|
209
|
-
entryPoints: [tcpTunnelCliEntry],
|
|
210
|
-
bundle: true,
|
|
211
|
-
platform: 'node',
|
|
212
|
-
target: 'node20',
|
|
213
|
-
format: 'esm',
|
|
214
|
-
outfile: join(packageRoot, 'dist/tcp-tunnel-cli.mjs'),
|
|
215
|
-
minify: true,
|
|
216
|
-
sourcemap: false,
|
|
217
|
-
legalComments: 'none',
|
|
218
|
-
external: ['ws'],
|
|
219
|
-
banner,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// The in-guest Linzumi MCP bridge: a self-contained MCP server bundle that the
|
|
223
|
-
// VM Codex supervisor injects into a coding job's microVM and runs with a plain
|
|
224
|
-
// `node`. Unlike dist/index.js it bundles EVERYTHING (no `external`), so the
|
|
225
|
-
// guest needs no extra npm packages - only Node, which provisioning already
|
|
226
|
-
// installs. The MCP server path uses only the bundled @modelcontextprotocol/sdk
|
|
227
|
-
// and global fetch; it never touches ws/undici/blessed/claude-agent-sdk, so
|
|
228
|
-
// bundling with no externals stays lean (Spec:
|
|
229
|
-
// plans/2026-06-12-local-vm-codex-execution-phase1.md in-guest MCP bridge).
|
|
230
|
-
// Output extension is .mjs (not .js): the bundle is ESM (banner createRequire +
|
|
231
|
-
// a node:url built-in import are top-level `import` statements). In the guest it
|
|
232
|
-
// lives at /opt/linzumi/ next to the CJS relay.js with no package.json in scope,
|
|
233
|
-
// so a `.js` would be treated as CommonJS on Node versions without ESM-syntax
|
|
234
|
-
// detection and throw "Cannot use import statement outside a module". A
|
|
235
|
-
// `package.json {"type":"module"}` marker would fix mcp-server but BREAK the CJS
|
|
236
|
-
// relay.js at the same path, so the unambiguous `.mjs` extension is the correct
|
|
237
|
-
// fix.
|
|
238
|
-
await build({
|
|
239
|
-
entryPoints: [join(packageRoot, 'src/mcpServerEntry.ts')],
|
|
240
|
-
bundle: true,
|
|
241
|
-
platform: 'node',
|
|
242
|
-
target: 'node20',
|
|
243
|
-
format: 'esm',
|
|
244
|
-
outfile: join(packageRoot, 'dist/mcp-server.mjs'),
|
|
245
|
-
minify: true,
|
|
246
|
-
sourcemap: false,
|
|
247
|
-
legalComments: 'none',
|
|
248
|
-
banner,
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// M3 cluster 15: the TS impl's guest MCP bridge also ships CO-LOCATED at
|
|
252
|
-
// dist/impl-ts/mcp-server.mjs (the 'ts' candidate ladder's impl-specific
|
|
253
|
-
// head; the flat dist/mcp-server.mjs above stays as the rollback backstop
|
|
254
|
-
// and the guest path contract). Same bytes, copied - never a second build.
|
|
255
|
-
await mkdir(join(packageRoot, 'dist/impl-ts'), { recursive: true });
|
|
256
|
-
await copyFile(
|
|
257
|
-
join(packageRoot, 'dist/tcp-tunnel-cli.mjs'),
|
|
258
|
-
join(packageRoot, 'dist/impl-ts/tcp-tunnel-cli.mjs')
|
|
259
|
-
);
|
|
260
|
-
await copyFile(
|
|
261
|
-
join(packageRoot, 'dist/mcp-server.mjs'),
|
|
262
|
-
join(packageRoot, 'dist/impl-ts/mcp-server.mjs')
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
// M3 cluster 14 selector story (compute-nodes program, M3 packaging): the
|
|
266
|
-
// ReScript impl's guest MCP bridge, bundled from the compiled
|
|
267
|
-
// CommanderMcpEntry artifact into dist/impl-res/mcp-server.mjs - the head
|
|
268
|
-
// of the 'rescript' candidate ladder (mcpServerBundleCandidates in
|
|
269
|
-
// src/vmSandbox/startVmCodexAppServer.ts / CommanderMcpBundleSelection.res).
|
|
270
|
-
// Same no-externals rationale as dist/mcp-server.mjs above: the guest gets
|
|
271
|
-
// ONE file and a bare `node`. Like the F5 cross-harness bridge, the
|
|
272
|
-
// ReScript artifacts may not be built in this checkout (publish CI builds
|
|
273
|
-
// only @linzumi/cli); the selector ladder is fail-soft by design - a
|
|
274
|
-
// missing impl-res bundle falls back to the flat TS bundle - so the honest
|
|
275
|
-
// posture here is warn-and-skip, never a broken stub. Post-M4.3 this leg
|
|
276
|
-
// BECOMES the dist/mcp-server.mjs build and the TS leg above is deleted
|
|
277
|
-
// with the TS impl.
|
|
278
|
-
try {
|
|
279
|
-
await build({
|
|
280
|
-
entryPoints: [
|
|
281
|
-
join(
|
|
282
|
-
packageRoot,
|
|
283
|
-
'../linzumi-cli-rescript/src/mcp/CommanderMcpEntry.res.mjs'
|
|
284
|
-
),
|
|
285
|
-
],
|
|
286
|
-
bundle: true,
|
|
287
|
-
platform: 'node',
|
|
288
|
-
target: 'node20',
|
|
289
|
-
format: 'esm',
|
|
290
|
-
outfile: join(packageRoot, 'dist/impl-res/mcp-server.mjs'),
|
|
291
|
-
minify: true,
|
|
292
|
-
sourcemap: false,
|
|
293
|
-
legalComments: 'none',
|
|
294
|
-
banner,
|
|
295
|
-
});
|
|
296
|
-
} catch (error) {
|
|
297
|
-
console.warn(
|
|
298
|
-
'[build] impl-res mcp-server bundle could not be built (are the ' +
|
|
299
|
-
'linzumi-cli-rescript artifacts compiled?); the selector ladder falls ' +
|
|
300
|
-
`back to the flat dist/mcp-server.mjs: ${
|
|
301
|
-
error instanceof Error ? error.message : String(error)
|
|
302
|
-
}`
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Long-lived topics producer (spec/topics-rules.md :topics/first-producers-v1
|
|
307
|
-
// slice a): `linzumi topics post` is ReScript per the zero-TS program
|
|
308
|
-
// (CommanderTopicsCli.res); bundle it beside the flat CLI bundle so the
|
|
309
|
-
// dispatcher's `new URL('./topics-cli.mjs', import.meta.url)` resolves at
|
|
310
|
-
// runtime. Publish CI compiles @linzumi/cli-rescript before pack, so real
|
|
311
|
-
// releases always carry it; a checkout without the compiled artifacts gets
|
|
312
|
-
// the same warn-and-skip posture as the mcp bundle above (the dispatcher
|
|
313
|
-
// then falls back to the package-source candidate, and failing that prints
|
|
314
|
-
// an honest "unavailable in this build" refusal).
|
|
315
|
-
try {
|
|
316
|
-
await build({
|
|
317
|
-
entryPoints: [
|
|
318
|
-
join(
|
|
319
|
-
packageRoot,
|
|
320
|
-
'../linzumi-cli-rescript/src/topics/CommanderTopicsCli.res.mjs'
|
|
321
|
-
),
|
|
322
|
-
],
|
|
323
|
-
bundle: true,
|
|
324
|
-
platform: 'node',
|
|
325
|
-
target: 'node20',
|
|
326
|
-
format: 'esm',
|
|
327
|
-
outfile: join(packageRoot, 'dist/topics-cli.mjs'),
|
|
328
|
-
minify: true,
|
|
329
|
-
sourcemap: false,
|
|
330
|
-
legalComments: 'none',
|
|
331
|
-
banner,
|
|
332
|
-
});
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.warn(
|
|
335
|
-
'[build] topics-cli bundle could not be built (are the ' +
|
|
336
|
-
'linzumi-cli-rescript artifacts compiled?); `linzumi topics post` ' +
|
|
337
|
-
`falls back to the package-source candidate: ${
|
|
338
|
-
error instanceof Error ? error.message : String(error)
|
|
339
|
-
}`
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// The in-guest CLOUD turn driver: a self-contained dist/cloud-supervisor.mjs
|
|
344
|
-
// bundle that vmCloudSupervisor injects onto the persistent overlay of a
|
|
345
|
-
// workspace-agent smolcloud VM and runs with a bare `node` (the guest's
|
|
346
|
-
// node:22-bookworm-slim has node, but no npm packages beyond what provisioning
|
|
347
|
-
// installs globally). Unlike dist/index.js it bundles EVERYTHING (no
|
|
348
|
-
// `external`) so the guest needs no extra packages. It uses `ws` (bundled) for
|
|
349
|
-
// the guest-loopback codex websocket. Same .mjs rationale as mcp-server.mjs:
|
|
350
|
-
// the bundle is ESM (banner createRequire + node:* built-in imports) and lives
|
|
351
|
-
// next to CJS files with no package.json in scope, so the explicit .mjs makes
|
|
352
|
-
// Node treat it as ESM regardless of Node version.
|
|
353
|
-
await build({
|
|
354
|
-
entryPoints: [join(packageRoot, 'src/cloudSupervisorEntry.ts')],
|
|
355
|
-
bundle: true,
|
|
356
|
-
platform: 'node',
|
|
357
|
-
target: 'node20',
|
|
358
|
-
format: 'esm',
|
|
359
|
-
outfile: join(packageRoot, 'dist/impl-ts/cloud-supervisor.mjs'),
|
|
360
|
-
minify: true,
|
|
361
|
-
sourcemap: false,
|
|
362
|
-
legalComments: 'none',
|
|
363
|
-
banner,
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// The in-guest bundles are impl-AGNOSTIC guest artifacts (they run inside the
|
|
367
|
-
// VM, not inside either commander), so the impl-res dist carries the same
|
|
368
|
-
// bytes: its vmCloudSupervisor twin resolves siblings of ITS bundle exactly
|
|
369
|
-
// like the TS one does (resolveDistBundle / dirname(import.meta.url)).
|
|
370
|
-
await mkdir(join(packageRoot, 'dist/impl-res'), { recursive: true });
|
|
371
|
-
await copyFile(
|
|
372
|
-
join(packageRoot, 'dist/impl-ts/cloud-supervisor.mjs'),
|
|
373
|
-
join(packageRoot, 'dist/impl-res/cloud-supervisor.mjs')
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
// F5 cross-harness failover bridge (Workstream F task F5): the runner
|
|
377
|
-
// dynamic-imports ./crossHarnessFailover.mjs relative to its own bundle, so
|
|
378
|
-
// dist ships a self-contained sibling bundle with the compiled ReScript
|
|
379
|
-
// transpiler artifacts (packages/linzumi-cli-rescript, built by `rescript`)
|
|
380
|
-
// resolved at build time. When the artifacts are not built in this checkout
|
|
381
|
-
// the publish must not silently ship a broken rung: emit an HONEST stub
|
|
382
|
-
// whose import succeeds but whose executor refuses loudly - the ladder then
|
|
383
|
-
// terminates red-with-receipts exactly as F2 (the flag's rollback posture) -
|
|
384
|
-
// and warn in the build log.
|
|
385
|
-
try {
|
|
124
|
+
await rm(join(packageRoot, 'dist'), { recursive: true, force: true });
|
|
125
|
+
await mkdir(join(packageRoot, 'dist/native/darwin'), { recursive: true });
|
|
126
|
+
await copyFile(identityAsset, join(packageRoot, 'dist/native/darwin/preview-process-identity'));
|
|
127
|
+
await chmod(join(packageRoot, 'dist/native/darwin/preview-process-identity'), 0o755);
|
|
128
|
+
await copyFile(identityManifestPath, join(packageRoot, 'dist/native/darwin/manifest.json'));
|
|
129
|
+
await mkdir(join(packageRoot, 'dist/assets'), { recursive: true });
|
|
130
|
+
await copyFile(join(packageRoot, 'src/assets/linzumi-logo.svg'), join(packageRoot, 'dist/assets/linzumi-logo.svg'));
|
|
131
|
+
|
|
132
|
+
for (const [source, output, external] of [
|
|
133
|
+
['entry/CommanderEntryMain.res.mjs', 'index.js', ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk']],
|
|
134
|
+
['mcp/CommanderMcpEntry.res.mjs', 'mcp-server.mjs', []],
|
|
135
|
+
['worker/CommanderHostedAccountWorker.res.mjs', 'hosted-coding-account-worker.js', []],
|
|
136
|
+
['topics/CommanderTopicsCli.res.mjs', 'topics-cli.mjs', []],
|
|
137
|
+
['forwarding/CommanderTcpTunnelCli.res.mjs', 'tcp-tunnel-cli.mjs', ['ws', 'undici']],
|
|
138
|
+
]) {
|
|
386
139
|
await build({
|
|
387
|
-
entryPoints: [join(
|
|
140
|
+
entryPoints: [join(commanderSource, source)],
|
|
388
141
|
bundle: true,
|
|
389
142
|
platform: 'node',
|
|
390
143
|
target: 'node20',
|
|
391
144
|
format: 'esm',
|
|
392
|
-
outfile: join(packageRoot, 'dist
|
|
145
|
+
outfile: join(packageRoot, 'dist', output),
|
|
393
146
|
minify: true,
|
|
394
147
|
sourcemap: false,
|
|
395
148
|
legalComments: 'none',
|
|
149
|
+
external,
|
|
396
150
|
banner,
|
|
151
|
+
define: {'process.env.LINZUMI_CLI_VERSION': JSON.stringify(packageVersion)},
|
|
397
152
|
});
|
|
398
|
-
} catch (error) {
|
|
399
|
-
console.warn(
|
|
400
|
-
'[build] cross-harness failover bridge could not be bundled (are the ' +
|
|
401
|
-
'linzumi-cli-rescript artifacts built?); shipping the honest refusal ' +
|
|
402
|
-
`stub instead: ${error instanceof Error ? error.message : String(error)}`
|
|
403
|
-
);
|
|
404
|
-
await mkdir(join(packageRoot, 'dist/impl-ts'), { recursive: true });
|
|
405
|
-
await writeFile(
|
|
406
|
-
join(packageRoot, 'dist/impl-ts/crossHarnessFailover.mjs'),
|
|
407
|
-
'export async function transpileAndSeedClaudeSession() {\n' +
|
|
408
|
-
' return {\n' +
|
|
409
|
-
' ok: false,\n' +
|
|
410
|
-
" failure: 'the cross-harness failover transpiler bridge was not bundled into this build',\n" +
|
|
411
|
-
' };\n' +
|
|
412
|
-
'}\n' +
|
|
413
|
-
'export async function seedCanonicalTranscriptIntoClaudeSession() {\n' +
|
|
414
|
-
' return {\n' +
|
|
415
|
-
' ok: false,\n' +
|
|
416
|
-
" failure: 'the canonical Claude rehydration bridge was not bundled into this build',\n" +
|
|
417
|
-
' };\n' +
|
|
418
|
-
'}\n'
|
|
419
|
-
);
|
|
420
153
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
join(packageRoot, 'src/assets/linzumi-logo.svg'),
|
|
432
|
-
join(packageRoot, implDir, 'assets/linzumi-logo.svg')
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// M3 cluster 15: the ReScript commander itself - dist/impl-res/index.js,
|
|
437
|
-
// bundled from the compiled composition-root entry
|
|
438
|
-
// (packages/linzumi-cli-rescript/src/entry/CommanderEntryMain.res). Same
|
|
439
|
-
// externals/banner posture as the TS impl bundle: the runtime deps stay
|
|
440
|
-
// external (they ship in the package's dependency closure), everything else
|
|
441
|
-
// is inlined. When the ReScript artifacts are not compiled in this checkout
|
|
442
|
-
// the build ships an HONEST refusal stub whose import succeeds but whose
|
|
443
|
-
// main() refuses loudly - the chooser's short-lived-exit breaker then falls
|
|
444
|
-
// back to TS (never a silent wrong impl). Publish CI compiles the ReScript
|
|
445
|
-
// package first and the package-size gate asserts the REAL bundle shipped,
|
|
446
|
-
// so the stub can never reach npm.
|
|
447
|
-
try {
|
|
448
|
-
await build({
|
|
449
|
-
entryPoints: [
|
|
450
|
-
join(
|
|
451
|
-
packageRoot,
|
|
452
|
-
'../linzumi-cli-rescript/src/entry/CommanderEntryMain.res.mjs'
|
|
453
|
-
),
|
|
454
|
-
],
|
|
455
|
-
bundle: true,
|
|
456
|
-
platform: 'node',
|
|
457
|
-
target: 'node20',
|
|
458
|
-
format: 'esm',
|
|
459
|
-
outfile: join(packageRoot, 'dist/impl-res/index.js'),
|
|
460
|
-
minify: true,
|
|
461
|
-
sourcemap: false,
|
|
462
|
-
legalComments: 'none',
|
|
463
|
-
external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
|
|
464
|
-
banner,
|
|
465
|
-
define: {
|
|
466
|
-
'process.env.LINZUMI_CLI_VERSION': JSON.stringify(packageVersion),
|
|
467
|
-
},
|
|
468
|
-
});
|
|
469
|
-
} catch (error) {
|
|
470
|
-
console.warn(
|
|
471
|
-
'[build] impl-res commander bundle could not be built (are the ' +
|
|
472
|
-
'linzumi-cli-rescript artifacts compiled?); shipping the honest ' +
|
|
473
|
-
`refusal stub instead: ${error instanceof Error ? error.message : String(error)}`
|
|
474
|
-
);
|
|
475
|
-
await writeFile(
|
|
476
|
-
join(packageRoot, 'dist/impl-res/index.js'),
|
|
477
|
-
'export async function main() {\n' +
|
|
478
|
-
" throw new Error('the rescript commander implementation was not bundled into this build');\n" +
|
|
479
|
-
'}\n'
|
|
480
|
-
);
|
|
154
|
+
for (const [source, output] of [
|
|
155
|
+
['remoteCodexHarnessWorkerEntrypoint.ts', 'remote-codex-harness-worker.js'],
|
|
156
|
+
['cloudSupervisorEntry.ts', 'cloud-supervisor.mjs'],
|
|
157
|
+
['crossHarnessFailover.mjs', 'crossHarnessFailover.mjs'],
|
|
158
|
+
]) {
|
|
159
|
+
await build({entryPoints: [join(packageRoot, 'src', source)], bundle: true,
|
|
160
|
+
platform: 'node', target: 'node20', format: 'esm',
|
|
161
|
+
outfile: join(packageRoot, 'dist', output), minify: true,
|
|
162
|
+
sourcemap: false, legalComments: 'none', banner,
|
|
163
|
+
external: output === 'remote-codex-harness-worker.js' ? ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'] : []});
|
|
481
164
|
}
|
|
482
|
-
|
|
483
|
-
// LOAD-SMOKE TOOTH (prod RCA 2026-08-06): parsing alone did not catch a
|
|
484
|
-
// createRequire dependency that was absent from the Nix image. The published
|
|
485
|
-
// package has a separate build recipe, so import its fully installed commander
|
|
486
|
-
// bundle here as well; any eager MODULE_NOT_FOUND or other load-time failure
|
|
487
|
-
// fails the build.
|
|
488
|
-
await import(pathToFileURL(join(packageRoot, 'dist/impl-ts/index.js')).href);
|
|
165
|
+
await import(pathToFileURL(join(packageRoot, 'dist/index.js')).href);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Spec: spec/single-commander.md. Retained operational version pins.
|
|
1
2
|
// Single-command CLI version bump.
|
|
2
3
|
//
|
|
3
4
|
// Releasing the CLI:
|
|
@@ -5,7 +6,8 @@
|
|
|
5
6
|
// this script too, so a REAL version literal here would collide with the
|
|
6
7
|
// live package version and abort a bump.)
|
|
7
8
|
//
|
|
8
|
-
// 1. pnpm bump-version <new-version>
|
|
9
|
+
// 1. pnpm bump-version <new-version> --spec plans/<release-spec>.md
|
|
10
|
+
// (for example: 1.2.3-beta with its approved release spec)
|
|
9
11
|
// 2. Run the test suite: pnpm vitest run --config vitest.config.mjs
|
|
10
12
|
// 3. Commit with a "[deploy-commander]" title and merge to main (the
|
|
11
13
|
// deprecated "[deploy-cli]" marker was retired in the N3.9 window on
|
|
@@ -21,8 +23,10 @@
|
|
|
21
23
|
// drift past a release.
|
|
22
24
|
//
|
|
23
25
|
// Flags:
|
|
26
|
+
// --spec <path> Required standalone release spec under plans/.
|
|
24
27
|
// --dry-run Plan and verify every rewrite without writing files.
|
|
25
28
|
// --root <dir> Operate on a different package root (used by tests).
|
|
29
|
+
// Release contract: plans/2026-07-12-linzumi-unscoped-npm-package-spec.md
|
|
26
30
|
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
27
31
|
import { dirname, join, relative } from 'node:path';
|
|
28
32
|
import { fileURLToPath } from 'node:url';
|
|
@@ -33,11 +37,12 @@ function fail(message) {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
const usage =
|
|
36
|
-
'usage: node scripts/bump-cli-version.mjs <new-version> [--dry-run] [--root <dir>]';
|
|
40
|
+
'usage: node scripts/bump-cli-version.mjs <new-version> --spec plans/<release-spec>.md [--dry-run] [--root <dir>]';
|
|
37
41
|
|
|
38
42
|
const rawArgs = process.argv.slice(2);
|
|
39
43
|
let dryRun = false;
|
|
40
44
|
let explicitRoot;
|
|
45
|
+
let releaseSpec;
|
|
41
46
|
const positional = [];
|
|
42
47
|
|
|
43
48
|
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
@@ -45,6 +50,13 @@ for (let index = 0; index < rawArgs.length; index += 1) {
|
|
|
45
50
|
|
|
46
51
|
if (arg === '--dry-run') {
|
|
47
52
|
dryRun = true;
|
|
53
|
+
} else if (arg === '--spec') {
|
|
54
|
+
releaseSpec = rawArgs[index + 1];
|
|
55
|
+
index += 1;
|
|
56
|
+
|
|
57
|
+
if (releaseSpec === undefined) {
|
|
58
|
+
fail(`--spec requires a standalone release spec under plans/\n${usage}`);
|
|
59
|
+
}
|
|
48
60
|
} else if (arg === '--root') {
|
|
49
61
|
explicitRoot = rawArgs[index + 1];
|
|
50
62
|
index += 1;
|
|
@@ -75,6 +87,26 @@ const packageRoot =
|
|
|
75
87
|
explicitRoot ?? dirname(dirname(fileURLToPath(import.meta.url)));
|
|
76
88
|
const aliasPackageRoot = join(dirname(packageRoot), 'linzumi');
|
|
77
89
|
|
|
90
|
+
if (
|
|
91
|
+
releaseSpec === undefined ||
|
|
92
|
+
!/^plans\/[A-Za-z0-9][A-Za-z0-9._/-]*\.md$/.test(releaseSpec) ||
|
|
93
|
+
releaseSpec.split('/').some((part) => part === '.' || part === '..')
|
|
94
|
+
) {
|
|
95
|
+
fail(`--spec requires a standalone release spec under plans/\n${usage}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
if (
|
|
100
|
+
readFileSync(join(packageRoot, '../..', releaseSpec), 'utf8').trim() === ''
|
|
101
|
+
) {
|
|
102
|
+
fail(`--spec requires a nonempty standalone release spec: ${releaseSpec}`);
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
fail(
|
|
106
|
+
`--spec requires a readable standalone release spec: ${releaseSpec} (${error.message})`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
78
110
|
let manifest;
|
|
79
111
|
|
|
80
112
|
try {
|
|
@@ -141,12 +173,6 @@ const knownSites = [
|
|
|
141
173
|
pattern: `expect(manifest.version).toBe('${oldVersion}');`,
|
|
142
174
|
replacement: `expect(manifest.version).toBe('${newVersion}');`,
|
|
143
175
|
},
|
|
144
|
-
{
|
|
145
|
-
file: 'test/cli.test.ts',
|
|
146
|
-
description: '--version output expectation',
|
|
147
|
-
pattern: `'linzumi ${oldVersion}'`,
|
|
148
|
-
replacement: `'linzumi ${newVersion}'`,
|
|
149
|
-
},
|
|
150
176
|
{
|
|
151
177
|
file: 'test/runnerBridge.test.ts',
|
|
152
178
|
description: 'runner join metadata version pin',
|
|
@@ -193,7 +219,7 @@ function appendVersionSpecComment(content) {
|
|
|
193
219
|
const today = new Date().toISOString().slice(0, 10);
|
|
194
220
|
const entry = [
|
|
195
221
|
`- Date: ${today}`,
|
|
196
|
-
|
|
222
|
+
` Spec: ${releaseSpec}`,
|
|
197
223
|
` Relationship: Bumps the published CLI version so the`,
|
|
198
224
|
' current mainline CLI can be released under a fresh immutable npm',
|
|
199
225
|
' version with `[deploy-commander]`.',
|
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
- Date: 2026-07-15
|
|
3
|
-
Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
|
|
4
|
-
(M3 packaging paragraph: "Package-size budget in publish CI")
|
|
5
|
-
Relationship: The M3 cluster 15 publish-CI package-size gate. Runs
|
|
6
|
-
`npm pack --dry-run --json`, enforces the dual-impl package budget with the
|
|
7
|
-
warn-band shape of test/ciBudget.ts (warn at 85% of the fail limit,
|
|
8
|
-
actionable per-file top-offender breakdown - never a bare at-limit
|
|
9
|
-
assertion; see the 2026-07-12 "expected 1000 to be less than 1000" staging
|
|
10
|
-
red), and asserts the dual-impl packaging PARITY facts: both impl bundles
|
|
11
|
-
ship for real (the impl-res honest-refusal stub that dev builds emit when
|
|
12
|
-
the ReScript artifacts are not compiled must never reach npm), the chooser
|
|
13
|
-
stays tiny, and the guest-bundle ladder files exist.
|
|
14
|
-
|
|
15
|
-
Budget rationale (measured 2026-07-15 on this branch): the dual-impl
|
|
16
|
-
package measures ~4.96 MB unpacked with the impl-res stub; the real
|
|
17
|
-
impl-res commander bundle lands well under 3 MB (it bundles the same code
|
|
18
|
-
the ~2.26 MB impl-ts bundle carries, compiled from ReScript). 12 MB
|
|
19
|
-
unpacked caps runaway growth at roughly 1.5x the projected dual-impl size
|
|
20
|
-
while leaving the dual-impl period real headroom; the 85% warn band
|
|
21
|
-
(10.2 MB) makes erosion loud before it reds a publish.
|
|
22
|
-
*/
|
|
1
|
+
// Spec: spec/single-commander.md. One complete Commander bundle.
|
|
23
2
|
import { execFileSync } from 'node:child_process';
|
|
24
3
|
import { dirname } from 'node:path';
|
|
25
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -28,42 +7,22 @@ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
|
28
7
|
|
|
29
8
|
export const UNPACKED_SIZE_LIMIT_BYTES = 12 * 1024 * 1024;
|
|
30
9
|
export const WARN_RATIO = 0.85;
|
|
31
|
-
// The chooser must stay a TINY plain-JS shim (it runs before either impl
|
|
32
|
-
// bundle loads); a fat dist/index.js means impl code leaked into it.
|
|
33
|
-
export const CHOOSER_SIZE_LIMIT_BYTES = 64 * 1024;
|
|
34
|
-
// A real commander bundle is megabytes; the honest-refusal stub is ~125
|
|
35
|
-
// bytes. Anything under this floor is not a real implementation.
|
|
36
10
|
export const REAL_IMPL_BUNDLE_FLOOR_BYTES = 200 * 1024;
|
|
37
11
|
|
|
38
12
|
const requiredFiles = [
|
|
39
13
|
'bin/linzumi.js',
|
|
40
|
-
'bin/remote-codex-harness-worker.js',
|
|
41
14
|
'dist/index.js',
|
|
42
|
-
'dist/
|
|
43
|
-
'dist/
|
|
15
|
+
'dist/remote-codex-harness-worker.js',
|
|
16
|
+
'dist/hosted-coding-account-worker.js',
|
|
17
|
+
'dist/cloud-supervisor.mjs',
|
|
18
|
+
'dist/crossHarnessFailover.mjs',
|
|
44
19
|
'dist/mcp-server.mjs',
|
|
45
|
-
'dist/
|
|
46
|
-
'dist/
|
|
47
|
-
'dist/
|
|
48
|
-
'dist/
|
|
49
|
-
'dist/impl-ts/cloud-supervisor.mjs',
|
|
50
|
-
'dist/impl-res/cloud-supervisor.mjs',
|
|
20
|
+
'dist/topics-cli.mjs',
|
|
21
|
+
'dist/tcp-tunnel-cli.mjs',
|
|
22
|
+
'dist/native/darwin/preview-process-identity',
|
|
23
|
+
'dist/assets/linzumi-logo.svg',
|
|
51
24
|
];
|
|
52
|
-
|
|
53
|
-
// Per-file real-bundle floors (Greptile P2): the co-located guest bundles
|
|
54
|
-
// have honest-stub fallbacks in the build too (the F5 refusal stub is ~200
|
|
55
|
-
// bytes; the real bridge is ~8 KB; the real cloud supervisor is ~93 KB), so
|
|
56
|
-
// each gets a floor that distinguishes stub from real - a botched build must
|
|
57
|
-
// red the publish, not ship a refusal stub to the fleet.
|
|
58
|
-
export const GUEST_BUNDLE_FLOOR_BYTES = {
|
|
59
|
-
'dist/impl-ts/crossHarnessFailover.mjs': 2 * 1024,
|
|
60
|
-
'dist/impl-res/crossHarnessFailover.mjs': 2 * 1024,
|
|
61
|
-
'dist/impl-ts/cloud-supervisor.mjs': 20 * 1024,
|
|
62
|
-
'dist/impl-res/cloud-supervisor.mjs': 20 * 1024,
|
|
63
|
-
'dist/mcp-server.mjs': 100 * 1024,
|
|
64
|
-
'dist/impl-ts/mcp-server.mjs': 100 * 1024,
|
|
65
|
-
'dist/impl-res/mcp-server.mjs': 100 * 1024,
|
|
66
|
-
};
|
|
25
|
+
export const GUEST_BUNDLE_FLOOR_BYTES = {'dist/mcp-server.mjs': 100 * 1024, 'dist/crossHarnessFailover.mjs': 1024};
|
|
67
26
|
|
|
68
27
|
function formatMb(bytes) {
|
|
69
28
|
return `${Math.round((bytes / (1024 * 1024)) * 100) / 100} MB`;
|
|
@@ -89,17 +48,10 @@ export function evaluatePackedPackage(packReport) {
|
|
|
89
48
|
}
|
|
90
49
|
}
|
|
91
50
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
failures.push(
|
|
95
|
-
`dist/index.js (the chooser) is ${chooserSize} bytes > ${CHOOSER_SIZE_LIMIT_BYTES}: impl code must not leak into the chooser shim`
|
|
96
|
-
);
|
|
51
|
+
for (const path of files.keys()) {
|
|
52
|
+
if (/^dist\/impl-/.test(path)) failures.push(`obsolete implementation bundle: ${path}`);
|
|
97
53
|
}
|
|
98
|
-
|
|
99
|
-
for (const implBundle of [
|
|
100
|
-
'dist/impl-ts/index.js',
|
|
101
|
-
'dist/impl-res/index.js',
|
|
102
|
-
]) {
|
|
54
|
+
for (const implBundle of ['dist/index.js']) {
|
|
103
55
|
const size = files.get(implBundle) ?? 0;
|
|
104
56
|
if (size > 0 && size < REAL_IMPL_BUNDLE_FLOOR_BYTES) {
|
|
105
57
|
failures.push(
|