@golba98/codexa 1.0.1 → 1.0.3
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 +391 -94
- package/package.json +14 -8
- package/src/app.tsx +196 -63
- package/src/commands/handler.ts +9 -4
- package/src/config/buildInfo.ts +3 -0
- package/src/config/launchArgs.ts +10 -0
- package/src/config/layeredConfig.ts +0 -0
- package/src/config/persistence.ts +51 -0
- package/src/config/runtimeConfig.ts +4 -0
- package/src/config/settings.ts +6 -13
- package/src/config/toml-serialize.ts +0 -0
- package/src/config/trustStore.ts +0 -0
- package/src/config/updateCheckCache.ts +63 -0
- package/src/core/attachments.ts +0 -0
- package/src/core/auth/codexAuth.ts +0 -0
- package/src/core/channel.ts +23 -0
- package/src/core/cleanupFastFail.ts +0 -0
- package/src/core/clipboard.ts +0 -0
- package/src/core/codex.ts +0 -0
- package/src/core/codexExecArgs.ts +0 -0
- package/src/core/codexLaunch.ts +0 -0
- package/src/core/codexPrompt.ts +0 -0
- package/src/core/executables/claudeExecutable.ts +0 -0
- package/src/core/executables/codexExecutable.ts +1 -0
- package/src/core/executables/executableResolver.ts +61 -43
- package/src/core/executables/geminiExecutable.ts +0 -0
- package/src/core/githubDiagnostics.ts +0 -0
- package/src/core/hollowResponseFormat.ts +0 -0
- package/src/core/inputDebug.ts +0 -0
- package/src/core/launchContext.ts +33 -33
- package/src/core/models/codexCapabilities.ts +0 -0
- package/src/core/models/codexModelCapabilities.ts +0 -0
- package/src/core/models/modelSpecs.ts +0 -0
- package/src/core/perf/profiler.ts +0 -0
- package/src/core/perf/renderDebug.ts +0 -0
- package/src/core/planStorage.ts +0 -0
- package/src/core/process/CommandRunner.ts +0 -0
- package/src/core/process/processValidation.ts +9 -5
- package/src/core/projectInstructions.ts +0 -0
- package/src/core/providerLauncher/launcher.ts +59 -42
- package/src/core/providerLauncher/registry.ts +0 -0
- package/src/core/providerLauncher/types.ts +0 -0
- package/src/core/providerLauncher/workspaceConfig.ts +0 -0
- package/src/core/providerRuntime/anthropic.ts +0 -0
- package/src/core/providerRuntime/capabilityProfile.ts +0 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +0 -0
- package/src/core/providerRuntime/contextMetadata.ts +0 -0
- package/src/core/providerRuntime/gemini.ts +0 -0
- package/src/core/providerRuntime/lmstudio.ts +0 -0
- package/src/core/providerRuntime/local.ts +0 -0
- package/src/core/providerRuntime/models.ts +0 -0
- package/src/core/providerRuntime/reasoning.ts +0 -0
- package/src/core/providerRuntime/registry.ts +0 -0
- package/src/core/providerRuntime/types.ts +0 -0
- package/src/core/providers/codexJsonStream.ts +0 -0
- package/src/core/providers/codexSubprocess.ts +0 -0
- package/src/core/providers/codexTranscript.ts +0 -0
- package/src/core/providers/openaiNative.ts +0 -0
- package/src/core/providers/registry.ts +0 -0
- package/src/core/providers/types.ts +0 -0
- package/src/core/terminal/startupClear.ts +20 -0
- package/src/core/terminal/terminalCapabilities.ts +0 -0
- package/src/core/terminal/terminalControl.ts +0 -0
- package/src/core/terminal/terminalSanitize.ts +0 -0
- package/src/core/terminal/terminalSelection.ts +0 -0
- package/src/core/terminal/terminalTitle.ts +0 -0
- package/src/core/updateCheck.ts +188 -0
- package/src/core/workspaceActivity.ts +0 -0
- package/src/core/workspaceGuard.ts +0 -0
- package/src/core/workspaceRoot.ts +0 -0
- package/src/exec.ts +0 -0
- package/src/headless/execArgs.ts +1 -0
- package/src/headless/execRunner.ts +0 -0
- package/src/index.tsx +13 -9
- package/src/session/appSession.ts +0 -0
- package/src/session/chatLifecycle.ts +0 -0
- package/src/session/liveRenderScheduler.ts +0 -0
- package/src/session/planFlow.ts +0 -0
- package/src/session/planTranscript.ts +0 -0
- package/src/session/promptRunSchedule.ts +0 -0
- package/src/session/types.ts +2 -1
- package/src/test/runtimeTestUtils.ts +0 -0
- package/src/types/react-dom.d.ts +0 -0
- package/src/ui/ActionRequiredBlock.tsx +5 -5
- package/src/ui/ActivityBars.tsx +3 -3
- package/src/ui/ActivityIndicator.tsx +6 -6
- package/src/ui/AgentBlock.tsx +6 -6
- package/src/ui/AnimatedStatusText.tsx +1 -1
- package/src/ui/AppShell.tsx +13 -5
- package/src/ui/AttachmentImportPanel.tsx +8 -8
- package/src/ui/AuthPanel.tsx +20 -20
- package/src/ui/BackendPicker.tsx +0 -0
- package/src/ui/BottomComposer.tsx +117 -78
- package/src/ui/DashCard.tsx +3 -3
- package/src/ui/Markdown.tsx +17 -17
- package/src/ui/ModePicker.tsx +0 -0
- package/src/ui/ModelPicker.tsx +0 -0
- package/src/ui/ModelPickerScreen.tsx +10 -10
- package/src/ui/ModelReasoningPicker.tsx +14 -14
- package/src/ui/Panel.tsx +3 -3
- package/src/ui/PermissionsPanel.tsx +0 -0
- package/src/ui/PlanActionPicker.tsx +6 -6
- package/src/ui/PlanReviewPanel.tsx +8 -8
- package/src/ui/ProviderPicker.tsx +18 -18
- package/src/ui/ReasoningPicker.tsx +0 -0
- package/src/ui/RunFooter.tsx +3 -3
- package/src/ui/SelectionPanel.tsx +4 -4
- package/src/ui/SettingsPanel.tsx +8 -8
- package/src/ui/Spinner.tsx +1 -1
- package/src/ui/StaticIntroItem.tsx +0 -0
- package/src/ui/StaticTranscriptItem.tsx +0 -0
- package/src/ui/TextEntryPanel.tsx +10 -10
- package/src/ui/ThemePicker.tsx +0 -0
- package/src/ui/ThinkingBlock.tsx +8 -8
- package/src/ui/Timeline.tsx +21 -17
- package/src/ui/TopHeader.tsx +421 -257
- package/src/ui/TurnGroup.tsx +31 -31
- package/src/ui/UpdateAvailableCard.tsx +41 -0
- package/src/ui/UpdatePromptPanel.tsx +203 -0
- package/src/ui/busyStatusAnimation.ts +0 -0
- package/src/ui/commandNormalize.ts +0 -0
- package/src/ui/diffRenderer.ts +0 -0
- package/src/ui/focus.ts +3 -0
- package/src/ui/inputBuffer.ts +0 -0
- package/src/ui/layout.ts +1 -1
- package/src/ui/logoVariants.ts +107 -0
- package/src/ui/modeDisplay.ts +12 -12
- package/src/ui/outputPipeline.ts +0 -0
- package/src/ui/progressEntries.ts +0 -0
- package/src/ui/runActivityView.ts +0 -0
- package/src/ui/runtimeDisplay.ts +100 -0
- package/src/ui/slashCommands.ts +0 -0
- package/src/ui/terminalAnswerFormat.ts +0 -0
- package/src/ui/textLayout.ts +0 -0
- package/src/ui/theme.tsx +274 -395
- package/src/ui/themeFlow.ts +0 -0
- package/src/ui/timelineMeasure.ts +29 -19
- package/src/ui/useThrottledValue.ts +0 -0
- package/scripts/audit-codexa-capabilities.mjs +0 -466
- package/scripts/smoke-terminal-bench.mjs +0 -35
- package/src/appRenderStability.test.ts +0 -131
- package/src/commands/handler.test.ts +0 -643
- package/src/config/launchArgs.test.ts +0 -158
- package/src/config/layeredConfig.test.ts +0 -143
- package/src/config/persistence.test.ts +0 -110
- package/src/config/runtimeConfig.test.ts +0 -218
- package/src/config/settings.test.ts +0 -155
- package/src/config/trustStore.test.ts +0 -29
- package/src/core/attachments.test.ts +0 -155
- package/src/core/auth/codexAuth.test.ts +0 -68
- package/src/core/cleanupFastFail.test.ts +0 -76
- package/src/core/codexExecArgs.test.ts +0 -195
- package/src/core/codexLaunch.test.ts +0 -205
- package/src/core/codexPrompt.test.ts +0 -252
- package/src/core/executables/codexExecutable.test.ts +0 -212
- package/src/core/executables/executableResolver.test.ts +0 -129
- package/src/core/executables/geminiExecutable.test.ts +0 -116
- package/src/core/executables/pathSanityScan.test.ts +0 -47
- package/src/core/githubDiagnostics.test.ts +0 -92
- package/src/core/hollowResponseFormat.test.ts +0 -58
- package/src/core/launchContext.test.ts +0 -157
- package/src/core/models/codexCapabilities.test.ts +0 -45
- package/src/core/models/codexModelCapabilities.test.ts +0 -246
- package/src/core/models/modelSpecs.test.ts +0 -283
- package/src/core/perf/renderDebug.test.ts +0 -230
- package/src/core/planStorage.test.ts +0 -143
- package/src/core/process/CommandRunner.test.ts +0 -105
- package/src/core/projectInstructions.test.ts +0 -50
- package/src/core/providerLauncher/launcher.test.ts +0 -238
- package/src/core/providerLauncher/registry.test.ts +0 -324
- package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
- package/src/core/providerRuntime/anthropic.test.ts +0 -1120
- package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
- package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
- package/src/core/providerRuntime/gemini.test.ts +0 -437
- package/src/core/providerRuntime/lmstudio.test.ts +0 -168
- package/src/core/providerRuntime/local.test.ts +0 -787
- package/src/core/providerRuntime/registry.test.ts +0 -233
- package/src/core/providers/codexJsonStream.test.ts +0 -148
- package/src/core/providers/codexSubprocess.test.ts +0 -68
- package/src/core/providers/codexTranscript.test.ts +0 -284
- package/src/core/terminal/terminalCapabilities.test.ts +0 -93
- package/src/core/terminal/terminalControl.test.ts +0 -75
- package/src/core/terminal/terminalSanitize.test.ts +0 -22
- package/src/core/terminal/terminalSelection.test.ts +0 -42
- package/src/core/terminal/terminalTitle.test.ts +0 -328
- package/src/core/workspaceActivity.test.ts +0 -163
- package/src/core/workspaceGuard.test.ts +0 -151
- package/src/core/workspaceRoot.test.ts +0 -23
- package/src/exec.test.ts +0 -13
- package/src/headless/execArgs.test.ts +0 -147
- package/src/headless/execRunner.test.ts +0 -434
- package/src/index.test.tsx +0 -618
- package/src/session/appSession.test.ts +0 -897
- package/src/session/chatLifecycle.test.ts +0 -64
- package/src/session/liveRenderScheduler.test.ts +0 -201
- package/src/session/planFlow.test.ts +0 -103
- package/src/session/planTranscript.test.ts +0 -65
- package/src/session/promptRunSchedule.test.ts +0 -36
- package/src/ui/ActivityIndicator.test.tsx +0 -58
- package/src/ui/AgentBlock.test.ts +0 -6
- package/src/ui/AnimatedStatusText.test.ts +0 -16
- package/src/ui/AppShell.test.tsx +0 -1739
- package/src/ui/AttachmentImportPanel.test.tsx +0 -204
- package/src/ui/BottomComposer.test.ts +0 -674
- package/src/ui/CodexLogo.tsx +0 -55
- package/src/ui/Markdown.test.ts +0 -157
- package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
- package/src/ui/ModelPickerScreen.test.tsx +0 -99
- package/src/ui/ModelPickerState.test.tsx +0 -151
- package/src/ui/ModelReasoningPicker.test.tsx +0 -447
- package/src/ui/PlanReviewPanel.test.tsx +0 -267
- package/src/ui/PromptCardBorder.test.tsx +0 -161
- package/src/ui/ProviderPicker.test.tsx +0 -289
- package/src/ui/ProviderShortcut.test.tsx +0 -143
- package/src/ui/SettingsPanel.test.tsx +0 -233
- package/src/ui/Timeline.test.ts +0 -2067
- package/src/ui/TimelineNavigation.test.tsx +0 -201
- package/src/ui/TopHeader.test.tsx +0 -239
- package/src/ui/TurnGroup.test.tsx +0 -365
- package/src/ui/busyStatusAnimation.test.ts +0 -30
- package/src/ui/commandNormalize.test.ts +0 -142
- package/src/ui/diffRenderer.test.ts +0 -102
- package/src/ui/focusFlow.test.tsx +0 -1098
- package/src/ui/inputBuffer.test.ts +0 -151
- package/src/ui/layout.test.ts +0 -145
- package/src/ui/modeDisplay.test.ts +0 -42
- package/src/ui/runActivityView.test.ts +0 -89
- package/src/ui/runLifecycleView.test.tsx +0 -237
- package/src/ui/statusRenderIsolation.test.tsx +0 -654
- package/src/ui/terminalAnswerFormat.test.ts +0 -19
- package/src/ui/textLayout.test.ts +0 -18
- package/src/ui/themeFlow.test.ts +0 -53
- package/src/ui/timelineMeasureCache.test.ts +0 -986
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { APP_VERSION } from "../config/settings.js";
|
|
2
|
+
import { isLocalDevChannel } from "./channel.js";
|
|
3
|
+
|
|
4
|
+
export const CODEXA_NPM_PACKAGE = "@golba98/codexa";
|
|
5
|
+
export const CODEXA_NPM_REGISTRY_URL = "https://registry.npmjs.org/@golba98%2Fcodexa";
|
|
6
|
+
export const CODEXA_UPDATE_COMMAND = `npm install -g ${CODEXA_NPM_PACKAGE}@latest`;
|
|
7
|
+
|
|
8
|
+
export type UpdateStatus = "up-to-date" | "update-available" | "unknown" | "error";
|
|
9
|
+
|
|
10
|
+
export interface NpmRegistryMetadata {
|
|
11
|
+
"dist-tags": { latest?: string };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface UpdateCheckResult {
|
|
15
|
+
status: UpdateStatus;
|
|
16
|
+
currentVersion: string;
|
|
17
|
+
latestVersion: string | null;
|
|
18
|
+
errorMessage?: string;
|
|
19
|
+
checkedAt: number;
|
|
20
|
+
source?: "npm" | "cache";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const FETCH_TIMEOUT_MS = 5000;
|
|
24
|
+
|
|
25
|
+
/** Strip a leading "v" so "v1.0.2" and "1.0.2" are treated as equal. */
|
|
26
|
+
export function normalizeVersion(v: string): string {
|
|
27
|
+
return v.startsWith("v") ? v.slice(1) : v;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
31
|
+
|
|
32
|
+
/** Returns true for valid semver strings with or without a leading "v". */
|
|
33
|
+
export function isValidSemver(v: string): boolean {
|
|
34
|
+
return SEMVER_RE.test(normalizeVersion(v));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function shouldRunStartupUpdateCheck(
|
|
38
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
39
|
+
enabled = true,
|
|
40
|
+
): boolean {
|
|
41
|
+
return enabled && !isLocalDevChannel(env);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Compares two semver strings numerically. Returns negative if a < b, 0 if equal, positive if a > b.
|
|
45
|
+
// Pre-release versions (e.g. 1.0.2-beta.1) sort below their release counterpart (1.0.2 > 1.0.2-beta.1).
|
|
46
|
+
// Leading "v" is stripped before comparison.
|
|
47
|
+
export function compareSemver(a: string, b: string): number {
|
|
48
|
+
const parseParts = (v: string): { numeric: number[]; prerelease: string | null } => {
|
|
49
|
+
const norm = normalizeVersion(v);
|
|
50
|
+
const dashIdx = norm.indexOf("-");
|
|
51
|
+
const base = dashIdx === -1 ? norm : norm.slice(0, dashIdx);
|
|
52
|
+
const prerelease = dashIdx === -1 ? null : norm.slice(dashIdx + 1);
|
|
53
|
+
const numeric = base.split(".").map((p) => parseInt(p, 10) || 0);
|
|
54
|
+
return { numeric, prerelease };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const pa = parseParts(a);
|
|
58
|
+
const pb = parseParts(b);
|
|
59
|
+
const len = Math.max(pa.numeric.length, pb.numeric.length);
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < len; i++) {
|
|
62
|
+
const diff = (pa.numeric[i] ?? 0) - (pb.numeric[i] ?? 0);
|
|
63
|
+
if (diff !== 0) return diff;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Same numeric version: no pre-release > has pre-release
|
|
67
|
+
if (pa.prerelease === null && pb.prerelease !== null) return 1;
|
|
68
|
+
if (pa.prerelease !== null && pb.prerelease === null) return -1;
|
|
69
|
+
if (pa.prerelease !== null && pb.prerelease !== null) {
|
|
70
|
+
return pa.prerelease < pb.prerelease ? -1 : pa.prerelease > pb.prerelease ? 1 : 0;
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isNewerVersion(candidate: string, current: string): boolean {
|
|
76
|
+
return compareSemver(candidate, current) > 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface UpdateCheckOverrides {
|
|
80
|
+
currentVersion?: string;
|
|
81
|
+
fetchNpmMetadataFn?: (url: string) => Promise<NpmRegistryMetadata>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function defaultFetchNpmMetadata(url: string): Promise<NpmRegistryMetadata> {
|
|
85
|
+
const controller = new AbortController();
|
|
86
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
87
|
+
try {
|
|
88
|
+
const res = await fetch(url, {
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
headers: { "User-Agent": `${CODEXA_NPM_PACKAGE}-update-checker/1.0` },
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) throw new Error(`npm registry returned HTTP ${res.status}`);
|
|
93
|
+
return await res.json() as NpmRegistryMetadata;
|
|
94
|
+
} finally {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function checkForUpdates(
|
|
100
|
+
opts?: { enabled?: boolean },
|
|
101
|
+
overrides?: UpdateCheckOverrides,
|
|
102
|
+
): Promise<UpdateCheckResult> {
|
|
103
|
+
const currentVersion = normalizeVersion(overrides?.currentVersion ?? APP_VERSION);
|
|
104
|
+
|
|
105
|
+
if (opts?.enabled === false) {
|
|
106
|
+
return { status: "unknown", currentVersion, latestVersion: null, checkedAt: Date.now(), source: "npm" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const fetchFn = overrides?.fetchNpmMetadataFn ?? defaultFetchNpmMetadata;
|
|
111
|
+
const metadata = await fetchFn(CODEXA_NPM_REGISTRY_URL);
|
|
112
|
+
const rawLatest = metadata["dist-tags"]?.latest;
|
|
113
|
+
|
|
114
|
+
if (!rawLatest) {
|
|
115
|
+
return {
|
|
116
|
+
status: "error",
|
|
117
|
+
currentVersion,
|
|
118
|
+
latestVersion: null,
|
|
119
|
+
errorMessage: "npm registry response did not include dist-tags.latest",
|
|
120
|
+
checkedAt: Date.now(),
|
|
121
|
+
source: "npm",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const latestVersion = normalizeVersion(rawLatest);
|
|
126
|
+
|
|
127
|
+
if (!isValidSemver(latestVersion) || !isValidSemver(currentVersion)) {
|
|
128
|
+
return {
|
|
129
|
+
status: "unknown",
|
|
130
|
+
currentVersion,
|
|
131
|
+
latestVersion: rawLatest,
|
|
132
|
+
errorMessage: `Invalid semver — current: "${currentVersion}", latest: "${rawLatest}"`,
|
|
133
|
+
checkedAt: Date.now(),
|
|
134
|
+
source: "npm",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const status = isNewerVersion(latestVersion, currentVersion) ? "update-available" : "up-to-date";
|
|
139
|
+
return { status, currentVersion, latestVersion, checkedAt: Date.now(), source: "npm" };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return {
|
|
142
|
+
status: "error",
|
|
143
|
+
currentVersion,
|
|
144
|
+
latestVersion: null,
|
|
145
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
146
|
+
checkedAt: Date.now(),
|
|
147
|
+
source: "npm",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function formatUpdateInstructions(result: UpdateCheckResult | null): string {
|
|
153
|
+
const current = result?.currentVersion ?? APP_VERSION;
|
|
154
|
+
const latest = result?.latestVersion ?? "unknown";
|
|
155
|
+
|
|
156
|
+
if (result?.status === "error") {
|
|
157
|
+
return [
|
|
158
|
+
`Current installed version: ${current}`,
|
|
159
|
+
`npm latest version: ${latest}`,
|
|
160
|
+
`Error checking npm update status: ${result.errorMessage ?? "unknown error"}`,
|
|
161
|
+
].join("\n");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let statusLine: string;
|
|
165
|
+
if (result?.status === "update-available" && result.latestVersion) {
|
|
166
|
+
statusLine = `Update available: Codexa ${result.latestVersion} is available. You are using ${current}.`;
|
|
167
|
+
} else if (result?.status === "up-to-date") {
|
|
168
|
+
statusLine = "Already up to date.";
|
|
169
|
+
} else {
|
|
170
|
+
statusLine = "Status unknown — could not reach npm registry.";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return [
|
|
174
|
+
`Current installed version: ${current}`,
|
|
175
|
+
`npm latest version: ${latest}`,
|
|
176
|
+
`Status: ${statusLine}`,
|
|
177
|
+
"",
|
|
178
|
+
`Run: ${CODEXA_UPDATE_COMMAND}`,
|
|
179
|
+
].join("\n");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function formatLocalDevUpdateStatus(): string {
|
|
183
|
+
return [
|
|
184
|
+
"Running local-dev Codexa.",
|
|
185
|
+
"Automatic published npm update prompts are disabled for this channel.",
|
|
186
|
+
"Run /update check to explicitly check the published npm package.",
|
|
187
|
+
].join("\n");
|
|
188
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/exec.ts
CHANGED
|
File without changes
|
package/src/headless/execArgs.ts
CHANGED
|
File without changes
|
package/src/index.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
traceTerminalClear,
|
|
20
20
|
writeTerminalControl,
|
|
21
21
|
} from "./core/terminal/terminalControl.js";
|
|
22
|
+
import { performStartupClear } from "./core/terminal/startupClear.js";
|
|
22
23
|
|
|
23
24
|
type RenderHandle = Pick<Instance, "clear" | "cleanup" | "waitUntilExit">;
|
|
24
25
|
const KITTY_KEYBOARD_OPTIONS: RenderOptions["kittyKeyboard"] = {
|
|
@@ -174,15 +175,18 @@ export function startApp({
|
|
|
174
175
|
durationMs: 3500,
|
|
175
176
|
});
|
|
176
177
|
|
|
177
|
-
// Clear the screen and move cursor
|
|
178
|
-
//
|
|
179
|
-
// the
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
178
|
+
// Clear the screen (viewport + scrollback) and move cursor home before Ink
|
|
179
|
+
// renders the first frame. This removes any previous terminal content so
|
|
180
|
+
// the app opens into a clean screen. We stay in the normal screen buffer
|
|
181
|
+
// (no \x1b[?1049h) to preserve mouse text selection after exit.
|
|
182
|
+
// Skipped when --no-clear or CODEXA_NO_CLEAR=1 is set.
|
|
183
|
+
// NOTE: Mouse reporting is NOT enabled here — managed solely by app.tsx.
|
|
184
|
+
traceTerminalClear("src/index.tsx:startup", { mode: "transcript" });
|
|
185
|
+
performStartupClear({
|
|
186
|
+
write: (c) => writeStdout(c, "src/index.tsx:startup"),
|
|
187
|
+
noClear: launchArgs.noClear,
|
|
188
|
+
env,
|
|
189
|
+
});
|
|
186
190
|
const startupWorkspaceRoot = resolveWorkspaceRoot();
|
|
187
191
|
const startupSettings = loadSettings();
|
|
188
192
|
const startupTitle =
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/session/planFlow.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/session/types.ts
CHANGED
|
@@ -23,7 +23,8 @@ export type Screen =
|
|
|
23
23
|
| "permissions-network-picker"
|
|
24
24
|
| "permissions-add-writable-root"
|
|
25
25
|
| "permissions-remove-writable-root"
|
|
26
|
-
| "import-confirmation"
|
|
26
|
+
| "import-confirmation"
|
|
27
|
+
| "update-prompt";
|
|
27
28
|
|
|
28
29
|
// ─── Provider readiness ───────────────────────────────────────────────────────
|
|
29
30
|
|
|
File without changes
|
package/src/types/react-dom.d.ts
CHANGED
|
File without changes
|
|
@@ -22,15 +22,15 @@ export function ActionRequiredBlock({ cols, turnIndex, question }: ActionRequire
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
<Box borderStyle="single" borderColor={theme.
|
|
25
|
+
<Box borderStyle="single" borderColor={theme.borderFocused} flexDirection="column" marginBottom={1} width="100%">
|
|
26
26
|
<Box width="100%" justifyContent="space-between" overflow="hidden" paddingX={1}>
|
|
27
|
-
<Text color={theme.
|
|
28
|
-
<Text color={theme.
|
|
27
|
+
<Text color={theme.text} bold>{`[${turnIndex}] ACTION REQUIRED`}</Text>
|
|
28
|
+
<Text color={theme.text} bold>{"⚡"}</Text>
|
|
29
29
|
</Box>
|
|
30
30
|
<Box flexDirection="column" paddingX={2} marginTop={1} marginBottom={1} width="100%">
|
|
31
|
-
<Text color={theme.
|
|
31
|
+
<Text color={theme.text} bold>{"Verification Question"}</Text>
|
|
32
32
|
{wrappedContent.map((row, index) => (
|
|
33
|
-
<Text key={`content-${index}`} color={theme.
|
|
33
|
+
<Text key={`content-${index}`} color={theme.text}>{row || " "}</Text>
|
|
34
34
|
))}
|
|
35
35
|
</Box>
|
|
36
36
|
</Box>
|
package/src/ui/ActivityBars.tsx
CHANGED
|
@@ -18,7 +18,7 @@ interface WaveBarProps {
|
|
|
18
18
|
|
|
19
19
|
export function WaveBar({ tick, color }: WaveBarProps) {
|
|
20
20
|
const theme = useTheme();
|
|
21
|
-
const activeColor = color ?? theme.
|
|
21
|
+
const activeColor = color ?? theme.accent;
|
|
22
22
|
|
|
23
23
|
let str = "";
|
|
24
24
|
for (let c = 0; c < WAVE_COLS; c++) {
|
|
@@ -48,8 +48,8 @@ interface PulseBarProps {
|
|
|
48
48
|
|
|
49
49
|
export function PulseBar({ tick, color, dimColor }: PulseBarProps) {
|
|
50
50
|
const theme = useTheme();
|
|
51
|
-
const activeColor = color ?? theme.
|
|
52
|
-
const inactiveColor = dimColor ?? theme.
|
|
51
|
+
const activeColor = color ?? theme.accent;
|
|
52
|
+
const inactiveColor = dimColor ?? theme.textDim;
|
|
53
53
|
|
|
54
54
|
const raw = Math.floor(tick / PULSE_SPEED) % PULSE_PERIOD;
|
|
55
55
|
const pos = raw < PULSE_MAX_POS ? raw : PULSE_PERIOD - raw;
|
|
@@ -32,26 +32,26 @@ export function ActivityIndicator({ uiState, externalCliStatus = "idle" }: Activ
|
|
|
32
32
|
}, [isAnimated]);
|
|
33
33
|
|
|
34
34
|
let glyph = "?";
|
|
35
|
-
let color = theme.
|
|
35
|
+
let color = theme.textDim;
|
|
36
36
|
let bold = false;
|
|
37
37
|
|
|
38
38
|
if (isError) {
|
|
39
39
|
glyph = "�";
|
|
40
|
-
color = theme.
|
|
40
|
+
color = theme.error;
|
|
41
41
|
bold = true;
|
|
42
42
|
} else if (isAction) {
|
|
43
43
|
glyph = "?";
|
|
44
|
-
color = theme.
|
|
44
|
+
color = theme.warning;
|
|
45
45
|
bold = true;
|
|
46
46
|
} else if (isStarting || isThinking) {
|
|
47
47
|
glyph = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length]!;
|
|
48
|
-
color = theme.
|
|
48
|
+
color = theme.text;
|
|
49
49
|
} else if (isStreaming) {
|
|
50
50
|
glyph = STREAMING_FRAMES[frameIndex % STREAMING_FRAMES.length]!;
|
|
51
|
-
color = theme.
|
|
51
|
+
color = theme.success;
|
|
52
52
|
} else {
|
|
53
53
|
glyph = "?";
|
|
54
|
-
color = theme.
|
|
54
|
+
color = theme.textDim;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
return <Text color={color} bold={bold}>{glyph}</Text>;
|
package/src/ui/AgentBlock.tsx
CHANGED
|
@@ -49,7 +49,7 @@ const StreamingCursor = memo(function StreamingCursor() {
|
|
|
49
49
|
const theme = useTheme();
|
|
50
50
|
return (
|
|
51
51
|
<Box width="100%" paddingLeft={2}>
|
|
52
|
-
<Text color={theme.
|
|
52
|
+
<Text color={theme.accent}>{"▌"}</Text>
|
|
53
53
|
</Box>
|
|
54
54
|
);
|
|
55
55
|
});
|
|
@@ -93,14 +93,14 @@ export function AgentBlock({
|
|
|
93
93
|
: runStatus;
|
|
94
94
|
const heading = run?.runtime.model ? run.runtime.model.toUpperCase().replace(/-/g, " ") : "Codex";
|
|
95
95
|
|
|
96
|
-
const borderColor = dim ? theme.
|
|
96
|
+
const borderColor = dim ? theme.border : (runPhase === "streaming" ? theme.borderFocused : theme.border);
|
|
97
97
|
|
|
98
98
|
return (
|
|
99
99
|
<DashCard cols={cols} title={heading} rightBadge={rightBadge} borderColor={borderColor}>
|
|
100
100
|
{!streaming && failureMessage && (
|
|
101
101
|
<Box flexDirection="column" width="100%">
|
|
102
102
|
{wrapPlainText(failureMessage, contentWidth).map((row, index) => (
|
|
103
|
-
<Text key={index} color={theme.
|
|
103
|
+
<Text key={index} color={theme.error}>{index === 0 ? `✕ ${row || " "}` : row || " "}</Text>
|
|
104
104
|
))}
|
|
105
105
|
</Box>
|
|
106
106
|
)}
|
|
@@ -116,12 +116,12 @@ export function AgentBlock({
|
|
|
116
116
|
{!streaming && run && run.status !== "running" && (
|
|
117
117
|
<Box flexDirection="column" width="100%">
|
|
118
118
|
{run.status === "canceled" && cancelMessage ? (
|
|
119
|
-
<Text color={theme.
|
|
119
|
+
<Text color={theme.warning}>{cancelMessage}</Text>
|
|
120
120
|
) : run.status === "completed" && pipelineState.length === 0 ? (
|
|
121
|
-
<Text color={theme.
|
|
121
|
+
<Text color={theme.textDim}>{"(no output)"}</Text>
|
|
122
122
|
) : null}
|
|
123
123
|
{run.truncatedOutput && (
|
|
124
|
-
<Text color={theme.
|
|
124
|
+
<Text color={theme.textDim}>{RUN_OUTPUT_TRUNCATION_NOTICE}</Text>
|
|
125
125
|
)}
|
|
126
126
|
</Box>
|
|
127
127
|
)}
|
|
@@ -62,7 +62,7 @@ export function AnimatedStatusText({ baseText, isActive, isError = false, animat
|
|
|
62
62
|
const suffix = isActive ? animationFrame ?? localFrame : "";
|
|
63
63
|
|
|
64
64
|
return (
|
|
65
|
-
<Text color={isError ? theme.
|
|
65
|
+
<Text color={isError ? theme.error : theme.info} wrap="truncate">
|
|
66
66
|
{renderedText}{suffix}
|
|
67
67
|
</Text>
|
|
68
68
|
);
|
package/src/ui/AppShell.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Box, Text, useStdin } from "ink";
|
|
3
|
+
import { useTheme } from "./theme.js";
|
|
3
4
|
import type { RuntimeSummary } from "../config/runtimeConfig.js";
|
|
4
5
|
import type { CodexAuthState } from "../core/auth/codexAuth.js";
|
|
5
6
|
import { HEADER_CONFIG_DEFAULTS, type HeaderConfig } from "../config/settings.js";
|
|
@@ -23,11 +24,11 @@ import {
|
|
|
23
24
|
} from "./Timeline.js";
|
|
24
25
|
import { buildNativeTranscriptParts, type NativeTranscriptRowItem, type TimelineRow } from "./timelineMeasure.js";
|
|
25
26
|
import type { TerminalSelectionProfile } from "../core/terminal/terminalSelection.js";
|
|
26
|
-
import { MemoizedTopHeader, measureTopHeaderRows } from "./TopHeader.js";
|
|
27
|
+
import { MemoizedTopHeader, measureTopHeaderRows, type UpdateAvailableInfo } from "./TopHeader.js";
|
|
27
28
|
|
|
28
|
-
const COMPACT_HEADER_TO_COMPOSER_GAP_ROWS =
|
|
29
|
-
const MEDIUM_HEADER_TO_COMPOSER_GAP_ROWS =
|
|
30
|
-
const TALL_HEADER_TO_COMPOSER_GAP_ROWS =
|
|
29
|
+
const COMPACT_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
30
|
+
const MEDIUM_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
31
|
+
const TALL_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
31
32
|
|
|
32
33
|
// ─── Types & constants ────────────────────────────────────────────────────────
|
|
33
34
|
|
|
@@ -57,6 +58,7 @@ export interface AppShellProps {
|
|
|
57
58
|
selectionProfile?: TerminalSelectionProfile;
|
|
58
59
|
clearCount?: number;
|
|
59
60
|
headerConfig?: HeaderConfig;
|
|
61
|
+
updateAvailable?: UpdateAvailableInfo | null;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
// ─── Helpers & subcomponents ─────────────────────────────────────────────────
|
|
@@ -161,7 +163,9 @@ function AppShellInner({
|
|
|
161
163
|
selectionProfile,
|
|
162
164
|
clearCount = 0,
|
|
163
165
|
headerConfig = HEADER_CONFIG_DEFAULTS,
|
|
166
|
+
updateAvailable = null,
|
|
164
167
|
}: AppShellProps) {
|
|
168
|
+
const theme = useTheme();
|
|
165
169
|
renderDebug.useRenderDebug("AppShell", {
|
|
166
170
|
cols: layout.cols,
|
|
167
171
|
rows: layout.rows,
|
|
@@ -188,7 +192,7 @@ function AppShellInner({
|
|
|
188
192
|
|
|
189
193
|
const shellWidth = getShellWidth(layout.cols);
|
|
190
194
|
const shellHeight = getShellHeight(layout.rows);
|
|
191
|
-
const headerRows = measureTopHeaderRows(layout);
|
|
195
|
+
const headerRows = measureTopHeaderRows(layout, headerConfig, !!updateAvailable);
|
|
192
196
|
const headerToContentGapRows = calculateHeaderToContentGapRows(layout);
|
|
193
197
|
const showComposer = screen === "main";
|
|
194
198
|
const showMainPanel = screen === "main" && mainPanel !== undefined && mainPanel !== null;
|
|
@@ -510,6 +514,7 @@ function AppShellInner({
|
|
|
510
514
|
layout={layout}
|
|
511
515
|
runtimeSummary={runtimeSummary}
|
|
512
516
|
headerConfig={headerConfig}
|
|
517
|
+
updateAvailable={updateAvailable}
|
|
513
518
|
/>
|
|
514
519
|
|
|
515
520
|
{headerToContentGapRows > 0 && (
|
|
@@ -540,6 +545,7 @@ function AppShellInner({
|
|
|
540
545
|
layout={layout}
|
|
541
546
|
runtimeSummary={runtimeSummary}
|
|
542
547
|
headerConfig={headerConfig}
|
|
548
|
+
updateAvailable={updateAvailable}
|
|
543
549
|
/>
|
|
544
550
|
|
|
545
551
|
{headerToContentGapRows > 0 && (
|
|
@@ -594,6 +600,7 @@ function AppShellInner({
|
|
|
594
600
|
layout={layout}
|
|
595
601
|
runtimeSummary={runtimeSummary}
|
|
596
602
|
headerConfig={headerConfig}
|
|
603
|
+
updateAvailable={updateAvailable}
|
|
597
604
|
/>
|
|
598
605
|
|
|
599
606
|
{headerToContentGapRows > 0 && (
|
|
@@ -693,6 +700,7 @@ export const AppShell = memo(AppShellInner, (prev, next) => {
|
|
|
693
700
|
prev.mouseCapture === next.mouseCapture &&
|
|
694
701
|
prev.onMouseActivity === next.onMouseActivity &&
|
|
695
702
|
prev.clearCount === next.clearCount &&
|
|
703
|
+
prev.updateAvailable === next.updateAvailable &&
|
|
696
704
|
panelPropsEqual
|
|
697
705
|
);
|
|
698
706
|
});
|
|
@@ -52,20 +52,20 @@ export function AttachmentImportPanel({
|
|
|
52
52
|
<Box flexDirection="column" width="100%" marginTop={1}>
|
|
53
53
|
<Box
|
|
54
54
|
borderStyle="round"
|
|
55
|
-
borderColor={theme.
|
|
55
|
+
borderColor={theme.border}
|
|
56
56
|
paddingX={2}
|
|
57
57
|
paddingY={1}
|
|
58
58
|
width="100%"
|
|
59
59
|
>
|
|
60
|
-
<Text color={theme.
|
|
61
|
-
<Text color={theme.
|
|
60
|
+
<Text color={theme.accent} bold>IMPORT FILE </Text>
|
|
61
|
+
<Text color={theme.textMuted}>
|
|
62
62
|
Copy {files.length} outside-workspace {fileLabel} into .codexa/attachments?
|
|
63
63
|
</Text>
|
|
64
64
|
</Box>
|
|
65
65
|
|
|
66
66
|
<Box
|
|
67
67
|
borderStyle="round"
|
|
68
|
-
borderColor={theme.
|
|
68
|
+
borderColor={theme.borderFocused}
|
|
69
69
|
paddingX={2}
|
|
70
70
|
paddingY={1}
|
|
71
71
|
marginTop={1}
|
|
@@ -74,8 +74,8 @@ export function AttachmentImportPanel({
|
|
|
74
74
|
>
|
|
75
75
|
{files.map((file, i) => (
|
|
76
76
|
<Box key={i} flexDirection="column" marginBottom={i < files.length - 1 ? 1 : 0}>
|
|
77
|
-
<Text color={theme.
|
|
78
|
-
<Text color={theme.
|
|
77
|
+
<Text color={theme.text}>{path.basename(file.srcPath)}</Text>
|
|
78
|
+
<Text color={theme.textDim}>
|
|
79
79
|
{"→ "}{relativeAttachmentsDir}/{file.destFilename}
|
|
80
80
|
</Text>
|
|
81
81
|
</Box>
|
|
@@ -83,14 +83,14 @@ export function AttachmentImportPanel({
|
|
|
83
83
|
|
|
84
84
|
{showVisionWarning && (
|
|
85
85
|
<Box marginTop={1}>
|
|
86
|
-
<Text color={theme.
|
|
86
|
+
<Text color={theme.warning}>
|
|
87
87
|
Note: active model may not support images.
|
|
88
88
|
</Text>
|
|
89
89
|
</Box>
|
|
90
90
|
)}
|
|
91
91
|
|
|
92
92
|
<Box marginTop={1}>
|
|
93
|
-
<Text color={theme.
|
|
93
|
+
<Text color={theme.textDim}>Enter copy and continue Esc cancel</Text>
|
|
94
94
|
</Box>
|
|
95
95
|
</Box>
|
|
96
96
|
</Box>
|
package/src/ui/AuthPanel.tsx
CHANGED
|
@@ -49,10 +49,10 @@ export function AuthPanel({
|
|
|
49
49
|
const authStateLabel = getAuthStateLabel(authStatus.state);
|
|
50
50
|
const authStateColor =
|
|
51
51
|
authStatus.state === "authenticated"
|
|
52
|
-
? theme.
|
|
52
|
+
? theme.success
|
|
53
53
|
: authStatus.state === "unauthenticated"
|
|
54
|
-
? theme.
|
|
55
|
-
: theme.
|
|
54
|
+
? theme.error
|
|
55
|
+
: theme.warning;
|
|
56
56
|
const checkedAtLabel = authStatus.checkedAt > 0
|
|
57
57
|
? new Date(authStatus.checkedAt).toLocaleTimeString()
|
|
58
58
|
: "not checked yet";
|
|
@@ -60,52 +60,52 @@ export function AuthPanel({
|
|
|
60
60
|
return (
|
|
61
61
|
<Box
|
|
62
62
|
borderStyle="round"
|
|
63
|
-
borderColor={theme.
|
|
63
|
+
borderColor={theme.borderFocused}
|
|
64
64
|
flexDirection="column"
|
|
65
65
|
paddingX={2}
|
|
66
66
|
paddingY={1}
|
|
67
67
|
marginTop={1}
|
|
68
68
|
width="100%"
|
|
69
69
|
>
|
|
70
|
-
<Text color={theme.
|
|
70
|
+
<Text color={theme.accent} bold>
|
|
71
71
|
Auth and subscription guidance
|
|
72
72
|
</Text>
|
|
73
|
-
<Text color={theme.
|
|
74
|
-
<Text color={theme.
|
|
75
|
-
<Text color={theme.
|
|
73
|
+
<Text color={theme.textMuted}>Current backend: {provider.label}</Text>
|
|
74
|
+
<Text color={theme.textMuted}>Current preference: {formatAuthPreferenceLabel(authPreference)}</Text>
|
|
75
|
+
<Text color={theme.info}>Backend auth: {provider.authLabel}</Text>
|
|
76
76
|
<Text color={authStateColor}>Runtime auth state: {authStateLabel}</Text>
|
|
77
|
-
<Text color={theme.
|
|
78
|
-
<Text color={theme.
|
|
79
|
-
<Text color={theme.
|
|
77
|
+
<Text color={theme.textDim}>Last checked: {checkedAtLabel}</Text>
|
|
78
|
+
<Text color={theme.textDim}>Probe summary: {authStatus.rawSummary || "No probe output yet"}</Text>
|
|
79
|
+
<Text color={theme.text}>
|
|
80
80
|
This UI securely bridges to the Codexa neural network. It does not collect or store your ChatGPT credentials.
|
|
81
81
|
</Text>
|
|
82
|
-
<Text color={theme.
|
|
82
|
+
<Text color={theme.text}>{provider.statusMessage}</Text>
|
|
83
83
|
<Box flexDirection="column" marginTop={1}>
|
|
84
|
-
<Text color={theme.
|
|
85
|
-
<Text color={theme.
|
|
86
|
-
<Text color={theme.
|
|
87
|
-
<Text color={theme.
|
|
84
|
+
<Text color={theme.info}>Commands:</Text>
|
|
85
|
+
<Text color={theme.text}> /login guided ChatGPT sign-in steps</Text>
|
|
86
|
+
<Text color={theme.text}> /logout guided sign-out steps</Text>
|
|
87
|
+
<Text color={theme.text}> /auth status refresh Codexa authentication</Text>
|
|
88
88
|
</Box>
|
|
89
89
|
<Box flexDirection="column" marginTop={1}>
|
|
90
90
|
{AUTH_PREFERENCES.map((item, index) => (
|
|
91
|
-
<Text key={item.id} color={item.id === authPreference ? theme.
|
|
91
|
+
<Text key={item.id} color={item.id === authPreference ? theme.success : theme.text}>
|
|
92
92
|
{index + 1}. {item.label} {item.id === authPreference ? "✓" : ""}
|
|
93
93
|
</Text>
|
|
94
94
|
))}
|
|
95
95
|
</Box>
|
|
96
96
|
<Box marginTop={1}>
|
|
97
|
-
<Text color={theme.
|
|
97
|
+
<Text color={theme.textDim}>
|
|
98
98
|
Press 1-{AUTH_PREFERENCES.length} to change preference, R to refresh status, Esc to close.
|
|
99
99
|
</Text>
|
|
100
100
|
</Box>
|
|
101
101
|
{authStatusBusy && (
|
|
102
102
|
<Box marginTop={1}>
|
|
103
|
-
<Text color={theme.
|
|
103
|
+
<Text color={theme.warning}>Checking auth status...</Text>
|
|
104
104
|
</Box>
|
|
105
105
|
)}
|
|
106
106
|
{authStatus.recommendedAction && (
|
|
107
107
|
<Box marginTop={1}>
|
|
108
|
-
<Text color={theme.
|
|
108
|
+
<Text color={theme.textDim}>Next step: {authStatus.recommendedAction}</Text>
|
|
109
109
|
</Box>
|
|
110
110
|
)}
|
|
111
111
|
</Box>
|
package/src/ui/BackendPicker.tsx
CHANGED
|
File without changes
|