ubume 0.1.0
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 +88 -0
- package/bin/codexa-local-harness-bridge.js +3 -0
- package/bin/codexa.js +28 -0
- package/bin/ubume-local-harness-bridge.js +325 -0
- package/bin/ubume.js +398 -0
- package/package.json +66 -0
- package/src/app.tsx +5759 -0
- package/src/commands/handler.ts +889 -0
- package/src/config/appVersion.ts +69 -0
- package/src/config/buildInfo.ts +3 -0
- package/src/config/launchArgs.ts +196 -0
- package/src/config/layeredConfig.ts +853 -0
- package/src/config/legacyEnv.ts +16 -0
- package/src/config/persistence.ts +377 -0
- package/src/config/runtimeConfig.ts +558 -0
- package/src/config/settings.ts +405 -0
- package/src/config/toml-serialize.ts +109 -0
- package/src/config/trustStore.ts +84 -0
- package/src/config/updateCheckCache.ts +85 -0
- package/src/core/README.md +55 -0
- package/src/core/agent/loop.ts +464 -0
- package/src/core/agent/protocol.ts +345 -0
- package/src/core/agent/tools.ts +423 -0
- package/src/core/auth/codexAuth.ts +359 -0
- package/src/core/codex/codexExecArgs.ts +166 -0
- package/src/core/codex/codexLaunch.ts +163 -0
- package/src/core/codex/codexPrompt.ts +429 -0
- package/src/core/debug/inputDebug.ts +51 -0
- package/src/core/debug/localStreamDebug.ts +50 -0
- package/src/core/debug/modelStateDebug.ts +35 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/claudeExecutable.ts +63 -0
- package/src/core/executables/codexExecutable.ts +160 -0
- package/src/core/executables/executableResolver.ts +164 -0
- package/src/core/executables/geminiExecutable.ts +78 -0
- package/src/core/models/codexCapabilities.ts +97 -0
- package/src/core/models/codexModelCapabilities.ts +624 -0
- package/src/core/models/codexModelsCacheSeed.ts +153 -0
- package/src/core/models/modelSpecs.ts +303 -0
- package/src/core/models/providerModelCache.ts +94 -0
- package/src/core/perf/profiler.ts +125 -0
- package/src/core/perf/renderDebug.ts +398 -0
- package/src/core/process/CommandRunner.ts +280 -0
- package/src/core/process/processValidation.ts +111 -0
- package/src/core/providerLauncher/launcher.ts +220 -0
- package/src/core/providerLauncher/registry.ts +354 -0
- package/src/core/providerLauncher/types.ts +95 -0
- package/src/core/providerLauncher/workspaceConfig.ts +487 -0
- package/src/core/providerRuntime/anthropic.ts +580 -0
- package/src/core/providerRuntime/antigravity.ts +500 -0
- package/src/core/providerRuntime/capabilityProfile.ts +383 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -0
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/codexaCupy.ts +97 -0
- package/src/core/providerRuntime/codexaNative.ts +425 -0
- package/src/core/providerRuntime/contextMetadata.ts +397 -0
- package/src/core/providerRuntime/gemini.ts +789 -0
- package/src/core/providerRuntime/lmstudio.ts +118 -0
- package/src/core/providerRuntime/local.ts +770 -0
- package/src/core/providerRuntime/localHarness/runtime.ts +1090 -0
- package/src/core/providerRuntime/localOutputBudget.ts +17 -0
- package/src/core/providerRuntime/mistralVibe.ts +667 -0
- package/src/core/providerRuntime/models.ts +175 -0
- package/src/core/providerRuntime/reasoning.ts +20 -0
- package/src/core/providerRuntime/registry.ts +286 -0
- package/src/core/providerRuntime/types.ts +142 -0
- package/src/core/providerRuntime/unsloth.ts +216 -0
- package/src/core/providers/codexJsonStream.ts +305 -0
- package/src/core/providers/codexSubprocess.ts +378 -0
- package/src/core/providers/codexTranscript.ts +695 -0
- package/src/core/providers/openaiNative.ts +13 -0
- package/src/core/providers/registry.ts +21 -0
- package/src/core/providers/types.ts +91 -0
- package/src/core/shared/attachments.ts +101 -0
- package/src/core/shared/cleanupFastFail.ts +67 -0
- package/src/core/shared/clipboard.ts +24 -0
- package/src/core/shared/clipboardImage.ts +111 -0
- package/src/core/shared/githubDiagnostics.ts +222 -0
- package/src/core/shared/hollowResponseFormat.ts +39 -0
- package/src/core/terminal/clearFrameBoundary.ts +852 -0
- package/src/core/terminal/frameLock.ts +110 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/startupClear.ts +20 -0
- package/src/core/terminal/terminalCapabilities.ts +100 -0
- package/src/core/terminal/terminalControl.ts +169 -0
- package/src/core/terminal/terminalSanitize.ts +147 -0
- package/src/core/terminal/terminalTitle.ts +400 -0
- package/src/core/version/channel.ts +27 -0
- package/src/core/version/packageManager.ts +119 -0
- package/src/core/version/updateCheck.ts +203 -0
- package/src/core/workspace/appData.ts +107 -0
- package/src/core/workspace/conversationStore.ts +335 -0
- package/src/core/workspace/launchContext.ts +259 -0
- package/src/core/workspace/planStorage.ts +135 -0
- package/src/core/workspace/projectInstructions.ts +54 -0
- package/src/core/workspace/scratchDir.ts +64 -0
- package/src/core/workspace/workspaceActivity.ts +384 -0
- package/src/core/workspace/workspaceGuard.ts +377 -0
- package/src/core/workspace/workspaceRoot.ts +47 -0
- package/src/exec.ts +73 -0
- package/src/headless/execArgs.ts +296 -0
- package/src/headless/execRunner.ts +304 -0
- package/src/index.tsx +270 -0
- package/src/legacyEnvBootstrap.ts +5 -0
- package/src/session/appSession.ts +771 -0
- package/src/session/chatLifecycle.ts +994 -0
- package/src/session/conversation.ts +107 -0
- package/src/session/liveRenderScheduler.ts +214 -0
- package/src/session/persistedResponse.ts +93 -0
- package/src/session/planFlow.ts +159 -0
- package/src/session/planTranscript.ts +19 -0
- package/src/session/promptRunSchedule.ts +26 -0
- package/src/session/types.ts +234 -0
- package/src/test/runtimeTestUtils.ts +14 -0
- package/src/types/react-dom.d.ts +3 -0
- package/src/ui/chrome/ActivityBars.tsx +68 -0
- package/src/ui/chrome/ActivityIndicator.tsx +58 -0
- package/src/ui/chrome/AnimatedStatusText.tsx +69 -0
- package/src/ui/chrome/AppShell.tsx +474 -0
- package/src/ui/chrome/BottomComposer.tsx +1135 -0
- package/src/ui/chrome/DashCard.tsx +82 -0
- package/src/ui/chrome/RunFooter.tsx +65 -0
- package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
- package/src/ui/chrome/Spinner.tsx +25 -0
- package/src/ui/chrome/TopHeader.tsx +439 -0
- package/src/ui/chrome/UpdateAvailableCard.tsx +42 -0
- package/src/ui/chrome/busyStatusAnimation.ts +11 -0
- package/src/ui/input/commandNormalize.ts +66 -0
- package/src/ui/input/focus.ts +73 -0
- package/src/ui/input/imageAttachments.ts +18 -0
- package/src/ui/input/inputBuffer.ts +203 -0
- package/src/ui/input/pastedContent.ts +75 -0
- package/src/ui/input/rawArrowKeys.ts +28 -0
- package/src/ui/input/slashCommands.ts +43 -0
- package/src/ui/input/useStdinRawModeLease.ts +23 -0
- package/src/ui/layout.ts +560 -0
- package/src/ui/panels/AttachmentImportPanel.tsx +131 -0
- package/src/ui/panels/AuthPanel.tsx +149 -0
- package/src/ui/panels/BackendPicker.tsx +28 -0
- package/src/ui/panels/ModePicker.tsx +31 -0
- package/src/ui/panels/ModelPicker.tsx +31 -0
- package/src/ui/panels/ModelPickerScreen.tsx +761 -0
- package/src/ui/panels/ModelReasoningPicker.tsx +458 -0
- package/src/ui/panels/Panel.tsx +51 -0
- package/src/ui/panels/PermissionsPanel.tsx +78 -0
- package/src/ui/panels/PlanActionPicker.tsx +187 -0
- package/src/ui/panels/ProviderPicker.tsx +753 -0
- package/src/ui/panels/ProviderSetupPrompt.tsx +52 -0
- package/src/ui/panels/ReasoningPicker.tsx +46 -0
- package/src/ui/panels/ResumePicker.tsx +90 -0
- package/src/ui/panels/SelectionPanel.tsx +138 -0
- package/src/ui/panels/SettingsPanel.tsx +156 -0
- package/src/ui/panels/TextEntryPanel.tsx +139 -0
- package/src/ui/panels/ThemePicker.tsx +32 -0
- package/src/ui/panels/ToolApprovalPanel.tsx +46 -0
- package/src/ui/panels/UpdatePromptPanel.tsx +236 -0
- package/src/ui/panels/responsivePickerViewport.ts +64 -0
- package/src/ui/render/Markdown.tsx +331 -0
- package/src/ui/render/diffRenderer.ts +116 -0
- package/src/ui/render/logoVariants.ts +113 -0
- package/src/ui/render/modeDisplay.ts +52 -0
- package/src/ui/render/outputPipeline.ts +64 -0
- package/src/ui/render/runtimeDisplay.ts +128 -0
- package/src/ui/render/terminalAnswerFormat.ts +128 -0
- package/src/ui/render/textLayout.ts +392 -0
- package/src/ui/theme.tsx +274 -0
- package/src/ui/themeFlow.ts +41 -0
- package/src/ui/timeline/ActionRequiredBlock.tsx +38 -0
- package/src/ui/timeline/AgentBlock.tsx +130 -0
- package/src/ui/timeline/StaticIntroItem.tsx +54 -0
- package/src/ui/timeline/ThinkingBlock.tsx +100 -0
- package/src/ui/timeline/Timeline.tsx +1410 -0
- package/src/ui/timeline/TranscriptShell.tsx +302 -0
- package/src/ui/timeline/TurnGroup.tsx +673 -0
- package/src/ui/timeline/layoutListWindow.ts +145 -0
- package/src/ui/timeline/liveViewportWindow.ts +68 -0
- package/src/ui/timeline/progressEntries.ts +156 -0
- package/src/ui/timeline/runActivityView.ts +37 -0
- package/src/ui/timeline/staticTranscriptCache.ts +174 -0
- package/src/ui/timeline/streamCoalesce.ts +53 -0
- package/src/ui/timeline/timelineMeasure.ts +3273 -0
- package/src/ui/useThrottledValue.ts +31 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import "./legacyEnvBootstrap.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { render, type Instance, type RenderOptions } from "ink";
|
|
4
|
+
import { App } from "./app.js";
|
|
5
|
+
import { parseLaunchArgs, type LaunchArgs } from "./config/launchArgs.js";
|
|
6
|
+
import { loadSettings } from "./config/persistence.js";
|
|
7
|
+
import { APP_NAME, formatTerminalTitlePath } from "./config/settings.js";
|
|
8
|
+
import { getTerminalCapability } from "./core/terminal/terminalCapabilities.js";
|
|
9
|
+
import * as renderDebug from "./core/perf/renderDebug.js";
|
|
10
|
+
import { MIN_VIEWPORT_COLS, MIN_VIEWPORT_ROWS } from "./ui/layout.js";
|
|
11
|
+
import {
|
|
12
|
+
setIntendedTerminalTitle,
|
|
13
|
+
} from "./core/terminal/terminalTitle.js";
|
|
14
|
+
import { resolveWorkspaceRoot } from "./core/workspace/workspaceRoot.js";
|
|
15
|
+
import {
|
|
16
|
+
createTerminalModeController,
|
|
17
|
+
writeTerminalControl,
|
|
18
|
+
} from "./core/terminal/terminalControl.js";
|
|
19
|
+
import { resolveInkRenderInstance, type InkRenderInstance } from "./core/terminal/inkRenderReset.js";
|
|
20
|
+
import { resetFrameLockForResize, wrapStdoutWithFrameLock } from "./core/terminal/frameLock.js";
|
|
21
|
+
|
|
22
|
+
type RenderHandle = Pick<Instance, "clear" | "cleanup" | "waitUntilExit">;
|
|
23
|
+
|
|
24
|
+
export function resolveKittyKeyboardOptions(
|
|
25
|
+
env: Record<string, string | undefined>,
|
|
26
|
+
): RenderOptions["kittyKeyboard"] | undefined {
|
|
27
|
+
// Ink's auto mode probes with CSI ? u. Kitty's response can race Ink's
|
|
28
|
+
// regular input listeners and leak into both the first frame and composer.
|
|
29
|
+
// A direct Kitty session is already known to support the protocol, so enable
|
|
30
|
+
// it without probing. Avoid protocol negotiation in all other terminals.
|
|
31
|
+
if ((env.TERM ?? "").toLowerCase() !== "xterm-kitty") {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
mode: "enabled",
|
|
37
|
+
flags: ["disambiguateEscapeCodes"],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AppStdout {
|
|
42
|
+
isTTY: boolean;
|
|
43
|
+
write: (chunk: string) => boolean;
|
|
44
|
+
on: (event: "resize" | string, listener: (...args: unknown[]) => void) => void;
|
|
45
|
+
off: (event: "resize" | string, listener: (...args: unknown[]) => void) => void;
|
|
46
|
+
columns?: number;
|
|
47
|
+
rows?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface StartAppDependencies {
|
|
51
|
+
stdin: Pick<NodeJS.ReadStream, "isTTY">;
|
|
52
|
+
stdout: AppStdout;
|
|
53
|
+
stderr: Pick<NodeJS.WriteStream, "write"> & Partial<Pick<NodeJS.WriteStream, "isTTY">>;
|
|
54
|
+
env: Record<string, string | undefined>;
|
|
55
|
+
platform: NodeJS.Platform;
|
|
56
|
+
argv: string[];
|
|
57
|
+
renderApp: (node: React.ReactElement, options?: RenderOptions) => RenderHandle;
|
|
58
|
+
resolveInkInstanceForStdout: (stdout: AppStdout) => InkRenderInstance | null;
|
|
59
|
+
registerExitHandler: (handler: () => void) => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface StartAppResult {
|
|
63
|
+
started: boolean;
|
|
64
|
+
exitCode: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface ActiveRootState {
|
|
68
|
+
cleanup: () => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let activeRoot: ActiveRootState | null = null;
|
|
72
|
+
|
|
73
|
+
function debugLaunch(env: Record<string, string | undefined>, write: (chunk: string, source: string) => boolean, fields: Record<string, unknown>): void {
|
|
74
|
+
if (env.UBUME_DEBUG_LAUNCH !== "1" && env.CODEXA_DEBUG_LAUNCH !== "1") {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
write(`[ubume:launch] ${JSON.stringify(fields)}\n`, "src/index.tsx:launchDebug");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function hasInvalidRestoreDimensions(stdout: Pick<AppStdout, "columns" | "rows">): boolean {
|
|
82
|
+
const cols = stdout.columns;
|
|
83
|
+
const rows = stdout.rows;
|
|
84
|
+
return !Number.isFinite(cols) || !Number.isFinite(rows)
|
|
85
|
+
|| (cols ?? 0) < MIN_VIEWPORT_COLS || (rows ?? 0) < MIN_VIEWPORT_ROWS;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function startApp({
|
|
89
|
+
stdin = process.stdin,
|
|
90
|
+
stdout = process.stdout,
|
|
91
|
+
stderr = process.stderr,
|
|
92
|
+
env = process.env,
|
|
93
|
+
platform = process.platform,
|
|
94
|
+
argv = process.argv.slice(2),
|
|
95
|
+
renderApp = render,
|
|
96
|
+
resolveInkInstanceForStdout = resolveInkRenderInstance,
|
|
97
|
+
registerExitHandler = (handler) => {
|
|
98
|
+
process.on("exit", handler);
|
|
99
|
+
},
|
|
100
|
+
}: Partial<StartAppDependencies> = {}): StartAppResult {
|
|
101
|
+
renderDebug.configureRenderDebug(env);
|
|
102
|
+
|
|
103
|
+
if (activeRoot) {
|
|
104
|
+
return { started: true, exitCode: 0 };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Wrap stdout to implement frame locking, deduplication, and width-safe row
|
|
108
|
+
// padding via \x1b[K injection across the entire TUI.
|
|
109
|
+
const wrappedStdout = wrapStdoutWithFrameLock({ stdout, env });
|
|
110
|
+
|
|
111
|
+
const terminal = createTerminalModeController((chunk) => wrappedStdout.write(chunk));
|
|
112
|
+
const writeStdout = (chunk: string, source: string): boolean => terminal.write(chunk, source);
|
|
113
|
+
|
|
114
|
+
const writeStderr = (chunk: string, source: string): boolean => {
|
|
115
|
+
return writeTerminalControl((value) => stderr.write(value), "stderr", source, chunk);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
debugLaunch(env, writeStderr, {
|
|
119
|
+
phase: "startApp",
|
|
120
|
+
resolvedLaunchMode: "interactive-ui",
|
|
121
|
+
stdinIsTTY: Boolean(stdin.isTTY),
|
|
122
|
+
stdoutIsTTY: Boolean(stdout.isTTY),
|
|
123
|
+
stderrIsTTY: Boolean(stderr.isTTY),
|
|
124
|
+
TERM: env.TERM,
|
|
125
|
+
WT_SESSION: env.WT_SESSION,
|
|
126
|
+
TERM_PROGRAM: env.TERM_PROGRAM,
|
|
127
|
+
argv,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const capability = getTerminalCapability({
|
|
131
|
+
stdinIsTTY: Boolean(stdin.isTTY),
|
|
132
|
+
stdoutIsTTY: Boolean(stdout.isTTY),
|
|
133
|
+
platform,
|
|
134
|
+
env,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!capability.supported) {
|
|
138
|
+
writeStderr(`${capability.message}\n`, "src/index.tsx:unsupportedTerminal");
|
|
139
|
+
return { started: false, exitCode: 1 };
|
|
140
|
+
}
|
|
141
|
+
if (capability.warning) {
|
|
142
|
+
writeStderr(`${capability.warning}\n`, "src/index.tsx:terminalCapabilityWarning");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const parsedLaunchArgs = parseLaunchArgs(argv);
|
|
146
|
+
if (!parsedLaunchArgs.ok) {
|
|
147
|
+
writeStderr(`${parsedLaunchArgs.error}\n`, "src/index.tsx:launchArgs");
|
|
148
|
+
return { started: false, exitCode: 1 };
|
|
149
|
+
}
|
|
150
|
+
const launchArgs: LaunchArgs = parsedLaunchArgs.value;
|
|
151
|
+
// Main chat starts in the normal screen buffer so the startup frame and later
|
|
152
|
+
// transcript rows are owned by native terminal scrollback. Overlay screens
|
|
153
|
+
// enter alternate-screen mode from app.tsx only while an overlay is active.
|
|
154
|
+
const startupWorkspaceRoot = resolveWorkspaceRoot();
|
|
155
|
+
const startupSettings = loadSettings();
|
|
156
|
+
const startupTitle =
|
|
157
|
+
formatTerminalTitlePath(startupWorkspaceRoot, startupSettings.ui.terminalTitleMode) || APP_NAME;
|
|
158
|
+
setIntendedTerminalTitle(startupTitle, {
|
|
159
|
+
force: true,
|
|
160
|
+
reason: "startup-title",
|
|
161
|
+
write: (chunk) => writeStdout(chunk, "src/index.tsx:startup.title"),
|
|
162
|
+
});
|
|
163
|
+
terminal.setMouseReporting(false, "src/index.tsx:startup.nativeScroll");
|
|
164
|
+
terminal.setBracketedPaste(true, "src/index.tsx:startup.bracketedPaste");
|
|
165
|
+
|
|
166
|
+
let cleanupDone = false;
|
|
167
|
+
let repaintArmed = false;
|
|
168
|
+
let renderHandle: RenderHandle | null = null;
|
|
169
|
+
let inkInstance: InkRenderInstance | null = null;
|
|
170
|
+
let previousResizeCols = stdout.columns;
|
|
171
|
+
let previousResizeRows = stdout.rows;
|
|
172
|
+
|
|
173
|
+
const onResize = () => {
|
|
174
|
+
resetFrameLockForResize(wrappedStdout);
|
|
175
|
+
renderDebug.traceEvent("terminal", "resize", {
|
|
176
|
+
previousCols: previousResizeCols,
|
|
177
|
+
previousRows: previousResizeRows,
|
|
178
|
+
cols: stdout.columns,
|
|
179
|
+
rows: stdout.rows,
|
|
180
|
+
invalid: hasInvalidRestoreDimensions(stdout),
|
|
181
|
+
});
|
|
182
|
+
previousResizeCols = stdout.columns;
|
|
183
|
+
previousResizeRows = stdout.rows;
|
|
184
|
+
|
|
185
|
+
if (hasInvalidRestoreDimensions(stdout)) {
|
|
186
|
+
// Transient invalid dimensions (e.g. during maximize/restore on
|
|
187
|
+
// Windows). Keep the previous frame visible and let useTerminalViewport
|
|
188
|
+
// preserve the last renderable layout until valid dimensions return.
|
|
189
|
+
repaintArmed = true;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (repaintArmed) {
|
|
194
|
+
repaintArmed = false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// useTerminalViewport owns resize-driven React state. Do not clear the
|
|
198
|
+
// terminal, reset Ink output caches, or force Ink renders here; those
|
|
199
|
+
// imperative paths can produce a transient empty frame during streaming.
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const cleanup = () => {
|
|
203
|
+
if (cleanupDone) return;
|
|
204
|
+
cleanupDone = true;
|
|
205
|
+
stdout.off("resize", onResize);
|
|
206
|
+
renderHandle?.cleanup();
|
|
207
|
+
// Restore terminal state: disable mouse reporting and bracketed paste.
|
|
208
|
+
terminal.setMouseReporting(false, "src/index.tsx:cleanup.mouse");
|
|
209
|
+
terminal.setBracketedPaste(false, "src/index.tsx:cleanup.bracketedPaste");
|
|
210
|
+
terminal.setAlternateScreen(false, "src/index.tsx:cleanup.alternateScreen");
|
|
211
|
+
terminal.resetModes();
|
|
212
|
+
activeRoot = null;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const handleFatal = (error: unknown) => {
|
|
216
|
+
cleanup();
|
|
217
|
+
if (error instanceof Error) {
|
|
218
|
+
writeStderr(`${error.stack || error.message}\n`, "src/index.tsx:fatalError");
|
|
219
|
+
} else if (error) {
|
|
220
|
+
writeStderr(`${String(error)}\n`, "src/index.tsx:fatalUnknown");
|
|
221
|
+
}
|
|
222
|
+
process.exit(1);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const handleSignal = () => {
|
|
226
|
+
cleanup();
|
|
227
|
+
process.exit(0);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
stdout.on("resize", onResize);
|
|
231
|
+
registerExitHandler(cleanup);
|
|
232
|
+
|
|
233
|
+
// Ensure clean teardown on signals and unhandled failures to prevent
|
|
234
|
+
// leaving the terminal in mouse-reporting mode.
|
|
235
|
+
process.on("SIGINT", handleSignal);
|
|
236
|
+
process.on("SIGTERM", handleSignal);
|
|
237
|
+
process.on("uncaughtException", handleFatal);
|
|
238
|
+
process.on("unhandledRejection", handleFatal);
|
|
239
|
+
|
|
240
|
+
const kittyKeyboard = resolveKittyKeyboardOptions(env);
|
|
241
|
+
renderHandle = renderApp(<App launchArgs={launchArgs} />, {
|
|
242
|
+
...(kittyKeyboard ? { kittyKeyboard } : {}),
|
|
243
|
+
stdout: wrappedStdout as any,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Resolve the real Ink class instance to get access to lastOutput,
|
|
247
|
+
// onRender, calculateLayout, etc. Gracefully degrades to null in tests.
|
|
248
|
+
inkInstance = resolveInkInstanceForStdout(wrappedStdout);
|
|
249
|
+
|
|
250
|
+
// Remove Ink's own resize handler so the app is the sole resize handler.
|
|
251
|
+
// This eliminates the race where Ink's resized() fires alongside our
|
|
252
|
+
// onResize, causing interleaved renders that desync logUpdate.
|
|
253
|
+
if (inkInstance?.unsubscribeResize) {
|
|
254
|
+
inkInstance.unsubscribeResize();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
void renderHandle.waitUntilExit().finally(cleanup);
|
|
258
|
+
|
|
259
|
+
activeRoot = { cleanup };
|
|
260
|
+
return { started: true, exitCode: 0 };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const isMainModule = Boolean((import.meta as ImportMeta & { main?: boolean }).main);
|
|
264
|
+
|
|
265
|
+
if (isMainModule) {
|
|
266
|
+
const result = startApp();
|
|
267
|
+
if (!result.started) {
|
|
268
|
+
process.exitCode = result.exitCode;
|
|
269
|
+
}
|
|
270
|
+
}
|