@cuna_labs/cli 0.1.0 → 0.1.1
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/dist/bin/cuna.js +15 -2
- package/dist/bin/cuna.js.map +1 -1
- package/dist/cli/command-help.js +2 -2
- package/dist/cli/command-help.js.map +1 -1
- package/dist/cli/first-line.d.ts +80 -0
- package/dist/cli/first-line.d.ts.map +1 -0
- package/dist/cli/first-line.js +119 -0
- package/dist/cli/first-line.js.map +1 -0
- package/dist/cli/help.js +1 -1
- package/dist/cli/process-entrypoint.d.ts +3 -0
- package/dist/cli/process-entrypoint.d.ts.map +1 -1
- package/dist/cli/process-entrypoint.js +3 -1
- package/dist/cli/process-entrypoint.js.map +1 -1
- package/dist/cli/progress-line.d.ts +78 -0
- package/dist/cli/progress-line.d.ts.map +1 -0
- package/dist/cli/progress-line.js +94 -0
- package/dist/cli/progress-line.js.map +1 -0
- package/dist/cli/recorded-launch-prompt.d.ts +83 -0
- package/dist/cli/recorded-launch-prompt.d.ts.map +1 -0
- package/dist/cli/recorded-launch-prompt.js +80 -0
- package/dist/cli/recorded-launch-prompt.js.map +1 -0
- package/dist/cli/run.d.ts +19 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +233 -75
- package/dist/cli/run.js.map +1 -1
- package/dist/commands/commands.js +6 -4
- package/dist/commands/commands.js.map +1 -1
- package/dist/journey/account-identity.d.ts +55 -0
- package/dist/journey/account-identity.d.ts.map +1 -0
- package/dist/journey/account-identity.js +69 -0
- package/dist/journey/account-identity.js.map +1 -0
- package/dist/journey/api-effects.d.ts +9 -0
- package/dist/journey/api-effects.d.ts.map +1 -1
- package/dist/journey/api-effects.js +172 -19
- package/dist/journey/api-effects.js.map +1 -1
- package/dist/journey/index.d.ts +3 -1
- package/dist/journey/index.d.ts.map +1 -1
- package/dist/journey/index.js +2 -0
- package/dist/journey/index.js.map +1 -1
- package/dist/journey/orchestrator.d.ts +24 -0
- package/dist/journey/orchestrator.d.ts.map +1 -1
- package/dist/journey/orchestrator.js +21 -3
- package/dist/journey/orchestrator.js.map +1 -1
- package/dist/journey/provider-launch-intent.d.ts +11 -0
- package/dist/journey/provider-launch-intent.d.ts.map +1 -1
- package/dist/journey/provider-launch-intent.js +50 -15
- package/dist/journey/provider-launch-intent.js.map +1 -1
- package/dist/journey/remote-workspace.d.ts +11 -0
- package/dist/journey/remote-workspace.d.ts.map +1 -1
- package/dist/journey/remote-workspace.js +94 -14
- package/dist/journey/remote-workspace.js.map +1 -1
- package/dist/journey/root-entry.d.ts +15 -0
- package/dist/journey/root-entry.d.ts.map +1 -1
- package/dist/journey/root-entry.js +18 -6
- package/dist/journey/root-entry.js.map +1 -1
- package/dist/journey/selection.d.ts +9 -0
- package/dist/journey/selection.d.ts.map +1 -1
- package/dist/journey/selection.js +14 -2
- package/dist/journey/selection.js.map +1 -1
- package/dist/journey/session-disposition.d.ts +45 -0
- package/dist/journey/session-disposition.d.ts.map +1 -0
- package/dist/journey/session-disposition.js +7 -0
- package/dist/journey/session-disposition.js.map +1 -0
- package/dist/journey/wait-policy.d.ts +218 -0
- package/dist/journey/wait-policy.d.ts.map +1 -0
- package/dist/journey/wait-policy.js +233 -0
- package/dist/journey/wait-policy.js.map +1 -0
- package/dist/journey/workspace-effects.d.ts +7 -1
- package/dist/journey/workspace-effects.d.ts.map +1 -1
- package/dist/journey/workspace-effects.js +131 -14
- package/dist/journey/workspace-effects.js.map +1 -1
- package/dist/machines/explorer.js +2 -2
- package/dist/machines/explorer.js.map +1 -1
- package/dist/machines/machine-first.d.ts.map +1 -1
- package/dist/machines/machine-first.js +2 -0
- package/dist/machines/machine-first.js.map +1 -1
- package/dist/machines/session-actionability.js +10 -0
- package/dist/machines/session-actionability.js.map +1 -1
- package/dist/machines/session-visibility.d.ts +2 -0
- package/dist/machines/session-visibility.d.ts.map +1 -1
- package/dist/machines/session-visibility.js +5 -0
- package/dist/machines/session-visibility.js.map +1 -1
- package/dist/platform/process-lock.d.ts +21 -0
- package/dist/platform/process-lock.d.ts.map +1 -0
- package/dist/platform/process-lock.js +114 -0
- package/dist/platform/process-lock.js.map +1 -0
- package/dist/runtime/boundary.d.ts +2 -0
- package/dist/runtime/boundary.d.ts.map +1 -1
- package/dist/runtime/boundary.js +155 -8
- package/dist/runtime/boundary.js.map +1 -1
- package/dist/runtime/node-foreground-session.d.ts +10 -0
- package/dist/runtime/node-foreground-session.d.ts.map +1 -1
- package/dist/runtime/node-foreground-session.js +112 -4
- package/dist/runtime/node-foreground-session.js.map +1 -1
- package/dist/runtime/terminal-client-identity.d.ts +66 -0
- package/dist/runtime/terminal-client-identity.d.ts.map +1 -0
- package/dist/runtime/terminal-client-identity.js +184 -0
- package/dist/runtime/terminal-client-identity.js.map +1 -0
- package/dist/runtime/terminal-transport.js +2 -2
- package/dist/runtime/terminal-transport.js.map +1 -1
- package/dist/sync/continuous-sync-supervisor.d.ts +64 -1
- package/dist/sync/continuous-sync-supervisor.d.ts.map +1 -1
- package/dist/sync/continuous-sync-supervisor.js +247 -28
- package/dist/sync/continuous-sync-supervisor.js.map +1 -1
- package/dist/sync/journal.js +9 -39
- package/dist/sync/journal.js.map +1 -1
- package/dist/sync/workspace-sync-product-service.d.ts +83 -1
- package/dist/sync/workspace-sync-product-service.d.ts.map +1 -1
- package/dist/sync/workspace-sync-product-service.js +324 -12
- package/dist/sync/workspace-sync-product-service.js.map +1 -1
- package/dist/terminal/foreground.d.ts.map +1 -1
- package/dist/terminal/foreground.js +82 -15
- package/dist/terminal/foreground.js.map +1 -1
- package/dist/terminal/workbench.d.ts +7 -0
- package/dist/terminal/workbench.d.ts.map +1 -1
- package/dist/terminal/workbench.js +24 -6
- package/dist/terminal/workbench.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ONE QUESTION THE CLI ASKS MID-JOURNEY, AND WHAT THE SCREEN OWES THE
|
|
3
|
+
* PERSON THE MOMENT THEY ANSWER IT.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS A MODULE AND NOT FOUR LINES IN `run.ts`. Measured 2026-09-22
|
|
6
|
+
* (`prds/cuna-cli-latency-before-20260922.md` § 3, finding 1): after this
|
|
7
|
+
* answer was accepted, the terminal went BYTE-silent — no output at all, not a
|
|
8
|
+
* repeating spinner — for 2 317 ms, 3 864 ms and 18 314 ms in three of five
|
|
9
|
+
* runs. The screen froze on the person's own keystroke, which is the worst
|
|
10
|
+
* shape a stall can take, because the last thing they did is the thing that
|
|
11
|
+
* appears to have broken it.
|
|
12
|
+
*
|
|
13
|
+
* The cause was structural: the question gave the progress row up in order to
|
|
14
|
+
* be readable, and nothing took the row back until the next remote step
|
|
15
|
+
* happened to report. Whatever that step cost was silence. So the repair is not
|
|
16
|
+
* "make the next step faster" — it is that the answer itself is an event worth
|
|
17
|
+
* rendering, and it is rendered from the branch that was actually taken rather
|
|
18
|
+
* than from the question that was asked.
|
|
19
|
+
*
|
|
20
|
+
* Keeping it here rather than inline is what makes that assertable: `run.ts`
|
|
21
|
+
* reaches this code only through a real TTY, a real `process.stdin` and a
|
|
22
|
+
* 90 ms paint timer, none of which can express the defect, which was entirely
|
|
23
|
+
* about WHEN a line appears relative to an answer.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Asked verbatim, and quoted verbatim in the measurement, so the two can be
|
|
27
|
+
* compared without guessing which build produced a transcript.
|
|
28
|
+
*/
|
|
29
|
+
export const RECORDED_LAUNCH_QUESTION = "A previous launch is recorded. Create another session? [y/N; No resumes the recorded launch] ";
|
|
30
|
+
/**
|
|
31
|
+
* How long the screen may stay unchanged after the answer is accepted.
|
|
32
|
+
*
|
|
33
|
+
* DERIVATION. Not a tuning knob: it is the bound this module exists to hold,
|
|
34
|
+
* and it is deliberately far below the 2 000 ms threshold the measurement used
|
|
35
|
+
* to call a stall a stall, because a stall that begins on the person's own
|
|
36
|
+
* keypress reads as a crash rather than as waiting. The measured worst case was
|
|
37
|
+
* 18 314 ms, which is 36 times this.
|
|
38
|
+
*/
|
|
39
|
+
export const RECORDED_LAUNCH_ACKNOWLEDGEMENT_BUDGET_MS = 500;
|
|
40
|
+
/**
|
|
41
|
+
* What answers the recorded-launch question for a folder journey.
|
|
42
|
+
* `--new-session` is that answer already given on the command line, so it is
|
|
43
|
+
* not asked again: asking let a No, or a slow answer, override the flag (qa6
|
|
44
|
+
* witness 2026-09-22). Without the flag the person is asked.
|
|
45
|
+
*/
|
|
46
|
+
export function recordedLaunchConfirmation(newSessionFlag, ask) {
|
|
47
|
+
return newSessionFlag ? async () => true : ask;
|
|
48
|
+
}
|
|
49
|
+
/** Only an explicit yes creates; anything else, including empty, resumes. */
|
|
50
|
+
export function recordedLaunchWantsNewSession(answer) {
|
|
51
|
+
return /^y(?:es)?$/iu.test(answer.trim());
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What the screen says next, named for the branch that was taken rather than
|
|
55
|
+
* for the question that was asked.
|
|
56
|
+
*
|
|
57
|
+
* `No` resumes a launch that already exists, and calling that "Creating" would
|
|
58
|
+
* be the CLI announcing work it is not about to do — which is the same defect
|
|
59
|
+
* as the silence, one layer up: a screen that does not describe what is
|
|
60
|
+
* happening.
|
|
61
|
+
*/
|
|
62
|
+
export function recordedLaunchAcknowledgement(wantsNewSession, createLabel) {
|
|
63
|
+
return wantsNewSession ? createLabel : "Resuming the recorded launch";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Ask the recorded-launch question and answer the screen at once.
|
|
67
|
+
*
|
|
68
|
+
* THE BOUND THIS GIVES, STATED EXACTLY. `acknowledge` is called synchronously
|
|
69
|
+
* between the answer arriving and this function resolving, so no remote work
|
|
70
|
+
* can be dispatched in between — not because a timer says so, but because
|
|
71
|
+
* there is nowhere for it to go. The caller therefore cannot reintroduce the
|
|
72
|
+
* measured silence by being slow; it can only reintroduce it by writing
|
|
73
|
+
* nothing, which is what `acknowledge` exists to prevent.
|
|
74
|
+
*/
|
|
75
|
+
export async function askRecordedLaunch(input) {
|
|
76
|
+
const wantsNewSession = recordedLaunchWantsNewSession(await input.ask(RECORDED_LAUNCH_QUESTION));
|
|
77
|
+
input.acknowledge(recordedLaunchAcknowledgement(wantsNewSession, input.createLabel));
|
|
78
|
+
return wantsNewSession;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=recorded-launch-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorded-launch-prompt.js","sourceRoot":"","sources":["../../src/cli/recorded-launch-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,+FAA+F,CAAC;AAElG;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yCAAyC,GAAG,GAAG,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,cAAuB,EACvB,GAAoC;IAEpC,OAAO,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,6BAA6B,CAAC,MAAc;IAC1D,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B,CAAC,eAAwB,EAAE,WAAmB;IACzF,OAAO,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,8BAA8B,CAAC;AACxE,CAAC;AAgBD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAgC;IACtE,MAAM,eAAe,GAAG,6BAA6B,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACjG,KAAK,CAAC,WAAW,CAAC,6BAA6B,CAAC,eAAe,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IACrF,OAAO,eAAe,CAAC;AACzB,CAAC"}
|
package/dist/cli/run.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { type ExitCode } from "../core/errors.js";
|
|
|
10
10
|
import type { SecureCredentialBackend } from "../credentials/contracts.js";
|
|
11
11
|
import { CredentialVault } from "../credentials/vault.js";
|
|
12
12
|
import { type AgentJourneyEffects, type ReconciledAgentJourneyIntent } from "../journey/index.js";
|
|
13
|
+
import { type FirstLine } from "./first-line.js";
|
|
13
14
|
import { type RootJourneyRunner } from "../journey/root-entry.js";
|
|
14
15
|
import { type PlatformAdapter } from "../platform/adapter.js";
|
|
15
16
|
import { type RuntimeFeatureGate } from "../runtime/contracts.js";
|
|
@@ -50,6 +51,24 @@ export interface RunCliDependencies {
|
|
|
50
51
|
readonly managedWorkspaceMachineId?: string;
|
|
51
52
|
/** Test seam for the folder a command resolves its workspace binding from. */
|
|
52
53
|
readonly workspaceRoot?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Test seam for the stream a mid-journey question reads its answer from.
|
|
56
|
+
* Production always leaves this absent and uses the real `process.stdin`.
|
|
57
|
+
*
|
|
58
|
+
* It exists because the recorded-launch question is the one place the CLI
|
|
59
|
+
* stops and waits for a person mid-journey, and the measured defect there was
|
|
60
|
+
* entirely about WHEN the next line appears relative to the answer
|
|
61
|
+
* (`prds/cuna-cli-latency-before-20260922.md` § 3, finding 1). Without a seam
|
|
62
|
+
* that ordering is only assertable through a real TTY, and the live evidence
|
|
63
|
+
* for the repair is n=0: none of the nine runs of § 8.2 met the question.
|
|
64
|
+
*/
|
|
65
|
+
readonly promptInput?: NodeJS.ReadableStream;
|
|
66
|
+
/**
|
|
67
|
+
* The row `bin/cuna.ts` painted before this module loaded, if it painted one.
|
|
68
|
+
* The paint site that would have drawn the same row claims it instead of
|
|
69
|
+
* drawing it again; see `cli/first-line.ts`.
|
|
70
|
+
*/
|
|
71
|
+
readonly firstLine?: FirstLine;
|
|
53
72
|
readonly automaticJourneyEffectsFactory?: (input: {
|
|
54
73
|
readonly client: CunaApiClient;
|
|
55
74
|
readonly intent: ReconciledAgentJourneyIntent;
|
package/dist/cli/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/run.js
CHANGED
|
@@ -5,8 +5,9 @@ import { runOwnerGrantsScreen } from "../runtime/owner-grants-screen.js";
|
|
|
5
5
|
import { ownerGrantOperationStore } from "../runtime/owner-grant-operations.js";
|
|
6
6
|
import { runProviderScreen } from "../machines/provider-screen.js";
|
|
7
7
|
import { Writable } from "node:stream";
|
|
8
|
-
import { terminalCellWidth
|
|
8
|
+
import { terminalCellWidth } from "../terminal/cell-width.js";
|
|
9
9
|
import { createInterface } from "node:readline/promises";
|
|
10
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
10
11
|
import { mkdir } from "node:fs/promises";
|
|
11
12
|
import { createHash } from "node:crypto";
|
|
12
13
|
import { launchRemoteWorkspaceSession } from "../journey/remote-workspace.js";
|
|
@@ -26,8 +27,12 @@ import { integerArgument } from "../core/validation.js";
|
|
|
26
27
|
import { CredentialBoundaryError } from "../credentials/errors.js";
|
|
27
28
|
import { CredentialVault } from "../credentials/vault.js";
|
|
28
29
|
import { LocalEncryptedSessionBackend, localEncryptedSessionPaths } from "../credentials/local-session.js";
|
|
29
|
-
import { conservativeFilesystemCapabilities, createApiAgentJourneyEffects, createWorkspaceJourneyEffects, orchestrateAgentJourney, preflightAgentJourneyInvocation, } from "../journey/index.js";
|
|
30
|
-
import {
|
|
30
|
+
import { conservativeFilesystemCapabilities, createApiAgentJourneyEffects, createWorkspaceJourneyEffects, journeyWaitLine, orchestrateAgentJourney, preflightAgentJourneyInvocation, readAccountIdentityWithin, } from "../journey/index.js";
|
|
31
|
+
import { agentSessionDispositionLine, composeInlineProgressLine, inlineProgressColumns, renderInlineProgressFrame, } from "./progress-line.js";
|
|
32
|
+
import { agentDisplayName, journeyPreparationLabel, ROOT_FIRST_LINE_LABEL, } from "./first-line.js";
|
|
33
|
+
import { askRecordedLaunch, recordedLaunchConfirmation } from "./recorded-launch-prompt.js";
|
|
34
|
+
import { settledAgentSessionDisposition } from "../journey/session-disposition.js";
|
|
35
|
+
import { agentJourneyCommand, rootJourneyArgv, runNodeRootJourney, } from "../journey/root-entry.js";
|
|
31
36
|
import { createPlatformAdapter } from "../platform/adapter.js";
|
|
32
37
|
import { CLI_VERSION, OUTPUT_SCHEMA_VERSION } from "../version.js";
|
|
33
38
|
import { runtimeFeatureGates } from "../runtime/contracts.js";
|
|
@@ -421,12 +426,6 @@ function managedWorkspaceScope(intent, machineId) {
|
|
|
421
426
|
}
|
|
422
427
|
return `${intent.agent}-${intent.machine.kind}`;
|
|
423
428
|
}
|
|
424
|
-
function agentDisplayName(agent) {
|
|
425
|
-
return agent === "claude-code" ? "Claude Code"
|
|
426
|
-
: agent === "codex" ? "Codex"
|
|
427
|
-
: agent === "opencode" ? "OpenCode"
|
|
428
|
-
: "OpenClaw";
|
|
429
|
-
}
|
|
430
429
|
/**
|
|
431
430
|
* `doctor` must distinguish two independent facts: whether this host can
|
|
432
431
|
* protect a durable login-code envelope, and whether the selected deployment
|
|
@@ -620,51 +619,58 @@ function writeTerminalSupervisorReadiness(stream, color, state, sessionIds) {
|
|
|
620
619
|
: "After the stopped-machine supervisor update, open this same AgentSession again.\n");
|
|
621
620
|
}
|
|
622
621
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
if (tty._handle.getWindowSize(size) === 0 && size.length === 2 &&
|
|
633
|
-
Number.isSafeInteger(size[0]) && size[0] >= 2 && size[0] <= 4096 &&
|
|
634
|
-
Number.isSafeInteger(size[1]) && size[1] >= 1 && size[1] <= 4096)
|
|
635
|
-
return size[0];
|
|
636
|
-
}
|
|
637
|
-
catch { /* An unavailable native observation does not break progress. */ }
|
|
638
|
-
}
|
|
639
|
-
return Number.isSafeInteger(tty.columns) && tty.columns >= 2 && tty.columns <= 4096 ? tty.columns : 80;
|
|
640
|
-
}
|
|
641
|
-
function startInlineProgress(stream, color, initialLabel = "Loading machines") {
|
|
642
|
-
const frames = ["◐", "◓", "◑", "◒"];
|
|
643
|
-
const bars = ["━╺━━━━", "━━╺━━━", "━━━╺━━", "━━━━╺━", "━━━━━╺", "━━━━╸━", "━━━╸━━", "━━╸━━━"];
|
|
644
|
-
const startedAt = Date.now();
|
|
645
|
-
let frame = 0;
|
|
622
|
+
/**
|
|
623
|
+
* Start the one inline progress row, or continue a row `cli/first-line.ts`
|
|
624
|
+
* already painted before this module loaded (`adopted`). An adopted row is not
|
|
625
|
+
* painted again: its frame 0 is on screen, so the loop starts at frame 1 and its
|
|
626
|
+
* clocks start when that row appeared, not when this code got to run.
|
|
627
|
+
*/
|
|
628
|
+
function startInlineProgress(stream, color, initialLabel = "Loading machines", adopted) {
|
|
629
|
+
const startedAt = adopted?.paintedAt ?? Date.now();
|
|
630
|
+
let frame = adopted === undefined ? 0 : 1;
|
|
646
631
|
let label = initialLabel;
|
|
632
|
+
// When THIS label started, not when the spinner did. The measured defect was
|
|
633
|
+
// label dwell — 61 259 ms on one unchanging sentence
|
|
634
|
+
// (`prds/cuna-cli-latency-before-20260922.md` § 3) — so the number a person
|
|
635
|
+
// needs is how long the current step has been running, not the command.
|
|
636
|
+
let labelStartedAt = startedAt;
|
|
637
|
+
let waiting;
|
|
647
638
|
let stopped = false;
|
|
648
|
-
let lastColumns = 0;
|
|
649
|
-
let lastCells = 0;
|
|
639
|
+
let lastColumns = adopted?.columns ?? 0;
|
|
640
|
+
let lastCells = adopted?.cells ?? 0;
|
|
650
641
|
const paint = () => {
|
|
651
642
|
const columns = inlineProgressColumns(stream);
|
|
652
|
-
const
|
|
643
|
+
const now = Date.now();
|
|
653
644
|
// A spinner alone is too easy to mistake for a frozen cursor on slower
|
|
654
|
-
// Windows terminals
|
|
655
|
-
//
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
645
|
+
// Windows terminals, and `· still working` — the whole escalation this
|
|
646
|
+
// replaces — said nothing a second repaint had not already said. The
|
|
647
|
+
// sentence and its thresholds live in `progress-line.ts`, where they can
|
|
648
|
+
// be asserted without a clock or a TTY.
|
|
649
|
+
const { headline, trailer } = composeInlineProgressLine({
|
|
650
|
+
label,
|
|
651
|
+
...(waiting === undefined ? {} : {
|
|
652
|
+
waiting: {
|
|
653
|
+
waitingFor: waiting.notice.waitingFor,
|
|
654
|
+
// Re-derived per repaint: the reporter speaks once per poll, up to
|
|
655
|
+
// 1 600 ms apart, and a number that only moves when the poll does
|
|
656
|
+
// reproduces the dwell it is meant to cure.
|
|
657
|
+
elapsedMs: waiting.notice.elapsedMs + (now - waiting.observedAt),
|
|
658
|
+
deadlineMs: waiting.notice.deadlineMs,
|
|
659
|
+
},
|
|
660
|
+
}),
|
|
661
|
+
labelElapsedMs: now - labelStartedAt,
|
|
662
|
+
totalElapsedMs: now - startedAt,
|
|
663
|
+
});
|
|
664
|
+
const { styled, fitted } = renderInlineProgressFrame({ headline, trailer, frame, columns, color });
|
|
666
665
|
if (lastColumns > columns && lastCells >= columns) {
|
|
667
666
|
// A terminal resize can reflow our previously single row before repaint.
|
|
667
|
+
// HAZARD, unrepaired: this assumes the terminal reflowed. One that
|
|
668
|
+
// truncated instead still has a single row here, so the walk clears
|
|
669
|
+
// whatever is above it — on this path, the durable `note()` line naming
|
|
670
|
+
// the AgentSession. @xterm/headless truncates, which is why
|
|
671
|
+
// `test/cli.test.mjs` asserts the progress row is never BELOW the durable
|
|
672
|
+
// rows rather than exactly at row 0. Distinguishing the two needs
|
|
673
|
+
// terminal feedback the CLI does not have.
|
|
668
674
|
for (let row = 0; row < Math.floor(lastCells / columns); row += 1)
|
|
669
675
|
stream.write("\r\u001b[2K\u001b[1A");
|
|
670
676
|
}
|
|
@@ -673,14 +679,33 @@ function startInlineProgress(stream, color, initialLabel = "Loading machines") {
|
|
|
673
679
|
lastCells = terminalCellWidth(fitted);
|
|
674
680
|
frame += 1;
|
|
675
681
|
};
|
|
676
|
-
|
|
682
|
+
if (adopted === undefined)
|
|
683
|
+
paint();
|
|
677
684
|
const timer = setInterval(paint, 90);
|
|
678
685
|
timer.unref();
|
|
679
686
|
return Object.freeze({
|
|
680
687
|
update(nextLabel) {
|
|
681
|
-
if (stopped
|
|
688
|
+
if (stopped)
|
|
682
689
|
return;
|
|
683
|
-
label
|
|
690
|
+
// A repeat of the same label must not restart the dwell clock, but it
|
|
691
|
+
// MUST still end a wait: the phase resuming is exactly the state change
|
|
692
|
+
// the wait line was standing in for, and an early return on the label
|
|
693
|
+
// alone would leave the stale wait on screen forever.
|
|
694
|
+
if (nextLabel === label && waiting === undefined)
|
|
695
|
+
return;
|
|
696
|
+
waiting = undefined;
|
|
697
|
+
if (nextLabel !== label) {
|
|
698
|
+
label = nextLabel;
|
|
699
|
+
labelStartedAt = Date.now();
|
|
700
|
+
}
|
|
701
|
+
paint();
|
|
702
|
+
},
|
|
703
|
+
wait(notice) {
|
|
704
|
+
if (stopped)
|
|
705
|
+
return;
|
|
706
|
+
waiting = notice === undefined ? undefined : Object.freeze({ notice, observedAt: Date.now() });
|
|
707
|
+
if (notice === undefined)
|
|
708
|
+
labelStartedAt = Date.now();
|
|
684
709
|
paint();
|
|
685
710
|
},
|
|
686
711
|
note(line) {
|
|
@@ -812,11 +837,6 @@ function journeyPhaseLabel(phase, agent) {
|
|
|
812
837
|
: `Opening ${display}`;
|
|
813
838
|
}
|
|
814
839
|
}
|
|
815
|
-
function journeyPreparationLabel(agent) {
|
|
816
|
-
return agent === "opencode"
|
|
817
|
-
? "Preparing OpenCode — use /connect in its terminal"
|
|
818
|
-
: `Preparing ${agentDisplayName(agent)}`;
|
|
819
|
-
}
|
|
820
840
|
function foregroundAttachLabel(agent) {
|
|
821
841
|
return agent === "opencode"
|
|
822
842
|
? "Opening OpenCode terminal — use /connect there"
|
|
@@ -841,9 +861,21 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
841
861
|
let interactiveCloseUi = false;
|
|
842
862
|
let interactiveCloseColor = false;
|
|
843
863
|
let terminalSessionIds = [];
|
|
864
|
+
// Known once configuration is read; every attach happens after that.
|
|
865
|
+
let terminalClients;
|
|
844
866
|
const runForeground = async (input) => {
|
|
845
867
|
terminalSessionIds = [...input.agentSessionIds];
|
|
846
|
-
await (dependencies.foregroundTerminalRunner ?? runNodeForegroundSessions)(
|
|
868
|
+
await (dependencies.foregroundTerminalRunner ?? runNodeForegroundSessions)({
|
|
869
|
+
...(terminalClients === undefined ? {} : { terminalClients }),
|
|
870
|
+
onNotice: (line) => {
|
|
871
|
+
const row = inlineJourneyProgress ?? inlineRootProgress;
|
|
872
|
+
if (row !== undefined)
|
|
873
|
+
row.note(line);
|
|
874
|
+
else
|
|
875
|
+
streams.stderr.write(`${line}\n`);
|
|
876
|
+
},
|
|
877
|
+
...input,
|
|
878
|
+
});
|
|
847
879
|
};
|
|
848
880
|
try {
|
|
849
881
|
const parsed = parseArgv(argv);
|
|
@@ -950,7 +982,7 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
950
982
|
interactiveCloseUi = true;
|
|
951
983
|
interactiveCloseColor = color;
|
|
952
984
|
if (streams.stderrIsTTY === true) {
|
|
953
|
-
inlineRootProgress = startInlineProgress(streams.stderr, color, "
|
|
985
|
+
inlineRootProgress = startInlineProgress(streams.stderr, color, ROOT_FIRST_LINE_LABEL, dependencies.firstLine?.claim({ kind: "root", label: ROOT_FIRST_LINE_LABEL, color, stream: streams.stderr }));
|
|
954
986
|
}
|
|
955
987
|
else {
|
|
956
988
|
streams.stderr.write("Cuna: starting...\n");
|
|
@@ -1001,8 +1033,9 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1001
1033
|
// tells the truth immediately without claiming that attach has begun.
|
|
1002
1034
|
const preparation = journeyPreparationLabel(journeyIntent.agent);
|
|
1003
1035
|
const color = !booleanOption(parsed, "no-color") && !Object.hasOwn(effectiveEnvironment, "NO_COLOR");
|
|
1004
|
-
if (streams.stderrIsTTY === true)
|
|
1005
|
-
inlineJourneyProgress = startInlineProgress(streams.stderr, color, preparation);
|
|
1036
|
+
if (streams.stderrIsTTY === true) {
|
|
1037
|
+
inlineJourneyProgress = startInlineProgress(streams.stderr, color, preparation, dependencies.firstLine?.claim({ kind: "journey", label: preparation, color, stream: streams.stderr }));
|
|
1038
|
+
}
|
|
1006
1039
|
else
|
|
1007
1040
|
streams.stderr.write(`Cuna: ${preparation.charAt(0).toLowerCase()}${preparation.slice(1)}...\n`);
|
|
1008
1041
|
}
|
|
@@ -1046,6 +1079,7 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1046
1079
|
},
|
|
1047
1080
|
...(creatingProfile ? { allowMissingProfile: true } : {}),
|
|
1048
1081
|
});
|
|
1082
|
+
terminalClients = Object.freeze({ platform, profile: config.profile });
|
|
1049
1083
|
if (interactiveRoot) {
|
|
1050
1084
|
inlineRootProgress?.update(config.apiKey === undefined ? "Checking your Cuna sign-in" : "Checking Cuna access");
|
|
1051
1085
|
}
|
|
@@ -1488,6 +1522,80 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1488
1522
|
throw unsupportedError("openclaw", "provider_route_unavailable");
|
|
1489
1523
|
}
|
|
1490
1524
|
const journeyAgent = journeyIntent.agent;
|
|
1525
|
+
const journeyColor = () => !booleanOption(parsed, "no-color") && !Object.hasOwn(effectiveEnvironment, "NO_COLOR");
|
|
1526
|
+
const resumeJourneyProgress = () => {
|
|
1527
|
+
if (streams.stderrIsTTY === true) {
|
|
1528
|
+
return startInlineProgress(streams.stderr, journeyColor(), journeyPhaseLabel("create-agent-session", journeyAgent));
|
|
1529
|
+
}
|
|
1530
|
+
return undefined;
|
|
1531
|
+
};
|
|
1532
|
+
// Both launch paths render these two the same way, so they are written
|
|
1533
|
+
// once: a declared wait takes the spinner row and gives it back, while a
|
|
1534
|
+
// settled AgentSession is a durable note that stays above it. Without the
|
|
1535
|
+
// row, each becomes a plain line on stderr.
|
|
1536
|
+
const renderJourneyWait = (notice) => {
|
|
1537
|
+
if (inlineJourneyProgress !== undefined)
|
|
1538
|
+
inlineJourneyProgress.wait(notice);
|
|
1539
|
+
else
|
|
1540
|
+
streams.stderr.write(`${journeyWaitLine(notice)}\n`);
|
|
1541
|
+
};
|
|
1542
|
+
const renderAgentSession = (event) => {
|
|
1543
|
+
const line = agentSessionDispositionLine(event);
|
|
1544
|
+
if (inlineJourneyProgress !== undefined)
|
|
1545
|
+
inlineJourneyProgress.note(line);
|
|
1546
|
+
else
|
|
1547
|
+
streams.stderr.write(`${line}\n`);
|
|
1548
|
+
};
|
|
1549
|
+
/**
|
|
1550
|
+
* True once the recorded-launch question has been answered No, on either
|
|
1551
|
+
* launch path. It is what separates a new launch from a resumed one; see
|
|
1552
|
+
* `journey/session-disposition.ts` for why nothing else on the path can.
|
|
1553
|
+
*/
|
|
1554
|
+
let recordedLaunchResumed = false;
|
|
1555
|
+
/**
|
|
1556
|
+
* Ask the one recorded-launch question, and answer the screen at once.
|
|
1557
|
+
*
|
|
1558
|
+
* ONE HELPER BECAUSE THERE WAS ONE QUESTION AND TWO BEHAVIOURS. The
|
|
1559
|
+
* journey copy stopped the spinner to ask and never took the row back;
|
|
1560
|
+
* the remote-menu copy left the spinner running, so its 90 ms repaint
|
|
1561
|
+
* erased the question before it could be read. Measured 2026-09-22
|
|
1562
|
+
* (`prds/cuna-cli-latency-before-20260922.md` § 3, finding 1): the
|
|
1563
|
+
* terminal went BYTE-silent for up to 18 314 ms in 3 of 5 runs, always
|
|
1564
|
+
* immediately after this answer was accepted — the screen froze on the
|
|
1565
|
+
* person's own keystroke. What is said, and that it is said before this
|
|
1566
|
+
* returns, live in `recorded-launch-prompt.ts`, which can be asserted
|
|
1567
|
+
* without a TTY; this closure owns only the readline and the row.
|
|
1568
|
+
*/
|
|
1569
|
+
const askCreateAnotherSession = async (signal) => {
|
|
1570
|
+
inlineJourneyProgress?.stop();
|
|
1571
|
+
inlineJourneyProgress = undefined;
|
|
1572
|
+
const another = await askRecordedLaunch({
|
|
1573
|
+
// The readline is opened and closed inside `ask`, so it has released
|
|
1574
|
+
// the cursor before the acknowledgement is painted onto the same row.
|
|
1575
|
+
ask: async (question) => {
|
|
1576
|
+
const prompt = createInterface({ input: dependencies.promptInput ?? process.stdin, output: streams.stderr });
|
|
1577
|
+
try {
|
|
1578
|
+
return signal === undefined
|
|
1579
|
+
? await prompt.question(question)
|
|
1580
|
+
: await prompt.question(question, { signal });
|
|
1581
|
+
}
|
|
1582
|
+
finally {
|
|
1583
|
+
prompt.close();
|
|
1584
|
+
}
|
|
1585
|
+
},
|
|
1586
|
+
acknowledge: (line) => {
|
|
1587
|
+
if (streams.stderrIsTTY === true) {
|
|
1588
|
+
inlineJourneyProgress = startInlineProgress(streams.stderr, journeyColor(), line);
|
|
1589
|
+
}
|
|
1590
|
+
else {
|
|
1591
|
+
streams.stderr.write(`${line}\n`);
|
|
1592
|
+
}
|
|
1593
|
+
},
|
|
1594
|
+
createLabel: journeyPhaseLabel("create-agent-session", journeyAgent),
|
|
1595
|
+
});
|
|
1596
|
+
recordedLaunchResumed = recordedLaunchResumed || !another;
|
|
1597
|
+
return another;
|
|
1598
|
+
};
|
|
1491
1599
|
if (credentialMode === undefined) {
|
|
1492
1600
|
throw new CunaError({
|
|
1493
1601
|
code: "cuna.auth.required",
|
|
@@ -1501,7 +1609,30 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1501
1609
|
// Read before the effects branch, not inside it: the principal and the
|
|
1502
1610
|
// workspace are half of the machine-create request identity, so every
|
|
1503
1611
|
// journey needs them, including the one built from injected effects.
|
|
1504
|
-
|
|
1612
|
+
//
|
|
1613
|
+
// Under the journey's own wait policy rather than under the per-request
|
|
1614
|
+
// budget alone. Being first is exactly why this read needed it: it sits
|
|
1615
|
+
// outside `journey/api-effects.ts` and `journey/remote-workspace.ts`, so
|
|
1616
|
+
// the re-issue repair reached neither of the two places it runs, and one
|
|
1617
|
+
// elapsed 15 000 ms budget still ended the whole command — measured
|
|
1618
|
+
// 2026-09-22 (`prds/cuna-cli-latency-before-20260922.md` § 8.3, runs `a5`
|
|
1619
|
+
// and the `--timeout-ms 800` control `slow1`, both exit 5 on `GET /v1/me`).
|
|
1620
|
+
// `journey/account-identity.ts` holds the deadline and its derivation.
|
|
1621
|
+
let accountWaitShown = false;
|
|
1622
|
+
const identity = await readAccountIdentityWithin({
|
|
1623
|
+
read: () => client.getIdentity(dependencies.signal),
|
|
1624
|
+
signal: dependencies.signal ?? new AbortController().signal,
|
|
1625
|
+
sleep: async (milliseconds, signal) => { await delay(milliseconds, undefined, { signal }); },
|
|
1626
|
+
now: dependencies.now ?? Date.now,
|
|
1627
|
+
onWait: (notice) => {
|
|
1628
|
+
accountWaitShown = true;
|
|
1629
|
+
renderJourneyWait(notice);
|
|
1630
|
+
},
|
|
1631
|
+
});
|
|
1632
|
+
// The account answered, so the row stops counting a wait that is over.
|
|
1633
|
+
// Nothing else ends it: the next phase label can be several steps away.
|
|
1634
|
+
if (accountWaitShown)
|
|
1635
|
+
inlineJourneyProgress?.wait(undefined);
|
|
1505
1636
|
const workspaceId = identity.workspaceId;
|
|
1506
1637
|
if (workspaceId === undefined) {
|
|
1507
1638
|
throw new CunaError({
|
|
@@ -1518,22 +1649,25 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1518
1649
|
const preset = await (dependencies.providerScreenRunner ?? runProviderScreen)(client, { kind: "preset", agent: journeyAgent }, undefined, dependencies.signal);
|
|
1519
1650
|
if (preset === undefined)
|
|
1520
1651
|
return EXIT_CODES.success;
|
|
1652
|
+
inlineJourneyProgress = resumeJourneyProgress();
|
|
1521
1653
|
const agentSessionId = await launchRemoteWorkspaceSession({
|
|
1522
1654
|
preset,
|
|
1523
1655
|
providerLaunchState: { stateDirectory: platform.paths.stateDirectory, ownerId: identity.id },
|
|
1524
|
-
confirmNew:
|
|
1525
|
-
return /^y(?:es)?$/iu.test((await prompt.question("A previous launch is recorded. Create another session? [y/N; No resumes the recorded launch] ", { signal: dependencies.signal })).trim());
|
|
1526
|
-
}
|
|
1527
|
-
finally {
|
|
1528
|
-
prompt.close();
|
|
1529
|
-
} },
|
|
1656
|
+
confirmNew: () => askCreateAnotherSession(dependencies.signal),
|
|
1530
1657
|
client, machineId: dependencies.managedWorkspaceMachineId, workspaceId, agent: journeyAgent,
|
|
1531
1658
|
onProgress: (label) => inlineJourneyProgress?.update(label),
|
|
1659
|
+
onWait: renderJourneyWait,
|
|
1660
|
+
onAgentSession: renderAgentSession,
|
|
1532
1661
|
...(dependencies.signal === undefined ? {} : { signal: dependencies.signal }),
|
|
1533
1662
|
...(dependencies.now === undefined ? {} : { now: dependencies.now }),
|
|
1534
1663
|
});
|
|
1664
|
+
inlineJourneyProgress?.stop();
|
|
1665
|
+
inlineJourneyProgress = undefined;
|
|
1535
1666
|
return await runCli([
|
|
1536
|
-
|
|
1667
|
+
// The COMMAND, not the agent identifier. `claude-code` is not a
|
|
1668
|
+
// command, and passing it here ended this path at `Unknown command
|
|
1669
|
+
// claude-code` right after the AgentSession was created.
|
|
1670
|
+
agentJourneyCommand(journeyAgent),
|
|
1537
1671
|
"--agent-session", agentSessionId,
|
|
1538
1672
|
...(booleanOption(parsed, "no-color") ? ["--no-color"] : []),
|
|
1539
1673
|
], { ...dependencies, ...(humanAuth === undefined ? {} : { humanAuth }) });
|
|
@@ -1584,15 +1718,17 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1584
1718
|
effects = createApiAgentJourneyEffects({
|
|
1585
1719
|
client,
|
|
1586
1720
|
requestedAgent: journeyAgent,
|
|
1587
|
-
confirmNewProviderLaunch:
|
|
1588
|
-
return /^y(?:es)?$/iu.test((await prompt.question("A previous launch is recorded. Create another session? [y/N; No resumes the recorded launch] ", { signal })).trim());
|
|
1589
|
-
}
|
|
1590
|
-
finally {
|
|
1591
|
-
prompt.close();
|
|
1592
|
-
} },
|
|
1721
|
+
confirmNewProviderLaunch: recordedLaunchConfirmation(journeyIntent.newSession, askCreateAnotherSession),
|
|
1593
1722
|
providerLaunchState: { stateDirectory: platform.paths.stateDirectory, ownerId: identity.id, workspaceId },
|
|
1594
|
-
selectProviderPreset: async (signal) => {
|
|
1595
|
-
|
|
1723
|
+
selectProviderPreset: async (signal) => {
|
|
1724
|
+
inlineJourneyProgress?.stop();
|
|
1725
|
+
inlineJourneyProgress = undefined;
|
|
1726
|
+
const preset = await (dependencies.providerScreenRunner ?? runProviderScreen)(client, { kind: "preset", agent: journeyAgent }, undefined, signal);
|
|
1727
|
+
if (!preset)
|
|
1728
|
+
throw new CunaError({ code: "cuna.provider.selection_cancelled", message: "Provider selection cancelled. The synchronized Workspace is preserved.", exitCode: EXIT_CODES.usage });
|
|
1729
|
+
inlineJourneyProgress = resumeJourneyProgress();
|
|
1730
|
+
return preset;
|
|
1731
|
+
},
|
|
1596
1732
|
inspectWorkspace: workspace.inspectWorkspace,
|
|
1597
1733
|
synchronizeWorkspace: workspace.synchronizeWorkspace,
|
|
1598
1734
|
// The spinner and the prompt write to the same row of the same
|
|
@@ -1638,6 +1774,7 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1638
1774
|
signal,
|
|
1639
1775
|
});
|
|
1640
1776
|
},
|
|
1777
|
+
onWait: renderJourneyWait,
|
|
1641
1778
|
...(dependencies.now === undefined ? {} : { now: dependencies.now }),
|
|
1642
1779
|
});
|
|
1643
1780
|
}
|
|
@@ -1648,6 +1785,23 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1648
1785
|
baseEffects.onPhase?.(phase);
|
|
1649
1786
|
inlineJourneyProgress?.update(journeyPhaseLabel(phase, journeyAgent));
|
|
1650
1787
|
},
|
|
1788
|
+
onAgentSession(event) {
|
|
1789
|
+
baseEffects.onAgentSession?.(event);
|
|
1790
|
+
// The orchestrator reads its disposition from the selection plan,
|
|
1791
|
+
// which cannot see the answer to the recorded-launch question asked
|
|
1792
|
+
// inside the create. On No, the create carries the recorded operation
|
|
1793
|
+
// id and returns the row that launch already produced, so the plan
|
|
1794
|
+
// still says `create-required` while the screen has just said
|
|
1795
|
+
// `Resuming the recorded launch`. This closure owns that question, so
|
|
1796
|
+
// it is the only place that can reconcile the two.
|
|
1797
|
+
renderAgentSession({
|
|
1798
|
+
...event,
|
|
1799
|
+
disposition: settledAgentSessionDisposition({
|
|
1800
|
+
planned: event.disposition,
|
|
1801
|
+
resumedRecordedLaunch: recordedLaunchResumed,
|
|
1802
|
+
}),
|
|
1803
|
+
});
|
|
1804
|
+
},
|
|
1651
1805
|
async attach(input) {
|
|
1652
1806
|
inlineJourneyProgress?.update(`Attaching to ${agentDisplayName(journeyAgent)}`);
|
|
1653
1807
|
try {
|
|
@@ -1913,6 +2067,10 @@ export async function runCli(argv, dependencies = {}) {
|
|
|
1913
2067
|
return EXIT_CODES.success;
|
|
1914
2068
|
}
|
|
1915
2069
|
catch (unknownError) {
|
|
2070
|
+
// A first line painted before this module loaded, and refused before any
|
|
2071
|
+
// progress row took it over, is cleared here, before the error is written
|
|
2072
|
+
// after it on the same row.
|
|
2073
|
+
dependencies.firstLine?.release();
|
|
1916
2074
|
inlineMachinesProgress?.stop();
|
|
1917
2075
|
inlineJourneyProgress?.stop();
|
|
1918
2076
|
inlineRootProgress?.stop();
|