poe-code 3.0.59 → 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,121 +1,1894 @@
1
- import { createBinaryExistsCheck, createSpawnHealthCheck } from "../utils/command-checks.js";
2
- import { configMutation, fileMutation } from "@poe-code/config-mutations";
3
- import { KIMI_MODELS, DEFAULT_KIMI_MODEL, PROVIDER_NAME, stripModelNamespace } from "../cli/constants.js";
4
- import { createProvider } from "./create-provider.js";
5
- import { kimiAgent } from "@poe-code/agent-defs";
6
- export const KIMI_INSTALL_DEFINITION = {
7
- id: "kimi",
8
- summary: "Kimi CLI",
9
- check: createBinaryExistsCheck("kimi", "kimi-cli-binary", "Kimi CLI binary must exist"),
10
- 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 = [
11
629
  {
12
- id: "install-kimi-cli-uv",
13
- command: "uv",
14
- args: ["tool", "install", "--python", "3.13", "kimi-cli"]
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
640
+ {
641
+ command: "sh",
642
+ args: [
643
+ "-c",
644
+ commonPaths.map((p) => `test -f "${p}"`).join(" || ")
645
+ ],
646
+ validate: (result) => result.exitCode === 0
15
647
  }
16
- ],
17
- successMessage: "Installed Kimi CLI via uv."
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
18
694
  };
19
- function providerModel(model) {
20
- const stripped = stripModelNamespace(model);
21
- return `${PROVIDER_NAME}/${stripped}`;
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
+ };
22
703
  }
