@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
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md (M3 cluster 15)
|
|
2
|
-
// Relationship: Builds the ReScript-impl differential entry
|
|
3
|
-
// (packages/linzumi-cli-rescript/src/entry/CommanderDifferentialMain.res,
|
|
4
|
-
// compiled by `rescript` to .res.mjs) into a single runnable bundle at
|
|
5
|
-
// .differential/impl-res.mjs - the peer of scripts/build-differential-entry.mjs's
|
|
6
|
-
// impl-ts bundle, with the SAME platform/target/format/externals/banner so the
|
|
7
|
-
// differential harness compares the two impls under one bundling reality.
|
|
8
|
-
//
|
|
9
|
-
// Version injection: the ReScript package ships no version literal (repo
|
|
10
|
-
// rule); the CLI version is esbuild-`define`d into the bundle as
|
|
11
|
-
// process.env.LINZUMI_CLI_VERSION (read via CommanderEntryVersion.res), so the
|
|
12
|
-
// hello `version` equals the TS impl's linzumiCliVersion (== package.json
|
|
13
|
-
// version) and the min-version refusal transcripts stay byte-identical.
|
|
14
|
-
//
|
|
15
|
-
// FAIL LOUDLY when the compiled ReScript artifacts are missing: the
|
|
16
|
-
// differential harness must never silently fall back to a stub.
|
|
17
|
-
import { existsSync } from 'node:fs';
|
|
18
|
-
import { readFile } from 'node:fs/promises';
|
|
19
|
-
import { dirname, join } from 'node:path';
|
|
20
|
-
import { fileURLToPath } from 'node:url';
|
|
21
|
-
import { build } from 'esbuild';
|
|
22
|
-
|
|
23
|
-
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
24
|
-
|
|
25
|
-
const entryPoint = join(
|
|
26
|
-
packageRoot,
|
|
27
|
-
'../linzumi-cli-rescript/src/entry/CommanderDifferentialMain.res.mjs'
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
if (!existsSync(entryPoint)) {
|
|
31
|
-
console.error(
|
|
32
|
-
`impl-res differential entry artifact missing: ${entryPoint}\n` +
|
|
33
|
-
'run `pnpm build` in packages/linzumi-cli-rescript first (rescript compiles src/**.res -> .res.mjs in-source)'
|
|
34
|
-
);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const packageJson = JSON.parse(
|
|
39
|
-
await readFile(join(packageRoot, 'package.json'), 'utf8')
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// Renamed banner import, mirroring scripts/build-differential-entry.mjs: this
|
|
43
|
-
// bundle keeps source-level identifiers and the sources may import
|
|
44
|
-
// createRequire themselves - the published banner's bare name would collide.
|
|
45
|
-
const banner = {
|
|
46
|
-
js: "import { createRequire as __differentialCreateRequire } from 'node:module';\nconst require = __differentialCreateRequire(import.meta.url);",
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
await build({
|
|
50
|
-
entryPoints: [entryPoint],
|
|
51
|
-
bundle: true,
|
|
52
|
-
platform: 'node',
|
|
53
|
-
target: 'node20',
|
|
54
|
-
format: 'esm',
|
|
55
|
-
outfile: join(packageRoot, '.differential/impl-res.mjs'),
|
|
56
|
-
minify: false,
|
|
57
|
-
sourcemap: false,
|
|
58
|
-
legalComments: 'none',
|
|
59
|
-
external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
|
|
60
|
-
banner,
|
|
61
|
-
define: {
|
|
62
|
-
'process.env.LINZUMI_CLI_VERSION': JSON.stringify(packageJson.version),
|
|
63
|
-
},
|
|
64
|
-
});
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
// Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md (M2.5 + M2.6)
|
|
2
|
-
// Relationship: Builds the TS-impl differential entry
|
|
3
|
-
// (src/differentialCommanderEntry.ts) into a single runnable bundle at
|
|
4
|
-
// .differential/impl-ts.mjs, mirroring the published dist/index.js build
|
|
5
|
-
// configuration (same platform/target/format/externals/banner) so the
|
|
6
|
-
// differential harness exercises the SAME bundling reality the shipped CLI
|
|
7
|
-
// has - this is the M3 packaging seam's dist/impl-ts precursor: the M3
|
|
8
|
-
// chooser design ships dist/impl-ts/ + dist/impl-res/ side by side, and the
|
|
9
|
-
// M2.5 differential runner's impl parameter selects between them (the
|
|
10
|
-
// impl-res twin is scripts/build-differential-entry-impl-res.mjs).
|
|
11
|
-
//
|
|
12
|
-
// M2.6 mutation mode (`--mutation <name>`): applies ONE seeded behavior
|
|
13
|
-
// delta from scripts/differentialMutations.mjs as a build-time source patch
|
|
14
|
-
// and emits .differential/impl-ts.mutant-<name>.mjs instead. The harness's
|
|
15
|
-
// anti-false-green mutation check builds each mutant and asserts the
|
|
16
|
-
// differential suite goes RED on it. Every patch anchor must match the
|
|
17
|
-
// current source EXACTLY ONCE - zero or multiple matches fail the build
|
|
18
|
-
// loudly, so a commander refactor can never silently no-op a mutation.
|
|
19
|
-
//
|
|
20
|
-
// Unminified deliberately: the bundle is a local test artifact
|
|
21
|
-
// (.differential/ is gitignored, never published), and readable output makes
|
|
22
|
-
// differential findings debuggable.
|
|
23
|
-
import { copyFile, mkdir, readFile } from 'node:fs/promises';
|
|
24
|
-
import { dirname, join } from 'node:path';
|
|
25
|
-
import { fileURLToPath } from 'node:url';
|
|
26
|
-
import { build } from 'esbuild';
|
|
27
|
-
import { differentialMutations } from './differentialMutations.mjs';
|
|
28
|
-
|
|
29
|
-
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
30
|
-
|
|
31
|
-
function parseArgs(argv) {
|
|
32
|
-
if (argv.length === 0) {
|
|
33
|
-
return { mutation: undefined };
|
|
34
|
-
}
|
|
35
|
-
if (argv[0] === '--mutation' && argv.length === 2) {
|
|
36
|
-
const mutation = differentialMutations.find(
|
|
37
|
-
(entry) => entry.name === argv[1]
|
|
38
|
-
);
|
|
39
|
-
if (mutation === undefined) {
|
|
40
|
-
const known = differentialMutations.map((entry) => entry.name);
|
|
41
|
-
throw new Error(
|
|
42
|
-
`unknown differential mutation ${JSON.stringify(argv[1])}; known mutations: ${known.join(', ')}`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
return { mutation };
|
|
46
|
-
}
|
|
47
|
-
throw new Error(
|
|
48
|
-
'usage: node scripts/build-differential-entry.mjs [--mutation <name>]'
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const { mutation } = parseArgs(process.argv.slice(2));
|
|
53
|
-
|
|
54
|
-
// Count non-overlapping occurrences of `needle` in `haystack`. An empty
|
|
55
|
-
// needle is a registry authoring error, refused loudly (indexOf('') matches
|
|
56
|
-
// at every position, which would both loop forever here and make the
|
|
57
|
-
// exactly-once rule meaningless); the harness registry conformance pins the
|
|
58
|
-
// same rule at rest (Greptile finding on #3115).
|
|
59
|
-
function occurrences(haystack, needle) {
|
|
60
|
-
if (needle.length === 0) {
|
|
61
|
-
throw new Error('mutation patch `find` must be a non-empty string');
|
|
62
|
-
}
|
|
63
|
-
let count = 0;
|
|
64
|
-
let from = 0;
|
|
65
|
-
for (;;) {
|
|
66
|
-
const at = haystack.indexOf(needle, from);
|
|
67
|
-
if (at === -1) {
|
|
68
|
-
return count;
|
|
69
|
-
}
|
|
70
|
-
count += 1;
|
|
71
|
-
from = at + needle.length;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// esbuild onLoad plugin: apply the mutation's patches to their target
|
|
76
|
-
// sources, enforcing the exactly-once anchor rule per patch. Also asserts
|
|
77
|
-
// every patch was actually loaded by the bundle (a patched-but-unbundled
|
|
78
|
-
// file would be a silent no-op mutation).
|
|
79
|
-
function mutationPlugin(activeMutation) {
|
|
80
|
-
const patchesByFile = new Map();
|
|
81
|
-
for (const patch of activeMutation.patches) {
|
|
82
|
-
const absolute = join(packageRoot, patch.file);
|
|
83
|
-
const existing = patchesByFile.get(absolute) ?? [];
|
|
84
|
-
existing.push(patch);
|
|
85
|
-
patchesByFile.set(absolute, existing);
|
|
86
|
-
}
|
|
87
|
-
const applied = new Set();
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
plugin: {
|
|
91
|
-
name: 'differential-mutation',
|
|
92
|
-
setup(pluginBuild) {
|
|
93
|
-
pluginBuild.onLoad({ filter: /\.ts$/ }, async (args) => {
|
|
94
|
-
const patches = patchesByFile.get(args.path);
|
|
95
|
-
if (patches === undefined) {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
let contents = await readFile(args.path, 'utf8');
|
|
99
|
-
for (const patch of patches) {
|
|
100
|
-
const found = occurrences(contents, patch.find);
|
|
101
|
-
if (found !== 1) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
`mutation ${activeMutation.name}: anchor in ${patch.file} matched ${found} times (must match exactly once); the commander source moved - re-anchor the patch in scripts/differentialMutations.mjs`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
contents = contents.replace(patch.find, patch.replace);
|
|
107
|
-
applied.add(`${patch.file}:${patch.find.slice(0, 40)}`);
|
|
108
|
-
}
|
|
109
|
-
return { contents, loader: 'ts' };
|
|
110
|
-
});
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
assertAllApplied() {
|
|
114
|
-
const expected = activeMutation.patches.length;
|
|
115
|
-
if (applied.size !== expected) {
|
|
116
|
-
throw new Error(
|
|
117
|
-
`mutation ${activeMutation.name}: only ${applied.size} of ${expected} patches were loaded into the bundle - a patched file is not part of the differential entry`
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Renamed banner import: unlike the minified dist build, this bundle keeps
|
|
125
|
-
// source-level identifiers, and the commander sources themselves import
|
|
126
|
-
// createRequire - the published banner's bare name would collide.
|
|
127
|
-
const banner = {
|
|
128
|
-
js: "import { createRequire as __differentialCreateRequire } from 'node:module';\nconst require = __differentialCreateRequire(import.meta.url);",
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const outfile =
|
|
132
|
-
mutation === undefined
|
|
133
|
-
? join(packageRoot, '.differential/impl-ts.mjs')
|
|
134
|
-
: join(packageRoot, `.differential/impl-ts.mutant-${mutation.name}.mjs`);
|
|
135
|
-
|
|
136
|
-
const mutationHooks =
|
|
137
|
-
mutation === undefined ? undefined : mutationPlugin(mutation);
|
|
138
|
-
|
|
139
|
-
await build({
|
|
140
|
-
entryPoints: [join(packageRoot, 'src/differentialCommanderEntry.ts')],
|
|
141
|
-
bundle: true,
|
|
142
|
-
platform: 'node',
|
|
143
|
-
target: 'node20',
|
|
144
|
-
format: 'esm',
|
|
145
|
-
outfile,
|
|
146
|
-
minify: false,
|
|
147
|
-
sourcemap: false,
|
|
148
|
-
legalComments: 'none',
|
|
149
|
-
external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
|
|
150
|
-
banner,
|
|
151
|
-
plugins: mutationHooks === undefined ? [] : [mutationHooks.plugin],
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
mutationHooks?.assertAllApplied();
|
|
155
|
-
|
|
156
|
-
// The runtime resolves assets/ relative to the bundle (same as dist/).
|
|
157
|
-
await mkdir(join(packageRoot, '.differential/assets'), { recursive: true });
|
|
158
|
-
await copyFile(
|
|
159
|
-
join(packageRoot, 'src/assets/linzumi-logo.svg'),
|
|
160
|
-
join(packageRoot, '.differential/assets/linzumi-logo.svg')
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
// F5: the cross-harness failover bridge rides next to the bundle exactly as
|
|
164
|
-
// it does in dist/ (the runner dynamic-imports ./crossHarnessFailover.mjs
|
|
165
|
-
// relative to import.meta.url), bundled with the compiled ReScript
|
|
166
|
-
// transpiler artifacts resolved at build time. Unlike the dist build there
|
|
167
|
-
// is NO stub fallback here: the differential harness must exercise the REAL
|
|
168
|
-
// transpile leg, so missing artifacts fail this build loudly.
|
|
169
|
-
await build({
|
|
170
|
-
entryPoints: [join(packageRoot, 'src/crossHarnessFailover.mjs')],
|
|
171
|
-
bundle: true,
|
|
172
|
-
platform: 'node',
|
|
173
|
-
target: 'node20',
|
|
174
|
-
format: 'esm',
|
|
175
|
-
outfile: join(packageRoot, '.differential/crossHarnessFailover.mjs'),
|
|
176
|
-
minify: false,
|
|
177
|
-
sourcemap: false,
|
|
178
|
-
legalComments: 'none',
|
|
179
|
-
banner,
|
|
180
|
-
});
|
|
@@ -1,186 +0,0 @@
|
|
|
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
|
-
);
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
|
|
2
|
-
// (M3 cluster 11, the TUI - visual evidence).
|
|
3
|
-
// Relationship: Builds the TS-side VISUAL-EVIDENCE demo driver: a bundle
|
|
4
|
-
// that mounts the REAL TS commander console TUI (reportRunnerConsoleEvent
|
|
5
|
-
// on a live TTY) and replays the SAME dummy-event feed the ReScript demo
|
|
6
|
-
// entrypoint replays
|
|
7
|
-
// (packages/linzumi-cli-rescript/src/tui/commanderTuiDemoFeed.json), so
|
|
8
|
-
// the cluster's before/after terminal captures compare the two
|
|
9
|
-
// implementations on identical input.
|
|
10
|
-
//
|
|
11
|
-
// Run (inside a real terminal / tmux pane):
|
|
12
|
-
// node scripts/build-tui-demo-evidence.mjs
|
|
13
|
-
// LINZUMI_TUI_DEMO_FIXED_NOW=<ms> node .differential/tui-demo.mjs
|
|
14
|
-
//
|
|
15
|
-
// Env knobs (mirroring the ReScript demo):
|
|
16
|
-
// LINZUMI_TUI_DEMO_FIXED_NOW pin Date.now() (and therefore every
|
|
17
|
-
// time-ago cell + the render ticker clock)
|
|
18
|
-
// so captures are deterministic.
|
|
19
|
-
//
|
|
20
|
-
// The output is a local evidence artifact (.differential/ is gitignored,
|
|
21
|
-
// never published).
|
|
22
|
-
import { mkdir } from 'node:fs/promises';
|
|
23
|
-
import { dirname, join } from 'node:path';
|
|
24
|
-
import { fileURLToPath } from 'node:url';
|
|
25
|
-
import { build } from 'esbuild';
|
|
26
|
-
|
|
27
|
-
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
28
|
-
|
|
29
|
-
const driverSource = `
|
|
30
|
-
import { readFileSync } from 'node:fs';
|
|
31
|
-
import { join } from 'node:path';
|
|
32
|
-
import { reportRunnerConsoleEvent } from './src/runnerConsoleReporter';
|
|
33
|
-
|
|
34
|
-
const fixedNowRaw = process.env.LINZUMI_TUI_DEMO_FIXED_NOW;
|
|
35
|
-
const fixedNow = fixedNowRaw === undefined ? undefined : Number(fixedNowRaw);
|
|
36
|
-
if (fixedNow !== undefined && Number.isFinite(fixedNow)) {
|
|
37
|
-
const RealDate = Date;
|
|
38
|
-
class FixedDate extends RealDate {
|
|
39
|
-
constructor(...args) {
|
|
40
|
-
if (args.length === 0) {
|
|
41
|
-
super(fixedNow);
|
|
42
|
-
} else {
|
|
43
|
-
super(...args);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
static now() {
|
|
47
|
-
return fixedNow;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
globalThis.Date = FixedDate;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const now = Date.now();
|
|
54
|
-
|
|
55
|
-
// Resolve *_offset timestamp fields against the demo clock (the ReScript
|
|
56
|
-
// demo performs the same resolution).
|
|
57
|
-
function resolveOffsets(value) {
|
|
58
|
-
if (Array.isArray(value)) {
|
|
59
|
-
return value.map(resolveOffsets);
|
|
60
|
-
}
|
|
61
|
-
if (value !== null && typeof value === 'object') {
|
|
62
|
-
const out = {};
|
|
63
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
64
|
-
if (key.endsWith('_offset') && typeof entry === 'number') {
|
|
65
|
-
out[key.slice(0, -'_offset'.length)] = now + entry;
|
|
66
|
-
} else {
|
|
67
|
-
out[key] = resolveOffsets(entry);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
return value;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const feedPath = join(
|
|
76
|
-
process.cwd(),
|
|
77
|
-
'../linzumi-cli-rescript/src/tui/commanderTuiDemoFeed.json'
|
|
78
|
-
);
|
|
79
|
-
const feed = JSON.parse(readFileSync(feedPath, 'utf8'));
|
|
80
|
-
for (const entry of feed.events) {
|
|
81
|
-
reportRunnerConsoleEvent(entry.event, resolveOffsets(entry.payload));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Keep the process (and the TUI) alive for interactive navigation; the
|
|
85
|
-
// TUI's own C-c binding exits.
|
|
86
|
-
setInterval(() => {}, 1000);
|
|
87
|
-
`;
|
|
88
|
-
|
|
89
|
-
await mkdir(join(packageRoot, '.differential'), { recursive: true });
|
|
90
|
-
|
|
91
|
-
await build({
|
|
92
|
-
stdin: {
|
|
93
|
-
contents: driverSource,
|
|
94
|
-
resolveDir: packageRoot,
|
|
95
|
-
sourcefile: 'tui-demo-driver.ts',
|
|
96
|
-
loader: 'ts',
|
|
97
|
-
},
|
|
98
|
-
bundle: true,
|
|
99
|
-
platform: 'node',
|
|
100
|
-
target: 'node20',
|
|
101
|
-
format: 'esm',
|
|
102
|
-
outfile: join(packageRoot, '.differential/tui-demo.mjs'),
|
|
103
|
-
minify: false,
|
|
104
|
-
sourcemap: false,
|
|
105
|
-
legalComments: 'none',
|
|
106
|
-
external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
|
|
107
|
-
});
|