poe-code 3.0.201 → 3.0.203
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/cli/commands/experiment.js +11 -4
- package/dist/cli/commands/experiment.js.map +1 -1
- package/dist/cli/commands/ralph.js +12 -5
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/runtime-options.d.ts +10 -0
- package/dist/cli/commands/runtime-options.js +23 -0
- package/dist/cli/commands/runtime-options.js.map +1 -0
- package/dist/cli/commands/spawn.js +9 -3
- package/dist/cli/commands/spawn.js.map +1 -1
- package/dist/index.js +22975 -20513
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +2741 -1706
- package/dist/providers/claude-code.js.map +4 -4
- package/dist/providers/codex.js +2770 -1735
- package/dist/providers/codex.js.map +4 -4
- package/dist/providers/goose.js +2640 -1605
- package/dist/providers/goose.js.map +4 -4
- package/dist/providers/kimi.js +2740 -1705
- package/dist/providers/kimi.js.map +4 -4
- package/dist/providers/opencode.js +2741 -1706
- package/dist/providers/opencode.js.map +4 -4
- package/dist/providers/poe-agent.js +19779 -17296
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/providers/spawn-options.d.ts +6 -0
- package/dist/sdk/experiment.js +5 -0
- package/dist/sdk/experiment.js.map +1 -1
- package/dist/sdk/ralph.js +5 -0
- package/dist/sdk/ralph.js.map +1 -1
- package/dist/sdk/spawn.js +17 -1
- package/dist/sdk/spawn.js.map +1 -1
- package/dist/sdk/types.d.ts +11 -0
- package/package.json +1 -1
- package/packages/memory/dist/index.js +2642 -444
- package/packages/memory/dist/index.js.map +4 -4
- package/packages/superintendent/dist/commands/run.d.ts +35 -0
- package/packages/superintendent/dist/commands/run.js +49 -1
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +30 -0
- package/packages/superintendent/dist/runtime/agent-runner.d.ts +30 -0
- package/packages/superintendent/dist/runtime/agent-runner.js +119 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +6 -1
- package/packages/superintendent/dist/runtime/loop.js +3 -11
- package/packages/superintendent/dist/runtime/run-builder.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-builder.js +3 -25
- package/packages/superintendent/dist/runtime/run-inspector.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-inspector.js +3 -25
- package/packages/superintendent/dist/runtime/run-owner-review.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-owner-review.js +3 -25
- package/packages/superintendent/dist/runtime/run-superintendent.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-superintendent.js +3 -25
package/dist/providers/goose.js
CHANGED
|
@@ -52,8 +52,11 @@ var require_config_toml = __commonJS({
|
|
|
52
52
|
}
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
// packages/agent-spawn/src/
|
|
56
|
-
import { spawn } from "node:child_process";
|
|
55
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
56
|
+
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
57
|
+
|
|
58
|
+
// packages/agent-harness-tools/src/paths.ts
|
|
59
|
+
import path from "node:path";
|
|
57
60
|
|
|
58
61
|
// packages/agent-defs/src/agents/claude-code.ts
|
|
59
62
|
var claudeCodeAgent = {
|
|
@@ -188,1833 +191,2865 @@ for (const agent of allAgents) {
|
|
|
188
191
|
}
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
// packages/agent-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
// packages/agent-harness-tools/src/select-agent.ts
|
|
195
|
+
var loopAgents = allAgents.filter(
|
|
196
|
+
(agent) => agent.binaryName !== void 0 || agent.id === "poe-agent"
|
|
197
|
+
);
|
|
198
|
+
var supportedAgents = loopAgents.map((agent) => agent.id).join(", ");
|
|
199
|
+
|
|
200
|
+
// packages/file-lock/src/lock.ts
|
|
201
|
+
import * as fsPromises from "node:fs/promises";
|
|
202
|
+
import * as os from "node:os";
|
|
203
|
+
|
|
204
|
+
// packages/agent-harness-tools/src/run-logs.ts
|
|
205
|
+
import path2 from "node:path";
|
|
206
|
+
|
|
207
|
+
// packages/agent-harness-tools/src/log-stream.ts
|
|
208
|
+
import nodeFs from "node:fs";
|
|
209
|
+
|
|
210
|
+
// packages/agent-harness-tools/src/run-poe-command.ts
|
|
211
|
+
import { randomBytes } from "node:crypto";
|
|
212
|
+
|
|
213
|
+
// packages/agent-harness-tools/src/poe-command-execution.ts
|
|
214
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
215
|
+
import os3 from "node:os";
|
|
216
|
+
|
|
217
|
+
// packages/poe-code-config/src/runtime.ts
|
|
218
|
+
import { existsSync } from "node:fs";
|
|
219
|
+
import path3 from "node:path";
|
|
220
|
+
var defaultWorkspaceExclude = [
|
|
221
|
+
".git",
|
|
222
|
+
"node_modules",
|
|
223
|
+
"dist",
|
|
224
|
+
".turbo",
|
|
225
|
+
".next",
|
|
226
|
+
".poe-code/state.json"
|
|
227
|
+
];
|
|
228
|
+
var runtimeConfigScope = {
|
|
229
|
+
scope: "runtime",
|
|
230
|
+
schema: {
|
|
231
|
+
type: {
|
|
232
|
+
type: "string",
|
|
233
|
+
default: "host",
|
|
234
|
+
doc: "Runtime backend: host, docker, or e2b"
|
|
235
|
+
},
|
|
236
|
+
build_args: {
|
|
237
|
+
type: "json",
|
|
238
|
+
default: {},
|
|
239
|
+
parse: parseBuildArgs,
|
|
240
|
+
doc: "Build arguments passed to the runtime image build"
|
|
241
|
+
},
|
|
242
|
+
mounts: {
|
|
243
|
+
type: "json",
|
|
244
|
+
default: [],
|
|
245
|
+
parse: parseMounts,
|
|
246
|
+
doc: "Additional runtime mounts"
|
|
247
|
+
},
|
|
248
|
+
runner: {
|
|
249
|
+
type: "json",
|
|
250
|
+
default: createDefaultRunnerScope(),
|
|
251
|
+
parse: parseRunner,
|
|
252
|
+
doc: "Runner process and workspace transfer settings"
|
|
253
|
+
},
|
|
254
|
+
link: {
|
|
255
|
+
type: "string",
|
|
256
|
+
default: "",
|
|
257
|
+
doc: "Informational link for the runtime definition"
|
|
258
|
+
},
|
|
259
|
+
image: {
|
|
260
|
+
type: "string",
|
|
261
|
+
default: "",
|
|
262
|
+
doc: "Prebuilt Docker image"
|
|
263
|
+
},
|
|
264
|
+
dockerfile: {
|
|
265
|
+
type: "string",
|
|
266
|
+
default: "",
|
|
267
|
+
doc: "Path to the Dockerfile used for docker or e2b builds"
|
|
268
|
+
},
|
|
269
|
+
build_context: {
|
|
270
|
+
type: "string",
|
|
271
|
+
default: "",
|
|
272
|
+
doc: "Path to the Docker build context"
|
|
273
|
+
},
|
|
274
|
+
engine: {
|
|
275
|
+
type: "string",
|
|
276
|
+
default: "",
|
|
277
|
+
doc: "Container engine for Docker runtime"
|
|
278
|
+
},
|
|
279
|
+
network: {
|
|
280
|
+
type: "string",
|
|
281
|
+
default: "",
|
|
282
|
+
doc: "Docker network"
|
|
283
|
+
},
|
|
284
|
+
extra_args: {
|
|
285
|
+
type: "json",
|
|
286
|
+
default: void 0,
|
|
287
|
+
parse: parseOptionalStringArray,
|
|
288
|
+
doc: "Extra Docker runtime arguments"
|
|
289
|
+
},
|
|
290
|
+
template_id: {
|
|
291
|
+
type: "string",
|
|
292
|
+
default: "",
|
|
293
|
+
doc: "Prebuilt E2B template id"
|
|
294
|
+
},
|
|
295
|
+
cpu: {
|
|
296
|
+
type: "json",
|
|
297
|
+
default: void 0,
|
|
298
|
+
parse: parseOptionalNumber,
|
|
299
|
+
doc: "E2B CPU count"
|
|
300
|
+
},
|
|
301
|
+
memory_mb: {
|
|
302
|
+
type: "json",
|
|
303
|
+
default: void 0,
|
|
304
|
+
parse: parseOptionalNumber,
|
|
305
|
+
doc: "E2B memory in megabytes"
|
|
306
|
+
},
|
|
307
|
+
timeout_minutes: {
|
|
308
|
+
type: "json",
|
|
309
|
+
default: void 0,
|
|
310
|
+
parse: parseOptionalNumber,
|
|
311
|
+
doc: "E2B timeout in minutes"
|
|
312
|
+
},
|
|
313
|
+
preserve_after_exit_hours: {
|
|
314
|
+
type: "json",
|
|
315
|
+
default: void 0,
|
|
316
|
+
parse: parseOptionalNumber,
|
|
317
|
+
doc: "Hours to keep an E2B sandbox alive after job exit"
|
|
318
|
+
},
|
|
319
|
+
api_key_env: {
|
|
320
|
+
type: "string",
|
|
321
|
+
default: "",
|
|
322
|
+
doc: "Environment variable name containing the E2B API key"
|
|
204
323
|
}
|
|
205
|
-
out[name] = mapped;
|
|
206
324
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
325
|
+
};
|
|
326
|
+
function parseRunner(raw) {
|
|
327
|
+
if (raw === void 0) {
|
|
328
|
+
return createDefaultRunnerScope();
|
|
329
|
+
}
|
|
330
|
+
const record = asRecord(raw);
|
|
331
|
+
if (record === void 0) {
|
|
332
|
+
throw new Error("runner: expected an object.");
|
|
333
|
+
}
|
|
334
|
+
const uploadMaxFileMb = parseOptionalNumber(record.upload_max_file_mb, "runner.upload_max_file_mb") ?? 100;
|
|
335
|
+
if (uploadMaxFileMb <= 0) {
|
|
336
|
+
throw new Error("runner.upload_max_file_mb: expected a positive finite number.");
|
|
337
|
+
}
|
|
338
|
+
return omitUndefined({
|
|
339
|
+
detach: parseOptionalBoolean(record.detach, "runner.detach") ?? false,
|
|
340
|
+
upload_max_file_mb: uploadMaxFileMb,
|
|
341
|
+
download_conflict: parseDownloadConflict(record.download_conflict),
|
|
342
|
+
workspace: parseRunnerWorkspace(record.workspace)
|
|
343
|
+
});
|
|
211
344
|
}
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
345
|
+
function createDefaultRunnerScope() {
|
|
346
|
+
return {
|
|
347
|
+
detach: false,
|
|
348
|
+
upload_max_file_mb: 100,
|
|
349
|
+
download_conflict: "refuse",
|
|
350
|
+
workspace: {
|
|
351
|
+
exclude: [...defaultWorkspaceExclude]
|
|
352
|
+
}
|
|
353
|
+
};
|
|
215
354
|
}
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
355
|
+
function parseRunnerWorkspace(value) {
|
|
356
|
+
if (value === void 0) {
|
|
357
|
+
return {
|
|
358
|
+
exclude: [...defaultWorkspaceExclude]
|
|
359
|
+
};
|
|
220
360
|
}
|
|
221
|
-
|
|
361
|
+
const record = asRecord(value);
|
|
362
|
+
if (record === void 0) {
|
|
363
|
+
throw new Error("runner.workspace: expected an object.");
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
exclude: parseOptionalStringArray(record.exclude, "runner.workspace.exclude") ?? [
|
|
367
|
+
...defaultWorkspaceExclude
|
|
368
|
+
]
|
|
369
|
+
};
|
|
222
370
|
}
|
|
223
|
-
function
|
|
224
|
-
|
|
371
|
+
function parseDownloadConflict(value) {
|
|
372
|
+
if (value === void 0) {
|
|
373
|
+
return "refuse";
|
|
374
|
+
}
|
|
375
|
+
if (value === "refuse" || value === "overwrite") {
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
throw new Error('runner.download_conflict: expected "refuse" or "overwrite".');
|
|
225
379
|
}
|
|
226
|
-
function
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
380
|
+
function parseBuildArgs(value) {
|
|
381
|
+
if (value === void 0) {
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
const record = asRecord(value);
|
|
385
|
+
if (record === void 0) {
|
|
386
|
+
throw new Error("build_args: expected an object.");
|
|
387
|
+
}
|
|
388
|
+
const parsed = {};
|
|
389
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
390
|
+
if (typeof entry !== "string") {
|
|
391
|
+
throw new Error(`build_args.${key}: expected a string.`);
|
|
232
392
|
}
|
|
233
|
-
|
|
393
|
+
parsed[key] = entry;
|
|
234
394
|
}
|
|
235
|
-
return
|
|
395
|
+
return parsed;
|
|
236
396
|
}
|
|
237
|
-
function
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
397
|
+
function parseMounts(value) {
|
|
398
|
+
if (value === void 0) {
|
|
399
|
+
return [];
|
|
400
|
+
}
|
|
401
|
+
if (!Array.isArray(value)) {
|
|
402
|
+
throw new Error("mounts: expected an array.");
|
|
403
|
+
}
|
|
404
|
+
return value.map((entry, index) => {
|
|
405
|
+
const record = asRecord(entry);
|
|
406
|
+
if (record === void 0) {
|
|
407
|
+
throw new Error(`mounts[${index}]: expected an object.`);
|
|
244
408
|
}
|
|
245
|
-
|
|
246
|
-
|
|
409
|
+
const source = record.source;
|
|
410
|
+
const target = record.target;
|
|
411
|
+
if (typeof source !== "string") {
|
|
412
|
+
throw new Error(`mounts[${index}].source: expected a string.`);
|
|
247
413
|
}
|
|
248
|
-
if (
|
|
249
|
-
|
|
414
|
+
if (typeof target !== "string") {
|
|
415
|
+
throw new Error(`mounts[${index}].target: expected a string.`);
|
|
250
416
|
}
|
|
417
|
+
return omitUndefined({
|
|
418
|
+
source,
|
|
419
|
+
target,
|
|
420
|
+
readonly: parseOptionalBoolean(record.readonly, `mounts[${index}].readonly`)
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function parseOptionalStringArray(value, key = "") {
|
|
425
|
+
if (value === void 0) {
|
|
426
|
+
return void 0;
|
|
251
427
|
}
|
|
252
|
-
|
|
428
|
+
if (!Array.isArray(value)) {
|
|
429
|
+
throw new Error(`${key ? `${key}: ` : ""}expected an array.`);
|
|
430
|
+
}
|
|
431
|
+
return value.map((entry, index) => {
|
|
432
|
+
if (typeof entry !== "string") {
|
|
433
|
+
throw new Error(`${key}[${index}]: expected a string.`);
|
|
434
|
+
}
|
|
435
|
+
return entry;
|
|
436
|
+
});
|
|
253
437
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
438
|
+
function parseOptionalNumber(value, key = "") {
|
|
439
|
+
if (value === void 0) {
|
|
440
|
+
return void 0;
|
|
441
|
+
}
|
|
442
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
443
|
+
throw new Error(`${key ? `${key}: ` : ""}expected a finite number.`);
|
|
444
|
+
}
|
|
445
|
+
return value;
|
|
446
|
+
}
|
|
447
|
+
function parseOptionalBoolean(value, key) {
|
|
448
|
+
if (value === void 0) {
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
if (typeof value !== "boolean") {
|
|
452
|
+
throw new Error(`${key}: expected a boolean.`);
|
|
453
|
+
}
|
|
454
|
+
return value;
|
|
455
|
+
}
|
|
456
|
+
function asRecord(value) {
|
|
457
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
458
|
+
return void 0;
|
|
459
|
+
}
|
|
460
|
+
return value;
|
|
461
|
+
}
|
|
462
|
+
function omitUndefined(value) {
|
|
463
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
259
464
|
}
|
|
260
465
|
|
|
261
|
-
// packages/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
modelFlag: "--model",
|
|
269
|
-
modelStripProviderPrefix: true,
|
|
270
|
-
modelTransform: (model) => model.replaceAll(".", "-"),
|
|
271
|
-
defaultArgs: [
|
|
272
|
-
"--output-format",
|
|
273
|
-
"stream-json",
|
|
274
|
-
"--verbose"
|
|
275
|
-
],
|
|
276
|
-
mcpArgs: serializeJsonMcpArgs,
|
|
277
|
-
modes: {
|
|
278
|
-
yolo: ["--dangerously-skip-permissions"],
|
|
279
|
-
edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
|
|
280
|
-
read: ["--permission-mode", "plan"]
|
|
281
|
-
},
|
|
282
|
-
stdinMode: {
|
|
283
|
-
omitPrompt: true,
|
|
284
|
-
extraArgs: ["--input-format", "text"]
|
|
285
|
-
},
|
|
286
|
-
interactive: {
|
|
287
|
-
defaultArgs: []
|
|
288
|
-
},
|
|
289
|
-
resumeCommand: (threadId) => ["--resume", threadId]
|
|
290
|
-
};
|
|
466
|
+
// packages/poe-code-config/src/schema.ts
|
|
467
|
+
function defineScope(scope, schema) {
|
|
468
|
+
return {
|
|
469
|
+
scope,
|
|
470
|
+
schema
|
|
471
|
+
};
|
|
472
|
+
}
|
|
291
473
|
|
|
292
|
-
// packages/
|
|
293
|
-
var
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
defaultArgs: ["--skip-git-repo-check", "--json"],
|
|
302
|
-
mcpArgs: serializeCodexMcpArgs,
|
|
303
|
-
mcpArgsBeforeCommand: true,
|
|
304
|
-
modes: {
|
|
305
|
-
yolo: ["-s", "danger-full-access"],
|
|
306
|
-
edit: ["-s", "workspace-write"],
|
|
307
|
-
read: ["-s", "read-only"]
|
|
308
|
-
},
|
|
309
|
-
stdinMode: {
|
|
310
|
-
omitPrompt: true,
|
|
311
|
-
extraArgs: ["-"]
|
|
312
|
-
},
|
|
313
|
-
interactive: {
|
|
314
|
-
defaultArgs: ["-a", "never"]
|
|
315
|
-
},
|
|
316
|
-
resumeCommand: (threadId, cwd) => ["resume", "-C", cwd, threadId]
|
|
317
|
-
};
|
|
474
|
+
// packages/poe-code-config/src/plan-scope.ts
|
|
475
|
+
var planConfigScope = defineScope("plan", {
|
|
476
|
+
plan_directory: {
|
|
477
|
+
type: "string",
|
|
478
|
+
default: "docs/plans",
|
|
479
|
+
env: "POE_PLAN_DIRECTORY",
|
|
480
|
+
doc: "Directory where planning documents are stored"
|
|
481
|
+
}
|
|
482
|
+
});
|
|
318
483
|
|
|
319
|
-
// packages/
|
|
320
|
-
|
|
321
|
-
kind: "cli",
|
|
322
|
-
agentId: "opencode",
|
|
323
|
-
// ACP adapter support: yes (adapter: "opencode").
|
|
324
|
-
// OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
|
|
325
|
-
// (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
|
|
326
|
-
adapter: "opencode",
|
|
327
|
-
promptFlag: "run",
|
|
328
|
-
modelFlag: "--model",
|
|
329
|
-
modelStripProviderPrefix: false,
|
|
330
|
-
modelTransform: (model) => {
|
|
331
|
-
return model.startsWith("poe/") ? model : `poe/${model}`;
|
|
332
|
-
},
|
|
333
|
-
defaultArgs: ["--format", "json"],
|
|
334
|
-
modes: {
|
|
335
|
-
yolo: [],
|
|
336
|
-
edit: [],
|
|
337
|
-
read: ["--agent", "plan"]
|
|
338
|
-
},
|
|
339
|
-
interactive: {
|
|
340
|
-
defaultArgs: [],
|
|
341
|
-
promptFlag: "--prompt"
|
|
342
|
-
},
|
|
343
|
-
resumeCommand: (threadId, cwd) => [cwd, "--session", threadId],
|
|
344
|
-
mcpEnv: serializeOpenCodeMcpEnv
|
|
345
|
-
};
|
|
346
|
-
var openCodeAcpSpawnConfig = {
|
|
347
|
-
kind: "acp",
|
|
348
|
-
agentId: "opencode",
|
|
349
|
-
acpArgs: ["acp"],
|
|
350
|
-
skipAuth: true,
|
|
351
|
-
mcpEnv: serializeOpenCodeMcpEnv
|
|
352
|
-
};
|
|
484
|
+
// packages/poe-code-config/src/store.ts
|
|
485
|
+
import path8 from "node:path";
|
|
353
486
|
|
|
354
|
-
// packages/
|
|
355
|
-
|
|
356
|
-
kind: "cli",
|
|
357
|
-
agentId: "kimi",
|
|
358
|
-
// ACP adapter support: yes (adapter: "kimi").
|
|
359
|
-
// Kimi's `--output-format stream-json` emits OpenAI-style `{ role, content }` JSON
|
|
360
|
-
// (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
|
|
361
|
-
adapter: "kimi",
|
|
362
|
-
promptFlag: "-p",
|
|
363
|
-
modelStripProviderPrefix: true,
|
|
364
|
-
defaultArgs: ["--print", "--output-format", "stream-json"],
|
|
365
|
-
mcpArgs: serializeJsonMcpArgs,
|
|
366
|
-
modes: {
|
|
367
|
-
yolo: ["--yolo"],
|
|
368
|
-
edit: [],
|
|
369
|
-
read: []
|
|
370
|
-
},
|
|
371
|
-
stdinMode: {
|
|
372
|
-
omitPrompt: true,
|
|
373
|
-
extraArgs: ["--input-format", "stream-json"]
|
|
374
|
-
},
|
|
375
|
-
interactive: {
|
|
376
|
-
defaultArgs: [],
|
|
377
|
-
promptFlag: "-p"
|
|
378
|
-
},
|
|
379
|
-
resumeCommand: (threadId, cwd) => ["--session", threadId, "--work-dir", cwd]
|
|
380
|
-
};
|
|
381
|
-
var kimiAcpSpawnConfig = {
|
|
382
|
-
kind: "acp",
|
|
383
|
-
agentId: "kimi",
|
|
384
|
-
acpArgs: ["acp"]
|
|
385
|
-
};
|
|
487
|
+
// packages/config-extends/src/discover.ts
|
|
488
|
+
import path4 from "node:path";
|
|
386
489
|
|
|
387
|
-
// packages/
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
agentId: "goose",
|
|
392
|
-
adapter: "native",
|
|
393
|
-
promptFlag: "--text",
|
|
394
|
-
modelFlag: "--model",
|
|
395
|
-
modelStripProviderPrefix: false,
|
|
396
|
-
defaultArgs: ["run", "--output-format", "stream-json"],
|
|
397
|
-
defaultArgsPosition: "beforePrompt",
|
|
398
|
-
mcpArgs: serializeGooseMcpArgs,
|
|
399
|
-
mcpArgsPosition: "beforePrompt",
|
|
400
|
-
modes: {
|
|
401
|
-
yolo: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "auto" } },
|
|
402
|
-
edit: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "smart_approve" } },
|
|
403
|
-
read: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "chat" } }
|
|
404
|
-
},
|
|
405
|
-
stdinMode: {
|
|
406
|
-
omitPrompt: true,
|
|
407
|
-
extraArgs: ["--instructions", "-"]
|
|
408
|
-
},
|
|
409
|
-
interactive: {
|
|
410
|
-
defaultArgs: ["session"],
|
|
411
|
-
defaultArgsPosition: "beforePrompt"
|
|
412
|
-
},
|
|
413
|
-
resumeCommand: () => ["run", "--resume", "--text", "continue"]
|
|
414
|
-
};
|
|
415
|
-
var gooseAcpSpawnConfig = {
|
|
416
|
-
kind: "acp",
|
|
417
|
-
agentId: "goose",
|
|
418
|
-
acpArgs: ["acp"],
|
|
419
|
-
env: gooseFileSecretsEnv,
|
|
420
|
-
skipAuth: true
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
// packages/agent-spawn/src/configs/index.ts
|
|
424
|
-
var allSpawnConfigs = [
|
|
425
|
-
claudeCodeSpawnConfig,
|
|
426
|
-
codexSpawnConfig,
|
|
427
|
-
openCodeSpawnConfig,
|
|
428
|
-
kimiSpawnConfig,
|
|
429
|
-
gooseSpawnConfig
|
|
430
|
-
];
|
|
431
|
-
var lookup2 = /* @__PURE__ */ new Map();
|
|
432
|
-
for (const config of allSpawnConfigs) {
|
|
433
|
-
lookup2.set(config.agentId, config);
|
|
434
|
-
}
|
|
435
|
-
var acpLookup = /* @__PURE__ */ new Map();
|
|
436
|
-
acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
|
|
437
|
-
acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
|
|
438
|
-
acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
|
|
439
|
-
|
|
440
|
-
// packages/agent-spawn/src/spawn.ts
|
|
441
|
-
import { spawn as spawnChildProcess } from "node:child_process";
|
|
442
|
-
import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
|
|
443
|
-
import path from "node:path";
|
|
490
|
+
// packages/config-extends/src/parse.ts
|
|
491
|
+
import path5 from "node:path";
|
|
492
|
+
import matter from "gray-matter";
|
|
493
|
+
import { parse as parseYaml } from "yaml";
|
|
444
494
|
|
|
445
|
-
// packages/
|
|
446
|
-
import
|
|
495
|
+
// packages/config-extends/src/resolve.ts
|
|
496
|
+
import path6 from "node:path";
|
|
447
497
|
|
|
448
|
-
// packages/
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
498
|
+
// packages/config-mutations/src/mutations/config-mutation.ts
|
|
499
|
+
function merge(options) {
|
|
500
|
+
return {
|
|
501
|
+
kind: "configMerge",
|
|
502
|
+
target: options.target,
|
|
503
|
+
value: options.value,
|
|
504
|
+
format: options.format,
|
|
505
|
+
pruneByPrefix: options.pruneByPrefix,
|
|
506
|
+
label: options.label
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function prune(options) {
|
|
510
|
+
return {
|
|
511
|
+
kind: "configPrune",
|
|
512
|
+
target: options.target,
|
|
513
|
+
shape: options.shape,
|
|
514
|
+
format: options.format,
|
|
515
|
+
onlyIf: options.onlyIf,
|
|
516
|
+
label: options.label
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function transform(options) {
|
|
520
|
+
return {
|
|
521
|
+
kind: "configTransform",
|
|
522
|
+
target: options.target,
|
|
523
|
+
format: options.format,
|
|
524
|
+
transform: options.transform,
|
|
525
|
+
label: options.label
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
var configMutation = {
|
|
529
|
+
merge,
|
|
530
|
+
prune,
|
|
531
|
+
transform
|
|
481
532
|
};
|
|
482
533
|
|
|
483
|
-
// packages/
|
|
484
|
-
|
|
534
|
+
// packages/config-mutations/src/mutations/file-mutation.ts
|
|
535
|
+
function ensureDirectory(options) {
|
|
536
|
+
return {
|
|
537
|
+
kind: "ensureDirectory",
|
|
538
|
+
path: options.path,
|
|
539
|
+
label: options.label
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function remove(options) {
|
|
543
|
+
return {
|
|
544
|
+
kind: "removeFile",
|
|
545
|
+
target: options.target,
|
|
546
|
+
whenEmpty: options.whenEmpty,
|
|
547
|
+
whenContentMatches: options.whenContentMatches,
|
|
548
|
+
label: options.label
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function removeDirectory(options) {
|
|
552
|
+
return {
|
|
553
|
+
kind: "removeDirectory",
|
|
554
|
+
path: options.path,
|
|
555
|
+
force: options.force,
|
|
556
|
+
label: options.label
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function chmod(options) {
|
|
560
|
+
return {
|
|
561
|
+
kind: "chmod",
|
|
562
|
+
target: options.target,
|
|
563
|
+
mode: options.mode,
|
|
564
|
+
label: options.label
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function backup(options) {
|
|
568
|
+
return {
|
|
569
|
+
kind: "backup",
|
|
570
|
+
target: options.target,
|
|
571
|
+
label: options.label
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
var fileMutation = {
|
|
575
|
+
ensureDirectory,
|
|
576
|
+
remove,
|
|
577
|
+
removeDirectory,
|
|
578
|
+
chmod,
|
|
579
|
+
backup
|
|
580
|
+
};
|
|
485
581
|
|
|
486
|
-
// packages/
|
|
487
|
-
import
|
|
582
|
+
// packages/config-mutations/src/execution/apply-mutation.ts
|
|
583
|
+
import Mustache from "mustache";
|
|
488
584
|
|
|
489
|
-
// packages/
|
|
490
|
-
import
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
var cached;
|
|
494
|
-
function resolveOutputFormat(env = process.env) {
|
|
495
|
-
const scoped = formatStorage.getStore();
|
|
496
|
-
if (scoped) {
|
|
497
|
-
return scoped;
|
|
498
|
-
}
|
|
499
|
-
if (cached) {
|
|
500
|
-
return cached;
|
|
501
|
-
}
|
|
502
|
-
const raw = env.OUTPUT_FORMAT?.toLowerCase();
|
|
503
|
-
cached = VALID_FORMATS.has(raw) ? raw : "terminal";
|
|
504
|
-
return cached;
|
|
585
|
+
// packages/config-mutations/src/formats/json.ts
|
|
586
|
+
import * as jsonc from "jsonc-parser";
|
|
587
|
+
function isConfigObject(value) {
|
|
588
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
505
589
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const apple = env.APPLE_INTERFACE_STYLE;
|
|
510
|
-
if (typeof apple === "string") {
|
|
511
|
-
return apple.toLowerCase() === "dark" ? "dark" : "light";
|
|
512
|
-
}
|
|
513
|
-
const vscodeKind = env.VSCODE_COLOR_THEME_KIND;
|
|
514
|
-
if (typeof vscodeKind === "string") {
|
|
515
|
-
const normalized = vscodeKind.toLowerCase();
|
|
516
|
-
if (normalized.includes("light")) {
|
|
517
|
-
return "light";
|
|
518
|
-
}
|
|
519
|
-
if (normalized.includes("dark")) {
|
|
520
|
-
return "dark";
|
|
521
|
-
}
|
|
590
|
+
function parse2(content) {
|
|
591
|
+
if (!content || content.trim() === "") {
|
|
592
|
+
return {};
|
|
522
593
|
}
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
594
|
+
const errors = [];
|
|
595
|
+
const parsed = jsonc.parse(content, errors, {
|
|
596
|
+
allowTrailingComma: true,
|
|
597
|
+
disallowComments: false
|
|
598
|
+
});
|
|
599
|
+
if (errors.length > 0) {
|
|
600
|
+
throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
|
|
530
601
|
}
|
|
531
|
-
|
|
532
|
-
}
|
|
533
|
-
function resolveThemeName(env = process.env) {
|
|
534
|
-
const raw = (env.POE_CODE_THEME ?? env.POE_THEME)?.toLowerCase();
|
|
535
|
-
if (raw === "light" || raw === "dark") {
|
|
536
|
-
return raw;
|
|
602
|
+
if (parsed === null || parsed === void 0) {
|
|
603
|
+
return {};
|
|
537
604
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
return detected;
|
|
605
|
+
if (!isConfigObject(parsed)) {
|
|
606
|
+
throw new Error("Expected JSON object.");
|
|
541
607
|
}
|
|
542
|
-
return
|
|
608
|
+
return parsed;
|
|
543
609
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
return cachedTheme;
|
|
548
|
-
}
|
|
549
|
-
const themeName = resolveThemeName(env);
|
|
550
|
-
cachedTheme = themeName === "light" ? light : dark;
|
|
551
|
-
return cachedTheme;
|
|
610
|
+
function serialize(obj) {
|
|
611
|
+
return `${JSON.stringify(obj, null, 2)}
|
|
612
|
+
`;
|
|
552
613
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if (
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if (format === "markdown") return "[x]";
|
|
599
|
-
return "\u25C6";
|
|
600
|
-
},
|
|
601
|
-
get inactive() {
|
|
602
|
-
const format = resolveOutputFormat();
|
|
603
|
-
if (format === "json") return "inactive";
|
|
604
|
-
if (format === "markdown") return "[ ]";
|
|
605
|
-
return "\u25CB";
|
|
614
|
+
function merge2(base, patch) {
|
|
615
|
+
const result = { ...base };
|
|
616
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
617
|
+
if (value === void 0) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const existing = result[key];
|
|
621
|
+
if (isConfigObject(existing) && isConfigObject(value)) {
|
|
622
|
+
result[key] = merge2(existing, value);
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
result[key] = value;
|
|
626
|
+
}
|
|
627
|
+
return result;
|
|
628
|
+
}
|
|
629
|
+
function prune2(obj, shape) {
|
|
630
|
+
let changed = false;
|
|
631
|
+
const result = { ...obj };
|
|
632
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
633
|
+
if (!(key in result)) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const current = result[key];
|
|
637
|
+
if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
|
|
638
|
+
delete result[key];
|
|
639
|
+
changed = true;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
if (isConfigObject(pattern) && isConfigObject(current)) {
|
|
643
|
+
const { changed: childChanged, result: childResult } = prune2(
|
|
644
|
+
current,
|
|
645
|
+
pattern
|
|
646
|
+
);
|
|
647
|
+
if (childChanged) {
|
|
648
|
+
changed = true;
|
|
649
|
+
}
|
|
650
|
+
if (Object.keys(childResult).length === 0) {
|
|
651
|
+
delete result[key];
|
|
652
|
+
} else {
|
|
653
|
+
result[key] = childResult;
|
|
654
|
+
}
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
delete result[key];
|
|
658
|
+
changed = true;
|
|
606
659
|
}
|
|
660
|
+
return { changed, result };
|
|
661
|
+
}
|
|
662
|
+
var jsonFormat = {
|
|
663
|
+
parse: parse2,
|
|
664
|
+
serialize,
|
|
665
|
+
merge: merge2,
|
|
666
|
+
prune: prune2
|
|
607
667
|
};
|
|
608
668
|
|
|
609
|
-
// packages/
|
|
610
|
-
import
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
import chalk5 from "chalk";
|
|
614
|
-
|
|
615
|
-
// packages/design-system/src/internal/strip-ansi.ts
|
|
616
|
-
function stripAnsi(value) {
|
|
617
|
-
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
669
|
+
// packages/config-mutations/src/formats/toml.ts
|
|
670
|
+
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
671
|
+
function isConfigObject2(value) {
|
|
672
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
618
673
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
symbol = chalk5.gray("\u2502"),
|
|
623
|
-
secondarySymbol = chalk5.gray("\u2502"),
|
|
624
|
-
spacing: spacing2 = 1,
|
|
625
|
-
withGuide = true
|
|
626
|
-
} = {}) {
|
|
627
|
-
const lines = [];
|
|
628
|
-
const showGuide = withGuide !== false;
|
|
629
|
-
const contentLines = msg.split("\n");
|
|
630
|
-
const prefix = showGuide ? `${symbol} ` : "";
|
|
631
|
-
const continuationPrefix = showGuide ? `${secondarySymbol} ` : "";
|
|
632
|
-
const emptyGuide = showGuide ? secondarySymbol : "";
|
|
633
|
-
for (let index = 0; index < spacing2; index += 1) {
|
|
634
|
-
lines.push(emptyGuide);
|
|
635
|
-
}
|
|
636
|
-
if (contentLines.length === 0) {
|
|
637
|
-
process.stdout.write("\n");
|
|
638
|
-
return;
|
|
674
|
+
function parse3(content) {
|
|
675
|
+
if (!content || content.trim() === "") {
|
|
676
|
+
return {};
|
|
639
677
|
}
|
|
640
|
-
const
|
|
641
|
-
if (
|
|
642
|
-
|
|
643
|
-
} else {
|
|
644
|
-
lines.push(showGuide ? symbol : "");
|
|
678
|
+
const parsed = parseToml(content);
|
|
679
|
+
if (!isConfigObject2(parsed)) {
|
|
680
|
+
throw new Error("Expected TOML document to be a table.");
|
|
645
681
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
682
|
+
return parsed;
|
|
683
|
+
}
|
|
684
|
+
function serialize2(obj) {
|
|
685
|
+
const serialized = stringifyToml(obj);
|
|
686
|
+
return serialized.endsWith("\n") ? serialized : `${serialized}
|
|
687
|
+
`;
|
|
688
|
+
}
|
|
689
|
+
function merge3(base, patch) {
|
|
690
|
+
const result = { ...base };
|
|
691
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
692
|
+
if (value === void 0) {
|
|
649
693
|
continue;
|
|
650
694
|
}
|
|
651
|
-
|
|
695
|
+
const existing = result[key];
|
|
696
|
+
if (isConfigObject2(existing) && isConfigObject2(value)) {
|
|
697
|
+
result[key] = merge3(existing, value);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
result[key] = value;
|
|
652
701
|
}
|
|
653
|
-
|
|
654
|
-
`);
|
|
702
|
+
return result;
|
|
655
703
|
}
|
|
656
|
-
function
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
704
|
+
function prune3(obj, shape) {
|
|
705
|
+
let changed = false;
|
|
706
|
+
const result = { ...obj };
|
|
707
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
708
|
+
if (!(key in result)) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
const current = result[key];
|
|
712
|
+
if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
|
|
713
|
+
delete result[key];
|
|
714
|
+
changed = true;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (isConfigObject2(pattern) && isConfigObject2(current)) {
|
|
718
|
+
const { changed: childChanged, result: childResult } = prune3(
|
|
719
|
+
current,
|
|
720
|
+
pattern
|
|
721
|
+
);
|
|
722
|
+
if (childChanged) {
|
|
723
|
+
changed = true;
|
|
724
|
+
}
|
|
725
|
+
if (Object.keys(childResult).length === 0) {
|
|
726
|
+
delete result[key];
|
|
727
|
+
} else {
|
|
728
|
+
result[key] = childResult;
|
|
729
|
+
}
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
delete result[key];
|
|
733
|
+
changed = true;
|
|
669
734
|
}
|
|
670
|
-
|
|
735
|
+
return { changed, result };
|
|
671
736
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
message(msg, { symbol: symbols.info });
|
|
737
|
+
var tomlFormat = {
|
|
738
|
+
parse: parse3,
|
|
739
|
+
serialize: serialize2,
|
|
740
|
+
merge: merge3,
|
|
741
|
+
prune: prune3
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
// packages/config-mutations/src/formats/yaml.ts
|
|
745
|
+
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
746
|
+
function isConfigObject3(value) {
|
|
747
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
687
748
|
}
|
|
688
|
-
function
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
process.stdout.write(`- **success:** ${stripAnsi(msg)}
|
|
692
|
-
`);
|
|
693
|
-
return;
|
|
749
|
+
function parse4(content) {
|
|
750
|
+
if (!content || content.trim() === "") {
|
|
751
|
+
return {};
|
|
694
752
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
`
|
|
699
|
-
);
|
|
700
|
-
return;
|
|
753
|
+
const parsed = parseYaml2(content);
|
|
754
|
+
if (parsed === null || parsed === void 0) {
|
|
755
|
+
return {};
|
|
701
756
|
}
|
|
702
|
-
|
|
757
|
+
if (!isConfigObject3(parsed)) {
|
|
758
|
+
throw new Error("Expected YAML object.");
|
|
759
|
+
}
|
|
760
|
+
return parsed;
|
|
703
761
|
}
|
|
704
|
-
function
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
`);
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
if (format === "json") {
|
|
712
|
-
process.stdout.write(
|
|
713
|
-
`${JSON.stringify({ level: "warn", message: stripAnsi(msg) })}
|
|
714
|
-
`
|
|
715
|
-
);
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
message(msg, { symbol: chalk5.yellow("\u25B2") });
|
|
719
|
-
}
|
|
720
|
-
function error(msg) {
|
|
721
|
-
const format = resolveOutputFormat();
|
|
722
|
-
if (format === "markdown") {
|
|
723
|
-
process.stdout.write(`- **error:** ${stripAnsi(msg)}
|
|
724
|
-
`);
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
if (format === "json") {
|
|
728
|
-
process.stdout.write(
|
|
729
|
-
`${JSON.stringify({ level: "error", message: stripAnsi(msg) })}
|
|
730
|
-
`
|
|
731
|
-
);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
message(msg, { symbol: chalk5.red("\u25A0") });
|
|
762
|
+
function serialize3(obj) {
|
|
763
|
+
const serialized = stringifyYaml(obj);
|
|
764
|
+
return serialized.endsWith("\n") ? serialized : `${serialized}
|
|
765
|
+
`;
|
|
735
766
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
error
|
|
742
|
-
};
|
|
743
|
-
|
|
744
|
-
// packages/design-system/src/components/logger.ts
|
|
745
|
-
function createLogger(emitter) {
|
|
746
|
-
const emit = (level, message2) => {
|
|
747
|
-
if (emitter) {
|
|
748
|
-
emitter(message2);
|
|
749
|
-
return;
|
|
767
|
+
function merge4(base, patch) {
|
|
768
|
+
const result = { ...base };
|
|
769
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
770
|
+
if (value === void 0) {
|
|
771
|
+
continue;
|
|
750
772
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
773
|
+
const existing = result[key];
|
|
774
|
+
if (isConfigObject3(existing) && isConfigObject3(value)) {
|
|
775
|
+
result[key] = merge4(existing, value);
|
|
776
|
+
continue;
|
|
754
777
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
778
|
+
result[key] = value;
|
|
779
|
+
}
|
|
780
|
+
return result;
|
|
781
|
+
}
|
|
782
|
+
function prune4(obj, shape) {
|
|
783
|
+
let changed = false;
|
|
784
|
+
const result = { ...obj };
|
|
785
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
786
|
+
if (!(key in result)) {
|
|
787
|
+
continue;
|
|
758
788
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
789
|
+
const current = result[key];
|
|
790
|
+
if (isConfigObject3(pattern) && Object.keys(pattern).length === 0) {
|
|
791
|
+
delete result[key];
|
|
792
|
+
changed = true;
|
|
793
|
+
continue;
|
|
762
794
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
emit("info", message2);
|
|
768
|
-
},
|
|
769
|
-
success(message2) {
|
|
770
|
-
emit("success", message2);
|
|
771
|
-
},
|
|
772
|
-
warn(message2) {
|
|
773
|
-
emit("warn", message2);
|
|
774
|
-
},
|
|
775
|
-
error(message2) {
|
|
776
|
-
emit("error", message2);
|
|
777
|
-
},
|
|
778
|
-
resolved(label, value) {
|
|
779
|
-
if (emitter) {
|
|
780
|
-
emitter(`${label}: ${value}`);
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
log.message(`${label}
|
|
784
|
-
${value}`, { symbol: symbols.resolved });
|
|
785
|
-
},
|
|
786
|
-
errorResolved(label, value) {
|
|
787
|
-
if (emitter) {
|
|
788
|
-
emitter(`${label}: ${value}`);
|
|
789
|
-
return;
|
|
795
|
+
if (isConfigObject3(pattern) && isConfigObject3(current)) {
|
|
796
|
+
const { changed: childChanged, result: childResult } = prune4(current, pattern);
|
|
797
|
+
if (childChanged) {
|
|
798
|
+
changed = true;
|
|
790
799
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (emitter) {
|
|
796
|
-
emitter(message2);
|
|
797
|
-
return;
|
|
800
|
+
if (Object.keys(childResult).length === 0) {
|
|
801
|
+
delete result[key];
|
|
802
|
+
} else {
|
|
803
|
+
result[key] = childResult;
|
|
798
804
|
}
|
|
799
|
-
|
|
805
|
+
continue;
|
|
800
806
|
}
|
|
801
|
-
|
|
807
|
+
delete result[key];
|
|
808
|
+
changed = true;
|
|
809
|
+
}
|
|
810
|
+
return { changed, result };
|
|
802
811
|
}
|
|
803
|
-
var
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
import chalk7 from "chalk";
|
|
810
|
-
|
|
811
|
-
// packages/design-system/src/acp/writer.ts
|
|
812
|
-
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
813
|
-
var storage = new AsyncLocalStorage2();
|
|
814
|
-
|
|
815
|
-
// packages/design-system/src/acp/components.ts
|
|
816
|
-
var AGENT_PREFIX = `${chalk7.green.bold("\u2713")} agent: `;
|
|
812
|
+
var yamlFormat = {
|
|
813
|
+
parse: parse4,
|
|
814
|
+
serialize: serialize3,
|
|
815
|
+
merge: merge4,
|
|
816
|
+
prune: prune4
|
|
817
|
+
};
|
|
817
818
|
|
|
818
|
-
// packages/
|
|
819
|
-
|
|
819
|
+
// packages/config-mutations/src/formats/index.ts
|
|
820
|
+
var formatRegistry = {
|
|
821
|
+
json: jsonFormat,
|
|
822
|
+
toml: tomlFormat,
|
|
823
|
+
yaml: yamlFormat
|
|
824
|
+
};
|
|
825
|
+
var extensionMap = {
|
|
826
|
+
".json": "json",
|
|
827
|
+
".toml": "toml",
|
|
828
|
+
".yaml": "yaml",
|
|
829
|
+
".yml": "yaml"
|
|
830
|
+
};
|
|
831
|
+
function getConfigFormat(pathOrFormat) {
|
|
832
|
+
if (pathOrFormat in formatRegistry) {
|
|
833
|
+
return formatRegistry[pathOrFormat];
|
|
834
|
+
}
|
|
835
|
+
const ext = getExtension(pathOrFormat);
|
|
836
|
+
const formatName = extensionMap[ext];
|
|
837
|
+
if (!formatName) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
`Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return formatRegistry[formatName];
|
|
843
|
+
}
|
|
844
|
+
function detectFormat(path18) {
|
|
845
|
+
const ext = getExtension(path18);
|
|
846
|
+
return extensionMap[ext];
|
|
847
|
+
}
|
|
848
|
+
function getExtension(path18) {
|
|
849
|
+
const lastDot = path18.lastIndexOf(".");
|
|
850
|
+
if (lastDot === -1) {
|
|
851
|
+
return "";
|
|
852
|
+
}
|
|
853
|
+
return path18.slice(lastDot).toLowerCase();
|
|
854
|
+
}
|
|
820
855
|
|
|
821
|
-
// packages/
|
|
822
|
-
import
|
|
823
|
-
|
|
856
|
+
// packages/config-mutations/src/execution/path-utils.ts
|
|
857
|
+
import path7 from "node:path";
|
|
858
|
+
function expandHome(targetPath, homeDir) {
|
|
859
|
+
if (!targetPath?.startsWith("~")) {
|
|
860
|
+
return targetPath;
|
|
861
|
+
}
|
|
862
|
+
if (targetPath.startsWith("~./")) {
|
|
863
|
+
targetPath = `~/.${targetPath.slice(3)}`;
|
|
864
|
+
}
|
|
865
|
+
let remainder = targetPath.slice(1);
|
|
866
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
867
|
+
remainder = remainder.slice(1);
|
|
868
|
+
} else if (remainder.startsWith(".")) {
|
|
869
|
+
remainder = remainder.slice(1);
|
|
870
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
871
|
+
remainder = remainder.slice(1);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return remainder.length === 0 ? homeDir : path7.join(homeDir, remainder);
|
|
875
|
+
}
|
|
876
|
+
function validateHomePath(targetPath) {
|
|
877
|
+
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
878
|
+
throw new Error("Target path must be a non-empty string.");
|
|
879
|
+
}
|
|
880
|
+
if (!targetPath.startsWith("~")) {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`All target paths must be home-relative (start with ~). Received: "${targetPath}"`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function resolvePath(rawPath, homeDir, pathMapper) {
|
|
887
|
+
validateHomePath(rawPath);
|
|
888
|
+
const expanded = expandHome(rawPath, homeDir);
|
|
889
|
+
if (!pathMapper) {
|
|
890
|
+
return expanded;
|
|
891
|
+
}
|
|
892
|
+
const rawDirectory = path7.dirname(expanded);
|
|
893
|
+
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
894
|
+
targetDirectory: rawDirectory
|
|
895
|
+
});
|
|
896
|
+
const filename = path7.basename(expanded);
|
|
897
|
+
return filename.length === 0 ? mappedDirectory : path7.join(mappedDirectory, filename);
|
|
898
|
+
}
|
|
824
899
|
|
|
825
|
-
// packages/
|
|
826
|
-
|
|
827
|
-
|
|
900
|
+
// packages/config-mutations/src/fs-utils.ts
|
|
901
|
+
function isNotFound(error2) {
|
|
902
|
+
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
903
|
+
}
|
|
904
|
+
async function readFileIfExists(fs, target) {
|
|
905
|
+
try {
|
|
906
|
+
return await fs.readFile(target, "utf8");
|
|
907
|
+
} catch (error2) {
|
|
908
|
+
if (isNotFound(error2)) {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
throw error2;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function pathExists(fs, target) {
|
|
915
|
+
try {
|
|
916
|
+
await fs.stat(target);
|
|
917
|
+
return true;
|
|
918
|
+
} catch (error2) {
|
|
919
|
+
if (isNotFound(error2)) {
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
throw error2;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function createTimestamp() {
|
|
926
|
+
return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
|
|
927
|
+
}
|
|
828
928
|
|
|
829
|
-
// packages/
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
// packages/design-system/src/prompts/primitives/note.ts
|
|
837
|
-
import chalk11 from "chalk";
|
|
838
|
-
|
|
839
|
-
// packages/design-system/src/prompts/primitives/outro.ts
|
|
840
|
-
import chalk12 from "chalk";
|
|
841
|
-
|
|
842
|
-
// packages/design-system/src/prompts/primitives/spinner.ts
|
|
843
|
-
import chalk14 from "chalk";
|
|
844
|
-
|
|
845
|
-
// packages/design-system/src/static/spinner.ts
|
|
846
|
-
import chalk13 from "chalk";
|
|
847
|
-
|
|
848
|
-
// packages/design-system/src/static/menu.ts
|
|
849
|
-
import chalk16 from "chalk";
|
|
850
|
-
|
|
851
|
-
// packages/agent-spawn/src/autonomous.ts
|
|
852
|
-
var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
853
|
-
|
|
854
|
-
// packages/agent-spawn/src/acp/replay.ts
|
|
855
|
-
import path2 from "node:path";
|
|
856
|
-
import { homedir as homedir2 } from "node:os";
|
|
857
|
-
import { open, readdir } from "node:fs/promises";
|
|
858
|
-
import { createInterface } from "node:readline";
|
|
859
|
-
|
|
860
|
-
// packages/poe-acp-client/src/acp-client.ts
|
|
861
|
-
import { isAbsolute } from "node:path";
|
|
862
|
-
|
|
863
|
-
// packages/poe-acp-client/src/acp-transport.ts
|
|
864
|
-
import {
|
|
865
|
-
spawn as spawnChildProcess3
|
|
866
|
-
} from "node:child_process";
|
|
867
|
-
|
|
868
|
-
// packages/poe-acp-client/src/run-report.ts
|
|
869
|
-
import * as fsPromises from "node:fs/promises";
|
|
870
|
-
import { homedir } from "node:os";
|
|
871
|
-
import { join } from "node:path";
|
|
872
|
-
|
|
873
|
-
// packages/agent-spawn/src/acp/spawn.ts
|
|
874
|
-
import { spawn as spawnChildProcess4 } from "node:child_process";
|
|
875
|
-
|
|
876
|
-
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
877
|
-
import path3 from "node:path";
|
|
878
|
-
import { homedir as homedir3 } from "node:os";
|
|
879
|
-
import { mkdir, open as open2 } from "node:fs/promises";
|
|
880
|
-
|
|
881
|
-
// src/utils/command-checks.ts
|
|
882
|
-
function formatCommandRunnerResult(result) {
|
|
883
|
-
const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
|
|
884
|
-
const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
|
|
885
|
-
return `stdout:
|
|
886
|
-
${stdout}
|
|
887
|
-
stderr:
|
|
888
|
-
${stderr}`;
|
|
929
|
+
// packages/config-mutations/src/execution/apply-mutation.ts
|
|
930
|
+
function resolveValue(resolver, options) {
|
|
931
|
+
if (typeof resolver === "function") {
|
|
932
|
+
return resolver(options);
|
|
933
|
+
}
|
|
934
|
+
return resolver;
|
|
889
935
|
}
|
|
890
|
-
function
|
|
891
|
-
|
|
936
|
+
function createInvalidDocumentBackupPath(targetPath) {
|
|
937
|
+
const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
|
|
938
|
+
return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
|
|
892
939
|
}
|
|
893
|
-
function
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
description: describeCommandExpectation(
|
|
897
|
-
options.command,
|
|
898
|
-
options.args,
|
|
899
|
-
options.expectedOutput
|
|
900
|
-
),
|
|
901
|
-
async run(context) {
|
|
902
|
-
await runAndMatchOutput(context, options);
|
|
903
|
-
}
|
|
904
|
-
};
|
|
940
|
+
async function backupInvalidDocument(fs, targetPath, content) {
|
|
941
|
+
const backupPath = createInvalidDocumentBackupPath(targetPath);
|
|
942
|
+
await fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
905
943
|
}
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
detail
|
|
930
|
-
].join("\n")
|
|
931
|
-
);
|
|
944
|
+
function describeMutation(kind, targetPath) {
|
|
945
|
+
const displayPath = targetPath ?? "target";
|
|
946
|
+
switch (kind) {
|
|
947
|
+
case "ensureDirectory":
|
|
948
|
+
return `Create ${displayPath}`;
|
|
949
|
+
case "removeDirectory":
|
|
950
|
+
return `Remove directory ${displayPath}`;
|
|
951
|
+
case "backup":
|
|
952
|
+
return `Backup ${displayPath}`;
|
|
953
|
+
case "templateWrite":
|
|
954
|
+
return `Write ${displayPath}`;
|
|
955
|
+
case "chmod":
|
|
956
|
+
return `Set permissions on ${displayPath}`;
|
|
957
|
+
case "removeFile":
|
|
958
|
+
return `Remove ${displayPath}`;
|
|
959
|
+
case "configMerge":
|
|
960
|
+
case "configPrune":
|
|
961
|
+
case "configTransform":
|
|
962
|
+
case "templateMergeToml":
|
|
963
|
+
case "templateMergeJson":
|
|
964
|
+
return `Update ${displayPath}`;
|
|
965
|
+
default:
|
|
966
|
+
return "Operation";
|
|
932
967
|
}
|
|
933
968
|
}
|
|
934
|
-
function
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
if (lines.some((line) => line === expected)) {
|
|
941
|
-
return true;
|
|
969
|
+
function pruneKeysByPrefix(table, prefix) {
|
|
970
|
+
const result = {};
|
|
971
|
+
for (const [key, value] of Object.entries(table)) {
|
|
972
|
+
if (!key.startsWith(prefix)) {
|
|
973
|
+
result[key] = value;
|
|
974
|
+
}
|
|
942
975
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
978
|
+
function isConfigObject4(value) {
|
|
979
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
980
|
+
}
|
|
981
|
+
function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
|
|
982
|
+
const result = { ...base };
|
|
983
|
+
const prefixMap = pruneByPrefix ?? {};
|
|
984
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
985
|
+
const current = result[key];
|
|
986
|
+
const prefix = prefixMap[key];
|
|
987
|
+
if (isConfigObject4(current) && isConfigObject4(value)) {
|
|
988
|
+
if (prefix) {
|
|
989
|
+
const pruned = pruneKeysByPrefix(current, prefix);
|
|
990
|
+
result[key] = { ...pruned, ...value };
|
|
991
|
+
} else {
|
|
992
|
+
result[key] = mergeWithPruneByPrefix(
|
|
993
|
+
current,
|
|
994
|
+
value,
|
|
995
|
+
prefixMap
|
|
996
|
+
);
|
|
949
997
|
}
|
|
950
|
-
} catch {
|
|
951
998
|
continue;
|
|
952
999
|
}
|
|
1000
|
+
result[key] = value;
|
|
953
1001
|
}
|
|
954
|
-
return
|
|
955
|
-
}
|
|
956
|
-
function renderCommandLine(command, args) {
|
|
957
|
-
return [command, ...args].map(quoteIfNeeded).join(" ").trim();
|
|
1002
|
+
return result;
|
|
958
1003
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1004
|
+
async function applyMutation(mutation, context, options) {
|
|
1005
|
+
switch (mutation.kind) {
|
|
1006
|
+
case "ensureDirectory":
|
|
1007
|
+
return applyEnsureDirectory(mutation, context, options);
|
|
1008
|
+
case "removeDirectory":
|
|
1009
|
+
return applyRemoveDirectory(mutation, context, options);
|
|
1010
|
+
case "removeFile":
|
|
1011
|
+
return applyRemoveFile(mutation, context, options);
|
|
1012
|
+
case "chmod":
|
|
1013
|
+
return applyChmod(mutation, context, options);
|
|
1014
|
+
case "backup":
|
|
1015
|
+
return applyBackup(mutation, context, options);
|
|
1016
|
+
case "configMerge":
|
|
1017
|
+
return applyConfigMerge(mutation, context, options);
|
|
1018
|
+
case "configPrune":
|
|
1019
|
+
return applyConfigPrune(mutation, context, options);
|
|
1020
|
+
case "configTransform":
|
|
1021
|
+
return applyConfigTransform(mutation, context, options);
|
|
1022
|
+
case "templateWrite":
|
|
1023
|
+
return applyTemplateWrite(mutation, context, options);
|
|
1024
|
+
case "templateMergeToml":
|
|
1025
|
+
return applyTemplateMerge(mutation, context, options, "toml");
|
|
1026
|
+
case "templateMergeJson":
|
|
1027
|
+
return applyTemplateMerge(mutation, context, options, "json");
|
|
1028
|
+
default: {
|
|
1029
|
+
const never = mutation;
|
|
1030
|
+
throw new Error(`Unknown mutation kind: ${never.kind}`);
|
|
1031
|
+
}
|
|
965
1032
|
}
|
|
966
|
-
return value;
|
|
967
|
-
}
|
|
968
|
-
function needsQuoting(value) {
|
|
969
|
-
return value.includes(" ") || value.includes(" ") || value.includes("\n");
|
|
970
1033
|
}
|
|
971
|
-
function
|
|
1034
|
+
async function applyEnsureDirectory(mutation, context, options) {
|
|
1035
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
1036
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1037
|
+
const details = {
|
|
1038
|
+
kind: mutation.kind,
|
|
1039
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1040
|
+
targetPath
|
|
1041
|
+
};
|
|
1042
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1043
|
+
if (!context.dryRun) {
|
|
1044
|
+
await context.fs.mkdir(targetPath, { recursive: true });
|
|
1045
|
+
}
|
|
972
1046
|
return {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
`$HOME/.local/bin/${binaryName}`,
|
|
980
|
-
`$HOME/.claude/local/bin/${binaryName}`
|
|
981
|
-
];
|
|
982
|
-
const detectors = [
|
|
983
|
-
{
|
|
984
|
-
command: "which",
|
|
985
|
-
args: [binaryName],
|
|
986
|
-
validate: (result) => result.exitCode === 0
|
|
987
|
-
},
|
|
988
|
-
{
|
|
989
|
-
command: "where",
|
|
990
|
-
args: [binaryName],
|
|
991
|
-
validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
|
|
992
|
-
},
|
|
993
|
-
// Check common installation paths using shell expansion for $HOME
|
|
994
|
-
{
|
|
995
|
-
command: "sh",
|
|
996
|
-
args: [
|
|
997
|
-
"-c",
|
|
998
|
-
commonPaths.map((p) => `test -f "${p}"`).join(" || ")
|
|
999
|
-
],
|
|
1000
|
-
validate: (result) => result.exitCode === 0
|
|
1001
|
-
}
|
|
1002
|
-
];
|
|
1003
|
-
for (const detector of detectors) {
|
|
1004
|
-
const result = await runCommand2(detector.command, detector.args);
|
|
1005
|
-
if (detector.validate(result)) {
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
throw new Error(`${binaryName} CLI binary not found on PATH.`);
|
|
1010
|
-
}
|
|
1047
|
+
outcome: {
|
|
1048
|
+
changed: !existed,
|
|
1049
|
+
effect: "mkdir",
|
|
1050
|
+
detail: existed ? "noop" : "create"
|
|
1051
|
+
},
|
|
1052
|
+
details
|
|
1011
1053
|
};
|
|
1012
1054
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
kind:
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
format: options.format,
|
|
1021
|
-
pruneByPrefix: options.pruneByPrefix,
|
|
1022
|
-
label: options.label
|
|
1055
|
+
async function applyRemoveDirectory(mutation, context, options) {
|
|
1056
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
1057
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1058
|
+
const details = {
|
|
1059
|
+
kind: mutation.kind,
|
|
1060
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1061
|
+
targetPath
|
|
1023
1062
|
};
|
|
1024
|
-
|
|
1025
|
-
|
|
1063
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1064
|
+
if (!existed) {
|
|
1065
|
+
return {
|
|
1066
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1067
|
+
details
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
if (typeof context.fs.rm !== "function") {
|
|
1071
|
+
return {
|
|
1072
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1073
|
+
details
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
if (mutation.force) {
|
|
1077
|
+
if (!context.dryRun) {
|
|
1078
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
1079
|
+
}
|
|
1080
|
+
return {
|
|
1081
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1082
|
+
details
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
const entries = await context.fs.readdir(targetPath);
|
|
1086
|
+
if (entries.length > 0) {
|
|
1087
|
+
return {
|
|
1088
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1089
|
+
details
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
if (!context.dryRun) {
|
|
1093
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
1094
|
+
}
|
|
1026
1095
|
return {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
shape: options.shape,
|
|
1030
|
-
format: options.format,
|
|
1031
|
-
onlyIf: options.onlyIf,
|
|
1032
|
-
label: options.label
|
|
1096
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1097
|
+
details
|
|
1033
1098
|
};
|
|
1034
1099
|
}
|
|
1035
|
-
function
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1100
|
+
async function applyRemoveFile(mutation, context, options) {
|
|
1101
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1102
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1103
|
+
const details = {
|
|
1104
|
+
kind: mutation.kind,
|
|
1105
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1106
|
+
targetPath
|
|
1042
1107
|
};
|
|
1108
|
+
try {
|
|
1109
|
+
const content = await context.fs.readFile(targetPath, "utf8");
|
|
1110
|
+
const trimmed = content.trim();
|
|
1111
|
+
if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
|
|
1112
|
+
return {
|
|
1113
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1114
|
+
details
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
if (mutation.whenEmpty && trimmed.length > 0) {
|
|
1118
|
+
return {
|
|
1119
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1120
|
+
details
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
if (!context.dryRun) {
|
|
1124
|
+
await context.fs.unlink(targetPath);
|
|
1125
|
+
}
|
|
1126
|
+
return {
|
|
1127
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1128
|
+
details
|
|
1129
|
+
};
|
|
1130
|
+
} catch (error2) {
|
|
1131
|
+
if (isNotFound(error2)) {
|
|
1132
|
+
return {
|
|
1133
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1134
|
+
details
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
throw error2;
|
|
1138
|
+
}
|
|
1043
1139
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
function ensureDirectory(options) {
|
|
1052
|
-
return {
|
|
1053
|
-
kind: "ensureDirectory",
|
|
1054
|
-
path: options.path,
|
|
1055
|
-
label: options.label
|
|
1140
|
+
async function applyChmod(mutation, context, options) {
|
|
1141
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1142
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1143
|
+
const details = {
|
|
1144
|
+
kind: mutation.kind,
|
|
1145
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1146
|
+
targetPath
|
|
1056
1147
|
};
|
|
1148
|
+
if (typeof context.fs.chmod !== "function") {
|
|
1149
|
+
return {
|
|
1150
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1151
|
+
details
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
try {
|
|
1155
|
+
const stat2 = await context.fs.stat(targetPath);
|
|
1156
|
+
const currentMode = typeof stat2.mode === "number" ? stat2.mode & 511 : null;
|
|
1157
|
+
if (currentMode === mutation.mode) {
|
|
1158
|
+
return {
|
|
1159
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1160
|
+
details
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
if (!context.dryRun) {
|
|
1164
|
+
await context.fs.chmod(targetPath, mutation.mode);
|
|
1165
|
+
}
|
|
1166
|
+
return {
|
|
1167
|
+
outcome: { changed: true, effect: "chmod", detail: "update" },
|
|
1168
|
+
details
|
|
1169
|
+
};
|
|
1170
|
+
} catch (error2) {
|
|
1171
|
+
if (isNotFound(error2)) {
|
|
1172
|
+
return {
|
|
1173
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1174
|
+
details
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
throw error2;
|
|
1178
|
+
}
|
|
1057
1179
|
}
|
|
1058
|
-
function
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1180
|
+
async function applyBackup(mutation, context, options) {
|
|
1181
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1182
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1183
|
+
const details = {
|
|
1184
|
+
kind: mutation.kind,
|
|
1185
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1186
|
+
targetPath
|
|
1065
1187
|
};
|
|
1066
|
-
|
|
1067
|
-
|
|
1188
|
+
const content = await readFileIfExists(context.fs, targetPath);
|
|
1189
|
+
if (content === null) {
|
|
1190
|
+
return {
|
|
1191
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1192
|
+
details
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
if (!context.dryRun) {
|
|
1196
|
+
const backupPath = `${targetPath}.backup-${createTimestamp()}`;
|
|
1197
|
+
await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
1198
|
+
}
|
|
1068
1199
|
return {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
force: options.force,
|
|
1072
|
-
label: options.label
|
|
1200
|
+
outcome: { changed: true, effect: "copy", detail: "backup" },
|
|
1201
|
+
details
|
|
1073
1202
|
};
|
|
1074
1203
|
}
|
|
1075
|
-
function
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
label:
|
|
1204
|
+
async function applyConfigMerge(mutation, context, options) {
|
|
1205
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1206
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1207
|
+
const details = {
|
|
1208
|
+
kind: mutation.kind,
|
|
1209
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1210
|
+
targetPath
|
|
1081
1211
|
};
|
|
1082
|
-
|
|
1083
|
-
|
|
1212
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1213
|
+
if (!formatName) {
|
|
1214
|
+
throw new Error(
|
|
1215
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
const format = getConfigFormat(formatName);
|
|
1219
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1220
|
+
let current;
|
|
1221
|
+
try {
|
|
1222
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1223
|
+
} catch {
|
|
1224
|
+
if (rawContent !== null) {
|
|
1225
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1226
|
+
}
|
|
1227
|
+
current = {};
|
|
1228
|
+
}
|
|
1229
|
+
const value = resolveValue(mutation.value, options);
|
|
1230
|
+
let merged;
|
|
1231
|
+
if (mutation.pruneByPrefix) {
|
|
1232
|
+
merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
|
|
1233
|
+
} else {
|
|
1234
|
+
merged = format.merge(current, value);
|
|
1235
|
+
}
|
|
1236
|
+
const serialized = format.serialize(merged);
|
|
1237
|
+
const changed = serialized !== rawContent;
|
|
1238
|
+
if (changed && !context.dryRun) {
|
|
1239
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1240
|
+
}
|
|
1084
1241
|
return {
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1242
|
+
outcome: {
|
|
1243
|
+
changed,
|
|
1244
|
+
effect: changed ? "write" : "none",
|
|
1245
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
1246
|
+
},
|
|
1247
|
+
details
|
|
1088
1248
|
};
|
|
1089
1249
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1105
|
-
}
|
|
1106
|
-
function parse3(content) {
|
|
1107
|
-
if (!content || content.trim() === "") {
|
|
1108
|
-
return {};
|
|
1250
|
+
async function applyConfigPrune(mutation, context, options) {
|
|
1251
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1252
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1253
|
+
const details = {
|
|
1254
|
+
kind: mutation.kind,
|
|
1255
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1256
|
+
targetPath
|
|
1257
|
+
};
|
|
1258
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1259
|
+
if (rawContent === null) {
|
|
1260
|
+
return {
|
|
1261
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1262
|
+
details
|
|
1263
|
+
};
|
|
1109
1264
|
}
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
if (errors.length > 0) {
|
|
1116
|
-
throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
|
|
1265
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1266
|
+
if (!formatName) {
|
|
1267
|
+
throw new Error(
|
|
1268
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1269
|
+
);
|
|
1117
1270
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1271
|
+
const format = getConfigFormat(formatName);
|
|
1272
|
+
let current;
|
|
1273
|
+
try {
|
|
1274
|
+
current = format.parse(rawContent);
|
|
1275
|
+
} catch {
|
|
1276
|
+
return {
|
|
1277
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1278
|
+
details
|
|
1279
|
+
};
|
|
1120
1280
|
}
|
|
1121
|
-
if (!
|
|
1122
|
-
|
|
1281
|
+
if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
|
|
1282
|
+
return {
|
|
1283
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1284
|
+
details
|
|
1285
|
+
};
|
|
1123
1286
|
}
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
if (
|
|
1134
|
-
|
|
1135
|
-
}
|
|
1136
|
-
const existing = result[key];
|
|
1137
|
-
if (isConfigObject(existing) && isConfigObject(value)) {
|
|
1138
|
-
result[key] = merge2(existing, value);
|
|
1139
|
-
continue;
|
|
1287
|
+
const shape = resolveValue(mutation.shape, options);
|
|
1288
|
+
const { changed, result } = format.prune(current, shape);
|
|
1289
|
+
if (!changed) {
|
|
1290
|
+
return {
|
|
1291
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1292
|
+
details
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
if (Object.keys(result).length === 0) {
|
|
1296
|
+
if (!context.dryRun) {
|
|
1297
|
+
await context.fs.unlink(targetPath);
|
|
1140
1298
|
}
|
|
1141
|
-
|
|
1299
|
+
return {
|
|
1300
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1301
|
+
details
|
|
1302
|
+
};
|
|
1142
1303
|
}
|
|
1143
|
-
|
|
1304
|
+
const serialized = format.serialize(result);
|
|
1305
|
+
if (!context.dryRun) {
|
|
1306
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1307
|
+
}
|
|
1308
|
+
return {
|
|
1309
|
+
outcome: { changed: true, effect: "write", detail: "update" },
|
|
1310
|
+
details
|
|
1311
|
+
};
|
|
1144
1312
|
}
|
|
1145
|
-
function
|
|
1146
|
-
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1313
|
+
async function applyConfigTransform(mutation, context, options) {
|
|
1314
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1315
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1316
|
+
const details = {
|
|
1317
|
+
kind: mutation.kind,
|
|
1318
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1319
|
+
targetPath
|
|
1320
|
+
};
|
|
1321
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1322
|
+
if (!formatName) {
|
|
1323
|
+
throw new Error(
|
|
1324
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
const format = getConfigFormat(formatName);
|
|
1328
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1329
|
+
let current;
|
|
1330
|
+
try {
|
|
1331
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1332
|
+
} catch {
|
|
1333
|
+
if (rawContent !== null) {
|
|
1334
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1151
1335
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1336
|
+
current = {};
|
|
1337
|
+
}
|
|
1338
|
+
const { content: transformed, changed } = mutation.transform(current, options);
|
|
1339
|
+
if (!changed) {
|
|
1340
|
+
return {
|
|
1341
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1342
|
+
details
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (transformed === null) {
|
|
1346
|
+
if (rawContent === null) {
|
|
1347
|
+
return {
|
|
1348
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1349
|
+
details
|
|
1350
|
+
};
|
|
1157
1351
|
}
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1160
|
-
current,
|
|
1161
|
-
pattern
|
|
1162
|
-
);
|
|
1163
|
-
if (childChanged) {
|
|
1164
|
-
changed = true;
|
|
1165
|
-
}
|
|
1166
|
-
if (Object.keys(childResult).length === 0) {
|
|
1167
|
-
delete result[key];
|
|
1168
|
-
} else {
|
|
1169
|
-
result[key] = childResult;
|
|
1170
|
-
}
|
|
1171
|
-
continue;
|
|
1352
|
+
if (!context.dryRun) {
|
|
1353
|
+
await context.fs.unlink(targetPath);
|
|
1172
1354
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1355
|
+
return {
|
|
1356
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
1357
|
+
details
|
|
1358
|
+
};
|
|
1175
1359
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1360
|
+
const serialized = format.serialize(transformed);
|
|
1361
|
+
if (!context.dryRun) {
|
|
1362
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1363
|
+
}
|
|
1364
|
+
return {
|
|
1365
|
+
outcome: {
|
|
1366
|
+
changed: true,
|
|
1367
|
+
effect: "write",
|
|
1368
|
+
detail: rawContent === null ? "create" : "update"
|
|
1369
|
+
},
|
|
1370
|
+
details
|
|
1371
|
+
};
|
|
1189
1372
|
}
|
|
1190
|
-
function
|
|
1191
|
-
if (!
|
|
1192
|
-
|
|
1373
|
+
async function applyTemplateWrite(mutation, context, options) {
|
|
1374
|
+
if (!context.templates) {
|
|
1375
|
+
throw new Error(
|
|
1376
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
1377
|
+
);
|
|
1193
1378
|
}
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1196
|
-
|
|
1379
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1380
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1381
|
+
const details = {
|
|
1382
|
+
kind: mutation.kind,
|
|
1383
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1384
|
+
targetPath
|
|
1385
|
+
};
|
|
1386
|
+
const template = await context.templates(mutation.templateId);
|
|
1387
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
1388
|
+
const rendered = Mustache.render(template, templateContext);
|
|
1389
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
1390
|
+
if (!context.dryRun) {
|
|
1391
|
+
await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
|
|
1197
1392
|
}
|
|
1198
|
-
return
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1393
|
+
return {
|
|
1394
|
+
outcome: {
|
|
1395
|
+
changed: true,
|
|
1396
|
+
effect: "write",
|
|
1397
|
+
detail: existed ? "update" : "create"
|
|
1398
|
+
},
|
|
1399
|
+
details
|
|
1400
|
+
};
|
|
1204
1401
|
}
|
|
1205
|
-
function
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
const existing = result[key];
|
|
1212
|
-
if (isConfigObject2(existing) && isConfigObject2(value)) {
|
|
1213
|
-
result[key] = merge3(existing, value);
|
|
1214
|
-
continue;
|
|
1215
|
-
}
|
|
1216
|
-
result[key] = value;
|
|
1402
|
+
async function applyTemplateMerge(mutation, context, options, formatName) {
|
|
1403
|
+
if (!context.templates) {
|
|
1404
|
+
throw new Error(
|
|
1405
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
1406
|
+
);
|
|
1217
1407
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
}
|
|
1246
|
-
continue;
|
|
1408
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
1409
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
1410
|
+
const details = {
|
|
1411
|
+
kind: mutation.kind,
|
|
1412
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1413
|
+
targetPath
|
|
1414
|
+
};
|
|
1415
|
+
const format = getConfigFormat(formatName);
|
|
1416
|
+
const template = await context.templates(mutation.templateId);
|
|
1417
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
1418
|
+
const rendered = Mustache.render(template, templateContext);
|
|
1419
|
+
let templateDoc;
|
|
1420
|
+
try {
|
|
1421
|
+
templateDoc = format.parse(rendered);
|
|
1422
|
+
} catch (error2) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
`Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error2}`,
|
|
1425
|
+
{ cause: error2 }
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1429
|
+
let current;
|
|
1430
|
+
try {
|
|
1431
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
1432
|
+
} catch {
|
|
1433
|
+
if (rawContent !== null) {
|
|
1434
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1247
1435
|
}
|
|
1248
|
-
|
|
1249
|
-
changed = true;
|
|
1436
|
+
current = {};
|
|
1250
1437
|
}
|
|
1251
|
-
|
|
1438
|
+
const merged = format.merge(current, templateDoc);
|
|
1439
|
+
const serialized = format.serialize(merged);
|
|
1440
|
+
const changed = serialized !== rawContent;
|
|
1441
|
+
if (changed && !context.dryRun) {
|
|
1442
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
outcome: {
|
|
1446
|
+
changed,
|
|
1447
|
+
effect: changed ? "write" : "none",
|
|
1448
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
1449
|
+
},
|
|
1450
|
+
details
|
|
1451
|
+
};
|
|
1252
1452
|
}
|
|
1253
|
-
var tomlFormat = {
|
|
1254
|
-
parse: parse4,
|
|
1255
|
-
serialize: serialize2,
|
|
1256
|
-
merge: merge3,
|
|
1257
|
-
prune: prune3
|
|
1258
|
-
};
|
|
1259
1453
|
|
|
1260
|
-
// packages/config-mutations/src/
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1454
|
+
// packages/config-mutations/src/execution/run-mutations.ts
|
|
1455
|
+
async function runMutations(mutations, context, options) {
|
|
1456
|
+
const effects = [];
|
|
1457
|
+
let anyChanged = false;
|
|
1458
|
+
const resolverOptions = options ?? {};
|
|
1459
|
+
for (const mutation of mutations) {
|
|
1460
|
+
const { outcome } = await executeMutation(
|
|
1461
|
+
mutation,
|
|
1462
|
+
context,
|
|
1463
|
+
resolverOptions
|
|
1464
|
+
);
|
|
1465
|
+
effects.push(outcome);
|
|
1466
|
+
if (outcome.changed) {
|
|
1467
|
+
anyChanged = true;
|
|
1468
|
+
}
|
|
1272
1469
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1470
|
+
return {
|
|
1471
|
+
changed: anyChanged,
|
|
1472
|
+
effects
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
async function executeMutation(mutation, context, options) {
|
|
1476
|
+
context.observers?.onStart?.({
|
|
1477
|
+
kind: mutation.kind,
|
|
1478
|
+
label: mutation.label ?? mutation.kind,
|
|
1479
|
+
targetPath: void 0
|
|
1480
|
+
// Will be resolved during apply
|
|
1481
|
+
});
|
|
1482
|
+
try {
|
|
1483
|
+
const { outcome, details } = await applyMutation(mutation, context, options);
|
|
1484
|
+
context.observers?.onComplete?.(details, outcome);
|
|
1485
|
+
return { outcome, details };
|
|
1486
|
+
} catch (error2) {
|
|
1487
|
+
context.observers?.onError?.(
|
|
1488
|
+
{
|
|
1489
|
+
kind: mutation.kind,
|
|
1490
|
+
label: mutation.label ?? mutation.kind,
|
|
1491
|
+
targetPath: void 0
|
|
1492
|
+
},
|
|
1493
|
+
error2
|
|
1494
|
+
);
|
|
1495
|
+
throw error2;
|
|
1275
1496
|
}
|
|
1276
|
-
return parsed;
|
|
1277
1497
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1498
|
+
|
|
1499
|
+
// packages/config-mutations/src/template/render.ts
|
|
1500
|
+
import Mustache2 from "mustache";
|
|
1501
|
+
var originalEscape = Mustache2.escape;
|
|
1502
|
+
|
|
1503
|
+
// packages/poe-code-config/src/store.ts
|
|
1504
|
+
var EMPTY_DOCUMENT = `${JSON.stringify({}, null, 2)}
|
|
1505
|
+
`;
|
|
1506
|
+
|
|
1507
|
+
// packages/poe-code-config/src/inspect.ts
|
|
1508
|
+
import path9 from "node:path";
|
|
1509
|
+
var EMPTY_DOCUMENT2 = `${JSON.stringify({}, null, 2)}
|
|
1281
1510
|
`;
|
|
1511
|
+
|
|
1512
|
+
// packages/poe-code-config/src/state/index.ts
|
|
1513
|
+
import os2 from "node:os";
|
|
1514
|
+
|
|
1515
|
+
// packages/poe-code-config/src/state/jobs.ts
|
|
1516
|
+
import path10 from "node:path";
|
|
1517
|
+
|
|
1518
|
+
// packages/poe-code-config/src/state/fs.ts
|
|
1519
|
+
import * as nodeFs2 from "node:fs/promises";
|
|
1520
|
+
|
|
1521
|
+
// packages/poe-code-config/src/state/templates.ts
|
|
1522
|
+
import path11 from "node:path";
|
|
1523
|
+
|
|
1524
|
+
// packages/agent-harness-tools/src/execution-env.ts
|
|
1525
|
+
var executionEnvFactories = /* @__PURE__ */ new Map();
|
|
1526
|
+
function registerExecutionEnvFactory(factory) {
|
|
1527
|
+
executionEnvFactories.set(factory.type, factory);
|
|
1282
1528
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1529
|
+
|
|
1530
|
+
// packages/agent-harness-tools/src/workspace-transfer.ts
|
|
1531
|
+
import { createHash } from "node:crypto";
|
|
1532
|
+
import { promises as nodeFs3 } from "node:fs";
|
|
1533
|
+
import path12 from "node:path";
|
|
1534
|
+
|
|
1535
|
+
// packages/process-runner/src/docker/context.ts
|
|
1536
|
+
import { execSync } from "node:child_process";
|
|
1537
|
+
function detectContext() {
|
|
1538
|
+
try {
|
|
1539
|
+
const output = execSync("colima list --json", {
|
|
1540
|
+
encoding: "utf-8",
|
|
1541
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
1542
|
+
});
|
|
1543
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
1544
|
+
for (const line of lines) {
|
|
1545
|
+
const profile = JSON.parse(line);
|
|
1546
|
+
if (profile.status === "Running" && profile.runtime === "docker") {
|
|
1547
|
+
const name = profile.name ?? profile.profile;
|
|
1548
|
+
if (!name) {
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
return name === "default" ? "colima" : `colima-${name}`;
|
|
1552
|
+
}
|
|
1293
1553
|
}
|
|
1294
|
-
|
|
1554
|
+
} catch {
|
|
1555
|
+
return null;
|
|
1295
1556
|
}
|
|
1296
|
-
return
|
|
1557
|
+
return null;
|
|
1297
1558
|
}
|
|
1298
|
-
function
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
for (const [key, pattern] of Object.entries(shape)) {
|
|
1302
|
-
if (!(key in result)) {
|
|
1303
|
-
continue;
|
|
1304
|
-
}
|
|
1305
|
-
const current = result[key];
|
|
1306
|
-
if (isConfigObject3(pattern) && Object.keys(pattern).length === 0) {
|
|
1307
|
-
delete result[key];
|
|
1308
|
-
changed = true;
|
|
1309
|
-
continue;
|
|
1310
|
-
}
|
|
1311
|
-
if (isConfigObject3(pattern) && isConfigObject3(current)) {
|
|
1312
|
-
const { changed: childChanged, result: childResult } = prune4(current, pattern);
|
|
1313
|
-
if (childChanged) {
|
|
1314
|
-
changed = true;
|
|
1315
|
-
}
|
|
1316
|
-
if (Object.keys(childResult).length === 0) {
|
|
1317
|
-
delete result[key];
|
|
1318
|
-
} else {
|
|
1319
|
-
result[key] = childResult;
|
|
1320
|
-
}
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
delete result[key];
|
|
1324
|
-
changed = true;
|
|
1559
|
+
function buildContextArgs(engine, context) {
|
|
1560
|
+
if (engine === "docker" && context) {
|
|
1561
|
+
return ["--context", context];
|
|
1325
1562
|
}
|
|
1326
|
-
return
|
|
1563
|
+
return [];
|
|
1327
1564
|
}
|
|
1328
|
-
var yamlFormat = {
|
|
1329
|
-
parse: parse5,
|
|
1330
|
-
serialize: serialize3,
|
|
1331
|
-
merge: merge4,
|
|
1332
|
-
prune: prune4
|
|
1333
|
-
};
|
|
1334
1565
|
|
|
1335
|
-
// packages/
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
};
|
|
1341
|
-
var extensionMap = {
|
|
1342
|
-
".json": "json",
|
|
1343
|
-
".toml": "toml",
|
|
1344
|
-
".yaml": "yaml",
|
|
1345
|
-
".yml": "yaml"
|
|
1346
|
-
};
|
|
1347
|
-
function getConfigFormat(pathOrFormat) {
|
|
1348
|
-
if (pathOrFormat in formatRegistry) {
|
|
1349
|
-
return formatRegistry[pathOrFormat];
|
|
1566
|
+
// packages/process-runner/src/docker/engine.ts
|
|
1567
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
1568
|
+
function detectEngine() {
|
|
1569
|
+
if (isEngineAvailable("docker")) {
|
|
1570
|
+
return "docker";
|
|
1350
1571
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
if (!formatName) {
|
|
1354
|
-
throw new Error(
|
|
1355
|
-
`Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
|
|
1356
|
-
);
|
|
1572
|
+
if (isEngineAvailable("podman")) {
|
|
1573
|
+
return "podman";
|
|
1357
1574
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
const ext = getExtension(path5);
|
|
1362
|
-
return extensionMap[ext];
|
|
1575
|
+
throw new Error(
|
|
1576
|
+
"No container engine found. Please install Docker or Podman:\n - Docker Desktop: https://www.docker.com/products/docker-desktop\n - Colima (macOS): brew install colima && colima start\n - Podman: https://podman.io/docs/installation"
|
|
1577
|
+
);
|
|
1363
1578
|
}
|
|
1364
|
-
function
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1579
|
+
function isEngineAvailable(engine) {
|
|
1580
|
+
try {
|
|
1581
|
+
execSync2(`${engine} --version`, {
|
|
1582
|
+
stdio: "ignore"
|
|
1583
|
+
});
|
|
1584
|
+
return true;
|
|
1585
|
+
} catch {
|
|
1586
|
+
return false;
|
|
1368
1587
|
}
|
|
1369
|
-
return path5.slice(lastDot).toLowerCase();
|
|
1370
1588
|
}
|
|
1371
1589
|
|
|
1372
|
-
// packages/
|
|
1373
|
-
import
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1590
|
+
// packages/process-runner/src/docker/docker-runner.ts
|
|
1591
|
+
import * as childProcess from "node:child_process";
|
|
1592
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1593
|
+
|
|
1594
|
+
// packages/process-runner/src/docker/args.ts
|
|
1595
|
+
import path13 from "node:path";
|
|
1596
|
+
function buildDockerRunArgs(input) {
|
|
1597
|
+
const args = [input.engine];
|
|
1598
|
+
if (input.engine === "docker" && input.context) {
|
|
1599
|
+
args.push("--context", input.context);
|
|
1377
1600
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1601
|
+
args.push("run");
|
|
1602
|
+
if (input.rm) {
|
|
1603
|
+
args.push("--rm");
|
|
1380
1604
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
remainder = remainder.slice(1);
|
|
1384
|
-
} else if (remainder.startsWith(".")) {
|
|
1385
|
-
remainder = remainder.slice(1);
|
|
1386
|
-
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
1387
|
-
remainder = remainder.slice(1);
|
|
1388
|
-
}
|
|
1605
|
+
if (input.detached) {
|
|
1606
|
+
args.push("-d");
|
|
1389
1607
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
function validateHomePath(targetPath) {
|
|
1393
|
-
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
1394
|
-
throw new Error("Target path must be a non-empty string.");
|
|
1608
|
+
if (input.interactive) {
|
|
1609
|
+
args.push("-i");
|
|
1395
1610
|
}
|
|
1396
|
-
if (
|
|
1397
|
-
|
|
1398
|
-
`All target paths must be home-relative (start with ~). Received: "${targetPath}"`
|
|
1399
|
-
);
|
|
1611
|
+
if (input.tty) {
|
|
1612
|
+
args.push("-t");
|
|
1400
1613
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
const expanded = expandHome(rawPath, homeDir);
|
|
1405
|
-
if (!pathMapper) {
|
|
1406
|
-
return expanded;
|
|
1614
|
+
args.push("--name", input.containerName);
|
|
1615
|
+
if (input.cwd !== void 0) {
|
|
1616
|
+
args.push("-w", input.cwd);
|
|
1407
1617
|
}
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1618
|
+
for (const [key, value] of Object.entries(input.env ?? {})) {
|
|
1619
|
+
args.push("-e", `${key}=${value}`);
|
|
1620
|
+
}
|
|
1621
|
+
for (const mount of input.mounts) {
|
|
1622
|
+
const volume = `${path13.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
|
|
1623
|
+
args.push("-v", volume);
|
|
1624
|
+
}
|
|
1625
|
+
for (const port of input.ports) {
|
|
1626
|
+
const mapping = `${port.host}:${port.container}${port.protocol === void 0 || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
|
|
1627
|
+
args.push("-p", mapping);
|
|
1628
|
+
}
|
|
1629
|
+
if (input.network !== void 0) {
|
|
1630
|
+
args.push("--network", input.network);
|
|
1631
|
+
}
|
|
1632
|
+
args.push(...input.extraArgs, input.image, input.command, ...input.args);
|
|
1633
|
+
return args;
|
|
1414
1634
|
}
|
|
1415
1635
|
|
|
1416
|
-
// packages/
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1636
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
1637
|
+
import { createHash as createHash2, randomBytes as randomBytes3 } from "node:crypto";
|
|
1638
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
1639
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
1640
|
+
import { tmpdir } from "node:os";
|
|
1641
|
+
import path14 from "node:path";
|
|
1642
|
+
|
|
1643
|
+
// packages/process-runner/src/host/host-runner.ts
|
|
1644
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
1645
|
+
function createHostRunner(options = {}) {
|
|
1646
|
+
const detached = options.detached === true;
|
|
1647
|
+
return {
|
|
1648
|
+
name: "host",
|
|
1649
|
+
exec(spec) {
|
|
1650
|
+
const stdinMode = spec.stdin ?? "ignore";
|
|
1651
|
+
const stdoutMode = spec.stdout ?? "pipe";
|
|
1652
|
+
const stderrMode = spec.stderr ?? "pipe";
|
|
1653
|
+
const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
|
|
1654
|
+
const child = spawnChildProcess(spec.command, spec.args ?? [], {
|
|
1655
|
+
cwd: spec.cwd,
|
|
1656
|
+
env: spec.env,
|
|
1657
|
+
stdio,
|
|
1658
|
+
...detached ? { detached: true } : {}
|
|
1659
|
+
});
|
|
1660
|
+
if (detached) {
|
|
1661
|
+
child.unref();
|
|
1662
|
+
}
|
|
1663
|
+
const kill = (signal) => {
|
|
1664
|
+
if (detached && process.platform !== "win32" && child.pid !== void 0) {
|
|
1665
|
+
process.kill(-child.pid, signal);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
child.kill(signal);
|
|
1669
|
+
};
|
|
1670
|
+
let settled = false;
|
|
1671
|
+
let resolveResult = null;
|
|
1672
|
+
const result = new Promise((resolve2) => {
|
|
1673
|
+
resolveResult = resolve2;
|
|
1674
|
+
});
|
|
1675
|
+
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
1676
|
+
kill("SIGTERM");
|
|
1677
|
+
});
|
|
1678
|
+
child.once("close", (code) => {
|
|
1679
|
+
if (settled) return;
|
|
1680
|
+
settled = true;
|
|
1681
|
+
cleanupAbort();
|
|
1682
|
+
resolveResult?.({ exitCode: code ?? 1 });
|
|
1683
|
+
});
|
|
1684
|
+
child.once("error", () => {
|
|
1685
|
+
if (settled) return;
|
|
1686
|
+
settled = true;
|
|
1687
|
+
cleanupAbort();
|
|
1688
|
+
resolveResult?.({ exitCode: 1 });
|
|
1689
|
+
});
|
|
1690
|
+
return {
|
|
1691
|
+
pid: child.pid ?? null,
|
|
1692
|
+
stdin: child.stdin,
|
|
1693
|
+
stdout: child.stdout,
|
|
1694
|
+
stderr: child.stderr,
|
|
1695
|
+
result,
|
|
1696
|
+
kill
|
|
1697
|
+
};
|
|
1426
1698
|
}
|
|
1427
|
-
|
|
1428
|
-
}
|
|
1699
|
+
};
|
|
1429
1700
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
} catch (error2) {
|
|
1435
|
-
if (isNotFound(error2)) {
|
|
1436
|
-
return false;
|
|
1437
|
-
}
|
|
1438
|
-
throw error2;
|
|
1701
|
+
function bindAbortSignal(signal, onAbort) {
|
|
1702
|
+
if (signal === void 0) {
|
|
1703
|
+
return () => {
|
|
1704
|
+
};
|
|
1439
1705
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1706
|
+
if (signal.aborted) {
|
|
1707
|
+
onAbort();
|
|
1708
|
+
return () => {
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1712
|
+
return () => {
|
|
1713
|
+
signal.removeEventListener("abort", onAbort);
|
|
1714
|
+
};
|
|
1443
1715
|
}
|
|
1444
1716
|
|
|
1445
|
-
// packages/
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1717
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
1718
|
+
var containerCommand = ["sh", "-c", "while :; do sleep 3600; done"];
|
|
1719
|
+
var dockerExecutionEnvFactory = {
|
|
1720
|
+
type: "docker",
|
|
1721
|
+
supportsDetach: true,
|
|
1722
|
+
async open(spec) {
|
|
1723
|
+
const runtime = parseDockerRuntime(spec.runtime);
|
|
1724
|
+
const runner = spec.hostRunner ?? createHostRunner();
|
|
1725
|
+
const engine = runtime.engine ?? detectEngine();
|
|
1726
|
+
const context = detectContext();
|
|
1727
|
+
const image = await resolveImage({
|
|
1728
|
+
spec,
|
|
1729
|
+
runtime,
|
|
1730
|
+
runner,
|
|
1731
|
+
engine,
|
|
1732
|
+
context
|
|
1733
|
+
});
|
|
1734
|
+
const containerName = createContainerName();
|
|
1735
|
+
const runArgs = buildDockerRunArgs({
|
|
1736
|
+
engine,
|
|
1737
|
+
context,
|
|
1738
|
+
image,
|
|
1739
|
+
command: containerCommand[0],
|
|
1740
|
+
args: containerCommand.slice(1),
|
|
1741
|
+
cwd: void 0,
|
|
1742
|
+
env: void 0,
|
|
1743
|
+
mounts: runtime.mounts ?? [],
|
|
1744
|
+
ports: [],
|
|
1745
|
+
network: runtime.network,
|
|
1746
|
+
containerName,
|
|
1747
|
+
detached: true,
|
|
1748
|
+
interactive: true,
|
|
1749
|
+
tty: false,
|
|
1750
|
+
rm: false,
|
|
1751
|
+
extraArgs: runtime.extra_args ?? []
|
|
1752
|
+
});
|
|
1753
|
+
const [command, ...args] = runArgs;
|
|
1754
|
+
const id = (await runAndRead(runner, { command, args, stdout: "pipe", stderr: "pipe" })).trim();
|
|
1755
|
+
return createDockerEnv({
|
|
1756
|
+
id,
|
|
1757
|
+
spec,
|
|
1758
|
+
runner,
|
|
1759
|
+
engine,
|
|
1760
|
+
context
|
|
1761
|
+
});
|
|
1762
|
+
},
|
|
1763
|
+
async attach(envId) {
|
|
1764
|
+
const engine = detectEngine();
|
|
1765
|
+
return createDockerEnv({
|
|
1766
|
+
id: envId,
|
|
1767
|
+
spec: createAttachedSpec(),
|
|
1768
|
+
runner: createHostRunner(),
|
|
1769
|
+
engine,
|
|
1770
|
+
context: detectContext()
|
|
1771
|
+
});
|
|
1449
1772
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1773
|
+
};
|
|
1774
|
+
function createDockerEnv(input) {
|
|
1775
|
+
const containerRef = input.id;
|
|
1776
|
+
return {
|
|
1777
|
+
id: containerRef,
|
|
1778
|
+
job: null,
|
|
1779
|
+
async uploadWorkspace() {
|
|
1780
|
+
const tempDir = mkdtempSync(path14.join(tmpdir(), "poe-docker-upload-"));
|
|
1781
|
+
const archivePath = path14.join(tempDir, "workspace.tar");
|
|
1782
|
+
try {
|
|
1783
|
+
const excludeArgs = input.spec.uploadIgnoreFiles.flatMap((ignored) => [
|
|
1784
|
+
"--exclude",
|
|
1785
|
+
ignored
|
|
1786
|
+
]);
|
|
1787
|
+
const tarArgs = [...excludeArgs, "-cf", archivePath, "-C", input.spec.cwd, "."];
|
|
1788
|
+
await runOrThrow(input.runner, {
|
|
1789
|
+
command: "tar",
|
|
1790
|
+
args: tarArgs,
|
|
1791
|
+
stdout: "pipe",
|
|
1792
|
+
stderr: "pipe"
|
|
1793
|
+
});
|
|
1794
|
+
await runOrThrow(input.runner, {
|
|
1795
|
+
command: input.engine,
|
|
1796
|
+
args: [
|
|
1797
|
+
...buildContextArgs(input.engine, input.context),
|
|
1798
|
+
"cp",
|
|
1799
|
+
archivePath,
|
|
1800
|
+
`${containerRef}:/tmp/poe-workspace-upload.tar`
|
|
1801
|
+
],
|
|
1802
|
+
stdout: "pipe",
|
|
1803
|
+
stderr: "pipe"
|
|
1804
|
+
});
|
|
1805
|
+
await runOrThrow(input.runner, {
|
|
1806
|
+
command: input.engine,
|
|
1807
|
+
args: [
|
|
1808
|
+
...buildContextArgs(input.engine, input.context),
|
|
1809
|
+
"exec",
|
|
1810
|
+
containerRef,
|
|
1811
|
+
"sh",
|
|
1812
|
+
"-c",
|
|
1813
|
+
`mkdir -p ${shellQuote(input.spec.cwd)} && tar -xf /tmp/poe-workspace-upload.tar -C ${shellQuote(input.spec.cwd)}`
|
|
1814
|
+
],
|
|
1815
|
+
stdout: "pipe",
|
|
1816
|
+
stderr: "pipe"
|
|
1817
|
+
});
|
|
1818
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
1819
|
+
} finally {
|
|
1820
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1821
|
+
}
|
|
1822
|
+
},
|
|
1823
|
+
async downloadWorkspace(opts) {
|
|
1824
|
+
const tempDir = mkdtempSync(path14.join(tmpdir(), "poe-docker-download-"));
|
|
1825
|
+
const archivePath = path14.join(tempDir, "workspace.tar");
|
|
1826
|
+
try {
|
|
1827
|
+
await runOrThrow(input.runner, {
|
|
1828
|
+
command: input.engine,
|
|
1829
|
+
args: [
|
|
1830
|
+
...buildContextArgs(input.engine, input.context),
|
|
1831
|
+
"exec",
|
|
1832
|
+
containerRef,
|
|
1833
|
+
"sh",
|
|
1834
|
+
"-c",
|
|
1835
|
+
`tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote(input.spec.cwd)} .`
|
|
1836
|
+
],
|
|
1837
|
+
stdout: "pipe",
|
|
1838
|
+
stderr: "pipe"
|
|
1839
|
+
});
|
|
1840
|
+
await runOrThrow(input.runner, {
|
|
1841
|
+
command: input.engine,
|
|
1842
|
+
args: [
|
|
1843
|
+
...buildContextArgs(input.engine, input.context),
|
|
1844
|
+
"cp",
|
|
1845
|
+
`${containerRef}:/tmp/poe-workspace-download.tar`,
|
|
1846
|
+
archivePath
|
|
1847
|
+
],
|
|
1848
|
+
stdout: "pipe",
|
|
1849
|
+
stderr: "pipe"
|
|
1850
|
+
});
|
|
1851
|
+
const extractMode = opts.conflictPolicy === "refuse" ? "-xkf" : "-xf";
|
|
1852
|
+
await runOrThrow(input.runner, {
|
|
1853
|
+
command: "tar",
|
|
1854
|
+
args: [extractMode, archivePath, "-C", input.spec.cwd],
|
|
1855
|
+
stdout: "pipe",
|
|
1856
|
+
stderr: "pipe"
|
|
1857
|
+
});
|
|
1858
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
1859
|
+
} finally {
|
|
1860
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1863
|
+
exec(spec) {
|
|
1864
|
+
return input.runner.exec({
|
|
1865
|
+
command: input.engine,
|
|
1866
|
+
args: [
|
|
1867
|
+
...buildContextArgs(input.engine, input.context),
|
|
1868
|
+
"exec",
|
|
1869
|
+
...spec.stdin === "pipe" || spec.stdin === "inherit" ? ["-i"] : [],
|
|
1870
|
+
...spec.tty === true ? ["-t"] : [],
|
|
1871
|
+
...spec.cwd !== void 0 ? ["-w", spec.cwd] : [],
|
|
1872
|
+
...buildEnvArgs(spec.env),
|
|
1873
|
+
containerRef,
|
|
1874
|
+
spec.command,
|
|
1875
|
+
...spec.args ?? []
|
|
1876
|
+
],
|
|
1877
|
+
stdin: spec.stdin,
|
|
1878
|
+
stdout: spec.stdout,
|
|
1879
|
+
stderr: spec.stderr,
|
|
1880
|
+
tty: spec.tty
|
|
1881
|
+
});
|
|
1882
|
+
},
|
|
1883
|
+
async detach() {
|
|
1884
|
+
return createContainerJob(containerRef, input.runner, input.engine, input.context);
|
|
1885
|
+
},
|
|
1886
|
+
shell() {
|
|
1887
|
+
const shellSpec = input.spec.shellSpec;
|
|
1888
|
+
return this.exec({
|
|
1889
|
+
command: shellSpec?.command ?? input.spec.env.SHELL ?? "sh",
|
|
1890
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
1891
|
+
cwd: input.spec.cwd,
|
|
1892
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env,
|
|
1893
|
+
stdin: "inherit",
|
|
1894
|
+
stdout: "inherit",
|
|
1895
|
+
stderr: "inherit",
|
|
1896
|
+
tty: true
|
|
1897
|
+
});
|
|
1898
|
+
},
|
|
1899
|
+
async close() {
|
|
1900
|
+
await runOrThrow(input.runner, {
|
|
1901
|
+
command: input.engine,
|
|
1902
|
+
args: [...buildContextArgs(input.engine, input.context), "rm", "-f", containerRef],
|
|
1903
|
+
stdout: "pipe",
|
|
1904
|
+
stderr: "pipe"
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1455
1908
|
}
|
|
1456
|
-
async function
|
|
1457
|
-
|
|
1458
|
-
|
|
1909
|
+
async function resolveImage(input) {
|
|
1910
|
+
if (input.runtime.image !== void 0) {
|
|
1911
|
+
return input.runtime.image;
|
|
1912
|
+
}
|
|
1913
|
+
const dockerfilePath = path14.resolve(
|
|
1914
|
+
input.spec.cwd,
|
|
1915
|
+
input.runtime.dockerfile ?? path14.join(".poe-code", "Dockerfile")
|
|
1916
|
+
);
|
|
1917
|
+
const buildContext = path14.resolve(input.spec.cwd, input.runtime.build_context ?? ".");
|
|
1918
|
+
const dockerfileBytes = await readFile2(dockerfilePath);
|
|
1919
|
+
const hash = hashDockerTemplate(dockerfileBytes, input.runtime.build_args ?? {});
|
|
1920
|
+
const cached2 = await input.spec.state?.templates.get("docker", hash);
|
|
1921
|
+
if (cached2?.image !== void 0) {
|
|
1922
|
+
return cached2.image;
|
|
1923
|
+
}
|
|
1924
|
+
const image = `poe-code/local:${hash}`;
|
|
1925
|
+
await buildImage({
|
|
1926
|
+
runner: input.runner,
|
|
1927
|
+
engine: input.engine,
|
|
1928
|
+
context: input.context,
|
|
1929
|
+
image,
|
|
1930
|
+
dockerfilePath,
|
|
1931
|
+
buildContext,
|
|
1932
|
+
buildArgs: input.runtime.build_args ?? {}
|
|
1933
|
+
});
|
|
1934
|
+
await input.spec.state?.templates.put("docker", {
|
|
1935
|
+
hash,
|
|
1936
|
+
image,
|
|
1937
|
+
runtime_type: "docker",
|
|
1938
|
+
dockerfile_path: dockerfilePath,
|
|
1939
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1940
|
+
});
|
|
1941
|
+
return image;
|
|
1942
|
+
}
|
|
1943
|
+
function hashDockerTemplate(dockerfileBytes, buildArgs) {
|
|
1944
|
+
const hash = createHash2("sha256");
|
|
1945
|
+
hash.update(dockerfileBytes);
|
|
1946
|
+
hash.update("\0");
|
|
1947
|
+
for (const [key, value] of sortedBuildArgs(buildArgs)) {
|
|
1948
|
+
hash.update(key);
|
|
1949
|
+
hash.update("=");
|
|
1950
|
+
hash.update(value);
|
|
1951
|
+
hash.update("\0");
|
|
1952
|
+
}
|
|
1953
|
+
return hash.digest("hex");
|
|
1954
|
+
}
|
|
1955
|
+
async function buildImage(input) {
|
|
1956
|
+
await runOrThrow(input.runner, {
|
|
1957
|
+
command: input.engine,
|
|
1958
|
+
args: [
|
|
1959
|
+
...buildContextArgs(input.engine, input.context),
|
|
1960
|
+
"build",
|
|
1961
|
+
"--tag",
|
|
1962
|
+
input.image,
|
|
1963
|
+
"-f",
|
|
1964
|
+
input.dockerfilePath,
|
|
1965
|
+
...sortedBuildArgs(input.buildArgs).flatMap(([key, value]) => [
|
|
1966
|
+
"--build-arg",
|
|
1967
|
+
`${key}=${value}`
|
|
1968
|
+
]),
|
|
1969
|
+
input.buildContext
|
|
1970
|
+
],
|
|
1971
|
+
stdout: "pipe",
|
|
1972
|
+
stderr: "pipe"
|
|
1973
|
+
});
|
|
1459
1974
|
}
|
|
1460
|
-
function
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
case "backup":
|
|
1468
|
-
return `Backup ${displayPath}`;
|
|
1469
|
-
case "templateWrite":
|
|
1470
|
-
return `Write ${displayPath}`;
|
|
1471
|
-
case "chmod":
|
|
1472
|
-
return `Set permissions on ${displayPath}`;
|
|
1473
|
-
case "removeFile":
|
|
1474
|
-
return `Remove ${displayPath}`;
|
|
1475
|
-
case "configMerge":
|
|
1476
|
-
case "configPrune":
|
|
1477
|
-
case "configTransform":
|
|
1478
|
-
case "templateMergeToml":
|
|
1479
|
-
case "templateMergeJson":
|
|
1480
|
-
return `Update ${displayPath}`;
|
|
1481
|
-
default:
|
|
1482
|
-
return "Operation";
|
|
1975
|
+
function parseDockerRuntime(runtime) {
|
|
1976
|
+
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
|
|
1977
|
+
throw new Error("docker runtime must be an object");
|
|
1978
|
+
}
|
|
1979
|
+
const record = runtime;
|
|
1980
|
+
if (record.type !== "docker") {
|
|
1981
|
+
throw new Error('docker runtime type must be "docker"');
|
|
1483
1982
|
}
|
|
1983
|
+
return record;
|
|
1484
1984
|
}
|
|
1485
|
-
function
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1985
|
+
async function runAndRead(runner, spec) {
|
|
1986
|
+
const handle = runner.exec(spec);
|
|
1987
|
+
const stdout = readStream(handle.stdout);
|
|
1988
|
+
const stderr = readStream(handle.stderr);
|
|
1989
|
+
const result = await handle.result;
|
|
1990
|
+
const output = await stdout;
|
|
1991
|
+
if (result.exitCode !== 0) {
|
|
1992
|
+
const errorOutput = await stderr;
|
|
1993
|
+
throw new Error(
|
|
1994
|
+
`Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${errorOutput ? `
|
|
1995
|
+
${errorOutput}` : ""}`
|
|
1996
|
+
);
|
|
1491
1997
|
}
|
|
1492
|
-
return
|
|
1998
|
+
return output;
|
|
1493
1999
|
}
|
|
1494
|
-
function
|
|
1495
|
-
|
|
2000
|
+
async function runOrThrow(runner, spec) {
|
|
2001
|
+
await runAndRead(runner, spec);
|
|
1496
2002
|
}
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
1501
|
-
const current = result[key];
|
|
1502
|
-
const prefix = prefixMap[key];
|
|
1503
|
-
if (isConfigObject4(current) && isConfigObject4(value)) {
|
|
1504
|
-
if (prefix) {
|
|
1505
|
-
const pruned = pruneKeysByPrefix(current, prefix);
|
|
1506
|
-
result[key] = { ...pruned, ...value };
|
|
1507
|
-
} else {
|
|
1508
|
-
result[key] = mergeWithPruneByPrefix(
|
|
1509
|
-
current,
|
|
1510
|
-
value,
|
|
1511
|
-
prefixMap
|
|
1512
|
-
);
|
|
1513
|
-
}
|
|
1514
|
-
continue;
|
|
1515
|
-
}
|
|
1516
|
-
result[key] = value;
|
|
2003
|
+
async function readStream(stream) {
|
|
2004
|
+
if (stream === null) {
|
|
2005
|
+
return "";
|
|
1517
2006
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
case "ensureDirectory":
|
|
1523
|
-
return applyEnsureDirectory(mutation, context, options);
|
|
1524
|
-
case "removeDirectory":
|
|
1525
|
-
return applyRemoveDirectory(mutation, context, options);
|
|
1526
|
-
case "removeFile":
|
|
1527
|
-
return applyRemoveFile(mutation, context, options);
|
|
1528
|
-
case "chmod":
|
|
1529
|
-
return applyChmod(mutation, context, options);
|
|
1530
|
-
case "backup":
|
|
1531
|
-
return applyBackup(mutation, context, options);
|
|
1532
|
-
case "configMerge":
|
|
1533
|
-
return applyConfigMerge(mutation, context, options);
|
|
1534
|
-
case "configPrune":
|
|
1535
|
-
return applyConfigPrune(mutation, context, options);
|
|
1536
|
-
case "configTransform":
|
|
1537
|
-
return applyConfigTransform(mutation, context, options);
|
|
1538
|
-
case "templateWrite":
|
|
1539
|
-
return applyTemplateWrite(mutation, context, options);
|
|
1540
|
-
case "templateMergeToml":
|
|
1541
|
-
return applyTemplateMerge(mutation, context, options, "toml");
|
|
1542
|
-
case "templateMergeJson":
|
|
1543
|
-
return applyTemplateMerge(mutation, context, options, "json");
|
|
1544
|
-
default: {
|
|
1545
|
-
const never = mutation;
|
|
1546
|
-
throw new Error(`Unknown mutation kind: ${never.kind}`);
|
|
1547
|
-
}
|
|
2007
|
+
stream.setEncoding("utf8");
|
|
2008
|
+
const chunks = [];
|
|
2009
|
+
for await (const chunk of stream) {
|
|
2010
|
+
chunks.push(String(chunk));
|
|
1548
2011
|
}
|
|
2012
|
+
return chunks.join("");
|
|
1549
2013
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
targetPath
|
|
1557
|
-
};
|
|
1558
|
-
const existed = await pathExists(context.fs, targetPath);
|
|
1559
|
-
if (!context.dryRun) {
|
|
1560
|
-
await context.fs.mkdir(targetPath, { recursive: true });
|
|
2014
|
+
function sortedBuildArgs(buildArgs) {
|
|
2015
|
+
return Object.entries(buildArgs).sort(([left], [right]) => left.localeCompare(right));
|
|
2016
|
+
}
|
|
2017
|
+
function buildEnvArgs(env) {
|
|
2018
|
+
if (env === void 0) {
|
|
2019
|
+
return [];
|
|
1561
2020
|
}
|
|
2021
|
+
return Object.entries(env).flatMap(([key, value]) => ["-e", `${key}=${value}`]);
|
|
2022
|
+
}
|
|
2023
|
+
function createContainerName() {
|
|
2024
|
+
return `poe-env-${randomBytes3(6).toString("hex")}`;
|
|
2025
|
+
}
|
|
2026
|
+
async function createContainerJob(containerId, runner, engine, context) {
|
|
1562
2027
|
return {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
2028
|
+
id: containerId,
|
|
2029
|
+
envId: containerId,
|
|
2030
|
+
tool: "docker",
|
|
2031
|
+
argv: ["attach", containerId],
|
|
2032
|
+
async status() {
|
|
2033
|
+
const handle = runner.exec({
|
|
2034
|
+
command: engine,
|
|
2035
|
+
args: [
|
|
2036
|
+
...buildContextArgs(engine, context),
|
|
2037
|
+
"inspect",
|
|
2038
|
+
"-f",
|
|
2039
|
+
"{{.State.Status}}",
|
|
2040
|
+
containerId
|
|
2041
|
+
],
|
|
2042
|
+
stdout: "pipe",
|
|
2043
|
+
stderr: "pipe"
|
|
2044
|
+
});
|
|
2045
|
+
const stdout = await readStream(handle.stdout);
|
|
2046
|
+
const result = await handle.result;
|
|
2047
|
+
if (result.exitCode !== 0) {
|
|
2048
|
+
return "lost";
|
|
2049
|
+
}
|
|
2050
|
+
return stdout.trim() === "running" ? "running" : "exited";
|
|
1567
2051
|
},
|
|
1568
|
-
|
|
2052
|
+
async *stream() {
|
|
2053
|
+
},
|
|
2054
|
+
async wait() {
|
|
2055
|
+
const handle = runner.exec({
|
|
2056
|
+
command: engine,
|
|
2057
|
+
args: [...buildContextArgs(engine, context), "wait", containerId],
|
|
2058
|
+
stdout: "pipe",
|
|
2059
|
+
stderr: "pipe"
|
|
2060
|
+
});
|
|
2061
|
+
const stdout = await readStream(handle.stdout);
|
|
2062
|
+
const result = await handle.result;
|
|
2063
|
+
return { exitCode: Number.parseInt(stdout.trim(), 10) || result.exitCode };
|
|
2064
|
+
},
|
|
2065
|
+
async kill(signal) {
|
|
2066
|
+
const args = signal === void 0 || signal === "SIGTERM" ? ["stop", containerId] : ["kill", ...signal === "SIGKILL" ? [] : [`--signal=${signal}`], containerId];
|
|
2067
|
+
await runOrThrow(runner, {
|
|
2068
|
+
command: engine,
|
|
2069
|
+
args: [...buildContextArgs(engine, context), ...args],
|
|
2070
|
+
stdout: "pipe",
|
|
2071
|
+
stderr: "pipe"
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
1569
2074
|
};
|
|
1570
2075
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
2076
|
+
function createAttachedSpec() {
|
|
2077
|
+
return {
|
|
2078
|
+
cwd: "/workspace",
|
|
2079
|
+
runtime: {
|
|
2080
|
+
type: "docker",
|
|
2081
|
+
image: "attached",
|
|
2082
|
+
build_args: {},
|
|
2083
|
+
mounts: []
|
|
2084
|
+
},
|
|
2085
|
+
env: {},
|
|
2086
|
+
uploadIgnoreFiles: [],
|
|
2087
|
+
jobLabel: {
|
|
2088
|
+
tool: "docker",
|
|
2089
|
+
argv: []
|
|
2090
|
+
}
|
|
1578
2091
|
};
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2092
|
+
}
|
|
2093
|
+
function shellQuote(value) {
|
|
2094
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// packages/process-runner/src/host/host-execution-env.ts
|
|
2098
|
+
var hostExecutionEnvFactory = {
|
|
2099
|
+
type: "host",
|
|
2100
|
+
supportsDetach: false,
|
|
2101
|
+
async open(openSpec) {
|
|
1587
2102
|
return {
|
|
1588
|
-
|
|
1589
|
-
|
|
2103
|
+
id: "host",
|
|
2104
|
+
job: null,
|
|
2105
|
+
async uploadWorkspace() {
|
|
2106
|
+
return {
|
|
2107
|
+
files: 0,
|
|
2108
|
+
bytes: 0,
|
|
2109
|
+
skipped: []
|
|
2110
|
+
};
|
|
2111
|
+
},
|
|
2112
|
+
async downloadWorkspace() {
|
|
2113
|
+
return {
|
|
2114
|
+
files: 0,
|
|
2115
|
+
bytes: 0,
|
|
2116
|
+
conflicts: []
|
|
2117
|
+
};
|
|
2118
|
+
},
|
|
2119
|
+
exec(spec) {
|
|
2120
|
+
return createHostRunner().exec(spec);
|
|
2121
|
+
},
|
|
2122
|
+
async detach() {
|
|
2123
|
+
throw new Error("host runtime does not support detach because host has no addressable env");
|
|
2124
|
+
},
|
|
2125
|
+
shell() {
|
|
2126
|
+
const shellSpec = openSpec.shellSpec;
|
|
2127
|
+
return createHostRunner().exec({
|
|
2128
|
+
command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
2129
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
2130
|
+
cwd: openSpec.cwd,
|
|
2131
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
|
|
2132
|
+
stdin: "inherit",
|
|
2133
|
+
stdout: "inherit",
|
|
2134
|
+
stderr: "inherit",
|
|
2135
|
+
tty: true
|
|
2136
|
+
});
|
|
2137
|
+
},
|
|
2138
|
+
async close() {
|
|
2139
|
+
}
|
|
1590
2140
|
};
|
|
2141
|
+
},
|
|
2142
|
+
async attach() {
|
|
2143
|
+
throw new Error("host runtime does not support reattach");
|
|
1591
2144
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
2145
|
+
};
|
|
2146
|
+
|
|
2147
|
+
// packages/process-runner/src/testing/mock-runner.ts
|
|
2148
|
+
import { Readable, Writable } from "node:stream";
|
|
2149
|
+
|
|
2150
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
2151
|
+
registerExecutionEnvFactory(hostExecutionEnvFactory);
|
|
2152
|
+
registerExecutionEnvFactory(dockerExecutionEnvFactory);
|
|
2153
|
+
if (process.env.VITEST === "true") {
|
|
2154
|
+
registerExecutionEnvFactory(createTestHostExecutionEnvFactory());
|
|
2155
|
+
}
|
|
2156
|
+
function createTestHostExecutionEnvFactory() {
|
|
2157
|
+
return {
|
|
2158
|
+
type: "host",
|
|
2159
|
+
supportsDetach: false,
|
|
2160
|
+
open: ((openSpec) => {
|
|
2161
|
+
return {
|
|
2162
|
+
id: "host",
|
|
2163
|
+
job: null,
|
|
2164
|
+
async uploadWorkspace() {
|
|
2165
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
2166
|
+
},
|
|
2167
|
+
async downloadWorkspace() {
|
|
2168
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
2169
|
+
},
|
|
2170
|
+
exec(spec) {
|
|
2171
|
+
return runHost(spawnChildProcess2, spec);
|
|
2172
|
+
},
|
|
2173
|
+
async detach() {
|
|
2174
|
+
throw new Error(
|
|
2175
|
+
"host runtime does not support detach because host has no addressable env"
|
|
2176
|
+
);
|
|
2177
|
+
},
|
|
2178
|
+
shell() {
|
|
2179
|
+
return runHost(spawnChildProcess2, {
|
|
2180
|
+
command: openSpec.shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
2181
|
+
args: openSpec.shellSpec?.args,
|
|
2182
|
+
cwd: openSpec.cwd,
|
|
2183
|
+
env: openSpec.shellSpec && "env" in openSpec.shellSpec ? openSpec.shellSpec.env : openSpec.env,
|
|
2184
|
+
stdin: "inherit",
|
|
2185
|
+
stdout: "inherit",
|
|
2186
|
+
stderr: "inherit",
|
|
2187
|
+
tty: true
|
|
2188
|
+
});
|
|
2189
|
+
},
|
|
2190
|
+
async close() {
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
}),
|
|
2194
|
+
async attach() {
|
|
2195
|
+
throw new Error("host runtime does not support reattach");
|
|
1595
2196
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
function runHost(spawnProcess, spec) {
|
|
2200
|
+
const stdin = spec.stdin ?? "ignore";
|
|
2201
|
+
const stdout = spec.stdout ?? "pipe";
|
|
2202
|
+
const stderr = spec.stderr ?? "pipe";
|
|
2203
|
+
const stdio = stdin === "inherit" && stdout === "inherit" && stderr === "inherit" ? "inherit" : [stdin, stdout, stderr];
|
|
2204
|
+
const child = spawnProcess(spec.command, spec.args ?? [], {
|
|
2205
|
+
cwd: spec.cwd,
|
|
2206
|
+
env: spec.env,
|
|
2207
|
+
stdio
|
|
2208
|
+
});
|
|
2209
|
+
const result = new Promise((resolve2) => {
|
|
2210
|
+
child.once("close", (code) => {
|
|
2211
|
+
resolve2({ exitCode: code ?? 1 });
|
|
2212
|
+
});
|
|
2213
|
+
child.once("error", () => {
|
|
2214
|
+
resolve2({ exitCode: 1 });
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
const kill = (signal) => {
|
|
2218
|
+
child.kill(signal);
|
|
2219
|
+
};
|
|
2220
|
+
if (spec.signal?.aborted) {
|
|
2221
|
+
kill("SIGTERM");
|
|
2222
|
+
} else {
|
|
2223
|
+
spec.signal?.addEventListener("abort", () => kill("SIGTERM"), { once: true });
|
|
1610
2224
|
}
|
|
1611
2225
|
return {
|
|
1612
|
-
|
|
1613
|
-
|
|
2226
|
+
pid: child.pid ?? null,
|
|
2227
|
+
stdin: child.stdin,
|
|
2228
|
+
stdout: child.stdout,
|
|
2229
|
+
stderr: child.stderr,
|
|
2230
|
+
result,
|
|
2231
|
+
kill
|
|
1614
2232
|
};
|
|
1615
2233
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
|
|
1628
|
-
return {
|
|
1629
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1630
|
-
details
|
|
1631
|
-
};
|
|
2234
|
+
|
|
2235
|
+
// packages/agent-spawn/src/run-command.ts
|
|
2236
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2237
|
+
|
|
2238
|
+
// packages/agent-spawn/src/configs/mcp.ts
|
|
2239
|
+
function toJsonMcpServers(servers) {
|
|
2240
|
+
const out = {};
|
|
2241
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
2242
|
+
const mapped = { command: server.command };
|
|
2243
|
+
if (server.args && server.args.length > 0) {
|
|
2244
|
+
mapped.args = server.args;
|
|
1632
2245
|
}
|
|
1633
|
-
if (
|
|
1634
|
-
|
|
1635
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1636
|
-
details
|
|
1637
|
-
};
|
|
2246
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
2247
|
+
mapped.env = server.env;
|
|
1638
2248
|
}
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
2249
|
+
if (server.timeout !== void 0) {
|
|
2250
|
+
mapped.timeout = server.timeout;
|
|
1641
2251
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
2252
|
+
out[name] = mapped;
|
|
2253
|
+
}
|
|
2254
|
+
return out;
|
|
2255
|
+
}
|
|
2256
|
+
function toTomlString(value) {
|
|
2257
|
+
return JSON.stringify(value);
|
|
2258
|
+
}
|
|
2259
|
+
function toTomlArray(values) {
|
|
2260
|
+
const serialized = values.map((value) => toTomlString(value));
|
|
2261
|
+
return `[${serialized.join(", ")}]`;
|
|
2262
|
+
}
|
|
2263
|
+
function toTomlInlineTable(values) {
|
|
2264
|
+
const parts = [];
|
|
2265
|
+
for (const [key, value] of Object.entries(values)) {
|
|
2266
|
+
parts.push(`${JSON.stringify(key)}=${toTomlString(value)}`);
|
|
2267
|
+
}
|
|
2268
|
+
return `{${parts.join(", ")}}`;
|
|
2269
|
+
}
|
|
2270
|
+
function serializeJsonMcpArgs(servers) {
|
|
2271
|
+
return ["--mcp-config", JSON.stringify({ mcpServers: toJsonMcpServers(servers) })];
|
|
2272
|
+
}
|
|
2273
|
+
function serializeOpenCodeMcpEnv(servers) {
|
|
2274
|
+
const mcp = {};
|
|
2275
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
2276
|
+
const entry = { type: "local", command: [server.command, ...server.args ?? []] };
|
|
2277
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
2278
|
+
entry.environment = server.env;
|
|
1652
2279
|
}
|
|
1653
|
-
|
|
2280
|
+
mcp[name] = entry;
|
|
2281
|
+
}
|
|
2282
|
+
return { OPENCODE_CONFIG_CONTENT: JSON.stringify({ mcp }) };
|
|
2283
|
+
}
|
|
2284
|
+
function serializeCodexMcpArgs(servers) {
|
|
2285
|
+
const args = [];
|
|
2286
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
2287
|
+
const prefix = `mcp_servers.${name}`;
|
|
2288
|
+
args.push("-c", `${prefix}.command=${toTomlString(server.command)}`);
|
|
2289
|
+
if (server.args && server.args.length > 0) {
|
|
2290
|
+
args.push("-c", `${prefix}.args=${toTomlArray(server.args)}`);
|
|
2291
|
+
}
|
|
2292
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
2293
|
+
args.push("-c", `${prefix}.env=${toTomlInlineTable(server.env)}`);
|
|
2294
|
+
}
|
|
2295
|
+
if (server.timeout !== void 0) {
|
|
2296
|
+
args.push("-c", `${prefix}.timeout=${server.timeout}`);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
return args;
|
|
2300
|
+
}
|
|
2301
|
+
function serializeGooseMcpArgs(servers) {
|
|
2302
|
+
return Object.values(servers).flatMap((server) => [
|
|
2303
|
+
"--with-extension",
|
|
2304
|
+
[server.command, ...server.args ?? []].join(" ")
|
|
2305
|
+
]);
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
// packages/agent-spawn/src/configs/claude-code.ts
|
|
2309
|
+
var claudeCodeSpawnConfig = {
|
|
2310
|
+
kind: "cli",
|
|
2311
|
+
agentId: "claude-code",
|
|
2312
|
+
// ACP adapter support: yes (adapter: "claude")
|
|
2313
|
+
adapter: "claude",
|
|
2314
|
+
promptFlag: "-p",
|
|
2315
|
+
modelFlag: "--model",
|
|
2316
|
+
modelStripProviderPrefix: true,
|
|
2317
|
+
modelTransform: (model) => model.replaceAll(".", "-"),
|
|
2318
|
+
defaultArgs: [
|
|
2319
|
+
"--output-format",
|
|
2320
|
+
"stream-json",
|
|
2321
|
+
"--verbose"
|
|
2322
|
+
],
|
|
2323
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
2324
|
+
modes: {
|
|
2325
|
+
yolo: ["--dangerously-skip-permissions"],
|
|
2326
|
+
edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
|
|
2327
|
+
read: ["--permission-mode", "plan"]
|
|
2328
|
+
},
|
|
2329
|
+
stdinMode: {
|
|
2330
|
+
omitPrompt: true,
|
|
2331
|
+
extraArgs: ["--input-format", "text"]
|
|
2332
|
+
},
|
|
2333
|
+
interactive: {
|
|
2334
|
+
defaultArgs: []
|
|
2335
|
+
},
|
|
2336
|
+
resumeCommand: (threadId) => ["--resume", threadId]
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2339
|
+
// packages/agent-spawn/src/configs/codex.ts
|
|
2340
|
+
var codexSpawnConfig = {
|
|
2341
|
+
kind: "cli",
|
|
2342
|
+
agentId: "codex",
|
|
2343
|
+
// ACP adapter support: yes (adapter: "codex")
|
|
2344
|
+
adapter: "codex",
|
|
2345
|
+
promptFlag: "exec",
|
|
2346
|
+
modelFlag: "--model",
|
|
2347
|
+
modelStripProviderPrefix: true,
|
|
2348
|
+
defaultArgs: ["--skip-git-repo-check", "--json"],
|
|
2349
|
+
mcpArgs: serializeCodexMcpArgs,
|
|
2350
|
+
mcpArgsBeforeCommand: true,
|
|
2351
|
+
modes: {
|
|
2352
|
+
yolo: ["-s", "danger-full-access"],
|
|
2353
|
+
edit: ["-s", "workspace-write"],
|
|
2354
|
+
read: ["-s", "read-only"]
|
|
2355
|
+
},
|
|
2356
|
+
stdinMode: {
|
|
2357
|
+
omitPrompt: true,
|
|
2358
|
+
extraArgs: ["-"]
|
|
2359
|
+
},
|
|
2360
|
+
interactive: {
|
|
2361
|
+
defaultArgs: ["-a", "never"]
|
|
2362
|
+
},
|
|
2363
|
+
resumeCommand: (threadId, cwd) => ["resume", "-C", cwd, threadId]
|
|
2364
|
+
};
|
|
2365
|
+
|
|
2366
|
+
// packages/agent-spawn/src/configs/opencode.ts
|
|
2367
|
+
var openCodeSpawnConfig = {
|
|
2368
|
+
kind: "cli",
|
|
2369
|
+
agentId: "opencode",
|
|
2370
|
+
// ACP adapter support: yes (adapter: "opencode").
|
|
2371
|
+
// OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
|
|
2372
|
+
// (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
|
|
2373
|
+
adapter: "opencode",
|
|
2374
|
+
promptFlag: "run",
|
|
2375
|
+
modelFlag: "--model",
|
|
2376
|
+
modelStripProviderPrefix: false,
|
|
2377
|
+
modelTransform: (model) => {
|
|
2378
|
+
return model.startsWith("poe/") ? model : `poe/${model}`;
|
|
2379
|
+
},
|
|
2380
|
+
defaultArgs: ["--format", "json"],
|
|
2381
|
+
modes: {
|
|
2382
|
+
yolo: [],
|
|
2383
|
+
edit: [],
|
|
2384
|
+
read: ["--agent", "plan"]
|
|
2385
|
+
},
|
|
2386
|
+
interactive: {
|
|
2387
|
+
defaultArgs: [],
|
|
2388
|
+
promptFlag: "--prompt"
|
|
2389
|
+
},
|
|
2390
|
+
resumeCommand: (threadId, cwd) => [cwd, "--session", threadId],
|
|
2391
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
2392
|
+
};
|
|
2393
|
+
var openCodeAcpSpawnConfig = {
|
|
2394
|
+
kind: "acp",
|
|
2395
|
+
agentId: "opencode",
|
|
2396
|
+
acpArgs: ["acp"],
|
|
2397
|
+
skipAuth: true,
|
|
2398
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
2399
|
+
};
|
|
2400
|
+
|
|
2401
|
+
// packages/agent-spawn/src/configs/kimi.ts
|
|
2402
|
+
var kimiSpawnConfig = {
|
|
2403
|
+
kind: "cli",
|
|
2404
|
+
agentId: "kimi",
|
|
2405
|
+
// ACP adapter support: yes (adapter: "kimi").
|
|
2406
|
+
// Kimi's `--output-format stream-json` emits OpenAI-style `{ role, content }` JSON
|
|
2407
|
+
// (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
|
|
2408
|
+
adapter: "kimi",
|
|
2409
|
+
promptFlag: "-p",
|
|
2410
|
+
modelStripProviderPrefix: true,
|
|
2411
|
+
defaultArgs: ["--print", "--output-format", "stream-json"],
|
|
2412
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
2413
|
+
modes: {
|
|
2414
|
+
yolo: ["--yolo"],
|
|
2415
|
+
edit: [],
|
|
2416
|
+
read: []
|
|
2417
|
+
},
|
|
2418
|
+
stdinMode: {
|
|
2419
|
+
omitPrompt: true,
|
|
2420
|
+
extraArgs: ["--input-format", "stream-json"]
|
|
2421
|
+
},
|
|
2422
|
+
interactive: {
|
|
2423
|
+
defaultArgs: [],
|
|
2424
|
+
promptFlag: "-p"
|
|
2425
|
+
},
|
|
2426
|
+
resumeCommand: (threadId, cwd) => ["--session", threadId, "--work-dir", cwd]
|
|
2427
|
+
};
|
|
2428
|
+
var kimiAcpSpawnConfig = {
|
|
2429
|
+
kind: "acp",
|
|
2430
|
+
agentId: "kimi",
|
|
2431
|
+
acpArgs: ["acp"]
|
|
2432
|
+
};
|
|
2433
|
+
|
|
2434
|
+
// packages/agent-spawn/src/configs/goose.ts
|
|
2435
|
+
var gooseFileSecretsEnv = { GOOSE_DISABLE_KEYRING: "1" };
|
|
2436
|
+
var gooseSpawnConfig = {
|
|
2437
|
+
kind: "cli",
|
|
2438
|
+
agentId: "goose",
|
|
2439
|
+
adapter: "native",
|
|
2440
|
+
promptFlag: "--text",
|
|
2441
|
+
modelFlag: "--model",
|
|
2442
|
+
modelStripProviderPrefix: false,
|
|
2443
|
+
defaultArgs: ["run", "--output-format", "stream-json"],
|
|
2444
|
+
defaultArgsPosition: "beforePrompt",
|
|
2445
|
+
mcpArgs: serializeGooseMcpArgs,
|
|
2446
|
+
mcpArgsPosition: "beforePrompt",
|
|
2447
|
+
modes: {
|
|
2448
|
+
yolo: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "auto" } },
|
|
2449
|
+
edit: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "smart_approve" } },
|
|
2450
|
+
read: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "chat" } }
|
|
2451
|
+
},
|
|
2452
|
+
stdinMode: {
|
|
2453
|
+
omitPrompt: true,
|
|
2454
|
+
extraArgs: ["--instructions", "-"]
|
|
2455
|
+
},
|
|
2456
|
+
interactive: {
|
|
2457
|
+
defaultArgs: ["session"],
|
|
2458
|
+
defaultArgsPosition: "beforePrompt"
|
|
2459
|
+
},
|
|
2460
|
+
resumeCommand: () => ["run", "--resume", "--text", "continue"]
|
|
2461
|
+
};
|
|
2462
|
+
var gooseAcpSpawnConfig = {
|
|
2463
|
+
kind: "acp",
|
|
2464
|
+
agentId: "goose",
|
|
2465
|
+
acpArgs: ["acp"],
|
|
2466
|
+
env: gooseFileSecretsEnv,
|
|
2467
|
+
skipAuth: true
|
|
2468
|
+
};
|
|
2469
|
+
|
|
2470
|
+
// packages/agent-spawn/src/configs/index.ts
|
|
2471
|
+
var allSpawnConfigs = [
|
|
2472
|
+
claudeCodeSpawnConfig,
|
|
2473
|
+
codexSpawnConfig,
|
|
2474
|
+
openCodeSpawnConfig,
|
|
2475
|
+
kimiSpawnConfig,
|
|
2476
|
+
gooseSpawnConfig
|
|
2477
|
+
];
|
|
2478
|
+
var lookup2 = /* @__PURE__ */ new Map();
|
|
2479
|
+
for (const config of allSpawnConfigs) {
|
|
2480
|
+
lookup2.set(config.agentId, config);
|
|
2481
|
+
}
|
|
2482
|
+
var acpLookup = /* @__PURE__ */ new Map();
|
|
2483
|
+
acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
|
|
2484
|
+
acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
|
|
2485
|
+
acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
|
|
2486
|
+
|
|
2487
|
+
// packages/agent-spawn/src/spawn.ts
|
|
2488
|
+
import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
|
|
2489
|
+
import path15 from "node:path";
|
|
2490
|
+
|
|
2491
|
+
// packages/design-system/src/tokens/colors.ts
|
|
2492
|
+
import chalk from "chalk";
|
|
2493
|
+
var dark = {
|
|
2494
|
+
header: (text4) => chalk.magentaBright.bold(text4),
|
|
2495
|
+
divider: (text4) => chalk.dim(text4),
|
|
2496
|
+
prompt: (text4) => chalk.cyan(text4),
|
|
2497
|
+
number: (text4) => chalk.cyanBright(text4),
|
|
2498
|
+
intro: (text4) => chalk.bgMagenta.white(` Poe - ${text4} `),
|
|
2499
|
+
resolvedSymbol: chalk.magenta("\u25C7"),
|
|
2500
|
+
errorSymbol: chalk.red("\u25A0"),
|
|
2501
|
+
accent: (text4) => chalk.cyan(text4),
|
|
2502
|
+
muted: (text4) => chalk.dim(text4),
|
|
2503
|
+
success: (text4) => chalk.green(text4),
|
|
2504
|
+
warning: (text4) => chalk.yellow(text4),
|
|
2505
|
+
error: (text4) => chalk.red(text4),
|
|
2506
|
+
info: (text4) => chalk.magenta(text4),
|
|
2507
|
+
badge: (text4) => chalk.bgYellow.black(` ${text4} `)
|
|
2508
|
+
};
|
|
2509
|
+
var light = {
|
|
2510
|
+
header: (text4) => chalk.hex("#a200ff").bold(text4),
|
|
2511
|
+
divider: (text4) => chalk.hex("#666666")(text4),
|
|
2512
|
+
prompt: (text4) => chalk.hex("#006699").bold(text4),
|
|
2513
|
+
number: (text4) => chalk.hex("#0077cc").bold(text4),
|
|
2514
|
+
intro: (text4) => chalk.bgHex("#a200ff").white(` Poe - ${text4} `),
|
|
2515
|
+
resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
|
|
2516
|
+
errorSymbol: chalk.hex("#cc0000")("\u25A0"),
|
|
2517
|
+
accent: (text4) => chalk.hex("#006699").bold(text4),
|
|
2518
|
+
muted: (text4) => chalk.hex("#666666")(text4),
|
|
2519
|
+
success: (text4) => chalk.hex("#008800")(text4),
|
|
2520
|
+
warning: (text4) => chalk.hex("#cc6600")(text4),
|
|
2521
|
+
error: (text4) => chalk.hex("#cc0000")(text4),
|
|
2522
|
+
info: (text4) => chalk.hex("#a200ff")(text4),
|
|
2523
|
+
badge: (text4) => chalk.bgHex("#cc6600").white(` ${text4} `)
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2526
|
+
// packages/design-system/src/tokens/typography.ts
|
|
2527
|
+
import chalk2 from "chalk";
|
|
2528
|
+
|
|
2529
|
+
// packages/design-system/src/components/text.ts
|
|
2530
|
+
import chalk3 from "chalk";
|
|
2531
|
+
|
|
2532
|
+
// packages/design-system/src/internal/output-format.ts
|
|
2533
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2534
|
+
var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
|
|
2535
|
+
var formatStorage = new AsyncLocalStorage();
|
|
2536
|
+
var cached;
|
|
2537
|
+
function resolveOutputFormat(env = process.env) {
|
|
2538
|
+
const scoped = formatStorage.getStore();
|
|
2539
|
+
if (scoped) {
|
|
2540
|
+
return scoped;
|
|
2541
|
+
}
|
|
2542
|
+
if (cached) {
|
|
2543
|
+
return cached;
|
|
1654
2544
|
}
|
|
2545
|
+
const raw = env.OUTPUT_FORMAT?.toLowerCase();
|
|
2546
|
+
cached = VALID_FORMATS.has(raw) ? raw : "terminal";
|
|
2547
|
+
return cached;
|
|
1655
2548
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
targetPath
|
|
1663
|
-
};
|
|
1664
|
-
if (typeof context.fs.chmod !== "function") {
|
|
1665
|
-
return {
|
|
1666
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1667
|
-
details
|
|
1668
|
-
};
|
|
2549
|
+
|
|
2550
|
+
// packages/design-system/src/internal/theme-detect.ts
|
|
2551
|
+
function detectThemeFromEnv(env) {
|
|
2552
|
+
const apple = env.APPLE_INTERFACE_STYLE;
|
|
2553
|
+
if (typeof apple === "string") {
|
|
2554
|
+
return apple.toLowerCase() === "dark" ? "dark" : "light";
|
|
1669
2555
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
const
|
|
1673
|
-
if (
|
|
1674
|
-
return
|
|
1675
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1676
|
-
details
|
|
1677
|
-
};
|
|
2556
|
+
const vscodeKind = env.VSCODE_COLOR_THEME_KIND;
|
|
2557
|
+
if (typeof vscodeKind === "string") {
|
|
2558
|
+
const normalized = vscodeKind.toLowerCase();
|
|
2559
|
+
if (normalized.includes("light")) {
|
|
2560
|
+
return "light";
|
|
1678
2561
|
}
|
|
1679
|
-
if (
|
|
1680
|
-
|
|
2562
|
+
if (normalized.includes("dark")) {
|
|
2563
|
+
return "dark";
|
|
1681
2564
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
if (
|
|
1688
|
-
return
|
|
1689
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1690
|
-
details
|
|
1691
|
-
};
|
|
2565
|
+
}
|
|
2566
|
+
const colorFGBG = env.COLORFGBG;
|
|
2567
|
+
if (typeof colorFGBG === "string") {
|
|
2568
|
+
const parts = colorFGBG.split(";").map((part) => Number.parseInt(part, 10));
|
|
2569
|
+
const background = parts.at(-1);
|
|
2570
|
+
if (Number.isFinite(background)) {
|
|
2571
|
+
return background >= 8 ? "light" : "dark";
|
|
1692
2572
|
}
|
|
1693
|
-
throw error2;
|
|
1694
2573
|
}
|
|
2574
|
+
return void 0;
|
|
1695
2575
|
}
|
|
1696
|
-
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
kind: mutation.kind,
|
|
1701
|
-
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1702
|
-
targetPath
|
|
1703
|
-
};
|
|
1704
|
-
const content = await readFileIfExists(context.fs, targetPath);
|
|
1705
|
-
if (content === null) {
|
|
1706
|
-
return {
|
|
1707
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1708
|
-
details
|
|
1709
|
-
};
|
|
2576
|
+
function resolveThemeName(env = process.env) {
|
|
2577
|
+
const raw = (env.POE_CODE_THEME ?? env.POE_THEME)?.toLowerCase();
|
|
2578
|
+
if (raw === "light" || raw === "dark") {
|
|
2579
|
+
return raw;
|
|
1710
2580
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
2581
|
+
const detected = detectThemeFromEnv(env);
|
|
2582
|
+
if (detected) {
|
|
2583
|
+
return detected;
|
|
1714
2584
|
}
|
|
1715
|
-
return
|
|
1716
|
-
outcome: { changed: true, effect: "copy", detail: "backup" },
|
|
1717
|
-
details
|
|
1718
|
-
};
|
|
2585
|
+
return "dark";
|
|
1719
2586
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
kind: mutation.kind,
|
|
1725
|
-
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1726
|
-
targetPath
|
|
1727
|
-
};
|
|
1728
|
-
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1729
|
-
if (!formatName) {
|
|
1730
|
-
throw new Error(
|
|
1731
|
-
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1732
|
-
);
|
|
2587
|
+
var cachedTheme;
|
|
2588
|
+
function getTheme(env) {
|
|
2589
|
+
if (cachedTheme) {
|
|
2590
|
+
return cachedTheme;
|
|
1733
2591
|
}
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
2592
|
+
const themeName = resolveThemeName(env);
|
|
2593
|
+
cachedTheme = themeName === "light" ? light : dark;
|
|
2594
|
+
return cachedTheme;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
// packages/design-system/src/components/symbols.ts
|
|
2598
|
+
import chalk4 from "chalk";
|
|
2599
|
+
var symbols = {
|
|
2600
|
+
get info() {
|
|
2601
|
+
const format = resolveOutputFormat();
|
|
2602
|
+
if (format === "json") return "info";
|
|
2603
|
+
if (format === "markdown") return "(i)";
|
|
2604
|
+
return chalk4.magenta("\u25CF");
|
|
2605
|
+
},
|
|
2606
|
+
get success() {
|
|
2607
|
+
const format = resolveOutputFormat();
|
|
2608
|
+
if (format === "json") return "success";
|
|
2609
|
+
if (format === "markdown") return "[ok]";
|
|
2610
|
+
return chalk4.magenta("\u25C6");
|
|
2611
|
+
},
|
|
2612
|
+
get resolved() {
|
|
2613
|
+
const format = resolveOutputFormat();
|
|
2614
|
+
if (format === "json") return "resolved";
|
|
2615
|
+
if (format === "markdown") return ">";
|
|
2616
|
+
return getTheme().resolvedSymbol;
|
|
2617
|
+
},
|
|
2618
|
+
get errorResolved() {
|
|
2619
|
+
const format = resolveOutputFormat();
|
|
2620
|
+
if (format === "json") return "error";
|
|
2621
|
+
if (format === "markdown") return "[!]";
|
|
2622
|
+
return getTheme().errorSymbol;
|
|
2623
|
+
},
|
|
2624
|
+
get bar() {
|
|
2625
|
+
const format = resolveOutputFormat();
|
|
2626
|
+
if (format === "json") return "";
|
|
2627
|
+
if (format === "markdown") return "|";
|
|
2628
|
+
return "\u2502";
|
|
2629
|
+
},
|
|
2630
|
+
cornerTopRight: "\u256E",
|
|
2631
|
+
cornerBottomRight: "\u256F",
|
|
2632
|
+
get warning() {
|
|
2633
|
+
const format = resolveOutputFormat();
|
|
2634
|
+
if (format === "json") return "warning";
|
|
2635
|
+
if (format === "markdown") return "[!]";
|
|
2636
|
+
return "\u25B2";
|
|
2637
|
+
},
|
|
2638
|
+
get active() {
|
|
2639
|
+
const format = resolveOutputFormat();
|
|
2640
|
+
if (format === "json") return "active";
|
|
2641
|
+
if (format === "markdown") return "[x]";
|
|
2642
|
+
return "\u25C6";
|
|
2643
|
+
},
|
|
2644
|
+
get inactive() {
|
|
2645
|
+
const format = resolveOutputFormat();
|
|
2646
|
+
if (format === "json") return "inactive";
|
|
2647
|
+
if (format === "markdown") return "[ ]";
|
|
2648
|
+
return "\u25CB";
|
|
1744
2649
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2650
|
+
};
|
|
2651
|
+
|
|
2652
|
+
// packages/design-system/src/components/logger.ts
|
|
2653
|
+
import chalk6 from "chalk";
|
|
2654
|
+
|
|
2655
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
2656
|
+
import chalk5 from "chalk";
|
|
2657
|
+
|
|
2658
|
+
// packages/design-system/src/internal/strip-ansi.ts
|
|
2659
|
+
function stripAnsi(value) {
|
|
2660
|
+
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
2664
|
+
function writeTerminalMessage(msg, {
|
|
2665
|
+
symbol = chalk5.gray("\u2502"),
|
|
2666
|
+
secondarySymbol = chalk5.gray("\u2502"),
|
|
2667
|
+
spacing: spacing2 = 1,
|
|
2668
|
+
withGuide = true
|
|
2669
|
+
} = {}) {
|
|
2670
|
+
const lines = [];
|
|
2671
|
+
const showGuide = withGuide !== false;
|
|
2672
|
+
const contentLines = msg.split("\n");
|
|
2673
|
+
const prefix = showGuide ? `${symbol} ` : "";
|
|
2674
|
+
const continuationPrefix = showGuide ? `${secondarySymbol} ` : "";
|
|
2675
|
+
const emptyGuide = showGuide ? secondarySymbol : "";
|
|
2676
|
+
for (let index = 0; index < spacing2; index += 1) {
|
|
2677
|
+
lines.push(emptyGuide);
|
|
2678
|
+
}
|
|
2679
|
+
if (contentLines.length === 0) {
|
|
2680
|
+
process.stdout.write("\n");
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
const [firstLine = "", ...continuationLines] = contentLines;
|
|
2684
|
+
if (firstLine.length > 0) {
|
|
2685
|
+
lines.push(`${prefix}${firstLine}`);
|
|
1749
2686
|
} else {
|
|
1750
|
-
|
|
2687
|
+
lines.push(showGuide ? symbol : "");
|
|
1751
2688
|
}
|
|
1752
|
-
const
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2689
|
+
for (const line of continuationLines) {
|
|
2690
|
+
if (line.length > 0) {
|
|
2691
|
+
lines.push(`${continuationPrefix}${line}`);
|
|
2692
|
+
continue;
|
|
2693
|
+
}
|
|
2694
|
+
lines.push(emptyGuide);
|
|
1756
2695
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
changed,
|
|
1760
|
-
effect: changed ? "write" : "none",
|
|
1761
|
-
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
1762
|
-
},
|
|
1763
|
-
details
|
|
1764
|
-
};
|
|
2696
|
+
process.stdout.write(`${lines.join("\n")}
|
|
2697
|
+
`);
|
|
1765
2698
|
}
|
|
1766
|
-
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
targetPath
|
|
1773
|
-
};
|
|
1774
|
-
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
1775
|
-
if (rawContent === null) {
|
|
1776
|
-
return {
|
|
1777
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1778
|
-
details
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
1782
|
-
if (!formatName) {
|
|
1783
|
-
throw new Error(
|
|
1784
|
-
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1785
|
-
);
|
|
2699
|
+
function message(msg, options) {
|
|
2700
|
+
const format = resolveOutputFormat();
|
|
2701
|
+
if (format === "markdown") {
|
|
2702
|
+
process.stdout.write(`- ${stripAnsi(msg)}
|
|
2703
|
+
`);
|
|
2704
|
+
return;
|
|
1786
2705
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
return
|
|
1793
|
-
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1794
|
-
details
|
|
1795
|
-
};
|
|
2706
|
+
if (format === "json") {
|
|
2707
|
+
process.stdout.write(
|
|
2708
|
+
`${JSON.stringify({ level: "message", message: stripAnsi(msg) })}
|
|
2709
|
+
`
|
|
2710
|
+
);
|
|
2711
|
+
return;
|
|
1796
2712
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
2713
|
+
writeTerminalMessage(msg, options);
|
|
2714
|
+
}
|
|
2715
|
+
function info(msg) {
|
|
2716
|
+
const format = resolveOutputFormat();
|
|
2717
|
+
if (format === "markdown") {
|
|
2718
|
+
process.stdout.write(`- **info:** ${stripAnsi(msg)}
|
|
2719
|
+
`);
|
|
2720
|
+
return;
|
|
1802
2721
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
};
|
|
2722
|
+
if (format === "json") {
|
|
2723
|
+
process.stdout.write(
|
|
2724
|
+
`${JSON.stringify({ level: "info", message: stripAnsi(msg) })}
|
|
2725
|
+
`
|
|
2726
|
+
);
|
|
2727
|
+
return;
|
|
1810
2728
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2729
|
+
message(msg, { symbol: symbols.info });
|
|
2730
|
+
}
|
|
2731
|
+
function success(msg) {
|
|
2732
|
+
const format = resolveOutputFormat();
|
|
2733
|
+
if (format === "markdown") {
|
|
2734
|
+
process.stdout.write(`- **success:** ${stripAnsi(msg)}
|
|
2735
|
+
`);
|
|
2736
|
+
return;
|
|
1819
2737
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
2738
|
+
if (format === "json") {
|
|
2739
|
+
process.stdout.write(
|
|
2740
|
+
`${JSON.stringify({ level: "success", message: stripAnsi(msg) })}
|
|
2741
|
+
`
|
|
2742
|
+
);
|
|
2743
|
+
return;
|
|
1823
2744
|
}
|
|
1824
|
-
|
|
1825
|
-
outcome: { changed: true, effect: "write", detail: "update" },
|
|
1826
|
-
details
|
|
1827
|
-
};
|
|
2745
|
+
message(msg, { symbol: symbols.success });
|
|
1828
2746
|
}
|
|
1829
|
-
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
2747
|
+
function warn(msg) {
|
|
2748
|
+
const format = resolveOutputFormat();
|
|
2749
|
+
if (format === "markdown") {
|
|
2750
|
+
process.stdout.write(`- **warning:** ${stripAnsi(msg)}
|
|
2751
|
+
`);
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
if (format === "json") {
|
|
2755
|
+
process.stdout.write(
|
|
2756
|
+
`${JSON.stringify({ level: "warn", message: stripAnsi(msg) })}
|
|
2757
|
+
`
|
|
1841
2758
|
);
|
|
2759
|
+
return;
|
|
1842
2760
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
}
|
|
1852
|
-
current = {};
|
|
2761
|
+
message(msg, { symbol: chalk5.yellow("\u25B2") });
|
|
2762
|
+
}
|
|
2763
|
+
function error(msg) {
|
|
2764
|
+
const format = resolveOutputFormat();
|
|
2765
|
+
if (format === "markdown") {
|
|
2766
|
+
process.stdout.write(`- **error:** ${stripAnsi(msg)}
|
|
2767
|
+
`);
|
|
2768
|
+
return;
|
|
1853
2769
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2770
|
+
if (format === "json") {
|
|
2771
|
+
process.stdout.write(
|
|
2772
|
+
`${JSON.stringify({ level: "error", message: stripAnsi(msg) })}
|
|
2773
|
+
`
|
|
2774
|
+
);
|
|
2775
|
+
return;
|
|
1860
2776
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2777
|
+
message(msg, { symbol: chalk5.red("\u25A0") });
|
|
2778
|
+
}
|
|
2779
|
+
var log = {
|
|
2780
|
+
info,
|
|
2781
|
+
success,
|
|
2782
|
+
message,
|
|
2783
|
+
warn,
|
|
2784
|
+
error
|
|
2785
|
+
};
|
|
2786
|
+
|
|
2787
|
+
// packages/design-system/src/components/logger.ts
|
|
2788
|
+
function createLogger(emitter) {
|
|
2789
|
+
const emit = (level, message2) => {
|
|
2790
|
+
if (emitter) {
|
|
2791
|
+
emitter(message2);
|
|
2792
|
+
return;
|
|
1867
2793
|
}
|
|
1868
|
-
if (
|
|
1869
|
-
|
|
2794
|
+
if (level === "success") {
|
|
2795
|
+
log.success(message2);
|
|
2796
|
+
return;
|
|
1870
2797
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2798
|
+
if (level === "warn") {
|
|
2799
|
+
log.warn(message2);
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
if (level === "error") {
|
|
2803
|
+
log.error(message2);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
log.info(message2);
|
|
2807
|
+
};
|
|
1880
2808
|
return {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
effect: "write",
|
|
1884
|
-
detail: rawContent === null ? "create" : "update"
|
|
2809
|
+
info(message2) {
|
|
2810
|
+
emit("info", message2);
|
|
1885
2811
|
},
|
|
1886
|
-
|
|
2812
|
+
success(message2) {
|
|
2813
|
+
emit("success", message2);
|
|
2814
|
+
},
|
|
2815
|
+
warn(message2) {
|
|
2816
|
+
emit("warn", message2);
|
|
2817
|
+
},
|
|
2818
|
+
error(message2) {
|
|
2819
|
+
emit("error", message2);
|
|
2820
|
+
},
|
|
2821
|
+
resolved(label, value) {
|
|
2822
|
+
if (emitter) {
|
|
2823
|
+
emitter(`${label}: ${value}`);
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
log.message(`${label}
|
|
2827
|
+
${value}`, { symbol: symbols.resolved });
|
|
2828
|
+
},
|
|
2829
|
+
errorResolved(label, value) {
|
|
2830
|
+
if (emitter) {
|
|
2831
|
+
emitter(`${label}: ${value}`);
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
log.message(`${label}
|
|
2835
|
+
${value}`, { symbol: symbols.errorResolved });
|
|
2836
|
+
},
|
|
2837
|
+
message(message2, symbol) {
|
|
2838
|
+
if (emitter) {
|
|
2839
|
+
emitter(message2);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
log.message(message2, { symbol: symbol ?? chalk6.gray("\u2502") });
|
|
2843
|
+
}
|
|
1887
2844
|
};
|
|
1888
2845
|
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2846
|
+
var logger = createLogger();
|
|
2847
|
+
|
|
2848
|
+
// packages/design-system/src/components/table.ts
|
|
2849
|
+
import { Table } from "console-table-printer";
|
|
2850
|
+
|
|
2851
|
+
// packages/design-system/src/acp/components.ts
|
|
2852
|
+
import chalk7 from "chalk";
|
|
2853
|
+
|
|
2854
|
+
// packages/design-system/src/acp/writer.ts
|
|
2855
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
2856
|
+
var storage = new AsyncLocalStorage2();
|
|
2857
|
+
|
|
2858
|
+
// packages/design-system/src/acp/components.ts
|
|
2859
|
+
var AGENT_PREFIX = `${chalk7.green.bold("\u2713")} agent: `;
|
|
2860
|
+
|
|
2861
|
+
// packages/design-system/src/dashboard/buffer.ts
|
|
2862
|
+
import chalk8 from "chalk";
|
|
2863
|
+
|
|
2864
|
+
// packages/design-system/src/dashboard/terminal.ts
|
|
2865
|
+
import readline from "node:readline";
|
|
2866
|
+
import { PassThrough } from "node:stream";
|
|
2867
|
+
|
|
2868
|
+
// packages/design-system/src/prompts/index.ts
|
|
2869
|
+
import chalk15 from "chalk";
|
|
2870
|
+
import * as clack from "@clack/prompts";
|
|
2871
|
+
|
|
2872
|
+
// packages/design-system/src/prompts/primitives/cancel.ts
|
|
2873
|
+
import chalk9 from "chalk";
|
|
2874
|
+
import { isCancel } from "@clack/prompts";
|
|
2875
|
+
|
|
2876
|
+
// packages/design-system/src/prompts/primitives/intro.ts
|
|
2877
|
+
import chalk10 from "chalk";
|
|
2878
|
+
|
|
2879
|
+
// packages/design-system/src/prompts/primitives/note.ts
|
|
2880
|
+
import chalk11 from "chalk";
|
|
2881
|
+
|
|
2882
|
+
// packages/design-system/src/prompts/primitives/outro.ts
|
|
2883
|
+
import chalk12 from "chalk";
|
|
2884
|
+
|
|
2885
|
+
// packages/design-system/src/prompts/primitives/spinner.ts
|
|
2886
|
+
import chalk14 from "chalk";
|
|
2887
|
+
|
|
2888
|
+
// packages/design-system/src/static/spinner.ts
|
|
2889
|
+
import chalk13 from "chalk";
|
|
2890
|
+
|
|
2891
|
+
// packages/design-system/src/static/menu.ts
|
|
2892
|
+
import chalk16 from "chalk";
|
|
2893
|
+
|
|
2894
|
+
// packages/agent-spawn/src/autonomous.ts
|
|
2895
|
+
var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2896
|
+
|
|
2897
|
+
// packages/agent-spawn/src/acp/replay.ts
|
|
2898
|
+
import path16 from "node:path";
|
|
2899
|
+
import { homedir as homedir2 } from "node:os";
|
|
2900
|
+
import { open as open2, readdir } from "node:fs/promises";
|
|
2901
|
+
import { createInterface } from "node:readline";
|
|
2902
|
+
|
|
2903
|
+
// packages/poe-acp-client/src/acp-client.ts
|
|
2904
|
+
import { isAbsolute } from "node:path";
|
|
2905
|
+
|
|
2906
|
+
// packages/poe-acp-client/src/acp-transport.ts
|
|
2907
|
+
import {
|
|
2908
|
+
spawn as spawnChildProcess3
|
|
2909
|
+
} from "node:child_process";
|
|
2910
|
+
|
|
2911
|
+
// packages/poe-acp-client/src/run-report.ts
|
|
2912
|
+
import * as fsPromises2 from "node:fs/promises";
|
|
2913
|
+
import { homedir } from "node:os";
|
|
2914
|
+
import { join } from "node:path";
|
|
2915
|
+
|
|
2916
|
+
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
2917
|
+
import path17 from "node:path";
|
|
2918
|
+
import { homedir as homedir3 } from "node:os";
|
|
2919
|
+
import { mkdir, open as open3 } from "node:fs/promises";
|
|
2920
|
+
|
|
2921
|
+
// src/utils/command-checks.ts
|
|
2922
|
+
function formatCommandRunnerResult(result) {
|
|
2923
|
+
const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
|
|
2924
|
+
const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
|
|
2925
|
+
return `stdout:
|
|
2926
|
+
${stdout}
|
|
2927
|
+
stderr:
|
|
2928
|
+
${stderr}`;
|
|
2929
|
+
}
|
|
2930
|
+
function describeCommandExpectation(command, args, expectedOutput) {
|
|
2931
|
+
return `${renderCommandLine(command, args)} (expecting "${expectedOutput}")`;
|
|
2932
|
+
}
|
|
2933
|
+
function createCommandExpectationCheck(options) {
|
|
1909
2934
|
return {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2935
|
+
id: options.id,
|
|
2936
|
+
description: describeCommandExpectation(
|
|
2937
|
+
options.command,
|
|
2938
|
+
options.args,
|
|
2939
|
+
options.expectedOutput
|
|
2940
|
+
),
|
|
2941
|
+
async run(context) {
|
|
2942
|
+
await runAndMatchOutput(context, options);
|
|
2943
|
+
}
|
|
1916
2944
|
};
|
|
1917
2945
|
}
|
|
1918
|
-
async function
|
|
1919
|
-
|
|
2946
|
+
async function runAndMatchOutput(context, options) {
|
|
2947
|
+
const rendered = renderCommandLine(options.command, options.args);
|
|
2948
|
+
if (options.skipOnDryRun !== false && context.isDryRun) {
|
|
2949
|
+
if (context.logDryRun) {
|
|
2950
|
+
context.logDryRun(
|
|
2951
|
+
`Dry run: ${rendered} (expecting "${options.expectedOutput}")`
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
const result = options.commandOptions ? await context.runCommand(options.command, options.args, options.commandOptions) : await context.runCommand(options.command, options.args);
|
|
2957
|
+
if (result.exitCode !== 0) {
|
|
2958
|
+
const detail = formatCommandRunnerResult(result);
|
|
1920
2959
|
throw new Error(
|
|
1921
|
-
|
|
2960
|
+
[`Command ${rendered} failed with exit code ${result.exitCode}.`, detail].join("\n")
|
|
1922
2961
|
);
|
|
1923
2962
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
kind: mutation.kind,
|
|
1928
|
-
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1929
|
-
targetPath
|
|
1930
|
-
};
|
|
1931
|
-
const format = getConfigFormat(formatName);
|
|
1932
|
-
const template = await context.templates(mutation.templateId);
|
|
1933
|
-
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
1934
|
-
const rendered = Mustache.render(template, templateContext);
|
|
1935
|
-
let templateDoc;
|
|
1936
|
-
try {
|
|
1937
|
-
templateDoc = format.parse(rendered);
|
|
1938
|
-
} catch (error2) {
|
|
2963
|
+
if (!stdoutMatchesExpected(result.stdout, options.expectedOutput)) {
|
|
2964
|
+
const detail = formatCommandRunnerResult(result);
|
|
2965
|
+
const received = result.stdout.trim();
|
|
1939
2966
|
throw new Error(
|
|
1940
|
-
|
|
1941
|
-
|
|
2967
|
+
[
|
|
2968
|
+
`Command ${rendered} failed: expected "${options.expectedOutput}" but received "${received}".`,
|
|
2969
|
+
detail
|
|
2970
|
+
].join("\n")
|
|
1942
2971
|
);
|
|
1943
2972
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
if (rawContent !== null) {
|
|
1950
|
-
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
1951
|
-
}
|
|
1952
|
-
current = {};
|
|
2973
|
+
}
|
|
2974
|
+
function stdoutMatchesExpected(stdout, expected) {
|
|
2975
|
+
const trimmed = stdout.trim();
|
|
2976
|
+
if (trimmed === expected) {
|
|
2977
|
+
return true;
|
|
1953
2978
|
}
|
|
1954
|
-
const
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
if (changed && !context.dryRun) {
|
|
1958
|
-
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
2979
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
2980
|
+
if (lines.some((line) => line === expected)) {
|
|
2981
|
+
return true;
|
|
1959
2982
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
// packages/config-mutations/src/execution/run-mutations.ts
|
|
1971
|
-
async function runMutations(mutations, context, options) {
|
|
1972
|
-
const effects = [];
|
|
1973
|
-
let anyChanged = false;
|
|
1974
|
-
const resolverOptions = options ?? {};
|
|
1975
|
-
for (const mutation of mutations) {
|
|
1976
|
-
const { outcome } = await executeMutation(
|
|
1977
|
-
mutation,
|
|
1978
|
-
context,
|
|
1979
|
-
resolverOptions
|
|
1980
|
-
);
|
|
1981
|
-
effects.push(outcome);
|
|
1982
|
-
if (outcome.changed) {
|
|
1983
|
-
anyChanged = true;
|
|
2983
|
+
for (const line of lines) {
|
|
2984
|
+
if (line[0] !== "{") continue;
|
|
2985
|
+
try {
|
|
2986
|
+
const parsed = JSON.parse(line);
|
|
2987
|
+
if (parsed.type === "result" && parsed.result === expected) {
|
|
2988
|
+
return true;
|
|
2989
|
+
}
|
|
2990
|
+
} catch {
|
|
2991
|
+
continue;
|
|
1984
2992
|
}
|
|
1985
2993
|
}
|
|
1986
|
-
return
|
|
1987
|
-
changed: anyChanged,
|
|
1988
|
-
effects
|
|
1989
|
-
};
|
|
2994
|
+
return false;
|
|
1990
2995
|
}
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
context.observers?.onComplete?.(details, outcome);
|
|
2001
|
-
return { outcome, details };
|
|
2002
|
-
} catch (error2) {
|
|
2003
|
-
context.observers?.onError?.(
|
|
2004
|
-
{
|
|
2005
|
-
kind: mutation.kind,
|
|
2006
|
-
label: mutation.label ?? mutation.kind,
|
|
2007
|
-
targetPath: void 0
|
|
2008
|
-
},
|
|
2009
|
-
error2
|
|
2010
|
-
);
|
|
2011
|
-
throw error2;
|
|
2996
|
+
function renderCommandLine(command, args) {
|
|
2997
|
+
return [command, ...args].map(quoteIfNeeded).join(" ").trim();
|
|
2998
|
+
}
|
|
2999
|
+
function quoteIfNeeded(value) {
|
|
3000
|
+
if (value.length === 0) {
|
|
3001
|
+
return '""';
|
|
3002
|
+
}
|
|
3003
|
+
if (needsQuoting(value)) {
|
|
3004
|
+
return `"${value.replaceAll('"', '\\"')}"`;
|
|
2012
3005
|
}
|
|
3006
|
+
return value;
|
|
3007
|
+
}
|
|
3008
|
+
function needsQuoting(value) {
|
|
3009
|
+
return value.includes(" ") || value.includes(" ") || value.includes("\n");
|
|
3010
|
+
}
|
|
3011
|
+
function createBinaryExistsCheck(binaryName, id, description) {
|
|
3012
|
+
return {
|
|
3013
|
+
id,
|
|
3014
|
+
description,
|
|
3015
|
+
async run({ runCommand: runCommand2 }) {
|
|
3016
|
+
const commonPaths = [
|
|
3017
|
+
`/usr/local/bin/${binaryName}`,
|
|
3018
|
+
`/usr/bin/${binaryName}`,
|
|
3019
|
+
`$HOME/.local/bin/${binaryName}`,
|
|
3020
|
+
`$HOME/.claude/local/bin/${binaryName}`
|
|
3021
|
+
];
|
|
3022
|
+
const detectors = [
|
|
3023
|
+
{
|
|
3024
|
+
command: "which",
|
|
3025
|
+
args: [binaryName],
|
|
3026
|
+
validate: (result) => result.exitCode === 0
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
command: "where",
|
|
3030
|
+
args: [binaryName],
|
|
3031
|
+
validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
|
|
3032
|
+
},
|
|
3033
|
+
// Check common installation paths using shell expansion for $HOME
|
|
3034
|
+
{
|
|
3035
|
+
command: "sh",
|
|
3036
|
+
args: [
|
|
3037
|
+
"-c",
|
|
3038
|
+
commonPaths.map((p) => `test -f "${p}"`).join(" || ")
|
|
3039
|
+
],
|
|
3040
|
+
validate: (result) => result.exitCode === 0
|
|
3041
|
+
}
|
|
3042
|
+
];
|
|
3043
|
+
for (const detector of detectors) {
|
|
3044
|
+
const result = await runCommand2(detector.command, detector.args);
|
|
3045
|
+
if (detector.validate(result)) {
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
throw new Error(`${binaryName} CLI binary not found on PATH.`);
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
2013
3052
|
}
|
|
2014
|
-
|
|
2015
|
-
// packages/config-mutations/src/template/render.ts
|
|
2016
|
-
import Mustache2 from "mustache";
|
|
2017
|
-
var originalEscape = Mustache2.escape;
|
|
2018
3053
|
|
|
2019
3054
|
// src/services/service-install.ts
|
|
2020
3055
|
async function runServiceInstall(definition, context) {
|