23
- function buildKimiArgs(prompt, extraArgs) {
24
- return ["--quiet", "-p", prompt, ...(extraArgs ?? [])];
25
- }
26
- export const kimiService = createProvider({
27
- ...kimiAgent,
28
- disabled: false,
29
- supportsStdinPrompt: false,
30
- configurePrompts: {
31
- model: {
32
- label: "Kimi default model",
33
- defaultValue: DEFAULT_KIMI_MODEL,
34
- choices: KIMI_MODELS.map((id) => ({
35
- title: id,
36
- value: id
37
- }))
38
- }
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/execution/apply-mutation.ts
745
+ import Mustache from "mustache";
746
+
747
+ // packages/config-mutations/src/formats/json.ts
748
+ import * as jsonc from "jsonc-parser";
749
+ function isConfigObject(value) {
750
+ return typeof value === "object" && value !== null && !Array.isArray(value);
751
+ }
752
+ function parse2(content) {
753
+ if (!content || content.trim() === "") {
754
+ return {};
755
+ }
756
+ const errors = [];
757
+ const parsed = jsonc.parse(content, errors, {
758
+ allowTrailingComma: true,
759
+ disallowComments: false
760
+ });
761
+ if (errors.length > 0) {
762
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
763
+ }
764
+ if (parsed === null || parsed === void 0) {
765
+ return {};
766
+ }
767
+ if (!isConfigObject(parsed)) {
768
+ throw new Error("Expected JSON object.");
769
+ }
770
+ return parsed;
771
+ }
772
+ function serialize(obj) {
773
+ return `${JSON.stringify(obj, null, 2)}
774
+ `;
775
+ }
776
+ function merge2(base, patch) {
777
+ const result = { ...base };
778
+ for (const [key, value] of Object.entries(patch)) {
779
+ if (value === void 0) {
780
+ continue;
781
+ }
782
+ const existing = result[key];
783
+ if (isConfigObject(existing) && isConfigObject(value)) {
784
+ result[key] = merge2(existing, value);
785
+ continue;
786
+ }
787
+ result[key] = value;
788
+ }
789
+ return result;
790
+ }
791
+ function prune2(obj, shape) {
792
+ let changed = false;
793
+ const result = { ...obj };
794
+ for (const [key, pattern] of Object.entries(shape)) {
795
+ if (!(key in result)) {
796
+ continue;
797
+ }
798
+ const current = result[key];
799
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
800
+ delete result[key];
801
+ changed = true;
802
+ continue;
803
+ }
804
+ if (isConfigObject(pattern) && isConfigObject(current)) {
805
+ const { changed: childChanged, result: childResult } = prune2(
806
+ current,
807
+ pattern
808
+ );
809
+ if (childChanged) {
810
+ changed = true;
811
+ }
812
+ if (Object.keys(childResult).length === 0) {
813
+ delete result[key];
814
+ } else {
815
+ result[key] = childResult;
816
+ }
817
+ continue;
818
+ }
819
+ delete result[key];
820
+ changed = true;
821
+ }
822
+ return { changed, result };
823
+ }
824
+ var jsonFormat = {
825
+ parse: parse2,
826
+ serialize,
827
+ merge: merge2,
828
+ prune: prune2
829
+ };
830
+
831
+ // packages/config-mutations/src/formats/toml.ts
832
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
833
+ function isConfigObject2(value) {
834
+ return typeof value === "object" && value !== null && !Array.isArray(value);
835
+ }
836
+ function parse3(content) {
837
+ if (!content || content.trim() === "") {
838
+ return {};
839
+ }
840
+ const parsed = parseToml(content);
841
+ if (!isConfigObject2(parsed)) {
842
+ throw new Error("Expected TOML document to be a table.");
843
+ }
844
+ return parsed;
845
+ }
846
+ function serialize2(obj) {
847
+ const serialized = stringifyToml(obj);
848
+ return serialized.endsWith("\n") ? serialized : `${serialized}
849
+ `;
850
+ }
851
+ function merge3(base, patch) {
852
+ const result = { ...base };
853
+ for (const [key, value] of Object.entries(patch)) {
854
+ if (value === void 0) {
855
+ continue;
856
+ }
857
+ const existing = result[key];
858
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
859
+ result[key] = merge3(existing, value);
860
+ continue;
861
+ }
862
+ result[key] = value;
863
+ }
864
+ return result;
865
+ }
866
+ function prune3(obj, shape) {
867
+ let changed = false;
868
+ const result = { ...obj };
869
+ for (const [key, pattern] of Object.entries(shape)) {
870
+ if (!(key in result)) {
871
+ continue;
872
+ }
873
+ const current = result[key];
874
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
875
+ delete result[key];
876
+ changed = true;
877
+ continue;
878
+ }
879
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
880
+ const { changed: childChanged, result: childResult } = prune3(
881
+ current,
882
+ pattern
883
+ );
884
+ if (childChanged) {
885
+ changed = true;
886
+ }
887
+ if (Object.keys(childResult).length === 0) {
888
+ delete result[key];
889
+ } else {
890
+ result[key] = childResult;
891
+ }
892
+ continue;
893
+ }
894
+ delete result[key];
895
+ changed = true;
896
+ }
897
+ return { changed, result };
898
+ }
899
+ var tomlFormat = {
900
+ parse: parse3,
901
+ serialize: serialize2,
902
+ merge: merge3,
903
+ prune: prune3
904
+ };
905
+
906
+ // packages/config-mutations/src/formats/index.ts
907
+ var formatRegistry = {
908
+ json: jsonFormat,
909
+ toml: tomlFormat
910
+ };
911
+ var extensionMap = {
912
+ ".json": "json",
913
+ ".toml": "toml"
914
+ };
915
+ function getConfigFormat(pathOrFormat) {
916
+ if (pathOrFormat in formatRegistry) {
917
+ return formatRegistry[pathOrFormat];
918
+ }
919
+ const ext = getExtension(pathOrFormat);
920
+ const formatName = extensionMap[ext];
921
+ if (!formatName) {
922
+ throw new Error(
923
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
924
+ );
925
+ }
926
+ return formatRegistry[formatName];
927
+ }
928
+ function detectFormat(path2) {
929
+ const ext = getExtension(path2);
930
+ return extensionMap[ext];
931
+ }
932
+ function getExtension(path2) {
933
+ const lastDot = path2.lastIndexOf(".");
934
+ if (lastDot === -1) {
935
+ return "";
936
+ }
937
+ return path2.slice(lastDot).toLowerCase();
938
+ }
939
+
940
+ // packages/config-mutations/src/execution/path-utils.ts
941
+ import path from "node:path";
942
+ function expandHome(targetPath, homeDir) {
943
+ if (!targetPath?.startsWith("~")) {
944
+ return targetPath;
945
+ }
946
+ if (targetPath.startsWith("~./")) {
947
+ targetPath = `~/.${targetPath.slice(3)}`;
948
+ }
949
+ let remainder = targetPath.slice(1);
950
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
951
+ remainder = remainder.slice(1);
952
+ } else if (remainder.startsWith(".")) {
953
+ remainder = remainder.slice(1);
954
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
955
+ remainder = remainder.slice(1);
956
+ }
957
+ }
958
+ return remainder.length === 0 ? homeDir : path.join(homeDir, remainder);
959
+ }
960
+ function validateHomePath(targetPath) {
961
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
962
+ throw new Error("Target path must be a non-empty string.");
963
+ }
964
+ if (!targetPath.startsWith("~")) {
965
+ throw new Error(
966
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
967
+ );
968
+ }
969
+ }
970
+ function resolvePath(rawPath, homeDir, pathMapper) {
971
+ validateHomePath(rawPath);
972
+ const expanded = expandHome(rawPath, homeDir);
973
+ if (!pathMapper) {
974
+ return expanded;
975
+ }
976
+ const rawDirectory = path.dirname(expanded);
977
+ const mappedDirectory = pathMapper.mapTargetDirectory({
978
+ targetDirectory: rawDirectory
979
+ });
980
+ const filename = path.basename(expanded);
981
+ return filename.length === 0 ? mappedDirectory : path.join(mappedDirectory, filename);
982
+ }
983
+
984
+ // packages/config-mutations/src/fs-utils.ts
985
+ function isNotFound(error) {
986
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
987
+ }
988
+ async function readFileIfExists(fs, target) {
989
+ try {
990
+ return await fs.readFile(target, "utf8");
991
+ } catch (error) {
992
+ if (isNotFound(error)) {
993
+ return null;
994
+ }
995
+ throw error;
996
+ }
997
+ }
998
+ async function pathExists(fs, target) {
999
+ try {
1000
+ await fs.stat(target);
1001
+ return true;
1002
+ } catch (error) {
1003
+ if (isNotFound(error)) {
1004
+ return false;
1005
+ }
1006
+ throw error;
1007
+ }
1008
+ }
1009
+ function createTimestamp() {
1010
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
1011
+ }
1012
+
1013
+ // packages/config-mutations/src/execution/apply-mutation.ts
1014
+ function resolveValue(resolver, options) {
1015
+ if (typeof resolver === "function") {
1016
+ return resolver(options);
1017
+ }
1018
+ return resolver;
1019
+ }
1020
+ function createInvalidDocumentBackupPath(targetPath) {
1021
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
1022
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
1023
+ }
1024
+ async function backupInvalidDocument(fs, targetPath, content) {
1025
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
1026
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
1027
+ }
1028
+ function describeMutation(kind, targetPath) {
1029
+ const displayPath = targetPath ?? "target";
1030
+ switch (kind) {
1031
+ case "ensureDirectory":
1032
+ return `Create ${displayPath}`;
1033
+ case "removeDirectory":
1034
+ return `Remove directory ${displayPath}`;
1035
+ case "backup":
1036
+ return `Backup ${displayPath}`;
1037
+ case "templateWrite":
1038
+ return `Write ${displayPath}`;
1039
+ case "chmod":
1040
+ return `Set permissions on ${displayPath}`;
1041
+ case "removeFile":
1042
+ return `Remove ${displayPath}`;
1043
+ case "configMerge":
1044
+ case "configPrune":
1045
+ case "configTransform":
1046
+ case "templateMergeToml":
1047
+ case "templateMergeJson":
1048
+ return `Update ${displayPath}`;
1049
+ default:
1050
+ return "Operation";
1051
+ }
1052
+ }
1053
+ function pruneKeysByPrefix(table, prefix) {
1054
+ const result = {};
1055
+ for (const [key, value] of Object.entries(table)) {
1056
+ if (!key.startsWith(prefix)) {
1057
+ result[key] = value;
1058
+ }
1059
+ }
1060
+ return result;
1061
+ }
1062
+ function isConfigObject3(value) {
1063
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1064
+ }
1065
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
1066
+ const result = { ...base };
1067
+ const prefixMap = pruneByPrefix ?? {};
1068
+ for (const [key, value] of Object.entries(patch)) {
1069
+ const current = result[key];
1070
+ const prefix = prefixMap[key];
1071
+ if (isConfigObject3(current) && isConfigObject3(value)) {
1072
+ if (prefix) {
1073
+ const pruned = pruneKeysByPrefix(current, prefix);
1074
+ result[key] = { ...pruned, ...value };
1075
+ } else {
1076
+ result[key] = mergeWithPruneByPrefix(
1077
+ current,
1078
+ value,
1079
+ prefixMap
1080
+ );
1081
+ }
1082
+ continue;
1083
+ }
1084
+ result[key] = value;
1085
+ }
1086
+ return result;
1087
+ }
1088
+ async function applyMutation(mutation, context, options) {
1089
+ switch (mutation.kind) {
1090
+ case "ensureDirectory":
1091
+ return applyEnsureDirectory(mutation, context, options);
1092
+ case "removeDirectory":
1093
+ return applyRemoveDirectory(mutation, context, options);
1094
+ case "removeFile":
1095
+ return applyRemoveFile(mutation, context, options);
1096
+ case "chmod":
1097
+ return applyChmod(mutation, context, options);
1098
+ case "backup":
1099
+ return applyBackup(mutation, context, options);
1100
+ case "configMerge":
1101
+ return applyConfigMerge(mutation, context, options);
1102
+ case "configPrune":
1103
+ return applyConfigPrune(mutation, context, options);
1104
+ case "configTransform":
1105
+ return applyConfigTransform(mutation, context, options);
1106
+ case "templateWrite":
1107
+ return applyTemplateWrite(mutation, context, options);
1108
+ case "templateMergeToml":
1109
+ return applyTemplateMerge(mutation, context, options, "toml");
1110
+ case "templateMergeJson":
1111
+ return applyTemplateMerge(mutation, context, options, "json");
1112
+ default: {
1113
+ const never = mutation;
1114
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
1115
+ }
1116
+ }
1117
+ }
1118
+ async function applyEnsureDirectory(mutation, context, options) {
1119
+ const rawPath = resolveValue(mutation.path, options);
1120
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1121
+ const details = {
1122
+ kind: mutation.kind,
1123
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1124
+ targetPath
1125
+ };
1126
+ const existed = await pathExists(context.fs, targetPath);
1127
+ if (!context.dryRun) {
1128
+ await context.fs.mkdir(targetPath, { recursive: true });
1129
+ }
1130
+ return {
1131
+ outcome: {
1132
+ changed: !existed,
1133
+ effect: "mkdir",
1134
+ detail: existed ? "noop" : "create"
39
1135
  },
40
- isolatedEnv: {
41
- // Use "kimi-cli" to avoid stripAgentHome stripping ".kimi" from paths
42
- agentBinary: "kimi-cli",
43
- configProbe: { kind: "isolatedFile", relativePath: ".kimi/config.toml" },
44
- env: {
45
- HOME: { kind: "isolatedDir" }
46
- }
1136
+ details
1137
+ };
1138
+ }
1139
+ async function applyRemoveDirectory(mutation, context, options) {
1140
+ const rawPath = resolveValue(mutation.path, options);
1141
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1142
+ const details = {
1143
+ kind: mutation.kind,
1144
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1145
+ targetPath
1146
+ };
1147
+ const existed = await pathExists(context.fs, targetPath);
1148
+ if (!existed) {
1149
+ return {
1150
+ outcome: { changed: false, effect: "none", detail: "noop" },
1151
+ details
1152
+ };
1153
+ }
1154
+ if (typeof context.fs.rm !== "function") {
1155
+ return {
1156
+ outcome: { changed: false, effect: "none", detail: "noop" },
1157
+ details
1158
+ };
1159
+ }
1160
+ if (mutation.force) {
1161
+ if (!context.dryRun) {
1162
+ await context.fs.rm(targetPath, { recursive: true, force: true });
1163
+ }
1164
+ return {
1165
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1166
+ details
1167
+ };
1168
+ }
1169
+ const entries = await context.fs.readdir(targetPath);
1170
+ if (entries.length > 0) {
1171
+ return {
1172
+ outcome: { changed: false, effect: "none", detail: "noop" },
1173
+ details
1174
+ };
1175
+ }
1176
+ if (!context.dryRun) {
1177
+ await context.fs.rm(targetPath, { recursive: true, force: true });
1178
+ }
1179
+ return {
1180
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1181
+ details
1182
+ };
1183
+ }
1184
+ async function applyRemoveFile(mutation, context, options) {
1185
+ const rawPath = resolveValue(mutation.target, options);
1186
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1187
+ const details = {
1188
+ kind: mutation.kind,
1189
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1190
+ targetPath
1191
+ };
1192
+ try {
1193
+ const content = await context.fs.readFile(targetPath, "utf8");
1194
+ const trimmed = content.trim();
1195
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
1196
+ return {
1197
+ outcome: { changed: false, effect: "none", detail: "noop" },
1198
+ details
1199
+ };
1200
+ }
1201
+ if (mutation.whenEmpty && trimmed.length > 0) {
1202
+ return {
1203
+ outcome: { changed: false, effect: "none", detail: "noop" },
1204
+ details
1205
+ };
1206
+ }
1207
+ if (!context.dryRun) {
1208
+ await context.fs.unlink(targetPath);
1209
+ }
1210
+ return {
1211
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1212
+ details
1213
+ };
1214
+ } catch (error) {
1215
+ if (isNotFound(error)) {
1216
+ return {
1217
+ outcome: { changed: false, effect: "none", detail: "noop" },
1218
+ details
1219
+ };
1220
+ }
1221
+ throw error;
1222
+ }
1223
+ }
1224
+ async function applyChmod(mutation, context, options) {
1225
+ const rawPath = resolveValue(mutation.target, options);
1226
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1227
+ const details = {
1228
+ kind: mutation.kind,
1229
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1230
+ targetPath
1231
+ };
1232
+ if (typeof context.fs.chmod !== "function") {
1233
+ return {
1234
+ outcome: { changed: false, effect: "none", detail: "noop" },
1235
+ details
1236
+ };
1237
+ }
1238
+ try {
1239
+ const stat = await context.fs.stat(targetPath);
1240
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
1241
+ if (currentMode === mutation.mode) {
1242
+ return {
1243
+ outcome: { changed: false, effect: "none", detail: "noop" },
1244
+ details
1245
+ };
1246
+ }
1247
+ if (!context.dryRun) {
1248
+ await context.fs.chmod(targetPath, mutation.mode);
1249
+ }
1250
+ return {
1251
+ outcome: { changed: true, effect: "chmod", detail: "update" },
1252
+ details
1253
+ };
1254
+ } catch (error) {
1255
+ if (isNotFound(error)) {
1256
+ return {
1257
+ outcome: { changed: false, effect: "none", detail: "noop" },
1258
+ details
1259
+ };
1260
+ }
1261
+ throw error;
1262
+ }
1263
+ }
1264
+ async function applyBackup(mutation, context, options) {
1265
+ const rawPath = resolveValue(mutation.target, options);
1266
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1267
+ const details = {
1268
+ kind: mutation.kind,
1269
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1270
+ targetPath
1271
+ };
1272
+ const content = await readFileIfExists(context.fs, targetPath);
1273
+ if (content === null) {
1274
+ return {
1275
+ outcome: { changed: false, effect: "none", detail: "noop" },
1276
+ details
1277
+ };
1278
+ }
1279
+ if (!context.dryRun) {
1280
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
1281
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
1282
+ }
1283
+ return {
1284
+ outcome: { changed: true, effect: "copy", detail: "backup" },
1285
+ details
1286
+ };
1287
+ }
1288
+ async function applyConfigMerge(mutation, context, options) {
1289
+ const rawPath = resolveValue(mutation.target, options);
1290
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1291
+ const details = {
1292
+ kind: mutation.kind,
1293
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1294
+ targetPath
1295
+ };
1296
+ const formatName = mutation.format ?? detectFormat(rawPath);
1297
+ if (!formatName) {
1298
+ throw new Error(
1299
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1300
+ );
1301
+ }
1302
+ const format = getConfigFormat(formatName);
1303
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1304
+ let current;
1305
+ try {
1306
+ current = rawContent === null ? {} : format.parse(rawContent);
1307
+ } catch {
1308
+ if (rawContent !== null) {
1309
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1310
+ }
1311
+ current = {};
1312
+ }
1313
+ const value = resolveValue(mutation.value, options);
1314
+ let merged;
1315
+ if (mutation.pruneByPrefix) {
1316
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
1317
+ } else {
1318
+ merged = format.merge(current, value);
1319
+ }
1320
+ const serialized = format.serialize(merged);
1321
+ const changed = serialized !== rawContent;
1322
+ if (changed && !context.dryRun) {
1323
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1324
+ }
1325
+ return {
1326
+ outcome: {
1327
+ changed,
1328
+ effect: changed ? "write" : "none",
1329
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
1330
+ },
1331
+ details
1332
+ };
1333
+ }
1334
+ async function applyConfigPrune(mutation, context, options) {
1335
+ const rawPath = resolveValue(mutation.target, options);
1336
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1337
+ const details = {
1338
+ kind: mutation.kind,
1339
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1340
+ targetPath
1341
+ };
1342
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1343
+ if (rawContent === null) {
1344
+ return {
1345
+ outcome: { changed: false, effect: "none", detail: "noop" },
1346
+ details
1347
+ };
1348
+ }
1349
+ const formatName = mutation.format ?? detectFormat(rawPath);
1350
+ if (!formatName) {
1351
+ throw new Error(
1352
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1353
+ );
1354
+ }
1355
+ const format = getConfigFormat(formatName);
1356
+ let current;
1357
+ try {
1358
+ current = format.parse(rawContent);
1359
+ } catch {
1360
+ return {
1361
+ outcome: { changed: false, effect: "none", detail: "noop" },
1362
+ details
1363
+ };
1364
+ }
1365
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
1366
+ return {
1367
+ outcome: { changed: false, effect: "none", detail: "noop" },
1368
+ details
1369
+ };
1370
+ }
1371
+ const shape = resolveValue(mutation.shape, options);
1372
+ const { changed, result } = format.prune(current, shape);
1373
+ if (!changed) {
1374
+ return {
1375
+ outcome: { changed: false, effect: "none", detail: "noop" },
1376
+ details
1377
+ };
1378
+ }
1379
+ if (Object.keys(result).length === 0) {
1380
+ if (!context.dryRun) {
1381
+ await context.fs.unlink(targetPath);
1382
+ }
1383
+ return {
1384
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1385
+ details
1386
+ };
1387
+ }
1388
+ const serialized = format.serialize(result);
1389
+ if (!context.dryRun) {
1390
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1391
+ }
1392
+ return {
1393
+ outcome: { changed: true, effect: "write", detail: "update" },
1394
+ details
1395
+ };
1396
+ }
1397
+ async function applyConfigTransform(mutation, context, options) {
1398
+ const rawPath = resolveValue(mutation.target, options);
1399
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1400
+ const details = {
1401
+ kind: mutation.kind,
1402
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1403
+ targetPath
1404
+ };
1405
+ const formatName = mutation.format ?? detectFormat(rawPath);
1406
+ if (!formatName) {
1407
+ throw new Error(
1408
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
1409
+ );
1410
+ }
1411
+ const format = getConfigFormat(formatName);
1412
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1413
+ let current;
1414
+ try {
1415
+ current = rawContent === null ? {} : format.parse(rawContent);
1416
+ } catch {
1417
+ if (rawContent !== null) {
1418
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1419
+ }
1420
+ current = {};
1421
+ }
1422
+ const { content: transformed, changed } = mutation.transform(current, options);
1423
+ if (!changed) {
1424
+ return {
1425
+ outcome: { changed: false, effect: "none", detail: "noop" },
1426
+ details
1427
+ };
1428
+ }
1429
+ if (transformed === null) {
1430
+ if (rawContent === null) {
1431
+ return {
1432
+ outcome: { changed: false, effect: "none", detail: "noop" },
1433
+ details
1434
+ };
1435
+ }
1436
+ if (!context.dryRun) {
1437
+ await context.fs.unlink(targetPath);
1438
+ }
1439
+ return {
1440
+ outcome: { changed: true, effect: "delete", detail: "delete" },
1441
+ details
1442
+ };
1443
+ }
1444
+ const serialized = format.serialize(transformed);
1445
+ if (!context.dryRun) {
1446
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1447
+ }
1448
+ return {
1449
+ outcome: {
1450
+ changed: true,
1451
+ effect: "write",
1452
+ detail: rawContent === null ? "create" : "update"
1453
+ },
1454
+ details
1455
+ };
1456
+ }
1457
+ async function applyTemplateWrite(mutation, context, options) {
1458
+ if (!context.templates) {
1459
+ throw new Error(
1460
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
1461
+ );
1462
+ }
1463
+ const rawPath = resolveValue(mutation.target, options);
1464
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1465
+ const details = {
1466
+ kind: mutation.kind,
1467
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1468
+ targetPath
1469
+ };
1470
+ const template = await context.templates(mutation.templateId);
1471
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
1472
+ const rendered = Mustache.render(template, templateContext);
1473
+ const existed = await pathExists(context.fs, targetPath);
1474
+ if (!context.dryRun) {
1475
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
1476
+ }
1477
+ return {
1478
+ outcome: {
1479
+ changed: true,
1480
+ effect: "write",
1481
+ detail: existed ? "update" : "create"
47
1482
  },
48
- test(context) {
49
- return context.runCheck(createSpawnHealthCheck("kimi", {
50
- model: context.model,
51
- expectedOutput: "KIMI_OK"
52
- }));
1483
+ details
1484
+ };
1485
+ }
1486
+ async function applyTemplateMerge(mutation, context, options, formatName) {
1487
+ if (!context.templates) {
1488
+ throw new Error(
1489
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
1490
+ );
1491
+ }
1492
+ const rawPath = resolveValue(mutation.target, options);
1493
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1494
+ const details = {
1495
+ kind: mutation.kind,
1496
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1497
+ targetPath
1498
+ };
1499
+ const format = getConfigFormat(formatName);
1500
+ const template = await context.templates(mutation.templateId);
1501
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
1502
+ const rendered = Mustache.render(template, templateContext);
1503
+ let templateDoc;
1504
+ try {
1505
+ templateDoc = format.parse(rendered);
1506
+ } catch (error) {
1507
+ throw new Error(
1508
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
1509
+ { cause: error }
1510
+ );
1511
+ }
1512
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1513
+ let current;
1514
+ try {
1515
+ current = rawContent === null ? {} : format.parse(rawContent);
1516
+ } catch {
1517
+ if (rawContent !== null) {
1518
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1519
+ }
1520
+ current = {};
1521
+ }
1522
+ const merged = format.merge(current, templateDoc);
1523
+ const serialized = format.serialize(merged);
1524
+ const changed = serialized !== rawContent;
1525
+ if (changed && !context.dryRun) {
1526
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1527
+ }
1528
+ return {
1529
+ outcome: {
1530
+ changed,
1531
+ effect: changed ? "write" : "none",
1532
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
53
1533
  },
54
- manifest: {
55
- configure: [
56
- fileMutation.ensureDirectory({ path: "~/.kimi" }),
57
- configMutation.merge({
58
- target: "~/.kimi/config.toml",
59
- pruneByPrefix: { models: `${PROVIDER_NAME}/` },
60
- value: (ctx) => {
61
- const { model, apiKey, env } = (ctx ?? {});
62
- const selectedModel = model ?? DEFAULT_KIMI_MODEL;
63
- const models = {};
64
- for (const m of KIMI_MODELS) {
65
- models[providerModel(m)] = {
66
- provider: PROVIDER_NAME,
67
- model: stripModelNamespace(m),
68
- max_context_size: 256000
69
- };
70
- }
71
- return {
72
- default_model: providerModel(selectedModel),
73
- default_thinking: true,
74
- models,
75
- providers: {
76
- [PROVIDER_NAME]: {
77
- type: "openai_legacy",
78
- base_url: env.poeApiBaseUrl,
79
- api_key: apiKey ?? ""
80
- }
81
- }
82
- };
83
- }
84
- })
85
- ],
86
- unconfigure: [
87
- configMutation.transform({
88
- target: "~/.kimi/config.toml",
89
- transform: (document) => {
90
- const providers = document.providers;
91
- if (!providers || typeof providers !== "object") {
92
- return { changed: false, content: document };
93
- }
94
- if (!(PROVIDER_NAME in providers)) {
95
- return { changed: false, content: document };
96
- }
97
- const { [PROVIDER_NAME]: ignoredProvider, ...rest } = providers;
98
- void ignoredProvider;
99
- const updatedProviders = rest;
100
- if (Object.keys(updatedProviders).length === 0) {
101
- const { providers: ignoredProviders, ...docWithoutProviders } = document;
102
- void ignoredProviders;
103
- return { changed: true, content: docWithoutProviders };
104
- }
105
- return { changed: true, content: { ...document, providers: updatedProviders } };
106
- }
107
- })
108
- ]
1534
+ details
1535
+ };
1536
+ }
1537
+
1538
+ // packages/config-mutations/src/execution/run-mutations.ts
1539
+ async function runMutations(mutations, context, options) {
1540
+ const effects = [];
1541
+ let anyChanged = false;
1542
+ const resolverOptions = options ?? {};
1543
+ for (const mutation of mutations) {
1544
+ const { outcome } = await executeMutation(
1545
+ mutation,
1546
+ context,
1547
+ resolverOptions
1548
+ );
1549
+ effects.push(outcome);
1550
+ if (outcome.changed) {
1551
+ anyChanged = true;
1552
+ }
1553
+ }
1554
+ return {
1555
+ changed: anyChanged,
1556
+ effects
1557
+ };
1558
+ }
1559
+ async function executeMutation(mutation, context, options) {
1560
+ context.observers?.onStart?.({
1561
+ kind: mutation.kind,
1562
+ label: mutation.label ?? mutation.kind,
1563
+ targetPath: void 0
1564
+ // Will be resolved during apply
1565
+ });
1566
+ try {
1567
+ const { outcome, details } = await applyMutation(mutation, context, options);
1568
+ context.observers?.onComplete?.(details, outcome);
1569
+ return { outcome, details };
1570
+ } catch (error) {
1571
+ context.observers?.onError?.(
1572
+ {
1573
+ kind: mutation.kind,
1574
+ label: mutation.label ?? mutation.kind,
1575
+ targetPath: void 0
1576
+ },
1577
+ error
1578
+ );
1579
+ throw error;
1580
+ }
1581
+ }
1582
+
1583
+ // packages/config-mutations/src/template/render.ts
1584
+ import Mustache2 from "mustache";
1585
+ var originalEscape = Mustache2.escape;
1586
+
1587
+ // src/cli/constants.ts
1588
+ var CLAUDE_CODE_VARIANTS = {
1589
+ haiku: "anthropic/claude-haiku-4.5",
1590
+ sonnet: "anthropic/claude-sonnet-4.6",
1591
+ opus: "anthropic/claude-opus-4.6"
1592
+ };
1593
+ var DEFAULT_CLAUDE_CODE_MODEL = CLAUDE_CODE_VARIANTS.sonnet;
1594
+ function stripModelNamespace2(model) {
1595
+ const slashIndex = model.indexOf("/");
1596
+ const id = slashIndex === -1 ? model : model.slice(slashIndex + 1);
1597
+ return id.toLowerCase();
1598
+ }
1599
+ var CODEX_MODELS = [
1600
+ "openai/gpt-5.2-codex",
1601
+ "openai/gpt-5.2",
1602
+ "openai/gpt-5.2-chat",
1603
+ "openai/gpt-5.2-pro",
1604
+ "openai/gpt-5.1",
1605
+ "openai/gpt-5.1-codex-mini"
1606
+ ];
1607
+ var DEFAULT_CODEX_MODEL = CODEX_MODELS[0];
1608
+ var KIMI_MODELS = [
1609
+ "novitaai/kimi-k2.5",
1610
+ "novitaai/kimi-k2-thinking"
1611
+ ];
1612
+ var DEFAULT_KIMI_MODEL = KIMI_MODELS[0];
1613
+ var PROVIDER_NAME = "poe";
1614
+
1615
+ // src/services/service-install.ts
1616
+ async function runServiceInstall(definition, context) {
1617
+ const checkContext = {
1618
+ isDryRun: context.isDryRun,
1619
+ runCommand: context.runCommand
1620
+ };
1621
+ let needsInstall = false;
1622
+ try {
1623
+ await definition.check.run(checkContext);
1624
+ context.logger(`${definition.summary} already installed.`);
1625
+ } catch (error) {
1626
+ const detail = error instanceof Error ? error.message : String(error);
1627
+ context.logger(`${definition.summary} not detected: ${detail}`);
1628
+ needsInstall = true;
1629
+ }
1630
+ if (!needsInstall) {
1631
+ return false;
1632
+ }
1633
+ if (context.isDryRun) {
1634
+ logInstallDryRun(definition, context);
1635
+ return true;
1636
+ }
1637
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1638
+ for (const step of platformSteps) {
1639
+ await runInstallStep(step, context);
1640
+ }
1641
+ await definition.check.run(checkContext);
1642
+ if (definition.postChecks) {
1643
+ for (const postCheck of definition.postChecks) {
1644
+ await postCheck.run(checkContext);
1645
+ }
1646
+ }
1647
+ context.logger(
1648
+ definition.successMessage ?? `${definition.summary} installed.`
1649
+ );
1650
+ return true;
1651
+ }
1652
+ function describeInstallCommand(step) {
1653
+ return `[${step.id}] ${formatCommand2(step.command, step.args)}`;
1654
+ }
1655
+ function formatCommand2(command, args) {
1656
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
1657
+ }
1658
+ function quoteIfNeeded(value) {
1659
+ if (value.length === 0) {
1660
+ return '""';
1661
+ }
1662
+ if (value.includes(" ") || value.includes(" ") || value.includes("\n")) {
1663
+ return `"${value.replaceAll('"', '\\"')}"`;
1664
+ }
1665
+ return value;
1666
+ }
1667
+ function filterStepsByPlatform(steps, platform) {
1668
+ return steps.filter(
1669
+ (step) => !step.platforms || step.platforms.includes(platform)
1670
+ );
1671
+ }
1672
+ function logInstallDryRun(definition, context) {
1673
+ context.logger(`Dry run: would install ${definition.summary}.`);
1674
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1675
+ for (const step of platformSteps) {
1676
+ context.logger(`Dry run: ${describeInstallCommand(step)}`);
1677
+ }
1678
+ }
1679
+ async function runInstallStep(step, context) {
1680
+ context.logger(`Running ${describeInstallCommand(step)}`);
1681
+ const result = await context.runCommand(step.command, step.args);
1682
+ if (result.exitCode !== 0) {
1683
+ const stderr = result.stderr.trim();
1684
+ const suffix = stderr.length > 0 ? `: ${stderr}` : "";
1685
+ throw new Error(
1686
+ `${describeInstallCommand(step)} failed with exit code ${result.exitCode}${suffix}`
1687
+ );
1688
+ }
1689
+ }
1690
+
1691
+ // src/providers/create-provider.ts
1692
+ var templateImports = {
1693
+ "python/env.hbs": () => Promise.resolve().then(() => __toESM(require_env(), 1)),
1694
+ "python/main.py.hbs": () => Promise.resolve().then(() => __toESM(require_main_py(), 1)),
1695
+ "python/requirements.txt.hbs": () => Promise.resolve().then(() => __toESM(require_requirements_txt(), 1)),
1696
+ "codex/config.toml.hbs": () => Promise.resolve().then(() => __toESM(require_config_toml(), 1))
1697
+ };
1698
+ async function loadTemplate(templateId) {
1699
+ const loader = templateImports[templateId];
1700
+ if (!loader) {
1701
+ throw new Error(`Template not found: ${templateId}`);
1702
+ }
1703
+ const module = await loader();
1704
+ return module.default;
1705
+ }
1706
+ function createProvider(opts) {
1707
+ const provider2 = {
1708
+ id: opts.id,
1709
+ summary: opts.summary,
1710
+ name: opts.name,
1711
+ aliases: opts.aliases,
1712
+ label: opts.label,
1713
+ branding: opts.branding,
1714
+ disabled: opts.disabled,
1715
+ supportsStdinPrompt: opts.supportsStdinPrompt,
1716
+ configurePrompts: opts.configurePrompts,
1717
+ postConfigureMessages: opts.postConfigureMessages,
1718
+ isolatedEnv: opts.isolatedEnv,
1719
+ async configure(context, runOptions) {
1720
+ await runMutations(opts.manifest.configure, {
1721
+ fs: context.fs,
1722
+ homeDir: context.env.homeDir,
1723
+ observers: runOptions?.observers,
1724
+ templates: loadTemplate,
1725
+ pathMapper: context.pathMapper
1726
+ }, context.options);
1727
+ context.command.flushDryRun({ emitIfEmpty: false });
109
1728
  },
110
- install: KIMI_INSTALL_DEFINITION,
111
- spawn(context, options) {
112
- const args = buildKimiArgs(options.prompt, options.args);
113
- if (options.cwd) {
114
- return context.command.runCommand("kimi", args, {
115
- cwd: options.cwd
116
- });
1729
+ async unconfigure(context, runOptions) {
1730
+ if (!opts.manifest.unconfigure) {
1731
+ return false;
1732
+ }
1733
+ const result = await runMutations(opts.manifest.unconfigure, {
1734
+ fs: context.fs,
1735
+ homeDir: context.env.homeDir,
1736
+ observers: runOptions?.observers,
1737
+ templates: loadTemplate,
1738
+ pathMapper: context.pathMapper
1739
+ }, context.options);
1740
+ context.command.flushDryRun({ emitIfEmpty: false });
1741
+ return result.changed;
1742
+ }
1743
+ };
1744
+ if (opts.install) {
1745
+ provider2.install = createInstallRunner(opts.install);
1746
+ }
1747
+ if (opts.test) {
1748
+ provider2.test = opts.test;
1749
+ }
1750
+ if (opts.spawn) {
1751
+ provider2.spawn = opts.spawn;
1752
+ }
1753
+ return provider2;
1754
+ }
1755
+ function createInstallRunner(definition) {
1756
+ return async (context) => {
1757
+ await runServiceInstall(definition, {
1758
+ isDryRun: context.logger.context.dryRun,
1759
+ runCommand: context.command.runCommand,
1760
+ logger: (message) => context.logger.verbose(message),
1761
+ platform: context.env.platform
1762
+ });
1763
+ };
1764
+ }
1765
+
1766
+ // src/providers/kimi.ts
1767
+ var KIMI_INSTALL_DEFINITION = {
1768
+ id: "kimi",
1769
+ summary: "Kimi CLI",
1770
+ check: createBinaryExistsCheck(
1771
+ "kimi",
1772
+ "kimi-cli-binary",
1773
+ "Kimi CLI binary must exist"
1774
+ ),
1775
+ steps: [
1776
+ {
1777
+ id: "install-kimi-cli-uv",
1778
+ command: "uv",
1779
+ args: ["tool", "install", "--python", "3.13", "kimi-cli"]
1780
+ }
1781
+ ],
1782
+ successMessage: "Installed Kimi CLI via uv."
1783
+ };
1784
+ function providerModel(model) {
1785
+ const stripped = stripModelNamespace2(model);
1786
+ return `${PROVIDER_NAME}/${stripped}`;
1787
+ }
1788
+ function buildKimiArgs(prompt, extraArgs) {
1789
+ return ["--quiet", "-p", prompt, ...extraArgs ?? []];
1790
+ }
1791
+ var kimiService = createProvider({
1792
+ ...kimiAgent,
1793
+ disabled: false,
1794
+ supportsStdinPrompt: false,
1795
+ configurePrompts: {
1796
+ model: {
1797
+ label: "Kimi default model",
1798
+ defaultValue: DEFAULT_KIMI_MODEL,
1799
+ choices: KIMI_MODELS.map((id) => ({
1800
+ title: id,
1801
+ value: id
1802
+ }))
1803
+ }
1804
+ },
1805
+ isolatedEnv: {
1806
+ // Use "kimi-cli" to avoid stripAgentHome stripping ".kimi" from paths
1807
+ agentBinary: "kimi-cli",
1808
+ configProbe: { kind: "isolatedFile", relativePath: ".kimi/config.toml" },
1809
+ env: {
1810
+ HOME: { kind: "isolatedDir" }
1811
+ }
1812
+ },
1813
+ test(context) {
1814
+ return context.runCheck(
1815
+ createSpawnHealthCheck("kimi", {
1816
+ model: context.model,
1817
+ expectedOutput: "KIMI_OK"
1818
+ })
1819
+ );
1820
+ },
1821
+ manifest: {
1822
+ configure: [
1823
+ fileMutation.ensureDirectory({ path: "~/.kimi" }),
1824
+ configMutation.merge({
1825
+ target: "~/.kimi/config.toml",
1826
+ pruneByPrefix: { models: `${PROVIDER_NAME}/` },
1827
+ value: (ctx) => {
1828
+ const { model, apiKey, env } = ctx ?? {};
1829
+ const selectedModel = model ?? DEFAULT_KIMI_MODEL;
1830
+ const models = {};
1831
+ for (const m of KIMI_MODELS) {
1832
+ models[providerModel(m)] = {
1833
+ provider: PROVIDER_NAME,
1834
+ model: stripModelNamespace2(m),
1835
+ max_context_size: 256e3
1836
+ };
1837
+ }
1838
+ return {
1839
+ default_model: providerModel(selectedModel),
1840
+ default_thinking: true,
1841
+ models,
1842
+ providers: {
1843
+ [PROVIDER_NAME]: {
1844
+ type: "openai_legacy",
1845
+ base_url: env.poeApiBaseUrl,
1846
+ api_key: apiKey ?? ""
1847
+ }
1848
+ }
1849
+ };
117
1850
  }
118
- return context.command.runCommand("kimi", args);
1851
+ })
1852
+ ],
1853
+ unconfigure: [
1854
+ configMutation.transform({
1855
+ target: "~/.kimi/config.toml",
1856
+ transform: (document) => {
1857
+ const providers = document.providers;
1858
+ if (!providers || typeof providers !== "object") {
1859
+ return { changed: false, content: document };
1860
+ }
1861
+ if (!(PROVIDER_NAME in providers)) {
1862
+ return { changed: false, content: document };
1863
+ }
1864
+ const { [PROVIDER_NAME]: ignoredProvider, ...rest } = providers;
1865
+ void ignoredProvider;
1866
+ const updatedProviders = rest;
1867
+ if (Object.keys(updatedProviders).length === 0) {
1868
+ const { providers: ignoredProviders, ...docWithoutProviders } = document;
1869
+ void ignoredProviders;
1870
+ return { changed: true, content: docWithoutProviders };
1871
+ }
1872
+ return { changed: true, content: { ...document, providers: updatedProviders } };
1873
+ }
1874
+ })
1875
+ ]
1876
+ },
1877
+ install: KIMI_INSTALL_DEFINITION,
1878
+ spawn(context, options) {
1879
+ const args = buildKimiArgs(options.prompt, options.args);
1880
+ if (options.cwd) {
1881
+ return context.command.runCommand("kimi", args, {
1882
+ cwd: options.cwd
1883
+ });
119
1884
  }
1885
+ return context.command.runCommand("kimi", args);
1886
+ }
120
1887
  });
121
- //# sourceMappingURL=kimi.js.map
1888
+ var provider = kimiService;
1889
+ export {
1890
+ KIMI_INSTALL_DEFINITION,
1891
+ kimiService,
1892
+ provider
1893
+ };
1894
+ //# sourceMappingURL=kimi.js.map