@golba98/codexa 1.0.1
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 +320 -0
- package/bin/codexa.js +445 -0
- package/package.json +45 -0
- package/scripts/audit-codexa-capabilities.mjs +466 -0
- package/scripts/smoke-terminal-bench.mjs +35 -0
- package/src/app.tsx +4561 -0
- package/src/appRenderStability.test.ts +131 -0
- package/src/commands/handler.test.ts +643 -0
- package/src/commands/handler.ts +875 -0
- package/src/config/launchArgs.test.ts +158 -0
- package/src/config/launchArgs.ts +186 -0
- package/src/config/layeredConfig.test.ts +143 -0
- package/src/config/layeredConfig.ts +836 -0
- package/src/config/persistence.test.ts +110 -0
- package/src/config/persistence.ts +311 -0
- package/src/config/runtimeConfig.test.ts +218 -0
- package/src/config/runtimeConfig.ts +554 -0
- package/src/config/settings.test.ts +155 -0
- package/src/config/settings.ts +401 -0
- package/src/config/toml-serialize.ts +98 -0
- package/src/config/trustStore.test.ts +29 -0
- package/src/config/trustStore.ts +68 -0
- package/src/core/attachments.test.ts +155 -0
- package/src/core/attachments.ts +71 -0
- package/src/core/auth/codexAuth.test.ts +68 -0
- package/src/core/auth/codexAuth.ts +359 -0
- package/src/core/cleanupFastFail.test.ts +76 -0
- package/src/core/cleanupFastFail.ts +67 -0
- package/src/core/clipboard.ts +24 -0
- package/src/core/codex.ts +124 -0
- package/src/core/codexExecArgs.test.ts +195 -0
- package/src/core/codexExecArgs.ts +152 -0
- package/src/core/codexLaunch.test.ts +205 -0
- package/src/core/codexLaunch.ts +162 -0
- package/src/core/codexPrompt.test.ts +252 -0
- package/src/core/codexPrompt.ts +428 -0
- package/src/core/executables/claudeExecutable.ts +63 -0
- package/src/core/executables/codexExecutable.test.ts +212 -0
- package/src/core/executables/codexExecutable.ts +159 -0
- package/src/core/executables/executableResolver.test.ts +129 -0
- package/src/core/executables/executableResolver.ts +138 -0
- package/src/core/executables/geminiExecutable.test.ts +116 -0
- package/src/core/executables/geminiExecutable.ts +78 -0
- package/src/core/executables/pathSanityScan.test.ts +47 -0
- package/src/core/githubDiagnostics.test.ts +92 -0
- package/src/core/githubDiagnostics.ts +222 -0
- package/src/core/hollowResponseFormat.test.ts +58 -0
- package/src/core/hollowResponseFormat.ts +39 -0
- package/src/core/inputDebug.ts +51 -0
- package/src/core/launchContext.test.ts +157 -0
- package/src/core/launchContext.ts +266 -0
- package/src/core/models/codexCapabilities.test.ts +45 -0
- package/src/core/models/codexCapabilities.ts +95 -0
- package/src/core/models/codexModelCapabilities.test.ts +246 -0
- package/src/core/models/codexModelCapabilities.ts +571 -0
- package/src/core/models/modelSpecs.test.ts +283 -0
- package/src/core/models/modelSpecs.ts +300 -0
- package/src/core/perf/profiler.ts +125 -0
- package/src/core/perf/renderDebug.test.ts +230 -0
- package/src/core/perf/renderDebug.ts +373 -0
- package/src/core/planStorage.test.ts +143 -0
- package/src/core/planStorage.ts +141 -0
- package/src/core/process/CommandRunner.test.ts +105 -0
- package/src/core/process/CommandRunner.ts +269 -0
- package/src/core/process/processValidation.ts +101 -0
- package/src/core/projectInstructions.test.ts +50 -0
- package/src/core/projectInstructions.ts +54 -0
- package/src/core/providerLauncher/launcher.test.ts +238 -0
- package/src/core/providerLauncher/launcher.ts +203 -0
- package/src/core/providerLauncher/registry.test.ts +324 -0
- package/src/core/providerLauncher/registry.ts +253 -0
- package/src/core/providerLauncher/types.ts +84 -0
- package/src/core/providerLauncher/workspaceConfig.test.ts +638 -0
- package/src/core/providerLauncher/workspaceConfig.ts +407 -0
- package/src/core/providerRuntime/anthropic.test.ts +1120 -0
- package/src/core/providerRuntime/anthropic.ts +576 -0
- package/src/core/providerRuntime/capabilityProfile.test.ts +311 -0
- package/src/core/providerRuntime/capabilityProfile.ts +288 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +446 -0
- package/src/core/providerRuntime/contextMetadata.test.ts +468 -0
- package/src/core/providerRuntime/contextMetadata.ts +409 -0
- package/src/core/providerRuntime/gemini.test.ts +437 -0
- package/src/core/providerRuntime/gemini.ts +784 -0
- package/src/core/providerRuntime/lmstudio.test.ts +168 -0
- package/src/core/providerRuntime/lmstudio.ts +118 -0
- package/src/core/providerRuntime/local.test.ts +787 -0
- package/src/core/providerRuntime/local.ts +754 -0
- package/src/core/providerRuntime/models.ts +150 -0
- package/src/core/providerRuntime/reasoning.ts +17 -0
- package/src/core/providerRuntime/registry.test.ts +233 -0
- package/src/core/providerRuntime/registry.ts +203 -0
- package/src/core/providerRuntime/types.ts +103 -0
- package/src/core/providers/codexJsonStream.test.ts +148 -0
- package/src/core/providers/codexJsonStream.ts +305 -0
- package/src/core/providers/codexSubprocess.test.ts +68 -0
- package/src/core/providers/codexSubprocess.ts +372 -0
- package/src/core/providers/codexTranscript.test.ts +284 -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 +59 -0
- package/src/core/terminal/terminalCapabilities.test.ts +93 -0
- package/src/core/terminal/terminalCapabilities.ts +100 -0
- package/src/core/terminal/terminalControl.test.ts +75 -0
- package/src/core/terminal/terminalControl.ts +147 -0
- package/src/core/terminal/terminalSanitize.test.ts +22 -0
- package/src/core/terminal/terminalSanitize.ts +147 -0
- package/src/core/terminal/terminalSelection.test.ts +42 -0
- package/src/core/terminal/terminalSelection.ts +66 -0
- package/src/core/terminal/terminalTitle.test.ts +328 -0
- package/src/core/terminal/terminalTitle.ts +483 -0
- package/src/core/workspaceActivity.test.ts +163 -0
- package/src/core/workspaceActivity.ts +380 -0
- package/src/core/workspaceGuard.test.ts +151 -0
- package/src/core/workspaceGuard.ts +288 -0
- package/src/core/workspaceRoot.test.ts +23 -0
- package/src/core/workspaceRoot.ts +47 -0
- package/src/exec.test.ts +13 -0
- package/src/exec.ts +72 -0
- package/src/headless/execArgs.test.ts +147 -0
- package/src/headless/execArgs.ts +294 -0
- package/src/headless/execRunner.test.ts +434 -0
- package/src/headless/execRunner.ts +304 -0
- package/src/index.test.tsx +618 -0
- package/src/index.tsx +296 -0
- package/src/session/appSession.test.ts +897 -0
- package/src/session/appSession.ts +761 -0
- package/src/session/chatLifecycle.test.ts +64 -0
- package/src/session/chatLifecycle.ts +951 -0
- package/src/session/liveRenderScheduler.test.ts +201 -0
- package/src/session/liveRenderScheduler.ts +214 -0
- package/src/session/planFlow.test.ts +103 -0
- package/src/session/planFlow.ts +149 -0
- package/src/session/planTranscript.test.ts +65 -0
- package/src/session/planTranscript.ts +15 -0
- package/src/session/promptRunSchedule.test.ts +36 -0
- package/src/session/promptRunSchedule.ts +26 -0
- package/src/session/types.ts +228 -0
- package/src/test/runtimeTestUtils.ts +14 -0
- package/src/types/react-dom.d.ts +3 -0
- package/src/ui/ActionRequiredBlock.tsx +38 -0
- package/src/ui/ActivityBars.tsx +68 -0
- package/src/ui/ActivityIndicator.test.tsx +58 -0
- package/src/ui/ActivityIndicator.tsx +58 -0
- package/src/ui/AgentBlock.test.ts +6 -0
- package/src/ui/AgentBlock.tsx +130 -0
- package/src/ui/AnimatedStatusText.test.ts +16 -0
- package/src/ui/AnimatedStatusText.tsx +69 -0
- package/src/ui/AppShell.test.tsx +1739 -0
- package/src/ui/AppShell.tsx +698 -0
- package/src/ui/AttachmentImportPanel.test.tsx +204 -0
- package/src/ui/AttachmentImportPanel.tsx +98 -0
- package/src/ui/AuthPanel.tsx +113 -0
- package/src/ui/BackendPicker.tsx +28 -0
- package/src/ui/BottomComposer.test.ts +674 -0
- package/src/ui/BottomComposer.tsx +1028 -0
- package/src/ui/CodexLogo.tsx +55 -0
- package/src/ui/DashCard.tsx +82 -0
- package/src/ui/Markdown.test.ts +157 -0
- package/src/ui/Markdown.tsx +310 -0
- package/src/ui/ModePicker.tsx +27 -0
- package/src/ui/ModelPicker.tsx +31 -0
- package/src/ui/ModelPickerProviderScope.test.tsx +411 -0
- package/src/ui/ModelPickerScreen.test.tsx +99 -0
- package/src/ui/ModelPickerScreen.tsx +416 -0
- package/src/ui/ModelPickerState.test.tsx +151 -0
- package/src/ui/ModelReasoningPicker.test.tsx +447 -0
- package/src/ui/ModelReasoningPicker.tsx +458 -0
- package/src/ui/Panel.tsx +51 -0
- package/src/ui/PermissionsPanel.tsx +78 -0
- package/src/ui/PlanActionPicker.tsx +119 -0
- package/src/ui/PlanReviewPanel.test.tsx +267 -0
- package/src/ui/PlanReviewPanel.tsx +212 -0
- package/src/ui/PromptCardBorder.test.tsx +161 -0
- package/src/ui/ProviderPicker.test.tsx +289 -0
- package/src/ui/ProviderPicker.tsx +321 -0
- package/src/ui/ProviderShortcut.test.tsx +143 -0
- package/src/ui/ReasoningPicker.tsx +46 -0
- package/src/ui/RunFooter.tsx +65 -0
- package/src/ui/SelectionPanel.tsx +67 -0
- package/src/ui/SettingsPanel.test.tsx +233 -0
- package/src/ui/SettingsPanel.tsx +156 -0
- package/src/ui/Spinner.tsx +25 -0
- package/src/ui/StaticIntroItem.tsx +54 -0
- package/src/ui/StaticTranscriptItem.tsx +56 -0
- package/src/ui/TextEntryPanel.tsx +139 -0
- package/src/ui/ThemePicker.tsx +31 -0
- package/src/ui/ThinkingBlock.tsx +100 -0
- package/src/ui/Timeline.test.ts +2067 -0
- package/src/ui/Timeline.tsx +1472 -0
- package/src/ui/TimelineNavigation.test.tsx +201 -0
- package/src/ui/TopHeader.test.tsx +239 -0
- package/src/ui/TopHeader.tsx +257 -0
- package/src/ui/TurnGroup.test.tsx +365 -0
- package/src/ui/TurnGroup.tsx +657 -0
- package/src/ui/busyStatusAnimation.test.ts +30 -0
- package/src/ui/busyStatusAnimation.ts +11 -0
- package/src/ui/commandNormalize.test.ts +142 -0
- package/src/ui/commandNormalize.ts +66 -0
- package/src/ui/diffRenderer.test.ts +102 -0
- package/src/ui/diffRenderer.ts +116 -0
- package/src/ui/focus.ts +61 -0
- package/src/ui/focusFlow.test.tsx +1098 -0
- package/src/ui/inputBuffer.test.ts +151 -0
- package/src/ui/inputBuffer.ts +203 -0
- package/src/ui/layout.test.ts +145 -0
- package/src/ui/layout.ts +287 -0
- package/src/ui/modeDisplay.test.ts +42 -0
- package/src/ui/modeDisplay.ts +52 -0
- package/src/ui/outputPipeline.ts +64 -0
- package/src/ui/progressEntries.ts +156 -0
- package/src/ui/runActivityView.test.ts +89 -0
- package/src/ui/runActivityView.ts +37 -0
- package/src/ui/runLifecycleView.test.tsx +237 -0
- package/src/ui/slashCommands.ts +41 -0
- package/src/ui/statusRenderIsolation.test.tsx +654 -0
- package/src/ui/terminalAnswerFormat.test.ts +19 -0
- package/src/ui/terminalAnswerFormat.ts +128 -0
- package/src/ui/textLayout.test.ts +18 -0
- package/src/ui/textLayout.ts +338 -0
- package/src/ui/theme.tsx +395 -0
- package/src/ui/themeFlow.test.ts +53 -0
- package/src/ui/themeFlow.ts +41 -0
- package/src/ui/timelineMeasure.ts +3088 -0
- package/src/ui/timelineMeasureCache.test.ts +986 -0
- package/src/ui/useThrottledValue.ts +31 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { join, posix, win32 } from "path";
|
|
2
|
+
import {
|
|
3
|
+
AVAILABLE_BACKENDS,
|
|
4
|
+
AVAILABLE_MODES,
|
|
5
|
+
DEFAULT_BACKEND,
|
|
6
|
+
DEFAULT_MODEL,
|
|
7
|
+
DEFAULT_MODE,
|
|
8
|
+
DEFAULT_REASONING_LEVEL,
|
|
9
|
+
formatBackendLabel,
|
|
10
|
+
formatModeLabel,
|
|
11
|
+
formatReasoningLabel,
|
|
12
|
+
normalizeReasoningForModel,
|
|
13
|
+
type AvailableBackend,
|
|
14
|
+
type AvailableMode,
|
|
15
|
+
type AvailableModel,
|
|
16
|
+
type ReasoningLevel,
|
|
17
|
+
} from "./settings.js";
|
|
18
|
+
|
|
19
|
+
export const AVAILABLE_APPROVAL_POLICIES = [
|
|
20
|
+
{ id: "inherit", label: "Inherit" },
|
|
21
|
+
{ id: "untrusted", label: "Untrusted" },
|
|
22
|
+
{ id: "on-request", label: "On request" },
|
|
23
|
+
{ id: "never", label: "Never" },
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export const AVAILABLE_SANDBOX_MODES = [
|
|
27
|
+
{ id: "inherit", label: "Inherit" },
|
|
28
|
+
{ id: "read-only", label: "Read only" },
|
|
29
|
+
{ id: "workspace-write", label: "Workspace write" },
|
|
30
|
+
{ id: "danger-full-access", label: "Danger full access" },
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export const AVAILABLE_NETWORK_ACCESS_VALUES = [
|
|
34
|
+
{ id: "inherit", label: "Inherit" },
|
|
35
|
+
{ id: "enabled", label: "Enabled" },
|
|
36
|
+
{ id: "disabled", label: "Disabled" },
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
export const AVAILABLE_SERVICE_TIERS = [
|
|
40
|
+
{ id: "flex", label: "Flex" },
|
|
41
|
+
{ id: "fast", label: "Fast" },
|
|
42
|
+
] as const;
|
|
43
|
+
|
|
44
|
+
export const AVAILABLE_PERSONALITIES = [
|
|
45
|
+
{ id: "none", label: "None" },
|
|
46
|
+
{ id: "friendly", label: "Friendly" },
|
|
47
|
+
{ id: "pragmatic", label: "Pragmatic" },
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
export type RuntimeApprovalPolicy = (typeof AVAILABLE_APPROVAL_POLICIES)[number]["id"];
|
|
51
|
+
export type ResolvedApprovalPolicy = Exclude<RuntimeApprovalPolicy, "inherit">;
|
|
52
|
+
export type RuntimeSandboxMode = (typeof AVAILABLE_SANDBOX_MODES)[number]["id"];
|
|
53
|
+
export type ResolvedSandboxMode = Exclude<RuntimeSandboxMode, "inherit">;
|
|
54
|
+
export type RuntimeNetworkAccess = (typeof AVAILABLE_NETWORK_ACCESS_VALUES)[number]["id"];
|
|
55
|
+
export type RuntimeServiceTier = (typeof AVAILABLE_SERVICE_TIERS)[number]["id"];
|
|
56
|
+
export type RuntimePersonality = (typeof AVAILABLE_PERSONALITIES)[number]["id"];
|
|
57
|
+
|
|
58
|
+
export interface RuntimePolicyConfig {
|
|
59
|
+
approvalPolicy: RuntimeApprovalPolicy;
|
|
60
|
+
sandboxMode: RuntimeSandboxMode;
|
|
61
|
+
networkAccess: RuntimeNetworkAccess;
|
|
62
|
+
writableRoots: string[];
|
|
63
|
+
serviceTier: RuntimeServiceTier;
|
|
64
|
+
personality: RuntimePersonality;
|
|
65
|
+
allowExternalFileImport: boolean;
|
|
66
|
+
attachmentDir: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RuntimeConfig {
|
|
70
|
+
provider: AvailableBackend;
|
|
71
|
+
model: AvailableModel;
|
|
72
|
+
reasoningLevel: ReasoningLevel;
|
|
73
|
+
mode: AvailableMode;
|
|
74
|
+
planMode: boolean;
|
|
75
|
+
geminiCommandPath?: string;
|
|
76
|
+
codexCommandPath?: string;
|
|
77
|
+
policy: RuntimePolicyConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface PartialRuntimeConfig extends Partial<Omit<RuntimeConfig, "policy">> {
|
|
81
|
+
policy?: Partial<RuntimePolicyConfig>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ResolvedRuntimePolicy {
|
|
85
|
+
approvalPolicy: ResolvedApprovalPolicy;
|
|
86
|
+
sandboxMode: ResolvedSandboxMode;
|
|
87
|
+
networkAccess: boolean;
|
|
88
|
+
writableRoots: string[];
|
|
89
|
+
serviceTier: RuntimeServiceTier;
|
|
90
|
+
personality: RuntimePersonality;
|
|
91
|
+
allowExternalFileImport: boolean;
|
|
92
|
+
attachmentDir: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ResolvedRuntimeConfig {
|
|
96
|
+
provider: AvailableBackend;
|
|
97
|
+
model: AvailableModel;
|
|
98
|
+
reasoningLevel: ReasoningLevel;
|
|
99
|
+
mode: AvailableMode;
|
|
100
|
+
planMode: boolean;
|
|
101
|
+
geminiCommandPath?: string;
|
|
102
|
+
codexCommandPath?: string;
|
|
103
|
+
policy: ResolvedRuntimePolicy;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface RuntimeStatusContext {
|
|
107
|
+
workspaceRoot: string;
|
|
108
|
+
tokensUsed?: number | null;
|
|
109
|
+
projectInstructions?: import("../core/projectInstructions.js").ProjectInstructionsLoadResult | null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface RuntimeSummary {
|
|
113
|
+
model: AvailableModel;
|
|
114
|
+
reasoningLabel: string;
|
|
115
|
+
modeLabel: string;
|
|
116
|
+
sandboxLabel: string;
|
|
117
|
+
approvalLabel: string;
|
|
118
|
+
networkLabel: string;
|
|
119
|
+
writableRootsLabel: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const DEFAULT_RUNTIME_POLICY: RuntimePolicyConfig = {
|
|
123
|
+
approvalPolicy: "inherit",
|
|
124
|
+
sandboxMode: "inherit",
|
|
125
|
+
networkAccess: "inherit",
|
|
126
|
+
writableRoots: [],
|
|
127
|
+
serviceTier: "flex",
|
|
128
|
+
personality: "none",
|
|
129
|
+
allowExternalFileImport: true,
|
|
130
|
+
attachmentDir: ".codexa/attachments",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const DEFAULT_RUNTIME_CONFIG: RuntimeConfig = {
|
|
134
|
+
provider: DEFAULT_BACKEND,
|
|
135
|
+
model: DEFAULT_MODEL,
|
|
136
|
+
reasoningLevel: normalizeReasoningForModel(DEFAULT_MODEL, DEFAULT_REASONING_LEVEL),
|
|
137
|
+
mode: DEFAULT_MODE,
|
|
138
|
+
planMode: false,
|
|
139
|
+
policy: DEFAULT_RUNTIME_POLICY,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
function detectPathApi(value: string): typeof win32 | typeof posix {
|
|
143
|
+
// Alternatives: drive-letter (C:\ or C:/), UNC (\\server), or any forward-slash (Unix).
|
|
144
|
+
// The forward-slash alternative is intentionally unanchored to catch mixed-separator paths.
|
|
145
|
+
return /^[a-zA-Z]:[\\/]|^\\\\|\\/.test(value) ? win32 : posix;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function toPlatformKey(path: string): string {
|
|
149
|
+
const isWindows = process.platform === "win32"
|
|
150
|
+
|| /^[A-Za-z]:[\\/]/.test(path)
|
|
151
|
+
|| /^\\\\/.test(path);
|
|
152
|
+
return isWindows ? path.toLowerCase() : path;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function stripTrailingSeparators(value: string, pathApi: typeof win32 | typeof posix): string {
|
|
156
|
+
const parsed = pathApi.parse(value);
|
|
157
|
+
if (value === parsed.root) {
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return value.replace(/[\\/]+$/, "");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function normalizePathValue(value: string): string {
|
|
165
|
+
const trimmed = value.trim();
|
|
166
|
+
if (!trimmed) return "";
|
|
167
|
+
const pathApi = detectPathApi(trimmed);
|
|
168
|
+
return stripTrailingSeparators(pathApi.normalize(trimmed), pathApi);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function dedupeWritableRoots(values: readonly string[]): string[] {
|
|
172
|
+
const seen = new Set<string>();
|
|
173
|
+
const normalized: string[] = [];
|
|
174
|
+
|
|
175
|
+
for (const value of values) {
|
|
176
|
+
if (typeof value !== "string") continue;
|
|
177
|
+
const trimmed = normalizePathValue(value);
|
|
178
|
+
if (!trimmed) continue;
|
|
179
|
+
const key = toPlatformKey(trimmed);
|
|
180
|
+
if (seen.has(key)) continue;
|
|
181
|
+
seen.add(key);
|
|
182
|
+
normalized.push(trimmed);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return normalized;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function isAvailableId<T extends string>(
|
|
189
|
+
values: readonly { id: T }[],
|
|
190
|
+
candidate: unknown,
|
|
191
|
+
): candidate is T {
|
|
192
|
+
return typeof candidate === "string" && values.some((value) => value.id === candidate);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizeRuntimeString(candidate: unknown, fallback: string): string {
|
|
196
|
+
return typeof candidate === "string" && candidate.trim().length > 0
|
|
197
|
+
? candidate.trim()
|
|
198
|
+
: fallback;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function normalizeRuntimePolicy(input: Partial<RuntimePolicyConfig> | null | undefined): RuntimePolicyConfig {
|
|
202
|
+
return {
|
|
203
|
+
approvalPolicy: isAvailableId(AVAILABLE_APPROVAL_POLICIES, input?.approvalPolicy)
|
|
204
|
+
? input!.approvalPolicy
|
|
205
|
+
: DEFAULT_RUNTIME_POLICY.approvalPolicy,
|
|
206
|
+
sandboxMode: isAvailableId(AVAILABLE_SANDBOX_MODES, input?.sandboxMode)
|
|
207
|
+
? input!.sandboxMode
|
|
208
|
+
: DEFAULT_RUNTIME_POLICY.sandboxMode,
|
|
209
|
+
networkAccess: isAvailableId(AVAILABLE_NETWORK_ACCESS_VALUES, input?.networkAccess)
|
|
210
|
+
? input!.networkAccess
|
|
211
|
+
: DEFAULT_RUNTIME_POLICY.networkAccess,
|
|
212
|
+
writableRoots: dedupeWritableRoots(input?.writableRoots ?? DEFAULT_RUNTIME_POLICY.writableRoots),
|
|
213
|
+
serviceTier: isAvailableId(AVAILABLE_SERVICE_TIERS, input?.serviceTier)
|
|
214
|
+
? input!.serviceTier
|
|
215
|
+
: DEFAULT_RUNTIME_POLICY.serviceTier,
|
|
216
|
+
personality: isAvailableId(AVAILABLE_PERSONALITIES, input?.personality)
|
|
217
|
+
? input!.personality
|
|
218
|
+
: DEFAULT_RUNTIME_POLICY.personality,
|
|
219
|
+
allowExternalFileImport: typeof input?.allowExternalFileImport === "boolean"
|
|
220
|
+
? input.allowExternalFileImport
|
|
221
|
+
: DEFAULT_RUNTIME_POLICY.allowExternalFileImport,
|
|
222
|
+
attachmentDir: normalizeRuntimeString(input?.attachmentDir, DEFAULT_RUNTIME_POLICY.attachmentDir),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function normalizeRuntimeConfig(input: PartialRuntimeConfig | null | undefined): RuntimeConfig {
|
|
227
|
+
const provider = isAvailableId(AVAILABLE_BACKENDS, input?.provider)
|
|
228
|
+
? input!.provider
|
|
229
|
+
: DEFAULT_RUNTIME_CONFIG.provider;
|
|
230
|
+
const model = normalizeRuntimeString(input?.model, DEFAULT_RUNTIME_CONFIG.model);
|
|
231
|
+
const mode = isAvailableId(AVAILABLE_MODES.map((item) => ({ id: item.key })), input?.mode)
|
|
232
|
+
? input!.mode
|
|
233
|
+
: DEFAULT_RUNTIME_CONFIG.mode;
|
|
234
|
+
const reasoningInput = normalizeRuntimeString(input?.reasoningLevel, DEFAULT_RUNTIME_CONFIG.reasoningLevel);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
provider,
|
|
238
|
+
model,
|
|
239
|
+
mode,
|
|
240
|
+
planMode: typeof input?.planMode === "boolean" ? input.planMode : DEFAULT_RUNTIME_CONFIG.planMode,
|
|
241
|
+
...(typeof input?.geminiCommandPath === "string" && input.geminiCommandPath.trim()
|
|
242
|
+
? { geminiCommandPath: input.geminiCommandPath.trim() }
|
|
243
|
+
: {}),
|
|
244
|
+
reasoningLevel: normalizeReasoningForModel(model, reasoningInput),
|
|
245
|
+
policy: normalizeRuntimePolicy(input?.policy),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function mergeRuntimeConfig(
|
|
250
|
+
base: RuntimeConfig,
|
|
251
|
+
override: PartialRuntimeConfig | null | undefined,
|
|
252
|
+
): RuntimeConfig {
|
|
253
|
+
if (!override) {
|
|
254
|
+
return normalizeRuntimeConfig(base);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return normalizeRuntimeConfig({
|
|
258
|
+
...base,
|
|
259
|
+
...override,
|
|
260
|
+
policy: {
|
|
261
|
+
...base.policy,
|
|
262
|
+
...override.policy,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function arraysEqual(left: readonly string[], right: readonly string[]): boolean {
|
|
268
|
+
if (left.length !== right.length) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return left.every((value, index) => value === right[index]);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function diffRuntimeConfig(base: RuntimeConfig, target: RuntimeConfig): PartialRuntimeConfig {
|
|
276
|
+
const normalizedBase = normalizeRuntimeConfig(base);
|
|
277
|
+
const normalizedTarget = normalizeRuntimeConfig(target);
|
|
278
|
+
const policyPatch: Partial<RuntimePolicyConfig> = {};
|
|
279
|
+
|
|
280
|
+
if (normalizedBase.policy.approvalPolicy !== normalizedTarget.policy.approvalPolicy) {
|
|
281
|
+
policyPatch.approvalPolicy = normalizedTarget.policy.approvalPolicy;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (normalizedBase.policy.sandboxMode !== normalizedTarget.policy.sandboxMode) {
|
|
285
|
+
policyPatch.sandboxMode = normalizedTarget.policy.sandboxMode;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (normalizedBase.policy.networkAccess !== normalizedTarget.policy.networkAccess) {
|
|
289
|
+
policyPatch.networkAccess = normalizedTarget.policy.networkAccess;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!arraysEqual(normalizedBase.policy.writableRoots, normalizedTarget.policy.writableRoots)) {
|
|
293
|
+
policyPatch.writableRoots = normalizedTarget.policy.writableRoots;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (normalizedBase.policy.serviceTier !== normalizedTarget.policy.serviceTier) {
|
|
297
|
+
policyPatch.serviceTier = normalizedTarget.policy.serviceTier;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (normalizedBase.policy.personality !== normalizedTarget.policy.personality) {
|
|
301
|
+
policyPatch.personality = normalizedTarget.policy.personality;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
...(normalizedBase.provider !== normalizedTarget.provider
|
|
306
|
+
? { provider: normalizedTarget.provider }
|
|
307
|
+
: {}),
|
|
308
|
+
...(normalizedBase.model !== normalizedTarget.model
|
|
309
|
+
? { model: normalizedTarget.model }
|
|
310
|
+
: {}),
|
|
311
|
+
...(normalizedBase.reasoningLevel !== normalizedTarget.reasoningLevel
|
|
312
|
+
? { reasoningLevel: normalizedTarget.reasoningLevel }
|
|
313
|
+
: {}),
|
|
314
|
+
...(normalizedBase.mode !== normalizedTarget.mode
|
|
315
|
+
? { mode: normalizedTarget.mode }
|
|
316
|
+
: {}),
|
|
317
|
+
...(normalizedBase.planMode !== normalizedTarget.planMode
|
|
318
|
+
? { planMode: normalizedTarget.planMode }
|
|
319
|
+
: {}),
|
|
320
|
+
...(normalizedBase.geminiCommandPath !== normalizedTarget.geminiCommandPath
|
|
321
|
+
? { geminiCommandPath: normalizedTarget.geminiCommandPath }
|
|
322
|
+
: {}),
|
|
323
|
+
...(Object.keys(policyPatch).length > 0 ? { policy: policyPatch } : {}),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function resolveInheritedApprovalPolicy(mode: AvailableMode): ResolvedApprovalPolicy {
|
|
328
|
+
switch (mode) {
|
|
329
|
+
case "suggest":
|
|
330
|
+
case "auto-edit":
|
|
331
|
+
case "full-auto":
|
|
332
|
+
default:
|
|
333
|
+
return "on-request";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function resolveInheritedSandboxMode(mode: AvailableMode): ResolvedSandboxMode {
|
|
338
|
+
switch (mode) {
|
|
339
|
+
case "suggest":
|
|
340
|
+
return "read-only";
|
|
341
|
+
case "auto-edit":
|
|
342
|
+
case "full-auto":
|
|
343
|
+
default:
|
|
344
|
+
return "workspace-write";
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function resolveRuntimeConfig(config: RuntimeConfig): ResolvedRuntimeConfig {
|
|
349
|
+
const normalized = normalizeRuntimeConfig(config);
|
|
350
|
+
const sandboxMode = normalized.policy.sandboxMode === "inherit"
|
|
351
|
+
? resolveInheritedSandboxMode(normalized.mode)
|
|
352
|
+
: normalized.policy.sandboxMode;
|
|
353
|
+
const approvalPolicy = normalized.policy.approvalPolicy === "inherit"
|
|
354
|
+
? resolveInheritedApprovalPolicy(normalized.mode)
|
|
355
|
+
: normalized.policy.approvalPolicy;
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
provider: normalized.provider,
|
|
359
|
+
model: normalized.model,
|
|
360
|
+
mode: normalized.mode,
|
|
361
|
+
planMode: normalized.planMode,
|
|
362
|
+
...(normalized.geminiCommandPath ? { geminiCommandPath: normalized.geminiCommandPath } : {}),
|
|
363
|
+
reasoningLevel: normalizeReasoningForModel(normalized.model, normalized.reasoningLevel),
|
|
364
|
+
policy: {
|
|
365
|
+
approvalPolicy,
|
|
366
|
+
sandboxMode,
|
|
367
|
+
networkAccess: normalized.policy.networkAccess === "enabled",
|
|
368
|
+
writableRoots: dedupeWritableRoots(normalized.policy.writableRoots),
|
|
369
|
+
serviceTier: normalized.policy.serviceTier,
|
|
370
|
+
personality: normalized.policy.personality,
|
|
371
|
+
allowExternalFileImport: normalized.policy.allowExternalFileImport,
|
|
372
|
+
attachmentDir: normalized.policy.attachmentDir,
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function addWritableRoot(config: RuntimeConfig, root: string): RuntimeConfig {
|
|
378
|
+
const nextRoots = dedupeWritableRoots([...config.policy.writableRoots, root]);
|
|
379
|
+
return {
|
|
380
|
+
...config,
|
|
381
|
+
policy: {
|
|
382
|
+
...config.policy,
|
|
383
|
+
writableRoots: nextRoots,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function removeWritableRoot(config: RuntimeConfig, root: string): RuntimeConfig {
|
|
389
|
+
const target = normalizePathValue(root);
|
|
390
|
+
const targetKey = toPlatformKey(target);
|
|
391
|
+
return {
|
|
392
|
+
...config,
|
|
393
|
+
policy: {
|
|
394
|
+
...config.policy,
|
|
395
|
+
writableRoots: config.policy.writableRoots.filter((value) => {
|
|
396
|
+
const key = toPlatformKey(normalizePathValue(value));
|
|
397
|
+
return key !== targetKey;
|
|
398
|
+
}),
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function clearWritableRoots(config: RuntimeConfig): RuntimeConfig {
|
|
404
|
+
return {
|
|
405
|
+
...config,
|
|
406
|
+
policy: {
|
|
407
|
+
...config.policy,
|
|
408
|
+
writableRoots: [],
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function labelForId(items: ReadonlyArray<{ id: string; label: string }>, id: string): string {
|
|
414
|
+
return items.find((item) => item.id === id)?.label ?? id;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function formatApprovalPolicyLabel(value: RuntimeApprovalPolicy | ResolvedApprovalPolicy): string {
|
|
418
|
+
return labelForId(AVAILABLE_APPROVAL_POLICIES, value);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export function formatSandboxModeLabel(value: RuntimeSandboxMode | ResolvedSandboxMode): string {
|
|
422
|
+
return labelForId(AVAILABLE_SANDBOX_MODES, value);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function formatNetworkAccessLabel(value: RuntimeNetworkAccess | boolean): string {
|
|
426
|
+
if (typeof value === "boolean") {
|
|
427
|
+
return value ? "Enabled" : "Disabled";
|
|
428
|
+
}
|
|
429
|
+
return labelForId(AVAILABLE_NETWORK_ACCESS_VALUES, value);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function formatServiceTierLabel(value: RuntimeServiceTier): string {
|
|
433
|
+
return labelForId(AVAILABLE_SERVICE_TIERS, value);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function formatPersonalityLabel(value: RuntimePersonality): string {
|
|
437
|
+
return labelForId(AVAILABLE_PERSONALITIES, value);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function buildRuntimeSummary(runtime: ResolvedRuntimeConfig): RuntimeSummary {
|
|
441
|
+
return {
|
|
442
|
+
model: runtime.model,
|
|
443
|
+
reasoningLabel: formatReasoningLabel(runtime.reasoningLevel),
|
|
444
|
+
modeLabel: formatModeLabel(runtime.mode),
|
|
445
|
+
sandboxLabel: formatSandboxModeLabel(runtime.policy.sandboxMode),
|
|
446
|
+
approvalLabel: formatApprovalPolicyLabel(runtime.policy.approvalPolicy),
|
|
447
|
+
networkLabel: runtime.policy.networkAccess ? "Net: on" : "Net: off",
|
|
448
|
+
writableRootsLabel: `Roots: ${runtime.policy.writableRoots.length}`,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function formatWritableRootsBlock(roots: readonly string[]): string {
|
|
453
|
+
return roots.length > 0
|
|
454
|
+
? roots.map((value) => ` - ${value}`).join("\n")
|
|
455
|
+
: " - none";
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export function formatPermissionsStatus(
|
|
459
|
+
runtime: RuntimeConfig,
|
|
460
|
+
resolvedRuntime: ResolvedRuntimeConfig,
|
|
461
|
+
workspaceRoot: string,
|
|
462
|
+
): string {
|
|
463
|
+
const configuredRoots = formatWritableRootsBlock(runtime.policy.writableRoots);
|
|
464
|
+
const effectiveRoots = formatWritableRootsBlock(resolvedRuntime.policy.writableRoots);
|
|
465
|
+
|
|
466
|
+
return [
|
|
467
|
+
"Permissions status:",
|
|
468
|
+
` Approval policy: configured ${formatApprovalPolicyLabel(runtime.policy.approvalPolicy)}; effective ${formatApprovalPolicyLabel(resolvedRuntime.policy.approvalPolicy)}`,
|
|
469
|
+
` Sandbox mode: configured ${formatSandboxModeLabel(runtime.policy.sandboxMode)}; effective ${formatSandboxModeLabel(resolvedRuntime.policy.sandboxMode)}`,
|
|
470
|
+
` Network access: configured ${formatNetworkAccessLabel(runtime.policy.networkAccess)}; effective ${formatNetworkAccessLabel(resolvedRuntime.policy.networkAccess)}`,
|
|
471
|
+
` Workspace root: ${workspaceRoot}`,
|
|
472
|
+
" Writable roots:",
|
|
473
|
+
" Configured:",
|
|
474
|
+
configuredRoots,
|
|
475
|
+
" Effective:",
|
|
476
|
+
effectiveRoots,
|
|
477
|
+
].join("\n");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export function formatRuntimeStatus(runtime: ResolvedRuntimeConfig, context: RuntimeStatusContext): string {
|
|
481
|
+
const writableRoots = runtime.policy.writableRoots.length > 0
|
|
482
|
+
? runtime.policy.writableRoots.map((value) => ` - ${value}`).join("\n")
|
|
483
|
+
: " - none";
|
|
484
|
+
|
|
485
|
+
const lines = [
|
|
486
|
+
"Runtime status:",
|
|
487
|
+
` Provider: ${formatBackendLabel(runtime.provider)}`,
|
|
488
|
+
` Model: ${runtime.model}`,
|
|
489
|
+
` Reasoning: ${formatReasoningLabel(runtime.reasoningLevel)}`,
|
|
490
|
+
` Mode: ${formatModeLabel(runtime.mode)}`,
|
|
491
|
+
` Plan mode: ${runtime.planMode ? "Enabled" : "Disabled"}`,
|
|
492
|
+
` Approval policy: ${formatApprovalPolicyLabel(runtime.policy.approvalPolicy)}`,
|
|
493
|
+
` Sandbox mode: ${formatSandboxModeLabel(runtime.policy.sandboxMode)}`,
|
|
494
|
+
` Network access: ${formatNetworkAccessLabel(runtime.policy.networkAccess)}`,
|
|
495
|
+
` Service tier: ${formatServiceTierLabel(runtime.policy.serviceTier)}`,
|
|
496
|
+
` Personality: ${formatPersonalityLabel(runtime.policy.personality)}`,
|
|
497
|
+
` Workspace root: ${context.workspaceRoot}`,
|
|
498
|
+
" Writable roots:",
|
|
499
|
+
writableRoots,
|
|
500
|
+
];
|
|
501
|
+
|
|
502
|
+
if (typeof context.tokensUsed === "number") {
|
|
503
|
+
lines.push(` Tokens used: ~${context.tokensUsed.toLocaleString("en-US")}`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (context.projectInstructions) {
|
|
507
|
+
const pi = context.projectInstructions;
|
|
508
|
+
if (pi.status === "loaded") {
|
|
509
|
+
lines.push(" Project instructions: loaded");
|
|
510
|
+
lines.push(` Source: ${pi.instructions.path}`);
|
|
511
|
+
} else if (pi.status === "error") {
|
|
512
|
+
lines.push(` Project instructions: error — ${pi.message}`);
|
|
513
|
+
lines.push(` Source: ${pi.path}`);
|
|
514
|
+
} else {
|
|
515
|
+
lines.push(" Project instructions: none");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return lines.join("\n");
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export function buildCodexConfigOverrides(runtime: ResolvedRuntimeConfig): string[] {
|
|
523
|
+
const overrides = [
|
|
524
|
+
`model_reasoning_effort=${runtime.reasoningLevel}`,
|
|
525
|
+
`approval_policy=${runtime.policy.approvalPolicy}`,
|
|
526
|
+
];
|
|
527
|
+
|
|
528
|
+
if (runtime.policy.networkAccess) {
|
|
529
|
+
overrides.push(`sandbox_workspace_write.network_access=${JSON.stringify(runtime.policy.networkAccess)}`);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (runtime.policy.writableRoots.length > 0) {
|
|
533
|
+
overrides.push(`sandbox_workspace_write.writable_roots=${JSON.stringify(runtime.policy.writableRoots)}`);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (runtime.policy.serviceTier !== "flex") {
|
|
537
|
+
overrides.push(`service_tier=${runtime.policy.serviceTier}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (runtime.policy.personality !== "none") {
|
|
541
|
+
overrides.push(`personality=${runtime.policy.personality}`);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return overrides;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export function resolveWritableRootCommandPath(pathValue: string, workspaceRoot: string): string {
|
|
548
|
+
const trimmed = pathValue.trim();
|
|
549
|
+
if (!trimmed) return trimmed;
|
|
550
|
+
if (/^(?:[a-zA-Z]:[\\/]|\\\\|\/)/.test(trimmed)) {
|
|
551
|
+
return normalizePathValue(trimmed);
|
|
552
|
+
}
|
|
553
|
+
return normalizePathValue(join(workspaceRoot, trimmed));
|
|
554
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_SHOW_BUSY_LOADER,
|
|
5
|
+
DEFAULT_TERMINAL_TITLE_MODE,
|
|
6
|
+
DEFAULT_WORKSPACE_DISPLAY_MODE,
|
|
7
|
+
DEFAULT_MODE,
|
|
8
|
+
USER_SETTING_DEFINITIONS,
|
|
9
|
+
formatBusyLoaderSettingValue,
|
|
10
|
+
formatModeLabel,
|
|
11
|
+
formatTerminalTitleModeLabel,
|
|
12
|
+
formatTerminalTitlePath,
|
|
13
|
+
formatWorkspaceDisplayModeLabel,
|
|
14
|
+
formatWorkspaceDisplayPath,
|
|
15
|
+
getCodexConfigFile,
|
|
16
|
+
getCodexHome,
|
|
17
|
+
getCodexaTrustStoreFile,
|
|
18
|
+
getNextMode,
|
|
19
|
+
normalizeReasoningForModel,
|
|
20
|
+
} from "./settings.js";
|
|
21
|
+
|
|
22
|
+
test("keeps supported reasoning levels for gpt-5.4-mini", () => {
|
|
23
|
+
assert.equal(normalizeReasoningForModel("gpt-5.4-mini", "high"), "high");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("keeps reasoning unchanged for non-mini models", () => {
|
|
27
|
+
assert.equal(normalizeReasoningForModel("gpt-5.4", "low"), "low");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
test("formats codex-style mode labels", () => {
|
|
32
|
+
assert.equal(formatModeLabel("suggest"), "Read-only");
|
|
33
|
+
assert.equal(formatModeLabel("auto-edit"), "Auto");
|
|
34
|
+
assert.equal(formatModeLabel("full-auto"), "Full Access");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("cycles modes in the same order as Ctrl+Y", () => {
|
|
38
|
+
assert.equal(getNextMode("suggest"), "auto-edit");
|
|
39
|
+
assert.equal(getNextMode("auto-edit"), "full-auto");
|
|
40
|
+
assert.equal(getNextMode("full-auto"), "suggest");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("defaults to full-auto mode", () => {
|
|
44
|
+
assert.equal(DEFAULT_MODE, "full-auto");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("defaults workspace display mode and busy loader", () => {
|
|
48
|
+
assert.equal(DEFAULT_WORKSPACE_DISPLAY_MODE, "dir");
|
|
49
|
+
assert.equal(DEFAULT_TERMINAL_TITLE_MODE, "dir");
|
|
50
|
+
assert.equal(DEFAULT_SHOW_BUSY_LOADER, true);
|
|
51
|
+
assert.equal(formatWorkspaceDisplayModeLabel("dir"), "Dir");
|
|
52
|
+
assert.equal(formatWorkspaceDisplayModeLabel("name"), "Name");
|
|
53
|
+
assert.equal(formatWorkspaceDisplayModeLabel("simple"), "Simple");
|
|
54
|
+
assert.equal(formatTerminalTitleModeLabel("dir"), "Dir");
|
|
55
|
+
assert.equal(formatBusyLoaderSettingValue(true), "true");
|
|
56
|
+
assert.equal(formatBusyLoaderSettingValue(false), "false");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("defines user settings through reusable schemas", () => {
|
|
60
|
+
assert.deepEqual(USER_SETTING_DEFINITIONS, [
|
|
61
|
+
{
|
|
62
|
+
key: "workspaceDisplayMode",
|
|
63
|
+
label: "Workspace display",
|
|
64
|
+
description: "Controls how the workspace label is displayed in the Codexa header.",
|
|
65
|
+
options: [
|
|
66
|
+
{ value: "dir", label: "Dir" },
|
|
67
|
+
{ value: "name", label: "Name" },
|
|
68
|
+
{ value: "simple", label: "Simple" },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "terminalTitleMode",
|
|
73
|
+
label: "Terminal title",
|
|
74
|
+
description: "Controls how the terminal tab/window title is displayed.",
|
|
75
|
+
options: [
|
|
76
|
+
{ value: "dir", label: "Dir" },
|
|
77
|
+
{ value: "name", label: "Name" },
|
|
78
|
+
{ value: "simple", label: "Simple" },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: "showBusyLoader",
|
|
83
|
+
label: "Busy loader",
|
|
84
|
+
description: "Controls whether the footer shows a subtle loading animation while Codexa is busy.",
|
|
85
|
+
options: [
|
|
86
|
+
{ value: "true", label: "True" },
|
|
87
|
+
{ value: "false", label: "False" },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: "terminalMouseMode",
|
|
92
|
+
label: "Mouse mode",
|
|
93
|
+
description:
|
|
94
|
+
"Selection (default): no mouse tracking — native drag-select and native wheel scroll work unmodified. "
|
|
95
|
+
+ "Scroll history via native terminal scrollback. "
|
|
96
|
+
+ "Wheel: enables SGR mouse tracking so the Codexa timeline captures wheel events for in-app scroll. "
|
|
97
|
+
+ "Native drag-select then requires Shift (Windows Terminal) or equivalent modifier. "
|
|
98
|
+
+ "Run /mouse to toggle for the current session.",
|
|
99
|
+
options: [
|
|
100
|
+
{ value: "selection", label: "Native selection" },
|
|
101
|
+
{ value: "wheel", label: "Wheel scroll" },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("formats workspace display paths without changing root semantics", () => {
|
|
108
|
+
assert.equal(formatWorkspaceDisplayPath("", "simple"), "");
|
|
109
|
+
assert.equal(
|
|
110
|
+
formatWorkspaceDisplayPath("C:\\Development\\1-JavaScript\\13-Custom CLI", "dir"),
|
|
111
|
+
"13-Custom CLI",
|
|
112
|
+
);
|
|
113
|
+
assert.equal(
|
|
114
|
+
formatWorkspaceDisplayPath("C:\\Development\\1-JavaScript\\13-Custom CLI", "name"),
|
|
115
|
+
"Codexa",
|
|
116
|
+
);
|
|
117
|
+
assert.equal(
|
|
118
|
+
formatWorkspaceDisplayPath("C:\\Development\\1-JavaScript\\13-Custom CLI", "simple"),
|
|
119
|
+
"13-Custom CLI",
|
|
120
|
+
);
|
|
121
|
+
assert.equal(formatWorkspaceDisplayPath("C:\\", "simple"), "C:\\");
|
|
122
|
+
assert.equal(formatWorkspaceDisplayPath("C:\\Workspace\\", "simple"), "Workspace");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("formats terminal title labels with the same workspace semantics", () => {
|
|
126
|
+
assert.equal(
|
|
127
|
+
formatTerminalTitlePath("C:\\Development\\1-JavaScript\\13-Custom-CLI-Normal", "dir"),
|
|
128
|
+
"13-Custom-CLI-Normal",
|
|
129
|
+
);
|
|
130
|
+
assert.equal(
|
|
131
|
+
formatTerminalTitlePath("C:\\Development\\1-JavaScript\\13-Custom-CLI-Normal", "name"),
|
|
132
|
+
"Codexa",
|
|
133
|
+
);
|
|
134
|
+
assert.equal(
|
|
135
|
+
formatTerminalTitlePath("C:\\Development\\1-JavaScript\\13-Custom-CLI-Normal", "simple"),
|
|
136
|
+
"Codexa",
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("resolves CODEX_HOME-derived paths from the live environment", () => {
|
|
141
|
+
const previousCodexHome = process.env.CODEX_HOME;
|
|
142
|
+
process.env.CODEX_HOME = "C:\\Temp\\codex-home";
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
assert.equal(getCodexHome(), "C:\\Temp\\codex-home");
|
|
146
|
+
assert.equal(getCodexConfigFile(), "C:\\Temp\\codex-home\\config.toml");
|
|
147
|
+
assert.equal(getCodexaTrustStoreFile(), "C:\\Temp\\codex-home\\codexa-trust.json");
|
|
148
|
+
} finally {
|
|
149
|
+
if (previousCodexHome === undefined) {
|
|
150
|
+
delete process.env.CODEX_HOME;
|
|
151
|
+
} else {
|
|
152
|
+
process.env.CODEX_HOME = previousCodexHome;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|