mustflow 2.18.0 → 2.18.3
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 +2 -2
- package/dist/cli/commands/explain-verify.js +2 -2
- package/dist/cli/commands/run/builtin-dispatch.js +92 -0
- package/dist/cli/commands/run/executor.js +112 -0
- package/dist/cli/commands/run/output.js +59 -0
- package/dist/cli/commands/run/process-tree.js +91 -0
- package/dist/cli/commands/run/receipt.js +42 -0
- package/dist/cli/commands/run.js +22 -414
- package/dist/cli/commands/verify/args.js +262 -0
- package/dist/cli/commands/verify.js +106 -263
- package/dist/cli/i18n/en.js +3 -1
- package/dist/cli/i18n/es.js +3 -1
- package/dist/cli/i18n/fr.js +3 -1
- package/dist/cli/i18n/hi.js +3 -1
- package/dist/cli/i18n/ko.js +3 -1
- package/dist/cli/i18n/zh.js +3 -1
- package/dist/cli/index.js +6 -72
- package/dist/cli/lib/command-registry.js +27 -0
- package/dist/cli/lib/repo-map.js +10 -3
- package/dist/core/atomic-state-write.js +31 -0
- package/dist/core/bounded-output.js +23 -1
- package/dist/core/check-issues.js +1 -0
- package/dist/core/command-contract-validation.js +57 -7
- package/dist/core/completion-verdict.js +2 -1
- package/dist/core/public-json-contracts.js +1 -1
- package/dist/core/run-receipt.js +20 -13
- package/dist/core/source-anchors.js +96 -24
- package/dist/core/verification-evidence.js +4 -1
- package/package.json +1 -1
- package/schemas/README.md +1 -1
- package/schemas/run-receipt.schema.json +26 -3
- package/schemas/verify-report.schema.json +1 -1
- package/schemas/verify-run-manifest.schema.json +1 -1
- package/templates/default/i18n.toml +7 -1
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +2 -1
- package/templates/default/locales/en/.mustflow/skills/routes.toml +6 -0
- package/templates/default/locales/en/.mustflow/skills/source-anchor-authoring/SKILL.md +147 -0
- package/templates/default/manifest.toml +8 -1
package/dist/cli/commands/run.js
CHANGED
|
@@ -1,388 +1,20 @@
|
|
|
1
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
2
1
|
import { performance } from 'node:perf_hooks';
|
|
3
|
-
import { canRunMustflowBuiltinInProcess, isMustflowBinName } from '../../core/command-classification.js';
|
|
4
2
|
import { createCommandEnv } from '../../core/command-env.js';
|
|
5
|
-
import { BoundedOutputBuffer } from '../../core/bounded-output.js';
|
|
6
3
|
import { printUsageError, renderCliError, renderHelp } from '../lib/cli-output.js';
|
|
7
4
|
import { readCommandContract, readMustflowConfigIfExists } from '../../core/config-loading.js';
|
|
8
5
|
import { resolveRunReceiptRetentionPolicy } from '../../core/retention-policy.js';
|
|
9
6
|
import { t } from '../lib/i18n.js';
|
|
10
|
-
import { getPackageVersion } from '../lib/package-info.js';
|
|
11
7
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
12
8
|
import { createRunPlan, createRunPreview, isMustflowBuiltinIntent, renderRunPreviewText, } from '../lib/run-plan.js';
|
|
13
|
-
import {
|
|
9
|
+
import { writeRunReceipt, } from '../../core/run-receipt.js';
|
|
14
10
|
import { recordRunPerformanceHistory } from '../../core/run-performance-history.js';
|
|
15
11
|
import { RunProfiler } from '../../core/run-profile.js';
|
|
16
12
|
import { finishRunWriteTracking, startRunWriteTracking } from '../../core/run-write-drift.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (text.length === 0) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
reporter[stream](text);
|
|
26
|
-
}
|
|
27
|
-
function signalProcessTree(pid, signal) {
|
|
28
|
-
if (!pid || pid <= 0) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (process.platform === 'win32') {
|
|
32
|
-
spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], {
|
|
33
|
-
stdio: 'ignore',
|
|
34
|
-
windowsHide: true,
|
|
35
|
-
});
|
|
36
|
-
if (signal === 'SIGKILL') {
|
|
37
|
-
try {
|
|
38
|
-
process.kill(pid, signal);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// taskkill may already have terminated the direct child.
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
process.kill(-pid, signal);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
try {
|
|
51
|
-
process.kill(pid, signal);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// The child may already be gone after Node's spawn timeout handling.
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function signalProcessTreeNonBlocking(pid, signal) {
|
|
59
|
-
if (!pid || pid <= 0) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (process.platform === 'win32') {
|
|
63
|
-
const killer = spawn('taskkill', ['/PID', String(pid), '/T', '/F'], {
|
|
64
|
-
stdio: 'ignore',
|
|
65
|
-
windowsHide: true,
|
|
66
|
-
detached: true,
|
|
67
|
-
});
|
|
68
|
-
killer.unref();
|
|
69
|
-
if (signal === 'SIGKILL') {
|
|
70
|
-
try {
|
|
71
|
-
process.kill(pid, signal);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// taskkill may already have terminated the direct child.
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
process.kill(-pid, signal);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
try {
|
|
84
|
-
process.kill(pid, signal);
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
// The child may already be gone after the timeout fired.
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function terminateProcessTree(pid) {
|
|
92
|
-
signalProcessTree(pid, 'SIGTERM');
|
|
93
|
-
}
|
|
94
|
-
function forceTerminateProcessTree(pid) {
|
|
95
|
-
signalProcessTree(pid, 'SIGKILL');
|
|
96
|
-
}
|
|
97
|
-
function terminateProcessTreeNonBlocking(pid) {
|
|
98
|
-
signalProcessTreeNonBlocking(pid, 'SIGTERM');
|
|
99
|
-
}
|
|
100
|
-
function forceTerminateProcessTreeNonBlocking(pid) {
|
|
101
|
-
signalProcessTreeNonBlocking(pid, 'SIGKILL');
|
|
102
|
-
}
|
|
103
|
-
function getKillMethod() {
|
|
104
|
-
return process.platform === 'win32' ? 'taskkill_process_tree' : 'process_group_sigterm';
|
|
105
|
-
}
|
|
106
|
-
function createBufferedReporter() {
|
|
107
|
-
const stdout = [];
|
|
108
|
-
const stderr = [];
|
|
109
|
-
return {
|
|
110
|
-
reporter: {
|
|
111
|
-
stdout(message) {
|
|
112
|
-
stdout.push(`${message}\n`);
|
|
113
|
-
},
|
|
114
|
-
stderr(message) {
|
|
115
|
-
stderr.push(`${message}\n`);
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
stdout() {
|
|
119
|
-
return stdout.join('');
|
|
120
|
-
},
|
|
121
|
-
stderr() {
|
|
122
|
-
return stderr.join('');
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* mf:anchor cli.run.builtin-inprocess
|
|
128
|
-
* purpose: Dispatch selected mustflow built-in commands without spawning a nested CLI process.
|
|
129
|
-
* search: builtin intent, in-process command, nested mf run, run receipt
|
|
130
|
-
* invariant: Only commands classified by command-classification can use this path.
|
|
131
|
-
* risk: config, state
|
|
132
|
-
*/
|
|
133
|
-
async function runKnownBuiltinCommand(args, reporter, lang) {
|
|
134
|
-
const [command, ...commandArgs] = args;
|
|
135
|
-
if ((command === '--version' || command === '-v' || command === 'version') && commandArgs.length === 0) {
|
|
136
|
-
reporter.stdout(getPackageVersion());
|
|
137
|
-
return 0;
|
|
138
|
-
}
|
|
139
|
-
if (!canRunMustflowBuiltinInProcess(command)) {
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
if (command === 'check') {
|
|
143
|
-
return (await import('./check.js')).runCheck(commandArgs, reporter, lang);
|
|
144
|
-
}
|
|
145
|
-
if (command === 'classify') {
|
|
146
|
-
return (await import('./classify.js')).runClassify(commandArgs, reporter, lang);
|
|
147
|
-
}
|
|
148
|
-
if (command === 'context') {
|
|
149
|
-
return (await import('./context.js')).runContext(commandArgs, reporter, lang);
|
|
150
|
-
}
|
|
151
|
-
if (command === 'doctor') {
|
|
152
|
-
return (await import('./doctor.js')).runDoctor(commandArgs, reporter, lang);
|
|
153
|
-
}
|
|
154
|
-
if (command === 'help') {
|
|
155
|
-
return (await import('./help.js')).runHelp(commandArgs, reporter, lang);
|
|
156
|
-
}
|
|
157
|
-
if (command === 'impact') {
|
|
158
|
-
return (await import('./impact.js')).runImpact(commandArgs, reporter, lang);
|
|
159
|
-
}
|
|
160
|
-
if (command === 'line-endings') {
|
|
161
|
-
return (await import('./line-endings.js')).runLineEndings(commandArgs, reporter, lang);
|
|
162
|
-
}
|
|
163
|
-
if (command === 'map') {
|
|
164
|
-
return (await import('./map.js')).runMap(commandArgs, reporter, lang);
|
|
165
|
-
}
|
|
166
|
-
if (command === 'status') {
|
|
167
|
-
return (await import('./status.js')).runStatus(commandArgs, reporter, lang);
|
|
168
|
-
}
|
|
169
|
-
if (command === 'update') {
|
|
170
|
-
return (await import('./update.js')).runUpdate(commandArgs, reporter, lang);
|
|
171
|
-
}
|
|
172
|
-
if (command === 'version-sources') {
|
|
173
|
-
return (await import('./version-sources.js')).runVersionSources(commandArgs, reporter, lang);
|
|
174
|
-
}
|
|
175
|
-
return undefined;
|
|
176
|
-
}
|
|
177
|
-
async function withWorkingDirectory(cwd, callback) {
|
|
178
|
-
const previousCwd = process.cwd();
|
|
179
|
-
process.chdir(cwd);
|
|
180
|
-
try {
|
|
181
|
-
return await callback();
|
|
182
|
-
}
|
|
183
|
-
finally {
|
|
184
|
-
process.chdir(previousCwd);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
async function runBuiltinArgvInProcess(commandArgv, cwd, lang) {
|
|
188
|
-
const [command = '', ...builtinArgs] = commandArgv;
|
|
189
|
-
if (!isMustflowBinName(command)) {
|
|
190
|
-
return undefined;
|
|
191
|
-
}
|
|
192
|
-
const output = createBufferedReporter();
|
|
193
|
-
try {
|
|
194
|
-
const status = await withWorkingDirectory(cwd, () => runKnownBuiltinCommand(builtinArgs, output.reporter, lang));
|
|
195
|
-
if (status === undefined) {
|
|
196
|
-
return undefined;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
status,
|
|
200
|
-
signal: null,
|
|
201
|
-
stdout: output.stdout(),
|
|
202
|
-
stderr: output.stderr(),
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
207
|
-
return {
|
|
208
|
-
status: 1,
|
|
209
|
-
signal: null,
|
|
210
|
-
stdout: output.stdout(),
|
|
211
|
-
stderr: `${output.stderr()}${message}\n`,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function runArgvCommand(command, cwd, maxOutputBytes, env, timeoutSeconds) {
|
|
216
|
-
return spawnSync(command?.executable ?? '', command?.args ?? [], {
|
|
217
|
-
cwd,
|
|
218
|
-
encoding: 'utf8',
|
|
219
|
-
input: '',
|
|
220
|
-
maxBuffer: maxOutputBytes,
|
|
221
|
-
env,
|
|
222
|
-
shell: command?.shell ?? false,
|
|
223
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
224
|
-
timeout: timeoutSeconds * 1000,
|
|
225
|
-
windowsHide: true,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
function writeStreamChunk(reporter, stream, chunk) {
|
|
229
|
-
if (stream === 'stdout') {
|
|
230
|
-
if (reporter.writeStdout) {
|
|
231
|
-
reporter.writeStdout(chunk);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
reporter.stdout(chunk.toString());
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (reporter.writeStderr) {
|
|
238
|
-
reporter.writeStderr(chunk);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
reporter.stderr(chunk.toString());
|
|
242
|
-
}
|
|
243
|
-
function runArgvCommandStreaming(command, cwd, env, timeoutSeconds, stdoutTailBytes, stderrTailBytes, reporter) {
|
|
244
|
-
return new Promise((resolve) => {
|
|
245
|
-
const stdout = new BoundedOutputBuffer(stdoutTailBytes);
|
|
246
|
-
const stderr = new BoundedOutputBuffer(stderrTailBytes);
|
|
247
|
-
let settled = false;
|
|
248
|
-
let timedOut = false;
|
|
249
|
-
let childError;
|
|
250
|
-
let childPid;
|
|
251
|
-
let timeout;
|
|
252
|
-
const child = spawn(command?.executable ?? '', command?.args ?? [], {
|
|
253
|
-
cwd,
|
|
254
|
-
env,
|
|
255
|
-
shell: command?.shell ?? false,
|
|
256
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
257
|
-
windowsHide: true,
|
|
258
|
-
detached: process.platform !== 'win32',
|
|
259
|
-
});
|
|
260
|
-
childPid = child.pid;
|
|
261
|
-
const finish = (status, signal) => {
|
|
262
|
-
if (settled) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
settled = true;
|
|
266
|
-
if (timeout) {
|
|
267
|
-
clearTimeout(timeout);
|
|
268
|
-
}
|
|
269
|
-
resolve({
|
|
270
|
-
status: timedOut ? null : status,
|
|
271
|
-
signal: timedOut ? null : signal,
|
|
272
|
-
error: timedOut ? Object.assign(new Error('Command timed out'), { code: 'ETIMEDOUT' }) : childError,
|
|
273
|
-
stdout: stdout.toSnapshot(),
|
|
274
|
-
stderr: stderr.toSnapshot(),
|
|
275
|
-
pid: childPid,
|
|
276
|
-
});
|
|
277
|
-
};
|
|
278
|
-
child.stdout?.on('data', (chunk) => {
|
|
279
|
-
stdout.append(chunk);
|
|
280
|
-
writeStreamChunk(reporter, 'stdout', chunk);
|
|
281
|
-
});
|
|
282
|
-
child.stderr?.on('data', (chunk) => {
|
|
283
|
-
stderr.append(chunk);
|
|
284
|
-
writeStreamChunk(reporter, 'stderr', chunk);
|
|
285
|
-
});
|
|
286
|
-
child.once('error', (error) => {
|
|
287
|
-
childError = error;
|
|
288
|
-
});
|
|
289
|
-
child.once('close', (status, signal) => {
|
|
290
|
-
finish(status, signal);
|
|
291
|
-
});
|
|
292
|
-
timeout = setTimeout(() => {
|
|
293
|
-
timedOut = true;
|
|
294
|
-
child.stdout?.destroy();
|
|
295
|
-
child.stderr?.destroy();
|
|
296
|
-
child.unref();
|
|
297
|
-
terminateProcessTreeNonBlocking(childPid);
|
|
298
|
-
forceTerminateProcessTreeNonBlocking(childPid);
|
|
299
|
-
finish(null, null);
|
|
300
|
-
}, timeoutSeconds * 1000);
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
function runShellCommand(command, cwd, maxOutputBytes, env, timeoutSeconds) {
|
|
304
|
-
return spawnSync(command ?? '', {
|
|
305
|
-
cwd,
|
|
306
|
-
encoding: 'utf8',
|
|
307
|
-
input: '',
|
|
308
|
-
maxBuffer: maxOutputBytes,
|
|
309
|
-
env,
|
|
310
|
-
shell: true,
|
|
311
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
312
|
-
timeout: timeoutSeconds * 1000,
|
|
313
|
-
windowsHide: true,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
function runShellCommandStreaming(command, cwd, env, timeoutSeconds, stdoutTailBytes, stderrTailBytes, reporter) {
|
|
317
|
-
return new Promise((resolve) => {
|
|
318
|
-
const stdout = new BoundedOutputBuffer(stdoutTailBytes);
|
|
319
|
-
const stderr = new BoundedOutputBuffer(stderrTailBytes);
|
|
320
|
-
let settled = false;
|
|
321
|
-
let timedOut = false;
|
|
322
|
-
let childError;
|
|
323
|
-
let childPid;
|
|
324
|
-
let timeout;
|
|
325
|
-
const child = spawn(command ?? '', {
|
|
326
|
-
cwd,
|
|
327
|
-
env,
|
|
328
|
-
shell: true,
|
|
329
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
330
|
-
windowsHide: true,
|
|
331
|
-
detached: process.platform !== 'win32',
|
|
332
|
-
});
|
|
333
|
-
childPid = child.pid;
|
|
334
|
-
const finish = (status, signal) => {
|
|
335
|
-
if (settled) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
settled = true;
|
|
339
|
-
if (timeout) {
|
|
340
|
-
clearTimeout(timeout);
|
|
341
|
-
}
|
|
342
|
-
resolve({
|
|
343
|
-
status: timedOut ? null : status,
|
|
344
|
-
signal: timedOut ? null : signal,
|
|
345
|
-
error: timedOut ? Object.assign(new Error('Command timed out'), { code: 'ETIMEDOUT' }) : childError,
|
|
346
|
-
stdout: stdout.toSnapshot(),
|
|
347
|
-
stderr: stderr.toSnapshot(),
|
|
348
|
-
pid: childPid,
|
|
349
|
-
});
|
|
350
|
-
};
|
|
351
|
-
child.stdout?.on('data', (chunk) => {
|
|
352
|
-
stdout.append(chunk);
|
|
353
|
-
writeStreamChunk(reporter, 'stdout', chunk);
|
|
354
|
-
});
|
|
355
|
-
child.stderr?.on('data', (chunk) => {
|
|
356
|
-
stderr.append(chunk);
|
|
357
|
-
writeStreamChunk(reporter, 'stderr', chunk);
|
|
358
|
-
});
|
|
359
|
-
child.once('error', (error) => {
|
|
360
|
-
childError = error;
|
|
361
|
-
});
|
|
362
|
-
child.once('close', (status, signal) => {
|
|
363
|
-
finish(status, signal);
|
|
364
|
-
});
|
|
365
|
-
timeout = setTimeout(() => {
|
|
366
|
-
timedOut = true;
|
|
367
|
-
child.stdout?.destroy();
|
|
368
|
-
child.stderr?.destroy();
|
|
369
|
-
child.unref();
|
|
370
|
-
terminateProcessTreeNonBlocking(childPid);
|
|
371
|
-
forceTerminateProcessTreeNonBlocking(childPid);
|
|
372
|
-
finish(null, null);
|
|
373
|
-
}, timeoutSeconds * 1000);
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
function getRunStatus(error, exitCode, successExitCodes) {
|
|
377
|
-
const errorWithCode = error;
|
|
378
|
-
if (errorWithCode?.code === 'ETIMEDOUT') {
|
|
379
|
-
return 'timed_out';
|
|
380
|
-
}
|
|
381
|
-
if (error) {
|
|
382
|
-
return 'start_failed';
|
|
383
|
-
}
|
|
384
|
-
return exitCode !== null && successExitCodes.includes(exitCode) ? 'passed' : 'failed';
|
|
385
|
-
}
|
|
13
|
+
import { runBuiltinArgvInProcess } from './run/builtin-dispatch.js';
|
|
14
|
+
import { getRunStatus, runArgvCommandStreaming, runShellCommandStreaming } from './run/executor.js';
|
|
15
|
+
import { emitOutput } from './run/output.js';
|
|
16
|
+
import { createPendingTimeoutTermination, getKillMethod, terminateProcessTree } from './run/process-tree.js';
|
|
17
|
+
import { assembleRunReceipt } from './run/receipt.js';
|
|
386
18
|
function getRunPlanDetail(plan, lang, fallbackKey) {
|
|
387
19
|
return plan.detail ?? t(lang, fallbackKey);
|
|
388
20
|
}
|
|
@@ -540,8 +172,6 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
540
172
|
}
|
|
541
173
|
const runReceiptPolicy = profiler.measure('retention_policy', () => resolveRunReceiptRetentionPolicy(readMustflowConfigIfExists(projectRoot)));
|
|
542
174
|
const env = profiler.measure('environment', () => createCommandEnv(projectRoot, { policy: plan.envPolicy, allowlist: plan.envAllowlist }));
|
|
543
|
-
const lifecycleValue = plan.lifecycle ?? 'oneshot';
|
|
544
|
-
const runPolicyValue = plan.runPolicy ?? 'agent_allowed';
|
|
545
175
|
const writeTracker = profiler.measure('write_drift_before', () => startRunWriteTracking(projectRoot, contract, intentName));
|
|
546
176
|
const stdoutTailBytes = Math.min(runReceiptPolicy.stdoutTailBytes, plan.maxOutputBytes);
|
|
547
177
|
const stderrTailBytes = Math.min(runReceiptPolicy.stderrTailBytes, plan.maxOutputBytes);
|
|
@@ -556,17 +186,11 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
556
186
|
}
|
|
557
187
|
}
|
|
558
188
|
if (plan.commandArgv) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return runArgvCommandStreaming(plan.argvCommand, plan.cwd, env, plan.timeoutSeconds, stdoutTailBytes, stderrTailBytes, reporter);
|
|
562
|
-
}
|
|
563
|
-
return runArgvCommand(plan.argvCommand, plan.cwd, plan.maxOutputBytes, env, plan.timeoutSeconds);
|
|
564
|
-
}
|
|
565
|
-
if (!json) {
|
|
566
|
-
streamedOutput = true;
|
|
567
|
-
return runShellCommandStreaming(plan.shellCommand, plan.cwd, env, plan.timeoutSeconds, stdoutTailBytes, stderrTailBytes, reporter);
|
|
189
|
+
streamedOutput = !json;
|
|
190
|
+
return runArgvCommandStreaming(plan.argvCommand, plan.cwd, env, plan.timeoutSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json, json);
|
|
568
191
|
}
|
|
569
|
-
|
|
192
|
+
streamedOutput = !json;
|
|
193
|
+
return runShellCommandStreaming(plan.shellCommand, plan.cwd, env, plan.timeoutSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json, json);
|
|
570
194
|
});
|
|
571
195
|
const childDurationMs = performance.now() - childStartedAtMs;
|
|
572
196
|
const finishedAt = new Date();
|
|
@@ -574,44 +198,28 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
574
198
|
const exitCode = typeof result.status === 'number' ? result.status : null;
|
|
575
199
|
const runStatus = getRunStatus(result.error, exitCode, plan.successExitCodes);
|
|
576
200
|
let killMethod = null;
|
|
201
|
+
let termination = null;
|
|
577
202
|
if (runStatus === 'timed_out') {
|
|
578
|
-
|
|
579
|
-
|
|
203
|
+
termination = result.termination ?? createPendingTimeoutTermination(getKillMethod());
|
|
204
|
+
killMethod = termination.method;
|
|
205
|
+
if (!result.termination && result.pid) {
|
|
206
|
+
terminateProcessTree(result.pid);
|
|
207
|
+
}
|
|
580
208
|
}
|
|
581
|
-
const receipt = profiler.measure('receipt_create', () =>
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
timedOut: runStatus === 'timed_out',
|
|
209
|
+
const receipt = profiler.measure('receipt_create', () => assembleRunReceipt({
|
|
210
|
+
intentName,
|
|
211
|
+
runStatus,
|
|
585
212
|
startedAt,
|
|
586
213
|
finishedAt,
|
|
587
214
|
projectRoot,
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
runPolicy: runPolicyValue,
|
|
591
|
-
mode: plan.mode,
|
|
592
|
-
argv: plan.commandArgv,
|
|
593
|
-
cmd: plan.shellCommand,
|
|
594
|
-
envPolicy: plan.envPolicy,
|
|
595
|
-
envAllowlist: plan.envAllowlist,
|
|
596
|
-
timeoutSeconds: plan.timeoutSeconds,
|
|
597
|
-
maxOutputBytes: plan.maxOutputBytes,
|
|
598
|
-
successExitCodes: plan.successExitCodes,
|
|
215
|
+
plan,
|
|
216
|
+
result,
|
|
599
217
|
exitCode,
|
|
600
|
-
signal: result.signal,
|
|
601
|
-
error: result.error?.message ?? null,
|
|
602
218
|
killMethod,
|
|
603
|
-
|
|
604
|
-
stderr: result.stderr,
|
|
219
|
+
termination,
|
|
605
220
|
writeDrift,
|
|
606
221
|
executorOverheadMs: Math.max(0, Math.round((performance.now() - executorStartedAtMs - childDurationMs) * 1000) / 1000),
|
|
607
222
|
phaseTimings: profiler.getReceiptPhases(),
|
|
608
|
-
selectionSummary: {
|
|
609
|
-
strategy: plan.testTargets.length > 0 ? 'project_test_selection' : 'direct_intent',
|
|
610
|
-
changed_file_count: 0,
|
|
611
|
-
changed_surface_counts: {},
|
|
612
|
-
selected_target_count: Math.max(1, plan.testTargets.length),
|
|
613
|
-
fallback_used: false,
|
|
614
|
-
},
|
|
615
223
|
stdoutTailBytes: runReceiptPolicy.stdoutTailBytes,
|
|
616
224
|
stderrTailBytes: runReceiptPolicy.stderrTailBytes,
|
|
617
225
|
}));
|