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/bin/ubume.js
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import { appendFileSync, mkdirSync, readFileSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
process.title = "UBUME";
|
|
10
|
+
|
|
11
|
+
// Pre-rename CODEXA_* variables keep working: copy each into its unset UBUME_* name.
|
|
12
|
+
// The Codexa model family keeps its CODEXA_NATIVE_/CUPY_/NUMPY_ names.
|
|
13
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
14
|
+
if (!key.startsWith("CODEXA_") || /^CODEXA_(NATIVE|CUPY|NUMPY)_/.test(key)) continue;
|
|
15
|
+
const ubumeKey = `UBUME_${key.slice("CODEXA_".length)}`;
|
|
16
|
+
if (process.env[ubumeKey] === undefined) process.env[ubumeKey] = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveLauncherDebugDir() {
|
|
20
|
+
const dataDir = process.env.UBUME_DATA_DIR?.trim()
|
|
21
|
+
|| process.env.CODEXA_DATA_DIR?.trim()
|
|
22
|
+
|| (process.platform === "win32"
|
|
23
|
+
? join(process.env.LOCALAPPDATA?.trim() || process.env.APPDATA?.trim() || join(homedir(), "AppData", "Local"), "Ubume")
|
|
24
|
+
: process.platform === "darwin"
|
|
25
|
+
? join(homedir(), "Library", "Application Support", "Ubume")
|
|
26
|
+
: join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "ubume"));
|
|
27
|
+
return join(dataDir, "debug");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
31
|
+
const packageRoot = dirname(dirname(currentFile));
|
|
32
|
+
const forwardArgs = process.argv.slice(2);
|
|
33
|
+
const workspaceRoot = process.cwd();
|
|
34
|
+
const launcherStartTimeMs = Number(process.env.UBUME_EXEC_TIMING_EPOCH_MS || process.env.CODEXA_EXEC_TIMING_EPOCH_MS) || Date.now();
|
|
35
|
+
let launcherPreviousElapsedMs = 0;
|
|
36
|
+
let intendedTerminalTitle = "Ubume";
|
|
37
|
+
|
|
38
|
+
function writeRenderDebugRecord(kind, fields) {
|
|
39
|
+
if (
|
|
40
|
+
process.env.UBUME_RENDER_DEBUG !== "1"
|
|
41
|
+
&& process.env.UBUME_DEBUG_RENDER !== "1"
|
|
42
|
+
&& process.env.CODEXA_RENDER_DEBUG !== "1"
|
|
43
|
+
&& process.env.CODEXA_DEBUG_RENDER !== "1"
|
|
44
|
+
) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const debugDir = resolveLauncherDebugDir();
|
|
50
|
+
mkdirSync(debugDir, { recursive: true });
|
|
51
|
+
appendFileSync(
|
|
52
|
+
process.env.UBUME_RENDER_DEBUG_FILE?.trim()
|
|
53
|
+
|| process.env.CODEXA_RENDER_DEBUG_FILE?.trim()
|
|
54
|
+
|| join(debugDir, "render-status.log"),
|
|
55
|
+
JSON.stringify({
|
|
56
|
+
ts: Date.now(),
|
|
57
|
+
pid: process.pid,
|
|
58
|
+
sessionId: `launcher-${Date.now()}-${process.pid}`,
|
|
59
|
+
kind,
|
|
60
|
+
...fields,
|
|
61
|
+
}) + "\n",
|
|
62
|
+
"utf8",
|
|
63
|
+
);
|
|
64
|
+
} catch {
|
|
65
|
+
// Debug logging must never disturb launcher startup.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function debugLaunch(message, fields = {}) {
|
|
70
|
+
if (process.env.UBUME_DEBUG_LAUNCH !== "1" && process.env.CODEXA_DEBUG_LAUNCH !== "1") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const serializedFields = Object.entries(fields)
|
|
75
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
76
|
+
.join(" ");
|
|
77
|
+
process.stderr.write(`[ubume:launch] ${message}${serializedFields ? ` ${serializedFields}` : ""}\n`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeTerminalTitleDebugRecord(fields) {
|
|
81
|
+
if (process.env.UBUME_DEBUG_TERMINAL_TITLE !== "1" && process.env.CODEXA_DEBUG_TERMINAL_TITLE !== "1") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const debugDir = resolveLauncherDebugDir();
|
|
87
|
+
mkdirSync(debugDir, { recursive: true });
|
|
88
|
+
appendFileSync(
|
|
89
|
+
process.env.UBUME_TERMINAL_TITLE_DEBUG_FILE?.trim()
|
|
90
|
+
|| process.env.CODEXA_TERMINAL_TITLE_DEBUG_FILE?.trim()
|
|
91
|
+
|| process.env.UBUME_RENDER_DEBUG_FILE?.trim()
|
|
92
|
+
|| process.env.CODEXA_RENDER_DEBUG_FILE?.trim()
|
|
93
|
+
|| join(debugDir, "render-status.log"),
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
ts: Date.now(),
|
|
96
|
+
pid: process.pid,
|
|
97
|
+
lifecycleState: "launcher",
|
|
98
|
+
...fields,
|
|
99
|
+
}) + "\n",
|
|
100
|
+
"utf8",
|
|
101
|
+
);
|
|
102
|
+
} catch {
|
|
103
|
+
// Debug logging must never disturb launcher startup.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function sanitizeTerminalTitle(title) {
|
|
108
|
+
return String(title ?? "")
|
|
109
|
+
.replace(/[\u0000-\u001f\u007f]/g, " ")
|
|
110
|
+
.replace(/\x1b/g, "")
|
|
111
|
+
.trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function normalizeTerminalTitle(title) {
|
|
115
|
+
const cleanTitle = sanitizeTerminalTitle(title);
|
|
116
|
+
if (!cleanTitle || /^[a-zA-Z]:[\\/]/.test(cleanTitle) || /^\\\\/.test(cleanTitle)) {
|
|
117
|
+
return "Ubume";
|
|
118
|
+
}
|
|
119
|
+
return cleanTitle;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function recordIntendedTitle(reason, force = true) {
|
|
123
|
+
writeTerminalTitleDebugRecord({
|
|
124
|
+
event: "ubumeTitleSkipped",
|
|
125
|
+
source: `bin/ubume.js:${reason}`,
|
|
126
|
+
title: intendedTerminalTitle,
|
|
127
|
+
reason,
|
|
128
|
+
force,
|
|
129
|
+
});
|
|
130
|
+
writeRenderDebugRecord("title", {
|
|
131
|
+
event: "launcherTitleSkipped",
|
|
132
|
+
source: `bin/ubume.js:${reason}`,
|
|
133
|
+
title: intendedTerminalTitle,
|
|
134
|
+
reason,
|
|
135
|
+
force,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function hasFlag(args, longFlag, shortFlag) {
|
|
140
|
+
for (const arg of args) {
|
|
141
|
+
if (arg === "--") return false;
|
|
142
|
+
if (arg === longFlag || arg === shortFlag) return true;
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readPackageVersion() {
|
|
148
|
+
try {
|
|
149
|
+
const raw = readFileSync(join(packageRoot, "package.json"), "utf8");
|
|
150
|
+
const parsed = JSON.parse(raw);
|
|
151
|
+
return typeof parsed.version === "string" && parsed.version.trim()
|
|
152
|
+
? parsed.version.trim()
|
|
153
|
+
: null;
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function printHelp() {
|
|
160
|
+
const version = readPackageVersion();
|
|
161
|
+
const versionLine = version ? `ubume ${version}` : "ubume";
|
|
162
|
+
console.log(`${versionLine}
|
|
163
|
+
|
|
164
|
+
Usage:
|
|
165
|
+
ubume
|
|
166
|
+
ubume "explain this repo"
|
|
167
|
+
ubume exec "print the current directory"
|
|
168
|
+
ubume --headless-benchmark "print the current directory"
|
|
169
|
+
ubume [options] [prompt]
|
|
170
|
+
|
|
171
|
+
Options:
|
|
172
|
+
-h, --help Show this help text and exit.
|
|
173
|
+
-v, --version Show the installed Ubume version and exit.
|
|
174
|
+
--headless-benchmark
|
|
175
|
+
Run Ubume through the headless benchmark path.
|
|
176
|
+
--profile <name> Load a profile from config.
|
|
177
|
+
-m, --model <name> Select the model for this launch.
|
|
178
|
+
--reasoning <effort>
|
|
179
|
+
Reasoning effort for ubume exec: none, minimal, low, medium, high, xhigh.
|
|
180
|
+
-c, --config <key=val> Override a runtime config value.
|
|
181
|
+
|
|
182
|
+
Inside Ubume:
|
|
183
|
+
/help Show interactive commands.
|
|
184
|
+
/model Open model selection.
|
|
185
|
+
/mode Open mode selection.
|
|
186
|
+
/exit Exit the interactive UI.
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const isHeadlessExec = forwardArgs[0] === "exec";
|
|
191
|
+
const isHeadlessBenchmark = forwardArgs[0] === "--headless-benchmark";
|
|
192
|
+
const isHeadlessMode = isHeadlessExec || isHeadlessBenchmark;
|
|
193
|
+
const execTimingEnabled = isHeadlessMode
|
|
194
|
+
&& (
|
|
195
|
+
process.env.UBUME_EXEC_TIMING === "1"
|
|
196
|
+
|| process.env.CODEXA_EXEC_TIMING === "1"
|
|
197
|
+
|| forwardArgs.includes("--timing")
|
|
198
|
+
|| forwardArgs.includes("--benchmark-diagnostics")
|
|
199
|
+
);
|
|
200
|
+
const parentStdinIsTTY = Boolean(process.stdin.isTTY);
|
|
201
|
+
const parentStdoutIsTTY = Boolean(process.stdout.isTTY);
|
|
202
|
+
const parentStderrIsTTY = Boolean(process.stderr.isTTY);
|
|
203
|
+
const parentHasTTY = parentStdinIsTTY && parentStdoutIsTTY;
|
|
204
|
+
|
|
205
|
+
function markExecTiming(phase, fields = {}) {
|
|
206
|
+
if (!execTimingEnabled) return;
|
|
207
|
+
const elapsedMs = Date.now() - launcherStartTimeMs;
|
|
208
|
+
const deltaMs = elapsedMs - launcherPreviousElapsedMs;
|
|
209
|
+
launcherPreviousElapsedMs = elapsedMs;
|
|
210
|
+
const formattedFields = Object.entries(fields)
|
|
211
|
+
.map(([key, value]) => {
|
|
212
|
+
const serialized = Array.isArray(value) || typeof value === "string"
|
|
213
|
+
? JSON.stringify(value)
|
|
214
|
+
: String(value);
|
|
215
|
+
return `${key}=${serialized}`;
|
|
216
|
+
})
|
|
217
|
+
.join(" ");
|
|
218
|
+
process.stderr.write(`[ubume exec timing] phase=${phase} elapsed_ms=${elapsedMs} delta_ms=${deltaMs}${formattedFields ? ` ${formattedFields}` : ""}\n`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
markExecTiming("launcher_start", { pid: process.pid });
|
|
222
|
+
|
|
223
|
+
if (!isHeadlessMode && parentHasTTY) {
|
|
224
|
+
intendedTerminalTitle = normalizeTerminalTitle(
|
|
225
|
+
process.env.UBUME_INITIAL_TERMINAL_TITLE
|
|
226
|
+
|| process.env.CODEXA_INITIAL_TERMINAL_TITLE
|
|
227
|
+
|| "Ubume",
|
|
228
|
+
);
|
|
229
|
+
recordIntendedTitle("launcher-startup-title-disabled");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!isHeadlessMode && hasFlag(forwardArgs, "--help", "-h")) {
|
|
233
|
+
printHelp();
|
|
234
|
+
process.exit(0);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!isHeadlessMode && hasFlag(forwardArgs, "--version", "-v")) {
|
|
238
|
+
console.log(readPackageVersion() ?? "unknown");
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Filters out terminal mouse reporting escape sequences from stdin data.
|
|
244
|
+
* Prevents SGR mouse clicks and scroll events from leaking into the TUI app.
|
|
245
|
+
*/
|
|
246
|
+
function createMouseFilter() {
|
|
247
|
+
let buffer = "";
|
|
248
|
+
let timer = null;
|
|
249
|
+
|
|
250
|
+
return (data) => {
|
|
251
|
+
buffer += data;
|
|
252
|
+
|
|
253
|
+
// Clear existing timer
|
|
254
|
+
if (timer) clearTimeout(timer);
|
|
255
|
+
|
|
256
|
+
// Check for complete SGR mouse sequences: ESC [ < button ; x ; y M/m
|
|
257
|
+
// Also handle scroll events: button 64/96 (scroll up), 65/97 (scroll down)
|
|
258
|
+
const sgrMouseRegex = /\x1b\[<[0-9]+;[0-9]+;[0-9]+[Mm]/g;
|
|
259
|
+
let hasFullSequence = sgrMouseRegex.test(buffer);
|
|
260
|
+
|
|
261
|
+
// If we have a complete sequence, filter and emit immediately
|
|
262
|
+
if (hasFullSequence) {
|
|
263
|
+
buffer = buffer.replace(sgrMouseRegex, "");
|
|
264
|
+
return buffer;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// If we have partial SGR start, wait for completion or timeout
|
|
268
|
+
if (/\x1b\[<[0-9]*;?[0-9]*;?[0-9]*$/.test(buffer)) {
|
|
269
|
+
timer = setTimeout(() => {
|
|
270
|
+
// Timeout: assume incomplete mouse sequence, just emit filtered buffer
|
|
271
|
+
timer = null;
|
|
272
|
+
}, 50);
|
|
273
|
+
return null; // Wait for more data
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Otherwise, emit immediately (normal input)
|
|
277
|
+
return buffer;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const bunExecutable = process.env.UBUME_BUN_EXECUTABLE?.trim()
|
|
282
|
+
|| process.env.CODEXA_BUN_EXECUTABLE?.trim()
|
|
283
|
+
|| (process.platform === "win32" ? "bun.exe" : "bun");
|
|
284
|
+
|
|
285
|
+
const appEntry = join(packageRoot, "src", "index.tsx");
|
|
286
|
+
const execEntry = join(packageRoot, "src", "exec.ts");
|
|
287
|
+
const bunEntry = isHeadlessMode ? execEntry : appEntry;
|
|
288
|
+
const bunForwardArgs = isHeadlessMode ? forwardArgs.slice(1) : forwardArgs;
|
|
289
|
+
|
|
290
|
+
// Detect if parent process has a real TTY
|
|
291
|
+
const childStdio = isHeadlessMode
|
|
292
|
+
? ["ignore", "inherit", "inherit"]
|
|
293
|
+
: parentHasTTY
|
|
294
|
+
? ["inherit", "inherit", "inherit"]
|
|
295
|
+
: ["pipe", "pipe", "pipe"];
|
|
296
|
+
|
|
297
|
+
debugLaunch("resolved launch mode", {
|
|
298
|
+
mode: isHeadlessMode ? "headless" : "interactive-ui",
|
|
299
|
+
stdinIsTTY: parentStdinIsTTY,
|
|
300
|
+
stdoutIsTTY: parentStdoutIsTTY,
|
|
301
|
+
stderrIsTTY: parentStderrIsTTY,
|
|
302
|
+
TERM: process.env.TERM,
|
|
303
|
+
WT_SESSION: process.env.WT_SESSION,
|
|
304
|
+
TERM_PROGRAM: process.env.TERM_PROGRAM,
|
|
305
|
+
argv: process.argv,
|
|
306
|
+
childStdio,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
markExecTiming("bun_spawn_start", { executable: bunExecutable });
|
|
310
|
+
if (!isHeadlessMode && parentHasTTY) {
|
|
311
|
+
recordIntendedTitle("before-bun-spawn-disabled");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const child = spawn(
|
|
315
|
+
bunExecutable,
|
|
316
|
+
["run", "--silent", bunEntry, ...(bunForwardArgs.length > 0 ? ["--", ...bunForwardArgs] : [])],
|
|
317
|
+
{
|
|
318
|
+
cwd: workspaceRoot,
|
|
319
|
+
stdio: childStdio,
|
|
320
|
+
env: {
|
|
321
|
+
...process.env,
|
|
322
|
+
CODEX_WORKSPACE_ROOT: workspaceRoot,
|
|
323
|
+
UBUME_LAUNCH_KIND: "installed-bin",
|
|
324
|
+
UBUME_PACKAGE_ROOT: packageRoot,
|
|
325
|
+
UBUME_LAUNCHER_SCRIPT: currentFile,
|
|
326
|
+
UBUME_RELAUNCH_EXECUTABLE: process.execPath,
|
|
327
|
+
UBUME_RELAUNCH_ARGS: JSON.stringify([currentFile, ...forwardArgs]),
|
|
328
|
+
UBUME_PARENT_HAS_TTY: parentHasTTY ? "1" : "0",
|
|
329
|
+
UBUME_HEADLESS_BENCHMARK: isHeadlessBenchmark ? "1" : "0",
|
|
330
|
+
UBUME_EXEC_TIMING_EPOCH_MS: String(launcherStartTimeMs),
|
|
331
|
+
UBUME_INITIAL_TERMINAL_TITLE: intendedTerminalTitle,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
child.once("spawn", () => {
|
|
337
|
+
if (!isHeadlessMode && parentHasTTY) {
|
|
338
|
+
recordIntendedTitle("after-bun-spawn-disabled");
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
if (!isHeadlessMode && !parentHasTTY) {
|
|
343
|
+
const mouseFilter = createMouseFilter();
|
|
344
|
+
|
|
345
|
+
process.stdin.on("data", (data) => {
|
|
346
|
+
const filtered = mouseFilter(data);
|
|
347
|
+
if (filtered) {
|
|
348
|
+
child.stdin.write(filtered);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
process.stdin.on("end", () => {
|
|
353
|
+
child.stdin.end();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
process.stdin.on("error", (error) => {
|
|
357
|
+
console.error(`stdin error: ${error.message}`);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
child.stdin.on("error", (error) => {
|
|
361
|
+
// Ignore broken pipe errors
|
|
362
|
+
if (error.code !== "EPIPE") {
|
|
363
|
+
console.error(`child stdin error: ${error.message}`);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!isHeadlessMode) {
|
|
369
|
+
child.stdout?.on("data", (chunk) => {
|
|
370
|
+
process.stdout.write(chunk);
|
|
371
|
+
});
|
|
372
|
+
child.stderr?.on("data", (chunk) => {
|
|
373
|
+
process.stderr.write(chunk);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
child.on("error", (error) => {
|
|
378
|
+
if (!isHeadlessMode && parentHasTTY) {
|
|
379
|
+
recordIntendedTitle("bun-spawn-error-disabled");
|
|
380
|
+
}
|
|
381
|
+
markExecTiming("bun_spawn_error", { message: error.message });
|
|
382
|
+
console.error(`Failed to launch Bun: ${error.message}`);
|
|
383
|
+
console.error("Bun is required to launch ubume. Install Bun, then run this command again.");
|
|
384
|
+
process.exit(1);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
child.on("close", (code, signal) => {
|
|
388
|
+
if (!isHeadlessMode && parentHasTTY) {
|
|
389
|
+
recordIntendedTitle("bun-close-disabled");
|
|
390
|
+
}
|
|
391
|
+
markExecTiming("launcher_exit", { exit_code: code ?? 0, signal: signal ?? null });
|
|
392
|
+
if (signal) {
|
|
393
|
+
process.kill(process.pid, signal);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
process.exit(code ?? 0);
|
|
398
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ubume",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Terminal UI for Ubume — TypeScript + Bun + Ink",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ubume": "bin/ubume.js",
|
|
8
|
+
"codexa": "bin/codexa.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "bun run --silent src/index.tsx",
|
|
12
|
+
"dev": "bun --watch --silent src/index.tsx",
|
|
13
|
+
"dev:run": "node scripts/run-local-dev.mjs",
|
|
14
|
+
"install:dev-bin": "node scripts/install-local-dev-bin.mjs",
|
|
15
|
+
"debug:claude-models": "bun run --silent src/core/providerRuntime/claudeCodeDiscoveryDebug.ts",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "bun test --max-concurrency=1",
|
|
18
|
+
"audit:ubume-gap": "node scripts/audit-ubume-capabilities.mjs",
|
|
19
|
+
"audit:codexa-gap": "node scripts/audit-ubume-capabilities.mjs",
|
|
20
|
+
"smoke:terminal-bench": "node scripts/smoke-terminal-bench.mjs",
|
|
21
|
+
"gen-build-info": "node scripts/gen-build-info.mjs",
|
|
22
|
+
"build": "bun run gen-build-info && bun run typecheck",
|
|
23
|
+
"prepublishOnly": "npm run gen-build-info && npm run typecheck && bun test --max-concurrency=1"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@deepseek-ai/dsh": "0.1.1-rc.2",
|
|
27
|
+
"@deepseek-ai/dsh-attachment-local": "0.1.1-rc.2",
|
|
28
|
+
"@deepseek-ai/dsh-llm": "0.1.1-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-scope": "0.1.1-rc.2",
|
|
30
|
+
"@deepseek-ai/dsh-sdk-protocol": "0.1.1-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.2",
|
|
32
|
+
"ink": "^7.1.0",
|
|
33
|
+
"ink-select-input": "^6.2.0",
|
|
34
|
+
"react": "^19.2.7",
|
|
35
|
+
"react-dom": "^19.2.7"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^26.1.1",
|
|
39
|
+
"@types/react": "^19.2.17",
|
|
40
|
+
"typescript": "^7.0.2"
|
|
41
|
+
},
|
|
42
|
+
"overrides": {
|
|
43
|
+
"ws": "^8.21.0"
|
|
44
|
+
},
|
|
45
|
+
"trustedDependencies": [
|
|
46
|
+
"@deepseek-ai/dsh-subprocess-local"
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/golba98/ubume-cli.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/golba98/ubume-cli/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/golba98/ubume-cli#readme",
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"registry": "https://registry.npmjs.org"
|
|
58
|
+
},
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"files": [
|
|
61
|
+
"bin/",
|
|
62
|
+
"src/",
|
|
63
|
+
"!src/**/*.test.ts",
|
|
64
|
+
"!src/**/*.test.tsx"
|
|
65
|
+
]
|
|
66
|
+
}
|