@aria-cli/cli 1.0.57 → 1.0.59
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/bin/aria.mjs +799 -668
- package/package.json +17 -76
- package/dist/.tsbuildinfo +0 -1
- package/dist/attached-local-control-client.js +0 -826
- package/dist/bootstrap-local-control-client.js +0 -2
- package/dist/capability-aware-method-proxy.js +0 -42
- package/dist/cli-context.js +0 -160
- package/dist/commands/arions.js +0 -174
- package/dist/commands/auth.js +0 -123
- package/dist/commands/daemon.js +0 -367
- package/dist/commands/definitions.js +0 -176
- package/dist/commands/index.js +0 -80
- package/dist/commands/login-handler.js +0 -1108
- package/dist/commands/logout-handler.js +0 -92
- package/dist/commands/memory-handlers.js +0 -89
- package/dist/commands/pairing.js +0 -60
- package/dist/commands/runtime-cutover-reset-command.js +0 -12
- package/dist/commands/runtime-cutover-reset.js +0 -265
- package/dist/commands/terminal-setup.js +0 -84
- package/dist/config/aria-config.js +0 -238
- package/dist/config/index.js +0 -3
- package/dist/config/loader.js +0 -97
- package/dist/config.js +0 -142
- package/dist/daemon-info.js +0 -10
- package/dist/ensure-daemon.js +0 -128
- package/dist/entrypoints/command-mode.js +0 -5
- package/dist/entrypoints/daemon.js +0 -50
- package/dist/entrypoints/headless-stdio.js +0 -25
- package/dist/entrypoints/interactive.js +0 -80
- package/dist/event-loop-watchdog.js +0 -73
- package/dist/headless/auth-orchestrator.js +0 -508
- package/dist/headless/auth-service.js +0 -43
- package/dist/headless/bootstrap-fast-path.js +0 -112
- package/dist/headless/call-command.js +0 -143
- package/dist/headless/daemon-service.js +0 -318
- package/dist/headless/hook-actions.js +0 -235
- package/dist/headless/hook-service.js +0 -42
- package/dist/headless/kernel-services.js +0 -216
- package/dist/headless/kernel.js +0 -785
- package/dist/headless/operations/arion.js +0 -119
- package/dist/headless/operations/auth.js +0 -45
- package/dist/headless/operations/client.js +0 -31
- package/dist/headless/operations/config.js +0 -69
- package/dist/headless/operations/daemon.js +0 -47
- package/dist/headless/operations/hook.js +0 -56
- package/dist/headless/operations/index.js +0 -11
- package/dist/headless/operations/memory.js +0 -102
- package/dist/headless/operations/message.js +0 -279
- package/dist/headless/operations/model.js +0 -100
- package/dist/headless/operations/peer.js +0 -56
- package/dist/headless/operations/run.js +0 -24
- package/dist/headless/operations/session.js +0 -90
- package/dist/headless/operations/system.js +0 -19
- package/dist/headless/operations/utils.js +0 -35
- package/dist/headless/run-orchestrator.js +0 -703
- package/dist/headless/stdio-server.js +0 -439
- package/dist/history/SessionHistory.js +0 -8
- package/dist/history/SessionHistoryClient.js +0 -186
- package/dist/history/conversation-message.js +0 -112
- package/dist/history/index.js +0 -8
- package/dist/history/jsonl-replay.js +0 -154
- package/dist/history/repair-tool-pairing.js +0 -84
- package/dist/history/stall-phase-bridge.js +0 -11
- package/dist/history/turn-accumulator.js +0 -427
- package/dist/index.js +0 -7
- package/dist/ink-repl.js +0 -4183
- package/dist/local-control-bootstrap.js +0 -26
- package/dist/local-control-client.js +0 -2
- package/dist/local-control-error-reporting.js +0 -34
- package/dist/local-control-http-client.js +0 -362
- package/dist/local-control-lazy-wrapper.js +0 -363
- package/dist/local-control-manager.js +0 -146
- package/dist/main.js +0 -62
- package/dist/network-security.js +0 -62
- package/dist/networking-server.js +0 -38
- package/dist/peer-identity.js +0 -23
- package/dist/polling-subscription.js +0 -34
- package/dist/relaunch.js +0 -617
- package/dist/release-notes.js +0 -35
- package/dist/repl-cleanup.js +0 -47
- package/dist/runtime/configure-bun-sqlite.js +0 -3
- package/dist/runtime/crash-handlers.js +0 -111
- package/dist/runtime/interactive-invocation.js +0 -39
- package/dist/runtime/internal-mode.js +0 -14
- package/dist/runtime/launch-spec.js +0 -64
- package/dist/runtime/owner-lease.js +0 -44
- package/dist/runtime/public-mode.js +0 -20
- package/dist/runtime/run-internal-mode.js +0 -18
- package/dist/runtime/runtime-kind.js +0 -32
- package/dist/runtime/spawn-aria.js +0 -38
- package/dist/selectable-client.js +0 -2
- package/dist/selectable-peer.js +0 -2
- package/dist/session.js +0 -203
- package/dist/slash-commands.js +0 -80
- package/dist/sounds.js +0 -210
- package/dist/ui/App.js +0 -526
- package/dist/ui/components/AnthropicMethodPicker.js +0 -6
- package/dist/ui/components/ArionPrompt.js +0 -15
- package/dist/ui/components/AutocompleteDropdown.js +0 -23
- package/dist/ui/components/AutonomySelector.js +0 -55
- package/dist/ui/components/Banner.js +0 -98
- package/dist/ui/components/ConversationHistory.js +0 -175
- package/dist/ui/components/CopilotDeviceLoginFlow.js +0 -88
- package/dist/ui/components/CopilotSourcePicker.js +0 -50
- package/dist/ui/components/Cost.js +0 -10
- package/dist/ui/components/CustomSelect/option-map.js +0 -30
- package/dist/ui/components/CustomSelect/select-option.js +0 -13
- package/dist/ui/components/CustomSelect/select.js +0 -42
- package/dist/ui/components/CustomSelect/use-select-state.js +0 -179
- package/dist/ui/components/CustomSelect/use-select.js +0 -15
- package/dist/ui/components/ErrorDisplay.js +0 -35
- package/dist/ui/components/FallbackToolUseRejectedMessage.js +0 -7
- package/dist/ui/components/FileEditToolUpdatedMessage.js +0 -57
- package/dist/ui/components/HandoffMarker.js +0 -18
- package/dist/ui/components/HighlightedCode.js +0 -21
- package/dist/ui/components/InputArea.js +0 -187
- package/dist/ui/components/Message.js +0 -25
- package/dist/ui/components/OAuthLoginFlow.js +0 -113
- package/dist/ui/components/OutputTruncation.js +0 -35
- package/dist/ui/components/PermissionPrompt.js +0 -79
- package/dist/ui/components/PipelineTimingPanel.js +0 -15
- package/dist/ui/components/ProviderMethodPicker.js +0 -61
- package/dist/ui/components/ProviderPicker.js +0 -63
- package/dist/ui/components/RenderItemView.js +0 -71
- package/dist/ui/components/Spinner.js +0 -46
- package/dist/ui/components/StatusBar.js +0 -95
- package/dist/ui/components/StreamingIndicator.js +0 -55
- package/dist/ui/components/StructuredDiff.js +0 -168
- package/dist/ui/components/TextInputOverlay.js +0 -43
- package/dist/ui/components/ThinkingBlock.js +0 -82
- package/dist/ui/components/ToolCost.js +0 -17
- package/dist/ui/components/ToolExecution.js +0 -61
- package/dist/ui/components/ToolHeader.js +0 -51
- package/dist/ui/components/ToolRenderLayoutContext.js +0 -14
- package/dist/ui/components/ToolResultWrapper.js +0 -6
- package/dist/ui/components/ToolUseLoader.js +0 -35
- package/dist/ui/components/TraceWaterfall.js +0 -91
- package/dist/ui/components/index.js +0 -33
- package/dist/ui/components/messages/AssistantTextMessage.js +0 -25
- package/dist/ui/components/messages/UserImageMessage.js +0 -12
- package/dist/ui/components/messages/UserTextMessage.js +0 -12
- package/dist/ui/components/overlays/ArionSelector.js +0 -68
- package/dist/ui/components/overlays/ClientSelector.js +0 -62
- package/dist/ui/components/overlays/CommandPalette.js +0 -67
- package/dist/ui/components/overlays/DaemonControl.js +0 -87
- package/dist/ui/components/overlays/InviteShareOverlay.js +0 -15
- package/dist/ui/components/overlays/JoinInviteOverlay.js +0 -32
- package/dist/ui/components/overlays/MemoryBrowser.js +0 -100
- package/dist/ui/components/overlays/MessageSelector.js +0 -123
- package/dist/ui/components/overlays/ModelSelector.js +0 -211
- package/dist/ui/components/overlays/PairRequestOverlay.js +0 -42
- package/dist/ui/components/overlays/PeerSelector.js +0 -84
- package/dist/ui/components/overlays/SessionSelector.js +0 -102
- package/dist/ui/components/overlays/SoundSelector.js +0 -86
- package/dist/ui/components/overlays/ThemeSelector.js +0 -139
- package/dist/ui/components/overlays/index.js +0 -15
- package/dist/ui/components/permissions/BashPermissionRequest/BashPermissionRequest.js +0 -53
- package/dist/ui/components/permissions/FallbackPermissionRequest.js +0 -56
- package/dist/ui/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.js +0 -76
- package/dist/ui/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js +0 -18
- package/dist/ui/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.js +0 -64
- package/dist/ui/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js +0 -26
- package/dist/ui/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js +0 -141
- package/dist/ui/components/permissions/PermissionRequest.js +0 -70
- package/dist/ui/components/permissions/PermissionRequestTitle.js +0 -41
- package/dist/ui/components/permissions/hooks.js +0 -10
- package/dist/ui/components/permissions/toolUseOptions.js +0 -68
- package/dist/ui/components/permissions/utils.js +0 -10
- package/dist/ui/components/text-input/Cursor.js +0 -326
- package/dist/ui/components/text-input/TextInput.js +0 -231
- package/dist/ui/components/text-input/imagePaste.js +0 -28
- package/dist/ui/components/text-input/index.js +0 -6
- package/dist/ui/components/text-input/useDoublePress.js +0 -30
- package/dist/ui/components/text-input/useTextInput.js +0 -245
- package/dist/ui/components/tool-types.js +0 -9
- package/dist/ui/constants/figures.js +0 -4
- package/dist/ui/constants/index.js +0 -3
- package/dist/ui/display-mode.js +0 -93
- package/dist/ui/display-policy.js +0 -19
- package/dist/ui/hooks/index.js +0 -6
- package/dist/ui/hooks/useCommandAutocomplete.js +0 -93
- package/dist/ui/hooks/useDoublePress.js +0 -37
- package/dist/ui/hooks/useIndicatorState.js +0 -55
- package/dist/ui/hooks/useInterval.js +0 -23
- package/dist/ui/hooks/useKeyboardShortcuts.js +0 -127
- package/dist/ui/hooks/useTerminalSize.js +0 -55
- package/dist/ui/hooks/useUnifiedMessages.js +0 -117
- package/dist/ui/indicator-state.js +0 -44
- package/dist/ui/markdown/highlight.js +0 -44
- package/dist/ui/markdown/index.js +0 -1460
- package/dist/ui/markdown/tokenizer.js +0 -24
- package/dist/ui/render-item.js +0 -5
- package/dist/ui/screens/REPL.js +0 -119
- package/dist/ui/screens/approval-lifecycle.js +0 -38
- package/dist/ui/status-line.js +0 -72
- package/dist/ui/theme/index.js +0 -51
- package/dist/ui/theme/themes/claude-dark-daltonized.js +0 -51
- package/dist/ui/theme/themes/claude-dark.js +0 -50
- package/dist/ui/theme/themes/claude-light-daltonized.js +0 -51
- package/dist/ui/theme/themes/claude-light.js +0 -50
- package/dist/ui/theme/themes/dark-accessible.js +0 -18
- package/dist/ui/theme/themes/dark.js +0 -49
- package/dist/ui/theme/themes/light-accessible.js +0 -18
- package/dist/ui/theme/themes/light.js +0 -49
- package/dist/ui/theme/types.js +0 -3
- package/dist/ui/theme.js +0 -142
- package/dist/ui/to-render-items.js +0 -145
- package/dist/ui/tools/AgentTool/index.js +0 -30
- package/dist/ui/tools/ArchitectTool/index.js +0 -31
- package/dist/ui/tools/AskUserTool/index.js +0 -46
- package/dist/ui/tools/BashTool/BashToolResultMessage.js +0 -11
- package/dist/ui/tools/BashTool/OutputLine.js +0 -21
- package/dist/ui/tools/BashTool/index.js +0 -91
- package/dist/ui/tools/BrowseTool/index.js +0 -43
- package/dist/ui/tools/BrowserTool/index.js +0 -47
- package/dist/ui/tools/CbmTool/index.js +0 -188
- package/dist/ui/tools/CheckDelegationTool/index.js +0 -46
- package/dist/ui/tools/CheckMessagesTool/index.js +0 -85
- package/dist/ui/tools/CreateQuipTool/index.js +0 -30
- package/dist/ui/tools/CreateSkillTool/index.js +0 -22
- package/dist/ui/tools/CreateToolTool/index.js +0 -31
- package/dist/ui/tools/DelegateRemoteTool/index.js +0 -42
- package/dist/ui/tools/DeployTool/index.js +0 -47
- package/dist/ui/tools/FffTool/index.js +0 -103
- package/dist/ui/tools/FileEditTool/index.js +0 -67
- package/dist/ui/tools/FileReadTool/index.js +0 -68
- package/dist/ui/tools/FileWriteTool/index.js +0 -61
- package/dist/ui/tools/ForkTool/index.js +0 -47
- package/dist/ui/tools/FrgTool/index.js +0 -96
- package/dist/ui/tools/GetThreadTool/index.js +0 -39
- package/dist/ui/tools/GlobTool/index.js +0 -50
- package/dist/ui/tools/GrepTool/index.js +0 -84
- package/dist/ui/tools/HatchArionTool/index.js +0 -36
- package/dist/ui/tools/LearnSkillTool/index.js +0 -22
- package/dist/ui/tools/LearnTool/index.js +0 -43
- package/dist/ui/tools/LearnToolTool/index.js +0 -22
- package/dist/ui/tools/ListClientsTool/index.js +0 -39
- package/dist/ui/tools/LspTool/index.js +0 -261
- package/dist/ui/tools/MCPTool/index.js +0 -33
- package/dist/ui/tools/ManageNetworkTool/index.js +0 -53
- package/dist/ui/tools/MemoryReadTool/index.js +0 -64
- package/dist/ui/tools/MemoryWriteTool/index.js +0 -20
- package/dist/ui/tools/NotebookEditTool/index.js +0 -33
- package/dist/ui/tools/NotebookReadTool/index.js +0 -25
- package/dist/ui/tools/OutlookReadTool/index.js +0 -66
- package/dist/ui/tools/OutlookReplyTool/index.js +0 -49
- package/dist/ui/tools/OutlookSendTool/index.js +0 -49
- package/dist/ui/tools/PauseDelegationTool/index.js +0 -35
- package/dist/ui/tools/ProbeTool/index.js +0 -121
- package/dist/ui/tools/ProcessTool/index.js +0 -66
- package/dist/ui/tools/QuestListTool/index.js +0 -46
- package/dist/ui/tools/QuestReportTool/index.js +0 -49
- package/dist/ui/tools/QuestUpdateTool/index.js +0 -87
- package/dist/ui/tools/QuipCommentTool/index.js +0 -69
- package/dist/ui/tools/QuipReadTool/index.js +0 -71
- package/dist/ui/tools/RestArionTool/index.js +0 -32
- package/dist/ui/tools/RestartTool/index.js +0 -35
- package/dist/ui/tools/ResumeDelegationTool/index.js +0 -35
- package/dist/ui/tools/RetireArionTool/index.js +0 -32
- package/dist/ui/tools/RgTool/index.js +0 -73
- package/dist/ui/tools/SearchKnowledgeTool/index.js +0 -43
- package/dist/ui/tools/SearchMessagesTool/index.js +0 -43
- package/dist/ui/tools/SelfDiagnoseTool/index.js +0 -61
- package/dist/ui/tools/SendMessageTool/index.js +0 -45
- package/dist/ui/tools/SerenaTool/index.js +0 -124
- package/dist/ui/tools/SessionHistoryTool/index.js +0 -52
- package/dist/ui/tools/SgTool/index.js +0 -80
- package/dist/ui/tools/SlackReactTool/index.js +0 -41
- package/dist/ui/tools/SlackReadTool/index.js +0 -48
- package/dist/ui/tools/SlackSendTool/index.js +0 -45
- package/dist/ui/tools/SpawnWorkerTool/index.js +0 -33
- package/dist/ui/tools/StickerRequestTool/index.js +0 -19
- package/dist/ui/tools/ThinkTool/index.js +0 -17
- package/dist/ui/tools/UgTool/index.js +0 -108
- package/dist/ui/tools/UseSkillTool/index.js +0 -22
- package/dist/ui/tools/WakeArionTool/index.js +0 -32
- package/dist/ui/tools/WebFetchTool/index.js +0 -56
- package/dist/ui/tools/WebSearchTool/index.js +0 -44
- package/dist/ui/tools/lsTool/index.js +0 -58
- package/dist/ui/tools/registry.js +0 -197
- package/dist/ui/tools/tool-renderer.js +0 -11
- package/dist/ui/tools/truncation.js +0 -35
- package/dist/ui/types/anthropic.js +0 -4
- package/dist/ui/types/index.js +0 -2
- package/dist/ui/types/message.js +0 -3
- package/dist/ui/types/tool.js +0 -4
- package/dist/ui/utils/array.js +0 -4
- package/dist/ui/utils/cursor.js +0 -131
- package/dist/ui/utils/diff.js +0 -120
- package/dist/ui/utils/format.js +0 -42
- package/dist/ui/utils/fuzzy.js +0 -59
- package/dist/ui/utils/index.js +0 -11
- package/dist/ui/utils/keys.js +0 -8
- package/dist/ui/utils/patch.js +0 -17
- package/dist/ui/utils/risk.js +0 -114
- package/dist/ui/utils/terminal-image.js +0 -70
- package/dist/ui/utils/validation.js +0 -48
- package/dist/ui/verb-pairs.js +0 -248
- package/dist/ui.js +0 -131
- package/src/entrypoints/command-mode.ts +0 -5
- package/src/entrypoints/daemon.ts +0 -54
- package/src/entrypoints/headless-stdio.ts +0 -27
- package/src/entrypoints/interactive.ts +0 -112
- package/src/main.ts +0 -72
- package/src/runtime/configure-bun-sqlite.ts +0 -3
- package/src/runtime/crash-handlers.ts +0 -128
- package/src/runtime/interactive-invocation.test.ts +0 -42
- package/src/runtime/interactive-invocation.ts +0 -51
- package/src/runtime/internal-mode.test.ts +0 -19
- package/src/runtime/internal-mode.ts +0 -24
- package/src/runtime/launch-spec.test.ts +0 -26
- package/src/runtime/launch-spec.ts +0 -84
- package/src/runtime/owner-lease.ts +0 -52
- package/src/runtime/public-mode.test.ts +0 -18
- package/src/runtime/public-mode.ts +0 -19
- package/src/runtime/run-internal-mode.ts +0 -19
- package/src/runtime/runtime-kind.test.ts +0 -23
- package/src/runtime/runtime-kind.ts +0 -41
- package/src/runtime/spawn-aria.ts +0 -62
package/dist/peer-identity.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export function discoveredPeerIdentityKey(peer) {
|
|
2
|
-
const nodeId = typeof peer.nodeId === "string" ? peer.nodeId.trim() : "";
|
|
3
|
-
const transport = typeof peer.transport === "string" ? peer.transport : "unknown";
|
|
4
|
-
if (nodeId) {
|
|
5
|
-
return `node:${nodeId}`;
|
|
6
|
-
}
|
|
7
|
-
const principalFingerprint = typeof peer.principalFingerprint === "string" ? peer.principalFingerprint.trim() : "";
|
|
8
|
-
if (principalFingerprint) {
|
|
9
|
-
return `principal:${principalFingerprint}`;
|
|
10
|
-
}
|
|
11
|
-
return `endpoint:${transport}:${peer.host}:${peer.port}:${peer.displayNameSnapshot}`;
|
|
12
|
-
}
|
|
13
|
-
export function mergeDiscoveredPeersByIdentity(preferredPeers, additionalPeers) {
|
|
14
|
-
const merged = new Map();
|
|
15
|
-
for (const peer of [...preferredPeers, ...additionalPeers]) {
|
|
16
|
-
const key = discoveredPeerIdentityKey(peer);
|
|
17
|
-
if (!merged.has(key)) {
|
|
18
|
-
merged.set(key, peer);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return [...merged.values()];
|
|
22
|
-
}
|
|
23
|
-
//# sourceMappingURL=peer-identity.js.map
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
async function sleep(ms) {
|
|
2
|
-
await new Promise((resolve) => setTimeout(resolve, Math.max(ms, 0)));
|
|
3
|
-
}
|
|
4
|
-
export async function* createPollingSubscription(loadOnce, options = { pollIntervalMs: 1_000 }) {
|
|
5
|
-
const seenVersions = new Map();
|
|
6
|
-
let afterCreatedAt = options.initialAfterCreatedAt ?? 0;
|
|
7
|
-
while (true) {
|
|
8
|
-
const snapshot = await loadOnce();
|
|
9
|
-
let emitted = false;
|
|
10
|
-
for (const item of snapshot) {
|
|
11
|
-
const createdAt = options.getCursor?.(item);
|
|
12
|
-
if (typeof createdAt === "number" && createdAt < afterCreatedAt) {
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
const identity = options.getIdentityKey?.(item);
|
|
16
|
-
if (typeof identity === "string") {
|
|
17
|
-
const versionKey = options.getVersionKey?.(item) ?? JSON.stringify(item);
|
|
18
|
-
if (seenVersions.get(identity) === versionKey) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
seenVersions.set(identity, versionKey);
|
|
22
|
-
}
|
|
23
|
-
if (typeof createdAt === "number") {
|
|
24
|
-
afterCreatedAt = Math.max(afterCreatedAt, createdAt);
|
|
25
|
-
}
|
|
26
|
-
emitted = true;
|
|
27
|
-
yield item;
|
|
28
|
-
}
|
|
29
|
-
if (!emitted) {
|
|
30
|
-
await sleep(options.pollIntervalMs);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
//# sourceMappingURL=polling-subscription.js.map
|
package/dist/relaunch.js
DELETED
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @aria/cli - Relaunch / self-restart infrastructure
|
|
3
|
-
*
|
|
4
|
-
* Implements the parent-child supervisor pattern (inspired by gemini-cli):
|
|
5
|
-
*
|
|
6
|
-
* Parent process (supervisor)
|
|
7
|
-
* └─ spawns child with ARIA_NO_RELAUNCH=true, stdio: inherit
|
|
8
|
-
* └─ loops: if child exits with RELAUNCH_EXIT_CODE → respawn
|
|
9
|
-
* └─ otherwise: propagate exit code and exit
|
|
10
|
-
*
|
|
11
|
-
* Child process (actual ARIA)
|
|
12
|
-
* └─ runs the Ink REPL normally
|
|
13
|
-
* └─ on restart: requestRelaunch() → process.exit(RELAUNCH_EXIT_CODE)
|
|
14
|
-
* └─ supervisor catches it and respawns
|
|
15
|
-
*
|
|
16
|
-
* This replaces the fragile "detached spawn + kill self" approach
|
|
17
|
-
* with a reliable supervisor loop that inherits the terminal properly.
|
|
18
|
-
*/
|
|
19
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync, } from "node:fs";
|
|
20
|
-
import { dirname, join, resolve } from "node:path";
|
|
21
|
-
import { NO_RELAUNCH_ENV, RELAUNCH_EXIT_CODE, RESUME_ARION_ENV, RESUME_SESSION_ENV, RESTART_KIND_ENV, clearRelaunchMarker as clearSharedRelaunchMarker, readRelaunchMarker as readSharedRelaunchMarker, writeRelaunchMarker as writeSharedRelaunchMarker, } from "@aria-cli/types";
|
|
22
|
-
import { getAriaDir } from "./config.js";
|
|
23
|
-
import { resolveAriaLaunchSpec } from "./runtime/launch-spec.js";
|
|
24
|
-
import { spawnAria } from "./runtime/spawn-aria.js";
|
|
25
|
-
export { NO_RELAUNCH_ENV, RELAUNCH_EXIT_CODE, RESUME_ARION_ENV, RESUME_SESSION_ENV, RESTART_KIND_ENV, } from "@aria-cli/types";
|
|
26
|
-
export function writeRelaunchMarker(marker) {
|
|
27
|
-
writeSharedRelaunchMarker(marker);
|
|
28
|
-
}
|
|
29
|
-
export function readRelaunchMarker(pid) {
|
|
30
|
-
return readSharedRelaunchMarker(pid);
|
|
31
|
-
}
|
|
32
|
-
export function clearRelaunchMarker(pid) {
|
|
33
|
-
clearSharedRelaunchMarker(pid);
|
|
34
|
-
}
|
|
35
|
-
function isIgnorableTerminalWriteError(error) {
|
|
36
|
-
if (!error || typeof error !== "object")
|
|
37
|
-
return false;
|
|
38
|
-
const maybe = error;
|
|
39
|
-
return maybe.code === "EPIPE" || maybe.code === "EIO" || maybe.code === "ERR_STREAM_DESTROYED";
|
|
40
|
-
}
|
|
41
|
-
function writeSupervisorStderr(message) {
|
|
42
|
-
try {
|
|
43
|
-
process.stderr.write(message);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
if (isIgnorableTerminalWriteError(error)) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function isProcessExitSentinel(error) {
|
|
53
|
-
if (!error || typeof error !== "object") {
|
|
54
|
-
return error instanceof Error && /^exit:\d+$/.test(error.message);
|
|
55
|
-
}
|
|
56
|
-
if (error.__ariaProcessExit === true) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
return error instanceof Error && /^exit:\d+$/.test(error.message);
|
|
60
|
-
}
|
|
61
|
-
/** Maximum age (ms) for a relaunch marker to be considered valid. */
|
|
62
|
-
const RELAUNCH_MARKER_TTL_MS = 30_000;
|
|
63
|
-
let pendingResumeSessionId = process.env[RESUME_SESSION_ENV] ?? null;
|
|
64
|
-
let pendingArionName = process.env[RESUME_ARION_ENV] || "ARIA";
|
|
65
|
-
export function setPendingResumeSessionId(sessionId) {
|
|
66
|
-
const normalized = sessionId?.trim() || null;
|
|
67
|
-
pendingResumeSessionId = normalized;
|
|
68
|
-
if (normalized) {
|
|
69
|
-
process.env[RESUME_SESSION_ENV] = normalized;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
delete process.env[RESUME_SESSION_ENV];
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export function getPendingResumeSessionId() {
|
|
76
|
-
return pendingResumeSessionId;
|
|
77
|
-
}
|
|
78
|
-
export function setPendingArionName(name) {
|
|
79
|
-
pendingArionName = name || "ARIA";
|
|
80
|
-
process.env[RESUME_ARION_ENV] = pendingArionName;
|
|
81
|
-
}
|
|
82
|
-
export function getPendingArionName() {
|
|
83
|
-
return pendingArionName;
|
|
84
|
-
}
|
|
85
|
-
function findBunWorkspaceRoot(startDir) {
|
|
86
|
-
let dir = resolve(startDir);
|
|
87
|
-
while (true) {
|
|
88
|
-
if (existsSync(join(dir, "bunfig.toml")) || existsSync(join(dir, "bun.lock")))
|
|
89
|
-
return dir;
|
|
90
|
-
const parent = dirname(dir);
|
|
91
|
-
if (parent === dir)
|
|
92
|
-
return null;
|
|
93
|
-
dir = parent;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function getBunCommand() {
|
|
97
|
-
return process.platform === "win32" ? "bun.exe" : "bun";
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Rebuild workspace before relaunch (dev workflow).
|
|
101
|
-
*
|
|
102
|
-
* This is best-effort and only runs when a bun workspace root is detected.
|
|
103
|
-
*/
|
|
104
|
-
export async function rebuildWorkspaceIfPresent(startDir = process.cwd()) {
|
|
105
|
-
const root = findBunWorkspaceRoot(startDir);
|
|
106
|
-
if (!root)
|
|
107
|
-
return { attempted: false };
|
|
108
|
-
const started = Date.now();
|
|
109
|
-
const command = getBunCommand();
|
|
110
|
-
const proc = Bun.spawn([command, "run", "build"], {
|
|
111
|
-
cwd: root,
|
|
112
|
-
env: process.env,
|
|
113
|
-
stdin: "inherit",
|
|
114
|
-
stdout: "inherit",
|
|
115
|
-
stderr: "inherit",
|
|
116
|
-
});
|
|
117
|
-
const code = await proc.exited;
|
|
118
|
-
if (code !== 0) {
|
|
119
|
-
throw new Error(`bun run build failed with exit code ${code}`);
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
attempted: true,
|
|
123
|
-
root,
|
|
124
|
-
durationMs: Date.now() - started,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Returns true if we are already running inside a supervised child process.
|
|
129
|
-
*/
|
|
130
|
-
export function isChildProcess() {
|
|
131
|
-
return process.env[NO_RELAUNCH_ENV] === "true";
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Trigger a relaunch from the child process.
|
|
135
|
-
*
|
|
136
|
-
* Two modes:
|
|
137
|
-
* 1. Supervised child (normal): IPC handoff to supervisor + exit(RELAUNCH_EXIT_CODE)
|
|
138
|
-
* 2. Compiled direct (no supervisor): spawn a new process with resume env vars,
|
|
139
|
-
* wait for it, and propagate its exit code. This avoids loading the 82MB
|
|
140
|
-
* binary twice on the initial startup while still supporting relaunch.
|
|
141
|
-
*/
|
|
142
|
-
export async function requestRelaunch(reason, resumeSessionId) {
|
|
143
|
-
const logReason = reason || "Restart requested";
|
|
144
|
-
if (resumeSessionId !== undefined) {
|
|
145
|
-
setPendingResumeSessionId(resumeSessionId);
|
|
146
|
-
}
|
|
147
|
-
const sessionId = getPendingResumeSessionId();
|
|
148
|
-
const arionName = getPendingArionName();
|
|
149
|
-
writeSupervisorStderr(`[aria] Relaunch requested: ${logReason} (session=${sessionId?.slice(0, 8) ?? "none"}, arion=${arionName})\n`);
|
|
150
|
-
// Write disk-backed marker as durable fallback in case IPC is lost.
|
|
151
|
-
const markerPid = process.ppid || process.pid;
|
|
152
|
-
writeRelaunchMarker({
|
|
153
|
-
sessionId,
|
|
154
|
-
arionName,
|
|
155
|
-
pid: markerPid,
|
|
156
|
-
timestamp: new Date().toISOString(),
|
|
157
|
-
});
|
|
158
|
-
// ── Compiled direct mode: no supervisor listening ──────────────────
|
|
159
|
-
// Spawn a new ARIA process (supervisor mode) with resume env vars.
|
|
160
|
-
// The current process waits for the child and propagates its exit code
|
|
161
|
-
// so the terminal stays owned by an ARIA process throughout.
|
|
162
|
-
const hasSupervisor = typeof process.send === "function";
|
|
163
|
-
if (!hasSupervisor) {
|
|
164
|
-
const spec = resolveAriaLaunchSpec({
|
|
165
|
-
mode: "repl",
|
|
166
|
-
args: [],
|
|
167
|
-
env: {
|
|
168
|
-
...process.env,
|
|
169
|
-
[NO_RELAUNCH_ENV]: "", // clear — enter supervisor mode
|
|
170
|
-
ARIA_USE_SUPERVISOR: "1", // skip compiled-direct, use supervisor loop
|
|
171
|
-
[RESUME_SESSION_ENV]: sessionId || "",
|
|
172
|
-
[RESUME_ARION_ENV]: arionName,
|
|
173
|
-
[RESTART_KIND_ENV]: "explicit",
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
writeSupervisorStderr(`[aria] Spawning new supervisor for relaunch...\n`);
|
|
177
|
-
const child = spawnAria(spec);
|
|
178
|
-
const code = await new Promise((resolve) => {
|
|
179
|
-
child.on("close", (c) => resolve(typeof c === "number" ? c : 1));
|
|
180
|
-
child.on("error", () => resolve(1));
|
|
181
|
-
});
|
|
182
|
-
process.exit(code);
|
|
183
|
-
throw new Error("Unreachable");
|
|
184
|
-
}
|
|
185
|
-
// ── Supervised child mode: IPC handoff ─────────────────────────────
|
|
186
|
-
// Inform the parent supervisor which session to auto-resume after respawn.
|
|
187
|
-
try {
|
|
188
|
-
const send = process.send;
|
|
189
|
-
if (typeof send === "function") {
|
|
190
|
-
const nonce = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
191
|
-
const acked = await new Promise((resolve) => {
|
|
192
|
-
let settled = false;
|
|
193
|
-
const timeout = setTimeout(() => {
|
|
194
|
-
if (settled)
|
|
195
|
-
return;
|
|
196
|
-
settled = true;
|
|
197
|
-
process.off("message", onMessage);
|
|
198
|
-
resolve(false);
|
|
199
|
-
}, 120);
|
|
200
|
-
const onMessage = (msg) => {
|
|
201
|
-
if (!msg || typeof msg !== "object")
|
|
202
|
-
return;
|
|
203
|
-
const payload = msg;
|
|
204
|
-
if (payload.type !== "aria-relaunch-ack")
|
|
205
|
-
return;
|
|
206
|
-
if (payload.nonce !== nonce)
|
|
207
|
-
return;
|
|
208
|
-
if (settled)
|
|
209
|
-
return;
|
|
210
|
-
settled = true;
|
|
211
|
-
clearTimeout(timeout);
|
|
212
|
-
process.off("message", onMessage);
|
|
213
|
-
resolve(true);
|
|
214
|
-
};
|
|
215
|
-
process.on("message", onMessage);
|
|
216
|
-
send({ type: "aria-relaunch", nonce, resumeSessionId: sessionId, arionName }, (err) => {
|
|
217
|
-
if (err && !settled) {
|
|
218
|
-
settled = true;
|
|
219
|
-
clearTimeout(timeout);
|
|
220
|
-
process.off("message", onMessage);
|
|
221
|
-
resolve(false);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
if (acked) {
|
|
226
|
-
writeSupervisorStderr(`[aria] IPC handoff acknowledged by supervisor\n`);
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
writeSupervisorStderr(`[aria] IPC ack timeout — disk marker will be used as fallback\n`);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
// Non-fatal: disk marker is the fallback.
|
|
235
|
-
}
|
|
236
|
-
process.exit(RELAUNCH_EXIT_CODE);
|
|
237
|
-
// Unreachable — satisfies TypeScript
|
|
238
|
-
throw new Error("Unreachable");
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Supervisor loop: spawns ARIA as a child process and respawns on RELAUNCH_EXIT_CODE.
|
|
242
|
-
*
|
|
243
|
-
* The child inherits the terminal (stdio: inherit) so the user experience
|
|
244
|
-
* is seamless. The parent just watches and re-executes when needed.
|
|
245
|
-
*
|
|
246
|
-
* @param scriptPath - Legacy compatibility label retained for tests/callers
|
|
247
|
-
* @param scriptArgs - Arguments to forward to the internal REPL mode
|
|
248
|
-
* @param nodeArgs - Legacy compatibility placeholder (ignored)
|
|
249
|
-
*/
|
|
250
|
-
/** Maximum consecutive crash restarts before giving up. */
|
|
251
|
-
const MAX_CRASH_RESTARTS = 3;
|
|
252
|
-
function getCrashStatePath(...parts) {
|
|
253
|
-
return join(getAriaDir(), ...parts);
|
|
254
|
-
}
|
|
255
|
-
function getCrashMarkerDir() {
|
|
256
|
-
return getCrashStatePath("crash-markers");
|
|
257
|
-
}
|
|
258
|
-
function getCrashMarkerPath(sessionId) {
|
|
259
|
-
return join(getCrashMarkerDir(), `${sessionId}.json`);
|
|
260
|
-
}
|
|
261
|
-
function readCrashMarker(sessionId) {
|
|
262
|
-
const markerPath = getCrashMarkerPath(sessionId);
|
|
263
|
-
if (!existsSync(markerPath))
|
|
264
|
-
return null;
|
|
265
|
-
try {
|
|
266
|
-
return JSON.parse(readFileSync(markerPath, "utf-8"));
|
|
267
|
-
}
|
|
268
|
-
catch {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function writeCrashMarker(marker) {
|
|
273
|
-
try {
|
|
274
|
-
const dir = getCrashMarkerDir();
|
|
275
|
-
mkdirSync(dir, { recursive: true });
|
|
276
|
-
writeFileSync(getCrashMarkerPath(marker.sessionId), JSON.stringify(marker), "utf-8");
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
// Best-effort — don't crash the supervisor over crash tracking
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function clearCrashMarker(sessionId) {
|
|
283
|
-
try {
|
|
284
|
-
const markerPath = getCrashMarkerPath(sessionId);
|
|
285
|
-
if (existsSync(markerPath))
|
|
286
|
-
unlinkSync(markerPath);
|
|
287
|
-
}
|
|
288
|
-
catch {
|
|
289
|
-
// Best-effort
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
function getCrashReportPath() {
|
|
293
|
-
return getCrashStatePath("crash-reports", "latest.json");
|
|
294
|
-
}
|
|
295
|
-
function abandonPoisonedResumeSession(sessionId, crashCount) {
|
|
296
|
-
writeSupervisorStderr(`[aria] Session ${sessionId} exceeded crash limit (${crashCount}/${MAX_CRASH_RESTARTS}). ` +
|
|
297
|
-
`Starting fresh instead of resuming.\n`);
|
|
298
|
-
clearCrashMarker(sessionId);
|
|
299
|
-
pendingResumeSessionId = null;
|
|
300
|
-
setPendingResumeSessionId(null);
|
|
301
|
-
}
|
|
302
|
-
function readAndClearCrashReport() {
|
|
303
|
-
const reportPath = getCrashReportPath();
|
|
304
|
-
if (!existsSync(reportPath))
|
|
305
|
-
return null;
|
|
306
|
-
try {
|
|
307
|
-
const raw = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
308
|
-
try {
|
|
309
|
-
unlinkSync(reportPath);
|
|
310
|
-
}
|
|
311
|
-
catch {
|
|
312
|
-
/* best effort */
|
|
313
|
-
}
|
|
314
|
-
return {
|
|
315
|
-
message: raw?.error?.message ?? "unknown",
|
|
316
|
-
stack: raw?.error?.stack ?? undefined,
|
|
317
|
-
heapMb: raw?.process?.heapUsedMb,
|
|
318
|
-
rssMb: raw?.process?.rssMb,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
try {
|
|
323
|
-
unlinkSync(reportPath);
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
/* best effort */
|
|
327
|
-
}
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Unified fallback resolver for recovering session context when IPC handoff
|
|
333
|
-
* is lost (child exited before parent received the message).
|
|
334
|
-
*
|
|
335
|
-
* Priority order:
|
|
336
|
-
* 1. Disk-backed relaunch marker (written by child before exit)
|
|
337
|
-
* 2. SessionHistory lookup (most recent incomplete session for the active arion)
|
|
338
|
-
*
|
|
339
|
-
* Used for both crash recovery AND explicit restart recovery.
|
|
340
|
-
*/
|
|
341
|
-
async function recoverResumeContext(currentArionName) {
|
|
342
|
-
// 1. Try disk-backed relaunch marker (namespaced by current child PID)
|
|
343
|
-
const marker = readRelaunchMarker(process.pid);
|
|
344
|
-
if (marker) {
|
|
345
|
-
const age = Date.now() - new Date(marker.timestamp).getTime();
|
|
346
|
-
if (age < RELAUNCH_MARKER_TTL_MS) {
|
|
347
|
-
clearRelaunchMarker(process.pid);
|
|
348
|
-
return {
|
|
349
|
-
sessionId: marker.sessionId,
|
|
350
|
-
arionName: marker.arionName || currentArionName,
|
|
351
|
-
source: "marker",
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
// Stale marker — clean up
|
|
355
|
-
clearRelaunchMarker(process.pid);
|
|
356
|
-
}
|
|
357
|
-
// 2. Query SessionHistory for most recent incomplete session
|
|
358
|
-
// First preferred arion, then scan other arions and choose most recently updated.
|
|
359
|
-
try {
|
|
360
|
-
const { SessionHistory } = await import("./history/index.js");
|
|
361
|
-
const { getAriaDir } = await import("./config.js");
|
|
362
|
-
const ariaDir = getAriaDir();
|
|
363
|
-
const candidateArions = new Set([currentArionName]);
|
|
364
|
-
const arionsDir = join(ariaDir, "arions");
|
|
365
|
-
if (existsSync(arionsDir)) {
|
|
366
|
-
for (const entry of readdirSync(arionsDir, { withFileTypes: true })) {
|
|
367
|
-
if (entry.isDirectory() && entry.name.trim()) {
|
|
368
|
-
candidateArions.add(entry.name.trim());
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
let best = null;
|
|
373
|
-
for (const arionName of candidateArions) {
|
|
374
|
-
const dbPath = SessionHistory.resolvePerArionPath(ariaDir, arionName);
|
|
375
|
-
if (!existsSync(dbPath))
|
|
376
|
-
continue;
|
|
377
|
-
const tmpHistory = new SessionHistory(dbPath);
|
|
378
|
-
try {
|
|
379
|
-
const incomplete = tmpHistory.getIncompleteSessions(1);
|
|
380
|
-
if (incomplete.length === 0)
|
|
381
|
-
continue;
|
|
382
|
-
const top = incomplete[0];
|
|
383
|
-
const updatedAtMs = top.updatedAt.getTime();
|
|
384
|
-
// Prefer current arion first when timestamps tie.
|
|
385
|
-
if (!best ||
|
|
386
|
-
updatedAtMs > best.updatedAtMs ||
|
|
387
|
-
(updatedAtMs === best.updatedAtMs && arionName === currentArionName)) {
|
|
388
|
-
best = {
|
|
389
|
-
sessionId: top.id,
|
|
390
|
-
arionName,
|
|
391
|
-
updatedAtMs,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
finally {
|
|
396
|
-
tmpHistory.close();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (best) {
|
|
400
|
-
return {
|
|
401
|
-
sessionId: best.sessionId,
|
|
402
|
-
arionName: best.arionName,
|
|
403
|
-
source: best.arionName === currentArionName ? "history" : "history-scan",
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
catch (err) {
|
|
408
|
-
writeSupervisorStderr(`[aria] SessionHistory fallback failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
409
|
-
}
|
|
410
|
-
return { sessionId: null, arionName: currentArionName, source: "none" };
|
|
411
|
-
}
|
|
412
|
-
export async function supervise(scriptPath, scriptArgs, nodeArgs = [], options = {}) {
|
|
413
|
-
// Restore crash count from persistent marker if resuming a session
|
|
414
|
-
let consecutiveCrashes = 0;
|
|
415
|
-
if (pendingResumeSessionId) {
|
|
416
|
-
const marker = readCrashMarker(pendingResumeSessionId);
|
|
417
|
-
if (marker) {
|
|
418
|
-
consecutiveCrashes = marker.count;
|
|
419
|
-
if (consecutiveCrashes >= MAX_CRASH_RESTARTS) {
|
|
420
|
-
abandonPoisonedResumeSession(pendingResumeSessionId, consecutiveCrashes);
|
|
421
|
-
consecutiveCrashes = 0;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
// Tracks the kind of restart so the child can distinguish explicit /restart
|
|
426
|
-
// from crash recovery (affects whether auto-continue message is injected).
|
|
427
|
-
let nextRestartKind;
|
|
428
|
-
while (true) {
|
|
429
|
-
try {
|
|
430
|
-
await options.beforeSpawn?.();
|
|
431
|
-
const result = await spawnChild(scriptPath, scriptArgs, nodeArgs, pendingResumeSessionId, pendingArionName, nextRestartKind);
|
|
432
|
-
nextRestartKind = undefined; // Reset for next iteration
|
|
433
|
-
const exitCode = result.code;
|
|
434
|
-
pendingResumeSessionId = result.resumeSessionId;
|
|
435
|
-
setPendingResumeSessionId(pendingResumeSessionId);
|
|
436
|
-
pendingArionName = result.arionName;
|
|
437
|
-
setPendingArionName(pendingArionName);
|
|
438
|
-
if (exitCode === 0) {
|
|
439
|
-
// Clean exit — clear any crash markers and relaunch marker
|
|
440
|
-
if (pendingResumeSessionId)
|
|
441
|
-
clearCrashMarker(pendingResumeSessionId);
|
|
442
|
-
clearRelaunchMarker(process.pid);
|
|
443
|
-
process.exit(0);
|
|
444
|
-
}
|
|
445
|
-
if (exitCode === RELAUNCH_EXIT_CODE) {
|
|
446
|
-
// Explicit restart request — reset crash counter and clear crash marker
|
|
447
|
-
consecutiveCrashes = 0;
|
|
448
|
-
if (pendingResumeSessionId)
|
|
449
|
-
clearCrashMarker(pendingResumeSessionId);
|
|
450
|
-
// If IPC didn't deliver the session ID, use the unified fallback resolver.
|
|
451
|
-
// This is the key fix: explicit restarts now have the same recovery
|
|
452
|
-
// guarantees as crash restarts.
|
|
453
|
-
if (!pendingResumeSessionId) {
|
|
454
|
-
const recovered = await recoverResumeContext(pendingArionName);
|
|
455
|
-
if (recovered.sessionId) {
|
|
456
|
-
pendingResumeSessionId = recovered.sessionId;
|
|
457
|
-
setPendingResumeSessionId(pendingResumeSessionId);
|
|
458
|
-
pendingArionName = recovered.arionName;
|
|
459
|
-
setPendingArionName(pendingArionName);
|
|
460
|
-
writeSupervisorStderr(`[aria] IPC session ID lost — recovered via ${recovered.source}: ${pendingResumeSessionId.slice(0, 8)}...\n`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
else {
|
|
464
|
-
// IPC succeeded — consume disk marker if present
|
|
465
|
-
clearRelaunchMarker(process.pid);
|
|
466
|
-
}
|
|
467
|
-
nextRestartKind = "explicit";
|
|
468
|
-
writeSupervisorStderr(`[aria] Respawning after relaunch request...\n`);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
// Unexpected exit (crash, segfault, OOM, unhandled exception)
|
|
472
|
-
nextRestartKind = "crash";
|
|
473
|
-
consecutiveCrashes++;
|
|
474
|
-
const crashAttempt = consecutiveCrashes;
|
|
475
|
-
const crashedSessionId = pendingResumeSessionId;
|
|
476
|
-
let abandonedResumeSession = false;
|
|
477
|
-
// Read crash report written by the child's uncaughtException/unhandledRejection handler
|
|
478
|
-
const crashReport = readAndClearCrashReport();
|
|
479
|
-
// Persist crash marker so the count survives supervisor restarts
|
|
480
|
-
if (crashedSessionId) {
|
|
481
|
-
writeCrashMarker({
|
|
482
|
-
sessionId: crashedSessionId,
|
|
483
|
-
count: crashAttempt,
|
|
484
|
-
lastExitCode: exitCode,
|
|
485
|
-
lastCrash: new Date().toISOString(),
|
|
486
|
-
lastError: crashReport?.message,
|
|
487
|
-
lastStack: crashReport?.stack?.slice(0, 2000),
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
// Log crash details to stderr for diagnostics
|
|
491
|
-
if (crashReport) {
|
|
492
|
-
writeSupervisorStderr(`[aria] Crash error: ${crashReport.message}\n`);
|
|
493
|
-
if (crashReport.stack) {
|
|
494
|
-
writeSupervisorStderr(`[aria] Stack: ${crashReport.stack.slice(0, 2000)}\n`);
|
|
495
|
-
}
|
|
496
|
-
if (crashReport.heapMb != null || crashReport.rssMb != null) {
|
|
497
|
-
writeSupervisorStderr(`[aria] Memory at crash: heap=${crashReport.heapMb ?? "?"}MB, rss=${crashReport.rssMb ?? "?"}MB\n`);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
if (crashedSessionId && crashAttempt >= MAX_CRASH_RESTARTS) {
|
|
501
|
-
abandonPoisonedResumeSession(crashedSessionId, crashAttempt);
|
|
502
|
-
consecutiveCrashes = 0;
|
|
503
|
-
abandonedResumeSession = true;
|
|
504
|
-
}
|
|
505
|
-
else if (crashAttempt > MAX_CRASH_RESTARTS) {
|
|
506
|
-
writeSupervisorStderr(`[aria] Too many consecutive crashes (${crashAttempt}), giving up (exit ${exitCode})\n`);
|
|
507
|
-
process.exit(exitCode);
|
|
508
|
-
}
|
|
509
|
-
const backoffMs = Math.min(1000 * Math.pow(2, Math.max(crashAttempt - 1, 0)), 30000);
|
|
510
|
-
writeSupervisorStderr(`[aria] Crash detected (exit ${exitCode}, attempt ${crashAttempt}/${MAX_CRASH_RESTARTS}). ` +
|
|
511
|
-
`Restarting in ${backoffMs}ms...\n`);
|
|
512
|
-
// Fallback: if IPC didn't deliver the session ID, use the unified resolver.
|
|
513
|
-
if (!pendingResumeSessionId && !abandonedResumeSession) {
|
|
514
|
-
const recovered = await recoverResumeContext(pendingArionName);
|
|
515
|
-
if (recovered.sessionId) {
|
|
516
|
-
pendingResumeSessionId = recovered.sessionId;
|
|
517
|
-
setPendingResumeSessionId(pendingResumeSessionId);
|
|
518
|
-
pendingArionName = recovered.arionName;
|
|
519
|
-
setPendingArionName(pendingArionName);
|
|
520
|
-
writeSupervisorStderr(`[aria] IPC session ID lost — recovered via ${recovered.source}: ${pendingResumeSessionId.slice(0, 8)}...\n`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
await new Promise((r) => setTimeout(r, backoffMs));
|
|
524
|
-
// Respawn with the same resume session ID so the child auto-continues
|
|
525
|
-
}
|
|
526
|
-
catch (error) {
|
|
527
|
-
if (isProcessExitSentinel(error)) {
|
|
528
|
-
throw error;
|
|
529
|
-
}
|
|
530
|
-
process.stdin.resume();
|
|
531
|
-
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
532
|
-
writeSupervisorStderr(`[aria] Fatal: failed to spawn child process.\n${message}\n`);
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
export async function superviseInteractiveSession(invocation, options = {}) {
|
|
538
|
-
const forwardedArgs = [...invocation.rawArgs];
|
|
539
|
-
return supervise("__internal-repl", forwardedArgs, [], options);
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Spawn a single child process and wait for it to exit.
|
|
543
|
-
*/
|
|
544
|
-
function spawnChild(scriptPath, scriptArgs, nodeArgs, resumeSessionId, arionName, restartKind) {
|
|
545
|
-
// Pause parent stdin while child owns the terminal
|
|
546
|
-
process.stdin.pause();
|
|
547
|
-
void scriptPath;
|
|
548
|
-
void nodeArgs;
|
|
549
|
-
const childEnv = {
|
|
550
|
-
...process.env,
|
|
551
|
-
[NO_RELAUNCH_ENV]: "true",
|
|
552
|
-
};
|
|
553
|
-
if (resumeSessionId) {
|
|
554
|
-
childEnv[RESUME_SESSION_ENV] = resumeSessionId;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
delete childEnv[RESUME_SESSION_ENV];
|
|
558
|
-
}
|
|
559
|
-
childEnv[RESUME_ARION_ENV] = arionName;
|
|
560
|
-
if (restartKind) {
|
|
561
|
-
childEnv[RESTART_KIND_ENV] = restartKind;
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
delete childEnv[RESTART_KIND_ENV];
|
|
565
|
-
}
|
|
566
|
-
const childArgs = scriptArgs;
|
|
567
|
-
const spec = resolveAriaLaunchSpec({
|
|
568
|
-
mode: "repl",
|
|
569
|
-
args: childArgs,
|
|
570
|
-
env: childEnv,
|
|
571
|
-
cwd: process.cwd(),
|
|
572
|
-
});
|
|
573
|
-
const child = spawnAria(spec, { ipc: true });
|
|
574
|
-
return new Promise((resolve, reject) => {
|
|
575
|
-
let resumeFromChild = resumeSessionId;
|
|
576
|
-
let arionFromChild = arionName;
|
|
577
|
-
child.on("message", (message) => {
|
|
578
|
-
if (!message || typeof message !== "object")
|
|
579
|
-
return;
|
|
580
|
-
const payload = message;
|
|
581
|
-
if (payload.type !== "aria-relaunch")
|
|
582
|
-
return;
|
|
583
|
-
if (typeof payload.resumeSessionId === "string" && payload.resumeSessionId.trim()) {
|
|
584
|
-
resumeFromChild = payload.resumeSessionId.trim();
|
|
585
|
-
}
|
|
586
|
-
else if (payload.resumeSessionId === null) {
|
|
587
|
-
resumeFromChild = null;
|
|
588
|
-
}
|
|
589
|
-
if (typeof payload.arionName === "string" && payload.arionName.trim()) {
|
|
590
|
-
arionFromChild = payload.arionName.trim();
|
|
591
|
-
}
|
|
592
|
-
// Acknowledge receipt so child can exit with confidence.
|
|
593
|
-
try {
|
|
594
|
-
child.send({
|
|
595
|
-
type: "aria-relaunch-ack",
|
|
596
|
-
nonce: typeof payload.nonce === "string" ? payload.nonce : undefined,
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
catch {
|
|
600
|
-
// Child may have already exited — non-fatal.
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
child.on("error", (err) => {
|
|
604
|
-
process.stdin.resume();
|
|
605
|
-
reject(err);
|
|
606
|
-
});
|
|
607
|
-
child.on("close", (code) => {
|
|
608
|
-
process.stdin.resume();
|
|
609
|
-
resolve({
|
|
610
|
-
code: (typeof code === "number" ? code : null) ?? 1,
|
|
611
|
-
resumeSessionId: resumeFromChild,
|
|
612
|
-
arionName: arionFromChild,
|
|
613
|
-
});
|
|
614
|
-
});
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
//# sourceMappingURL=relaunch.js.map
|