poe-code 3.0.60 → 3.0.61

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.
@@ -1,113 +1,1925 @@
1
- import { createBinaryExistsCheck, createSpawnHealthCheck } from "../utils/command-checks.js";
2
- import { configMutation, fileMutation, templateMutation, isConfigObject } from "@poe-code/config-mutations";
3
- import { createProvider } from "./create-provider.js";
4
- import { CODEX_MODELS, DEFAULT_CODEX_MODEL, DEFAULT_REASONING, stripModelNamespace } from "../cli/constants.js";
5
- import { codexAgent } from "@poe-code/agent-defs";
6
- const CODEX_PROVIDER_ID = "poe";
7
- export const CODEX_INSTALL_DEFINITION = {
8
- id: "codex",
9
- summary: "Codex CLI",
10
- check: createBinaryExistsCheck("codex", "codex-cli-binary", "Codex CLI binary must exist"),
11
- steps: [
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // src/templates/python/env.hbs
28
+ var require_env = __commonJS({
29
+ "src/templates/python/env.hbs"(exports, module) {
30
+ module.exports = "POE_API_KEY={{apiKey}}\nPOE_BASE_URL=https://api.poe.com/v1\nMODEL={{model}}\n";
31
+ }
32
+ });
33
+
34
+ // src/templates/python/main.py.hbs
35
+ var require_main_py = __commonJS({
36
+ "src/templates/python/main.py.hbs"(exports, module) {
37
+ module.exports = 'import os\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nclient = OpenAI(\n api_key=os.getenv("POE_API_KEY"),\n base_url=os.getenv("POE_BASE_URL")\n)\n\nresponse = client.chat.completions.create(\n model=os.getenv("MODEL", "{{model}}"),\n messages=[{"role": "user", "content": "Tell me a joke"}]\n)\n\nprint(response.choices[0].message.content)\n';
38
+ }
39
+ });
40
+
41
+ // src/templates/python/requirements.txt.hbs
42
+ var require_requirements_txt = __commonJS({
43
+ "src/templates/python/requirements.txt.hbs"(exports, module) {
44
+ module.exports = "openai>=1.0.0\npython-dotenv>=1.0.0\n";
45
+ }
46
+ });
47
+
48
+ // src/templates/codex/config.toml.hbs
49
+ var require_config_toml = __commonJS({
50
+ "src/templates/codex/config.toml.hbs"(exports, module) {
51
+ module.exports = 'model_provider = "poe"\nmodel = "{{{model}}}"\nmodel_reasoning_effort = "{{reasoningEffort}}"\n\n[model_providers.poe]\nname = "poe"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\n';
52
+ }
53
+ });
54
+
55
+ // packages/agent-spawn/src/run-command.ts
56
+ import { spawn } from "node:child_process";
57
+
58
+ // packages/agent-defs/src/agents/claude-code.ts
59
+ var claudeCodeAgent = {
60
+ id: "claude-code",
61
+ name: "claude-code",
62
+ label: "Claude Code",
63
+ summary: "Configure Claude Code to route through Poe.",
64
+ aliases: ["claude"],
65
+ binaryName: "claude",
66
+ configPath: "~/.claude/settings.json",
67
+ branding: {
68
+ colors: {
69
+ dark: "#C15F3C",
70
+ light: "#C15F3C"
71
+ }
72
+ }
73
+ };
74
+
75
+ // packages/agent-defs/src/agents/claude-desktop.ts
76
+ var claudeDesktopAgent = {
77
+ id: "claude-desktop",
78
+ name: "claude-desktop",
79
+ label: "Claude Desktop",
80
+ summary: "Anthropic's official desktop application for Claude",
81
+ configPath: "~/.claude/settings.json",
82
+ branding: {
83
+ colors: {
84
+ dark: "#D97757",
85
+ light: "#D97757"
86
+ }
87
+ }
88
+ };
89
+
90
+ // packages/agent-defs/src/agents/codex.ts
91
+ var codexAgent = {
92
+ id: "codex",
93
+ name: "codex",
94
+ label: "Codex",
95
+ summary: "Configure Codex to use Poe as the model provider.",
96
+ binaryName: "codex",
97
+ configPath: "~/.codex/config.toml",
98
+ branding: {
99
+ colors: {
100
+ dark: "#D5D9DF",
101
+ light: "#7A7F86"
102
+ }
103
+ }
104
+ };
105
+
106
+ // packages/agent-defs/src/agents/opencode.ts
107
+ var openCodeAgent = {
108
+ id: "opencode",
109
+ name: "opencode",
110
+ label: "OpenCode CLI",
111
+ summary: "Configure OpenCode CLI to use the Poe API.",
112
+ binaryName: "opencode",
113
+ configPath: "~/.config/opencode/config.json",
114
+ branding: {
115
+ colors: {
116
+ dark: "#4A4F55",
117
+ light: "#2F3338"
118
+ }
119
+ }
120
+ };
121
+
122
+ // packages/agent-defs/src/agents/kimi.ts
123
+ var kimiAgent = {
124
+ id: "kimi",
125
+ name: "kimi",
126
+ label: "Kimi",
127
+ summary: "Configure Kimi CLI to use Poe API",
128
+ aliases: ["kimi-cli"],
129
+ binaryName: "kimi",
130
+ configPath: "~/.kimi/config.toml",
131
+ branding: {
132
+ colors: {
133
+ dark: "#7B68EE",
134
+ light: "#6A5ACD"
135
+ }
136
+ }
137
+ };
138
+
139
+ // packages/agent-defs/src/registry.ts
140
+ var allAgents = [
141
+ claudeCodeAgent,
142
+ claudeDesktopAgent,
143
+ codexAgent,
144
+ openCodeAgent,
145
+ kimiAgent
146
+ ];
147
+ var lookup = /* @__PURE__ */ new Map();
148
+ for (const agent of allAgents) {
149
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
150
+ for (const value of values) {
151
+ const normalized = value.toLowerCase();
152
+ if (!lookup.has(normalized)) {
153
+ lookup.set(normalized, agent.id);
154
+ }
155
+ }
156
+ }
157
+ function resolveAgentId(input) {
158
+ if (!input) {
159
+ return void 0;
160
+ }
161
+ return lookup.get(input.toLowerCase());
162
+ }
163
+
164
+ // packages/agent-spawn/src/configs/claude-code.ts
165
+ var claudeCodeSpawnConfig = {
166
+ kind: "cli",
167
+ agentId: "claude-code",
168
+ // ACP adapter support: yes (adapter: "claude")
169
+ adapter: "claude",
170
+ promptFlag: "-p",
171
+ modelFlag: "--model",
172
+ modelStripProviderPrefix: true,
173
+ modelTransform: (model) => model.replaceAll(".", "-"),
174
+ defaultArgs: [
175
+ "--output-format",
176
+ "stream-json",
177
+ "--verbose"
178
+ ],
179
+ modes: {
180
+ yolo: ["--dangerously-skip-permissions"],
181
+ edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
182
+ read: ["--permission-mode", "plan"]
183
+ },
184
+ stdinMode: {
185
+ omitPrompt: true,
186
+ extraArgs: ["--input-format", "text"]
187
+ },
188
+ interactive: {
189
+ defaultArgs: []
190
+ },
191
+ resumeCommand: (threadId) => ["--resume", threadId]
192
+ };
193
+
194
+ // packages/agent-spawn/src/configs/codex.ts
195
+ var codexSpawnConfig = {
196
+ kind: "cli",
197
+ agentId: "codex",
198
+ // ACP adapter support: yes (adapter: "codex")
199
+ adapter: "codex",
200
+ promptFlag: "exec",
201
+ modelFlag: "--model",
202
+ modelStripProviderPrefix: true,
203
+ defaultArgs: ["--skip-git-repo-check", "--json"],
204
+ modes: {
205
+ yolo: ["-s", "danger-full-access"],
206
+ edit: ["-s", "workspace-write"],
207
+ read: ["-s", "read-only"]
208
+ },
209
+ stdinMode: {
210
+ omitPrompt: true,
211
+ extraArgs: ["-"]
212
+ },
213
+ interactive: {
214
+ defaultArgs: ["-a", "never"]
215
+ },
216
+ resumeCommand: (threadId, cwd) => ["resume", "-C", cwd, threadId]
217
+ };
218
+
219
+ // packages/agent-spawn/src/configs/opencode.ts
220
+ var openCodeSpawnConfig = {
221
+ kind: "cli",
222
+ agentId: "opencode",
223
+ // ACP adapter support: yes (adapter: "opencode").
224
+ // OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
225
+ // (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
226
+ adapter: "opencode",
227
+ promptFlag: "run",
228
+ modelFlag: "--model",
229
+ modelStripProviderPrefix: false,
230
+ modelTransform: (model) => {
231
+ return model.startsWith("poe/") ? model : `poe/${model}`;
232
+ },
233
+ defaultArgs: ["--format", "json"],
234
+ modes: {
235
+ yolo: [],
236
+ edit: [],
237
+ read: ["--agent", "plan"]
238
+ },
239
+ interactive: {
240
+ defaultArgs: [],
241
+ promptFlag: "--prompt"
242
+ },
243
+ resumeCommand: (threadId, cwd) => [cwd, "--session", threadId]
244
+ };
245
+
246
+ // packages/agent-spawn/src/configs/kimi.ts
247
+ var kimiSpawnConfig = {
248
+ kind: "cli",
249
+ agentId: "kimi",
250
+ // ACP adapter support: yes (adapter: "kimi").
251
+ // Kimi's `--output-format stream-json` emits OpenAI-style `{ role, content }` JSON
252
+ // (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
253
+ adapter: "kimi",
254
+ promptFlag: "-p",
255
+ modelStripProviderPrefix: true,
256
+ defaultArgs: ["--print", "--output-format", "stream-json"],
257
+ modes: {
258
+ yolo: ["--yolo"],
259
+ edit: [],
260
+ read: []
261
+ },
262
+ stdinMode: {
263
+ omitPrompt: true,
264
+ extraArgs: ["--input-format", "stream-json"]
265
+ },
266
+ interactive: {
267
+ defaultArgs: [],
268
+ promptFlag: "-p"
269
+ },
270
+ resumeCommand: (threadId, cwd) => ["--session", threadId, "--work-dir", cwd]
271
+ };
272
+
273
+ // packages/agent-spawn/src/configs/index.ts
274
+ var allSpawnConfigs = [
275
+ claudeCodeSpawnConfig,
276
+ codexSpawnConfig,
277
+ openCodeSpawnConfig,
278
+ kimiSpawnConfig
279
+ ];
280
+ var lookup2 = /* @__PURE__ */ new Map();
281
+ for (const config of allSpawnConfigs) {
282
+ lookup2.set(config.agentId, config);
283
+ }
284
+ function getSpawnConfig(input) {
285
+ const resolvedId = resolveAgentId(input);
286
+ if (!resolvedId) {
287
+ return void 0;
288
+ }
289
+ return lookup2.get(resolvedId);
290
+ }
291
+
292
+ // packages/agent-spawn/src/spawn.ts
293
+ import { spawn as spawnChildProcess } from "node:child_process";
294
+
295
+ // packages/agent-spawn/src/configs/resolve-config.ts
296
+ function resolveConfig(agentId) {
297
+ const resolvedAgentId = resolveAgentId(agentId);
298
+ if (!resolvedAgentId) {
299
+ throw new Error(`Unknown agent "${agentId}".`);
300
+ }
301
+ const agentDefinition = allAgents.find((agent) => agent.id === resolvedAgentId);
302
+ if (!agentDefinition) {
303
+ throw new Error(`Unknown agent "${agentId}".`);
304
+ }
305
+ const spawnConfig = getSpawnConfig(resolvedAgentId);
306
+ const binaryName = agentDefinition.binaryName;
307
+ return { agentId: resolvedAgentId, binaryName, spawnConfig };
308
+ }
309
+
310
+ // packages/agent-spawn/src/model-utils.ts
311
+ function stripModelNamespace(model) {
312
+ const slashIndex = model.indexOf("/");
313
+ return slashIndex === -1 ? model : model.slice(slashIndex + 1);
314
+ }
315
+
316
+ // packages/agent-spawn/src/spawn.ts
317
+ function resolveCliConfig(agentId) {
318
+ const resolved = resolveConfig(agentId);
319
+ if (!resolved.spawnConfig) {
320
+ throw new Error(`Agent "${resolved.agentId}" has no spawn config.`);
321
+ }
322
+ if (resolved.spawnConfig.kind !== "cli") {
323
+ throw new Error(`Agent "${resolved.agentId}" does not support CLI spawn.`);
324
+ }
325
+ if (!resolved.binaryName) {
326
+ throw new Error(`Agent "${resolved.agentId}" has no binaryName.`);
327
+ }
328
+ return {
329
+ agentId: resolved.agentId,
330
+ binaryName: resolved.binaryName,
331
+ spawnConfig: resolved.spawnConfig
332
+ };
333
+ }
334
+ function buildCliArgs(config, options, stdinMode) {
335
+ const args = stdinMode ? [
336
+ config.promptFlag,
337
+ ...stdinMode.omitPrompt ? [] : [options.prompt],
338
+ ...stdinMode.extraArgs
339
+ ] : [config.promptFlag, options.prompt];
340
+ if (options.model && config.modelFlag) {
341
+ let model = config.modelStripProviderPrefix ? stripModelNamespace(options.model) : options.model;
342
+ if (config.modelTransform) model = config.modelTransform(model);
343
+ args.push(config.modelFlag, model);
344
+ }
345
+ args.push(...config.defaultArgs);
346
+ args.push(...config.modes[options.mode ?? "yolo"]);
347
+ if (options.args && options.args.length > 0) {
348
+ args.push(...options.args);
349
+ }
350
+ return args;
351
+ }
352
+ function buildSpawnArgs(agentId, options) {
353
+ const { binaryName, spawnConfig } = resolveCliConfig(agentId);
354
+ const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
355
+ return { binaryName, args: buildCliArgs(spawnConfig, options, stdinMode) };
356
+ }
357
+
358
+ // packages/agent-spawn/src/spawn-interactive.ts
359
+ import { spawn as spawnChildProcess2 } from "node:child_process";
360
+
361
+ // packages/design-system/src/tokens/colors.ts
362
+ import chalk from "chalk";
363
+ var dark = {
364
+ header: (text4) => chalk.magentaBright.bold(text4),
365
+ divider: (text4) => chalk.dim(text4),
366
+ prompt: (text4) => chalk.cyan(text4),
367
+ number: (text4) => chalk.cyanBright(text4),
368
+ intro: (text4) => chalk.bgMagenta.white(` Poe - ${text4} `),
369
+ resolvedSymbol: chalk.magenta("\u25C7"),
370
+ errorSymbol: chalk.red("\u25A0"),
371
+ accent: (text4) => chalk.cyan(text4),
372
+ muted: (text4) => chalk.dim(text4),
373
+ success: (text4) => chalk.green(text4),
374
+ warning: (text4) => chalk.yellow(text4),
375
+ error: (text4) => chalk.red(text4),
376
+ info: (text4) => chalk.magenta(text4),
377
+ badge: (text4) => chalk.bgYellow.black(` ${text4} `)
378
+ };
379
+ var light = {
380
+ header: (text4) => chalk.hex("#a200ff").bold(text4),
381
+ divider: (text4) => chalk.hex("#666666")(text4),
382
+ prompt: (text4) => chalk.hex("#006699").bold(text4),
383
+ number: (text4) => chalk.hex("#0077cc").bold(text4),
384
+ intro: (text4) => chalk.bgHex("#a200ff").white(` Poe - ${text4} `),
385
+ resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
386
+ errorSymbol: chalk.hex("#cc0000")("\u25A0"),
387
+ accent: (text4) => chalk.hex("#006699").bold(text4),
388
+ muted: (text4) => chalk.hex("#666666")(text4),
389
+ success: (text4) => chalk.hex("#008800")(text4),
390
+ warning: (text4) => chalk.hex("#cc6600")(text4),
391
+ error: (text4) => chalk.hex("#cc0000")(text4),
392
+ info: (text4) => chalk.hex("#a200ff")(text4),
393
+ badge: (text4) => chalk.bgHex("#cc6600").white(` ${text4} `)
394
+ };
395
+
396
+ // packages/design-system/src/tokens/typography.ts
397
+ import chalk2 from "chalk";
398
+
399
+ // packages/design-system/src/components/text.ts
400
+ import chalk3 from "chalk";
401
+
402
+ // packages/design-system/src/internal/theme-detect.ts
403
+ function detectThemeFromEnv(env) {
404
+ const apple = env.APPLE_INTERFACE_STYLE;
405
+ if (typeof apple === "string") {
406
+ return apple.toLowerCase() === "dark" ? "dark" : "light";
407
+ }
408
+ const vscodeKind = env.VSCODE_COLOR_THEME_KIND;
409
+ if (typeof vscodeKind === "string") {
410
+ const normalized = vscodeKind.toLowerCase();
411
+ if (normalized.includes("light")) {
412
+ return "light";
413
+ }
414
+ if (normalized.includes("dark")) {
415
+ return "dark";
416
+ }
417
+ }
418
+ const colorFGBG = env.COLORFGBG;
419
+ if (typeof colorFGBG === "string") {
420
+ const parts = colorFGBG.split(";").map((part) => Number.parseInt(part, 10));
421
+ const background = parts.at(-1);
422
+ if (Number.isFinite(background)) {
423
+ return background >= 8 ? "light" : "dark";
424
+ }
425
+ }
426
+ return void 0;
427
+ }
428
+ function resolveThemeName(env = process.env) {
429
+ const raw = env.POE_CODE_THEME?.toLowerCase();
430
+ if (raw === "light" || raw === "dark") {
431
+ return raw;
432
+ }
433
+ const detected = detectThemeFromEnv(env);
434
+ if (detected) {
435
+ return detected;
436
+ }
437
+ return "dark";
438
+ }
439
+ var cachedTheme;
440
+ function getTheme(env) {
441
+ if (cachedTheme) {
442
+ return cachedTheme;
443
+ }
444
+ const themeName = resolveThemeName(env);
445
+ cachedTheme = themeName === "light" ? light : dark;
446
+ return cachedTheme;
447
+ }
448
+
449
+ // packages/design-system/src/components/symbols.ts
450
+ import chalk4 from "chalk";
451
+ var symbols = {
452
+ get info() {
453
+ return chalk4.magenta("\u25CF");
454
+ },
455
+ get success() {
456
+ return chalk4.magenta("\u25C6");
457
+ },
458
+ get resolved() {
459
+ const theme = getTheme();
460
+ return theme.resolvedSymbol;
461
+ },
462
+ get errorResolved() {
463
+ const theme = getTheme();
464
+ return theme.errorSymbol;
465
+ },
466
+ bar: "\u2502",
467
+ cornerTopRight: "\u256E",
468
+ cornerBottomRight: "\u256F",
469
+ warning: "\u25B2",
470
+ active: "\u25C6",
471
+ inactive: "\u25CB"
472
+ };
473
+
474
+ // packages/design-system/src/components/logger.ts
475
+ import { log } from "@clack/prompts";
476
+ import chalk5 from "chalk";
477
+
478
+ // packages/design-system/src/internal/output-format.ts
479
+ var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
480
+ var cached;
481
+ function resolveOutputFormat(env = process.env) {
482
+ if (cached) {
483
+ return cached;
484
+ }
485
+ const raw = env.OUTPUT_FORMAT?.toLowerCase();
486
+ cached = VALID_FORMATS.has(raw) ? raw : "terminal";
487
+ return cached;
488
+ }
489
+
490
+ // packages/design-system/src/components/logger.ts
491
+ function createLogger(emitter) {
492
+ const emit = (level, message) => {
493
+ if (emitter) {
494
+ emitter(message);
495
+ return;
496
+ }
497
+ if (resolveOutputFormat() !== "terminal") {
498
+ process.stdout.write(message + "\n");
499
+ return;
500
+ }
501
+ if (level === "success") {
502
+ log.message(message, { symbol: symbols.success });
503
+ return;
504
+ }
505
+ if (level === "warn") {
506
+ log.warn(message);
507
+ return;
508
+ }
509
+ if (level === "error") {
510
+ log.error(message);
511
+ return;
512
+ }
513
+ log.message(message, { symbol: symbols.info });
514
+ };
515
+ return {
516
+ info(message) {
517
+ emit("info", message);
518
+ },
519
+ success(message) {
520
+ emit("success", message);
521
+ },
522
+ warn(message) {
523
+ emit("warn", message);
524
+ },
525
+ error(message) {
526
+ emit("error", message);
527
+ },
528
+ resolved(label, value) {
529
+ if (emitter) {
530
+ emitter(`${label}: ${value}`);
531
+ return;
532
+ }
533
+ log.message(`${label}
534
+ ${value}`, { symbol: symbols.resolved });
535
+ },
536
+ errorResolved(label, value) {
537
+ if (emitter) {
538
+ emitter(`${label}: ${value}`);
539
+ return;
540
+ }
541
+ log.message(`${label}
542
+ ${value}`, { symbol: symbols.errorResolved });
543
+ },
544
+ message(message, symbol) {
545
+ if (emitter) {
546
+ emitter(message);
547
+ return;
548
+ }
549
+ log.message(message, { symbol: symbol ?? chalk5.gray("\u2502") });
550
+ }
551
+ };
552
+ }
553
+ var logger = createLogger();
554
+
555
+ // packages/design-system/src/components/table.ts
556
+ import { Table } from "console-table-printer";
557
+
558
+ // packages/design-system/src/acp/components.ts
559
+ import chalk6 from "chalk";
560
+ var AGENT_PREFIX = `${chalk6.green.bold("\u2713")} agent: `;
561
+
562
+ // packages/design-system/src/prompts/index.ts
563
+ import * as clack from "@clack/prompts";
564
+ import { isCancel, cancel, log as log2 } from "@clack/prompts";
565
+
566
+ // packages/design-system/src/static/spinner.ts
567
+ import chalk7 from "chalk";
568
+
569
+ // packages/design-system/src/static/menu.ts
570
+ import chalk8 from "chalk";
571
+
572
+ // packages/agent-spawn/src/acp/spawn.ts
573
+ import { spawn as spawnChildProcess3 } from "node:child_process";
574
+
575
+ // src/utils/command-checks.ts
576
+ function formatCommandRunnerResult(result) {
577
+ const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
578
+ const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
579
+ return `stdout:
580
+ ${stdout}
581
+ stderr:
582
+ ${stderr}`;
583
+ }
584
+ function createSpawnHealthCheck(agentId, options) {
585
+ const prompt = `Output exactly: ${options.expectedOutput}`;
586
+ const { binaryName, args } = buildSpawnArgs(agentId, {
587
+ prompt,
588
+ model: options.model,
589
+ mode: "yolo"
590
+ });
591
+ return {
592
+ id: `${agentId}-cli-health`,
593
+ description: `spawn ${agentId} (expecting "${options.expectedOutput}")`,
594
+ async run(context) {
595
+ if (context.isDryRun) {
596
+ context.logDryRun?.(
597
+ `Dry run: ${[binaryName, ...args].join(" ")} (expecting "${options.expectedOutput}")`
598
+ );
599
+ return;
600
+ }
601
+ const result = await context.runCommand(binaryName, args);
602
+ if (result.exitCode !== 0) {
603
+ throw new Error(
604
+ `spawn ${agentId} failed with exit code ${result.exitCode}.
605
+ ${formatCommandRunnerResult(result)}`
606
+ );
607
+ }
608
+ if (!result.stdout.includes(options.expectedOutput)) {
609
+ throw new Error(
610
+ `spawn ${agentId}: expected "${options.expectedOutput}" in stdout.
611
+ ${formatCommandRunnerResult(result)}`
612
+ );
613
+ }
614
+ }
615
+ };
616
+ }
617
+ function createBinaryExistsCheck(binaryName, id, description) {
618
+ return {
619
+ id,
620
+ description,
621
+ async run({ runCommand: runCommand2 }) {
622
+ const commonPaths = [
623
+ `/usr/local/bin/${binaryName}`,
624
+ `/usr/bin/${binaryName}`,
625
+ `$HOME/.local/bin/${binaryName}`,
626
+ `$HOME/.claude/local/bin/${binaryName}`
627
+ ];
628
+ const detectors = [
629
+ {
630
+ command: "which",
631
+ args: [binaryName],
632
+ validate: (result) => result.exitCode === 0
633
+ },
634
+ {
635
+ command: "where",
636
+ args: [binaryName],
637
+ validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
638
+ },
639
+ // Check common installation paths using shell expansion for $HOME
12
640
  {
13
- id: "install-codex-cli-npm",
14
- command: "npm",
15
- args: ["install", "-g", "@openai/codex"]
641
+ command: "sh",
642
+ args: [
643
+ "-c",
644
+ commonPaths.map((p) => `test -f "${p}"`).join(" || ")
645
+ ],
646
+ validate: (result) => result.exitCode === 0
16
647
  }
17
- ],
18
- successMessage: "Installed Codex CLI via npm."
648
+ ];
649
+ for (const detector of detectors) {
650
+ const result = await runCommand2(detector.command, detector.args);
651
+ if (detector.validate(result)) {
652
+ return;
653
+ }
654
+ }
655
+ throw new Error(`${binaryName} CLI binary not found on PATH.`);
656
+ }
657
+ };
658
+ }
659
+
660
+ // packages/config-mutations/src/mutations/config-mutation.ts
661
+ function merge(options) {
662
+ return {
663
+ kind: "configMerge",
664
+ target: options.target,
665
+ value: options.value,
666
+ format: options.format,
667
+ pruneByPrefix: options.pruneByPrefix,
668
+ label: options.label
669
+ };
670
+ }
671
+ function prune(options) {
672
+ return {
673
+ kind: "configPrune",
674
+ target: options.target,
675
+ shape: options.shape,
676
+ format: options.format,
677
+ onlyIf: options.onlyIf,
678
+ label: options.label
679
+ };
680
+ }
681
+ function transform(options) {
682
+ return {
683
+ kind: "configTransform",
684
+ target: options.target,
685
+ format: options.format,
686
+ transform: options.transform,
687
+ label: options.label
688
+ };
689
+ }
690
+ var configMutation = {
691
+ merge,
692
+ prune,
693
+ transform
19
694
  };
20
- function stripCodexConfiguration(document) {
21
- if (!isConfigObject(document)) {
22
- return { changed: false, empty: false };
695
+
696
+ // packages/config-mutations/src/mutations/file-mutation.ts
697
+ function ensureDirectory(options) {
698
+ return {
699
+ kind: "ensureDirectory",
700
+ path: options.path,
701
+ label: options.label
702
+ };
703
+ }
704
+ function remove(options) {
705
+ return {
706
+ kind: "removeFile",
707
+ target: options.target,
708
+ whenEmpty: options.whenEmpty,
709
+ whenContentMatches: options.whenContentMatches,
710
+ label: options.label
711
+ };
712
+ }
713
+ function removeDirectory(options) {
714
+ return {
715
+ kind: "removeDirectory",
716
+ path: options.path,
717
+ force: options.force,
718
+ label: options.label
719
+ };
720
+ }
721
+ function chmod(options) {
722
+ return {
723
+ kind: "chmod",
724
+ target: options.target,
725
+ mode: options.mode,
726
+ label: options.label
727
+ };
728
+ }
729
+ function backup(options) {
730
+ return {
731
+ kind: "backup",
732
+ target: options.target,
733
+ label: options.label
734
+ };
735
+ }
736
+ var fileMutation = {
737
+ ensureDirectory,
738
+ remove,
739
+ removeDirectory,
740
+ chmod,
741
+ backup
742
+ };
743
+
744
+ // packages/config-mutations/src/mutations/template-mutation.ts
745
+ function write(options) {
746
+ return {
747
+ kind: "templateWrite",
748
+ target: options.target,
749
+ templateId: options.templateId,
750
+ context: options.context,
751
+ label: options.label
752
+ };
753
+ }
754
+ function mergeToml(options) {
755
+ return {
756
+ kind: "templateMergeToml",
757
+ target: options.target,
758
+ templateId: options.templateId,
759
+ context: options.context,
760
+ label: options.label
761
+ };
762
+ }
763
+ function mergeJson(options) {
764
+ return {
765
+ kind: "templateMergeJson",
766
+ target: options.target,
767
+ templateId: options.templateId,
768
+ context: options.context,
769
+ label: options.label
770
+ };
771
+ }
772
+ var templateMutation = {
773
+ write,
774
+ mergeToml,
775
+ mergeJson
776
+ };
777
+
778
+ // packages/config-mutations/src/execution/apply-mutation.ts
779
+ import Mustache from "mustache";
780
+
781
+ // packages/config-mutations/src/formats/json.ts
782
+ import * as jsonc from "jsonc-parser";
783
+ function isConfigObject(value) {
784
+ return typeof value === "object" && value !== null && !Array.isArray(value);
785
+ }
786
+ function parse2(content) {
787
+ if (!content || content.trim() === "") {
788
+ return {};
789
+ }
790
+ const errors = [];
791
+ const parsed = jsonc.parse(content, errors, {
792
+ allowTrailingComma: true,
793
+ disallowComments: false
794
+ });
795
+ if (errors.length > 0) {
796
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
797
+ }
798
+ if (parsed === null || parsed === void 0) {
799
+ return {};
800
+ }
801
+ if (!isConfigObject(parsed)) {
802
+ throw new Error("Expected JSON object.");
803
+ }
804
+ return parsed;
805
+ }
806
+ function serialize(obj) {
807
+ return `${JSON.stringify(obj, null, 2)}
808
+ `;
809
+ }
810
+ function merge2(base, patch) {
811
+ const result = { ...base };
812
+ for (const [key, value] of Object.entries(patch)) {
813
+ if (value === void 0) {
814
+ continue;
815
+ }
816
+ const existing = result[key];
817
+ if (isConfigObject(existing) && isConfigObject(value)) {
818
+ result[key] = merge2(existing, value);
819
+ continue;
820
+ }
821
+ result[key] = value;
822
+ }
823
+ return result;
824
+ }
825
+ function prune2(obj, shape) {
826
+ let changed = false;
827
+ const result = { ...obj };
828
+ for (const [key, pattern] of Object.entries(shape)) {
829
+ if (!(key in result)) {
830
+ continue;
831
+ }
832
+ const current = result[key];
833
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
834
+ delete result[key];
835
+ changed = true;
836
+ continue;
837
+ }
838
+ if (isConfigObject(pattern) && isConfigObject(current)) {
839
+ const { changed: childChanged, result: childResult } = prune2(
840
+ current,
841
+ pattern
842
+ );
843
+ if (childChanged) {
844
+ changed = true;
845
+ }
846
+ if (Object.keys(childResult).length === 0) {
847
+ delete result[key];
848
+ } else {
849
+ result[key] = childResult;
850
+ }
851
+ continue;
852
+ }
853
+ delete result[key];
854
+ changed = true;
855
+ }
856
+ return { changed, result };
857
+ }
858
+ var jsonFormat = {
859
+ parse: parse2,
860
+ serialize,
861
+ merge: merge2,
862
+ prune: prune2
863
+ };
864
+
865
+ // packages/config-mutations/src/formats/toml.ts
866
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
867
+ function isConfigObject2(value) {
868
+ return typeof value === "object" && value !== null && !Array.isArray(value);
869
+ }
870
+ function parse3(content) {
871
+ if (!content || content.trim() === "") {
872
+ return {};
873
+ }
874
+ const parsed = parseToml(content);
875
+ if (!isConfigObject2(parsed)) {
876
+ throw new Error("Expected TOML document to be a table.");
877
+ }
878
+ return parsed;
879
+ }
880
+ function serialize2(obj) {
881
+ const serialized = stringifyToml(obj);
882
+ return serialized.endsWith("\n") ? serialized : `${serialized}
883
+ `;
884
+ }
885
+ function merge3(base, patch) {
886
+ const result = { ...base };
887
+ for (const [key, value] of Object.entries(patch)) {
888
+ if (value === void 0) {
889
+ continue;
890
+ }
891
+ const existing = result[key];
892
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
893
+ result[key] = merge3(existing, value);
894
+ continue;
23
895
  }
24
- if (document["model_provider"] !== CODEX_PROVIDER_ID) {
25
- return { changed: false, empty: false };
896
+ result[key] = value;
897
+ }
898
+ return result;
899
+ }
900
+ function prune3(obj, shape) {
901
+ let changed = false;
902
+ const result = { ...obj };
903
+ for (const [key, pattern] of Object.entries(shape)) {
904
+ if (!(key in result)) {
905
+ continue;
26
906
  }
27
- const providers = document["model_providers"];
28
- if (!isConfigObject(providers) || !(CODEX_PROVIDER_ID in providers)) {
29
- return { changed: false, empty: false };
907
+ const current = result[key];
908
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
909
+ delete result[key];
910
+ changed = true;
911
+ continue;
30
912
  }
31
- delete document["model_provider"];
32
- delete document["model"];
33
- delete document["model_reasoning_effort"];
34
- delete providers[CODEX_PROVIDER_ID];
35
- if (isTableEmpty(providers)) {
36
- delete document["model_providers"];
913
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
914
+ const { changed: childChanged, result: childResult } = prune3(
915
+ current,
916
+ pattern
917
+ );
918
+ if (childChanged) {
919
+ changed = true;
920
+ }
921
+ if (Object.keys(childResult).length === 0) {
922
+ delete result[key];
923
+ } else {
924
+ result[key] = childResult;
925
+ }
926
+ continue;
37
927
  }
928
+ delete result[key];
929
+ changed = true;
930
+ }
931
+ return { changed, result };
932
+ }
933
+ var tomlFormat = {
934
+ parse: parse3,
935
+ serialize: serialize2,
936
+ merge: merge3,
937
+ prune: prune3
938
+ };
939
+
940
+ // packages/config-mutations/src/formats/index.ts
941
+ var formatRegistry = {
942
+ json: jsonFormat,
943
+ toml: tomlFormat
944
+ };
945
+ var extensionMap = {
946
+ ".json": "json",
947
+ ".toml": "toml"
948
+ };
949
+ function getConfigFormat(pathOrFormat) {
950
+ if (pathOrFormat in formatRegistry) {
951
+ return formatRegistry[pathOrFormat];
952
+ }
953
+ const ext = getExtension(pathOrFormat);
954
+ const formatName = extensionMap[ext];
955
+ if (!formatName) {
956
+ throw new Error(
957
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
958
+ );
959
+ }
960
+ return formatRegistry[formatName];
961
+ }
962
+ function detectFormat(path2) {
963
+ const ext = getExtension(path2);
964
+ return extensionMap[ext];
965
+ }
966
+ function getExtension(path2) {
967
+ const lastDot = path2.lastIndexOf(".");
968
+ if (lastDot === -1) {
969
+ return "";
970
+ }
971
+ return path2.slice(lastDot).toLowerCase();
972
+ }
973
+
974
+ // packages/config-mutations/src/execution/path-utils.ts
975
+ import path from "node:path";
976
+ function expandHome(targetPath, homeDir) {
977
+ if (!targetPath?.startsWith("~")) {
978
+ return targetPath;
979
+ }
980
+ if (targetPath.startsWith("~./")) {
981
+ targetPath = `~/.${targetPath.slice(3)}`;
982
+ }
983
+ let remainder = targetPath.slice(1);
984
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
985
+ remainder = remainder.slice(1);
986
+ } else if (remainder.startsWith(".")) {
987
+ remainder = remainder.slice(1);
988
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
989
+ remainder = remainder.slice(1);
990
+ }
991
+ }
992
+ return remainder.length === 0 ? homeDir : path.join(homeDir, remainder);
993
+ }
994
+ function validateHomePath(targetPath) {
995
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
996
+ throw new Error("Target path must be a non-empty string.");
997
+ }
998
+ if (!targetPath.startsWith("~")) {
999
+ throw new Error(
1000
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
1001
+ );
1002
+ }
1003
+ }
1004
+ function resolvePath(rawPath, homeDir, pathMapper) {
1005
+ validateHomePath(rawPath);
1006
+ const expanded = expandHome(rawPath, homeDir);
1007
+ if (!pathMapper) {
1008
+ return expanded;
1009
+ }
1010
+ const rawDirectory = path.dirname(expanded);
1011
+ const mappedDirectory = pathMapper.mapTargetDirectory({
1012
+ targetDirectory: rawDirectory
1013
+ });
1014
+ const filename = path.basename(expanded);
1015
+ return filename.length === 0 ? mappedDirectory : path.join(mappedDirectory, filename);
1016
+ }
1017
+
1018
+ // packages/config-mutations/src/fs-utils.ts
1019
+ function isNotFound(error) {
1020
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1021
+ }
1022
+ async function readFileIfExists(fs, target) {
1023
+ try {
1024
+ return await fs.readFile(target, "utf8");
1025
+ } catch (error) {
1026
+ if (isNotFound(error)) {
1027
+ return null;
1028
+ }
1029
+ throw error;
1030
+ }
1031
+ }
1032
+ async function pathExists(fs, target) {
1033
+ try {
1034
+ await fs.stat(target);
1035
+ return true;
1036
+ } catch (error) {
1037
+ if (isNotFound(error)) {
1038
+ return false;
1039
+ }
1040
+ throw error;
1041
+ }
1042
+ }
1043
+ function createTimestamp() {
1044
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
1045
+ }
1046
+
1047
+ // packages/config-mutations/src/execution/apply-mutation.ts
1048
+ function resolveValue(resolver, options) {
1049
+ if (typeof resolver === "function") {
1050
+ return resolver(options);
1051
+ }
1052
+ return resolver;
1053
+ }
1054
+ function createInvalidDocumentBackupPath(targetPath) {
1055
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
1056
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
1057
+ }
1058
+ async function backupInvalidDocument(fs, targetPath, content) {
1059
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
1060
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
1061
+ }
1062
+ function describeMutation(kind, targetPath) {
1063
+ const displayPath = targetPath ?? "target";
1064
+ switch (kind) {
1065
+ case "ensureDirectory":
1066
+ return `Create ${displayPath}`;
1067
+ case "removeDirectory":
1068
+ return `Remove directory ${displayPath}`;
1069
+ case "backup":
1070
+ return `Backup ${displayPath}`;
1071
+ case "templateWrite":
1072
+ return `Write ${displayPath}`;
1073
+ case "chmod":
1074
+ return `Set permissions on ${displayPath}`;
1075
+ case "removeFile":
1076
+ return `Remove ${displayPath}`;
1077
+ case "configMerge":
1078
+ case "configPrune":
1079
+ case "configTransform":
1080
+ case "templateMergeToml":
1081
+ case "templateMergeJson":
1082
+ return `Update ${displayPath}`;
1083
+ default:
1084
+ return "Operation";
1085
+ }
1086
+ }
1087
+ function pruneKeysByPrefix(table, prefix) {
1088
+ const result = {};
1089
+ for (const [key, value] of Object.entries(table)) {
1090
+ if (!key.startsWith(prefix)) {
1091
+ result[key] = value;
1092
+ }
1093
+ }
1094
+ return result;
1095
+ }
1096
+ function isConfigObject3(value) {
1097
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1098
+ }
1099
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
1100
+ const result = { ...base };
1101
+ const prefixMap = pruneByPrefix ?? {};
1102
+ for (const [key, value] of Object.entries(patch)) {
1103
+ const current = result[key];
1104
+ const prefix = prefixMap[key];
1105
+ if (isConfigObject3(current) && isConfigObject3(value)) {
1106
+ if (prefix) {
1107
+ const pruned = pruneKeysByPrefix(current, prefix);
1108
+ result[key] = { ...pruned, ...value };
1109
+ } else {
1110
+ result[key] = mergeWithPruneByPrefix(
1111
+ current,
1112
+ value,
1113
+ prefixMap
1114
+ );
1115
+ }
1116
+ continue;
1117
+ }
1118
+ result[key] = value;
1119
+ }
1120
+ return result;
1121
+ }
1122
+ async function applyMutation(mutation, context, options) {
1123
+ switch (mutation.kind) {
1124
+ case "ensureDirectory":
1125
+ return applyEnsureDirectory(mutation, context, options);
1126
+ case "removeDirectory":
1127
+ return applyRemoveDirectory(mutation, context, options);
1128
+ case "removeFile":
1129
+ return applyRemoveFile(mutation, context, options);
1130
+ case "chmod":
1131
+ return applyChmod(mutation, context, options);
1132
+ case "backup":
1133
+ return applyBackup(mutation, context, options);
1134
+ case "configMerge":
1135
+ return applyConfigMerge(mutation, context, options);
1136
+ case "configPrune":
1137
+ return applyConfigPrune(mutation, context, options);
1138
+ case "configTransform":
1139
+ return applyConfigTransform(mutation, context, options);
1140
+ case "templateWrite":
1141
+ return applyTemplateWrite(mutation, context, options);
1142
+ case "templateMergeToml":
1143
+ return applyTemplateMerge(mutation, context, options, "toml");
1144
+ case "templateMergeJson":
1145
+ return applyTemplateMerge(mutation, context, options, "json");
1146
+ default: {
1147
+ const never = mutation;
1148
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
1149
+ }
1150
+ }
1151
+ }
1152
+ async function applyEnsureDirectory(mutation, context, options) {
1153
+ const rawPath = resolveValue(mutation.path, options);
1154
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1155
+ const details = {
1156
+ kind: mutation.kind,
1157
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1158
+ targetPath
1159
+ };
1160
+ const existed = await pathExists(context.fs, targetPath);
1161
+ if (!context.dryRun) {
1162
+ await context.fs.mkdir(targetPath, { recursive: true });
1163
+ }
1164
+ return {
1165
+ outcome: {
1166
+ changed: !existed,
1167
+ effect: "mkdir",
1168
+ detail: existed ? "noop" : "create"
1169
+ },
1170
+ details
1171
+ };
1172
+ }
1173
+ async function applyRemoveDirectory(mutation, context, options) {
1174
+ const rawPath = resolveValue(mutation.path, options);
1175
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1176
+ const details = {
1177
+ kind: mutation.kind,
1178
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1179
+ targetPath
1180
+ };
1181
+ const existed = await pathExists(context.fs, targetPath);
1182
+ if (!existed) {
38
1183
  return {
39
- changed: true,
40
- empty: isTableEmpty(document)
1184
+ outcome: { changed: false, effect: "none", detail: "noop" },
1185
+ details
41
1186
  };
1187
+ }
1188
+ if (typeof context.fs.rm !== "function") {
1189
+ return {
1190
+ outcome: { changed: false, effect: "none", detail: "noop" },
1191
+ details
1192
+ };
1193
+ }
1194
+ if (mutation.force) {
1195
+ if (!context.dryRun) {
1196
+ await context.fs.rm(targetPath, { recursive: true, force: true });
1197
+ }
1198
+ return {
1199
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1200
+ details
1201
+ };
1202
+ }
1203
+ const entries = await context.fs.readdir(targetPath);
1204
+ if (entries.length > 0) {
1205
+ return {
1206
+ outcome: { changed: false, effect: "none", detail: "noop" },
1207
+ details
1208
+ };
1209
+ }
1210
+ if (!context.dryRun) {
1211
+ await context.fs.rm(targetPath, { recursive: true, force: true });
1212
+ }
1213
+ return {
1214
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1215
+ details
1216
+ };
42
1217
  }
