@golba98/codexa 1.0.2 → 1.0.4
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 +396 -100
- package/bin/codexa.js +62 -144
- package/package.json +14 -8
- package/src/app.tsx +596 -306
- package/src/commands/handler.ts +6 -6
- package/src/config/buildInfo.ts +2 -2
- package/src/config/layeredConfig.ts +1 -1
- package/src/config/persistence.ts +10 -0
- package/src/config/runtimeConfig.ts +1 -1
- package/src/config/settings.ts +8 -16
- package/src/config/trustStore.ts +1 -1
- package/src/config/updateCheckCache.ts +19 -1
- package/src/core/README.md +52 -0
- package/src/core/agent/loop.ts +282 -0
- package/src/core/agent/protocol.ts +211 -0
- package/src/core/agent/tools.ts +414 -0
- package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
- package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
- package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
- package/src/core/debug/modelStateDebug.ts +34 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/codexExecutable.ts +1 -0
- package/src/core/executables/executableResolver.ts +65 -43
- package/src/core/perf/renderDebug.ts +10 -6
- package/src/core/process/processValidation.ts +9 -5
- package/src/core/providerLauncher/launcher.ts +59 -42
- package/src/core/providerLauncher/registry.ts +30 -14
- package/src/core/providerLauncher/types.ts +11 -9
- package/src/core/providerLauncher/workspaceConfig.ts +41 -26
- package/src/core/providerRuntime/anthropic.ts +7 -1
- package/src/core/providerRuntime/antigravity.ts +305 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +268 -22
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/contextMetadata.ts +12 -24
- package/src/core/providerRuntime/local.ts +129 -51
- package/src/core/providerRuntime/models.ts +22 -11
- package/src/core/providerRuntime/registry.ts +58 -31
- package/src/core/providerRuntime/types.ts +19 -14
- package/src/core/providers/codexSubprocess.ts +2 -2
- package/src/core/providers/types.ts +1 -1
- package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
- package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
- package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
- package/src/core/terminal/clearFrameBoundary.ts +814 -0
- package/src/core/terminal/frameLock.ts +109 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/terminalControl.ts +22 -0
- package/src/core/terminal/terminalTitle.ts +16 -102
- package/src/core/version/channel.ts +23 -0
- package/src/core/version/updateCheck.ts +193 -0
- package/src/core/{launchContext.ts → workspace/launchContext.ts} +32 -39
- package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
- package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
- package/src/headless/execRunner.ts +2 -2
- package/src/index.tsx +43 -89
- package/src/session/appSession.ts +10 -7
- package/src/session/chatLifecycle.ts +1 -1
- package/src/session/liveRenderScheduler.ts +1 -1
- package/src/session/types.ts +3 -2
- 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 +670 -719
- package/src/ui/AttachmentImportPanel.tsx +8 -8
- package/src/ui/AuthPanel.tsx +20 -20
- package/src/ui/BottomComposer.tsx +158 -118
- package/src/ui/DashCard.tsx +3 -3
- package/src/ui/Markdown.tsx +17 -17
- package/src/ui/ModelPickerScreen.tsx +222 -42
- package/src/ui/ModelReasoningPicker.tsx +15 -15
- package/src/ui/Panel.tsx +3 -3
- package/src/ui/PlanActionPicker.tsx +6 -6
- package/src/ui/PlanReviewPanel.tsx +9 -9
- package/src/ui/ProviderPicker.tsx +735 -321
- package/src/ui/RunFooter.tsx +3 -3
- package/src/ui/RuntimeStatusBar.tsx +108 -0
- package/src/ui/SelectionPanel.tsx +8 -4
- package/src/ui/SettingsPanel.tsx +9 -9
- package/src/ui/Spinner.tsx +1 -1
- package/src/ui/TextEntryPanel.tsx +11 -11
- package/src/ui/ThinkingBlock.tsx +8 -8
- package/src/ui/Timeline.tsx +1625 -1472
- package/src/ui/TopHeader.tsx +437 -293
- package/src/ui/TranscriptShell.tsx +322 -0
- package/src/ui/TurnGroup.tsx +33 -33
- package/src/ui/UpdateAvailableCard.tsx +41 -0
- package/src/ui/UpdatePromptPanel.tsx +197 -0
- package/src/ui/focus.ts +3 -0
- package/src/ui/layout.ts +299 -25
- package/src/ui/layoutListWindow.ts +145 -0
- package/src/ui/logoVariants.ts +103 -0
- package/src/ui/modeDisplay.ts +12 -12
- package/src/ui/runtimeDisplay.ts +112 -0
- package/src/ui/textLayout.ts +15 -4
- package/src/ui/theme.tsx +274 -395
- package/src/ui/timelineMeasure.ts +218 -136
- 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/codex.ts +0 -124
- 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/updateCheck.ts +0 -172
- 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/StaticTranscriptItem.tsx +0 -56
- 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/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
- /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
- /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
- /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
- /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
- /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import test from "node:test";
|
|
5
|
-
import { runCommand, runShellCommand, summarizeCommandResult, type CommandResult } from "./CommandRunner.js";
|
|
6
|
-
|
|
7
|
-
function makeResult(overrides: Partial<CommandResult> = {}): CommandResult {
|
|
8
|
-
return {
|
|
9
|
-
status: "completed",
|
|
10
|
-
exitCode: 0,
|
|
11
|
-
signal: null,
|
|
12
|
-
stdout: "",
|
|
13
|
-
stderr: "",
|
|
14
|
-
startedAt: 1,
|
|
15
|
-
endedAt: 2,
|
|
16
|
-
durationMs: 1,
|
|
17
|
-
userMessage: "Command completed.",
|
|
18
|
-
...overrides,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
test("summarizes ripgrep file listings without flooding the UI", () => {
|
|
23
|
-
const result = makeResult({
|
|
24
|
-
stdout: "src/app.tsx\nsrc/ui/BottomComposer.tsx\n",
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
assert.equal(summarizeCommandResult("rg --files", result), "Found 2 files.");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test("keeps a concise fallback summary for generic successful commands", () => {
|
|
31
|
-
const result = makeResult({
|
|
32
|
-
stdout: "alpha\nbeta\ngamma\n",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
assert.equal(summarizeCommandResult("node script.js", result), "Produced 3 lines of output.");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("preserves the failure message for unsuccessful commands", () => {
|
|
39
|
-
const result = makeResult({
|
|
40
|
-
status: "failed",
|
|
41
|
-
exitCode: 1,
|
|
42
|
-
userMessage: "git exited with code 1.",
|
|
43
|
-
stderr: "fatal: not a git repository",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
assert.equal(summarizeCommandResult("git status", result), "git exited with code 1.");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("runCommand executes a direct executable with argument array", async () => {
|
|
50
|
-
const runner = runCommand({
|
|
51
|
-
executable: process.execPath,
|
|
52
|
-
args: ["-e", "console.log(process.argv[1])", "direct-ok"],
|
|
53
|
-
cwd: process.cwd(),
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const result = await runner.result;
|
|
57
|
-
assert.equal(result.status, "completed");
|
|
58
|
-
assert.equal(result.exitCode, 0);
|
|
59
|
-
assert.equal(result.stdout.trim(), "direct-ok");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("runCommand rejects obvious executable injection", () => {
|
|
63
|
-
assert.throws(
|
|
64
|
-
() => runCommand({
|
|
65
|
-
executable: "node & echo injected",
|
|
66
|
-
args: ["--version"],
|
|
67
|
-
cwd: process.cwd(),
|
|
68
|
-
}),
|
|
69
|
-
/single executable name|shell metacharacters/i,
|
|
70
|
-
);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("runShellCommand is the explicit shell execution path", async () => {
|
|
74
|
-
const runner = runShellCommand("echo shell-ok", { cwd: process.cwd() });
|
|
75
|
-
const result = await runner.result;
|
|
76
|
-
|
|
77
|
-
assert.equal(result.status, "completed");
|
|
78
|
-
assert.equal(result.exitCode, 0);
|
|
79
|
-
assert.match(result.stdout, /shell-ok/);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("command runner reports lifecycle boundaries for terminal title reassertion", () => {
|
|
83
|
-
const source = readFileSync(fileURLToPath(new URL("./CommandRunner.ts", import.meta.url)), "utf8");
|
|
84
|
-
const beforeSpawnIndex = source.indexOf('handlers.onProcessLifecycle?.("before-spawn")');
|
|
85
|
-
const spawnIndex = source.indexOf("child = spawn(");
|
|
86
|
-
const spawnedIndex = source.indexOf('handlers.onProcessLifecycle?.("spawned")', spawnIndex);
|
|
87
|
-
const errorIndex = source.indexOf('child.once("error"', spawnIndex);
|
|
88
|
-
const lifecycleErrorIndex = source.indexOf('handlers.onProcessLifecycle?.("error")', errorIndex);
|
|
89
|
-
const closeIndex = source.indexOf('child.once("close"', spawnIndex);
|
|
90
|
-
const exitIndex = source.indexOf('handlers.onProcessLifecycle?.("exit")', closeIndex);
|
|
91
|
-
const cancelIndex = source.indexOf("cancel: () =>");
|
|
92
|
-
const lifecycleCancelIndex = source.indexOf('handlers.onProcessLifecycle?.("cancel")', cancelIndex);
|
|
93
|
-
|
|
94
|
-
assert.ok(beforeSpawnIndex >= 0 && beforeSpawnIndex < spawnIndex);
|
|
95
|
-
assert.ok(spawnedIndex > spawnIndex);
|
|
96
|
-
assert.ok(lifecycleErrorIndex > errorIndex);
|
|
97
|
-
assert.ok(exitIndex > closeIndex);
|
|
98
|
-
assert.ok(lifecycleCancelIndex > cancelIndex);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test("generic command runner does not expose shell mode", () => {
|
|
102
|
-
const source = readFileSync(fileURLToPath(new URL("./CommandRunner.ts", import.meta.url)), "utf8");
|
|
103
|
-
assert.equal(source.includes("shell?: boolean"), false);
|
|
104
|
-
assert.equal(source.includes("spec.shell"), false);
|
|
105
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import { loadProjectInstructions } from "./projectInstructions.js";
|
|
7
|
-
|
|
8
|
-
function withTempWorkspace(run: (workspaceRoot: string) => void) {
|
|
9
|
-
const workspaceRoot = mkdtempSync(join(tmpdir(), "codexa-instructions-"));
|
|
10
|
-
try {
|
|
11
|
-
run(workspaceRoot);
|
|
12
|
-
} finally {
|
|
13
|
-
rmSync(workspaceRoot, { recursive: true, force: true });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
test("loads AGENTS.md from the workspace root first", () => {
|
|
18
|
-
withTempWorkspace((workspaceRoot) => {
|
|
19
|
-
mkdirSync(join(workspaceRoot, ".codex"), { recursive: true });
|
|
20
|
-
writeFileSync(join(workspaceRoot, "AGENTS.md"), "root instructions\n", "utf8");
|
|
21
|
-
writeFileSync(join(workspaceRoot, ".codex", "AGENTS.md"), "nested instructions\n", "utf8");
|
|
22
|
-
|
|
23
|
-
const result = loadProjectInstructions(workspaceRoot);
|
|
24
|
-
|
|
25
|
-
assert.equal(result.status, "loaded");
|
|
26
|
-
if (result.status !== "loaded") return;
|
|
27
|
-
assert.equal(result.instructions.content, "root instructions");
|
|
28
|
-
assert.match(result.instructions.path, /AGENTS\.md$/);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("falls back to .codex/AGENTS.md", () => {
|
|
33
|
-
withTempWorkspace((workspaceRoot) => {
|
|
34
|
-
mkdirSync(join(workspaceRoot, ".codex"), { recursive: true });
|
|
35
|
-
writeFileSync(join(workspaceRoot, ".codex", "AGENTS.md"), "project instructions\n", "utf8");
|
|
36
|
-
|
|
37
|
-
const result = loadProjectInstructions(workspaceRoot);
|
|
38
|
-
|
|
39
|
-
assert.equal(result.status, "loaded");
|
|
40
|
-
if (result.status !== "loaded") return;
|
|
41
|
-
assert.equal(result.instructions.content, "project instructions");
|
|
42
|
-
assert.match(result.instructions.path, /\.codex[\\/]AGENTS\.md$/);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("treats missing instruction files as non-fatal", () => {
|
|
47
|
-
withTempWorkspace((workspaceRoot) => {
|
|
48
|
-
assert.deepEqual(loadProjectInstructions(workspaceRoot), { status: "missing" });
|
|
49
|
-
});
|
|
50
|
-
});
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { EventEmitter } from "events";
|
|
3
|
-
import test from "node:test";
|
|
4
|
-
import { buildProviderLaunchSpec, launchProviderCli } from "./launcher.js";
|
|
5
|
-
import type { ProviderConfig } from "./types.js";
|
|
6
|
-
|
|
7
|
-
function makeProvider(overrides: Partial<ProviderConfig> = {}): ProviderConfig {
|
|
8
|
-
return {
|
|
9
|
-
id: "openai",
|
|
10
|
-
displayName: "OpenAI",
|
|
11
|
-
currentModel: "gpt-5.4",
|
|
12
|
-
backendType: "codex-cli-auth",
|
|
13
|
-
routeMode: "in-codexa",
|
|
14
|
-
enabled: true,
|
|
15
|
-
statusLabel: "Enabled",
|
|
16
|
-
launchCommand: { executable: "codex", args: [] },
|
|
17
|
-
isDefault: true,
|
|
18
|
-
isActiveRoute: true,
|
|
19
|
-
routeUnavailableReason: null,
|
|
20
|
-
...overrides,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
test("builds launch specs for enabled providers", () => {
|
|
25
|
-
const spec = buildProviderLaunchSpec(makeProvider({
|
|
26
|
-
launchCommand: { executable: "claude", args: ["--resume"] },
|
|
27
|
-
}), "C:\\Workspace");
|
|
28
|
-
|
|
29
|
-
assert.equal("status" in spec, false);
|
|
30
|
-
if ("status" in spec) return;
|
|
31
|
-
assert.equal(spec.executable, "claude");
|
|
32
|
-
assert.deepEqual(spec.args, ["--resume"]);
|
|
33
|
-
assert.equal(spec.cwd, "C:\\Workspace");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("disabled providers fail before spawning", () => {
|
|
37
|
-
const result = buildProviderLaunchSpec(makeProvider({
|
|
38
|
-
id: "local",
|
|
39
|
-
displayName: "Local",
|
|
40
|
-
backendType: "local-openai-compatible",
|
|
41
|
-
enabled: false,
|
|
42
|
-
statusLabel: "Disabled",
|
|
43
|
-
launchCommand: null,
|
|
44
|
-
isDefault: false,
|
|
45
|
-
}), "C:\\Workspace");
|
|
46
|
-
|
|
47
|
-
assert.equal("status" in result, true);
|
|
48
|
-
assert.equal("status" in result ? result.status : "", "disabled");
|
|
49
|
-
assert.match("status" in result ? result.message : "", /Configure a command/i);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("unsafe configured launch commands fail before spawning", () => {
|
|
53
|
-
const result = buildProviderLaunchSpec(makeProvider({
|
|
54
|
-
launchCommand: { executable: "codex & calc", args: [] },
|
|
55
|
-
}), "C:\\Workspace");
|
|
56
|
-
|
|
57
|
-
assert.equal("status" in result, true);
|
|
58
|
-
assert.equal("status" in result ? result.status : "", "spawn-error");
|
|
59
|
-
assert.match("status" in result ? result.message : "", /unsafe launch command/i);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("missing command spawn errors become friendly launch results", async () => {
|
|
63
|
-
const child = new EventEmitter();
|
|
64
|
-
const spawnImpl = (() => {
|
|
65
|
-
queueMicrotask(() => {
|
|
66
|
-
const error = new Error("not found") as NodeJS.ErrnoException;
|
|
67
|
-
error.code = "ENOENT";
|
|
68
|
-
child.emit("error", error);
|
|
69
|
-
});
|
|
70
|
-
return child;
|
|
71
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
72
|
-
|
|
73
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
74
|
-
cwd: "C:\\Workspace",
|
|
75
|
-
commandExists: () => true,
|
|
76
|
-
spawnImpl,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
assert.equal(result.status, "missing-command");
|
|
80
|
-
assert.match(result.message, /codex.*PATH/i);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("missing command preflight fails before suspending raw mode", async () => {
|
|
84
|
-
const rawModes: boolean[] = [];
|
|
85
|
-
let didSpawn = false;
|
|
86
|
-
|
|
87
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
88
|
-
cwd: "C:\\Workspace",
|
|
89
|
-
stdin: {
|
|
90
|
-
isRaw: true,
|
|
91
|
-
setRawMode(enabled) {
|
|
92
|
-
rawModes.push(enabled);
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
commandExists: () => false,
|
|
96
|
-
spawnImpl: (() => {
|
|
97
|
-
didSpawn = true;
|
|
98
|
-
return new EventEmitter();
|
|
99
|
-
}) as unknown as typeof import("child_process").spawn,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
assert.equal(result.status, "missing-command");
|
|
103
|
-
assert.equal(didSpawn, false);
|
|
104
|
-
assert.deepEqual(rawModes, []);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("launch restores raw mode after child exits", async () => {
|
|
108
|
-
const child = new EventEmitter();
|
|
109
|
-
const rawModes: boolean[] = [];
|
|
110
|
-
const spawnImpl = (() => {
|
|
111
|
-
queueMicrotask(() => child.emit("close", 0, null));
|
|
112
|
-
return child;
|
|
113
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
114
|
-
|
|
115
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
116
|
-
cwd: "C:\\Workspace",
|
|
117
|
-
stdin: {
|
|
118
|
-
isRaw: true,
|
|
119
|
-
setRawMode(enabled) {
|
|
120
|
-
rawModes.push(enabled);
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
commandExists: () => true,
|
|
124
|
-
spawnImpl,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
assert.equal(result.status, "completed");
|
|
128
|
-
assert.deepEqual(rawModes, [false, true]);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("launch passes the workspace root as the child cwd", async () => {
|
|
132
|
-
const child = new EventEmitter();
|
|
133
|
-
let observedCwd = "";
|
|
134
|
-
let observedShell: boolean | undefined = undefined;
|
|
135
|
-
const spawnImpl = ((_executable: string, _args: string[], options: { cwd?: string; shell?: boolean }) => {
|
|
136
|
-
observedCwd = options.cwd ?? "";
|
|
137
|
-
observedShell = options.shell;
|
|
138
|
-
queueMicrotask(() => child.emit("close", 0, null));
|
|
139
|
-
return child;
|
|
140
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
141
|
-
|
|
142
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
143
|
-
cwd: "C:\\Workspace\\Project",
|
|
144
|
-
commandExists: () => true,
|
|
145
|
-
spawnImpl,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
assert.equal(result.status, "completed");
|
|
149
|
-
assert.equal(observedCwd, "C:\\Workspace\\Project");
|
|
150
|
-
assert.equal(observedShell, false);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("launch wraps Windows batch commands without enabling shell mode", async () => {
|
|
154
|
-
if (process.platform !== "win32") return;
|
|
155
|
-
|
|
156
|
-
const child = new EventEmitter();
|
|
157
|
-
let observedExecutable = "";
|
|
158
|
-
let observedArgs: string[] = [];
|
|
159
|
-
let observedShell: boolean | undefined = undefined;
|
|
160
|
-
const spawnImpl = ((executable: string, args: string[], options: { shell?: boolean }) => {
|
|
161
|
-
observedExecutable = executable;
|
|
162
|
-
observedArgs = args;
|
|
163
|
-
observedShell = options.shell;
|
|
164
|
-
queueMicrotask(() => child.emit("close", 0, null));
|
|
165
|
-
return child;
|
|
166
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
167
|
-
|
|
168
|
-
const result = await launchProviderCli(makeProvider({
|
|
169
|
-
launchCommand: { executable: "codex.cmd", args: ["--resume"] },
|
|
170
|
-
}), {
|
|
171
|
-
cwd: "C:\\Workspace",
|
|
172
|
-
commandExists: () => true,
|
|
173
|
-
spawnImpl,
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
assert.equal(result.status, "completed");
|
|
177
|
-
assert.equal(observedExecutable, "cmd.exe");
|
|
178
|
-
assert.deepEqual(observedArgs, ["/d", "/s", "/c", "call", "codex.cmd", "--resume"]);
|
|
179
|
-
assert.equal(observedShell, false);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("launch runs suspend and resume hooks around raw mode changes", async () => {
|
|
183
|
-
const child = new EventEmitter();
|
|
184
|
-
const events: string[] = [];
|
|
185
|
-
const spawnImpl = (() => {
|
|
186
|
-
events.push("spawn");
|
|
187
|
-
queueMicrotask(() => child.emit("close", 0, null));
|
|
188
|
-
return child;
|
|
189
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
190
|
-
|
|
191
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
192
|
-
cwd: "C:\\Workspace",
|
|
193
|
-
stdin: {
|
|
194
|
-
isRaw: true,
|
|
195
|
-
setRawMode(enabled) {
|
|
196
|
-
events.push(enabled ? "raw-on" : "raw-off");
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
beforeLaunch: () => events.push("before"),
|
|
200
|
-
afterLaunch: () => events.push("after"),
|
|
201
|
-
commandExists: () => true,
|
|
202
|
-
spawnImpl,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
assert.equal(result.status, "completed");
|
|
206
|
-
assert.deepEqual(events, ["before", "raw-off", "spawn", "raw-on", "after"]);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("launch restores terminal state after child spawn error", async () => {
|
|
210
|
-
const child = new EventEmitter();
|
|
211
|
-
const events: string[] = [];
|
|
212
|
-
const spawnImpl = (() => {
|
|
213
|
-
events.push("spawn");
|
|
214
|
-
queueMicrotask(() => {
|
|
215
|
-
const error = new Error("blocked") as NodeJS.ErrnoException;
|
|
216
|
-
error.code = "EPERM";
|
|
217
|
-
child.emit("error", error);
|
|
218
|
-
});
|
|
219
|
-
return child;
|
|
220
|
-
}) as unknown as typeof import("child_process").spawn;
|
|
221
|
-
|
|
222
|
-
const result = await launchProviderCli(makeProvider(), {
|
|
223
|
-
cwd: "C:\\Workspace",
|
|
224
|
-
stdin: {
|
|
225
|
-
isRaw: true,
|
|
226
|
-
setRawMode(enabled) {
|
|
227
|
-
events.push(enabled ? "raw-on" : "raw-off");
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
beforeLaunch: () => events.push("before"),
|
|
231
|
-
afterLaunch: () => events.push("after"),
|
|
232
|
-
commandExists: () => true,
|
|
233
|
-
spawnImpl,
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
assert.equal(result.status, "spawn-error");
|
|
237
|
-
assert.deepEqual(events, ["before", "raw-off", "spawn", "raw-on", "after"]);
|
|
238
|
-
});
|