@golba98/codexa 1.0.2 → 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 +12 -7
- package/src/app.tsx +149 -117
- package/src/commands/handler.ts +4 -4
- package/src/config/buildInfo.ts +2 -2
- package/src/config/persistence.ts +10 -0
- package/src/config/settings.ts +7 -15
- package/src/config/updateCheckCache.ts +19 -1
- package/src/core/channel.ts +23 -0
- package/src/core/executables/codexExecutable.ts +1 -0
- package/src/core/executables/executableResolver.ts +61 -43
- package/src/core/launchContext.ts +33 -33
- package/src/core/process/processValidation.ts +9 -5
- package/src/core/providerLauncher/launcher.ts +59 -42
- package/src/core/updateCheck.ts +111 -95
- package/src/session/types.ts +2 -1
- 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 +9 -24
- package/src/ui/AttachmentImportPanel.tsx +8 -8
- package/src/ui/AuthPanel.tsx +20 -20
- package/src/ui/BottomComposer.tsx +117 -78
- package/src/ui/DashCard.tsx +3 -3
- package/src/ui/Markdown.tsx +17 -17
- package/src/ui/ModelPickerScreen.tsx +10 -10
- package/src/ui/ModelReasoningPicker.tsx +14 -14
- package/src/ui/Panel.tsx +3 -3
- package/src/ui/PlanActionPicker.tsx +6 -6
- package/src/ui/PlanReviewPanel.tsx +8 -8
- package/src/ui/ProviderPicker.tsx +18 -18
- 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/TextEntryPanel.tsx +10 -10
- package/src/ui/ThinkingBlock.tsx +8 -8
- package/src/ui/Timeline.tsx +21 -17
- package/src/ui/TopHeader.tsx +421 -293
- package/src/ui/TurnGroup.tsx +31 -31
- package/src/ui/UpdateAvailableCard.tsx +41 -0
- package/src/ui/UpdatePromptPanel.tsx +203 -0
- package/src/ui/focus.ts +3 -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/runtimeDisplay.ts +100 -0
- package/src/ui/theme.tsx +274 -395
- package/src/ui/timelineMeasure.ts +29 -19
- package/scripts/audit-codexa-capabilities.mjs +0 -466
- package/scripts/gen-build-info.mjs +0 -33
- package/scripts/smoke-terminal-bench.mjs +0 -35
- package/src/appRenderStability.test.ts +0 -131
- package/src/commands/handler.test.ts +0 -655
- package/src/config/launchArgs.test.ts +0 -189
- package/src/config/layeredConfig.test.ts +0 -143
- package/src/config/persistence.test.ts +0 -114
- 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/startupClear.test.ts +0 -55
- 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/updateCheck.test.ts +0 -194
- 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 -436
- package/src/index.test.tsx +0 -620
- 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 -1776
- 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 -254
- 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 -146
- 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
package/src/app.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import type { LaunchArgs } from "./config/launchArgs.js";
|
|
18
18
|
import { loadSettings, saveSettings } from "./config/persistence.js";
|
|
19
19
|
import {
|
|
20
|
+
APP_VERSION,
|
|
20
21
|
type AuthPreference,
|
|
21
22
|
type AvailableBackend,
|
|
22
23
|
type AvailableMode,
|
|
@@ -110,15 +111,11 @@ import {
|
|
|
110
111
|
getPromptWorkspaceGuardMessage,
|
|
111
112
|
getShellWorkspaceGuardMessage,
|
|
112
113
|
} from "./core/workspaceGuard.js";
|
|
113
|
-
import {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
formatContextCompact,
|
|
119
|
-
formatContextLength,
|
|
120
|
-
resolveModelContextLength,
|
|
121
|
-
type ModelContextMetadata,
|
|
114
|
+
import {
|
|
115
|
+
formatContextCompact,
|
|
116
|
+
formatContextLength,
|
|
117
|
+
resolveModelContextLength,
|
|
118
|
+
type ModelContextMetadata,
|
|
122
119
|
} from "./core/providerRuntime/contextMetadata.js";
|
|
123
120
|
import { captureWorkspaceSnapshot, createWorkspaceActivityTracker, diffWorkspaceSnapshots } from "./core/workspaceActivity.js";
|
|
124
121
|
import { resolveWorkspaceRoot } from "./core/workspaceRoot.js";
|
|
@@ -214,10 +211,12 @@ import { ReasoningPicker } from "./ui/ReasoningPicker.js";
|
|
|
214
211
|
import { AttachmentImportPanel, type PendingImportFile } from "./ui/AttachmentImportPanel.js";
|
|
215
212
|
import { SelectionPanel } from "./ui/SelectionPanel.js";
|
|
216
213
|
import { SettingsPanel } from "./ui/SettingsPanel.js";
|
|
214
|
+
import { UpdatePromptPanel } from "./ui/UpdatePromptPanel.js";
|
|
217
215
|
import { measureTextEntryPanelRows, TextEntryPanel } from "./ui/TextEntryPanel.js";
|
|
218
216
|
import { ThemePicker } from "./ui/ThemePicker.js";
|
|
219
217
|
import { getFocusTargetForScreen, FOCUS_IDS } from "./ui/focus.js";
|
|
220
|
-
import { ThemeProvider, THEMES } from "./ui/theme.js";
|
|
218
|
+
import { ThemeProvider, THEMES } from "./ui/theme.js";
|
|
219
|
+
import { buildActiveRuntimeDisplay, runtimeDisplayToSummary } from "./ui/runtimeDisplay.js";
|
|
221
220
|
import {
|
|
222
221
|
cancelThemeSelection,
|
|
223
222
|
commitThemeSelection,
|
|
@@ -228,7 +227,8 @@ import {
|
|
|
228
227
|
} from "./ui/themeFlow.js";
|
|
229
228
|
import { isBusy as isUiBusy } from "./session/types.js";
|
|
230
229
|
import { AppShell } from "./ui/AppShell.js";
|
|
231
|
-
import { checkForUpdates, formatUpdateInstructions, type UpdateCheckResult } from "./core/updateCheck.js";
|
|
230
|
+
import { checkForUpdates, formatLocalDevUpdateStatus, formatUpdateInstructions, shouldRunStartupUpdateCheck, type UpdateCheckResult } from "./core/updateCheck.js";
|
|
231
|
+
import { isLocalDevChannel } from "./core/channel.js";
|
|
232
232
|
import {
|
|
233
233
|
isCacheValid,
|
|
234
234
|
loadUpdateCheckCache,
|
|
@@ -307,6 +307,9 @@ export function App({ launchArgs }: AppProps) {
|
|
|
307
307
|
? projectInstructionsLoad.instructions
|
|
308
308
|
: null;
|
|
309
309
|
const initialSettings = useRef(loadSettings());
|
|
310
|
+
const skippedUpdateVersionRef = useRef<string | null>(
|
|
311
|
+
initialSettings.current.updateCheck.skippedUpdateVersion ?? null,
|
|
312
|
+
);
|
|
310
313
|
const initialProviderWorkspaceConfig = useRef<ProviderWorkspaceConfig>(loadProviderWorkspaceConfig(workspaceRoot));
|
|
311
314
|
const initialLayeredConfig = useRef<LayeredConfigResult | null>(null);
|
|
312
315
|
if (initialLayeredConfig.current === null) {
|
|
@@ -765,16 +768,22 @@ export function App({ launchArgs }: AppProps) {
|
|
|
765
768
|
[planFlow],
|
|
766
769
|
);
|
|
767
770
|
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
771
|
+
const activeRuntimeDisplay = useMemo(() => buildActiveRuntimeDisplay({
|
|
772
|
+
route: activeProviderRoute,
|
|
773
|
+
reasoningLevel,
|
|
774
|
+
mode,
|
|
775
|
+
tokensUsed: estimateTokens(conversationChars),
|
|
776
|
+
modelCapability: currentModelCapability,
|
|
777
|
+
contextMetadata: activeContextMetadata,
|
|
778
|
+
}), [
|
|
779
|
+
activeContextMetadata,
|
|
780
|
+
activeProviderRoute,
|
|
781
|
+
conversationChars,
|
|
782
|
+
currentModelCapability,
|
|
783
|
+
mode,
|
|
784
|
+
reasoningLevel,
|
|
785
|
+
]);
|
|
786
|
+
const currentModelSpec = activeRuntimeDisplay.modelSpec;
|
|
778
787
|
|
|
779
788
|
const hasUserPrompt = useMemo(
|
|
780
789
|
() => staticEvents.some((e) => e.type === "user") || activeEvents.some((e) => e.type === "user"),
|
|
@@ -1363,33 +1372,41 @@ export function App({ launchArgs }: AppProps) {
|
|
|
1363
1372
|
// Non-blocking background update check — runs once at startup.
|
|
1364
1373
|
useEffect(() => {
|
|
1365
1374
|
const ucSettings = initialSettings.current.updateCheck ?? DEFAULT_UPDATE_CHECK_SETTINGS;
|
|
1366
|
-
if (!ucSettings.enabled) return;
|
|
1375
|
+
if (!shouldRunStartupUpdateCheck(process.env, ucSettings.enabled)) return;
|
|
1367
1376
|
|
|
1368
1377
|
const timer = setTimeout(() => {
|
|
1369
1378
|
void (async () => {
|
|
1370
1379
|
try {
|
|
1371
1380
|
const cache = loadUpdateCheckCache();
|
|
1372
|
-
if (cache && isCacheValid(cache, ucSettings.intervalHours)) {
|
|
1373
|
-
if (cache.updateAvailable) {
|
|
1374
|
-
setUpdateCheckResult({
|
|
1375
|
-
status: "update-available",
|
|
1376
|
-
currentVersion: cache.currentVersion,
|
|
1377
|
-
latestVersion: cache.latestVersion,
|
|
1378
|
-
checkedAt: cache.lastChecked,
|
|
1379
|
-
});
|
|
1380
|
-
|
|
1381
|
+
if (cache && isCacheValid(cache, ucSettings.intervalHours, APP_VERSION)) {
|
|
1382
|
+
if (cache.updateAvailable && cache.latestVersion) {
|
|
1383
|
+
setUpdateCheckResult({
|
|
1384
|
+
status: "update-available",
|
|
1385
|
+
currentVersion: cache.currentVersion,
|
|
1386
|
+
latestVersion: cache.latestVersion,
|
|
1387
|
+
checkedAt: cache.lastChecked,
|
|
1388
|
+
});
|
|
1389
|
+
if (cache.latestVersion !== skippedUpdateVersionRef.current) {
|
|
1390
|
+
setScreen("update-prompt");
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1381
1393
|
return;
|
|
1382
1394
|
}
|
|
1383
|
-
const result = await checkForUpdates({ enabled: true });
|
|
1384
|
-
if (result.status !== "error") {
|
|
1385
|
-
setUpdateCheckResult(result);
|
|
1386
|
-
saveUpdateCheckCache({
|
|
1387
|
-
lastChecked: result.checkedAt,
|
|
1388
|
-
currentVersion: result.currentVersion,
|
|
1389
|
-
latestVersion: result.latestVersion,
|
|
1390
|
-
updateAvailable: result.status === "update-available",
|
|
1391
|
-
});
|
|
1392
|
-
|
|
1395
|
+
const result = await checkForUpdates({ enabled: true });
|
|
1396
|
+
if (result.status !== "error") {
|
|
1397
|
+
setUpdateCheckResult(result);
|
|
1398
|
+
saveUpdateCheckCache({
|
|
1399
|
+
lastChecked: result.checkedAt,
|
|
1400
|
+
currentVersion: result.currentVersion,
|
|
1401
|
+
latestVersion: result.latestVersion,
|
|
1402
|
+
updateAvailable: result.status === "update-available",
|
|
1403
|
+
});
|
|
1404
|
+
if (result.status === "update-available" && result.latestVersion) {
|
|
1405
|
+
if (result.latestVersion !== skippedUpdateVersionRef.current) {
|
|
1406
|
+
setScreen("update-prompt");
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1393
1410
|
} catch {
|
|
1394
1411
|
// Never crash the TUI on a failed update check.
|
|
1395
1412
|
}
|
|
@@ -1898,6 +1915,33 @@ export function App({ launchArgs }: AppProps) {
|
|
|
1898
1915
|
setScreen("main");
|
|
1899
1916
|
}, [applyWorkspaceDisplayMode, showBusyLoader, terminalMouseMode, terminalTitleMode, workspaceDisplayMode]);
|
|
1900
1917
|
|
|
1918
|
+
const handleSkipUpdateForSession = useCallback(() => {
|
|
1919
|
+
setScreen("main");
|
|
1920
|
+
}, []);
|
|
1921
|
+
|
|
1922
|
+
const handleSkipUpdateVersion = useCallback((version: string) => {
|
|
1923
|
+
initialSettings.current.updateCheck = {
|
|
1924
|
+
...initialSettings.current.updateCheck,
|
|
1925
|
+
skippedUpdateVersion: version,
|
|
1926
|
+
};
|
|
1927
|
+
saveSettings({
|
|
1928
|
+
ui: {
|
|
1929
|
+
layoutStyle: initialSettings.current.ui.layoutStyle,
|
|
1930
|
+
theme: themeSelection.committedTheme,
|
|
1931
|
+
workspaceDisplayMode,
|
|
1932
|
+
terminalTitleMode,
|
|
1933
|
+
showBusyLoader,
|
|
1934
|
+
terminalMouseMode,
|
|
1935
|
+
customTheme,
|
|
1936
|
+
},
|
|
1937
|
+
auth: { preference: authPreference },
|
|
1938
|
+
header: headerConfig,
|
|
1939
|
+
updateCheck: initialSettings.current.updateCheck,
|
|
1940
|
+
});
|
|
1941
|
+
skippedUpdateVersionRef.current = version;
|
|
1942
|
+
setScreen("main");
|
|
1943
|
+
}, [authPreference, customTheme, headerConfig, showBusyLoader, terminalMouseMode, terminalTitleMode, themeSelection.committedTheme, workspaceDisplayMode]);
|
|
1944
|
+
|
|
1901
1945
|
const setApprovalPolicyWithNotice = useCallback((nextValue: RuntimeApprovalPolicy) => {
|
|
1902
1946
|
const gate = guardConfigMutation("mode", busy);
|
|
1903
1947
|
if (!gate.allowed) {
|
|
@@ -4051,6 +4095,10 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4051
4095
|
return;
|
|
4052
4096
|
case "update": {
|
|
4053
4097
|
const arg = commandResult.value ?? "status";
|
|
4098
|
+
if (isLocalDevChannel() && arg !== "check") {
|
|
4099
|
+
appendSystemEvent("Update", formatLocalDevUpdateStatus());
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4054
4102
|
void (async () => {
|
|
4055
4103
|
let freshResult = updateCheckResult;
|
|
4056
4104
|
if (arg === "check" || freshResult === null) {
|
|
@@ -4058,18 +4106,22 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4058
4106
|
freshResult = await checkForUpdates({ enabled: true });
|
|
4059
4107
|
setUpdateCheckResult(freshResult);
|
|
4060
4108
|
if (freshResult.status !== "error") {
|
|
4061
|
-
saveUpdateCheckCache({
|
|
4062
|
-
lastChecked: freshResult.checkedAt,
|
|
4063
|
-
currentVersion: freshResult.currentVersion,
|
|
4064
|
-
latestVersion: freshResult.latestVersion,
|
|
4065
|
-
updateAvailable: freshResult.status === "update-available",
|
|
4066
|
-
});
|
|
4067
|
-
}
|
|
4109
|
+
saveUpdateCheckCache({
|
|
4110
|
+
lastChecked: freshResult.checkedAt,
|
|
4111
|
+
currentVersion: freshResult.currentVersion,
|
|
4112
|
+
latestVersion: freshResult.latestVersion,
|
|
4113
|
+
updateAvailable: freshResult.status === "update-available",
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4068
4116
|
} catch {
|
|
4069
4117
|
freshResult = null;
|
|
4070
4118
|
}
|
|
4071
4119
|
}
|
|
4072
|
-
|
|
4120
|
+
if (freshResult?.status === "update-available" && freshResult.latestVersion) {
|
|
4121
|
+
setScreen("update-prompt");
|
|
4122
|
+
} else {
|
|
4123
|
+
appendSystemEvent("Update", formatUpdateInstructions(freshResult));
|
|
4124
|
+
}
|
|
4073
4125
|
})();
|
|
4074
4126
|
return;
|
|
4075
4127
|
}
|
|
@@ -4202,58 +4254,19 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4202
4254
|
workspaceRoot,
|
|
4203
4255
|
]);
|
|
4204
4256
|
|
|
4205
|
-
const modelDisplayName =
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
return model;
|
|
4219
|
-
}, [
|
|
4220
|
-
activeProviderRoute.modelId,
|
|
4221
|
-
activeProviderRoute.modelSelection,
|
|
4222
|
-
activeProviderRoute.providerId,
|
|
4223
|
-
activeProviderRoute.reasoning,
|
|
4224
|
-
currentModelCapability?.label,
|
|
4225
|
-
model,
|
|
4226
|
-
reasoningLevel,
|
|
4227
|
-
]);
|
|
4228
|
-
const composerReasoningLevel = activeProviderRoute.providerId === "anthropic"
|
|
4229
|
-
? ""
|
|
4230
|
-
: activeProviderRoute.reasoning ?? reasoningLevel;
|
|
4231
|
-
const headerRuntimeSummary = useMemo(() => {
|
|
4232
|
-
const contextLabel = activeContextMetadata?.contextLength != null
|
|
4233
|
-
? `${formatContextCompact(estimateTokens(conversationChars))} / ${activeContextMetadata.confidence === "estimated" ? "~" : ""}${formatContextCompact(activeContextMetadata.contextLength)}`
|
|
4234
|
-
: "Unknown";
|
|
4235
|
-
|
|
4236
|
-
return {
|
|
4237
|
-
...runtimeSummary,
|
|
4238
|
-
providerLabel: activeRouteProvider?.displayName ?? runtimeSummary.providerLabel,
|
|
4239
|
-
modelLabel: modelDisplayName,
|
|
4240
|
-
contextLabel,
|
|
4241
|
-
};
|
|
4242
|
-
}, [
|
|
4243
|
-
activeContextMetadata?.confidence,
|
|
4244
|
-
activeContextMetadata?.contextLength,
|
|
4245
|
-
activeRouteProvider?.displayName,
|
|
4246
|
-
conversationChars,
|
|
4247
|
-
modelDisplayName,
|
|
4248
|
-
runtimeSummary,
|
|
4249
|
-
]);
|
|
4250
|
-
const effectiveHeaderConfig = useMemo<HeaderConfig>(() => ({
|
|
4251
|
-
...headerConfig,
|
|
4252
|
-
showProvider: true,
|
|
4253
|
-
showModel: false,
|
|
4254
|
-
showReasoning: false,
|
|
4255
|
-
showContext: false,
|
|
4256
|
-
}), [headerConfig]);
|
|
4257
|
+
const modelDisplayName = activeRuntimeDisplay.modelDisplay;
|
|
4258
|
+
const composerReasoningLevel = "";
|
|
4259
|
+
const headerRuntimeSummary = useMemo(
|
|
4260
|
+
() => runtimeDisplayToSummary(activeRuntimeDisplay, runtimeSummary),
|
|
4261
|
+
[activeRuntimeDisplay, runtimeSummary],
|
|
4262
|
+
);
|
|
4263
|
+
const effectiveHeaderConfig = useMemo<HeaderConfig>(() => ({
|
|
4264
|
+
...headerConfig,
|
|
4265
|
+
showProvider: true,
|
|
4266
|
+
showModel: false,
|
|
4267
|
+
showReasoning: false,
|
|
4268
|
+
showContext: false,
|
|
4269
|
+
}), [headerConfig]);
|
|
4257
4270
|
|
|
4258
4271
|
// Memoize the composer element so AppShell's memo check (prev.composer ===
|
|
4259
4272
|
// next.composer) passes during streaming. Without this, a new JSX element is
|
|
@@ -4263,7 +4276,7 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4263
4276
|
if (planFlow.kind === "awaiting_action") {
|
|
4264
4277
|
if (!hasVisibleTranscriptPlan) {
|
|
4265
4278
|
return (
|
|
4266
|
-
<Text color={activeTheme.
|
|
4279
|
+
<Text color={activeTheme.textMuted}>
|
|
4267
4280
|
Plan could not be displayed. Please ask Codexa to regenerate the plan.
|
|
4268
4281
|
</Text>
|
|
4269
4282
|
);
|
|
@@ -4300,12 +4313,14 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4300
4313
|
<MemoizedBottomComposer
|
|
4301
4314
|
key={composerInstanceKey}
|
|
4302
4315
|
layout={terminalLayout}
|
|
4303
|
-
uiState={uiState}
|
|
4304
|
-
mode={mode}
|
|
4305
|
-
model={modelDisplayName}
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4316
|
+
uiState={uiState}
|
|
4317
|
+
mode={mode}
|
|
4318
|
+
model={modelDisplayName}
|
|
4319
|
+
footerModelDisplay={activeRuntimeDisplay.footerModelDisplay}
|
|
4320
|
+
themeName={activeThemeName}
|
|
4321
|
+
reasoningLevel={composerReasoningLevel}
|
|
4322
|
+
contextDisplay={activeRuntimeDisplay.contextDisplay}
|
|
4323
|
+
planMode={planMode}
|
|
4309
4324
|
showBusyLoader={showBusyLoader}
|
|
4310
4325
|
tokensUsed={estimateTokens(conversationChars)}
|
|
4311
4326
|
modelSpec={currentModelSpec}
|
|
@@ -4339,13 +4354,15 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4339
4354
|
handleCancel,
|
|
4340
4355
|
handlePlanFeedbackSubmit,
|
|
4341
4356
|
hasVisibleTranscriptPlan,
|
|
4342
|
-
activeTheme.
|
|
4357
|
+
activeTheme.textMuted,
|
|
4343
4358
|
composerInstanceKey,
|
|
4344
4359
|
terminalLayout,
|
|
4345
4360
|
uiState,
|
|
4346
|
-
mode,
|
|
4347
|
-
modelDisplayName,
|
|
4348
|
-
|
|
4361
|
+
mode,
|
|
4362
|
+
modelDisplayName,
|
|
4363
|
+
activeRuntimeDisplay.footerModelDisplay,
|
|
4364
|
+
activeRuntimeDisplay.contextDisplay,
|
|
4365
|
+
activeThemeName,
|
|
4349
4366
|
composerReasoningLevel,
|
|
4350
4367
|
planMode,
|
|
4351
4368
|
showBusyLoader,
|
|
@@ -4368,9 +4385,10 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4368
4385
|
togglePlanModeWithNotice,
|
|
4369
4386
|
handleClear,
|
|
4370
4387
|
cycleModeWithNotice,
|
|
4371
|
-
handleQuit,
|
|
4372
|
-
activeProviderRoute.providerId,
|
|
4373
|
-
|
|
4388
|
+
handleQuit,
|
|
4389
|
+
activeProviderRoute.providerId,
|
|
4390
|
+
sessionState.externalCliStatus,
|
|
4391
|
+
]);
|
|
4374
4392
|
|
|
4375
4393
|
// ─── Render ──────────────────────────────────────────────────────────────────
|
|
4376
4394
|
|
|
@@ -4395,7 +4413,11 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4395
4413
|
selectionProfile={selectionProfile}
|
|
4396
4414
|
clearCount={sessionState.clearCount}
|
|
4397
4415
|
headerConfig={effectiveHeaderConfig}
|
|
4398
|
-
|
|
4416
|
+
updateAvailable={
|
|
4417
|
+
updateCheckResult?.status === "update-available" && updateCheckResult.latestVersion
|
|
4418
|
+
? { latestVersion: updateCheckResult.latestVersion, currentVersion: updateCheckResult.currentVersion }
|
|
4419
|
+
: null
|
|
4420
|
+
}
|
|
4399
4421
|
panel={
|
|
4400
4422
|
<>
|
|
4401
4423
|
{screen === "backend-picker" && (
|
|
@@ -4644,6 +4666,16 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4644
4666
|
onCancel={handleImportCancel}
|
|
4645
4667
|
/>
|
|
4646
4668
|
)}
|
|
4669
|
+
|
|
4670
|
+
{screen === "update-prompt" && updateCheckResult?.status === "update-available" && updateCheckResult.latestVersion && (
|
|
4671
|
+
<UpdatePromptPanel
|
|
4672
|
+
focusId={FOCUS_IDS.updatePrompt}
|
|
4673
|
+
currentVersion={updateCheckResult.currentVersion}
|
|
4674
|
+
latestVersion={updateCheckResult.latestVersion}
|
|
4675
|
+
onSkip={handleSkipUpdateForSession}
|
|
4676
|
+
onSkipUntilNextVersion={handleSkipUpdateVersion}
|
|
4677
|
+
/>
|
|
4678
|
+
)}
|
|
4647
4679
|
</>
|
|
4648
4680
|
}
|
|
4649
4681
|
mainPanel={null}
|
|
@@ -4652,7 +4684,7 @@ export function App({ launchArgs }: AppProps) {
|
|
|
4652
4684
|
composerRows={composerRows}
|
|
4653
4685
|
panelHint={screen !== "main" && screen !== "model-picker" ? (
|
|
4654
4686
|
<Box marginTop={1} paddingX={1}>
|
|
4655
|
-
<Text color={activeTheme.
|
|
4687
|
+
<Text color={activeTheme.textDim}>Close the active panel with Esc to return to the composer.</Text>
|
|
4656
4688
|
</Box>
|
|
4657
4689
|
) : null}
|
|
4658
4690
|
/>
|
package/src/commands/handler.ts
CHANGED
|
@@ -398,10 +398,10 @@ function buildHelpMessage(context: CommandContext): string {
|
|
|
398
398
|
" /copy Copy last response to clipboard",
|
|
399
399
|
" /update [check] Show update status and instructions for updating Codexa",
|
|
400
400
|
" /help Show this help",
|
|
401
|
-
"",
|
|
402
|
-
"
|
|
403
|
-
" npm
|
|
404
|
-
"
|
|
401
|
+
"",
|
|
402
|
+
"Local development:",
|
|
403
|
+
" npm run install:dev-bin Install the codexa-dev command",
|
|
404
|
+
" codexa-dev Run this repo without replacing codexa",
|
|
405
405
|
"",
|
|
406
406
|
"Shortcuts:",
|
|
407
407
|
" Ctrl+B Open backend picker",
|
package/src/config/buildInfo.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated by scripts/gen-build-info.mjs — do not edit manually.
|
|
2
|
-
export const BUILD_COMMIT = "
|
|
3
|
-
export const
|
|
2
|
+
export const BUILD_COMMIT = "f4940aa066eae20c0b8decb1fb70f02aca0f2dc0" as const;
|
|
3
|
+
export const APP_VERSION = "1.0.3" as const;
|
|
@@ -50,6 +50,7 @@ export interface AuthSettings {
|
|
|
50
50
|
export interface UpdateCheckSettings {
|
|
51
51
|
enabled: boolean;
|
|
52
52
|
intervalHours: number;
|
|
53
|
+
skippedUpdateVersion?: string | null;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export const DEFAULT_UPDATE_CHECK_SETTINGS: UpdateCheckSettings = {
|
|
@@ -125,6 +126,7 @@ function normalizeUpdateCheckSettings(
|
|
|
125
126
|
intervalHours: typeof input?.intervalHours === "number" && input.intervalHours > 0
|
|
126
127
|
? input.intervalHours
|
|
127
128
|
: DEFAULT_UPDATE_CHECK_SETTINGS.intervalHours,
|
|
129
|
+
skippedUpdateVersion: input?.skippedUpdateVersion ?? null,
|
|
128
130
|
};
|
|
129
131
|
}
|
|
130
132
|
|
|
@@ -269,6 +271,11 @@ export function parseSettingsData(data: unknown): AppSettings {
|
|
|
269
271
|
: typeof updateCheckSource.interval_hours === "number"
|
|
270
272
|
? updateCheckSource.interval_hours
|
|
271
273
|
: undefined,
|
|
274
|
+
skippedUpdateVersion: typeof updateCheckSource.skippedUpdateVersion === "string"
|
|
275
|
+
? updateCheckSource.skippedUpdateVersion
|
|
276
|
+
: typeof updateCheckSource.skipped_update_version === "string"
|
|
277
|
+
? updateCheckSource.skipped_update_version
|
|
278
|
+
: null,
|
|
272
279
|
}),
|
|
273
280
|
};
|
|
274
281
|
}
|
|
@@ -299,6 +306,9 @@ export function serializeSettings(settings: AppSettings): Record<string, unknown
|
|
|
299
306
|
update_check: {
|
|
300
307
|
enabled: settings.updateCheck.enabled,
|
|
301
308
|
interval_hours: settings.updateCheck.intervalHours,
|
|
309
|
+
...(settings.updateCheck.skippedUpdateVersion != null
|
|
310
|
+
? { skipped_update_version: settings.updateCheck.skippedUpdateVersion }
|
|
311
|
+
: {}),
|
|
302
312
|
},
|
|
303
313
|
};
|
|
304
314
|
}
|
package/src/config/settings.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { homedir } from "os";
|
|
2
|
-
import { basename, join, parse, win32 } from "path";
|
|
3
|
-
import { BUILD_VERSION } from "./buildInfo.js";
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { basename, join, parse, win32 } from "path";
|
|
4
3
|
|
|
5
4
|
function isWindowsStylePath(p: string): boolean {
|
|
6
5
|
return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
|
|
@@ -10,14 +9,14 @@ function smartJoin(base: string, ...parts: string[]): string {
|
|
|
10
9
|
return isWindowsStylePath(base) ? win32.join(base, ...parts) : join(base, ...parts);
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export
|
|
14
|
-
export const
|
|
12
|
+
export { APP_VERSION } from "./buildInfo.js";
|
|
13
|
+
export const APP_NAME = "Codexa";
|
|
15
14
|
export const DEFAULT_BACKEND = "codex-subprocess";
|
|
16
15
|
export const DEFAULT_MODEL = "gpt-5.4";
|
|
17
16
|
export const DEFAULT_MODE = "full-auto";
|
|
18
17
|
export const DEFAULT_REASONING_LEVEL = "high";
|
|
19
18
|
export const DEFAULT_LAYOUT_STYLE = "gemini-shell";
|
|
20
|
-
export const DEFAULT_THEME = "
|
|
19
|
+
export const DEFAULT_THEME = "dark";
|
|
21
20
|
export const DEFAULT_WORKSPACE_DISPLAY_MODE = "dir";
|
|
22
21
|
export const DEFAULT_TERMINAL_TITLE_MODE = "dir";
|
|
23
22
|
export const DEFAULT_SHOW_BUSY_LOADER = true;
|
|
@@ -265,21 +264,14 @@ export function formatReasoningLabel(reasoning: string): string {
|
|
|
265
264
|
}
|
|
266
265
|
|
|
267
266
|
export const AVAILABLE_THEMES = [
|
|
267
|
+
{ id: "dark", label: "Codexa Dark" },
|
|
268
268
|
{ id: "purple", label: "Midnight Purple" },
|
|
269
269
|
{ id: "mono", label: "Black & White" },
|
|
270
|
-
{ id: "dark", label: "Modern Dark" },
|
|
271
270
|
{ id: "black", label: "Codex the Black" },
|
|
272
|
-
{ id: "emerald", label: "Emerald Night" },
|
|
273
|
-
{ id: "solar", label: "Solar Flare" },
|
|
274
|
-
{ id: "cyber", label: "Cyberpunk Neon" },
|
|
275
|
-
{ id: "ocean", label: "Deep Oceanic" },
|
|
276
271
|
{ id: "nordic", label: "Nordic Frost" },
|
|
277
|
-
{ id: "green", label: "Terminal Green" },
|
|
278
|
-
{ id: "amber", label: "Terminal Amber" },
|
|
279
|
-
{ id: "vaporwave", label: "Vaporwave Dream" },
|
|
280
272
|
{ id: "dracula", label: "Dracula Night" },
|
|
281
273
|
{ id: "gruvbox", label: "Gruvbox Hard" },
|
|
282
|
-
{ id: "
|
|
274
|
+
{ id: "ocean", label: "Deep Oceanic" },
|
|
283
275
|
{ id: "custom", label: "Customize..." },
|
|
284
276
|
] as const;
|
|
285
277
|
|
|
@@ -39,7 +39,25 @@ export function saveUpdateCheckCache(cache: UpdateCheckCache): void {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
function stripV(v: string): string {
|
|
43
|
+
return v.startsWith("v") ? v.slice(1) : v;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns true only when the cache is still usable:
|
|
48
|
+
* - `runningVersion` matches the version the cache was written for (version-mismatch = stale)
|
|
49
|
+
* - The cache was written within `intervalHours`
|
|
50
|
+
*/
|
|
51
|
+
export function isCacheValid(
|
|
52
|
+
cache: UpdateCheckCache,
|
|
53
|
+
intervalHours: number,
|
|
54
|
+
runningVersion?: string,
|
|
55
|
+
): boolean {
|
|
56
|
+
if (runningVersion !== undefined) {
|
|
57
|
+
if (stripV(cache.currentVersion) !== stripV(runningVersion)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
43
61
|
const maxAgeMs = intervalHours * 60 * 60 * 1000;
|
|
44
62
|
return Date.now() - cache.lastChecked < maxAgeMs;
|
|
45
63
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { APP_VERSION } from "../config/settings.js";
|
|
2
|
+
|
|
3
|
+
export const CODEXA_CHANNEL_ENV = "CODEXA_CHANNEL";
|
|
4
|
+
export const LOCAL_DEV_CHANNEL = "local-dev";
|
|
5
|
+
|
|
6
|
+
export function getCodexaChannel(env: NodeJS.ProcessEnv = process.env): string {
|
|
7
|
+
return env[CODEXA_CHANNEL_ENV]?.trim() || "published";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function isLocalDevChannel(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
11
|
+
return getCodexaChannel(env) === LOCAL_DEV_CHANNEL;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function formatCodexaVersionLabel(
|
|
15
|
+
version: string = APP_VERSION,
|
|
16
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
17
|
+
): string {
|
|
18
|
+
return isLocalDevChannel(env) ? `${version}-dev local` : version;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatCodexaBrandLabel(env: NodeJS.ProcessEnv = process.env): string {
|
|
22
|
+
return `Codexa v${formatCodexaVersionLabel(APP_VERSION, env)}`;
|
|
23
|
+
}
|
|
@@ -101,6 +101,7 @@ export function spawnCodexProcess(
|
|
|
101
101
|
options: SpawnOptions,
|
|
102
102
|
): ReturnType<typeof spawn> {
|
|
103
103
|
const spec = buildSpawnSpec(executable, args);
|
|
104
|
+
if (!spec.executable) throw new Error("Codex executable path is empty.");
|
|
104
105
|
return spawn(spec.executable, spec.args, { ...options, shell: false });
|
|
105
106
|
}
|
|
106
107
|
|