nolo-cli 0.1.38 → 0.1.40
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 +4 -4
- package/agent-runtime/agentRecordConfig.ts +0 -14
- package/agent-runtime/dialogWritePlan.ts +39 -0
- package/agent-runtime/hostAdapter.ts +4 -1
- package/agent-runtime/hybridRecordStore.ts +1 -1
- package/agent-runtime/index.ts +39 -18
- package/agent-runtime/localDialogRead.ts +23 -0
- package/agent-runtime/localLoop.ts +87 -7
- package/agent-runtime/localToolPolicy.ts +1 -17
- package/agent-runtime/localWorkspaceTools.ts +51 -110
- package/agent-runtime/noloWorkspaceTools.ts +371 -0
- package/agent-runtime/runtimeToolPolicy.ts +3 -14
- package/agent-runtime/runtimeToolSurface.ts +198 -0
- package/agent-runtime/types.ts +2 -1
- package/agentAliases.ts +8 -1
- package/agentRunCommand.ts +65 -95
- package/agentRuntimeLocal.ts +1 -7
- package/ai/agent/cliExecutor.ts +249 -34
- package/chat/messages/fetchMessages.ts +84 -0
- package/chat/messages/types.ts +185 -0
- package/cli/agentAliases.ts +8 -1
- package/cli/agentRunCommand.ts +65 -95
- package/cli/agentRuntimeLocal.ts +1 -7
- package/cli/cliEnvHelpers.ts +11 -7
- package/cli/client/agentRun.ts +94 -93
- package/cli/client/localRuntimeAdapter.ts +250 -69
- package/cli/commandRegistry.ts +2 -2
- package/cli/connectorRunArtifact.ts +0 -1
- package/cli/dialogCommands.ts +572 -0
- package/cli/dialogInternalCommandEntries.ts +4 -0
- package/cli/machineWsRunDispatch.ts +76 -39
- package/cli/offlineMarxistsAgentCommand.ts +6 -4
- package/cli/scriptCommandEntries.ts +0 -2
- package/cli/tui/readlineWorkspace.ts +69 -0
- package/cli/tui/session.ts +176 -1
- package/cliEnvHelpers.ts +11 -7
- package/client/agentConfigResolver.test.ts +4 -5
- package/client/agentRun.test.ts +307 -57
- package/client/agentRun.ts +94 -93
- package/client/localRuntimeAdapter.test.ts +332 -53
- package/client/localRuntimeAdapter.ts +250 -69
- package/client/localRuntimeDryRun.test.ts +23 -9
- package/client/localToolPolicy.test.ts +4 -4
- package/commandRegistry.ts +2 -2
- package/connectorRunArtifact.ts +0 -1
- package/database/server/db.ts +99 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/levelAuthorityStore.ts +20 -1
- package/database/server/memoryAuthorityStore.ts +133 -0
- package/database/server/serverStoreFactory.ts +118 -0
- package/dialogCommands.ts +572 -0
- package/dialogInternalCommandEntries.ts +4 -0
- package/machineWsRunDispatch.ts +76 -39
- package/offlineMarxistsAgentCommand.ts +6 -4
- package/package.json +11 -9
- package/scriptCommandEntries.ts +0 -2
- package/tui/readlineWorkspace.ts +69 -0
- package/tui/session.ts +176 -1
- package/agent-runtime/taskWorkspace.ts +0 -193
- package/agent-runtime/workspaceSession.ts +0 -76
- package/cli/client/taskWorktree.ts +0 -8
- package/cli/client/workspaceSession.ts +0 -11
- package/client/taskWorktree.ts +0 -8
- package/client/workspaceSession.test.ts +0 -57
- package/client/workspaceSession.ts +0 -11
package/client/agentRun.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../agentAliases";
|
|
10
10
|
import type { LocalAgentToolEvent } from "../agent-runtime/localLoop";
|
|
11
11
|
import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "../agentRuntimeLocal";
|
|
12
|
-
import { createCliLocalRuntimeAdapter } from "./localRuntimeAdapter";
|
|
12
|
+
import { createCliLocalRuntimeAdapter, isBuiltinNoloAgentRef } from "./localRuntimeAdapter";
|
|
13
13
|
import { createStreamingTextWriter } from "./streamingOutput";
|
|
14
14
|
import { prependTaskRunPrompt, type TaskRunPromptContext } from "./taskRunPrompt";
|
|
15
15
|
|
|
@@ -44,7 +44,6 @@ type RunAgentTurnOptions = {
|
|
|
44
44
|
parentDialogId?: string;
|
|
45
45
|
background?: boolean;
|
|
46
46
|
noStream?: boolean;
|
|
47
|
-
noDefaultTestRoot?: boolean;
|
|
48
47
|
scriptDir: string;
|
|
49
48
|
env: EnvLike;
|
|
50
49
|
output: OutputLike;
|
|
@@ -52,9 +51,9 @@ type RunAgentTurnOptions = {
|
|
|
52
51
|
localRuntimeAdapter?: AgentRuntimeHostAdapter;
|
|
53
52
|
localRuntimeAdapterFactory?: (env: EnvLike, options?: { cwd?: string }) => AgentRuntimeHostAdapter;
|
|
54
53
|
localRuntimeCwd?: string;
|
|
55
|
-
maxToolRounds?: number;
|
|
56
54
|
timeoutMs?: number;
|
|
57
55
|
traceTools?: boolean;
|
|
56
|
+
eventsMode?: "jsonl";
|
|
58
57
|
taskRunContext?: TaskRunPromptContext;
|
|
59
58
|
taskRunRecorder?: (args: {
|
|
60
59
|
options: RunAgentTurnOptions;
|
|
@@ -180,10 +179,27 @@ function isKnownServerPlatformAgent(options: RunAgentTurnOptions) {
|
|
|
180
179
|
const normalizedKey = normalizeAgentRef(options.agentKey);
|
|
181
180
|
return Boolean(normalizedKey && KNOWN_SERVER_PLATFORM_AGENT_ALIASES.has(normalizedKey));
|
|
182
181
|
}
|
|
183
|
-
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
182
|
+
|
|
183
|
+
function isMachineBoundLocalhostCustomProvider(agentConfig: any) {
|
|
184
|
+
const machineId =
|
|
185
|
+
agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object"
|
|
186
|
+
? String(agentConfig.runtimeBinding.machineId ?? "").trim()
|
|
187
|
+
: "";
|
|
188
|
+
const providerUrl = typeof agentConfig?.customProviderUrl === "string"
|
|
189
|
+
? agentConfig.customProviderUrl.trim()
|
|
190
|
+
: "";
|
|
191
|
+
if (!machineId || !providerUrl) return false;
|
|
192
|
+
try {
|
|
193
|
+
const hostname = new URL(providerUrl).hostname.toLowerCase();
|
|
194
|
+
return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
|
|
195
|
+
} catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function resolveAuthToken(env: EnvLike) {
|
|
201
|
+
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
202
|
+
}
|
|
187
203
|
|
|
188
204
|
function shouldShowUsage(env: EnvLike) {
|
|
189
205
|
return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
|
|
@@ -214,6 +230,10 @@ function resolveLocalRuntimeAdapter(options: RunAgentTurnOptions) {
|
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
async function shouldSkipAutoLocalForServerPlatformTools(options: RunAgentTurnOptions) {
|
|
233
|
+
if (isBuiltinNoloAgentRef(options.agentKey)) return false;
|
|
234
|
+
if (options.localRuntimeCwd) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
217
237
|
if (isKnownServerPlatformAgent(options)) {
|
|
218
238
|
options.output.write(
|
|
219
239
|
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. ` +
|
|
@@ -229,6 +249,13 @@ async function shouldSkipAutoLocalForServerPlatformTools(options: RunAgentTurnOp
|
|
|
229
249
|
} catch {
|
|
230
250
|
return false;
|
|
231
251
|
}
|
|
252
|
+
if (isMachineBoundLocalhostCustomProvider(agentConfig)) {
|
|
253
|
+
options.output.write(
|
|
254
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a machine-bound localhost custom provider. ` +
|
|
255
|
+
"Use --local explicitly to force the current machine.\n"
|
|
256
|
+
);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
232
259
|
const serverTools = resolveServerPlatformToolNames(agentConfig);
|
|
233
260
|
if (serverTools.length === 0) return false;
|
|
234
261
|
options.output.write(
|
|
@@ -250,7 +277,14 @@ function buildUserInputContent(message: string, imageUrls: string[] = []) {
|
|
|
250
277
|
}
|
|
251
278
|
|
|
252
279
|
function shouldTraceLocalTools(options: RunAgentTurnOptions) {
|
|
253
|
-
|
|
280
|
+
const setting = (options.env.NOLO_TRACE_TOOLS ?? "").trim().toLowerCase();
|
|
281
|
+
if (setting === "0" || setting === "false" || setting === "off") return false;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function resolveAgentEventMode(options: RunAgentTurnOptions): "text" | "jsonl" {
|
|
286
|
+
if (options.eventsMode === "jsonl") return "jsonl";
|
|
287
|
+
return options.env.NOLO_AGENT_EVENTS === "jsonl" ? "jsonl" : "text";
|
|
254
288
|
}
|
|
255
289
|
|
|
256
290
|
function shouldCollectLocalToolEvidence(options: RunAgentTurnOptions) {
|
|
@@ -279,15 +313,6 @@ function summarizeLocalToolEvidence(events: LocalAgentToolEvent[]): TaskRunToolE
|
|
|
279
313
|
};
|
|
280
314
|
}
|
|
281
315
|
|
|
282
|
-
const DEFAULT_LOCAL_AGENT_TOOL_ROUND_RETRY_LIMIT = 512;
|
|
283
|
-
|
|
284
|
-
function parseLocalToolRoundLimitError(error: unknown) {
|
|
285
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
286
|
-
const match = message.match(/Local agent exceeded max tool rounds:\s*(\d+)/);
|
|
287
|
-
if (!match) return null;
|
|
288
|
-
return Number(match[1]);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
316
|
function isMissingLocalAgentConfigError(error: unknown, agentRef: string) {
|
|
292
317
|
return Boolean(
|
|
293
318
|
error &&
|
|
@@ -298,37 +323,44 @@ function isMissingLocalAgentConfigError(error: unknown, agentRef: string) {
|
|
|
298
323
|
);
|
|
299
324
|
}
|
|
300
325
|
|
|
301
|
-
function parsePositiveInteger(value: string | undefined) {
|
|
302
|
-
if (!value) return null;
|
|
303
|
-
const parsed = Number(value);
|
|
304
|
-
if (!Number.isInteger(parsed) || parsed <= 0) return null;
|
|
305
|
-
return parsed;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function resolveLocalToolRoundRetryLimit(options: RunAgentTurnOptions) {
|
|
309
|
-
return (
|
|
310
|
-
parsePositiveInteger(options.env.NOLO_LOCAL_MAX_TOOL_ROUNDS_LIMIT) ??
|
|
311
|
-
DEFAULT_LOCAL_AGENT_TOOL_ROUND_RETRY_LIMIT
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function nextLocalToolRoundLimit(current: number) {
|
|
316
|
-
return current <= 0 ? 1 : current * 2;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
326
|
function formatToolTraceEvent(event: LocalAgentToolEvent) {
|
|
320
327
|
const round = event.round + 1;
|
|
328
|
+
const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
|
|
321
329
|
if (event.type === "tool-call") {
|
|
322
|
-
return `[nolo:tool]
|
|
330
|
+
return `[nolo:tool] #${round} -> ${event.toolName}${detail}\n`;
|
|
323
331
|
}
|
|
324
332
|
if (event.type === "tool-error") {
|
|
325
|
-
|
|
333
|
+
const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
|
|
334
|
+
return `[nolo:tool] #${round} !! ${event.toolName}${elapsed}: ${event.message ?? "failed"}\n`;
|
|
326
335
|
}
|
|
327
|
-
|
|
336
|
+
const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
|
|
337
|
+
const summary = event.summary ? ` ${event.summary}` : "";
|
|
338
|
+
return `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}\n`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function formatToolJsonEvent(event: LocalAgentToolEvent) {
|
|
342
|
+
return `${JSON.stringify({
|
|
343
|
+
schemaVersion: 1,
|
|
344
|
+
type: event.type,
|
|
345
|
+
round: event.round + 1,
|
|
346
|
+
tool: event.toolName,
|
|
347
|
+
toolCallId: event.toolCallId,
|
|
348
|
+
...(event.argumentsPreview ? { argsPreview: event.argumentsPreview } : {}),
|
|
349
|
+
...(typeof event.elapsedMs === "number" ? { elapsedMs: event.elapsedMs } : {}),
|
|
350
|
+
...(event.summary ? { summary: event.summary } : {}),
|
|
351
|
+
...(event.message ? { message: event.message } : {}),
|
|
352
|
+
})}\n`;
|
|
328
353
|
}
|
|
329
354
|
|
|
330
355
|
function shouldAttemptAutoLocal(options: RunAgentTurnOptions) {
|
|
331
356
|
if (options.localRuntimeAdapter || options.localRuntimeAdapterFactory) return true;
|
|
357
|
+
if (
|
|
358
|
+
options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" &&
|
|
359
|
+
isBuiltinNoloAgentRef(options.agentKey) &&
|
|
360
|
+
resolveAuthToken(options.env)
|
|
361
|
+
) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
332
364
|
return Boolean(
|
|
333
365
|
options.env.NOLO_LOCAL_OPENAI_API_KEY ||
|
|
334
366
|
options.env.OPENAI_API_KEY ||
|
|
@@ -594,7 +626,6 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
|
|
|
594
626
|
: {}),
|
|
595
627
|
...(options.parentDialogId ? { parentDialogId: options.parentDialogId } : {}),
|
|
596
628
|
...(options.background ? { background: true } : {}),
|
|
597
|
-
...(typeof options.maxToolRounds === "number" ? { maxRounds: options.maxToolRounds } : {}),
|
|
598
629
|
...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
|
|
599
630
|
stream: !options.noStream && !options.background,
|
|
600
631
|
}),
|
|
@@ -632,8 +663,21 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
|
|
|
632
663
|
|
|
633
664
|
if (!res.ok) {
|
|
634
665
|
options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
|
|
635
|
-
|
|
636
|
-
|
|
666
|
+
const errorText = typeof data?.error === "string" ? data.error.trim() : "";
|
|
667
|
+
const messageText = typeof data?.message === "string" ? data.message.trim() : "";
|
|
668
|
+
const reasonText = typeof data?.reason === "string" ? data.reason.trim() : "";
|
|
669
|
+
const codeText = typeof data?.code === "string" ? data.code.trim() : "";
|
|
670
|
+
if (errorText || messageText) {
|
|
671
|
+
options.output.write(`${errorText || messageText}\n`);
|
|
672
|
+
if (messageText && messageText !== errorText) {
|
|
673
|
+
options.output.write(`${messageText}\n`);
|
|
674
|
+
}
|
|
675
|
+
if (codeText && codeText !== errorText && codeText !== messageText) {
|
|
676
|
+
options.output.write(`code=${codeText}\n`);
|
|
677
|
+
}
|
|
678
|
+
if (reasonText && reasonText !== errorText && reasonText !== messageText) {
|
|
679
|
+
options.output.write(`reason=${reasonText}\n`);
|
|
680
|
+
}
|
|
637
681
|
}
|
|
638
682
|
await recordTaskRunOutcome({
|
|
639
683
|
options,
|
|
@@ -669,7 +713,7 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
|
|
|
669
713
|
}
|
|
670
714
|
|
|
671
715
|
async function runInjectedLocalAgentTurn(options: RunAgentTurnOptions) {
|
|
672
|
-
return
|
|
716
|
+
return runLocalAgentTurnForCli(options, { reportFailure: true });
|
|
673
717
|
}
|
|
674
718
|
|
|
675
719
|
async function refreshMissingLocalAgentConfig(options: RunAgentTurnOptions) {
|
|
@@ -679,53 +723,6 @@ async function refreshMissingLocalAgentConfig(options: RunAgentTurnOptions) {
|
|
|
679
723
|
return Boolean(agentConfig);
|
|
680
724
|
}
|
|
681
725
|
|
|
682
|
-
async function runLocalAgentTurnWithAutoRoundExpansion(
|
|
683
|
-
options: RunAgentTurnOptions,
|
|
684
|
-
settings: { reportFailure: boolean }
|
|
685
|
-
) {
|
|
686
|
-
const maxRetryLimit = resolveLocalToolRoundRetryLimit(options);
|
|
687
|
-
let currentMaxToolRounds = options.maxToolRounds;
|
|
688
|
-
let lastResult: Awaited<ReturnType<typeof runLocalAgentTurnForCli>> | null = null;
|
|
689
|
-
|
|
690
|
-
while (true) {
|
|
691
|
-
const attemptOptions = {
|
|
692
|
-
...options,
|
|
693
|
-
...(typeof currentMaxToolRounds === "number" ? { maxToolRounds: currentMaxToolRounds } : {}),
|
|
694
|
-
};
|
|
695
|
-
const result = await runLocalAgentTurnForCli(attemptOptions, { reportFailure: false });
|
|
696
|
-
lastResult = result;
|
|
697
|
-
if (result.exitCode === 0) return result;
|
|
698
|
-
|
|
699
|
-
const exhaustedRounds = parseLocalToolRoundLimitError(result.localError);
|
|
700
|
-
if (exhaustedRounds === null) break;
|
|
701
|
-
|
|
702
|
-
const nextMaxToolRounds = nextLocalToolRoundLimit(exhaustedRounds);
|
|
703
|
-
if (nextMaxToolRounds > maxRetryLimit) break;
|
|
704
|
-
|
|
705
|
-
options.output.write(
|
|
706
|
-
`[nolo] Local agent used all ${exhaustedRounds} tool rounds; retrying with ${nextMaxToolRounds}.\n`
|
|
707
|
-
);
|
|
708
|
-
currentMaxToolRounds = nextMaxToolRounds;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (settings.reportFailure && lastResult?.localError) {
|
|
712
|
-
options.output.write(
|
|
713
|
-
`[nolo] Local agent run failed: ${
|
|
714
|
-
lastResult.localError instanceof Error ? lastResult.localError.message : String(lastResult.localError)
|
|
715
|
-
}\n`
|
|
716
|
-
);
|
|
717
|
-
await recordTaskRunOutcome({
|
|
718
|
-
options,
|
|
719
|
-
status: "failed",
|
|
720
|
-
errorCode: "LOCAL_AGENT_FAILED",
|
|
721
|
-
resultSummary:
|
|
722
|
-
lastResult.localError instanceof Error ? lastResult.localError.message : String(lastResult.localError),
|
|
723
|
-
toolEvidence: lastResult.toolEvidence,
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
return lastResult ?? { exitCode: 1 };
|
|
727
|
-
}
|
|
728
|
-
|
|
729
726
|
async function runLocalAgentTurnForCli(
|
|
730
727
|
options: RunAgentTurnOptions,
|
|
731
728
|
settings: { reportFailure: boolean }
|
|
@@ -742,6 +739,7 @@ async function runLocalAgentTurnForCli(
|
|
|
742
739
|
try {
|
|
743
740
|
const traceLocalTools = shouldTraceLocalTools(options);
|
|
744
741
|
const collectToolEvidence = shouldCollectLocalToolEvidence(options);
|
|
742
|
+
const eventMode = resolveAgentEventMode(options);
|
|
745
743
|
const result = await runLocalAgentTurn({
|
|
746
744
|
adapter,
|
|
747
745
|
agentRef: options.agentKey,
|
|
@@ -756,7 +754,6 @@ async function runLocalAgentTurnForCli(
|
|
|
756
754
|
parentDialogId: options.parentDialogId,
|
|
757
755
|
background: options.background,
|
|
758
756
|
noStream: options.noStream,
|
|
759
|
-
maxToolRounds: options.maxToolRounds,
|
|
760
757
|
...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
|
|
761
758
|
...(traceLocalTools || collectToolEvidence
|
|
762
759
|
? {
|
|
@@ -764,7 +761,11 @@ async function runLocalAgentTurnForCli(
|
|
|
764
761
|
if (collectToolEvidence) toolEvents.push(event);
|
|
765
762
|
if (traceLocalTools) {
|
|
766
763
|
spinner.stop();
|
|
767
|
-
options.output.write(
|
|
764
|
+
options.output.write(
|
|
765
|
+
eventMode === "jsonl"
|
|
766
|
+
? formatToolJsonEvent(event)
|
|
767
|
+
: formatToolTraceEvent(event)
|
|
768
|
+
);
|
|
768
769
|
}
|
|
769
770
|
},
|
|
770
771
|
}
|
|
@@ -920,7 +921,7 @@ export async function runAgentTurn(options: RunAgentTurnOptions) {
|
|
|
920
921
|
if (runtimeMode === "auto" && shouldAttemptAutoLocal(options)) {
|
|
921
922
|
const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
|
|
922
923
|
if (!skipLocal) {
|
|
923
|
-
const localResult = await
|
|
924
|
+
const localResult = await runLocalAgentTurnForCli(options, { reportFailure: false });
|
|
924
925
|
if (localResult.exitCode === 0) {
|
|
925
926
|
return {
|
|
926
927
|
exitCode: localResult.exitCode,
|
|
@@ -934,7 +935,7 @@ export async function runAgentTurn(options: RunAgentTurnOptions) {
|
|
|
934
935
|
try {
|
|
935
936
|
const refreshed = await refreshMissingLocalAgentConfig(options);
|
|
936
937
|
if (refreshed) {
|
|
937
|
-
const retriedLocalResult = await
|
|
938
|
+
const retriedLocalResult = await runLocalAgentTurnForCli(options, {
|
|
938
939
|
reportFailure: false,
|
|
939
940
|
});
|
|
940
941
|
if (retriedLocalResult.exitCode === 0) {
|