43
- function isTableEmpty(value) {
44
- return isConfigObject(value) && Object.keys(value).length === 0;
45
- }
46
- export const codexService = createProvider({
47
- ...codexAgent,
48
- supportsStdinPrompt: true,
49
- configurePrompts: {
50
- model: {
51
- label: "Codex model",
52
- defaultValue: DEFAULT_CODEX_MODEL,
53
- choices: CODEX_MODELS.map((id) => ({
54
- title: id,
55
- value: id
56
- }))
57
- },
58
- reasoningEffort: {
59
- label: "Codex reasoning effort",
60
- defaultValue: DEFAULT_REASONING
61
- }
1218
+ async function applyRemoveFile(mutation, context, options) {
1219
+ const rawPath = resolveValue(mutation.target, options);
1220
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1221
+ const details = {
1222
+ kind: mutation.kind,
1223
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1224
+ targetPath
1225
+ };
1226
+ try {
1227
+ const content = await context.fs.readFile(targetPath, "utf8");
1228
+ const trimmed = content.trim();
1229
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
1230
+ return {
1231
+ outcome: { changed: false, effect: "none", detail: "noop" },
1232
+ details
1233
+ };
1234
+ }
1235
+ if (mutation.whenEmpty && trimmed.length > 0) {
1236
+ return {
1237
+ outcome: { changed: false, effect: "none", detail: "noop" },
1238
+ details
1239
+ };
1240
+ }
1241
+ if (!context.dryRun) {
1242
+ await context.fs.unlink(targetPath);
1243
+ }
1244
+ return {
1245
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1246
+ details
1247
+ };
1248
+ } catch (error) {
1249
+ if (isNotFound(error)) {
1250
+ return {
1251
+ outcome: { changed: false, effect: "none", detail: "noop" },
1252
+ details
1253
+ };
1254
+ }
1255
+ throw error;
1256
+ }
1257
+ }
1258
+ async function applyChmod(mutation, context, options) {
1259
+ const rawPath = resolveValue(mutation.target, options);
1260
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1261
+ const details = {
1262
+ kind: mutation.kind,
1263
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1264
+ targetPath
1265
+ };
1266
+ if (typeof context.fs.chmod !== "function") {
1267
+ return {
1268
+ outcome: { changed: false, effect: "none", detail: "noop" },
1269
+ details
1270
+ };
1271
+ }
1272
+ try {
1273
+ const stat = await context.fs.stat(targetPath);
1274
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
1275
+ if (currentMode === mutation.mode) {
1276
+ return {
1277
+ outcome: { changed: false, effect: "none", detail: "noop" },
1278
+ details
1279
+ };
1280
+ }
1281
+ if (!context.dryRun) {
1282
+ await context.fs.chmod(targetPath, mutation.mode);
1283
+ }
1284
+ return {
1285
+ outcome: { changed: true, effect: "chmod", detail: "update" },
1286
+ details
1287
+ };
1288
+ } catch (error) {
1289
+ if (isNotFound(error)) {
1290
+ return {
1291
+ outcome: { changed: false, effect: "none", detail: "noop" },
1292
+ details
1293
+ };
1294
+ }
1295
+ throw error;
1296
+ }
1297
+ }
1298
+ async function applyBackup(mutation, context, options) {
1299
+ const rawPath = resolveValue(mutation.target, options);
1300
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1301
+ const details = {
1302
+ kind: mutation.kind,
1303
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1304
+ targetPath
1305
+ };
1306
+ const content = await readFileIfExists(context.fs, targetPath);
1307
+ if (content === null) {
1308
+ return {
1309
+ outcome: { changed: false, effect: "none", detail: "noop" },
1310
+ details
1311
+ };
1312
+ }
1313
+ if (!context.dryRun) {
1314
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
1315
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
1316
+ }
1317
+ return {
1318
+ outcome: { changed: true, effect: "copy", detail: "backup" },
1319
+ details
1320
+ };
1321
+ }
1322
+ async function applyConfigMerge(mutation, context, options) {
1323
+ const rawPath = resolveValue(mutation.target, options);
1324
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1325
+ const details = {
1326
+ kind: mutation.kind,
1327
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1328
+ targetPath
1329
+ };
1330
+ const formatName = mutation.format ?? detectFormat(rawPath);
1331
+ if (!formatName) {
1332
+ throw new Error(
1333
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1334
+ );
1335
+ }
1336
+ const format = getConfigFormat(formatName);
1337
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1338
+ let current;
1339
+ try {
1340
+ current = rawContent === null ? {} : format.parse(rawContent);
1341
+ } catch {
1342
+ if (rawContent !== null) {
1343
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1344
+ }
1345
+ current = {};
1346
+ }
1347
+ const value = resolveValue(mutation.value, options);
1348
+ let merged;
1349
+ if (mutation.pruneByPrefix) {
1350
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
1351
+ } else {
1352
+ merged = format.merge(current, value);
1353
+ }
1354
+ const serialized = format.serialize(merged);
1355
+ const changed = serialized !== rawContent;
1356
+ if (changed && !context.dryRun) {
1357
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1358
+ }
1359
+ return {
1360
+ outcome: {
1361
+ changed,
1362
+ effect: changed ? "write" : "none",
1363
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
62
1364
  },
63
- isolatedEnv: {
64
- agentBinary: codexAgent.binaryName,
65
- configProbe: { kind: "isolatedFile", relativePath: "config.toml" },
66
- env: {
67
- CODEX_HOME: { kind: "isolatedDir" },
68
- XDG_CONFIG_HOME: { kind: "isolatedDir" }
69
- }
1365
+ details
1366
+ };
1367
+ }
1368
+ async function applyConfigPrune(mutation, context, options) {
1369
+ const rawPath = resolveValue(mutation.target, options);
1370
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1371
+ const details = {
1372
+ kind: mutation.kind,
1373
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1374
+ targetPath
1375
+ };
1376
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1377
+ if (rawContent === null) {
1378
+ return {
1379
+ outcome: { changed: false, effect: "none", detail: "noop" },
1380
+ details
1381
+ };
1382
+ }
1383
+ const formatName = mutation.format ?? detectFormat(rawPath);
1384
+ if (!formatName) {
1385
+ throw new Error(
1386
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1387
+ );
1388
+ }
1389
+ const format = getConfigFormat(formatName);
1390
+ let current;
1391
+ try {
1392
+ current = format.parse(rawContent);
1393
+ } catch {
1394
+ return {
1395
+ outcome: { changed: false, effect: "none", detail: "noop" },
1396
+ details
1397
+ };
1398
+ }
1399
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
1400
+ return {
1401
+ outcome: { changed: false, effect: "none", detail: "noop" },
1402
+ details
1403
+ };
1404
+ }
1405
+ const shape = resolveValue(mutation.shape, options);
1406
+ const { changed, result } = format.prune(current, shape);
1407
+ if (!changed) {
1408
+ return {
1409
+ outcome: { changed: false, effect: "none", detail: "noop" },
1410
+ details
1411
+ };
1412
+ }
1413
+ if (Object.keys(result).length === 0) {
1414
+ if (!context.dryRun) {
1415
+ await context.fs.unlink(targetPath);
1416
+ }
1417
+ return {
1418
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1419
+ details
1420
+ };
1421
+ }
1422
+ const serialized = format.serialize(result);
1423
+ if (!context.dryRun) {
1424
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1425
+ }
1426
+ return {
1427
+ outcome: { changed: true, effect: "write", detail: "update" },
1428
+ details
1429
+ };
1430
+ }
1431
+ async function applyConfigTransform(mutation, context, options) {
1432
+ const rawPath = resolveValue(mutation.target, options);
1433
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1434
+ const details = {
1435
+ kind: mutation.kind,
1436
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1437
+ targetPath
1438
+ };
1439
+ const formatName = mutation.format ?? detectFormat(rawPath);
1440
+ if (!formatName) {
1441
+ throw new Error(
1442
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1443
+ );
1444
+ }
1445
+ const format = getConfigFormat(formatName);
1446
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1447
+ let current;
1448
+ try {
1449
+ current = rawContent === null ? {} : format.parse(rawContent);
1450
+ } catch {
1451
+ if (rawContent !== null) {
1452
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1453
+ }
1454
+ current = {};
1455
+ }
1456
+ const { content: transformed, changed } = mutation.transform(current, options);
1457
+ if (!changed) {
1458
+ return {
1459
+ outcome: { changed: false, effect: "none", detail: "noop" },
1460
+ details
1461
+ };
1462
+ }
1463
+ if (transformed === null) {
1464
+ if (rawContent === null) {
1465
+ return {
1466
+ outcome: { changed: false, effect: "none", detail: "noop" },
1467
+ details
1468
+ };
1469
+ }
1470
+ if (!context.dryRun) {
1471
+ await context.fs.unlink(targetPath);
1472
+ }
1473
+ return {
1474
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1475
+ details
1476
+ };
1477
+ }
1478
+ const serialized = format.serialize(transformed);
1479
+ if (!context.dryRun) {
1480
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1481
+ }
1482
+ return {
1483
+ outcome: {
1484
+ changed: true,
1485
+ effect: "write",
1486
+ detail: rawContent === null ? "create" : "update"
1487
+ },
1488
+ details
1489
+ };
1490
+ }
1491
+ async function applyTemplateWrite(mutation, context, options) {
1492
+ if (!context.templates) {
1493
+ throw new Error(
1494
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
1495
+ );
1496
+ }
1497
+ const rawPath = resolveValue(mutation.target, options);
1498
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1499
+ const details = {
1500
+ kind: mutation.kind,
1501
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1502
+ targetPath
1503
+ };
1504
+ const template = await context.templates(mutation.templateId);
1505
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
1506
+ const rendered = Mustache.render(template, templateContext);
1507
+ const existed = await pathExists(context.fs, targetPath);
1508
+ if (!context.dryRun) {
1509
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
1510
+ }
1511
+ return {
1512
+ outcome: {
1513
+ changed: true,
1514
+ effect: "write",
1515
+ detail: existed ? "update" : "create"
1516
+ },
1517
+ details
1518
+ };
1519
+ }
1520
+ async function applyTemplateMerge(mutation, context, options, formatName) {
1521
+ if (!context.templates) {
1522
+ throw new Error(
1523
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
1524
+ );
1525
+ }
1526
+ const rawPath = resolveValue(mutation.target, options);
1527
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1528
+ const details = {
1529
+ kind: mutation.kind,
1530
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1531
+ targetPath
1532
+ };
1533
+ const format = getConfigFormat(formatName);
1534
+ const template = await context.templates(mutation.templateId);
1535
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
1536
+ const rendered = Mustache.render(template, templateContext);
1537
+ let templateDoc;
1538
+ try {
1539
+ templateDoc = format.parse(rendered);
1540
+ } catch (error) {
1541
+ throw new Error(
1542
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
1543
+ { cause: error }
1544
+ );
1545
+ }
1546
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1547
+ let current;
1548
+ try {
1549
+ current = rawContent === null ? {} : format.parse(rawContent);
1550
+ } catch {
1551
+ if (rawContent !== null) {
1552
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1553
+ }
1554
+ current = {};
1555
+ }
1556
+ const merged = format.merge(current, templateDoc);
1557
+ const serialized = format.serialize(merged);
1558
+ const changed = serialized !== rawContent;
1559
+ if (changed && !context.dryRun) {
1560
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1561
+ }
1562
+ return {
1563
+ outcome: {
1564
+ changed,
1565
+ effect: changed ? "write" : "none",
1566
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
70
1567
  },
71
- test(context) {
72
- return context.runCheck(createSpawnHealthCheck("codex", {
73
- model: context.model ?? DEFAULT_CODEX_MODEL,
74
- expectedOutput: "CODEX_OK"
75
- }));
1568
+ details
1569
+ };
1570
+ }
1571
+
1572
+ // packages/config-mutations/src/execution/run-mutations.ts
1573
+ async function runMutations(mutations, context, options) {
1574
+ const effects = [];
1575
+ let anyChanged = false;
1576
+ const resolverOptions = options ?? {};
1577
+ for (const mutation of mutations) {
1578
+ const { outcome } = await executeMutation(
1579
+ mutation,
1580
+ context,
1581
+ resolverOptions
1582
+ );
1583
+ effects.push(outcome);
1584
+ if (outcome.changed) {
1585
+ anyChanged = true;
1586
+ }
1587
+ }
1588
+ return {
1589
+ changed: anyChanged,
1590
+ effects
1591
+ };
1592
+ }
1593
+ async function executeMutation(mutation, context, options) {
1594
+ context.observers?.onStart?.({
1595
+ kind: mutation.kind,
1596
+ label: mutation.label ?? mutation.kind,
1597
+ targetPath: void 0
1598
+ // Will be resolved during apply
1599
+ });
1600
+ try {
1601
+ const { outcome, details } = await applyMutation(mutation, context, options);
1602
+ context.observers?.onComplete?.(details, outcome);
1603
+ return { outcome, details };
1604
+ } catch (error) {
1605
+ context.observers?.onError?.(
1606
+ {
1607
+ kind: mutation.kind,
1608
+ label: mutation.label ?? mutation.kind,
1609
+ targetPath: void 0
1610
+ },
1611
+ error
1612
+ );
1613
+ throw error;
1614
+ }
1615
+ }
1616
+
1617
+ // packages/config-mutations/src/template/render.ts
1618
+ import Mustache2 from "mustache";
1619
+ var originalEscape = Mustache2.escape;
1620
+
1621
+ // packages/config-mutations/src/types.ts
1622
+ function isConfigObject4(value) {
1623
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1624
+ }
1625
+
1626
+ // src/services/service-install.ts
1627
+ async function runServiceInstall(definition, context) {
1628
+ const checkContext = {
1629
+ isDryRun: context.isDryRun,
1630
+ runCommand: context.runCommand
1631
+ };
1632
+ let needsInstall = false;
1633
+ try {
1634
+ await definition.check.run(checkContext);
1635
+ context.logger(`${definition.summary} already installed.`);
1636
+ } catch (error) {
1637
+ const detail = error instanceof Error ? error.message : String(error);
1638
+ context.logger(`${definition.summary} not detected: ${detail}`);
1639
+ needsInstall = true;
1640
+ }
1641
+ if (!needsInstall) {
1642
+ return false;
1643
+ }
1644
+ if (context.isDryRun) {
1645
+ logInstallDryRun(definition, context);
1646
+ return true;
1647
+ }
1648
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1649
+ for (const step of platformSteps) {
1650
+ await runInstallStep(step, context);
1651
+ }
1652
+ await definition.check.run(checkContext);
1653
+ if (definition.postChecks) {
1654
+ for (const postCheck of definition.postChecks) {
1655
+ await postCheck.run(checkContext);
1656
+ }
1657
+ }
1658
+ context.logger(
1659
+ definition.successMessage ?? `${definition.summary} installed.`
1660
+ );
1661
+ return true;
1662
+ }
1663
+ function describeInstallCommand(step) {
1664
+ return `[${step.id}] ${formatCommand2(step.command, step.args)}`;
1665
+ }
1666
+ function formatCommand2(command, args) {
1667
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
1668
+ }
1669
+ function quoteIfNeeded(value) {
1670
+ if (value.length === 0) {
1671
+ return '""';
1672
+ }
1673
+ if (value.includes(" ") || value.includes(" ") || value.includes("\n")) {
1674
+ return `"${value.replaceAll('"', '\\"')}"`;
1675
+ }
1676
+ return value;
1677
+ }
1678
+ function filterStepsByPlatform(steps, platform) {
1679
+ return steps.filter(
1680
+ (step) => !step.platforms || step.platforms.includes(platform)
1681
+ );
1682
+ }
1683
+ function logInstallDryRun(definition, context) {
1684
+ context.logger(`Dry run: would install ${definition.summary}.`);
1685
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1686
+ for (const step of platformSteps) {
1687
+ context.logger(`Dry run: ${describeInstallCommand(step)}`);
1688
+ }
1689
+ }
1690
+ async function runInstallStep(step, context) {
1691
+ context.logger(`Running ${describeInstallCommand(step)}`);
1692
+ const result = await context.runCommand(step.command, step.args);
1693
+ if (result.exitCode !== 0) {
1694
+ const stderr = result.stderr.trim();
1695
+ const suffix = stderr.length > 0 ? `: ${stderr}` : "";
1696
+ throw new Error(
1697
+ `${describeInstallCommand(step)} failed with exit code ${result.exitCode}${suffix}`
1698
+ );
1699
+ }
1700
+ }
1701
+
1702
+ // src/providers/create-provider.ts
1703
+ var templateImports = {
1704
+ "python/env.hbs": () => Promise.resolve().then(() => __toESM(require_env(), 1)),
1705
+ "python/main.py.hbs": () => Promise.resolve().then(() => __toESM(require_main_py(), 1)),
1706
+ "python/requirements.txt.hbs": () => Promise.resolve().then(() => __toESM(require_requirements_txt(), 1)),
1707
+ "codex/config.toml.hbs": () => Promise.resolve().then(() => __toESM(require_config_toml(), 1))
1708
+ };
1709
+ async function loadTemplate(templateId) {
1710
+ const loader = templateImports[templateId];
1711
+ if (!loader) {
1712
+ throw new Error(`Template not found: ${templateId}`);
1713
+ }
1714
+ const module = await loader();
1715
+ return module.default;
1716
+ }
1717
+ function createProvider(opts) {
1718
+ const provider2 = {
1719
+ id: opts.id,
1720
+ summary: opts.summary,
1721
+ name: opts.name,
1722
+ aliases: opts.aliases,
1723
+ label: opts.label,
1724
+ branding: opts.branding,
1725
+ disabled: opts.disabled,
1726
+ supportsStdinPrompt: opts.supportsStdinPrompt,
1727
+ configurePrompts: opts.configurePrompts,
1728
+ postConfigureMessages: opts.postConfigureMessages,
1729
+ isolatedEnv: opts.isolatedEnv,
1730
+ async configure(context, runOptions) {
1731
+ await runMutations(opts.manifest.configure, {
1732
+ fs: context.fs,
1733
+ homeDir: context.env.homeDir,
1734
+ observers: runOptions?.observers,
1735
+ templates: loadTemplate,
1736
+ pathMapper: context.pathMapper
1737
+ }, context.options);
1738
+ context.command.flushDryRun({ emitIfEmpty: false });
76
1739
  },
77
- manifest: {
78
- configure: [
79
- fileMutation.ensureDirectory({ path: "~/.codex" }),
80
- fileMutation.backup({ target: "~/.codex/config.toml" }),
81
- templateMutation.mergeToml({
82
- target: "~/.codex/config.toml",
83
- templateId: "codex/config.toml.hbs",
84
- context: (ctx) => {
85
- const options = ctx;
86
- return {
87
- apiKey: options.apiKey,
88
- baseUrl: options.env.poeApiBaseUrl,
89
- model: stripModelNamespace(options.model ?? DEFAULT_CODEX_MODEL),
90
- reasoningEffort: options.reasoningEffort
91
- };
92
- }
93
- })
94
- ],
95
- unconfigure: [
96
- configMutation.transform({
97
- target: "~/.codex/config.toml",
98
- transform: (document) => {
99
- const result = stripCodexConfiguration(document);
100
- if (!result.changed) {
101
- return { changed: false, content: document };
102
- }
103
- return {
104
- changed: true,
105
- content: result.empty ? null : document
106
- };
107
- }
108
- })
109
- ]
1740
+ async unconfigure(context, runOptions) {
1741
+ if (!opts.manifest.unconfigure) {
1742
+ return false;
1743
+ }
1744
+ const result = await runMutations(opts.manifest.unconfigure, {
1745
+ fs: context.fs,
1746
+ homeDir: context.env.homeDir,
1747
+ observers: runOptions?.observers,
1748
+ templates: loadTemplate,
1749
+ pathMapper: context.pathMapper
1750
+ }, context.options);
1751
+ context.command.flushDryRun({ emitIfEmpty: false });
1752
+ return result.changed;
1753
+ }
1754
+ };
1755
+ if (opts.install) {
1756
+ provider2.install = createInstallRunner(opts.install);
1757
+ }
1758
+ if (opts.test) {
1759
+ provider2.test = opts.test;
1760
+ }
1761
+ if (opts.spawn) {
1762
+ provider2.spawn = opts.spawn;
1763
+ }
1764
+ return provider2;
1765
+ }
1766
+ function createInstallRunner(definition) {
1767
+ return async (context) => {
1768
+ await runServiceInstall(definition, {
1769
+ isDryRun: context.logger.context.dryRun,
1770
+ runCommand: context.command.runCommand,
1771
+ logger: (message) => context.logger.verbose(message),
1772
+ platform: context.env.platform
1773
+ });
1774
+ };
1775
+ }
1776
+
1777
+ // src/cli/constants.ts
1778
+ var CLAUDE_CODE_VARIANTS = {
1779
+ haiku: "anthropic/claude-haiku-4.5",
1780
+ sonnet: "anthropic/claude-sonnet-4.6",
1781
+ opus: "anthropic/claude-opus-4.6"
1782
+ };
1783
+ var DEFAULT_CLAUDE_CODE_MODEL = CLAUDE_CODE_VARIANTS.sonnet;
1784
+ function stripModelNamespace2(model) {
1785
+ const slashIndex = model.indexOf("/");
1786
+ const id = slashIndex === -1 ? model : model.slice(slashIndex + 1);
1787
+ return id.toLowerCase();
1788
+ }
1789
+ var CODEX_MODELS = [
1790
+ "openai/gpt-5.2-codex",
1791
+ "openai/gpt-5.2",
1792
+ "openai/gpt-5.2-chat",
1793
+ "openai/gpt-5.2-pro",
1794
+ "openai/gpt-5.1",
1795
+ "openai/gpt-5.1-codex-mini"
1796
+ ];
1797
+ var DEFAULT_CODEX_MODEL = CODEX_MODELS[0];
1798
+ var KIMI_MODELS = [
1799
+ "novitaai/kimi-k2.5",
1800
+ "novitaai/kimi-k2-thinking"
1801
+ ];
1802
+ var DEFAULT_KIMI_MODEL = KIMI_MODELS[0];
1803
+ var DEFAULT_REASONING = "medium";
1804
+
1805
+ // src/providers/codex.ts
1806
+ var CODEX_PROVIDER_ID = "poe";
1807
+ var CODEX_INSTALL_DEFINITION = {
1808
+ id: "codex",
1809
+ summary: "Codex CLI",
1810
+ check: createBinaryExistsCheck(
1811
+ "codex",
1812
+ "codex-cli-binary",
1813
+ "Codex CLI binary must exist"
1814
+ ),
1815
+ steps: [
1816
+ {
1817
+ id: "install-codex-cli-npm",
1818
+ command: "npm",
1819
+ args: ["install", "-g", "@openai/codex"]
1820
+ }
1821
+ ],
1822
+ successMessage: "Installed Codex CLI via npm."
1823
+ };
1824
+ function stripCodexConfiguration(document) {
1825
+ if (!isConfigObject4(document)) {
1826
+ return { changed: false, empty: false };
1827
+ }
1828
+ if (document["model_provider"] !== CODEX_PROVIDER_ID) {
1829
+ return { changed: false, empty: false };
1830
+ }
1831
+ const providers = document["model_providers"];
1832
+ if (!isConfigObject4(providers) || !(CODEX_PROVIDER_ID in providers)) {
1833
+ return { changed: false, empty: false };
1834
+ }
1835
+ delete document["model_provider"];
1836
+ delete document["model"];
1837
+ delete document["model_reasoning_effort"];
1838
+ delete providers[CODEX_PROVIDER_ID];
1839
+ if (isTableEmpty(providers)) {
1840
+ delete document["model_providers"];
1841
+ }
1842
+ return {
1843
+ changed: true,
1844
+ empty: isTableEmpty(document)
1845
+ };
1846
+ }
1847
+ function isTableEmpty(value) {
1848
+ return isConfigObject4(value) && Object.keys(value).length === 0;
1849
+ }
1850
+ var codexService = createProvider({
1851
+ ...codexAgent,
1852
+ supportsStdinPrompt: true,
1853
+ configurePrompts: {
1854
+ model: {
1855
+ label: "Codex model",
1856
+ defaultValue: DEFAULT_CODEX_MODEL,
1857
+ choices: CODEX_MODELS.map((id) => ({
1858
+ title: id,
1859
+ value: id
1860
+ }))
110
1861
  },
111
- install: CODEX_INSTALL_DEFINITION
1862
+ reasoningEffort: {
1863
+ label: "Codex reasoning effort",
1864
+ defaultValue: DEFAULT_REASONING
1865
+ }
1866
+ },
1867
+ isolatedEnv: {
1868
+ agentBinary: codexAgent.binaryName,
1869
+ configProbe: { kind: "isolatedFile", relativePath: "config.toml" },
1870
+ env: {
1871
+ CODEX_HOME: { kind: "isolatedDir" },
1872
+ XDG_CONFIG_HOME: { kind: "isolatedDir" }
1873
+ }
1874
+ },
1875
+ test(context) {
1876
+ return context.runCheck(
1877
+ createSpawnHealthCheck("codex", {
1878
+ model: context.model ?? DEFAULT_CODEX_MODEL,
1879
+ expectedOutput: "CODEX_OK"
1880
+ })
1881
+ );
1882
+ },
1883
+ manifest: {
1884
+ configure: [
1885
+ fileMutation.ensureDirectory({ path: "~/.codex" }),
1886
+ fileMutation.backup({ target: "~/.codex/config.toml" }),
1887
+ templateMutation.mergeToml({
1888
+ target: "~/.codex/config.toml",
1889
+ templateId: "codex/config.toml.hbs",
1890
+ context: (ctx) => {
1891
+ const options = ctx;
1892
+ return {
1893
+ apiKey: options.apiKey,
1894
+ baseUrl: options.env.poeApiBaseUrl,
1895
+ model: stripModelNamespace2(options.model ?? DEFAULT_CODEX_MODEL),
1896
+ reasoningEffort: options.reasoningEffort
1897
+ };
1898
+ }
1899
+ })
1900
+ ],
1901
+ unconfigure: [
1902
+ configMutation.transform({
1903
+ target: "~/.codex/config.toml",
1904
+ transform: (document) => {
1905
+ const result = stripCodexConfiguration(document);
1906
+ if (!result.changed) {
1907
+ return { changed: false, content: document };
1908
+ }
1909
+ return {
1910
+ changed: true,
1911
+ content: result.empty ? null : document
1912
+ };
1913
+ }
1914
+ })
1915
+ ]
1916
+ },
1917
+ install: CODEX_INSTALL_DEFINITION
112
1918
  });
113
- //# sourceMappingURL=codex.js.map
1919
+ var provider = codexService;
1920
+ export {
1921
+ CODEX_INSTALL_DEFINITION,
1922
+ codexService,
1923
+ provider
1924
+ };
1925
+ //# sourceMappingURL=codex.js.map