mustflow 2.18.2 → 2.18.7
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 -0
- package/dist/cli/commands/run/builtin-dispatch.js +92 -0
- package/dist/cli/commands/run/executor.js +149 -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 +17 -382
- package/dist/cli/commands/verify/args.js +262 -0
- package/dist/cli/commands/verify.js +1 -262
- package/dist/cli/i18n/en.js +1 -0
- package/dist/cli/i18n/es.js +1 -0
- package/dist/cli/i18n/fr.js +1 -0
- package/dist/cli/i18n/hi.js +1 -0
- package/dist/cli/i18n/ko.js +1 -0
- package/dist/cli/i18n/zh.js +1 -0
- package/dist/cli/index.js +6 -72
- package/dist/cli/lib/command-registry.js +27 -0
- package/dist/cli/lib/dashboard-export.js +2 -1
- package/dist/cli/lib/dashboard-html/locale-bootstrap.js +3 -2
- package/dist/cli/lib/dashboard-html/template.js +5 -4
- package/dist/cli/lib/html-json.js +11 -0
- package/dist/cli/lib/local-index/index.js +166 -14
- package/dist/cli/lib/run-plan.js +6 -0
- package/dist/core/check-issues.js +1 -0
- package/dist/core/command-contract-rules.js +0 -3
- package/dist/core/command-contract-validation.js +42 -4
- package/dist/core/command-intent-eligibility.js +4 -4
- package/dist/core/contract-lint.js +3 -3
- package/package.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,365 +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
|
-
}
|
|
23
|
-
const text = (typeof output === 'object' && 'tail' in output ? output.tail : output.toString()).trimEnd();
|
|
24
|
-
if (text.length === 0) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
reporter[stream](text);
|
|
28
|
-
}
|
|
29
|
-
function signalProcessTree(pid, signal) {
|
|
30
|
-
if (!pid || pid <= 0) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (process.platform === 'win32') {
|
|
34
|
-
spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], {
|
|
35
|
-
stdio: 'ignore',
|
|
36
|
-
windowsHide: true,
|
|
37
|
-
});
|
|
38
|
-
if (signal === 'SIGKILL') {
|
|
39
|
-
try {
|
|
40
|
-
process.kill(pid, signal);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// taskkill may already have terminated the direct child.
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
process.kill(-pid, signal);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
try {
|
|
53
|
-
process.kill(pid, signal);
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// The child may already be gone after Node's spawn timeout handling.
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function signalProcessTreeNonBlocking(pid, signal) {
|
|
61
|
-
if (!pid || pid <= 0) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (process.platform === 'win32') {
|
|
65
|
-
const killer = spawn('taskkill', ['/PID', String(pid), '/T', '/F'], {
|
|
66
|
-
stdio: 'ignore',
|
|
67
|
-
windowsHide: true,
|
|
68
|
-
detached: true,
|
|
69
|
-
});
|
|
70
|
-
killer.unref();
|
|
71
|
-
if (signal === 'SIGKILL') {
|
|
72
|
-
try {
|
|
73
|
-
process.kill(pid, signal);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// taskkill may already have terminated the direct child.
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
process.kill(-pid, signal);
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
try {
|
|
86
|
-
process.kill(pid, signal);
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
// The child may already be gone after the timeout fired.
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function terminateProcessTree(pid) {
|
|
94
|
-
signalProcessTree(pid, 'SIGTERM');
|
|
95
|
-
}
|
|
96
|
-
function forceTerminateProcessTree(pid) {
|
|
97
|
-
signalProcessTree(pid, 'SIGKILL');
|
|
98
|
-
}
|
|
99
|
-
function terminateProcessTreeNonBlocking(pid) {
|
|
100
|
-
signalProcessTreeNonBlocking(pid, 'SIGTERM');
|
|
101
|
-
}
|
|
102
|
-
function forceTerminateProcessTreeNonBlocking(pid) {
|
|
103
|
-
signalProcessTreeNonBlocking(pid, 'SIGKILL');
|
|
104
|
-
}
|
|
105
|
-
function getKillMethod() {
|
|
106
|
-
return process.platform === 'win32' ? 'taskkill_process_tree' : 'process_group_sigterm';
|
|
107
|
-
}
|
|
108
|
-
function createPendingTimeoutTermination(method) {
|
|
109
|
-
return {
|
|
110
|
-
reason: 'timeout',
|
|
111
|
-
method,
|
|
112
|
-
graceful_signal: 'SIGTERM',
|
|
113
|
-
forced_signal: 'SIGKILL',
|
|
114
|
-
forced_kill_attempted: true,
|
|
115
|
-
confirmed: false,
|
|
116
|
-
cleanup_pending: true,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
function createBufferedReporter() {
|
|
120
|
-
const stdout = [];
|
|
121
|
-
const stderr = [];
|
|
122
|
-
return {
|
|
123
|
-
reporter: {
|
|
124
|
-
stdout(message) {
|
|
125
|
-
stdout.push(`${message}\n`);
|
|
126
|
-
},
|
|
127
|
-
stderr(message) {
|
|
128
|
-
stderr.push(`${message}\n`);
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
stdout() {
|
|
132
|
-
return stdout.join('');
|
|
133
|
-
},
|
|
134
|
-
stderr() {
|
|
135
|
-
return stderr.join('');
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* mf:anchor cli.run.builtin-inprocess
|
|
141
|
-
* purpose: Dispatch selected mustflow built-in commands without spawning a nested CLI process.
|
|
142
|
-
* search: builtin intent, in-process command, nested mf run, run receipt
|
|
143
|
-
* invariant: Only commands classified by command-classification can use this path.
|
|
144
|
-
* risk: config, state
|
|
145
|
-
*/
|
|
146
|
-
async function runKnownBuiltinCommand(args, reporter, lang) {
|
|
147
|
-
const [command, ...commandArgs] = args;
|
|
148
|
-
if ((command === '--version' || command === '-v' || command === 'version') && commandArgs.length === 0) {
|
|
149
|
-
reporter.stdout(getPackageVersion());
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
if (!canRunMustflowBuiltinInProcess(command)) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
if (command === 'check') {
|
|
156
|
-
return (await import('./check.js')).runCheck(commandArgs, reporter, lang);
|
|
157
|
-
}
|
|
158
|
-
if (command === 'classify') {
|
|
159
|
-
return (await import('./classify.js')).runClassify(commandArgs, reporter, lang);
|
|
160
|
-
}
|
|
161
|
-
if (command === 'context') {
|
|
162
|
-
return (await import('./context.js')).runContext(commandArgs, reporter, lang);
|
|
163
|
-
}
|
|
164
|
-
if (command === 'doctor') {
|
|
165
|
-
return (await import('./doctor.js')).runDoctor(commandArgs, reporter, lang);
|
|
166
|
-
}
|
|
167
|
-
if (command === 'help') {
|
|
168
|
-
return (await import('./help.js')).runHelp(commandArgs, reporter, lang);
|
|
169
|
-
}
|
|
170
|
-
if (command === 'impact') {
|
|
171
|
-
return (await import('./impact.js')).runImpact(commandArgs, reporter, lang);
|
|
172
|
-
}
|
|
173
|
-
if (command === 'line-endings') {
|
|
174
|
-
return (await import('./line-endings.js')).runLineEndings(commandArgs, reporter, lang);
|
|
175
|
-
}
|
|
176
|
-
if (command === 'map') {
|
|
177
|
-
return (await import('./map.js')).runMap(commandArgs, reporter, lang);
|
|
178
|
-
}
|
|
179
|
-
if (command === 'status') {
|
|
180
|
-
return (await import('./status.js')).runStatus(commandArgs, reporter, lang);
|
|
181
|
-
}
|
|
182
|
-
if (command === 'update') {
|
|
183
|
-
return (await import('./update.js')).runUpdate(commandArgs, reporter, lang);
|
|
184
|
-
}
|
|
185
|
-
if (command === 'version-sources') {
|
|
186
|
-
return (await import('./version-sources.js')).runVersionSources(commandArgs, reporter, lang);
|
|
187
|
-
}
|
|
188
|
-
return undefined;
|
|
189
|
-
}
|
|
190
|
-
async function withWorkingDirectory(cwd, callback) {
|
|
191
|
-
const previousCwd = process.cwd();
|
|
192
|
-
process.chdir(cwd);
|
|
193
|
-
try {
|
|
194
|
-
return await callback();
|
|
195
|
-
}
|
|
196
|
-
finally {
|
|
197
|
-
process.chdir(previousCwd);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
async function runBuiltinArgvInProcess(commandArgv, cwd, lang) {
|
|
201
|
-
const [command = '', ...builtinArgs] = commandArgv;
|
|
202
|
-
if (!isMustflowBinName(command)) {
|
|
203
|
-
return undefined;
|
|
204
|
-
}
|
|
205
|
-
const output = createBufferedReporter();
|
|
206
|
-
try {
|
|
207
|
-
const status = await withWorkingDirectory(cwd, () => runKnownBuiltinCommand(builtinArgs, output.reporter, lang));
|
|
208
|
-
if (status === undefined) {
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
status,
|
|
213
|
-
signal: null,
|
|
214
|
-
stdout: output.stdout(),
|
|
215
|
-
stderr: output.stderr(),
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
220
|
-
return {
|
|
221
|
-
status: 1,
|
|
222
|
-
signal: null,
|
|
223
|
-
stdout: output.stdout(),
|
|
224
|
-
stderr: `${output.stderr()}${message}\n`,
|
|
225
|
-
};
|
|
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 createOutputLimitError(stream, maxOutputBytes) {
|
|
244
|
-
return Object.assign(new Error(`${stream} exceeded max_output_bytes (${maxOutputBytes})`), {
|
|
245
|
-
code: OUTPUT_LIMIT_ERROR_CODE,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
function runSpawnedCommandStreaming(command, cwd, env, timeoutSeconds, maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, streamOutput, enforceOutputLimit) {
|
|
249
|
-
return new Promise((resolve) => {
|
|
250
|
-
const stdout = new BoundedOutputBuffer(stdoutTailBytes);
|
|
251
|
-
const stderr = new BoundedOutputBuffer(stderrTailBytes);
|
|
252
|
-
let settled = false;
|
|
253
|
-
let timedOut = false;
|
|
254
|
-
let childError;
|
|
255
|
-
let childPid;
|
|
256
|
-
let stdoutBytes = 0;
|
|
257
|
-
let stderrBytes = 0;
|
|
258
|
-
let timeout;
|
|
259
|
-
let termination = null;
|
|
260
|
-
const child = spawn(command.executable, command.args ?? [], {
|
|
261
|
-
cwd,
|
|
262
|
-
env,
|
|
263
|
-
shell: command.shell,
|
|
264
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
265
|
-
windowsHide: true,
|
|
266
|
-
detached: process.platform !== 'win32',
|
|
267
|
-
});
|
|
268
|
-
childPid = child.pid;
|
|
269
|
-
const finish = (status, signal) => {
|
|
270
|
-
if (settled) {
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
settled = true;
|
|
274
|
-
if (timeout) {
|
|
275
|
-
clearTimeout(timeout);
|
|
276
|
-
}
|
|
277
|
-
resolve({
|
|
278
|
-
status: timedOut ? null : status,
|
|
279
|
-
signal: timedOut ? null : signal,
|
|
280
|
-
error: timedOut ? Object.assign(new Error('Command timed out'), { code: 'ETIMEDOUT' }) : childError,
|
|
281
|
-
stdout: stdout.toSnapshot(),
|
|
282
|
-
stderr: stderr.toSnapshot(),
|
|
283
|
-
pid: childPid,
|
|
284
|
-
termination,
|
|
285
|
-
});
|
|
286
|
-
};
|
|
287
|
-
const stopForOutputLimit = (stream) => {
|
|
288
|
-
if (settled || childError) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
childError = createOutputLimitError(stream, maxOutputBytes);
|
|
292
|
-
child.stdout?.destroy();
|
|
293
|
-
child.stderr?.destroy();
|
|
294
|
-
child.unref();
|
|
295
|
-
terminateProcessTreeNonBlocking(childPid);
|
|
296
|
-
forceTerminateProcessTreeNonBlocking(childPid);
|
|
297
|
-
finish(null, null);
|
|
298
|
-
};
|
|
299
|
-
child.stdout?.on('data', (chunk) => {
|
|
300
|
-
stdout.append(chunk);
|
|
301
|
-
stdoutBytes += chunk.byteLength;
|
|
302
|
-
if (streamOutput) {
|
|
303
|
-
writeStreamChunk(reporter, 'stdout', chunk);
|
|
304
|
-
}
|
|
305
|
-
if (enforceOutputLimit && stdoutBytes > maxOutputBytes) {
|
|
306
|
-
stopForOutputLimit('stdout');
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
child.stderr?.on('data', (chunk) => {
|
|
310
|
-
stderr.append(chunk);
|
|
311
|
-
stderrBytes += chunk.byteLength;
|
|
312
|
-
if (streamOutput) {
|
|
313
|
-
writeStreamChunk(reporter, 'stderr', chunk);
|
|
314
|
-
}
|
|
315
|
-
if (enforceOutputLimit && stderrBytes > maxOutputBytes) {
|
|
316
|
-
stopForOutputLimit('stderr');
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
child.once('error', (error) => {
|
|
320
|
-
childError = error;
|
|
321
|
-
});
|
|
322
|
-
child.once('close', (status, signal) => {
|
|
323
|
-
finish(status, signal);
|
|
324
|
-
});
|
|
325
|
-
timeout = setTimeout(() => {
|
|
326
|
-
timedOut = true;
|
|
327
|
-
child.stdout?.destroy();
|
|
328
|
-
child.stderr?.destroy();
|
|
329
|
-
child.unref();
|
|
330
|
-
termination = createPendingTimeoutTermination(getKillMethod());
|
|
331
|
-
terminateProcessTreeNonBlocking(childPid);
|
|
332
|
-
forceTerminateProcessTreeNonBlocking(childPid);
|
|
333
|
-
finish(null, null);
|
|
334
|
-
}, timeoutSeconds * 1000);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
function runArgvCommandStreaming(command, cwd, env, timeoutSeconds, maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, streamOutput, enforceOutputLimit) {
|
|
338
|
-
return runSpawnedCommandStreaming({ executable: command?.executable ?? '', args: command?.args ?? [], shell: command?.shell ?? false }, cwd, env, timeoutSeconds, maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, streamOutput, enforceOutputLimit);
|
|
339
|
-
}
|
|
340
|
-
function runShellCommandStreaming(command, cwd, env, timeoutSeconds, maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, streamOutput, enforceOutputLimit) {
|
|
341
|
-
return runSpawnedCommandStreaming({ executable: command ?? '', shell: true }, cwd, env, timeoutSeconds, maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, streamOutput, enforceOutputLimit);
|
|
342
|
-
}
|
|
343
|
-
function getRunStatus(error, exitCode, successExitCodes) {
|
|
344
|
-
const errorWithCode = error;
|
|
345
|
-
if (errorWithCode?.code === 'ETIMEDOUT') {
|
|
346
|
-
return 'timed_out';
|
|
347
|
-
}
|
|
348
|
-
if (isOutputLimitExceededError(error)) {
|
|
349
|
-
return 'output_limit_exceeded';
|
|
350
|
-
}
|
|
351
|
-
if (error) {
|
|
352
|
-
return 'start_failed';
|
|
353
|
-
}
|
|
354
|
-
return exitCode !== null && successExitCodes.includes(exitCode) ? 'passed' : 'failed';
|
|
355
|
-
}
|
|
356
|
-
function isOutputLimitExceededError(error) {
|
|
357
|
-
if (!error) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
const errorWithCode = error;
|
|
361
|
-
return errorWithCode.code === OUTPUT_LIMIT_ERROR_CODE || OUTPUT_LIMIT_ERROR_MESSAGE.test(error.message);
|
|
362
|
-
}
|
|
13
|
+
import { runBuiltinArgvInProcess } from './run/builtin-dispatch.js';
|
|
14
|
+
import { getRunStatus, runArgvCommandStreaming, runShellCommandStreaming } from './run/executor.js';
|
|
15
|
+
import { emitOutput, isOutputLimitExceededError } from './run/output.js';
|
|
16
|
+
import { createPendingTimeoutTermination, getKillMethod, terminateProcessTree } from './run/process-tree.js';
|
|
17
|
+
import { assembleRunReceipt } from './run/receipt.js';
|
|
363
18
|
function getRunPlanDetail(plan, lang, fallbackKey) {
|
|
364
19
|
return plan.detail ?? t(lang, fallbackKey);
|
|
365
20
|
}
|
|
@@ -517,8 +172,6 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
517
172
|
}
|
|
518
173
|
const runReceiptPolicy = profiler.measure('retention_policy', () => resolveRunReceiptRetentionPolicy(readMustflowConfigIfExists(projectRoot)));
|
|
519
174
|
const env = profiler.measure('environment', () => createCommandEnv(projectRoot, { policy: plan.envPolicy, allowlist: plan.envAllowlist }));
|
|
520
|
-
const lifecycleValue = plan.lifecycle ?? 'oneshot';
|
|
521
|
-
const runPolicyValue = plan.runPolicy ?? 'agent_allowed';
|
|
522
175
|
const writeTracker = profiler.measure('write_drift_before', () => startRunWriteTracking(projectRoot, contract, intentName));
|
|
523
176
|
const stdoutTailBytes = Math.min(runReceiptPolicy.stdoutTailBytes, plan.maxOutputBytes);
|
|
524
177
|
const stderrTailBytes = Math.min(runReceiptPolicy.stderrTailBytes, plan.maxOutputBytes);
|
|
@@ -534,10 +187,10 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
534
187
|
}
|
|
535
188
|
if (plan.commandArgv) {
|
|
536
189
|
streamedOutput = !json;
|
|
537
|
-
return runArgvCommandStreaming(plan.argvCommand, plan.cwd, env, plan.timeoutSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json,
|
|
190
|
+
return runArgvCommandStreaming(plan.argvCommand, plan.cwd, env, plan.timeoutSeconds, plan.killAfterSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json, true);
|
|
538
191
|
}
|
|
539
192
|
streamedOutput = !json;
|
|
540
|
-
return runShellCommandStreaming(plan.shellCommand, plan.cwd, env, plan.timeoutSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json,
|
|
193
|
+
return runShellCommandStreaming(plan.shellCommand, plan.cwd, env, plan.timeoutSeconds, plan.killAfterSeconds, plan.maxOutputBytes, stdoutTailBytes, stderrTailBytes, reporter, !json, true);
|
|
541
194
|
});
|
|
542
195
|
const childDurationMs = performance.now() - childStartedAtMs;
|
|
543
196
|
const finishedAt = new Date();
|
|
@@ -553,44 +206,22 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
553
206
|
terminateProcessTree(result.pid);
|
|
554
207
|
}
|
|
555
208
|
}
|
|
556
|
-
const receipt = profiler.measure('receipt_create', () =>
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
timedOut: runStatus === 'timed_out',
|
|
209
|
+
const receipt = profiler.measure('receipt_create', () => assembleRunReceipt({
|
|
210
|
+
intentName,
|
|
211
|
+
runStatus,
|
|
560
212
|
startedAt,
|
|
561
213
|
finishedAt,
|
|
562
214
|
projectRoot,
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
runPolicy: runPolicyValue,
|
|
566
|
-
mode: plan.mode,
|
|
567
|
-
argv: plan.commandArgv,
|
|
568
|
-
cmd: plan.shellCommand,
|
|
569
|
-
envPolicy: plan.envPolicy,
|
|
570
|
-
envAllowlist: plan.envAllowlist,
|
|
571
|
-
timeoutSeconds: plan.timeoutSeconds,
|
|
572
|
-
maxOutputBytes: plan.maxOutputBytes,
|
|
573
|
-
successExitCodes: plan.successExitCodes,
|
|
215
|
+
plan,
|
|
216
|
+
result,
|
|
574
217
|
exitCode,
|
|
575
|
-
signal: result.signal,
|
|
576
|
-
error: result.error?.message ?? null,
|
|
577
218
|
killMethod,
|
|
578
219
|
termination,
|
|
579
|
-
stdout: result.stdout,
|
|
580
|
-
stderr: result.stderr,
|
|
581
220
|
writeDrift,
|
|
582
221
|
executorOverheadMs: Math.max(0, Math.round((performance.now() - executorStartedAtMs - childDurationMs) * 1000) / 1000),
|
|
583
222
|
phaseTimings: profiler.getReceiptPhases(),
|
|
584
|
-
selectionSummary: {
|
|
585
|
-
strategy: plan.testTargets.length > 0 ? 'project_test_selection' : 'direct_intent',
|
|
586
|
-
changed_file_count: 0,
|
|
587
|
-
changed_surface_counts: {},
|
|
588
|
-
selected_target_count: Math.max(1, plan.testTargets.length),
|
|
589
|
-
fallback_used: false,
|
|
590
|
-
},
|
|
591
223
|
stdoutTailBytes: runReceiptPolicy.stdoutTailBytes,
|
|
592
224
|
stderrTailBytes: runReceiptPolicy.stderrTailBytes,
|
|
593
|
-
receiptPath: createRunReceiptRelativePath(),
|
|
594
225
|
}));
|
|
595
226
|
if (options.writeLatestReceipt !== false) {
|
|
596
227
|
profiler.measure('receipt_write', () => writeRunReceipt(projectRoot, receipt));
|
|
@@ -616,6 +247,10 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
|
|
|
616
247
|
reporter.stderr(t(lang, 'run.error.timedOut', { intent: intentName, seconds: plan.timeoutSeconds }));
|
|
617
248
|
return 1;
|
|
618
249
|
}
|
|
250
|
+
if (isOutputLimitExceededError(result.error)) {
|
|
251
|
+
reporter.stderr(t(lang, 'run.error.outputLimitExceeded', { intent: intentName, message: result.error.message }));
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
619
254
|
reporter.stderr(t(lang, 'run.error.startFailed', { intent: intentName, message: result.error.message }));
|
|
620
255
|
return 1;
|
|
621
256
|
}
|