poe-code 3.0.168 → 3.0.170
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/poe-goose.js +23 -0
- package/dist/cli/commands/configure-payload.js +10 -0
- package/dist/cli/commands/configure-payload.js.map +1 -1
- package/dist/cli/commands/spawn.js +4 -0
- package/dist/cli/commands/spawn.js.map +1 -1
- package/dist/cli/constants.d.ts +2 -0
- package/dist/cli/constants.js +2 -0
- package/dist/cli/constants.js.map +1 -1
- package/dist/cli/service-registry.d.ts +9 -0
- package/dist/cli/service-registry.js.map +1 -1
- package/dist/index.js +418 -48
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +185 -15
- package/dist/providers/claude-code.js.map +4 -4
- package/dist/providers/codex.js +190 -20
- package/dist/providers/codex.js.map +4 -4
- package/dist/providers/create-provider.d.ts +1 -0
- package/dist/providers/create-provider.js +1 -0
- package/dist/providers/create-provider.js.map +1 -1
- package/dist/providers/goose.d.ts +16 -0
- package/dist/providers/goose.js +2514 -0
- package/dist/providers/goose.js.map +7 -0
- package/dist/providers/kimi.js +184 -14
- package/dist/providers/kimi.js.map +4 -4
- package/dist/providers/opencode.js +184 -14
- package/dist/providers/opencode.js.map +4 -4
- package/dist/providers/poe-agent.js +83 -4
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/utils/command-checks.js +4 -2
- package/dist/utils/command-checks.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,2514 @@
|
|
|
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
|
+
// node_modules/sisteransi/src/index.js
|
|
28
|
+
var require_src = __commonJS({
|
|
29
|
+
"node_modules/sisteransi/src/index.js"(exports, module) {
|
|
30
|
+
"use strict";
|
|
31
|
+
var ESC = "\x1B";
|
|
32
|
+
var CSI = `${ESC}[`;
|
|
33
|
+
var beep = "\x07";
|
|
34
|
+
var cursor = {
|
|
35
|
+
to(x, y) {
|
|
36
|
+
if (!y) return `${CSI}${x + 1}G`;
|
|
37
|
+
return `${CSI}${y + 1};${x + 1}H`;
|
|
38
|
+
},
|
|
39
|
+
move(x, y) {
|
|
40
|
+
let ret = "";
|
|
41
|
+
if (x < 0) ret += `${CSI}${-x}D`;
|
|
42
|
+
else if (x > 0) ret += `${CSI}${x}C`;
|
|
43
|
+
if (y < 0) ret += `${CSI}${-y}A`;
|
|
44
|
+
else if (y > 0) ret += `${CSI}${y}B`;
|
|
45
|
+
return ret;
|
|
46
|
+
},
|
|
47
|
+
up: (count = 1) => `${CSI}${count}A`,
|
|
48
|
+
down: (count = 1) => `${CSI}${count}B`,
|
|
49
|
+
forward: (count = 1) => `${CSI}${count}C`,
|
|
50
|
+
backward: (count = 1) => `${CSI}${count}D`,
|
|
51
|
+
nextLine: (count = 1) => `${CSI}E`.repeat(count),
|
|
52
|
+
prevLine: (count = 1) => `${CSI}F`.repeat(count),
|
|
53
|
+
left: `${CSI}G`,
|
|
54
|
+
hide: `${CSI}?25l`,
|
|
55
|
+
show: `${CSI}?25h`,
|
|
56
|
+
save: `${ESC}7`,
|
|
57
|
+
restore: `${ESC}8`
|
|
58
|
+
};
|
|
59
|
+
var scroll = {
|
|
60
|
+
up: (count = 1) => `${CSI}S`.repeat(count),
|
|
61
|
+
down: (count = 1) => `${CSI}T`.repeat(count)
|
|
62
|
+
};
|
|
63
|
+
var erase = {
|
|
64
|
+
screen: `${CSI}2J`,
|
|
65
|
+
up: (count = 1) => `${CSI}1J`.repeat(count),
|
|
66
|
+
down: (count = 1) => `${CSI}J`.repeat(count),
|
|
67
|
+
line: `${CSI}2K`,
|
|
68
|
+
lineEnd: `${CSI}K`,
|
|
69
|
+
lineStart: `${CSI}1K`,
|
|
70
|
+
lines(count) {
|
|
71
|
+
let clear = "";
|
|
72
|
+
for (let i = 0; i < count; i++)
|
|
73
|
+
clear += this.line + (i < count - 1 ? cursor.up() : "");
|
|
74
|
+
if (count)
|
|
75
|
+
clear += cursor.left;
|
|
76
|
+
return clear;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
module.exports = { cursor, scroll, erase, beep };
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// src/templates/py-poe-spawn/env.hbs
|
|
84
|
+
var require_env = __commonJS({
|
|
85
|
+
"src/templates/py-poe-spawn/env.hbs"(exports, module) {
|
|
86
|
+
module.exports = "POE_API_KEY={{apiKey}}\nPOE_BASE_URL=https://api.poe.com/v1\nMODEL={{model}}\n";
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// src/templates/py-poe-spawn/main.py.hbs
|
|
91
|
+
var require_main_py = __commonJS({
|
|
92
|
+
"src/templates/py-poe-spawn/main.py.hbs"(exports, module) {
|
|
93
|
+
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';
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// src/templates/py-poe-spawn/requirements.txt.hbs
|
|
98
|
+
var require_requirements_txt = __commonJS({
|
|
99
|
+
"src/templates/py-poe-spawn/requirements.txt.hbs"(exports, module) {
|
|
100
|
+
module.exports = "openai>=1.0.0\npython-dotenv>=1.0.0\n";
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// src/templates/codex/config.toml.hbs
|
|
105
|
+
var require_config_toml = __commonJS({
|
|
106
|
+
"src/templates/codex/config.toml.hbs"(exports, module) {
|
|
107
|
+
module.exports = 'model_provider = "poe"\n\n[profiles."{{{profileName}}}"]\nmodel = "{{{model}}}"\nmodel_provider = "poe"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[model_providers.poe]\nname = "poe"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\nrequires_openai_auth = false\nsupports_websockets = false\n';
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// packages/agent-spawn/src/run-command.ts
|
|
112
|
+
import { spawn } from "node:child_process";
|
|
113
|
+
|
|
114
|
+
// packages/agent-defs/src/agents/claude-code.ts
|
|
115
|
+
var claudeCodeAgent = {
|
|
116
|
+
id: "claude-code",
|
|
117
|
+
name: "claude-code",
|
|
118
|
+
label: "Claude Code",
|
|
119
|
+
summary: "Configure Claude Code to route through Poe.",
|
|
120
|
+
aliases: ["claude"],
|
|
121
|
+
binaryName: "claude",
|
|
122
|
+
configPath: "~/.claude/settings.json",
|
|
123
|
+
branding: {
|
|
124
|
+
colors: {
|
|
125
|
+
dark: "#C15F3C",
|
|
126
|
+
light: "#C15F3C"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// packages/agent-defs/src/agents/claude-desktop.ts
|
|
132
|
+
var claudeDesktopAgent = {
|
|
133
|
+
id: "claude-desktop",
|
|
134
|
+
name: "claude-desktop",
|
|
135
|
+
label: "Claude Desktop",
|
|
136
|
+
summary: "Anthropic's official desktop application for Claude",
|
|
137
|
+
configPath: "~/.claude/settings.json",
|
|
138
|
+
branding: {
|
|
139
|
+
colors: {
|
|
140
|
+
dark: "#D97757",
|
|
141
|
+
light: "#D97757"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// packages/agent-defs/src/agents/codex.ts
|
|
147
|
+
var codexAgent = {
|
|
148
|
+
id: "codex",
|
|
149
|
+
name: "codex",
|
|
150
|
+
label: "Codex",
|
|
151
|
+
summary: "Configure Codex to use Poe as the model provider.",
|
|
152
|
+
binaryName: "codex",
|
|
153
|
+
configPath: "~/.codex/config.toml",
|
|
154
|
+
branding: {
|
|
155
|
+
colors: {
|
|
156
|
+
dark: "#D5D9DF",
|
|
157
|
+
light: "#7A7F86"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// packages/agent-defs/src/agents/opencode.ts
|
|
163
|
+
var openCodeAgent = {
|
|
164
|
+
id: "opencode",
|
|
165
|
+
name: "opencode",
|
|
166
|
+
label: "OpenCode CLI",
|
|
167
|
+
summary: "Configure OpenCode CLI to use the Poe API.",
|
|
168
|
+
binaryName: "opencode",
|
|
169
|
+
configPath: "~/.config/opencode/config.json",
|
|
170
|
+
branding: {
|
|
171
|
+
colors: {
|
|
172
|
+
dark: "#4A4F55",
|
|
173
|
+
light: "#2F3338"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// packages/agent-defs/src/agents/kimi.ts
|
|
179
|
+
var kimiAgent = {
|
|
180
|
+
id: "kimi",
|
|
181
|
+
name: "kimi",
|
|
182
|
+
label: "Kimi",
|
|
183
|
+
summary: "Configure Kimi CLI to use Poe API",
|
|
184
|
+
aliases: ["kimi-cli"],
|
|
185
|
+
binaryName: "kimi",
|
|
186
|
+
configPath: "~/.kimi/config.toml",
|
|
187
|
+
branding: {
|
|
188
|
+
colors: {
|
|
189
|
+
dark: "#7B68EE",
|
|
190
|
+
light: "#6A5ACD"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// packages/agent-defs/src/agents/goose.ts
|
|
196
|
+
var gooseAgent = {
|
|
197
|
+
id: "goose",
|
|
198
|
+
name: "goose",
|
|
199
|
+
label: "Goose",
|
|
200
|
+
summary: "Block's open-source AI agent with ACP support.",
|
|
201
|
+
binaryName: "goose",
|
|
202
|
+
configPath: "~/.config/goose/config.yaml",
|
|
203
|
+
branding: {
|
|
204
|
+
colors: {
|
|
205
|
+
dark: "#FF6B35",
|
|
206
|
+
light: "#E85D26"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// packages/agent-defs/src/registry.ts
|
|
212
|
+
var allAgents = [
|
|
213
|
+
claudeCodeAgent,
|
|
214
|
+
claudeDesktopAgent,
|
|
215
|
+
codexAgent,
|
|
216
|
+
openCodeAgent,
|
|
217
|
+
kimiAgent,
|
|
218
|
+
gooseAgent
|
|
219
|
+
];
|
|
220
|
+
var lookup = /* @__PURE__ */ new Map();
|
|
221
|
+
for (const agent of allAgents) {
|
|
222
|
+
const values = [agent.id, agent.name, ...agent.aliases ?? []];
|
|
223
|
+
for (const value of values) {
|
|
224
|
+
const normalized = value.toLowerCase();
|
|
225
|
+
if (!lookup.has(normalized)) {
|
|
226
|
+
lookup.set(normalized, agent.id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// packages/agent-spawn/src/configs/mcp.ts
|
|
232
|
+
function toJsonMcpServers(servers) {
|
|
233
|
+
const out = {};
|
|
234
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
235
|
+
const mapped = { command: server.command };
|
|
236
|
+
if (server.args && server.args.length > 0) {
|
|
237
|
+
mapped.args = server.args;
|
|
238
|
+
}
|
|
239
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
240
|
+
mapped.env = server.env;
|
|
241
|
+
}
|
|
242
|
+
out[name] = mapped;
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
function toTomlString(value) {
|
|
247
|
+
return JSON.stringify(value);
|
|
248
|
+
}
|
|
249
|
+
function toTomlArray(values) {
|
|
250
|
+
const serialized = values.map((value) => toTomlString(value));
|
|
251
|
+
return `[${serialized.join(", ")}]`;
|
|
252
|
+
}
|
|
253
|
+
function toTomlInlineTable(values) {
|
|
254
|
+
const parts = [];
|
|
255
|
+
for (const [key, value] of Object.entries(values)) {
|
|
256
|
+
parts.push(`${JSON.stringify(key)}=${toTomlString(value)}`);
|
|
257
|
+
}
|
|
258
|
+
return `{${parts.join(", ")}}`;
|
|
259
|
+
}
|
|
260
|
+
function serializeJsonMcpArgs(servers) {
|
|
261
|
+
return ["--mcp-config", JSON.stringify({ mcpServers: toJsonMcpServers(servers) })];
|
|
262
|
+
}
|
|
263
|
+
function serializeOpenCodeMcpEnv(servers) {
|
|
264
|
+
const mcp = {};
|
|
265
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
266
|
+
const entry = { type: "local", command: [server.command, ...server.args ?? []] };
|
|
267
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
268
|
+
entry.environment = server.env;
|
|
269
|
+
}
|
|
270
|
+
mcp[name] = entry;
|
|
271
|
+
}
|
|
272
|
+
return { OPENCODE_CONFIG_CONTENT: JSON.stringify({ mcp }) };
|
|
273
|
+
}
|
|
274
|
+
function serializeCodexMcpArgs(servers) {
|
|
275
|
+
const args = [];
|
|
276
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
277
|
+
const prefix = `mcp_servers.${name}`;
|
|
278
|
+
args.push("-c", `${prefix}.command=${toTomlString(server.command)}`);
|
|
279
|
+
if (server.args && server.args.length > 0) {
|
|
280
|
+
args.push("-c", `${prefix}.args=${toTomlArray(server.args)}`);
|
|
281
|
+
}
|
|
282
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
283
|
+
args.push("-c", `${prefix}.env=${toTomlInlineTable(server.env)}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return args;
|
|
287
|
+
}
|
|
288
|
+
function serializeGooseMcpArgs(servers) {
|
|
289
|
+
return Object.values(servers).flatMap((server) => [
|
|
290
|
+
"--with-extension",
|
|
291
|
+
[server.command, ...server.args ?? []].join(" ")
|
|
292
|
+
]);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// packages/agent-spawn/src/configs/claude-code.ts
|
|
296
|
+
var claudeCodeSpawnConfig = {
|
|
297
|
+
kind: "cli",
|
|
298
|
+
agentId: "claude-code",
|
|
299
|
+
// ACP adapter support: yes (adapter: "claude")
|
|
300
|
+
adapter: "claude",
|
|
301
|
+
promptFlag: "-p",
|
|
302
|
+
modelFlag: "--model",
|
|
303
|
+
modelStripProviderPrefix: true,
|
|
304
|
+
modelTransform: (model) => model.replaceAll(".", "-"),
|
|
305
|
+
defaultArgs: [
|
|
306
|
+
"--output-format",
|
|
307
|
+
"stream-json",
|
|
308
|
+
"--verbose"
|
|
309
|
+
],
|
|
310
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
311
|
+
modes: {
|
|
312
|
+
yolo: ["--dangerously-skip-permissions"],
|
|
313
|
+
edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
|
|
314
|
+
read: ["--permission-mode", "plan"]
|
|
315
|
+
},
|
|
316
|
+
stdinMode: {
|
|
317
|
+
omitPrompt: true,
|
|
318
|
+
extraArgs: ["--input-format", "text"]
|
|
319
|
+
},
|
|
320
|
+
interactive: {
|
|
321
|
+
defaultArgs: []
|
|
322
|
+
},
|
|
323
|
+
resumeCommand: (threadId) => ["--resume", threadId]
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// packages/agent-spawn/src/configs/codex.ts
|
|
327
|
+
var codexSpawnConfig = {
|
|
328
|
+
kind: "cli",
|
|
329
|
+
agentId: "codex",
|
|
330
|
+
// ACP adapter support: yes (adapter: "codex")
|
|
331
|
+
adapter: "codex",
|
|
332
|
+
promptFlag: "exec",
|
|
333
|
+
modelFlag: "--model",
|
|
334
|
+
modelStripProviderPrefix: true,
|
|
335
|
+
defaultArgs: ["--skip-git-repo-check", "--json"],
|
|
336
|
+
mcpArgs: serializeCodexMcpArgs,
|
|
337
|
+
mcpArgsBeforeCommand: true,
|
|
338
|
+
modes: {
|
|
339
|
+
yolo: ["-s", "danger-full-access"],
|
|
340
|
+
edit: ["-s", "workspace-write"],
|
|
341
|
+
read: ["-s", "read-only"]
|
|
342
|
+
},
|
|
343
|
+
stdinMode: {
|
|
344
|
+
omitPrompt: true,
|
|
345
|
+
extraArgs: ["-"]
|
|
346
|
+
},
|
|
347
|
+
interactive: {
|
|
348
|
+
defaultArgs: ["-a", "never"]
|
|
349
|
+
},
|
|
350
|
+
resumeCommand: (threadId, cwd) => ["resume", "-C", cwd, threadId]
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// packages/agent-spawn/src/configs/opencode.ts
|
|
354
|
+
var openCodeSpawnConfig = {
|
|
355
|
+
kind: "cli",
|
|
356
|
+
agentId: "opencode",
|
|
357
|
+
// ACP adapter support: yes (adapter: "opencode").
|
|
358
|
+
// OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
|
|
359
|
+
// (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
|
|
360
|
+
adapter: "opencode",
|
|
361
|
+
promptFlag: "run",
|
|
362
|
+
modelFlag: "--model",
|
|
363
|
+
modelStripProviderPrefix: false,
|
|
364
|
+
modelTransform: (model) => {
|
|
365
|
+
return model.startsWith("poe/") ? model : `poe/${model}`;
|
|
366
|
+
},
|
|
367
|
+
defaultArgs: ["--format", "json"],
|
|
368
|
+
modes: {
|
|
369
|
+
yolo: [],
|
|
370
|
+
edit: [],
|
|
371
|
+
read: ["--agent", "plan"]
|
|
372
|
+
},
|
|
373
|
+
interactive: {
|
|
374
|
+
defaultArgs: [],
|
|
375
|
+
promptFlag: "--prompt"
|
|
376
|
+
},
|
|
377
|
+
resumeCommand: (threadId, cwd) => [cwd, "--session", threadId],
|
|
378
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
379
|
+
};
|
|
380
|
+
var openCodeAcpSpawnConfig = {
|
|
381
|
+
kind: "acp",
|
|
382
|
+
agentId: "opencode",
|
|
383
|
+
acpArgs: ["acp"],
|
|
384
|
+
skipAuth: true,
|
|
385
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// packages/agent-spawn/src/configs/kimi.ts
|
|
389
|
+
var kimiSpawnConfig = {
|
|
390
|
+
kind: "cli",
|
|
391
|
+
agentId: "kimi",
|
|
392
|
+
// ACP adapter support: yes (adapter: "kimi").
|
|
393
|
+
// Kimi's `--output-format stream-json` emits OpenAI-style `{ role, content }` JSON
|
|
394
|
+
// (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
|
|
395
|
+
adapter: "kimi",
|
|
396
|
+
promptFlag: "-p",
|
|
397
|
+
modelStripProviderPrefix: true,
|
|
398
|
+
defaultArgs: ["--print", "--output-format", "stream-json"],
|
|
399
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
400
|
+
modes: {
|
|
401
|
+
yolo: ["--yolo"],
|
|
402
|
+
edit: [],
|
|
403
|
+
read: []
|
|
404
|
+
},
|
|
405
|
+
stdinMode: {
|
|
406
|
+
omitPrompt: true,
|
|
407
|
+
extraArgs: ["--input-format", "stream-json"]
|
|
408
|
+
},
|
|
409
|
+
interactive: {
|
|
410
|
+
defaultArgs: [],
|
|
411
|
+
promptFlag: "-p"
|
|
412
|
+
},
|
|
413
|
+
resumeCommand: (threadId, cwd) => ["--session", threadId, "--work-dir", cwd]
|
|
414
|
+
};
|
|
415
|
+
var kimiAcpSpawnConfig = {
|
|
416
|
+
kind: "acp",
|
|
417
|
+
agentId: "kimi",
|
|
418
|
+
acpArgs: ["acp"]
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// packages/agent-spawn/src/configs/goose.ts
|
|
422
|
+
var gooseSpawnConfig = {
|
|
423
|
+
kind: "cli",
|
|
424
|
+
agentId: "goose",
|
|
425
|
+
adapter: "native",
|
|
426
|
+
promptFlag: "--text",
|
|
427
|
+
modelFlag: "--model",
|
|
428
|
+
modelStripProviderPrefix: false,
|
|
429
|
+
defaultArgs: ["run", "--output-format", "stream-json"],
|
|
430
|
+
defaultArgsPosition: "beforePrompt",
|
|
431
|
+
mcpArgs: serializeGooseMcpArgs,
|
|
432
|
+
mcpArgsPosition: "beforePrompt",
|
|
433
|
+
modes: {
|
|
434
|
+
yolo: { env: { GOOSE_MODE: "auto" } },
|
|
435
|
+
edit: { env: { GOOSE_MODE: "smart_approve" } },
|
|
436
|
+
read: { env: { GOOSE_MODE: "chat" } }
|
|
437
|
+
},
|
|
438
|
+
stdinMode: {
|
|
439
|
+
omitPrompt: true,
|
|
440
|
+
extraArgs: ["--instructions", "-"]
|
|
441
|
+
},
|
|
442
|
+
interactive: {
|
|
443
|
+
defaultArgs: ["session"],
|
|
444
|
+
defaultArgsPosition: "beforePrompt"
|
|
445
|
+
},
|
|
446
|
+
resumeCommand: () => ["run", "--resume", "--text", "continue"]
|
|
447
|
+
};
|
|
448
|
+
var gooseAcpSpawnConfig = {
|
|
449
|
+
kind: "acp",
|
|
450
|
+
agentId: "goose",
|
|
451
|
+
acpArgs: ["acp"],
|
|
452
|
+
skipAuth: true
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// packages/agent-spawn/src/configs/index.ts
|
|
456
|
+
var allSpawnConfigs = [
|
|
457
|
+
claudeCodeSpawnConfig,
|
|
458
|
+
codexSpawnConfig,
|
|
459
|
+
openCodeSpawnConfig,
|
|
460
|
+
kimiSpawnConfig,
|
|
461
|
+
gooseSpawnConfig
|
|
462
|
+
];
|
|
463
|
+
var lookup2 = /* @__PURE__ */ new Map();
|
|
464
|
+
for (const config of allSpawnConfigs) {
|
|
465
|
+
lookup2.set(config.agentId, config);
|
|
466
|
+
}
|
|
467
|
+
var acpLookup = /* @__PURE__ */ new Map();
|
|
468
|
+
acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
|
|
469
|
+
acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
|
|
470
|
+
acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
|
|
471
|
+
|
|
472
|
+
// packages/agent-spawn/src/spawn.ts
|
|
473
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
474
|
+
|
|
475
|
+
// packages/agent-spawn/src/spawn-interactive.ts
|
|
476
|
+
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
477
|
+
|
|
478
|
+
// packages/design-system/src/tokens/colors.ts
|
|
479
|
+
import chalk from "chalk";
|
|
480
|
+
var dark = {
|
|
481
|
+
header: (text4) => chalk.magentaBright.bold(text4),
|
|
482
|
+
divider: (text4) => chalk.dim(text4),
|
|
483
|
+
prompt: (text4) => chalk.cyan(text4),
|
|
484
|
+
number: (text4) => chalk.cyanBright(text4),
|
|
485
|
+
intro: (text4) => chalk.bgMagenta.white(` Poe - ${text4} `),
|
|
486
|
+
resolvedSymbol: chalk.magenta("\u25C7"),
|
|
487
|
+
errorSymbol: chalk.red("\u25A0"),
|
|
488
|
+
accent: (text4) => chalk.cyan(text4),
|
|
489
|
+
muted: (text4) => chalk.dim(text4),
|
|
490
|
+
success: (text4) => chalk.green(text4),
|
|
491
|
+
warning: (text4) => chalk.yellow(text4),
|
|
492
|
+
error: (text4) => chalk.red(text4),
|
|
493
|
+
info: (text4) => chalk.magenta(text4),
|
|
494
|
+
badge: (text4) => chalk.bgYellow.black(` ${text4} `)
|
|
495
|
+
};
|
|
496
|
+
var light = {
|
|
497
|
+
header: (text4) => chalk.hex("#a200ff").bold(text4),
|
|
498
|
+
divider: (text4) => chalk.hex("#666666")(text4),
|
|
499
|
+
prompt: (text4) => chalk.hex("#006699").bold(text4),
|
|
500
|
+
number: (text4) => chalk.hex("#0077cc").bold(text4),
|
|
501
|
+
intro: (text4) => chalk.bgHex("#a200ff").white(` Poe - ${text4} `),
|
|
502
|
+
resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
|
|
503
|
+
errorSymbol: chalk.hex("#cc0000")("\u25A0"),
|
|
504
|
+
accent: (text4) => chalk.hex("#006699").bold(text4),
|
|
505
|
+
muted: (text4) => chalk.hex("#666666")(text4),
|
|
506
|
+
success: (text4) => chalk.hex("#008800")(text4),
|
|
507
|
+
warning: (text4) => chalk.hex("#cc6600")(text4),
|
|
508
|
+
error: (text4) => chalk.hex("#cc0000")(text4),
|
|
509
|
+
info: (text4) => chalk.hex("#a200ff")(text4),
|
|
510
|
+
badge: (text4) => chalk.bgHex("#cc6600").white(` ${text4} `)
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// packages/design-system/src/tokens/typography.ts
|
|
514
|
+
import chalk2 from "chalk";
|
|
515
|
+
|
|
516
|
+
// packages/design-system/src/components/text.ts
|
|
517
|
+
import chalk3 from "chalk";
|
|
518
|
+
|
|
519
|
+
// packages/design-system/src/internal/output-format.ts
|
|
520
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
521
|
+
var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
|
|
522
|
+
var formatStorage = new AsyncLocalStorage();
|
|
523
|
+
var cached;
|
|
524
|
+
function resolveOutputFormat(env = process.env) {
|
|
525
|
+
const scoped = formatStorage.getStore();
|
|
526
|
+
if (scoped) {
|
|
527
|
+
return scoped;
|
|
528
|
+
}
|
|
529
|
+
if (cached) {
|
|
530
|
+
return cached;
|
|
531
|
+
}
|
|
532
|
+
const raw = env.OUTPUT_FORMAT?.toLowerCase();
|
|
533
|
+
cached = VALID_FORMATS.has(raw) ? raw : "terminal";
|
|
534
|
+
return cached;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// packages/design-system/src/internal/theme-detect.ts
|
|
538
|
+
function detectThemeFromEnv(env) {
|
|
539
|
+
const apple = env.APPLE_INTERFACE_STYLE;
|
|
540
|
+
if (typeof apple === "string") {
|
|
541
|
+
return apple.toLowerCase() === "dark" ? "dark" : "light";
|
|
542
|
+
}
|
|
543
|
+
const vscodeKind = env.VSCODE_COLOR_THEME_KIND;
|
|
544
|
+
if (typeof vscodeKind === "string") {
|
|
545
|
+
const normalized = vscodeKind.toLowerCase();
|
|
546
|
+
if (normalized.includes("light")) {
|
|
547
|
+
return "light";
|
|
548
|
+
}
|
|
549
|
+
if (normalized.includes("dark")) {
|
|
550
|
+
return "dark";
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const colorFGBG = env.COLORFGBG;
|
|
554
|
+
if (typeof colorFGBG === "string") {
|
|
555
|
+
const parts = colorFGBG.split(";").map((part) => Number.parseInt(part, 10));
|
|
556
|
+
const background = parts.at(-1);
|
|
557
|
+
if (Number.isFinite(background)) {
|
|
558
|
+
return background >= 8 ? "light" : "dark";
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return void 0;
|
|
562
|
+
}
|
|
563
|
+
function resolveThemeName(env = process.env) {
|
|
564
|
+
const raw = (env.POE_CODE_THEME ?? env.POE_THEME)?.toLowerCase();
|
|
565
|
+
if (raw === "light" || raw === "dark") {
|
|
566
|
+
return raw;
|
|
567
|
+
}
|
|
568
|
+
const detected = detectThemeFromEnv(env);
|
|
569
|
+
if (detected) {
|
|
570
|
+
return detected;
|
|
571
|
+
}
|
|
572
|
+
return "dark";
|
|
573
|
+
}
|
|
574
|
+
var cachedTheme;
|
|
575
|
+
function getTheme(env) {
|
|
576
|
+
if (cachedTheme) {
|
|
577
|
+
return cachedTheme;
|
|
578
|
+
}
|
|
579
|
+
const themeName = resolveThemeName(env);
|
|
580
|
+
cachedTheme = themeName === "light" ? light : dark;
|
|
581
|
+
return cachedTheme;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// packages/design-system/src/components/symbols.ts
|
|
585
|
+
import chalk4 from "chalk";
|
|
586
|
+
var symbols = {
|
|
587
|
+
get info() {
|
|
588
|
+
const format = resolveOutputFormat();
|
|
589
|
+
if (format === "json") return "info";
|
|
590
|
+
if (format === "markdown") return "(i)";
|
|
591
|
+
return chalk4.magenta("\u25CF");
|
|
592
|
+
},
|
|
593
|
+
get success() {
|
|
594
|
+
const format = resolveOutputFormat();
|
|
595
|
+
if (format === "json") return "success";
|
|
596
|
+
if (format === "markdown") return "[ok]";
|
|
597
|
+
return chalk4.magenta("\u25C6");
|
|
598
|
+
},
|
|
599
|
+
get resolved() {
|
|
600
|
+
const format = resolveOutputFormat();
|
|
601
|
+
if (format === "json") return "resolved";
|
|
602
|
+
if (format === "markdown") return ">";
|
|
603
|
+
return getTheme().resolvedSymbol;
|
|
604
|
+
},
|
|
605
|
+
get errorResolved() {
|
|
606
|
+
const format = resolveOutputFormat();
|
|
607
|
+
if (format === "json") return "error";
|
|
608
|
+
if (format === "markdown") return "[!]";
|
|
609
|
+
return getTheme().errorSymbol;
|
|
610
|
+
},
|
|
611
|
+
get bar() {
|
|
612
|
+
const format = resolveOutputFormat();
|
|
613
|
+
if (format === "json") return "";
|
|
614
|
+
if (format === "markdown") return "|";
|
|
615
|
+
return "\u2502";
|
|
616
|
+
},
|
|
617
|
+
cornerTopRight: "\u256E",
|
|
618
|
+
cornerBottomRight: "\u256F",
|
|
619
|
+
get warning() {
|
|
620
|
+
const format = resolveOutputFormat();
|
|
621
|
+
if (format === "json") return "warning";
|
|
622
|
+
if (format === "markdown") return "[!]";
|
|
623
|
+
return "\u25B2";
|
|
624
|
+
},
|
|
625
|
+
get active() {
|
|
626
|
+
const format = resolveOutputFormat();
|
|
627
|
+
if (format === "json") return "active";
|
|
628
|
+
if (format === "markdown") return "[x]";
|
|
629
|
+
return "\u25C6";
|
|
630
|
+
},
|
|
631
|
+
get inactive() {
|
|
632
|
+
const format = resolveOutputFormat();
|
|
633
|
+
if (format === "json") return "inactive";
|
|
634
|
+
if (format === "markdown") return "[ ]";
|
|
635
|
+
return "\u25CB";
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// packages/design-system/src/components/logger.ts
|
|
640
|
+
import chalk6 from "chalk";
|
|
641
|
+
|
|
642
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
643
|
+
import chalk5 from "chalk";
|
|
644
|
+
|
|
645
|
+
// packages/design-system/src/internal/strip-ansi.ts
|
|
646
|
+
function stripAnsi(value) {
|
|
647
|
+
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
651
|
+
function writeTerminalMessage(msg, {
|
|
652
|
+
symbol = chalk5.gray("\u2502"),
|
|
653
|
+
secondarySymbol = chalk5.gray("\u2502"),
|
|
654
|
+
spacing: spacing2 = 1,
|
|
655
|
+
withGuide = true
|
|
656
|
+
} = {}) {
|
|
657
|
+
const lines = [];
|
|
658
|
+
const showGuide = withGuide !== false;
|
|
659
|
+
const contentLines = msg.split("\n");
|
|
660
|
+
const prefix = showGuide ? `${symbol} ` : "";
|
|
661
|
+
const continuationPrefix = showGuide ? `${secondarySymbol} ` : "";
|
|
662
|
+
const emptyGuide = showGuide ? secondarySymbol : "";
|
|
663
|
+
for (let index = 0; index < spacing2; index += 1) {
|
|
664
|
+
lines.push(emptyGuide);
|
|
665
|
+
}
|
|
666
|
+
if (contentLines.length === 0) {
|
|
667
|
+
process.stdout.write("\n");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const [firstLine = "", ...continuationLines] = contentLines;
|
|
671
|
+
if (firstLine.length > 0) {
|
|
672
|
+
lines.push(`${prefix}${firstLine}`);
|
|
673
|
+
} else {
|
|
674
|
+
lines.push(showGuide ? symbol : "");
|
|
675
|
+
}
|
|
676
|
+
for (const line of continuationLines) {
|
|
677
|
+
if (line.length > 0) {
|
|
678
|
+
lines.push(`${continuationPrefix}${line}`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
lines.push(emptyGuide);
|
|
682
|
+
}
|
|
683
|
+
process.stdout.write(`${lines.join("\n")}
|
|
684
|
+
`);
|
|
685
|
+
}
|
|
686
|
+
function message(msg, options) {
|
|
687
|
+
const format = resolveOutputFormat();
|
|
688
|
+
if (format === "markdown") {
|
|
689
|
+
process.stdout.write(`- ${stripAnsi(msg)}
|
|
690
|
+
`);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (format === "json") {
|
|
694
|
+
process.stdout.write(
|
|
695
|
+
`${JSON.stringify({ level: "message", message: stripAnsi(msg) })}
|
|
696
|
+
`
|
|
697
|
+
);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
writeTerminalMessage(msg, options);
|
|
701
|
+
}
|
|
702
|
+
function info(msg) {
|
|
703
|
+
const format = resolveOutputFormat();
|
|
704
|
+
if (format === "markdown") {
|
|
705
|
+
process.stdout.write(`- **info:** ${stripAnsi(msg)}
|
|
706
|
+
`);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (format === "json") {
|
|
710
|
+
process.stdout.write(
|
|
711
|
+
`${JSON.stringify({ level: "info", message: stripAnsi(msg) })}
|
|
712
|
+
`
|
|
713
|
+
);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
message(msg, { symbol: symbols.info });
|
|
717
|
+
}
|
|
718
|
+
function success(msg) {
|
|
719
|
+
const format = resolveOutputFormat();
|
|
720
|
+
if (format === "markdown") {
|
|
721
|
+
process.stdout.write(`- **success:** ${stripAnsi(msg)}
|
|
722
|
+
`);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (format === "json") {
|
|
726
|
+
process.stdout.write(
|
|
727
|
+
`${JSON.stringify({ level: "success", message: stripAnsi(msg) })}
|
|
728
|
+
`
|
|
729
|
+
);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
message(msg, { symbol: symbols.success });
|
|
733
|
+
}
|
|
734
|
+
function warn(msg) {
|
|
735
|
+
const format = resolveOutputFormat();
|
|
736
|
+
if (format === "markdown") {
|
|
737
|
+
process.stdout.write(`- **warning:** ${stripAnsi(msg)}
|
|
738
|
+
`);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (format === "json") {
|
|
742
|
+
process.stdout.write(
|
|
743
|
+
`${JSON.stringify({ level: "warn", message: stripAnsi(msg) })}
|
|
744
|
+
`
|
|
745
|
+
);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
message(msg, { symbol: chalk5.yellow("\u25B2") });
|
|
749
|
+
}
|
|
750
|
+
function error(msg) {
|
|
751
|
+
const format = resolveOutputFormat();
|
|
752
|
+
if (format === "markdown") {
|
|
753
|
+
process.stdout.write(`- **error:** ${stripAnsi(msg)}
|
|
754
|
+
`);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (format === "json") {
|
|
758
|
+
process.stdout.write(
|
|
759
|
+
`${JSON.stringify({ level: "error", message: stripAnsi(msg) })}
|
|
760
|
+
`
|
|
761
|
+
);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
message(msg, { symbol: chalk5.red("\u25A0") });
|
|
765
|
+
}
|
|
766
|
+
var log = {
|
|
767
|
+
info,
|
|
768
|
+
success,
|
|
769
|
+
message,
|
|
770
|
+
warn,
|
|
771
|
+
error
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// packages/design-system/src/components/logger.ts
|
|
775
|
+
function createLogger(emitter) {
|
|
776
|
+
const emit = (level, message2) => {
|
|
777
|
+
if (emitter) {
|
|
778
|
+
emitter(message2);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (level === "success") {
|
|
782
|
+
log.success(message2);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (level === "warn") {
|
|
786
|
+
log.warn(message2);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (level === "error") {
|
|
790
|
+
log.error(message2);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
log.info(message2);
|
|
794
|
+
};
|
|
795
|
+
return {
|
|
796
|
+
info(message2) {
|
|
797
|
+
emit("info", message2);
|
|
798
|
+
},
|
|
799
|
+
success(message2) {
|
|
800
|
+
emit("success", message2);
|
|
801
|
+
},
|
|
802
|
+
warn(message2) {
|
|
803
|
+
emit("warn", message2);
|
|
804
|
+
},
|
|
805
|
+
error(message2) {
|
|
806
|
+
emit("error", message2);
|
|
807
|
+
},
|
|
808
|
+
resolved(label, value) {
|
|
809
|
+
if (emitter) {
|
|
810
|
+
emitter(`${label}: ${value}`);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
log.message(`${label}
|
|
814
|
+
${value}`, { symbol: symbols.resolved });
|
|
815
|
+
},
|
|
816
|
+
errorResolved(label, value) {
|
|
817
|
+
if (emitter) {
|
|
818
|
+
emitter(`${label}: ${value}`);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
log.message(`${label}
|
|
822
|
+
${value}`, { symbol: symbols.errorResolved });
|
|
823
|
+
},
|
|
824
|
+
message(message2, symbol) {
|
|
825
|
+
if (emitter) {
|
|
826
|
+
emitter(message2);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
log.message(message2, { symbol: symbol ?? chalk6.gray("\u2502") });
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
var logger = createLogger();
|
|
834
|
+
|
|
835
|
+
// packages/design-system/src/components/table.ts
|
|
836
|
+
import { Table } from "console-table-printer";
|
|
837
|
+
|
|
838
|
+
// packages/design-system/src/acp/components.ts
|
|
839
|
+
import chalk7 from "chalk";
|
|
840
|
+
var AGENT_PREFIX = `${chalk7.green.bold("\u2713")} agent: `;
|
|
841
|
+
|
|
842
|
+
// packages/design-system/src/dashboard/buffer.ts
|
|
843
|
+
import chalk8 from "chalk";
|
|
844
|
+
|
|
845
|
+
// packages/design-system/src/dashboard/terminal.ts
|
|
846
|
+
import readline from "node:readline";
|
|
847
|
+
import { PassThrough } from "node:stream";
|
|
848
|
+
|
|
849
|
+
// packages/design-system/src/prompts/index.ts
|
|
850
|
+
import chalk15 from "chalk";
|
|
851
|
+
import * as clack from "@clack/prompts";
|
|
852
|
+
|
|
853
|
+
// packages/design-system/src/prompts/primitives/cancel.ts
|
|
854
|
+
import chalk9 from "chalk";
|
|
855
|
+
|
|
856
|
+
// node_modules/@clack/core/dist/index.mjs
|
|
857
|
+
var import_sisteransi = __toESM(require_src(), 1);
|
|
858
|
+
import { stdout as R, stdin as q } from "node:process";
|
|
859
|
+
import * as k from "node:readline";
|
|
860
|
+
import ot from "node:readline";
|
|
861
|
+
import { ReadStream as J } from "node:tty";
|
|
862
|
+
var P = new RegExp("[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*", "yu");
|
|
863
|
+
var ct = new RegExp("\\p{M}+", "gu");
|
|
864
|
+
var pt = { limit: 1 / 0, ellipsis: "" };
|
|
865
|
+
var ft = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
|
|
866
|
+
var j = "\x07";
|
|
867
|
+
var Q = "[";
|
|
868
|
+
var dt = "]";
|
|
869
|
+
var U = `${dt}8;;`;
|
|
870
|
+
var et = new RegExp(`(?:\\${Q}(?<code>\\d+)m|\\${U}(?<uri>.*)${j})`, "y");
|
|
871
|
+
var At = ["up", "down", "left", "right", "space", "enter", "cancel"];
|
|
872
|
+
var _ = { actions: new Set(At), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true };
|
|
873
|
+
var bt = globalThis.process.platform.startsWith("win");
|
|
874
|
+
|
|
875
|
+
// packages/design-system/src/prompts/primitives/intro.ts
|
|
876
|
+
import chalk10 from "chalk";
|
|
877
|
+
|
|
878
|
+
// packages/design-system/src/prompts/primitives/note.ts
|
|
879
|
+
import chalk11 from "chalk";
|
|
880
|
+
|
|
881
|
+
// packages/design-system/src/prompts/primitives/outro.ts
|
|
882
|
+
import chalk12 from "chalk";
|
|
883
|
+
|
|
884
|
+
// packages/design-system/src/prompts/primitives/spinner.ts
|
|
885
|
+
import chalk14 from "chalk";
|
|
886
|
+
|
|
887
|
+
// packages/design-system/src/static/spinner.ts
|
|
888
|
+
import chalk13 from "chalk";
|
|
889
|
+
|
|
890
|
+
// packages/design-system/src/static/menu.ts
|
|
891
|
+
import chalk16 from "chalk";
|
|
892
|
+
|
|
893
|
+
// packages/agent-spawn/src/acp/replay.ts
|
|
894
|
+
import path from "node:path";
|
|
895
|
+
import { homedir } from "node:os";
|
|
896
|
+
import { open, readdir } from "node:fs/promises";
|
|
897
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
898
|
+
|
|
899
|
+
// packages/agent-spawn/src/acp/spawn.ts
|
|
900
|
+
import { spawn as spawnChildProcess3 } from "node:child_process";
|
|
901
|
+
|
|
902
|
+
// packages/poe-acp-client/src/acp-client.ts
|
|
903
|
+
import { isAbsolute } from "node:path";
|
|
904
|
+
|
|
905
|
+
// packages/poe-acp-client/src/acp-transport.ts
|
|
906
|
+
import {
|
|
907
|
+
spawn as spawnChildProcess4
|
|
908
|
+
} from "node:child_process";
|
|
909
|
+
|
|
910
|
+
// packages/poe-acp-client/src/run-report.ts
|
|
911
|
+
import * as fsPromises from "node:fs/promises";
|
|
912
|
+
import { homedir as homedir2 } from "node:os";
|
|
913
|
+
import { join } from "node:path";
|
|
914
|
+
|
|
915
|
+
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
916
|
+
import path2 from "node:path";
|
|
917
|
+
import { homedir as homedir3 } from "node:os";
|
|
918
|
+
import { mkdir, open as open2 } from "node:fs/promises";
|
|
919
|
+
|
|
920
|
+
// src/utils/command-checks.ts
|
|
921
|
+
function formatCommandRunnerResult(result) {
|
|
922
|
+
const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
|
|
923
|
+
const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
|
|
924
|
+
return `stdout:
|
|
925
|
+
${stdout}
|
|
926
|
+
stderr:
|
|
927
|
+
${stderr}`;
|
|
928
|
+
}
|
|
929
|
+
function describeCommandExpectation(command, args, expectedOutput) {
|
|
930
|
+
return `${renderCommandLine(command, args)} (expecting "${expectedOutput}")`;
|
|
931
|
+
}
|
|
932
|
+
function createCommandExpectationCheck(options) {
|
|
933
|
+
return {
|
|
934
|
+
id: options.id,
|
|
935
|
+
description: describeCommandExpectation(
|
|
936
|
+
options.command,
|
|
937
|
+
options.args,
|
|
938
|
+
options.expectedOutput
|
|
939
|
+
),
|
|
940
|
+
async run(context) {
|
|
941
|
+
await runAndMatchOutput(context, options);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
async function runAndMatchOutput(context, options) {
|
|
946
|
+
const rendered = renderCommandLine(options.command, options.args);
|
|
947
|
+
if (options.skipOnDryRun !== false && context.isDryRun) {
|
|
948
|
+
if (context.logDryRun) {
|
|
949
|
+
context.logDryRun(
|
|
950
|
+
`Dry run: ${rendered} (expecting "${options.expectedOutput}")`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
const result = await context.runCommand(options.command, options.args);
|
|
956
|
+
if (result.exitCode !== 0) {
|
|
957
|
+
const detail = formatCommandRunnerResult(result);
|
|
958
|
+
throw new Error(
|
|
959
|
+
[`Command ${rendered} failed with exit code ${result.exitCode}.`, detail].join("\n")
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
if (!stdoutMatchesExpected(result.stdout, options.expectedOutput)) {
|
|
963
|
+
const detail = formatCommandRunnerResult(result);
|
|
964
|
+
const received = result.stdout.trim();
|
|
965
|
+
throw new Error(
|
|
966
|
+
[
|
|
967
|
+
`Command ${rendered} failed: expected "${options.expectedOutput}" but received "${received}".`,
|
|
968
|
+
detail
|
|
969
|
+
].join("\n")
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function stdoutMatchesExpected(stdout, expected) {
|
|
974
|
+
const trimmed = stdout.trim();
|
|
975
|
+
if (trimmed === expected) {
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
979
|
+
if (lines.some((line) => line === expected)) {
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
for (const line of lines) {
|
|
983
|
+
if (line[0] !== "{") continue;
|
|
984
|
+
try {
|
|
985
|
+
const parsed = JSON.parse(line);
|
|
986
|
+
if (parsed.type === "result" && parsed.result === expected) {
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
989
|
+
} catch {
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
function renderCommandLine(command, args) {
|
|
996
|
+
return [command, ...args].map(quoteIfNeeded).join(" ").trim();
|
|
997
|
+
}
|
|
998
|
+
function quoteIfNeeded(value) {
|
|
999
|
+
if (value.length === 0) {
|
|
1000
|
+
return '""';
|
|
1001
|
+
}
|
|
1002
|
+
if (needsQuoting(value)) {
|
|
1003
|
+
return `"${value.replaceAll('"', '\\"')}"`;
|
|
1004
|
+
}
|
|
1005
|
+
return value;
|
|
1006
|
+
}
|
|
1007
|
+
function needsQuoting(value) {
|
|
1008
|
+
return value.includes(" ") || value.includes(" ") || value.includes("\n");
|
|
1009
|
+
}
|
|
1010
|
+
function createBinaryExistsCheck(binaryName, id, description) {
|
|
1011
|
+
return {
|
|
1012
|
+
id,
|
|
1013
|
+
description,
|
|
1014
|
+
async run({ runCommand: runCommand2 }) {
|
|
1015
|
+
const commonPaths = [
|
|
1016
|
+
`/usr/local/bin/${binaryName}`,
|
|
1017
|
+
`/usr/bin/${binaryName}`,
|
|
1018
|
+
`$HOME/.local/bin/${binaryName}`,
|
|
1019
|
+
`$HOME/.claude/local/bin/${binaryName}`
|
|
1020
|
+
];
|
|
1021
|
+
const detectors = [
|
|
1022
|
+
{
|
|
1023
|
+
command: "which",
|
|
1024
|
+
args: [binaryName],
|
|
1025
|
+
validate: (result) => result.exitCode === 0
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
command: "where",
|
|
1029
|
+
args: [binaryName],
|
|
1030
|
+
validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
|
|
1031
|
+
},
|
|
1032
|
+
// Check common installation paths using shell expansion for $HOME
|
|
1033
|
+
{
|
|
1034
|
+
command: "sh",
|
|
1035
|
+
args: [
|
|
1036
|
+
"-c",
|
|
1037
|
+
commonPaths.map((p) => `test -f "${p}"`).join(" || ")
|
|
1038
|
+
],
|
|
1039
|
+
validate: (result) => result.exitCode === 0
|
|
1040
|
+
}
|
|
1041
|
+
];
|
|
1042
|
+
for (const detector of detectors) {
|
|
1043
|
+
const result = await runCommand2(detector.command, detector.args);
|
|
1044
|
+
if (detector.validate(result)) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
throw new Error(`${binaryName} CLI binary not found on PATH.`);
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// packages/config-mutations/src/mutations/config-mutation.ts
|
|
1054
|
+
function merge(options) {
|
|
1055
|
+
return {
|
|
1056
|
+
kind: "configMerge",
|
|
1057
|
+
target: options.target,
|
|
1058
|
+
value: options.value,
|
|
1059
|
+
format: options.format,
|
|
1060
|
+
pruneByPrefix: options.pruneByPrefix,
|
|
1061
|
+
label: options.label
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
function prune(options) {
|
|
1065
|
+
return {
|
|
1066
|
+
kind: "configPrune",
|
|
1067
|
+
target: options.target,
|
|
1068
|
+
shape: options.shape,
|
|
1069
|
+
format: options.format,
|
|
1070
|
+
onlyIf: options.onlyIf,
|
|
1071
|
+
label: options.label
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function transform(options) {
|
|
1075
|
+
return {
|
|
1076
|
+
kind: "configTransform",
|
|
1077
|
+
target: options.target,
|
|
1078
|
+
format: options.format,
|
|
1079
|
+
transform: options.transform,
|
|
1080
|
+
label: options.label
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
var configMutation = {
|
|
1084
|
+
merge,
|
|
1085
|
+
prune,
|
|
1086
|
+
transform
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
// packages/config-mutations/src/mutations/file-mutation.ts
|
|
1090
|
+
function ensureDirectory(options) {
|
|
1091
|
+
return {
|
|
1092
|
+
kind: "ensureDirectory",
|
|
1093
|
+
path: options.path,
|
|
1094
|
+
label: options.label
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
function remove(options) {
|
|
1098
|
+
return {
|
|
1099
|
+
kind: "removeFile",
|
|
1100
|
+
target: options.target,
|
|
1101
|
+
whenEmpty: options.whenEmpty,
|
|
1102
|
+
whenContentMatches: options.whenContentMatches,
|
|
1103
|
+
label: options.label
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function removeDirectory(options) {
|
|
1107
|
+
return {
|
|
1108
|
+
kind: "removeDirectory",
|
|
1109
|
+
path: options.path,
|
|
1110
|
+
force: options.force,
|
|
1111
|
+
label: options.label
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
function chmod(options) {
|
|
1115
|
+
return {
|
|
1116
|
+
kind: "chmod",
|
|
1117
|
+
target: options.target,
|
|
1118
|
+
mode: options.mode,
|
|
1119
|
+
label: options.label
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function backup(options) {
|
|
1123
|
+
return {
|
|
1124
|
+
kind: "backup",
|
|
1125
|
+
target: options.target,
|
|
1126
|
+
label: options.label
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
var fileMutation = {
|
|
1130
|
+
ensureDirectory,
|
|
1131
|
+
remove,
|
|
1132
|
+
removeDirectory,
|
|
1133
|
+
chmod,
|
|
1134
|
+
backup
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// packages/config-mutations/src/execution/apply-mutation.ts
|
|
1138
|
+
import Mustache from "mustache";
|
|
1139
|
+
|
|
1140
|
+
// packages/config-mutations/src/formats/json.ts
|
|
1141
|
+
import * as jsonc from "jsonc-parser";
|
|
1142
|
+
function isConfigObject(value) {
|
|
1143
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1144
|
+
}
|
|
1145
|
+
function parse3(content) {
|
|
1146
|
+
if (!content || content.trim() === "") {
|
|
1147
|
+
return {};
|
|
1148
|
+
}
|
|
1149
|
+
const errors = [];
|
|
1150
|
+
const parsed = jsonc.parse(content, errors, {
|
|
1151
|
+
allowTrailingComma: true,
|
|
1152
|
+
disallowComments: false
|
|
1153
|
+
});
|
|
1154
|
+
if (errors.length > 0) {
|
|
1155
|
+
throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
|
|
1156
|
+
}
|
|
1157
|
+
if (parsed === null || parsed === void 0) {
|
|
1158
|
+
return {};
|
|
1159
|
+
}
|
|
1160
|
+
if (!isConfigObject(parsed)) {
|
|
1161
|
+
throw new Error("Expected JSON object.");
|
|
1162
|
+
}
|
|
1163
|
+
return parsed;
|
|
1164
|
+
}
|
|
1165
|
+
function serialize(obj) {
|
|
1166
|
+
return `${JSON.stringify(obj, null, 2)}
|
|
1167
|
+
`;
|
|
1168
|
+
}
|
|
1169
|
+
function merge2(base, patch) {
|
|
1170
|
+
const result = { ...base };
|
|
1171
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1172
|
+
if (value === void 0) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
const existing = result[key];
|
|
1176
|
+
if (isConfigObject(existing) && isConfigObject(value)) {
|
|
1177
|
+
result[key] = merge2(existing, value);
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
result[key] = value;
|
|
1181
|
+
}
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
function prune2(obj, shape) {
|
|
1185
|
+
let changed = false;
|
|
1186
|
+
const result = { ...obj };
|
|
1187
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
1188
|
+
if (!(key in result)) {
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
const current = result[key];
|
|
1192
|
+
if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
|
|
1193
|
+
delete result[key];
|
|
1194
|
+
changed = true;
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
if (isConfigObject(pattern) && isConfigObject(current)) {
|
|
1198
|
+
const { changed: childChanged, result: childResult } = prune2(
|
|
1199
|
+
current,
|
|
1200
|
+
pattern
|
|
1201
|
+
);
|
|
1202
|
+
if (childChanged) {
|
|
1203
|
+
changed = true;
|
|
1204
|
+
}
|
|
1205
|
+
if (Object.keys(childResult).length === 0) {
|
|
1206
|
+
delete result[key];
|
|
1207
|
+
} else {
|
|
1208
|
+
result[key] = childResult;
|
|
1209
|
+
}
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
delete result[key];
|
|
1213
|
+
changed = true;
|
|
1214
|
+
}
|
|
1215
|
+
return { changed, result };
|
|
1216
|
+
}
|
|
1217
|
+
var jsonFormat = {
|
|
1218
|
+
parse: parse3,
|
|
1219
|
+
serialize,
|
|
1220
|
+
merge: merge2,
|
|
1221
|
+
prune: prune2
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
// packages/config-mutations/src/formats/toml.ts
|
|
1225
|
+
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
1226
|
+
function isConfigObject2(value) {
|
|
1227
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1228
|
+
}
|
|
1229
|
+
function parse4(content) {
|
|
1230
|
+
if (!content || content.trim() === "") {
|
|
1231
|
+
return {};
|
|
1232
|
+
}
|
|
1233
|
+
const parsed = parseToml(content);
|
|
1234
|
+
if (!isConfigObject2(parsed)) {
|
|
1235
|
+
throw new Error("Expected TOML document to be a table.");
|
|
1236
|
+
}
|
|
1237
|
+
return parsed;
|
|
1238
|
+
}
|
|
1239
|
+
function serialize2(obj) {
|
|
1240
|
+
const serialized = stringifyToml(obj);
|
|
1241
|
+
return serialized.endsWith("\n") ? serialized : `${serialized}
|
|
1242
|
+
`;
|
|
1243
|
+
}
|
|
1244
|
+
function merge3(base, patch) {
|
|
1245
|
+
const result = { ...base };
|
|
1246
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1247
|
+
if (value === void 0) {
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
const existing = result[key];
|
|
1251
|
+
if (isConfigObject2(existing) && isConfigObject2(value)) {
|
|
1252
|
+
result[key] = merge3(existing, value);
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
result[key] = value;
|
|
1256
|
+
}
|
|
1257
|
+
return result;
|
|
1258
|
+
}
|
|
1259
|
+
function prune3(obj, shape) {
|
|
1260
|
+
let changed = false;
|
|
1261
|
+
const result = { ...obj };
|
|
1262
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
1263
|
+
if (!(key in result)) {
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
const current = result[key];
|
|
1267
|
+
if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
|
|
1268
|
+
delete result[key];
|
|
1269
|
+
changed = true;
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (isConfigObject2(pattern) && isConfigObject2(current)) {
|
|
1273
|
+
const { changed: childChanged, result: childResult } = prune3(
|
|
1274
|
+
current,
|
|
1275
|
+
pattern
|
|
1276
|
+
);
|
|
1277
|
+
if (childChanged) {
|
|
1278
|
+
changed = true;
|
|
1279
|
+
}
|
|
1280
|
+
if (Object.keys(childResult).length === 0) {
|
|
1281
|
+
delete result[key];
|
|
1282
|
+
} else {
|
|
1283
|
+
result[key] = childResult;
|
|
1284
|
+
}
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
delete result[key];
|
|
1288
|
+
changed = true;
|
|
1289
|
+
}
|
|
1290
|
+
return { changed, result };
|
|
1291
|
+
}
|
|
1292
|
+
var tomlFormat = {
|
|
1293
|
+
parse: parse4,
|
|
1294
|
+
serialize: serialize2,
|
|
1295
|
+
merge: merge3,
|
|
1296
|
+
prune: prune3
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
// packages/config-mutations/src/formats/yaml.ts
|
|
1300
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
1301
|
+
function isConfigObject3(value) {
|
|
1302
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1303
|
+
}
|
|
1304
|
+
function parse5(content) {
|
|
1305
|
+
if (!content || content.trim() === "") {
|
|
1306
|
+
return {};
|
|
1307
|
+
}
|
|
1308
|
+
const parsed = parseYaml(content);
|
|
1309
|
+
if (parsed === null || parsed === void 0) {
|
|
1310
|
+
return {};
|
|
1311
|
+
}
|
|
1312
|
+
if (!isConfigObject3(parsed)) {
|
|
1313
|
+
throw new Error("Expected YAML object.");
|
|
1314
|
+
}
|
|
1315
|
+
return parsed;
|
|
1316
|
+
}
|
|
1317
|
+
function serialize3(obj) {
|
|
1318
|
+
const serialized = stringifyYaml(obj);
|
|
1319
|
+
return serialized.endsWith("\n") ? serialized : `${serialized}
|
|
1320
|
+
`;
|
|
1321
|
+
}
|
|
1322
|
+
function merge4(base, patch) {
|
|
1323
|
+
const result = { ...base };
|
|
1324
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1325
|
+
if (value === void 0) {
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const existing = result[key];
|
|
1329
|
+
if (isConfigObject3(existing) && isConfigObject3(value)) {
|
|
1330
|
+
result[key] = merge4(existing, value);
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1333
|
+
result[key] = value;
|
|
1334
|
+
}
|
|
1335
|
+
return result;
|
|
1336
|
+
}
|
|
1337
|
+
function prune4(obj, shape) {
|
|
1338
|
+
let changed = false;
|
|
1339
|
+
const result = { ...obj };
|
|
1340
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
1341
|
+
if (!(key in result)) {
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
const current = result[key];
|
|
1345
|
+
if (isConfigObject3(pattern) && Object.keys(pattern).length === 0) {
|
|
1346
|
+
delete result[key];
|
|
1347
|
+
changed = true;
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (isConfigObject3(pattern) && isConfigObject3(current)) {
|
|
1351
|
+
const { changed: childChanged, result: childResult } = prune4(current, pattern);
|
|
1352
|
+
if (childChanged) {
|
|
1353
|
+
changed = true;
|
|
1354
|
+
}
|
|
1355
|
+
if (Object.keys(childResult).length === 0) {
|
|
1356
|
+
delete result[key];
|
|
1357
|
+
} else {
|
|
1358
|
+
result[key] = childResult;
|
|
1359
|
+
}
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
delete result[key];
|
|
1363
|
+
changed = true;
|
|
1364
|
+
}
|
|
1365
|
+
return { changed, result };
|
|
1366
|
+
}
|
|
1367
|
+
var yamlFormat = {
|
|
1368
|
+
parse: parse5,
|
|
1369
|
+
serialize: serialize3,
|
|
1370
|
+
merge: merge4,
|
|
1371
|
+
prune: prune4
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
// packages/config-mutations/src/formats/index.ts
|
|
1375
|
+
var formatRegistry = {
|
|
1376
|
+
json: jsonFormat,
|
|
1377
|
+
toml: tomlFormat,
|
|
1378
|
+
yaml: yamlFormat
|
|
1379
|
+
};
|
|
1380
|
+
var extensionMap = {
|
|
1381
|
+
".json": "json",
|
|
1382
|
+
".toml": "toml",
|
|
1383
|
+
".yaml": "yaml",
|
|
1384
|
+
".yml": "yaml"
|
|
1385
|
+
};
|
|
1386
|
+
function getConfigFormat(pathOrFormat) {
|
|
1387
|
+
if (pathOrFormat in formatRegistry) {
|
|
1388
|
+
return formatRegistry[pathOrFormat];
|
|
1389
|
+
}
|
|
1390
|
+
const ext = getExtension(pathOrFormat);
|
|
1391
|
+
const formatName = extensionMap[ext];
|
|
1392
|
+
if (!formatName) {
|
|
1393
|
+
throw new Error(
|
|
1394
|
+
`Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
return formatRegistry[formatName];
|
|
1398
|
+
}
|
|
1399
|
+
function detectFormat(path4) {
|
|
1400
|
+
const ext = getExtension(path4);
|
|
1401
|
+
return extensionMap[ext];
|
|
1402
|
+
}
|
|
1403
|
+
function getExtension(path4) {
|
|
1404
|
+
const lastDot = path4.lastIndexOf(".");
|
|
1405
|
+
if (lastDot === -1) {
|
|
1406
|
+
return "";
|
|
1407
|
+
}
|
|
1408
|
+
return path4.slice(lastDot).toLowerCase();
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// packages/config-mutations/src/execution/path-utils.ts
|
|
1412
|
+
import path3 from "node:path";
|
|
1413
|
+
function expandHome(targetPath, homeDir) {
|
|
1414
|
+
if (!targetPath?.startsWith("~")) {
|
|
1415
|
+
return targetPath;
|
|
1416
|
+
}
|
|
1417
|
+
if (targetPath.startsWith("~./")) {
|
|
1418
|
+
targetPath = `~/.${targetPath.slice(3)}`;
|
|
1419
|
+
}
|
|
1420
|
+
let remainder = targetPath.slice(1);
|
|
1421
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
1422
|
+
remainder = remainder.slice(1);
|
|
1423
|
+
} else if (remainder.startsWith(".")) {
|
|
1424
|
+
remainder = remainder.slice(1);
|
|
1425
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
1426
|
+
remainder = remainder.slice(1);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return remainder.length === 0 ? homeDir : path3.join(homeDir, remainder);
|
|
1430
|
+
}
|
|
1431
|
+
function validateHomePath(targetPath) {
|
|
1432
|
+
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
1433
|
+
throw new Error("Target path must be a non-empty string.");
|
|
1434
|
+
}
|
|
1435
|
+
if (!targetPath.startsWith("~")) {
|
|
1436
|
+
throw new Error(
|
|
1437
|
+
`All target paths must be home-relative (start with ~). Received: "${targetPath}"`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
function resolvePath(rawPath, homeDir, pathMapper) {
|
|
1442
|
+
validateHomePath(rawPath);
|
|
1443
|
+
const expanded = expandHome(rawPath, homeDir);
|
|
1444
|
+
if (!pathMapper) {
|
|
1445
|
+
return expanded;
|
|
1446
|
+
}
|
|
1447
|
+
const rawDirectory = path3.dirname(expanded);
|
|
1448
|
+
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
1449
|
+
targetDirectory: rawDirectory
|
|
1450
|
+
});
|
|
1451
|
+
const filename = path3.basename(expanded);
|
|
1452
|
+
return filename.length === 0 ? mappedDirectory : path3.join(mappedDirectory, filename);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// packages/config-mutations/src/fs-utils.ts
|
|
1456
|
+
function isNotFound(error2) {
|
|
1457
|
+
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
1458
|
+
}
|
|
1459
|
+
async function readFileIfExists(fs, target) {
|
|
1460
|
+
try {
|
|
1461
|
+
return await fs.readFile(target, "utf8");
|
|
1462
|
+
} catch (error2) {
|
|
1463
|
+
if (isNotFound(error2)) {
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1466
|
+
throw error2;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
async function pathExists(fs, target) {
|
|
1470
|
+
try {
|
|
1471
|
+
await fs.stat(target);
|
|
1472
|
+
return true;
|
|
1473
|
+
} catch (error2) {
|
|
1474
|
+
if (isNotFound(error2)) {
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
throw error2;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
function createTimestamp() {
|
|
1481
|
+
return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// packages/config-mutations/src/execution/apply-mutation.ts
|
|
1485
|
+
function resolveValue(resolver, options) {
|
|
1486
|
+
if (typeof resolver === "function") {
|
|
1487
|
+
return resolver(options);
|
|
1488
|
+
}
|
|
1489
|
+
return resolver;
|
|
1490
|
+
}
|
|
1491
|
+
function createInvalidDocumentBackupPath(targetPath) {
|
|
1492
|
+
const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
|
|
1493
|
+
return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
|
|
1494
|
+
}
|
|
1495
|
+
async function backupInvalidDocument(fs, targetPath, content) {
|
|
1496
|
+
const backupPath = createInvalidDocumentBackupPath(targetPath);
|
|
1497
|
+
await fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
1498
|
+
}
|
|
1499
|
+
function describeMutation(kind, targetPath) {
|
|
1500
|
+
const displayPath = targetPath ?? "target";
|
|
1501
|
+
switch (kind) {
|
|
1502
|
+
case "ensureDirectory":
|
|
1503
|
+
return `Create ${displayPath}`;
|
|
1504
|
+
case "removeDirectory":
|
|
1505
|
+
return `Remove directory ${displayPath}`;
|
|
1506
|
+
case "backup":
|
|
1507
|
+
return `Backup ${displayPath}`;
|
|
1508
|
+
case "templateWrite":
|
|
1509
|
+
return `Write ${displayPath}`;
|
|
1510
|
+
case "chmod":
|
|
1511
|
+
return `Set permissions on ${displayPath}`;
|
|
1512
|
+
case "removeFile":
|
|
1513
|
+
return `Remove ${displayPath}`;
|
|
1514
|
+
case "configMerge":
|
|
1515
|
+
case "configPrune":
|
|
1516
|
+
case "configTransform":
|
|
1517
|
+
case "templateMergeToml":
|
|
1518
|
+
case "templateMergeJson":
|
|
1519
|
+
return `Update ${displayPath}`;
|
|
1520
|
+
default:
|
|
1521
|
+
return "Operation";
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
function pruneKeysByPrefix(table, prefix) {
|
|
1525
|
+
const result = {};
|
|
1526
|
+
for (const [key, value] of Object.entries(table)) {
|
|
1527
|
+
if (!key.startsWith(prefix)) {
|
|
1528
|
+
result[key] = value;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return result;
|
|
1532
|
+
}
|
|
1533
|
+
function isConfigObject4(value) {
|
|
1534
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1535
|
+
}
|
|
1536
|
+
function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
|
|
1537
|
+
const result = { ...base };
|
|
1538
|
+
const prefixMap = pruneByPrefix ?? {};
|
|
1539
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1540
|
+
const current = result[key];
|
|
1541
|
+
const prefix = prefixMap[key];
|
|
1542
|
+
if (isConfigObject4(current) && isConfigObject4(value)) {
|
|
1543
|
+
if (prefix) {
|
|
1544
|
+
const pruned = pruneKeysByPrefix(current, prefix);
|
|
1545
|
+
result[key] = { ...pruned, ...value };
|
|
1546
|
+
} else {
|
|
1547
|
+
result[key] = mergeWithPruneByPrefix(
|
|
1548
|
+
current,
|
|
1549
|
+
value,
|
|
1550
|
+
prefixMap
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
result[key] = value;
|
|
1556
|
+
}
|
|
1557
|
+
return result;
|
|
1558
|
+
}
|
|
1559
|
+
async function applyMutation(mutation, context, options) {
|
|
1560
|
+
switch (mutation.kind) {
|
|
1561
|
+
case "ensureDirectory":
|
|
1562
|
+
return applyEnsureDirectory(mutation, context, options);
|
|
1563
|
+
case "removeDirectory":
|
|
1564
|
+
return applyRemoveDirectory(mutation, context, options);
|
|
1565
|
+
case "removeFile":
|
|
1566
|
+
return applyRemoveFile(mutation, context, options);
|
|
1567
|
+
case "chmod":
|
|
1568
|
+
return applyChmod(mutation, context, options);
|
|
1569
|
+
case "backup":
|
|
1570
|
+
return applyBackup(mutation, context, options);
|
|
1571
|
+
case "configMerge":
|
|
1572
|
+
return applyConfigMerge(mutation, context, options);
|
|
1573
|
+
case "configPrune":
|
|
1574
|
+
return applyConfigPrune(mutation, context, options);
|
|
1575
|
+
case "configTransform":
|
|
1576
|
+
return applyConfigTransform(mutation, context, options);
|
|
1577
|
+
case "templateWrite":
|
|
1578
|
+
return applyTemplateWrite(mutation, context, options);
|
|
1579
|
+
case "templateMergeToml":
|
|
1580
|
+
return applyTemplateMerge(mutation, context, options, "toml");
|
|
1581
|
+
case "templateMergeJson":
|
|
1582
|
+
return applyTemplateMerge(mutation, context, options, "json");
|
|
1583
|
+
default: {
|
|
1584
|
+
const never = mutation;
|
|
1585
|
+
throw new Error(`Unknown mutation kind: ${never.kind}`);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
async function applyEnsureDirectory(mutation, context, options) {
|
|
1590
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
1591
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1592
|
+
const details = {
|
|
1593
|
+
kind: mutation.kind,
|
|
1594
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1595
|
+
targetPath
|
|
1596
|
+
};
|
|
1597
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1598
|
+
if (!context.dryRun) {
|
|
1599
|
+
await context.fs.mkdir(targetPath, { recursive: true });
|
|
1600
|
+
}
|
|
1601
|
+
return {
|
|
1602
|
+
outcome: {
|
|
1603
|
+
changed: !existed,
|
|
1604
|
+
effect: "mkdir",
|
|
1605
|
+
detail: existed ? "noop" : "create"
|
|
1606
|
+
},
|
|
1607
|
+
details
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
async function applyRemoveDirectory(mutation, context, options) {
|
|
1611
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
1612
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1613
|
+
const details = {
|
|
1614
|
+
kind: mutation.kind,
|
|
1615
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1616
|
+
targetPath
|
|
1617
|
+
};
|
|
1618
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1619
|
+
if (!existed) {
|
|
1620
|
+
return {
|
|
1621
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1622
|
+
details
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
if (typeof context.fs.rm !== "function") {
|
|
1626
|
+
return {
|
|
1627
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1628
|
+
details
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
if (mutation.force) {
|
|
1632
|
+
if (!context.dryRun) {
|
|
1633
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1637
|
+
details
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
const entries = await context.fs.readdir(targetPath);
|
|
1641
|
+
if (entries.length > 0) {
|
|
1642
|
+
return {
|
|
1643
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1644
|
+
details
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
if (!context.dryRun) {
|
|
1648
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
1649
|
+
}
|
|
1650
|
+
return {
|
|
1651
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1652
|
+
details
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
async function applyRemoveFile(mutation, context, options) {
|
|
1656
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1657
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1658
|
+
const details = {
|
|
1659
|
+
kind: mutation.kind,
|
|
1660
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1661
|
+
targetPath
|
|
1662
|
+
};
|
|
1663
|
+
try {
|
|
1664
|
+
const content = await context.fs.readFile(targetPath, "utf8");
|
|
1665
|
+
const trimmed = content.trim();
|
|
1666
|
+
if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
|
|
1667
|
+
return {
|
|
1668
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1669
|
+
details
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
if (mutation.whenEmpty && trimmed.length > 0) {
|
|
1673
|
+
return {
|
|
1674
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1675
|
+
details
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
if (!context.dryRun) {
|
|
1679
|
+
await context.fs.unlink(targetPath);
|
|
1680
|
+
}
|
|
1681
|
+
return {
|
|
1682
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1683
|
+
details
|
|
1684
|
+
};
|
|
1685
|
+
} catch (error2) {
|
|
1686
|
+
if (isNotFound(error2)) {
|
|
1687
|
+
return {
|
|
1688
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1689
|
+
details
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
throw error2;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
async function applyChmod(mutation, context, options) {
|
|
1696
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1697
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1698
|
+
const details = {
|
|
1699
|
+
kind: mutation.kind,
|
|
1700
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1701
|
+
targetPath
|
|
1702
|
+
};
|
|
1703
|
+
if (typeof context.fs.chmod !== "function") {
|
|
1704
|
+
return {
|
|
1705
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1706
|
+
details
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
try {
|
|
1710
|
+
const stat = await context.fs.stat(targetPath);
|
|
1711
|
+
const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
|
|
1712
|
+
if (currentMode === mutation.mode) {
|
|
1713
|
+
return {
|
|
1714
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1715
|
+
details
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
if (!context.dryRun) {
|
|
1719
|
+
await context.fs.chmod(targetPath, mutation.mode);
|
|
1720
|
+
}
|
|
1721
|
+
return {
|
|
1722
|
+
outcome: { changed: true, effect: "chmod", detail: "update" },
|
|
1723
|
+
details
|
|
1724
|
+
};
|
|
1725
|
+
} catch (error2) {
|
|
1726
|
+
if (isNotFound(error2)) {
|
|
1727
|
+
return {
|
|
1728
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1729
|
+
details
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
throw error2;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
async function applyBackup(mutation, context, options) {
|
|
1736
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1737
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1738
|
+
const details = {
|
|
1739
|
+
kind: mutation.kind,
|
|
1740
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1741
|
+
targetPath
|
|
1742
|
+
};
|
|
1743
|
+
const content = await readFileIfExists(context.fs, targetPath);
|
|
1744
|
+
if (content === null) {
|
|
1745
|
+
return {
|
|
1746
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1747
|
+
details
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
if (!context.dryRun) {
|
|
1751
|
+
const backupPath = `${targetPath}.backup-${createTimestamp()}`;
|
|
1752
|
+
await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
1753
|
+
}
|
|
1754
|
+
return {
|
|
1755
|
+
outcome: { changed: true, effect: "copy", detail: "backup" },
|
|
1756
|
+
details
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
async function applyConfigMerge(mutation, context, options) {
|
|
1760
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1761
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1762
|
+
const details = {
|
|
1763
|
+
kind: mutation.kind,
|
|
1764
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1765
|
+
targetPath
|
|
1766
|
+
};
|
|
1767
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1768
|
+
if (!formatName) {
|
|
1769
|
+
throw new Error(
|
|
1770
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
const format = getConfigFormat(formatName);
|
|
1774
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1775
|
+
let current;
|
|
1776
|
+
try {
|
|
1777
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1778
|
+
} catch {
|
|
1779
|
+
if (rawContent !== null) {
|
|
1780
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1781
|
+
}
|
|
1782
|
+
current = {};
|
|
1783
|
+
}
|
|
1784
|
+
const value = resolveValue(mutation.value, options);
|
|
1785
|
+
let merged;
|
|
1786
|
+
if (mutation.pruneByPrefix) {
|
|
1787
|
+
merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
|
|
1788
|
+
} else {
|
|
1789
|
+
merged = format.merge(current, value);
|
|
1790
|
+
}
|
|
1791
|
+
const serialized = format.serialize(merged);
|
|
1792
|
+
const changed = serialized !== rawContent;
|
|
1793
|
+
if (changed && !context.dryRun) {
|
|
1794
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1795
|
+
}
|
|
1796
|
+
return {
|
|
1797
|
+
outcome: {
|
|
1798
|
+
changed,
|
|
1799
|
+
effect: changed ? "write" : "none",
|
|
1800
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
1801
|
+
},
|
|
1802
|
+
details
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
async function applyConfigPrune(mutation, context, options) {
|
|
1806
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1807
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1808
|
+
const details = {
|
|
1809
|
+
kind: mutation.kind,
|
|
1810
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1811
|
+
targetPath
|
|
1812
|
+
};
|
|
1813
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1814
|
+
if (rawContent === null) {
|
|
1815
|
+
return {
|
|
1816
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1817
|
+
details
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1821
|
+
if (!formatName) {
|
|
1822
|
+
throw new Error(
|
|
1823
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
const format = getConfigFormat(formatName);
|
|
1827
|
+
let current;
|
|
1828
|
+
try {
|
|
1829
|
+
current = format.parse(rawContent);
|
|
1830
|
+
} catch {
|
|
1831
|
+
return {
|
|
1832
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1833
|
+
details
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
|
|
1837
|
+
return {
|
|
1838
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1839
|
+
details
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
const shape = resolveValue(mutation.shape, options);
|
|
1843
|
+
const { changed, result } = format.prune(current, shape);
|
|
1844
|
+
if (!changed) {
|
|
1845
|
+
return {
|
|
1846
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1847
|
+
details
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
if (Object.keys(result).length === 0) {
|
|
1851
|
+
if (!context.dryRun) {
|
|
1852
|
+
await context.fs.unlink(targetPath);
|
|
1853
|
+
}
|
|
1854
|
+
return {
|
|
1855
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1856
|
+
details
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
const serialized = format.serialize(result);
|
|
1860
|
+
if (!context.dryRun) {
|
|
1861
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1862
|
+
}
|
|
1863
|
+
return {
|
|
1864
|
+
outcome: { changed: true, effect: "write", detail: "update" },
|
|
1865
|
+
details
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
async function applyConfigTransform(mutation, context, options) {
|
|
1869
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1870
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1871
|
+
const details = {
|
|
1872
|
+
kind: mutation.kind,
|
|
1873
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1874
|
+
targetPath
|
|
1875
|
+
};
|
|
1876
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1877
|
+
if (!formatName) {
|
|
1878
|
+
throw new Error(
|
|
1879
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
const format = getConfigFormat(formatName);
|
|
1883
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1884
|
+
let current;
|
|
1885
|
+
try {
|
|
1886
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1887
|
+
} catch {
|
|
1888
|
+
if (rawContent !== null) {
|
|
1889
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1890
|
+
}
|
|
1891
|
+
current = {};
|
|
1892
|
+
}
|
|
1893
|
+
const { content: transformed, changed } = mutation.transform(current, options);
|
|
1894
|
+
if (!changed) {
|
|
1895
|
+
return {
|
|
1896
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1897
|
+
details
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
if (transformed === null) {
|
|
1901
|
+
if (rawContent === null) {
|
|
1902
|
+
return {
|
|
1903
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1904
|
+
details
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
if (!context.dryRun) {
|
|
1908
|
+
await context.fs.unlink(targetPath);
|
|
1909
|
+
}
|
|
1910
|
+
return {
|
|
1911
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1912
|
+
details
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
const serialized = format.serialize(transformed);
|
|
1916
|
+
if (!context.dryRun) {
|
|
1917
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1918
|
+
}
|
|
1919
|
+
return {
|
|
1920
|
+
outcome: {
|
|
1921
|
+
changed: true,
|
|
1922
|
+
effect: "write",
|
|
1923
|
+
detail: rawContent === null ? "create" : "update"
|
|
1924
|
+
},
|
|
1925
|
+
details
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
async function applyTemplateWrite(mutation, context, options) {
|
|
1929
|
+
if (!context.templates) {
|
|
1930
|
+
throw new Error(
|
|
1931
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1935
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1936
|
+
const details = {
|
|
1937
|
+
kind: mutation.kind,
|
|
1938
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1939
|
+
targetPath
|
|
1940
|
+
};
|
|
1941
|
+
const template = await context.templates(mutation.templateId);
|
|
1942
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
1943
|
+
const rendered = Mustache.render(template, templateContext);
|
|
1944
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1945
|
+
if (!context.dryRun) {
|
|
1946
|
+
await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
|
|
1947
|
+
}
|
|
1948
|
+
return {
|
|
1949
|
+
outcome: {
|
|
1950
|
+
changed: true,
|
|
1951
|
+
effect: "write",
|
|
1952
|
+
detail: existed ? "update" : "create"
|
|
1953
|
+
},
|
|
1954
|
+
details
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
async function applyTemplateMerge(mutation, context, options, formatName) {
|
|
1958
|
+
if (!context.templates) {
|
|
1959
|
+
throw new Error(
|
|
1960
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1964
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1965
|
+
const details = {
|
|
1966
|
+
kind: mutation.kind,
|
|
1967
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1968
|
+
targetPath
|
|
1969
|
+
};
|
|
1970
|
+
const format = getConfigFormat(formatName);
|
|
1971
|
+
const template = await context.templates(mutation.templateId);
|
|
1972
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
1973
|
+
const rendered = Mustache.render(template, templateContext);
|
|
1974
|
+
let templateDoc;
|
|
1975
|
+
try {
|
|
1976
|
+
templateDoc = format.parse(rendered);
|
|
1977
|
+
} catch (error2) {
|
|
1978
|
+
throw new Error(
|
|
1979
|
+
`Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error2}`,
|
|
1980
|
+
{ cause: error2 }
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1984
|
+
let current;
|
|
1985
|
+
try {
|
|
1986
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1987
|
+
} catch {
|
|
1988
|
+
if (rawContent !== null) {
|
|
1989
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1990
|
+
}
|
|
1991
|
+
current = {};
|
|
1992
|
+
}
|
|
1993
|
+
const merged = format.merge(current, templateDoc);
|
|
1994
|
+
const serialized = format.serialize(merged);
|
|
1995
|
+
const changed = serialized !== rawContent;
|
|
1996
|
+
if (changed && !context.dryRun) {
|
|
1997
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1998
|
+
}
|
|
1999
|
+
return {
|
|
2000
|
+
outcome: {
|
|
2001
|
+
changed,
|
|
2002
|
+
effect: changed ? "write" : "none",
|
|
2003
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
2004
|
+
},
|
|
2005
|
+
details
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// packages/config-mutations/src/execution/run-mutations.ts
|
|
2010
|
+
async function runMutations(mutations, context, options) {
|
|
2011
|
+
const effects = [];
|
|
2012
|
+
let anyChanged = false;
|
|
2013
|
+
const resolverOptions = options ?? {};
|
|
2014
|
+
for (const mutation of mutations) {
|
|
2015
|
+
const { outcome } = await executeMutation(
|
|
2016
|
+
mutation,
|
|
2017
|
+
context,
|
|
2018
|
+
resolverOptions
|
|
2019
|
+
);
|
|
2020
|
+
effects.push(outcome);
|
|
2021
|
+
if (outcome.changed) {
|
|
2022
|
+
anyChanged = true;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
return {
|
|
2026
|
+
changed: anyChanged,
|
|
2027
|
+
effects
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
async function executeMutation(mutation, context, options) {
|
|
2031
|
+
context.observers?.onStart?.({
|
|
2032
|
+
kind: mutation.kind,
|
|
2033
|
+
label: mutation.label ?? mutation.kind,
|
|
2034
|
+
targetPath: void 0
|
|
2035
|
+
// Will be resolved during apply
|
|
2036
|
+
});
|
|
2037
|
+
try {
|
|
2038
|
+
const { outcome, details } = await applyMutation(mutation, context, options);
|
|
2039
|
+
context.observers?.onComplete?.(details, outcome);
|
|
2040
|
+
return { outcome, details };
|
|
2041
|
+
} catch (error2) {
|
|
2042
|
+
context.observers?.onError?.(
|
|
2043
|
+
{
|
|
2044
|
+
kind: mutation.kind,
|
|
2045
|
+
label: mutation.label ?? mutation.kind,
|
|
2046
|
+
targetPath: void 0
|
|
2047
|
+
},
|
|
2048
|
+
error2
|
|
2049
|
+
);
|
|
2050
|
+
throw error2;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// packages/config-mutations/src/template/render.ts
|
|
2055
|
+
import Mustache2 from "mustache";
|
|
2056
|
+
var originalEscape = Mustache2.escape;
|
|
2057
|
+
|
|
2058
|
+
// src/services/service-install.ts
|
|
2059
|
+
async function runServiceInstall(definition, context) {
|
|
2060
|
+
const checkContext = {
|
|
2061
|
+
isDryRun: context.isDryRun,
|
|
2062
|
+
runCommand: context.runCommand
|
|
2063
|
+
};
|
|
2064
|
+
let needsInstall = false;
|
|
2065
|
+
try {
|
|
2066
|
+
await definition.check.run(checkContext);
|
|
2067
|
+
context.logger(`${definition.summary} already installed.`);
|
|
2068
|
+
} catch (error2) {
|
|
2069
|
+
const detail = error2 instanceof Error ? error2.message : String(error2);
|
|
2070
|
+
context.logger(`${definition.summary} not detected: ${detail}`);
|
|
2071
|
+
needsInstall = true;
|
|
2072
|
+
}
|
|
2073
|
+
if (!needsInstall) {
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
if (context.isDryRun) {
|
|
2077
|
+
logInstallDryRun(definition, context);
|
|
2078
|
+
return true;
|
|
2079
|
+
}
|
|
2080
|
+
const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
|
|
2081
|
+
for (const step of platformSteps) {
|
|
2082
|
+
await runInstallStep(step, context);
|
|
2083
|
+
}
|
|
2084
|
+
await definition.check.run(checkContext);
|
|
2085
|
+
if (definition.postChecks) {
|
|
2086
|
+
for (const postCheck of definition.postChecks) {
|
|
2087
|
+
await postCheck.run(checkContext);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
context.logger(
|
|
2091
|
+
definition.successMessage ?? `${definition.summary} installed.`
|
|
2092
|
+
);
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
function describeInstallCommand(step) {
|
|
2096
|
+
return `[${step.id}] ${formatCommand2(step.command, step.args)}`;
|
|
2097
|
+
}
|
|
2098
|
+
function formatCommand2(command, args) {
|
|
2099
|
+
return [command, ...args.map(quoteIfNeeded2)].join(" ");
|
|
2100
|
+
}
|
|
2101
|
+
function quoteIfNeeded2(value) {
|
|
2102
|
+
if (value.length === 0) {
|
|
2103
|
+
return '""';
|
|
2104
|
+
}
|
|
2105
|
+
if (value.includes(" ") || value.includes(" ") || value.includes("\n")) {
|
|
2106
|
+
return `"${value.replaceAll('"', '\\"')}"`;
|
|
2107
|
+
}
|
|
2108
|
+
return value;
|
|
2109
|
+
}
|
|
2110
|
+
function filterStepsByPlatform(steps, platform) {
|
|
2111
|
+
return steps.filter(
|
|
2112
|
+
(step) => !step.platforms || step.platforms.includes(platform)
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
function logInstallDryRun(definition, context) {
|
|
2116
|
+
context.logger(`Dry run: would install ${definition.summary}.`);
|
|
2117
|
+
const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
|
|
2118
|
+
for (const step of platformSteps) {
|
|
2119
|
+
context.logger(`Dry run: ${describeInstallCommand(step)}`);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
async function runInstallStep(step, context) {
|
|
2123
|
+
context.logger(`Running ${describeInstallCommand(step)}`);
|
|
2124
|
+
const result = await context.runCommand(step.command, step.args);
|
|
2125
|
+
if (result.exitCode !== 0) {
|
|
2126
|
+
const stderr = result.stderr.trim();
|
|
2127
|
+
const suffix = stderr.length > 0 ? `: ${stderr}` : "";
|
|
2128
|
+
throw new Error(
|
|
2129
|
+
`${describeInstallCommand(step)} failed with exit code ${result.exitCode}${suffix}`
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// src/providers/create-provider.ts
|
|
2135
|
+
var templateImports = {
|
|
2136
|
+
"py-poe-spawn/env.hbs": () => Promise.resolve().then(() => __toESM(require_env(), 1)),
|
|
2137
|
+
"py-poe-spawn/main.py.hbs": () => Promise.resolve().then(() => __toESM(require_main_py(), 1)),
|
|
2138
|
+
"py-poe-spawn/requirements.txt.hbs": () => Promise.resolve().then(() => __toESM(require_requirements_txt(), 1)),
|
|
2139
|
+
"codex/config.toml.hbs": () => Promise.resolve().then(() => __toESM(require_config_toml(), 1))
|
|
2140
|
+
};
|
|
2141
|
+
async function loadTemplate(templateId) {
|
|
2142
|
+
const loader = templateImports[templateId];
|
|
2143
|
+
if (!loader) {
|
|
2144
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
2145
|
+
}
|
|
2146
|
+
const module = await loader();
|
|
2147
|
+
return module.default;
|
|
2148
|
+
}
|
|
2149
|
+
function createProvider(opts) {
|
|
2150
|
+
const provider2 = {
|
|
2151
|
+
id: opts.id,
|
|
2152
|
+
summary: opts.summary,
|
|
2153
|
+
name: opts.name,
|
|
2154
|
+
aliases: opts.aliases,
|
|
2155
|
+
label: opts.label,
|
|
2156
|
+
branding: opts.branding,
|
|
2157
|
+
disabled: opts.disabled,
|
|
2158
|
+
supportsStdinPrompt: opts.supportsStdinPrompt,
|
|
2159
|
+
supportsMcpSpawn: opts.supportsMcpSpawn,
|
|
2160
|
+
configurePrompts: opts.configurePrompts,
|
|
2161
|
+
postConfigureMessages: opts.postConfigureMessages,
|
|
2162
|
+
extendConfigurePayload: opts.extendConfigurePayload,
|
|
2163
|
+
isolatedEnv: opts.isolatedEnv,
|
|
2164
|
+
async configure(context, runOptions) {
|
|
2165
|
+
await runMutations(opts.manifest.configure, {
|
|
2166
|
+
fs: context.fs,
|
|
2167
|
+
homeDir: context.env.homeDir,
|
|
2168
|
+
observers: runOptions?.observers,
|
|
2169
|
+
templates: loadTemplate,
|
|
2170
|
+
pathMapper: context.pathMapper
|
|
2171
|
+
}, context.options);
|
|
2172
|
+
context.command.flushDryRun({ emitIfEmpty: false });
|
|
2173
|
+
},
|
|
2174
|
+
async unconfigure(context, runOptions) {
|
|
2175
|
+
if (!opts.manifest.unconfigure) {
|
|
2176
|
+
return false;
|
|
2177
|
+
}
|
|
2178
|
+
const result = await runMutations(opts.manifest.unconfigure, {
|
|
2179
|
+
fs: context.fs,
|
|
2180
|
+
homeDir: context.env.homeDir,
|
|
2181
|
+
observers: runOptions?.observers,
|
|
2182
|
+
templates: loadTemplate,
|
|
2183
|
+
pathMapper: context.pathMapper
|
|
2184
|
+
}, context.options);
|
|
2185
|
+
context.command.flushDryRun({ emitIfEmpty: false });
|
|
2186
|
+
return result.changed;
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
if (opts.install) {
|
|
2190
|
+
provider2.install = createInstallRunner(opts.install);
|
|
2191
|
+
}
|
|
2192
|
+
if (opts.test) {
|
|
2193
|
+
provider2.test = opts.test;
|
|
2194
|
+
}
|
|
2195
|
+
if (opts.spawn) {
|
|
2196
|
+
provider2.spawn = opts.spawn;
|
|
2197
|
+
}
|
|
2198
|
+
return provider2;
|
|
2199
|
+
}
|
|
2200
|
+
function createInstallRunner(definition) {
|
|
2201
|
+
return async (context) => {
|
|
2202
|
+
await runServiceInstall(definition, {
|
|
2203
|
+
isDryRun: context.logger.context.dryRun,
|
|
2204
|
+
runCommand: context.command.runCommand,
|
|
2205
|
+
logger: (message2) => context.logger.verbose(message2),
|
|
2206
|
+
platform: context.env.platform
|
|
2207
|
+
});
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// src/cli/constants.ts
|
|
2212
|
+
var FRONTIER_MODELS = [
|
|
2213
|
+
"anthropic/claude-opus-4.6",
|
|
2214
|
+
"anthropic/claude-sonnet-4.6",
|
|
2215
|
+
"openai/gpt-5.3-codex",
|
|
2216
|
+
"openai/gpt-5.4",
|
|
2217
|
+
"google/gemini-3.1-pro"
|
|
2218
|
+
];
|
|
2219
|
+
var DEFAULT_FRONTIER_MODEL = "anthropic/claude-sonnet-4.6";
|
|
2220
|
+
var CLAUDE_CODE_VARIANTS = {
|
|
2221
|
+
haiku: "anthropic/claude-haiku-4.5",
|
|
2222
|
+
sonnet: "anthropic/claude-sonnet-4.6",
|
|
2223
|
+
opus: "anthropic/claude-opus-4.6"
|
|
2224
|
+
};
|
|
2225
|
+
var DEFAULT_CLAUDE_CODE_MODEL = CLAUDE_CODE_VARIANTS.sonnet;
|
|
2226
|
+
function stripModelNamespace2(model) {
|
|
2227
|
+
const slashIndex = model.indexOf("/");
|
|
2228
|
+
const id = slashIndex === -1 ? model : model.slice(slashIndex + 1);
|
|
2229
|
+
return id.toLowerCase();
|
|
2230
|
+
}
|
|
2231
|
+
var CODEX_MODELS = [
|
|
2232
|
+
"openai/gpt-5.4",
|
|
2233
|
+
"openai/gpt-5.3-codex",
|
|
2234
|
+
"openai/gpt-5.3-codex-spark",
|
|
2235
|
+
"openai/gpt-5.2-codex",
|
|
2236
|
+
"openai/gpt-5.2",
|
|
2237
|
+
"openai/gpt-5.2-chat",
|
|
2238
|
+
"openai/gpt-5.2-pro",
|
|
2239
|
+
"openai/gpt-5.1",
|
|
2240
|
+
"openai/gpt-5.1-codex-mini",
|
|
2241
|
+
"anthropic/claude-opus-4.6"
|
|
2242
|
+
];
|
|
2243
|
+
var DEFAULT_CODEX_MODEL = CODEX_MODELS[0];
|
|
2244
|
+
var KIMI_MODELS = [
|
|
2245
|
+
"novitaai/kimi-k2.5",
|
|
2246
|
+
"novitaai/kimi-k2-thinking",
|
|
2247
|
+
"novitaai/kimi-k2.5-fw"
|
|
2248
|
+
];
|
|
2249
|
+
var DEFAULT_KIMI_MODEL = KIMI_MODELS[0];
|
|
2250
|
+
var GOOSE_MODELS = FRONTIER_MODELS;
|
|
2251
|
+
var DEFAULT_GOOSE_MODEL = DEFAULT_FRONTIER_MODEL;
|
|
2252
|
+
|
|
2253
|
+
// src/providers/goose.ts
|
|
2254
|
+
var CUSTOM_PROVIDER_ID = "custom_poe";
|
|
2255
|
+
var CUSTOM_PROVIDER_FILE = "~/.config/goose/custom_providers/custom_poe.json";
|
|
2256
|
+
var GOOSE_CONFIG_FILE = "~/.config/goose/config.yaml";
|
|
2257
|
+
var GOOSE_SECRETS_FILE = "~/.config/goose/secrets.yaml";
|
|
2258
|
+
var CUSTOM_PROVIDER_API_KEY_ENV = "CUSTOM_POE_API_KEY";
|
|
2259
|
+
var HEALTH_CHECK_PROMPT = "Reply with exactly: GOOSE_OK";
|
|
2260
|
+
var GOOSE_INSTALL_DEFINITION = {
|
|
2261
|
+
id: "goose",
|
|
2262
|
+
summary: "Goose CLI",
|
|
2263
|
+
check: createBinaryExistsCheck(
|
|
2264
|
+
"goose",
|
|
2265
|
+
"goose-cli-binary",
|
|
2266
|
+
"Goose CLI binary must exist"
|
|
2267
|
+
),
|
|
2268
|
+
steps: [
|
|
2269
|
+
{
|
|
2270
|
+
id: "install-goose-cli-homebrew-or-script",
|
|
2271
|
+
command: "sh",
|
|
2272
|
+
args: [
|
|
2273
|
+
"-c",
|
|
2274
|
+
[
|
|
2275
|
+
"if command -v brew >/dev/null 2>&1; then",
|
|
2276
|
+
" brew install block-goose-cli || curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash;",
|
|
2277
|
+
"else",
|
|
2278
|
+
" curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash;",
|
|
2279
|
+
"fi"
|
|
2280
|
+
].join(" ")
|
|
2281
|
+
],
|
|
2282
|
+
platforms: ["darwin"]
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
id: "install-goose-cli-script-unix",
|
|
2286
|
+
command: "sh",
|
|
2287
|
+
args: [
|
|
2288
|
+
"-c",
|
|
2289
|
+
"curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash"
|
|
2290
|
+
],
|
|
2291
|
+
platforms: ["linux"]
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
id: "install-goose-cli-script-windows",
|
|
2295
|
+
command: "powershell",
|
|
2296
|
+
args: [
|
|
2297
|
+
"-Command",
|
|
2298
|
+
"irm https://github.com/block/goose/releases/download/stable/download_cli.ps1 | iex"
|
|
2299
|
+
],
|
|
2300
|
+
platforms: ["win32"]
|
|
2301
|
+
}
|
|
2302
|
+
],
|
|
2303
|
+
successMessage: "Installed Goose CLI."
|
|
2304
|
+
};
|
|
2305
|
+
function buildCustomProvider(baseUrl, modelContextLimits = {}) {
|
|
2306
|
+
return {
|
|
2307
|
+
name: CUSTOM_PROVIDER_ID,
|
|
2308
|
+
engine: "openai",
|
|
2309
|
+
display_name: "Poe",
|
|
2310
|
+
description: "Poe OpenAI-compatible API",
|
|
2311
|
+
api_key_env: CUSTOM_PROVIDER_API_KEY_ENV,
|
|
2312
|
+
base_url: `${baseUrl}/chat/completions`,
|
|
2313
|
+
models: GOOSE_MODELS.map((name) => ({
|
|
2314
|
+
name,
|
|
2315
|
+
context_limit: resolveGooseModelContextLimit(name, modelContextLimits)
|
|
2316
|
+
})),
|
|
2317
|
+
supports_streaming: true,
|
|
2318
|
+
requires_auth: true
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
function resolveGooseModelContextLimit(model, modelContextLimits) {
|
|
2322
|
+
const stripped = stripModelNamespace2(model);
|
|
2323
|
+
const dynamicValue = modelContextLimits[model] ?? modelContextLimits[stripped];
|
|
2324
|
+
if (typeof dynamicValue === "number" && Number.isFinite(dynamicValue) && dynamicValue > 0) {
|
|
2325
|
+
return dynamicValue;
|
|
2326
|
+
}
|
|
2327
|
+
throw new Error(`Missing Goose model context limit for ${model}.`);
|
|
2328
|
+
}
|
|
2329
|
+
function extractGooseModelContextLimits(response) {
|
|
2330
|
+
const entries = Array.isArray(response.data) ? response.data : [];
|
|
2331
|
+
const wanted = new Map(GOOSE_MODELS.map((model) => [stripModelNamespace2(model), model]));
|
|
2332
|
+
const limits = {};
|
|
2333
|
+
for (const entry of entries) {
|
|
2334
|
+
if (!entry || typeof entry !== "object") {
|
|
2335
|
+
continue;
|
|
2336
|
+
}
|
|
2337
|
+
const rawId = entry.id;
|
|
2338
|
+
const resolvedModel = typeof rawId === "string" ? wanted.get(rawId) : void 0;
|
|
2339
|
+
if (!resolvedModel) {
|
|
2340
|
+
continue;
|
|
2341
|
+
}
|
|
2342
|
+
const rawContextLength = entry.context_window?.context_length;
|
|
2343
|
+
if (typeof rawContextLength !== "number" || !Number.isFinite(rawContextLength) || rawContextLength <= 0) {
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
limits[resolvedModel] = rawContextLength;
|
|
2347
|
+
}
|
|
2348
|
+
return limits;
|
|
2349
|
+
}
|
|
2350
|
+
async function fetchGooseModelContextLimits(input) {
|
|
2351
|
+
const response = await input.httpClient(`${input.apiBaseUrl}/models`, {
|
|
2352
|
+
headers: {
|
|
2353
|
+
Authorization: `Bearer ${input.apiKey}`
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
if (!response.ok) {
|
|
2357
|
+
throw new Error(
|
|
2358
|
+
`Failed to fetch Poe model catalog for Goose configuration (status ${response.status}).`
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
const modelContextLimits = extractGooseModelContextLimits(
|
|
2362
|
+
await response.json()
|
|
2363
|
+
);
|
|
2364
|
+
const missing = GOOSE_MODELS.filter((model) => modelContextLimits[model] == null);
|
|
2365
|
+
if (missing.length > 0) {
|
|
2366
|
+
throw new Error(
|
|
2367
|
+
`Missing Goose model context limit for ${missing[0]}.`
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
return modelContextLimits;
|
|
2371
|
+
}
|
|
2372
|
+
var GOOSE_MODE_ENV = {
|
|
2373
|
+
yolo: "auto",
|
|
2374
|
+
edit: "smart_approve",
|
|
2375
|
+
read: "chat"
|
|
2376
|
+
};
|
|
2377
|
+
function buildRunOptions(options, model) {
|
|
2378
|
+
const baseArgs = [
|
|
2379
|
+
"run",
|
|
2380
|
+
"--provider",
|
|
2381
|
+
CUSTOM_PROVIDER_ID,
|
|
2382
|
+
"--model",
|
|
2383
|
+
model,
|
|
2384
|
+
"--output-format",
|
|
2385
|
+
"text"
|
|
2386
|
+
];
|
|
2387
|
+
const mcpArgs = options.mcpServers ? serializeGooseMcpArgs(options.mcpServers) : [];
|
|
2388
|
+
const modeEnv = options.mode && GOOSE_MODE_ENV[options.mode] ? { GOOSE_MODE: GOOSE_MODE_ENV[options.mode] } : void 0;
|
|
2389
|
+
if (options.useStdin) {
|
|
2390
|
+
return {
|
|
2391
|
+
args: [...baseArgs, ...mcpArgs, "--instructions", "-", ...options.args ?? []],
|
|
2392
|
+
commandOptions: {
|
|
2393
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
2394
|
+
...modeEnv ? { env: modeEnv } : {},
|
|
2395
|
+
stdin: options.prompt
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
return {
|
|
2400
|
+
args: [...baseArgs, ...mcpArgs, "--text", options.prompt, ...options.args ?? []],
|
|
2401
|
+
commandOptions: options.cwd || modeEnv ? { ...options.cwd ? { cwd: options.cwd } : {}, ...modeEnv ? { env: modeEnv } : {} } : void 0
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
var gooseService = createProvider({
|
|
2405
|
+
...gooseAgent,
|
|
2406
|
+
supportsStdinPrompt: true,
|
|
2407
|
+
supportsMcpSpawn: true,
|
|
2408
|
+
configurePrompts: {
|
|
2409
|
+
model: {
|
|
2410
|
+
label: "Goose default model",
|
|
2411
|
+
defaultValue: DEFAULT_GOOSE_MODEL,
|
|
2412
|
+
choices: GOOSE_MODELS.map((id) => ({
|
|
2413
|
+
title: id,
|
|
2414
|
+
value: id
|
|
2415
|
+
}))
|
|
2416
|
+
}
|
|
2417
|
+
},
|
|
2418
|
+
async extendConfigurePayload(context) {
|
|
2419
|
+
const { apiKey } = context.payload;
|
|
2420
|
+
const modelContextLimits = await fetchGooseModelContextLimits({
|
|
2421
|
+
apiBaseUrl: context.env.poeApiBaseUrl,
|
|
2422
|
+
apiKey,
|
|
2423
|
+
httpClient: context.httpClient
|
|
2424
|
+
});
|
|
2425
|
+
return {
|
|
2426
|
+
modelContextLimits
|
|
2427
|
+
};
|
|
2428
|
+
},
|
|
2429
|
+
isolatedEnv: {
|
|
2430
|
+
agentBinary: gooseAgent.binaryName,
|
|
2431
|
+
configProbe: { kind: "isolatedFile", relativePath: ".config/goose/config.yaml" },
|
|
2432
|
+
env: {
|
|
2433
|
+
HOME: { kind: "isolatedDir", relativePath: "" },
|
|
2434
|
+
XDG_CONFIG_HOME: { kind: "isolatedDir", relativePath: ".config" }
|
|
2435
|
+
}
|
|
2436
|
+
},
|
|
2437
|
+
manifest: {
|
|
2438
|
+
configure: [
|
|
2439
|
+
fileMutation.ensureDirectory({ path: "~/.config/goose/custom_providers" }),
|
|
2440
|
+
configMutation.merge({
|
|
2441
|
+
target: CUSTOM_PROVIDER_FILE,
|
|
2442
|
+
value: (ctx) => {
|
|
2443
|
+
const { env, modelContextLimits } = ctx ?? {};
|
|
2444
|
+
return buildCustomProvider(env.poeApiBaseUrl, modelContextLimits);
|
|
2445
|
+
}
|
|
2446
|
+
}),
|
|
2447
|
+
configMutation.merge({
|
|
2448
|
+
target: GOOSE_CONFIG_FILE,
|
|
2449
|
+
format: "yaml",
|
|
2450
|
+
value: (ctx) => {
|
|
2451
|
+
const { model } = ctx ?? {};
|
|
2452
|
+
return {
|
|
2453
|
+
GOOSE_PROVIDER: CUSTOM_PROVIDER_ID,
|
|
2454
|
+
GOOSE_MODEL: model ?? DEFAULT_GOOSE_MODEL,
|
|
2455
|
+
GOOSE_DISABLE_KEYRING: true
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
}),
|
|
2459
|
+
configMutation.merge({
|
|
2460
|
+
target: GOOSE_SECRETS_FILE,
|
|
2461
|
+
format: "yaml",
|
|
2462
|
+
value: (ctx) => {
|
|
2463
|
+
const { apiKey } = ctx ?? {};
|
|
2464
|
+
return {
|
|
2465
|
+
[CUSTOM_PROVIDER_API_KEY_ENV]: apiKey
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
})
|
|
2469
|
+
],
|
|
2470
|
+
unconfigure: [
|
|
2471
|
+
fileMutation.remove({ target: CUSTOM_PROVIDER_FILE }),
|
|
2472
|
+
configMutation.prune({
|
|
2473
|
+
target: GOOSE_CONFIG_FILE,
|
|
2474
|
+
format: "yaml",
|
|
2475
|
+
onlyIf: (document) => document.GOOSE_PROVIDER === CUSTOM_PROVIDER_ID,
|
|
2476
|
+
shape: {
|
|
2477
|
+
GOOSE_PROVIDER: true,
|
|
2478
|
+
GOOSE_MODEL: true,
|
|
2479
|
+
GOOSE_DISABLE_KEYRING: true
|
|
2480
|
+
}
|
|
2481
|
+
}),
|
|
2482
|
+
configMutation.prune({
|
|
2483
|
+
target: GOOSE_SECRETS_FILE,
|
|
2484
|
+
format: "yaml",
|
|
2485
|
+
shape: {
|
|
2486
|
+
[CUSTOM_PROVIDER_API_KEY_ENV]: true
|
|
2487
|
+
}
|
|
2488
|
+
})
|
|
2489
|
+
]
|
|
2490
|
+
},
|
|
2491
|
+
install: GOOSE_INSTALL_DEFINITION,
|
|
2492
|
+
test(context) {
|
|
2493
|
+
return context.runCheck(
|
|
2494
|
+
createCommandExpectationCheck({
|
|
2495
|
+
id: "goose-cli-health",
|
|
2496
|
+
command: "goose",
|
|
2497
|
+
args: ["run", "--text", HEALTH_CHECK_PROMPT, "--output-format", "text"],
|
|
2498
|
+
expectedOutput: "GOOSE_OK"
|
|
2499
|
+
})
|
|
2500
|
+
);
|
|
2501
|
+
},
|
|
2502
|
+
spawn(context, options) {
|
|
2503
|
+
const model = options.model ?? DEFAULT_GOOSE_MODEL;
|
|
2504
|
+
const { args, commandOptions } = buildRunOptions(options, model);
|
|
2505
|
+
return commandOptions ? context.command.runCommand("goose", args, commandOptions) : context.command.runCommand("goose", args);
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
var provider = gooseService;
|
|
2509
|
+
export {
|
|
2510
|
+
GOOSE_INSTALL_DEFINITION,
|
|
2511
|
+
gooseService,
|
|
2512
|
+
provider
|
|
2513
|
+
};
|
|
2514
|
+
//# sourceMappingURL=goose.js.map
|