poe-code 3.0.202 → 3.0.204
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/braintrust.d.ts +3 -0
- package/dist/cli/commands/braintrust.js +77 -0
- package/dist/cli/commands/braintrust.js.map +1 -0
- package/dist/cli/commands/configure.d.ts +1 -0
- package/dist/cli/commands/configure.js +197 -0
- package/dist/cli/commands/configure.js.map +1 -1
- package/dist/cli/commands/experiment.js +51 -7
- package/dist/cli/commands/experiment.js.map +1 -1
- package/dist/cli/commands/harness.d.ts +3 -0
- package/dist/cli/commands/harness.js +260 -0
- package/dist/cli/commands/harness.js.map +1 -0
- package/dist/cli/commands/pipeline.js +58 -24
- package/dist/cli/commands/pipeline.js.map +1 -1
- package/dist/cli/commands/ralph.js +19 -7
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/runtime/build.d.ts +7 -0
- package/dist/cli/commands/runtime/build.js +128 -0
- package/dist/cli/commands/runtime/build.js.map +1 -0
- package/dist/cli/commands/runtime/index.d.ts +3 -0
- package/dist/cli/commands/runtime/index.js +14 -0
- package/dist/cli/commands/runtime/index.js.map +1 -0
- package/dist/cli/commands/runtime/init.d.ts +7 -0
- package/dist/cli/commands/runtime/init.js +39 -0
- package/dist/cli/commands/runtime/init.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/attach.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/attach.js +35 -0
- package/dist/cli/commands/runtime/jobs/attach.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/index.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/index.js +16 -0
- package/dist/cli/commands/runtime/jobs/index.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/logs.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/logs.js +27 -0
- package/dist/cli/commands/runtime/jobs/logs.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/ls.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/ls.js +60 -0
- package/dist/cli/commands/runtime/jobs/ls.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/sandbox.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/sandbox.js +15 -0
- package/dist/cli/commands/runtime/jobs/sandbox.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/shared.d.ts +22 -0
- package/dist/cli/commands/runtime/jobs/shared.js +124 -0
- package/dist/cli/commands/runtime/jobs/shared.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/stop.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/stop.js +31 -0
- package/dist/cli/commands/runtime/jobs/stop.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/sync.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/sync.js +25 -0
- package/dist/cli/commands/runtime/jobs/sync.js.map +1 -0
- package/dist/cli/commands/runtime/shared.d.ts +20 -0
- package/dist/cli/commands/runtime/shared.js +69 -0
- package/dist/cli/commands/runtime/shared.js.map +1 -0
- package/dist/cli/commands/runtime/templates/clear.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/clear.js +53 -0
- package/dist/cli/commands/runtime/templates/clear.js.map +1 -0
- package/dist/cli/commands/runtime/templates/index.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/index.js +10 -0
- package/dist/cli/commands/runtime/templates/index.js.map +1 -0
- package/dist/cli/commands/runtime/templates/ls.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/ls.js +52 -0
- package/dist/cli/commands/runtime/templates/ls.js.map +1 -0
- package/dist/cli/commands/runtime-options.d.ts +11 -0
- package/dist/cli/commands/runtime-options.js +26 -0
- package/dist/cli/commands/runtime-options.js.map +1 -0
- package/dist/cli/commands/spawn.js +36 -7
- package/dist/cli/commands/spawn.js.map +1 -1
- package/dist/cli/program.js +17 -1
- package/dist/cli/program.js.map +1 -1
- package/dist/index.js +70879 -47381
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +3486 -852
- package/dist/providers/claude-code.js.map +4 -4
- package/dist/providers/codex.js +3486 -852
- package/dist/providers/codex.js.map +4 -4
- package/dist/providers/goose.js +3432 -798
- package/dist/providers/goose.js.map +4 -4
- package/dist/providers/kimi.js +3486 -852
- package/dist/providers/kimi.js.map +4 -4
- package/dist/providers/opencode.js +3486 -852
- package/dist/providers/opencode.js.map +4 -4
- package/dist/providers/poe-agent.js +20641 -17147
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/providers/spawn-options.d.ts +10 -1
- package/dist/sdk/experiment.js +6 -0
- package/dist/sdk/experiment.js.map +1 -1
- package/dist/sdk/ralph.js +108 -11
- package/dist/sdk/ralph.js.map +1 -1
- package/dist/sdk/spawn.js +27 -4
- package/dist/sdk/spawn.js.map +1 -1
- package/dist/sdk/types.d.ts +23 -1
- package/dist/utils/command-checks.js +2 -29
- package/dist/utils/command-checks.js.map +1 -1
- package/package.json +12 -7
- package/packages/design-system/dist/components/help-formatter-plain.d.ts +4 -0
- package/packages/design-system/dist/components/help-formatter-plain.js +132 -0
- package/packages/design-system/dist/components/help-formatter.d.ts +13 -0
- package/packages/design-system/dist/components/help-formatter.js +116 -7
- package/packages/design-system/dist/components/index.d.ts +2 -2
- package/packages/design-system/dist/components/index.js +1 -1
- package/packages/design-system/dist/components/text.d.ts +1 -0
- package/packages/design-system/dist/components/text.js +8 -0
- package/packages/design-system/dist/index.d.ts +3 -2
- package/packages/design-system/dist/index.js +2 -1
- package/packages/memory/dist/index.js +3406 -387
- package/packages/memory/dist/index.js.map +4 -4
- package/packages/superintendent/dist/commands/run.d.ts +45 -0
- package/packages/superintendent/dist/commands/run.js +133 -38
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +36 -0
- package/packages/superintendent/dist/runtime/agent-runner.d.ts +31 -0
- package/packages/superintendent/dist/runtime/agent-runner.js +121 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +7 -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
|
@@ -88,19 +88,11 @@ var KIMI_MODELS = [
|
|
|
88
88
|
var DEFAULT_KIMI_MODEL = KIMI_MODELS[0];
|
|
89
89
|
var PROVIDER_NAME = "poe";
|
|
90
90
|
|
|
91
|
-
// packages/agent-spawn/src/
|
|
92
|
-
import { spawn } from "node:child_process";
|
|
91
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
92
|
+
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
93
93
|
|
|
94
|
-
// packages/agent-
|
|
95
|
-
|
|
96
|
-
if (Array.isArray(modeConfig)) {
|
|
97
|
-
return { args: modeConfig };
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
args: modeConfig.args ?? [],
|
|
101
|
-
env: modeConfig.env && Object.keys(modeConfig.env).length > 0 ? modeConfig.env : void 0
|
|
102
|
-
};
|
|
103
|
-
}
|
|
94
|
+
// packages/agent-harness-tools/src/paths.ts
|
|
95
|
+
import path from "node:path";
|
|
104
96
|
|
|
105
97
|
// packages/agent-defs/src/agents/claude-code.ts
|
|
106
98
|
var claudeCodeAgent = {
|
|
@@ -241,918 +233,935 @@ function resolveAgentId(input) {
|
|
|
241
233
|
return lookup.get(input.toLowerCase());
|
|
242
234
|
}
|
|
243
235
|
|
|
244
|
-
// packages/agent-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
236
|
+
// packages/agent-harness-tools/src/select-agent.ts
|
|
237
|
+
var loopAgents = allAgents.filter(
|
|
238
|
+
(agent) => agent.binaryName !== void 0 || agent.id === "poe-agent"
|
|
239
|
+
);
|
|
240
|
+
var supportedAgents = loopAgents.map((agent) => agent.id).join(", ");
|
|
241
|
+
|
|
242
|
+
// packages/file-lock/src/lock.ts
|
|
243
|
+
import * as fsPromises from "node:fs/promises";
|
|
244
|
+
import * as os from "node:os";
|
|
245
|
+
|
|
246
|
+
// packages/agent-harness-tools/src/run-logs.ts
|
|
247
|
+
import path2 from "node:path";
|
|
248
|
+
|
|
249
|
+
// packages/agent-harness-tools/src/log-stream.ts
|
|
250
|
+
import nodeFs from "node:fs";
|
|
251
|
+
var JOB_DIR = "/tmp/poe-jobs";
|
|
252
|
+
var POLL_INTERVAL_MS = 250;
|
|
253
|
+
async function* streamLogFile(env, jobId, opts) {
|
|
254
|
+
const fs = env.fs ?? nodeFs;
|
|
255
|
+
const file = jobLogPath(jobId);
|
|
256
|
+
let byteOffset = opts.sinceByte ?? 0;
|
|
257
|
+
while (true) {
|
|
258
|
+
if (opts.since !== void 0 && !await wasModifiedSince(fs, file, opts.since)) {
|
|
259
|
+
await waitForLogChange(fs, file);
|
|
260
|
+
continue;
|
|
251
261
|
}
|
|
252
|
-
|
|
253
|
-
|
|
262
|
+
const result = await readLogChunk(fs, file, byteOffset);
|
|
263
|
+
if (result !== null) {
|
|
264
|
+
byteOffset = result.nextByteOffset;
|
|
265
|
+
yield result.chunk;
|
|
266
|
+
continue;
|
|
254
267
|
}
|
|
255
|
-
|
|
256
|
-
|
|
268
|
+
await waitForLogChange(fs, file);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function wasModifiedSince(fs, file, since) {
|
|
272
|
+
if (fs.promises.stat === void 0) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
const stat2 = await fs.promises.stat(file);
|
|
277
|
+
return stat2.mtimeMs >= since.getTime();
|
|
278
|
+
} catch (error2) {
|
|
279
|
+
if (isNodeError(error2) && error2.code === "ENOENT") {
|
|
280
|
+
return false;
|
|
257
281
|
}
|
|
258
|
-
|
|
282
|
+
throw error2;
|
|
259
283
|
}
|
|
260
|
-
return out;
|
|
261
284
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
285
|
+
async function waitForExit(env, jobId, opts = {}) {
|
|
286
|
+
const fs = env.fs ?? nodeFs;
|
|
287
|
+
const file = jobExitPath(jobId);
|
|
288
|
+
while (true) {
|
|
289
|
+
throwIfAborted(opts.signal);
|
|
290
|
+
const contents = await readTextFileIfExists(fs, file);
|
|
291
|
+
if (contents !== null) {
|
|
292
|
+
const text4 = contents.trim();
|
|
293
|
+
const exitCode = Number(text4);
|
|
294
|
+
if (text4.length === 0 || !Number.isInteger(exitCode)) {
|
|
295
|
+
throw new Error(`Invalid exit code in ${file}: ${contents}`);
|
|
296
|
+
}
|
|
297
|
+
return { exitCode };
|
|
298
|
+
}
|
|
299
|
+
await sleep(POLL_INTERVAL_MS, opts.signal);
|
|
300
|
+
}
|
|
264
301
|
}
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
return `[${serialized.join(", ")}]`;
|
|
302
|
+
function jobLogPath(jobId) {
|
|
303
|
+
return `${JOB_DIR}/${jobId}.log`;
|
|
268
304
|
}
|
|
269
|
-
function
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
305
|
+
function jobExitPath(jobId) {
|
|
306
|
+
return `${JOB_DIR}/${jobId}.exit`;
|
|
307
|
+
}
|
|
308
|
+
async function readLogChunk(fs, file, byteOffset) {
|
|
309
|
+
const contents = await readFileIfExists(fs, file);
|
|
310
|
+
if (contents === null || byteOffset >= contents.byteLength) {
|
|
311
|
+
return null;
|
|
273
312
|
}
|
|
274
|
-
return
|
|
313
|
+
return {
|
|
314
|
+
chunk: {
|
|
315
|
+
byteOffset,
|
|
316
|
+
data: contents.subarray(byteOffset).toString("utf8")
|
|
317
|
+
},
|
|
318
|
+
nextByteOffset: contents.byteLength
|
|
319
|
+
};
|
|
275
320
|
}
|
|
276
|
-
function
|
|
277
|
-
|
|
321
|
+
async function readTextFileIfExists(fs, file) {
|
|
322
|
+
const contents = await readFileIfExists(fs, file);
|
|
323
|
+
return contents?.toString("utf8") ?? null;
|
|
278
324
|
}
|
|
279
|
-
function
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
325
|
+
async function readFileIfExists(fs, file) {
|
|
326
|
+
try {
|
|
327
|
+
const contents = await fs.promises.readFile(file);
|
|
328
|
+
return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
329
|
+
} catch (error2) {
|
|
330
|
+
if (isNodeError(error2) && error2.code === "ENOENT") {
|
|
331
|
+
return null;
|
|
285
332
|
}
|
|
286
|
-
|
|
333
|
+
throw error2;
|
|
287
334
|
}
|
|
288
|
-
return { OPENCODE_CONFIG_CONTENT: JSON.stringify({ mcp }) };
|
|
289
335
|
}
|
|
290
|
-
function
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
336
|
+
async function waitForLogChange(fs, file) {
|
|
337
|
+
const watch = fs.watch;
|
|
338
|
+
if (typeof watch !== "function") {
|
|
339
|
+
await sleep(POLL_INTERVAL_MS);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
await new Promise((resolve2) => {
|
|
343
|
+
let watcher = null;
|
|
344
|
+
const timer = setTimeout(done, POLL_INTERVAL_MS);
|
|
345
|
+
function done() {
|
|
346
|
+
clearTimeout(timer);
|
|
347
|
+
watcher?.close();
|
|
348
|
+
resolve2();
|
|
297
349
|
}
|
|
298
|
-
|
|
299
|
-
|
|
350
|
+
try {
|
|
351
|
+
watcher = watch(file, done);
|
|
352
|
+
} catch {
|
|
353
|
+
done();
|
|
300
354
|
}
|
|
301
|
-
|
|
302
|
-
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function sleep(ms, signal) {
|
|
358
|
+
return new Promise((resolve2, reject) => {
|
|
359
|
+
let timer = null;
|
|
360
|
+
const abort = () => {
|
|
361
|
+
if (timer !== null) {
|
|
362
|
+
clearTimeout(timer);
|
|
363
|
+
}
|
|
364
|
+
reject(new Error("waitForExit aborted."));
|
|
365
|
+
};
|
|
366
|
+
if (signal?.aborted) {
|
|
367
|
+
abort();
|
|
368
|
+
return;
|
|
303
369
|
}
|
|
370
|
+
timer = setTimeout(() => {
|
|
371
|
+
signal?.removeEventListener("abort", abort);
|
|
372
|
+
resolve2();
|
|
373
|
+
}, ms);
|
|
374
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function throwIfAborted(signal) {
|
|
378
|
+
if (signal?.aborted) {
|
|
379
|
+
throw new Error("waitForExit aborted.");
|
|
304
380
|
}
|
|
305
|
-
return args;
|
|
306
381
|
}
|
|
307
|
-
function
|
|
308
|
-
return
|
|
309
|
-
"--with-extension",
|
|
310
|
-
[server.command, ...server.args ?? []].join(" ")
|
|
311
|
-
]);
|
|
382
|
+
function isNodeError(error2) {
|
|
383
|
+
return error2 instanceof Error && "code" in error2;
|
|
312
384
|
}
|
|
313
385
|
|
|
314
|
-
// packages/agent-
|
|
315
|
-
|
|
316
|
-
kind: "cli",
|
|
317
|
-
agentId: "claude-code",
|
|
318
|
-
// ACP adapter support: yes (adapter: "claude")
|
|
319
|
-
adapter: "claude",
|
|
320
|
-
promptFlag: "-p",
|
|
321
|
-
modelFlag: "--model",
|
|
322
|
-
modelStripProviderPrefix: true,
|
|
323
|
-
modelTransform: (model) => model.replaceAll(".", "-"),
|
|
324
|
-
defaultArgs: [
|
|
325
|
-
"--output-format",
|
|
326
|
-
"stream-json",
|
|
327
|
-
"--verbose"
|
|
328
|
-
],
|
|
329
|
-
mcpArgs: serializeJsonMcpArgs,
|
|
330
|
-
modes: {
|
|
331
|
-
yolo: ["--dangerously-skip-permissions"],
|
|
332
|
-
edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
|
|
333
|
-
read: ["--permission-mode", "plan"]
|
|
334
|
-
},
|
|
335
|
-
stdinMode: {
|
|
336
|
-
omitPrompt: true,
|
|
337
|
-
extraArgs: ["--input-format", "text"]
|
|
338
|
-
},
|
|
339
|
-
interactive: {
|
|
340
|
-
defaultArgs: []
|
|
341
|
-
},
|
|
342
|
-
resumeCommand: (threadId) => ["--resume", threadId]
|
|
343
|
-
};
|
|
386
|
+
// packages/agent-harness-tools/src/run-poe-command.ts
|
|
387
|
+
import { randomBytes } from "node:crypto";
|
|
344
388
|
|
|
345
|
-
// packages/agent-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
389
|
+
// packages/agent-harness-tools/src/binary-exists.ts
|
|
390
|
+
function createBinaryExistsDetectors(binaryName) {
|
|
391
|
+
const commonPaths = [
|
|
392
|
+
`/usr/local/bin/${binaryName}`,
|
|
393
|
+
`/usr/bin/${binaryName}`,
|
|
394
|
+
`$HOME/.local/bin/${binaryName}`,
|
|
395
|
+
`$HOME/.claude/local/bin/${binaryName}`
|
|
396
|
+
];
|
|
397
|
+
return [
|
|
398
|
+
{
|
|
399
|
+
command: "which",
|
|
400
|
+
args: [binaryName],
|
|
401
|
+
validate: (result) => result.exitCode === 0
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
command: "where",
|
|
405
|
+
args: [binaryName],
|
|
406
|
+
validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
command: "sh",
|
|
410
|
+
args: ["-c", commonPaths.map((p) => `test -f "${p}"`).join(" || ")],
|
|
411
|
+
validate: (result) => result.exitCode === 0
|
|
412
|
+
}
|
|
413
|
+
];
|
|
414
|
+
}
|
|
371
415
|
|
|
372
|
-
// packages/agent-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
agentId: "opencode",
|
|
376
|
-
// ACP adapter support: yes (adapter: "opencode").
|
|
377
|
-
// OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
|
|
378
|
-
// (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
|
|
379
|
-
adapter: "opencode",
|
|
380
|
-
promptFlag: "run",
|
|
381
|
-
modelFlag: "--model",
|
|
382
|
-
modelStripProviderPrefix: false,
|
|
383
|
-
modelTransform: (model) => {
|
|
384
|
-
return model.startsWith("poe/") ? model : `poe/${model}`;
|
|
385
|
-
},
|
|
386
|
-
defaultArgs: ["--format", "json"],
|
|
387
|
-
modes: {
|
|
388
|
-
yolo: [],
|
|
389
|
-
edit: [],
|
|
390
|
-
read: ["--agent", "plan"]
|
|
391
|
-
},
|
|
392
|
-
interactive: {
|
|
393
|
-
defaultArgs: [],
|
|
394
|
-
promptFlag: "--prompt"
|
|
395
|
-
},
|
|
396
|
-
resumeCommand: (threadId, cwd) => [cwd, "--session", threadId],
|
|
397
|
-
mcpEnv: serializeOpenCodeMcpEnv
|
|
398
|
-
};
|
|
399
|
-
var openCodeAcpSpawnConfig = {
|
|
400
|
-
kind: "acp",
|
|
401
|
-
agentId: "opencode",
|
|
402
|
-
acpArgs: ["acp"],
|
|
403
|
-
skipAuth: true,
|
|
404
|
-
mcpEnv: serializeOpenCodeMcpEnv
|
|
405
|
-
};
|
|
416
|
+
// packages/agent-harness-tools/src/poe-command-execution.ts
|
|
417
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
418
|
+
import os3 from "node:os";
|
|
406
419
|
|
|
407
|
-
// packages/
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
420
|
+
// packages/poe-code-config/src/runtime.ts
|
|
421
|
+
import { existsSync } from "node:fs";
|
|
422
|
+
import path3 from "node:path";
|
|
423
|
+
var defaultWorkspaceExclude = [
|
|
424
|
+
".git",
|
|
425
|
+
"node_modules",
|
|
426
|
+
"dist",
|
|
427
|
+
".turbo",
|
|
428
|
+
".next",
|
|
429
|
+
".poe-code/state.json"
|
|
430
|
+
];
|
|
431
|
+
var runtimeConfigScope = {
|
|
432
|
+
scope: "runtime",
|
|
433
|
+
schema: {
|
|
434
|
+
type: {
|
|
435
|
+
type: "string",
|
|
436
|
+
default: "host",
|
|
437
|
+
doc: "Runtime backend: host, docker, or e2b"
|
|
438
|
+
},
|
|
439
|
+
build_args: {
|
|
440
|
+
type: "json",
|
|
441
|
+
default: {},
|
|
442
|
+
parse: parseBuildArgs,
|
|
443
|
+
doc: "Build arguments passed to the runtime image build"
|
|
444
|
+
},
|
|
445
|
+
mounts: {
|
|
446
|
+
type: "json",
|
|
447
|
+
default: [],
|
|
448
|
+
parse: parseMounts,
|
|
449
|
+
doc: "Additional runtime mounts"
|
|
450
|
+
},
|
|
451
|
+
runner: {
|
|
452
|
+
type: "json",
|
|
453
|
+
default: createDefaultRunnerScope(),
|
|
454
|
+
parse: parseRunner,
|
|
455
|
+
doc: "Runner process and workspace transfer settings"
|
|
456
|
+
},
|
|
457
|
+
link: {
|
|
458
|
+
type: "string",
|
|
459
|
+
default: "",
|
|
460
|
+
doc: "Informational link for the runtime definition"
|
|
461
|
+
},
|
|
462
|
+
image: {
|
|
463
|
+
type: "string",
|
|
464
|
+
default: "",
|
|
465
|
+
doc: "Prebuilt Docker image"
|
|
466
|
+
},
|
|
467
|
+
dockerfile: {
|
|
468
|
+
type: "string",
|
|
469
|
+
default: "",
|
|
470
|
+
doc: "Path to the Dockerfile used for docker or e2b builds"
|
|
471
|
+
},
|
|
472
|
+
build_context: {
|
|
473
|
+
type: "string",
|
|
474
|
+
default: "",
|
|
475
|
+
doc: "Path to the Docker build context"
|
|
476
|
+
},
|
|
477
|
+
workspace_dir: {
|
|
478
|
+
type: "string",
|
|
479
|
+
default: "/workspace",
|
|
480
|
+
doc: "Sandbox-local workspace directory for E2B runtime upload, execution, and download"
|
|
481
|
+
},
|
|
482
|
+
engine: {
|
|
483
|
+
type: "string",
|
|
484
|
+
default: "",
|
|
485
|
+
doc: "Container engine for Docker runtime"
|
|
486
|
+
},
|
|
487
|
+
network: {
|
|
488
|
+
type: "string",
|
|
489
|
+
default: "",
|
|
490
|
+
doc: "Docker network"
|
|
491
|
+
},
|
|
492
|
+
extra_args: {
|
|
493
|
+
type: "json",
|
|
494
|
+
default: void 0,
|
|
495
|
+
parse: parseOptionalStringArray,
|
|
496
|
+
doc: "Extra Docker runtime arguments"
|
|
497
|
+
},
|
|
498
|
+
template_id: {
|
|
499
|
+
type: "string",
|
|
500
|
+
default: "",
|
|
501
|
+
doc: "Prebuilt E2B template id"
|
|
502
|
+
},
|
|
503
|
+
from_template: {
|
|
504
|
+
type: "string",
|
|
505
|
+
default: "",
|
|
506
|
+
doc: "Existing E2B template alias to extend instead of using the Dockerfile FROM image"
|
|
507
|
+
},
|
|
508
|
+
cpu: {
|
|
509
|
+
type: "json",
|
|
510
|
+
default: void 0,
|
|
511
|
+
parse: parseOptionalNumber,
|
|
512
|
+
doc: "E2B CPU count"
|
|
513
|
+
},
|
|
514
|
+
memory_mb: {
|
|
515
|
+
type: "json",
|
|
516
|
+
default: void 0,
|
|
517
|
+
parse: parseOptionalNumber,
|
|
518
|
+
doc: "E2B memory in megabytes"
|
|
519
|
+
},
|
|
520
|
+
timeout_minutes: {
|
|
521
|
+
type: "json",
|
|
522
|
+
default: void 0,
|
|
523
|
+
parse: parseOptionalNumber,
|
|
524
|
+
doc: "E2B timeout in minutes"
|
|
525
|
+
},
|
|
526
|
+
preserve_after_exit_hours: {
|
|
527
|
+
type: "json",
|
|
528
|
+
default: void 0,
|
|
529
|
+
parse: parseOptionalNumber,
|
|
530
|
+
doc: "Hours to keep an E2B sandbox alive after job exit"
|
|
531
|
+
}
|
|
532
|
+
}
|
|
438
533
|
};
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
stdinMode: {
|
|
459
|
-
omitPrompt: true,
|
|
460
|
-
extraArgs: ["--instructions", "-"]
|
|
461
|
-
},
|
|
462
|
-
interactive: {
|
|
463
|
-
defaultArgs: ["session"],
|
|
464
|
-
defaultArgsPosition: "beforePrompt"
|
|
465
|
-
},
|
|
466
|
-
resumeCommand: () => ["run", "--resume", "--text", "continue"]
|
|
467
|
-
};
|
|
468
|
-
var gooseAcpSpawnConfig = {
|
|
469
|
-
kind: "acp",
|
|
470
|
-
agentId: "goose",
|
|
471
|
-
acpArgs: ["acp"],
|
|
472
|
-
env: gooseFileSecretsEnv,
|
|
473
|
-
skipAuth: true
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
// packages/agent-spawn/src/configs/index.ts
|
|
477
|
-
var allSpawnConfigs = [
|
|
478
|
-
claudeCodeSpawnConfig,
|
|
479
|
-
codexSpawnConfig,
|
|
480
|
-
openCodeSpawnConfig,
|
|
481
|
-
kimiSpawnConfig,
|
|
482
|
-
gooseSpawnConfig
|
|
483
|
-
];
|
|
484
|
-
var lookup2 = /* @__PURE__ */ new Map();
|
|
485
|
-
for (const config of allSpawnConfigs) {
|
|
486
|
-
lookup2.set(config.agentId, config);
|
|
534
|
+
function parseRunner(raw) {
|
|
535
|
+
if (raw === void 0) {
|
|
536
|
+
return createDefaultRunnerScope();
|
|
537
|
+
}
|
|
538
|
+
const record = asRecord(raw);
|
|
539
|
+
if (record === void 0) {
|
|
540
|
+
throw new Error("runner: expected an object.");
|
|
541
|
+
}
|
|
542
|
+
const uploadMaxFileMb = parseOptionalNumber(record.upload_max_file_mb, "runner.upload_max_file_mb") ?? 100;
|
|
543
|
+
if (uploadMaxFileMb <= 0) {
|
|
544
|
+
throw new Error("runner.upload_max_file_mb: expected a positive finite number.");
|
|
545
|
+
}
|
|
546
|
+
return omitUndefined({
|
|
547
|
+
detach: parseOptionalBoolean(record.detach, "runner.detach") ?? false,
|
|
548
|
+
upload_max_file_mb: uploadMaxFileMb,
|
|
549
|
+
download_conflict: parseDownloadConflict(record.download_conflict),
|
|
550
|
+
sync: parseRunnerSync(record.sync),
|
|
551
|
+
workspace: parseRunnerWorkspace(record.workspace)
|
|
552
|
+
});
|
|
487
553
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
554
|
+
function createDefaultRunnerScope() {
|
|
555
|
+
return {
|
|
556
|
+
detach: false,
|
|
557
|
+
upload_max_file_mb: 100,
|
|
558
|
+
download_conflict: "refuse",
|
|
559
|
+
sync: "both",
|
|
560
|
+
workspace: {
|
|
561
|
+
exclude: [...defaultWorkspaceExclude]
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function parseRunnerWorkspace(value) {
|
|
566
|
+
if (value === void 0) {
|
|
567
|
+
return {
|
|
568
|
+
exclude: [...defaultWorkspaceExclude]
|
|
569
|
+
};
|
|
496
570
|
}
|
|
497
|
-
|
|
571
|
+
const record = asRecord(value);
|
|
572
|
+
if (record === void 0) {
|
|
573
|
+
throw new Error("runner.workspace: expected an object.");
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
exclude: parseOptionalStringArray(record.exclude, "runner.workspace.exclude") ?? [
|
|
577
|
+
...defaultWorkspaceExclude
|
|
578
|
+
]
|
|
579
|
+
};
|
|
498
580
|
}
|
|
499
|
-
function
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (config.kind !== "cli" || typeof config.mcpArgs !== "function" && typeof config.mcpEnv !== "function") {
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
supported.push(config.agentId);
|
|
581
|
+
function parseDownloadConflict(value) {
|
|
582
|
+
if (value === void 0) {
|
|
583
|
+
return "refuse";
|
|
506
584
|
}
|
|
507
|
-
|
|
585
|
+
if (value === "refuse" || value === "overwrite") {
|
|
586
|
+
return value;
|
|
587
|
+
}
|
|
588
|
+
throw new Error('runner.download_conflict: expected "refuse" or "overwrite".');
|
|
508
589
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
|
|
513
|
-
import path from "node:path";
|
|
514
|
-
|
|
515
|
-
// packages/agent-spawn/src/configs/resolve-config.ts
|
|
516
|
-
function resolveConfig(agentId) {
|
|
517
|
-
const resolvedAgentId = resolveAgentId(agentId);
|
|
518
|
-
if (!resolvedAgentId) {
|
|
519
|
-
throw new Error(`Unknown agent "${agentId}".`);
|
|
590
|
+
function parseRunnerSync(value) {
|
|
591
|
+
if (value === void 0) {
|
|
592
|
+
return "both";
|
|
520
593
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
throw new Error(`Unknown agent "${agentId}".`);
|
|
594
|
+
if (value === "both" || value === "upload" || value === "none") {
|
|
595
|
+
return value;
|
|
524
596
|
}
|
|
525
|
-
|
|
526
|
-
const binaryName = agentDefinition.binaryName;
|
|
527
|
-
return { agentId: resolvedAgentId, binaryName, spawnConfig };
|
|
597
|
+
throw new Error('runner.sync: expected "both", "upload", or "none".');
|
|
528
598
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
if (!servers) {
|
|
533
|
-
return false;
|
|
599
|
+
function parseBuildArgs(value) {
|
|
600
|
+
if (value === void 0) {
|
|
601
|
+
return {};
|
|
534
602
|
}
|
|
535
|
-
|
|
603
|
+
const record = asRecord(value);
|
|
604
|
+
if (record === void 0) {
|
|
605
|
+
throw new Error("build_args: expected an object.");
|
|
606
|
+
}
|
|
607
|
+
const parsed = {};
|
|
608
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
609
|
+
if (typeof entry !== "string") {
|
|
610
|
+
throw new Error(`build_args.${key}: expected a string.`);
|
|
611
|
+
}
|
|
612
|
+
parsed[key] = entry;
|
|
613
|
+
}
|
|
614
|
+
return parsed;
|
|
536
615
|
}
|
|
537
|
-
function
|
|
538
|
-
if (
|
|
616
|
+
function parseMounts(value) {
|
|
617
|
+
if (value === void 0) {
|
|
539
618
|
return [];
|
|
540
619
|
}
|
|
541
|
-
if (!
|
|
542
|
-
throw new Error(
|
|
620
|
+
if (!Array.isArray(value)) {
|
|
621
|
+
throw new Error("mounts: expected an array.");
|
|
543
622
|
}
|
|
544
|
-
|
|
545
|
-
|
|
623
|
+
return value.map((entry, index) => {
|
|
624
|
+
const record = asRecord(entry);
|
|
625
|
+
if (record === void 0) {
|
|
626
|
+
throw new Error(`mounts[${index}]: expected an object.`);
|
|
627
|
+
}
|
|
628
|
+
const source = record.source;
|
|
629
|
+
const target = record.target;
|
|
630
|
+
if (typeof source !== "string") {
|
|
631
|
+
throw new Error(`mounts[${index}].source: expected a string.`);
|
|
632
|
+
}
|
|
633
|
+
if (typeof target !== "string") {
|
|
634
|
+
throw new Error(`mounts[${index}].target: expected a string.`);
|
|
635
|
+
}
|
|
636
|
+
return omitUndefined({
|
|
637
|
+
source,
|
|
638
|
+
target,
|
|
639
|
+
readonly: parseOptionalBoolean(record.readonly, `mounts[${index}].readonly`)
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
function parseOptionalStringArray(value, key = "") {
|
|
644
|
+
if (value === void 0) {
|
|
645
|
+
return void 0;
|
|
546
646
|
}
|
|
547
|
-
|
|
647
|
+
if (!Array.isArray(value)) {
|
|
648
|
+
throw new Error(`${key ? `${key}: ` : ""}expected an array.`);
|
|
649
|
+
}
|
|
650
|
+
return value.map((entry, index) => {
|
|
651
|
+
if (typeof entry !== "string") {
|
|
652
|
+
throw new Error(`${key}[${index}]: expected a string.`);
|
|
653
|
+
}
|
|
654
|
+
return entry;
|
|
655
|
+
});
|
|
548
656
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
657
|
+
function parseOptionalNumber(value, key = "") {
|
|
658
|
+
if (value === void 0) {
|
|
659
|
+
return void 0;
|
|
660
|
+
}
|
|
661
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
662
|
+
throw new Error(`${key ? `${key}: ` : ""}expected a finite number.`);
|
|
663
|
+
}
|
|
664
|
+
return value;
|
|
554
665
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
666
|
+
function parseOptionalBoolean(value, key) {
|
|
667
|
+
if (value === void 0) {
|
|
668
|
+
return void 0;
|
|
669
|
+
}
|
|
670
|
+
if (typeof value !== "boolean") {
|
|
671
|
+
throw new Error(`${key}: expected a boolean.`);
|
|
672
|
+
}
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
function asRecord(value) {
|
|
676
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
677
|
+
return void 0;
|
|
678
|
+
}
|
|
679
|
+
return value;
|
|
680
|
+
}
|
|
681
|
+
function omitUndefined(value) {
|
|
682
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
560
683
|
}
|
|
561
684
|
|
|
562
|
-
// packages/
|
|
563
|
-
function
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
685
|
+
// packages/poe-code-config/src/schema.ts
|
|
686
|
+
function defineScope(scope, schema) {
|
|
687
|
+
return {
|
|
688
|
+
scope,
|
|
689
|
+
schema
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
var integrationsConfigScope = defineScope("integrations", {
|
|
693
|
+
braintrust: {
|
|
694
|
+
type: "json",
|
|
695
|
+
default: {
|
|
696
|
+
enabled: false
|
|
697
|
+
},
|
|
698
|
+
parse: parseBraintrustIntegrationConfig,
|
|
699
|
+
doc: "Braintrust integration configuration"
|
|
567
700
|
}
|
|
568
|
-
|
|
569
|
-
|
|
701
|
+
});
|
|
702
|
+
function parseBraintrustIntegrationConfig(value) {
|
|
703
|
+
if (!isRecord(value)) {
|
|
704
|
+
throw new Error("expected an object");
|
|
570
705
|
}
|
|
571
|
-
|
|
572
|
-
|
|
706
|
+
const enabled = value.enabled === void 0 ? false : value.enabled;
|
|
707
|
+
if (typeof enabled !== "boolean") {
|
|
708
|
+
throw new Error("enabled must be a boolean");
|
|
573
709
|
}
|
|
574
710
|
return {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
711
|
+
enabled,
|
|
712
|
+
...optionalStringEntry("apiKey", value.apiKey),
|
|
713
|
+
...optionalStringEntry("apiUrl", value.apiUrl),
|
|
714
|
+
...optionalStringEntry("project", value.project)
|
|
578
715
|
};
|
|
579
716
|
}
|
|
580
|
-
function
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
function getMcpArgsPosition(config) {
|
|
584
|
-
if (config.mcpArgsPosition) {
|
|
585
|
-
return config.mcpArgsPosition;
|
|
717
|
+
function optionalStringEntry(key, value) {
|
|
718
|
+
if (value === void 0) {
|
|
719
|
+
return {};
|
|
586
720
|
}
|
|
587
|
-
|
|
721
|
+
if (typeof value !== "string") {
|
|
722
|
+
throw new Error(`${key} must be a string`);
|
|
723
|
+
}
|
|
724
|
+
return { [key]: value };
|
|
588
725
|
}
|
|
589
|
-
function
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
726
|
+
function isRecord(value) {
|
|
727
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// packages/poe-code-config/src/plan-scope.ts
|
|
731
|
+
var planConfigScope = defineScope("plan", {
|
|
732
|
+
plan_directory: {
|
|
733
|
+
type: "string",
|
|
734
|
+
default: "docs/plans",
|
|
735
|
+
env: "POE_PLAN_DIRECTORY",
|
|
736
|
+
doc: "Directory where planning documents are stored"
|
|
596
737
|
}
|
|
597
|
-
|
|
598
|
-
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// packages/poe-code-config/src/store.ts
|
|
741
|
+
import path8 from "node:path";
|
|
742
|
+
|
|
743
|
+
// packages/config-extends/src/discover.ts
|
|
744
|
+
import path4 from "node:path";
|
|
745
|
+
async function findBase(name, bases, fs) {
|
|
746
|
+
const checkedPaths = [];
|
|
747
|
+
for (const basePath of bases) {
|
|
748
|
+
for (const extension of [".md", ".yaml", ".yml", ".json"]) {
|
|
749
|
+
const filePath = path4.join(basePath, `${name}${extension}`);
|
|
750
|
+
checkedPaths.push(filePath);
|
|
751
|
+
try {
|
|
752
|
+
return {
|
|
753
|
+
content: await fs.readFile(filePath, "utf8"),
|
|
754
|
+
filePath
|
|
755
|
+
};
|
|
756
|
+
} catch (error2) {
|
|
757
|
+
if (hasCode(error2, "ENOENT")) {
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
throw error2;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
599
763
|
}
|
|
600
|
-
|
|
601
|
-
|
|
764
|
+
throw new Error(`Base "${name}" not found.
|
|
765
|
+
Checked paths:
|
|
766
|
+
- ${checkedPaths.join("\n- ")}`);
|
|
767
|
+
}
|
|
768
|
+
function hasCode(error2, code) {
|
|
769
|
+
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// packages/config-extends/src/parse.ts
|
|
773
|
+
import path5 from "node:path";
|
|
774
|
+
import matter from "gray-matter";
|
|
775
|
+
import { parse as parseYaml } from "yaml";
|
|
776
|
+
function parseDocument(content, filePath) {
|
|
777
|
+
const normalizedContent = stripBom(content);
|
|
778
|
+
const format = detectFormat(normalizedContent, filePath);
|
|
779
|
+
const data = format === "markdown" ? parseMarkdown(normalizedContent) : toData(format === "json" ? JSON.parse(normalizedContent) : parseYaml(normalizedContent));
|
|
780
|
+
const hasExtendsField = Object.hasOwn(data, "extends");
|
|
781
|
+
const extendsValue = data.extends === true;
|
|
782
|
+
delete data.extends;
|
|
783
|
+
return {
|
|
784
|
+
data,
|
|
785
|
+
format,
|
|
786
|
+
extends: extendsValue,
|
|
787
|
+
hasExtendsField
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
function detectFormat(content, filePath) {
|
|
791
|
+
const extension = path5.extname(filePath).toLowerCase();
|
|
792
|
+
if (extension === ".md") {
|
|
793
|
+
return "markdown";
|
|
602
794
|
}
|
|
603
|
-
if (
|
|
604
|
-
|
|
605
|
-
config.promptFlag,
|
|
606
|
-
...stdinMode.omitPrompt ? [] : [options.prompt],
|
|
607
|
-
...stdinMode.extraArgs
|
|
608
|
-
);
|
|
609
|
-
} else {
|
|
610
|
-
args.push(config.promptFlag, options.prompt);
|
|
795
|
+
if (extension === ".yaml" || extension === ".yml") {
|
|
796
|
+
return "yaml";
|
|
611
797
|
}
|
|
612
|
-
if (
|
|
613
|
-
|
|
614
|
-
if (config.modelTransform) model = config.modelTransform(model);
|
|
615
|
-
args.push(config.modelFlag, model);
|
|
798
|
+
if (extension === ".json") {
|
|
799
|
+
return "json";
|
|
616
800
|
}
|
|
617
|
-
if (
|
|
618
|
-
|
|
801
|
+
if (content.startsWith("{")) {
|
|
802
|
+
return "json";
|
|
619
803
|
}
|
|
620
|
-
if (
|
|
621
|
-
|
|
804
|
+
if (content.startsWith("---\n") || content.startsWith("---\r\n")) {
|
|
805
|
+
return "markdown";
|
|
622
806
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
807
|
+
return "yaml";
|
|
808
|
+
}
|
|
809
|
+
function parseMarkdown(content) {
|
|
810
|
+
const document = matter(content);
|
|
811
|
+
return {
|
|
812
|
+
...toData(document.data),
|
|
813
|
+
prompt: document.content
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function toData(value) {
|
|
817
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
818
|
+
return {};
|
|
627
819
|
}
|
|
628
|
-
return {
|
|
820
|
+
return { ...value };
|
|
629
821
|
}
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return
|
|
822
|
+
function stripBom(content) {
|
|
823
|
+
if (!content.startsWith("\uFEFF")) {
|
|
824
|
+
return content;
|
|
825
|
+
}
|
|
826
|
+
return content.slice(1);
|
|
635
827
|
}
|
|
636
828
|
|
|
637
|
-
// packages/
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
muted: (text4) => chalk.dim(text4),
|
|
652
|
-
success: (text4) => chalk.green(text4),
|
|
653
|
-
warning: (text4) => chalk.yellow(text4),
|
|
654
|
-
error: (text4) => chalk.red(text4),
|
|
655
|
-
info: (text4) => chalk.magenta(text4),
|
|
656
|
-
badge: (text4) => chalk.bgYellow.black(` ${text4} `)
|
|
657
|
-
};
|
|
658
|
-
var light = {
|
|
659
|
-
header: (text4) => chalk.hex("#a200ff").bold(text4),
|
|
660
|
-
divider: (text4) => chalk.hex("#666666")(text4),
|
|
661
|
-
prompt: (text4) => chalk.hex("#006699").bold(text4),
|
|
662
|
-
number: (text4) => chalk.hex("#0077cc").bold(text4),
|
|
663
|
-
intro: (text4) => chalk.bgHex("#a200ff").white(` Poe - ${text4} `),
|
|
664
|
-
resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
|
|
665
|
-
errorSymbol: chalk.hex("#cc0000")("\u25A0"),
|
|
666
|
-
accent: (text4) => chalk.hex("#006699").bold(text4),
|
|
667
|
-
muted: (text4) => chalk.hex("#666666")(text4),
|
|
668
|
-
success: (text4) => chalk.hex("#008800")(text4),
|
|
669
|
-
warning: (text4) => chalk.hex("#cc6600")(text4),
|
|
670
|
-
error: (text4) => chalk.hex("#cc0000")(text4),
|
|
671
|
-
info: (text4) => chalk.hex("#a200ff")(text4),
|
|
672
|
-
badge: (text4) => chalk.bgHex("#cc6600").white(` ${text4} `)
|
|
673
|
-
};
|
|
674
|
-
|
|
675
|
-
// packages/design-system/src/tokens/typography.ts
|
|
676
|
-
import chalk2 from "chalk";
|
|
677
|
-
|
|
678
|
-
// packages/design-system/src/components/text.ts
|
|
679
|
-
import chalk3 from "chalk";
|
|
680
|
-
|
|
681
|
-
// packages/design-system/src/internal/output-format.ts
|
|
682
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
683
|
-
var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
|
|
684
|
-
var formatStorage = new AsyncLocalStorage();
|
|
685
|
-
var cached;
|
|
686
|
-
function resolveOutputFormat(env = process.env) {
|
|
687
|
-
const scoped = formatStorage.getStore();
|
|
688
|
-
if (scoped) {
|
|
689
|
-
return scoped;
|
|
690
|
-
}
|
|
691
|
-
if (cached) {
|
|
692
|
-
return cached;
|
|
829
|
+
// packages/config-extends/src/merge.ts
|
|
830
|
+
function mergeLayers(layers) {
|
|
831
|
+
return mergeObjectLayers(layers, []);
|
|
832
|
+
}
|
|
833
|
+
function mergeObjectLayers(layers, path22) {
|
|
834
|
+
const data = {};
|
|
835
|
+
const sources = {};
|
|
836
|
+
for (const key of collectKeys(layers)) {
|
|
837
|
+
const resolved = resolveKey(layers, key, path22);
|
|
838
|
+
if (resolved === void 0) {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
data[key] = resolved.value;
|
|
842
|
+
Object.assign(sources, resolved.sources);
|
|
693
843
|
}
|
|
694
|
-
|
|
695
|
-
cached = VALID_FORMATS.has(raw) ? raw : "terminal";
|
|
696
|
-
return cached;
|
|
844
|
+
return { data, sources };
|
|
697
845
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
846
|
+
function collectKeys(layers) {
|
|
847
|
+
const keys = /* @__PURE__ */ new Set();
|
|
848
|
+
for (const layer of layers) {
|
|
849
|
+
for (const key of Object.keys(layer.data)) {
|
|
850
|
+
keys.add(key);
|
|
851
|
+
}
|
|
704
852
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
853
|
+
return [...keys];
|
|
854
|
+
}
|
|
855
|
+
function resolveKey(layers, key, path22) {
|
|
856
|
+
let winningSource;
|
|
857
|
+
let winningValue;
|
|
858
|
+
const objectLayers = [];
|
|
859
|
+
for (const layer of layers) {
|
|
860
|
+
const candidate = layer.data[key];
|
|
861
|
+
if (!isWinningCandidate(key, candidate)) {
|
|
862
|
+
continue;
|
|
710
863
|
}
|
|
711
|
-
if (
|
|
712
|
-
|
|
864
|
+
if (winningSource === void 0) {
|
|
865
|
+
winningSource = layer.source;
|
|
866
|
+
winningValue = candidate;
|
|
867
|
+
if (isPlainObject(candidate)) {
|
|
868
|
+
objectLayers.push({
|
|
869
|
+
source: layer.source,
|
|
870
|
+
data: candidate
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
continue;
|
|
713
874
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (Number.isFinite(background)) {
|
|
720
|
-
return background >= 8 ? "light" : "dark";
|
|
875
|
+
if (isPlainObject(winningValue) && isPlainObject(candidate)) {
|
|
876
|
+
objectLayers.push({
|
|
877
|
+
source: layer.source,
|
|
878
|
+
data: candidate
|
|
879
|
+
});
|
|
721
880
|
}
|
|
722
881
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
function resolveThemeName(env = process.env) {
|
|
726
|
-
const raw = (env.POE_CODE_THEME ?? env.POE_THEME)?.toLowerCase();
|
|
727
|
-
if (raw === "light" || raw === "dark") {
|
|
728
|
-
return raw;
|
|
882
|
+
if (winningSource === void 0) {
|
|
883
|
+
return void 0;
|
|
729
884
|
}
|
|
730
|
-
const
|
|
731
|
-
if (
|
|
732
|
-
|
|
885
|
+
const fullPath = buildPath(path22, key);
|
|
886
|
+
if (isPlainObject(winningValue)) {
|
|
887
|
+
const merged = mergeObjectLayers(objectLayers, [...path22, key]);
|
|
888
|
+
return {
|
|
889
|
+
value: merged.data,
|
|
890
|
+
sources: {
|
|
891
|
+
[fullPath]: winningSource,
|
|
892
|
+
...merged.sources
|
|
893
|
+
}
|
|
894
|
+
};
|
|
733
895
|
}
|
|
734
|
-
return
|
|
896
|
+
return {
|
|
897
|
+
value: cloneValue(winningValue),
|
|
898
|
+
sources: {
|
|
899
|
+
[fullPath]: winningSource
|
|
900
|
+
}
|
|
901
|
+
};
|
|
735
902
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
return cachedTheme;
|
|
903
|
+
function isWinningCandidate(key, value) {
|
|
904
|
+
if (value === void 0) {
|
|
905
|
+
return false;
|
|
740
906
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
907
|
+
if (key === "prompt" && value === "") {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
return true;
|
|
744
911
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (format === "json") return "info";
|
|
752
|
-
if (format === "markdown") return "(i)";
|
|
753
|
-
return chalk4.magenta("\u25CF");
|
|
754
|
-
},
|
|
755
|
-
get success() {
|
|
756
|
-
const format = resolveOutputFormat();
|
|
757
|
-
if (format === "json") return "success";
|
|
758
|
-
if (format === "markdown") return "[ok]";
|
|
759
|
-
return chalk4.magenta("\u25C6");
|
|
760
|
-
},
|
|
761
|
-
get resolved() {
|
|
762
|
-
const format = resolveOutputFormat();
|
|
763
|
-
if (format === "json") return "resolved";
|
|
764
|
-
if (format === "markdown") return ">";
|
|
765
|
-
return getTheme().resolvedSymbol;
|
|
766
|
-
},
|
|
767
|
-
get errorResolved() {
|
|
768
|
-
const format = resolveOutputFormat();
|
|
769
|
-
if (format === "json") return "error";
|
|
770
|
-
if (format === "markdown") return "[!]";
|
|
771
|
-
return getTheme().errorSymbol;
|
|
772
|
-
},
|
|
773
|
-
get bar() {
|
|
774
|
-
const format = resolveOutputFormat();
|
|
775
|
-
if (format === "json") return "";
|
|
776
|
-
if (format === "markdown") return "|";
|
|
777
|
-
return "\u2502";
|
|
778
|
-
},
|
|
779
|
-
cornerTopRight: "\u256E",
|
|
780
|
-
cornerBottomRight: "\u256F",
|
|
781
|
-
get warning() {
|
|
782
|
-
const format = resolveOutputFormat();
|
|
783
|
-
if (format === "json") return "warning";
|
|
784
|
-
if (format === "markdown") return "[!]";
|
|
785
|
-
return "\u25B2";
|
|
786
|
-
},
|
|
787
|
-
get active() {
|
|
788
|
-
const format = resolveOutputFormat();
|
|
789
|
-
if (format === "json") return "active";
|
|
790
|
-
if (format === "markdown") return "[x]";
|
|
791
|
-
return "\u25C6";
|
|
792
|
-
},
|
|
793
|
-
get inactive() {
|
|
794
|
-
const format = resolveOutputFormat();
|
|
795
|
-
if (format === "json") return "inactive";
|
|
796
|
-
if (format === "markdown") return "[ ]";
|
|
797
|
-
return "\u25CB";
|
|
912
|
+
function buildPath(path22, key) {
|
|
913
|
+
return [...path22, key].join(".");
|
|
914
|
+
}
|
|
915
|
+
function isPlainObject(value) {
|
|
916
|
+
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
917
|
+
return false;
|
|
798
918
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// packages/design-system/src/components/logger.ts
|
|
802
|
-
import chalk6 from "chalk";
|
|
803
|
-
|
|
804
|
-
// packages/design-system/src/prompts/primitives/log.ts
|
|
805
|
-
import chalk5 from "chalk";
|
|
806
|
-
|
|
807
|
-
// packages/design-system/src/internal/strip-ansi.ts
|
|
808
|
-
function stripAnsi(value) {
|
|
809
|
-
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
919
|
+
const prototype = Object.getPrototypeOf(value);
|
|
920
|
+
return prototype === Object.prototype || prototype === null;
|
|
810
921
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
symbol = chalk5.gray("\u2502"),
|
|
815
|
-
secondarySymbol = chalk5.gray("\u2502"),
|
|
816
|
-
spacing: spacing2 = 1,
|
|
817
|
-
withGuide = true
|
|
818
|
-
} = {}) {
|
|
819
|
-
const lines = [];
|
|
820
|
-
const showGuide = withGuide !== false;
|
|
821
|
-
const contentLines = msg.split("\n");
|
|
822
|
-
const prefix = showGuide ? `${symbol} ` : "";
|
|
823
|
-
const continuationPrefix = showGuide ? `${secondarySymbol} ` : "";
|
|
824
|
-
const emptyGuide = showGuide ? secondarySymbol : "";
|
|
825
|
-
for (let index = 0; index < spacing2; index += 1) {
|
|
826
|
-
lines.push(emptyGuide);
|
|
922
|
+
function cloneValue(value) {
|
|
923
|
+
if (Array.isArray(value)) {
|
|
924
|
+
return value.map((entry) => cloneValue(entry));
|
|
827
925
|
}
|
|
828
|
-
if (
|
|
829
|
-
|
|
830
|
-
return;
|
|
926
|
+
if (!isPlainObject(value)) {
|
|
927
|
+
return value;
|
|
831
928
|
}
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
} else {
|
|
836
|
-
lines.push(showGuide ? symbol : "");
|
|
929
|
+
const clone = Object.create(Object.getPrototypeOf(value));
|
|
930
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
931
|
+
clone[key] = cloneValue(entry);
|
|
837
932
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
933
|
+
return clone;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// packages/config-extends/src/resolve.ts
|
|
937
|
+
import path6 from "node:path";
|
|
938
|
+
var MAX_EXTENDS_DEPTH = 5;
|
|
939
|
+
var YIELD_TOKEN = "{{yield}}";
|
|
940
|
+
async function resolve(chain, options) {
|
|
941
|
+
const { baseLayers, documentIndex, documentLayer } = classifyChain(chain);
|
|
942
|
+
const parsedDocument = parseDocument(documentLayer.content, documentLayer.filePath);
|
|
943
|
+
const resolvedBase = shouldResolveBase(parsedDocument, options.autoExtend) ? await resolveBaseChain({
|
|
944
|
+
name: documentLayer.baseName ?? getBaseName(documentLayer.filePath),
|
|
945
|
+
baseLayers,
|
|
946
|
+
options,
|
|
947
|
+
optional: !parsedDocument.extends,
|
|
948
|
+
visited: /* @__PURE__ */ new Set([documentLayer.filePath]),
|
|
949
|
+
depth: 1
|
|
950
|
+
}) : void 0;
|
|
951
|
+
const composedPrompt = composePromptChain(
|
|
952
|
+
{
|
|
953
|
+
source: documentLayer.source,
|
|
954
|
+
data: parsedDocument.data
|
|
955
|
+
},
|
|
956
|
+
resolvedBase?.layers ?? []
|
|
957
|
+
);
|
|
958
|
+
const merged = mergeLayers([
|
|
959
|
+
...collectDataLayers(chain.slice(0, documentIndex)),
|
|
960
|
+
{
|
|
961
|
+
source: documentLayer.source,
|
|
962
|
+
data: withResolvedPrompt(parsedDocument.data, composedPrompt?.prompt)
|
|
963
|
+
},
|
|
964
|
+
...stripResolvedBasePrompts(resolvedBase?.layers ?? [], composedPrompt?.consumedBaseIndexes ?? /* @__PURE__ */ new Set()),
|
|
965
|
+
...collectDataLayers(chain.slice(documentIndex + 1))
|
|
966
|
+
]);
|
|
967
|
+
if (composedPrompt !== void 0 && merged.sources.prompt === documentLayer.source && composedPrompt.source !== void 0) {
|
|
968
|
+
merged.sources.prompt = composedPrompt.source;
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
data: merged.data,
|
|
972
|
+
sources: merged.sources,
|
|
973
|
+
chain: [documentLayer.filePath, ...resolvedBase?.chain ?? []]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
function classifyChain(chain) {
|
|
977
|
+
const baseLayers = [];
|
|
978
|
+
const documentLayers = [];
|
|
979
|
+
for (const [index, layer] of chain.entries()) {
|
|
980
|
+
if (isDataLayer(layer)) {
|
|
841
981
|
continue;
|
|
842
982
|
}
|
|
843
|
-
|
|
983
|
+
if (isDocumentLayer(layer)) {
|
|
984
|
+
documentLayers.push({ index, layer });
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (isBaseLayer(layer)) {
|
|
988
|
+
baseLayers.push(layer);
|
|
989
|
+
}
|
|
844
990
|
}
|
|
845
|
-
|
|
846
|
-
`);
|
|
991
|
+
if (documentLayers.length !== 1) {
|
|
992
|
+
throw new Error(`Exactly one document layer is required, received ${documentLayers.length}.`);
|
|
993
|
+
}
|
|
994
|
+
return {
|
|
995
|
+
baseLayers,
|
|
996
|
+
documentIndex: documentLayers[0].index,
|
|
997
|
+
documentLayer: documentLayers[0].layer
|
|
998
|
+
};
|
|
847
999
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1000
|
+
async function resolveBaseChain({
|
|
1001
|
+
name,
|
|
1002
|
+
baseLayers,
|
|
1003
|
+
options,
|
|
1004
|
+
optional,
|
|
1005
|
+
visited,
|
|
1006
|
+
depth
|
|
1007
|
+
}) {
|
|
1008
|
+
if (depth > MAX_EXTENDS_DEPTH) {
|
|
1009
|
+
throw new Error(`Maximum extends depth exceeded (${MAX_EXTENDS_DEPTH}).`);
|
|
1010
|
+
}
|
|
1011
|
+
let discoveredBase;
|
|
1012
|
+
try {
|
|
1013
|
+
discoveredBase = await findBase(
|
|
1014
|
+
name,
|
|
1015
|
+
baseLayers.map((layer) => layer.path),
|
|
1016
|
+
options.fs
|
|
1017
|
+
);
|
|
1018
|
+
} catch (error2) {
|
|
1019
|
+
if (optional && isBaseNotFoundError(error2)) {
|
|
1020
|
+
return void 0;
|
|
1021
|
+
}
|
|
1022
|
+
throw error2;
|
|
854
1023
|
}
|
|
855
|
-
if (
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1024
|
+
if (visited.has(discoveredBase.filePath)) {
|
|
1025
|
+
if (optional) {
|
|
1026
|
+
return void 0;
|
|
1027
|
+
}
|
|
1028
|
+
throw new Error(
|
|
1029
|
+
`Circular extends detected.
|
|
1030
|
+
Visited files:
|
|
1031
|
+
- ${[...visited, discoveredBase.filePath].join("\n- ")}`
|
|
859
1032
|
);
|
|
860
|
-
return;
|
|
861
1033
|
}
|
|
862
|
-
|
|
1034
|
+
const matchedBaseIndex = baseLayers.findIndex(
|
|
1035
|
+
(layer) => layer.path === path6.dirname(discoveredBase.filePath)
|
|
1036
|
+
);
|
|
1037
|
+
if (matchedBaseIndex === -1) {
|
|
1038
|
+
throw new Error(`Resolved base is outside configured base paths: ${discoveredBase.filePath}`);
|
|
1039
|
+
}
|
|
1040
|
+
const parsedBase = parseDocument(discoveredBase.content, discoveredBase.filePath);
|
|
1041
|
+
const nextVisited = new Set(visited);
|
|
1042
|
+
nextVisited.add(discoveredBase.filePath);
|
|
1043
|
+
const nestedBase = parsedBase.extends ? await resolveBaseChain({
|
|
1044
|
+
name: getBaseName(discoveredBase.filePath),
|
|
1045
|
+
baseLayers: baseLayers.slice(matchedBaseIndex + 1),
|
|
1046
|
+
options,
|
|
1047
|
+
optional: false,
|
|
1048
|
+
visited: nextVisited,
|
|
1049
|
+
depth: depth + 1
|
|
1050
|
+
}) : void 0;
|
|
1051
|
+
return {
|
|
1052
|
+
layers: [
|
|
1053
|
+
{
|
|
1054
|
+
source: baseLayers[matchedBaseIndex].source,
|
|
1055
|
+
data: parsedBase.data
|
|
1056
|
+
},
|
|
1057
|
+
...nestedBase?.layers ?? []
|
|
1058
|
+
],
|
|
1059
|
+
chain: [discoveredBase.filePath, ...nestedBase?.chain ?? []]
|
|
1060
|
+
};
|
|
863
1061
|
}
|
|
864
|
-
function
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1062
|
+
function collectDataLayers(chain) {
|
|
1063
|
+
return chain.filter(isDataLayer);
|
|
1064
|
+
}
|
|
1065
|
+
function composePromptChain(documentLayer, baseLayers) {
|
|
1066
|
+
const documentPrompt = documentLayer.data.prompt;
|
|
1067
|
+
if (documentPrompt !== void 0 && typeof documentPrompt !== "string") {
|
|
1068
|
+
return void 0;
|
|
870
1069
|
}
|
|
871
|
-
if (
|
|
872
|
-
|
|
873
|
-
`${JSON.stringify({ level: "info", message: stripAnsi(msg) })}
|
|
874
|
-
`
|
|
875
|
-
);
|
|
876
|
-
return;
|
|
1070
|
+
if (documentPrompt !== void 0) {
|
|
1071
|
+
assertValidYieldCount(documentPrompt);
|
|
877
1072
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1073
|
+
let prompt = documentPrompt;
|
|
1074
|
+
let source = prompt === void 0 || prompt === "" ? void 0 : documentLayer.source;
|
|
1075
|
+
const consumedBaseIndexes = /* @__PURE__ */ new Set();
|
|
1076
|
+
for (const [index, layer] of baseLayers.entries()) {
|
|
1077
|
+
const candidate = layer.data.prompt;
|
|
1078
|
+
if (candidate === void 0) {
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (typeof candidate !== "string") {
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
assertValidYieldCount(candidate);
|
|
1085
|
+
consumedBaseIndexes.add(index);
|
|
1086
|
+
prompt = composeAdjacentPrompts(prompt, candidate);
|
|
1087
|
+
if (source === void 0 && candidate !== "") {
|
|
1088
|
+
source = layer.source;
|
|
1089
|
+
}
|
|
886
1090
|
}
|
|
887
|
-
if (
|
|
888
|
-
|
|
889
|
-
`${JSON.stringify({ level: "success", message: stripAnsi(msg) })}
|
|
890
|
-
`
|
|
891
|
-
);
|
|
892
|
-
return;
|
|
1091
|
+
if (prompt !== void 0 && prompt.includes(YIELD_TOKEN)) {
|
|
1092
|
+
throw new Error('Final resolved prompt contains an unresolved "{{yield}}" token.');
|
|
893
1093
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
function warn(msg) {
|
|
897
|
-
const format = resolveOutputFormat();
|
|
898
|
-
if (format === "markdown") {
|
|
899
|
-
process.stdout.write(`- **warning:** ${stripAnsi(msg)}
|
|
900
|
-
`);
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
if (format === "json") {
|
|
904
|
-
process.stdout.write(
|
|
905
|
-
`${JSON.stringify({ level: "warn", message: stripAnsi(msg) })}
|
|
906
|
-
`
|
|
907
|
-
);
|
|
908
|
-
return;
|
|
1094
|
+
if (prompt === void 0) {
|
|
1095
|
+
return void 0;
|
|
909
1096
|
}
|
|
910
|
-
|
|
1097
|
+
return {
|
|
1098
|
+
consumedBaseIndexes,
|
|
1099
|
+
prompt,
|
|
1100
|
+
source
|
|
1101
|
+
};
|
|
911
1102
|
}
|
|
912
|
-
function
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
process.stdout.write(`- **error:** ${stripAnsi(msg)}
|
|
916
|
-
`);
|
|
917
|
-
return;
|
|
1103
|
+
function composeAdjacentPrompts(high, low) {
|
|
1104
|
+
if (high === void 0 || high === "") {
|
|
1105
|
+
return low.includes(YIELD_TOKEN) ? replaceYield(low, "") : low;
|
|
918
1106
|
}
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
`${JSON.stringify({ level: "error", message: stripAnsi(msg) })}
|
|
922
|
-
`
|
|
923
|
-
);
|
|
924
|
-
return;
|
|
1107
|
+
if (high.includes(YIELD_TOKEN)) {
|
|
1108
|
+
return replaceYield(high, low);
|
|
925
1109
|
}
|
|
926
|
-
|
|
1110
|
+
if (low.includes(YIELD_TOKEN)) {
|
|
1111
|
+
return replaceYield(low, high);
|
|
1112
|
+
}
|
|
1113
|
+
return high;
|
|
927
1114
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
success,
|
|
931
|
-
message,
|
|
932
|
-
warn,
|
|
933
|
-
error
|
|
934
|
-
};
|
|
935
|
-
|
|
936
|
-
// packages/design-system/src/components/logger.ts
|
|
937
|
-
function createLogger(emitter) {
|
|
938
|
-
const emit = (level, message2) => {
|
|
939
|
-
if (emitter) {
|
|
940
|
-
emitter(message2);
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
if (level === "success") {
|
|
944
|
-
log.success(message2);
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
if (level === "warn") {
|
|
948
|
-
log.warn(message2);
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
if (level === "error") {
|
|
952
|
-
log.error(message2);
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
log.info(message2);
|
|
956
|
-
};
|
|
957
|
-
return {
|
|
958
|
-
info(message2) {
|
|
959
|
-
emit("info", message2);
|
|
960
|
-
},
|
|
961
|
-
success(message2) {
|
|
962
|
-
emit("success", message2);
|
|
963
|
-
},
|
|
964
|
-
warn(message2) {
|
|
965
|
-
emit("warn", message2);
|
|
966
|
-
},
|
|
967
|
-
error(message2) {
|
|
968
|
-
emit("error", message2);
|
|
969
|
-
},
|
|
970
|
-
resolved(label, value) {
|
|
971
|
-
if (emitter) {
|
|
972
|
-
emitter(`${label}: ${value}`);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
log.message(`${label}
|
|
976
|
-
${value}`, { symbol: symbols.resolved });
|
|
977
|
-
},
|
|
978
|
-
errorResolved(label, value) {
|
|
979
|
-
if (emitter) {
|
|
980
|
-
emitter(`${label}: ${value}`);
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
log.message(`${label}
|
|
984
|
-
${value}`, { symbol: symbols.errorResolved });
|
|
985
|
-
},
|
|
986
|
-
message(message2, symbol) {
|
|
987
|
-
if (emitter) {
|
|
988
|
-
emitter(message2);
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
log.message(message2, { symbol: symbol ?? chalk6.gray("\u2502") });
|
|
992
|
-
}
|
|
993
|
-
};
|
|
1115
|
+
function replaceYield(prompt, replacement) {
|
|
1116
|
+
return prompt.replace(YIELD_TOKEN, replacement);
|
|
994
1117
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
// packages/design-system/src/acp/components.ts
|
|
1001
|
-
import chalk7 from "chalk";
|
|
1002
|
-
|
|
1003
|
-
// packages/design-system/src/acp/writer.ts
|
|
1004
|
-
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
1005
|
-
var storage = new AsyncLocalStorage2();
|
|
1006
|
-
|
|
1007
|
-
// packages/design-system/src/acp/components.ts
|
|
1008
|
-
var AGENT_PREFIX = `${chalk7.green.bold("\u2713")} agent: `;
|
|
1009
|
-
|
|
1010
|
-
// packages/design-system/src/dashboard/buffer.ts
|
|
1011
|
-
import chalk8 from "chalk";
|
|
1012
|
-
|
|
1013
|
-
// packages/design-system/src/dashboard/terminal.ts
|
|
1014
|
-
import readline from "node:readline";
|
|
1015
|
-
import { PassThrough } from "node:stream";
|
|
1016
|
-
|
|
1017
|
-
// packages/design-system/src/prompts/index.ts
|
|
1018
|
-
import chalk15 from "chalk";
|
|
1019
|
-
import * as clack from "@clack/prompts";
|
|
1020
|
-
|
|
1021
|
-
// packages/design-system/src/prompts/primitives/cancel.ts
|
|
1022
|
-
import chalk9 from "chalk";
|
|
1023
|
-
import { isCancel } from "@clack/prompts";
|
|
1024
|
-
|
|
1025
|
-
// packages/design-system/src/prompts/primitives/intro.ts
|
|
1026
|
-
import chalk10 from "chalk";
|
|
1027
|
-
|
|
1028
|
-
// packages/design-system/src/prompts/primitives/note.ts
|
|
1029
|
-
import chalk11 from "chalk";
|
|
1030
|
-
|
|
1031
|
-
// packages/design-system/src/prompts/primitives/outro.ts
|
|
1032
|
-
import chalk12 from "chalk";
|
|
1033
|
-
|
|
1034
|
-
// packages/design-system/src/prompts/primitives/spinner.ts
|
|
1035
|
-
import chalk14 from "chalk";
|
|
1036
|
-
|
|
1037
|
-
// packages/design-system/src/static/spinner.ts
|
|
1038
|
-
import chalk13 from "chalk";
|
|
1039
|
-
|
|
1040
|
-
// packages/design-system/src/static/menu.ts
|
|
1041
|
-
import chalk16 from "chalk";
|
|
1042
|
-
|
|
1043
|
-
// packages/agent-spawn/src/autonomous.ts
|
|
1044
|
-
var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
1045
|
-
|
|
1046
|
-
// packages/agent-spawn/src/acp/replay.ts
|
|
1047
|
-
import path2 from "node:path";
|
|
1048
|
-
import { homedir as homedir2 } from "node:os";
|
|
1049
|
-
import { open, readdir } from "node:fs/promises";
|
|
1050
|
-
import { createInterface } from "node:readline";
|
|
1051
|
-
|
|
1052
|
-
// packages/poe-acp-client/src/acp-client.ts
|
|
1053
|
-
import { isAbsolute } from "node:path";
|
|
1054
|
-
|
|
1055
|
-
// packages/poe-acp-client/src/acp-transport.ts
|
|
1056
|
-
import {
|
|
1057
|
-
spawn as spawnChildProcess3
|
|
1058
|
-
} from "node:child_process";
|
|
1059
|
-
|
|
1060
|
-
// packages/poe-acp-client/src/run-report.ts
|
|
1061
|
-
import * as fsPromises from "node:fs/promises";
|
|
1062
|
-
import { homedir } from "node:os";
|
|
1063
|
-
import { join } from "node:path";
|
|
1064
|
-
|
|
1065
|
-
// packages/agent-spawn/src/acp/spawn.ts
|
|
1066
|
-
import { spawn as spawnChildProcess4 } from "node:child_process";
|
|
1067
|
-
|
|
1068
|
-
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
1069
|
-
import path3 from "node:path";
|
|
1070
|
-
import { homedir as homedir3 } from "node:os";
|
|
1071
|
-
import { mkdir, open as open2 } from "node:fs/promises";
|
|
1072
|
-
|
|
1073
|
-
// src/utils/command-checks.ts
|
|
1074
|
-
function formatCommandRunnerResult(result) {
|
|
1075
|
-
const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
|
|
1076
|
-
const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
|
|
1077
|
-
return `stdout:
|
|
1078
|
-
${stdout}
|
|
1079
|
-
stderr:
|
|
1080
|
-
${stderr}`;
|
|
1118
|
+
function assertValidYieldCount(prompt) {
|
|
1119
|
+
if (countYieldTokens(prompt) > 1) {
|
|
1120
|
+
throw new Error('Prompt composition supports exactly one "{{yield}}" token per prompt.');
|
|
1121
|
+
}
|
|
1081
1122
|
}
|
|
1082
|
-
function
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1123
|
+
function countYieldTokens(prompt) {
|
|
1124
|
+
return prompt.split(YIELD_TOKEN).length - 1;
|
|
1125
|
+
}
|
|
1126
|
+
function withResolvedPrompt(data, prompt) {
|
|
1127
|
+
if (prompt === void 0) {
|
|
1128
|
+
return data;
|
|
1129
|
+
}
|
|
1089
1130
|
return {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
async run(context) {
|
|
1093
|
-
if (context.isDryRun) {
|
|
1094
|
-
context.logDryRun?.(
|
|
1095
|
-
`Dry run: ${[binaryName, ...args].join(" ")} (expecting "${options.expectedOutput}")`
|
|
1096
|
-
);
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
const result = modeEnv ? await context.runCommand(binaryName, args, { env: modeEnv }) : await context.runCommand(binaryName, args);
|
|
1100
|
-
if (result.exitCode !== 0) {
|
|
1101
|
-
throw new Error(
|
|
1102
|
-
`spawn ${agentId} failed with exit code ${result.exitCode}.
|
|
1103
|
-
${formatCommandRunnerResult(result)}`
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
if (!result.stdout.includes(options.expectedOutput)) {
|
|
1107
|
-
throw new Error(
|
|
1108
|
-
`spawn ${agentId}: expected "${options.expectedOutput}" in stdout.
|
|
1109
|
-
${formatCommandRunnerResult(result)}`
|
|
1110
|
-
);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1131
|
+
...data,
|
|
1132
|
+
prompt
|
|
1113
1133
|
};
|
|
1114
1134
|
}
|
|
1115
|
-
function
|
|
1116
|
-
return {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
async run({ runCommand: runCommand2 }) {
|
|
1120
|
-
const commonPaths = [
|
|
1121
|
-
`/usr/local/bin/${binaryName}`,
|
|
1122
|
-
`/usr/bin/${binaryName}`,
|
|
1123
|
-
`$HOME/.local/bin/${binaryName}`,
|
|
1124
|
-
`$HOME/.claude/local/bin/${binaryName}`
|
|
1125
|
-
];
|
|
1126
|
-
const detectors = [
|
|
1127
|
-
{
|
|
1128
|
-
command: "which",
|
|
1129
|
-
args: [binaryName],
|
|
1130
|
-
validate: (result) => result.exitCode === 0
|
|
1131
|
-
},
|
|
1132
|
-
{
|
|
1133
|
-
command: "where",
|
|
1134
|
-
args: [binaryName],
|
|
1135
|
-
validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
|
|
1136
|
-
},
|
|
1137
|
-
// Check common installation paths using shell expansion for $HOME
|
|
1138
|
-
{
|
|
1139
|
-
command: "sh",
|
|
1140
|
-
args: [
|
|
1141
|
-
"-c",
|
|
1142
|
-
commonPaths.map((p) => `test -f "${p}"`).join(" || ")
|
|
1143
|
-
],
|
|
1144
|
-
validate: (result) => result.exitCode === 0
|
|
1145
|
-
}
|
|
1146
|
-
];
|
|
1147
|
-
for (const detector of detectors) {
|
|
1148
|
-
const result = await runCommand2(detector.command, detector.args);
|
|
1149
|
-
if (detector.validate(result)) {
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
throw new Error(`${binaryName} CLI binary not found on PATH.`);
|
|
1135
|
+
function stripResolvedBasePrompts(layers, consumedBaseIndexes) {
|
|
1136
|
+
return layers.map((layer, index) => {
|
|
1137
|
+
if (!consumedBaseIndexes.has(index) || typeof layer.data.prompt !== "string") {
|
|
1138
|
+
return layer;
|
|
1154
1139
|
}
|
|
1155
|
-
|
|
1140
|
+
const { prompt: ignoredPrompt, ...data } = layer.data;
|
|
1141
|
+
void ignoredPrompt;
|
|
1142
|
+
return {
|
|
1143
|
+
source: layer.source,
|
|
1144
|
+
data
|
|
1145
|
+
};
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
function getBaseName(filePath) {
|
|
1149
|
+
return path6.basename(filePath, path6.extname(filePath));
|
|
1150
|
+
}
|
|
1151
|
+
function shouldResolveBase(parsedDocument, autoExtend) {
|
|
1152
|
+
return parsedDocument.extends || autoExtend === true && !parsedDocument.hasExtendsField;
|
|
1153
|
+
}
|
|
1154
|
+
function isBaseNotFoundError(error2) {
|
|
1155
|
+
return error2 instanceof Error && error2.message.startsWith('Base "') && error2.message.includes('" not found.\nChecked paths:');
|
|
1156
|
+
}
|
|
1157
|
+
function isDataLayer(layer) {
|
|
1158
|
+
return "data" in layer;
|
|
1159
|
+
}
|
|
1160
|
+
function isDocumentLayer(layer) {
|
|
1161
|
+
return "filePath" in layer && "content" in layer;
|
|
1162
|
+
}
|
|
1163
|
+
function isBaseLayer(layer) {
|
|
1164
|
+
return "path" in layer;
|
|
1156
1165
|
}
|
|
1157
1166
|
|
|
1158
1167
|
// packages/config-mutations/src/mutations/config-mutation.ts
|
|
@@ -1247,7 +1256,7 @@ import * as jsonc from "jsonc-parser";
|
|
|
1247
1256
|
function isConfigObject(value) {
|
|
1248
1257
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1249
1258
|
}
|
|
1250
|
-
function
|
|
1259
|
+
function parse2(content) {
|
|
1251
1260
|
if (!content || content.trim() === "") {
|
|
1252
1261
|
return {};
|
|
1253
1262
|
}
|
|
@@ -1320,7 +1329,7 @@ function prune2(obj, shape) {
|
|
|
1320
1329
|
return { changed, result };
|
|
1321
1330
|
}
|
|
1322
1331
|
var jsonFormat = {
|
|
1323
|
-
parse:
|
|
1332
|
+
parse: parse2,
|
|
1324
1333
|
serialize,
|
|
1325
1334
|
merge: merge2,
|
|
1326
1335
|
prune: prune2
|
|
@@ -1331,7 +1340,7 @@ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
|
1331
1340
|
function isConfigObject2(value) {
|
|
1332
1341
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1333
1342
|
}
|
|
1334
|
-
function
|
|
1343
|
+
function parse3(content) {
|
|
1335
1344
|
if (!content || content.trim() === "") {
|
|
1336
1345
|
return {};
|
|
1337
1346
|
}
|
|
@@ -1395,22 +1404,22 @@ function prune3(obj, shape) {
|
|
|
1395
1404
|
return { changed, result };
|
|
1396
1405
|
}
|
|
1397
1406
|
var tomlFormat = {
|
|
1398
|
-
parse:
|
|
1407
|
+
parse: parse3,
|
|
1399
1408
|
serialize: serialize2,
|
|
1400
1409
|
merge: merge3,
|
|
1401
1410
|
prune: prune3
|
|
1402
1411
|
};
|
|
1403
1412
|
|
|
1404
1413
|
// packages/config-mutations/src/formats/yaml.ts
|
|
1405
|
-
import { parse as
|
|
1414
|
+
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
1406
1415
|
function isConfigObject3(value) {
|
|
1407
1416
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1408
1417
|
}
|
|
1409
|
-
function
|
|
1418
|
+
function parse4(content) {
|
|
1410
1419
|
if (!content || content.trim() === "") {
|
|
1411
1420
|
return {};
|
|
1412
1421
|
}
|
|
1413
|
-
const parsed =
|
|
1422
|
+
const parsed = parseYaml2(content);
|
|
1414
1423
|
if (parsed === null || parsed === void 0) {
|
|
1415
1424
|
return {};
|
|
1416
1425
|
}
|
|
@@ -1470,7 +1479,7 @@ function prune4(obj, shape) {
|
|
|
1470
1479
|
return { changed, result };
|
|
1471
1480
|
}
|
|
1472
1481
|
var yamlFormat = {
|
|
1473
|
-
parse:
|
|
1482
|
+
parse: parse4,
|
|
1474
1483
|
serialize: serialize3,
|
|
1475
1484
|
merge: merge4,
|
|
1476
1485
|
prune: prune4
|
|
@@ -1501,20 +1510,20 @@ function getConfigFormat(pathOrFormat) {
|
|
|
1501
1510
|
}
|
|
1502
1511
|
return formatRegistry[formatName];
|
|
1503
1512
|
}
|
|
1504
|
-
function
|
|
1505
|
-
const ext = getExtension(
|
|
1513
|
+
function detectFormat2(path22) {
|
|
1514
|
+
const ext = getExtension(path22);
|
|
1506
1515
|
return extensionMap[ext];
|
|
1507
1516
|
}
|
|
1508
|
-
function getExtension(
|
|
1509
|
-
const lastDot =
|
|
1517
|
+
function getExtension(path22) {
|
|
1518
|
+
const lastDot = path22.lastIndexOf(".");
|
|
1510
1519
|
if (lastDot === -1) {
|
|
1511
1520
|
return "";
|
|
1512
1521
|
}
|
|
1513
|
-
return
|
|
1522
|
+
return path22.slice(lastDot).toLowerCase();
|
|
1514
1523
|
}
|
|
1515
1524
|
|
|
1516
1525
|
// packages/config-mutations/src/execution/path-utils.ts
|
|
1517
|
-
import
|
|
1526
|
+
import path7 from "node:path";
|
|
1518
1527
|
function expandHome(targetPath, homeDir) {
|
|
1519
1528
|
if (!targetPath?.startsWith("~")) {
|
|
1520
1529
|
return targetPath;
|
|
@@ -1531,7 +1540,7 @@ function expandHome(targetPath, homeDir) {
|
|
|
1531
1540
|
remainder = remainder.slice(1);
|
|
1532
1541
|
}
|
|
1533
1542
|
}
|
|
1534
|
-
return remainder.length === 0 ? homeDir :
|
|
1543
|
+
return remainder.length === 0 ? homeDir : path7.join(homeDir, remainder);
|
|
1535
1544
|
}
|
|
1536
1545
|
function validateHomePath(targetPath) {
|
|
1537
1546
|
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
@@ -1549,19 +1558,19 @@ function resolvePath(rawPath, homeDir, pathMapper) {
|
|
|
1549
1558
|
if (!pathMapper) {
|
|
1550
1559
|
return expanded;
|
|
1551
1560
|
}
|
|
1552
|
-
const rawDirectory =
|
|
1561
|
+
const rawDirectory = path7.dirname(expanded);
|
|
1553
1562
|
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
1554
1563
|
targetDirectory: rawDirectory
|
|
1555
1564
|
});
|
|
1556
|
-
const filename =
|
|
1557
|
-
return filename.length === 0 ? mappedDirectory :
|
|
1565
|
+
const filename = path7.basename(expanded);
|
|
1566
|
+
return filename.length === 0 ? mappedDirectory : path7.join(mappedDirectory, filename);
|
|
1558
1567
|
}
|
|
1559
1568
|
|
|
1560
1569
|
// packages/config-mutations/src/fs-utils.ts
|
|
1561
1570
|
function isNotFound(error2) {
|
|
1562
1571
|
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
1563
1572
|
}
|
|
1564
|
-
async function
|
|
1573
|
+
async function readFileIfExists2(fs, target) {
|
|
1565
1574
|
try {
|
|
1566
1575
|
return await fs.readFile(target, "utf8");
|
|
1567
1576
|
} catch (error2) {
|
|
@@ -1812,8 +1821,8 @@ async function applyChmod(mutation, context, options) {
|
|
|
1812
1821
|
};
|
|
1813
1822
|
}
|
|
1814
1823
|
try {
|
|
1815
|
-
const
|
|
1816
|
-
const currentMode = typeof
|
|
1824
|
+
const stat2 = await context.fs.stat(targetPath);
|
|
1825
|
+
const currentMode = typeof stat2.mode === "number" ? stat2.mode & 511 : null;
|
|
1817
1826
|
if (currentMode === mutation.mode) {
|
|
1818
1827
|
return {
|
|
1819
1828
|
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
@@ -1845,7 +1854,7 @@ async function applyBackup(mutation, context, options) {
|
|
|
1845
1854
|
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1846
1855
|
targetPath
|
|
1847
1856
|
};
|
|
1848
|
-
const content = await
|
|
1857
|
+
const content = await readFileIfExists2(context.fs, targetPath);
|
|
1849
1858
|
if (content === null) {
|
|
1850
1859
|
return {
|
|
1851
1860
|
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
@@ -1869,14 +1878,14 @@ async function applyConfigMerge(mutation, context, options) {
|
|
|
1869
1878
|
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1870
1879
|
targetPath
|
|
1871
1880
|
};
|
|
1872
|
-
const formatName = mutation.format ??
|
|
1881
|
+
const formatName = mutation.format ?? detectFormat2(rawPath);
|
|
1873
1882
|
if (!formatName) {
|
|
1874
1883
|
throw new Error(
|
|
1875
1884
|
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1876
1885
|
);
|
|
1877
1886
|
}
|
|
1878
1887
|
const format = getConfigFormat(formatName);
|
|
1879
|
-
const rawContent = await
|
|
1888
|
+
const rawContent = await readFileIfExists2(context.fs, targetPath);
|
|
1880
1889
|
let current;
|
|
1881
1890
|
try {
|
|
1882
1891
|
current = rawContent === null ? {} : format.parse(rawContent);
|
|
@@ -1915,14 +1924,14 @@ async function applyConfigPrune(mutation, context, options) {
|
|
|
1915
1924
|
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1916
1925
|
targetPath
|
|
1917
1926
|
};
|
|
1918
|
-
const rawContent = await
|
|
1927
|
+
const rawContent = await readFileIfExists2(context.fs, targetPath);
|
|
1919
1928
|
if (rawContent === null) {
|
|
1920
1929
|
return {
|
|
1921
1930
|
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
1922
1931
|
details
|
|
1923
1932
|
};
|
|
1924
1933
|
}
|
|
1925
|
-
const formatName = mutation.format ??
|
|
1934
|
+
const formatName = mutation.format ?? detectFormat2(rawPath);
|
|
1926
1935
|
if (!formatName) {
|
|
1927
1936
|
throw new Error(
|
|
1928
1937
|
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
@@ -1978,14 +1987,14 @@ async function applyConfigTransform(mutation, context, options) {
|
|
|
1978
1987
|
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
1979
1988
|
targetPath
|
|
1980
1989
|
};
|
|
1981
|
-
const formatName = mutation.format ??
|
|
1990
|
+
const formatName = mutation.format ?? detectFormat2(rawPath);
|
|
1982
1991
|
if (!formatName) {
|
|
1983
1992
|
throw new Error(
|
|
1984
1993
|
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
1985
1994
|
);
|
|
1986
1995
|
}
|
|
1987
1996
|
const format = getConfigFormat(formatName);
|
|
1988
|
-
const rawContent = await
|
|
1997
|
+
const rawContent = await readFileIfExists2(context.fs, targetPath);
|
|
1989
1998
|
let current;
|
|
1990
1999
|
try {
|
|
1991
2000
|
current = rawContent === null ? {} : format.parse(rawContent);
|
|
@@ -2085,7 +2094,7 @@ async function applyTemplateMerge(mutation, context, options, formatName) {
|
|
|
2085
2094
|
{ cause: error2 }
|
|
2086
2095
|
);
|
|
2087
2096
|
}
|
|
2088
|
-
const rawContent = await
|
|
2097
|
+
const rawContent = await readFileIfExists2(context.fs, targetPath);
|
|
2089
2098
|
let current;
|
|
2090
2099
|
try {
|
|
2091
2100
|
current = rawContent === null ? {} : format.parse(rawContent);
|
|
@@ -2160,6 +2169,2631 @@ async function executeMutation(mutation, context, options) {
|
|
|
2160
2169
|
import Mustache2 from "mustache";
|
|
2161
2170
|
var originalEscape = Mustache2.escape;
|
|
2162
2171
|
|
|
2172
|
+
// packages/poe-code-config/src/store.ts
|
|
2173
|
+
async function readMergedDocument(fs, globalPath, projectPath) {
|
|
2174
|
+
const globalDocument = await readStoredDocument(fs, globalPath);
|
|
2175
|
+
if (!projectPath || projectPath === globalPath) {
|
|
2176
|
+
return globalDocument.data;
|
|
2177
|
+
}
|
|
2178
|
+
const projectDocument = await readStoredDocument(fs, projectPath);
|
|
2179
|
+
const resolved = await resolve(
|
|
2180
|
+
[
|
|
2181
|
+
{
|
|
2182
|
+
source: "project",
|
|
2183
|
+
filePath: projectPath,
|
|
2184
|
+
content: projectDocument.content
|
|
2185
|
+
},
|
|
2186
|
+
{
|
|
2187
|
+
source: "base",
|
|
2188
|
+
path: path8.dirname(globalPath)
|
|
2189
|
+
}
|
|
2190
|
+
],
|
|
2191
|
+
{
|
|
2192
|
+
fs: createResolvedConfigFs(fs, globalPath, globalDocument.content),
|
|
2193
|
+
autoExtend: true
|
|
2194
|
+
}
|
|
2195
|
+
);
|
|
2196
|
+
return normalizeDocument(resolved.data);
|
|
2197
|
+
}
|
|
2198
|
+
async function readStoredDocument(fs, filePath) {
|
|
2199
|
+
try {
|
|
2200
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
2201
|
+
return await parseStoredDocument(fs, filePath, raw);
|
|
2202
|
+
} catch (error2) {
|
|
2203
|
+
if (isNotFound(error2)) {
|
|
2204
|
+
return {
|
|
2205
|
+
content: EMPTY_DOCUMENT,
|
|
2206
|
+
data: {}
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
throw error2;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
async function parseStoredDocument(fs, filePath, raw) {
|
|
2213
|
+
try {
|
|
2214
|
+
return {
|
|
2215
|
+
content: raw,
|
|
2216
|
+
data: normalizeDocument(JSON.parse(raw))
|
|
2217
|
+
};
|
|
2218
|
+
} catch (error2) {
|
|
2219
|
+
if (error2 instanceof SyntaxError) {
|
|
2220
|
+
await recoverInvalidDocument(fs, filePath, raw);
|
|
2221
|
+
return {
|
|
2222
|
+
content: EMPTY_DOCUMENT,
|
|
2223
|
+
data: {}
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
throw error2;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
function normalizeDocument(value) {
|
|
2230
|
+
if (!isRecord2(value)) {
|
|
2231
|
+
return {};
|
|
2232
|
+
}
|
|
2233
|
+
const document = {};
|
|
2234
|
+
for (const [scope, scopeValues] of Object.entries(value)) {
|
|
2235
|
+
const normalizedValues = normalizeScopeValues(scopeValues);
|
|
2236
|
+
if (Object.keys(normalizedValues).length > 0) {
|
|
2237
|
+
document[scope] = normalizedValues;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return document;
|
|
2241
|
+
}
|
|
2242
|
+
function normalizeScopeValues(value) {
|
|
2243
|
+
if (!isRecord2(value)) {
|
|
2244
|
+
return {};
|
|
2245
|
+
}
|
|
2246
|
+
const normalized = {};
|
|
2247
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2248
|
+
if (entry !== void 0) {
|
|
2249
|
+
normalized[key] = entry;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
return normalized;
|
|
2253
|
+
}
|
|
2254
|
+
function createResolvedConfigFs(fs, globalPath, globalContent) {
|
|
2255
|
+
return {
|
|
2256
|
+
readFile(filePath, _encoding) {
|
|
2257
|
+
if (filePath === globalPath) {
|
|
2258
|
+
return Promise.resolve(globalContent);
|
|
2259
|
+
}
|
|
2260
|
+
return fs.readFile(filePath, "utf8");
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
async function recoverInvalidDocument(fs, filePath, content) {
|
|
2265
|
+
await fs.mkdir(path8.dirname(filePath), { recursive: true });
|
|
2266
|
+
const backupPath = createInvalidBackupPath(filePath);
|
|
2267
|
+
await fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
2268
|
+
await fs.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
|
|
2269
|
+
}
|
|
2270
|
+
function createInvalidBackupPath(filePath) {
|
|
2271
|
+
const directory = path8.dirname(filePath);
|
|
2272
|
+
const baseName = path8.basename(filePath);
|
|
2273
|
+
return path8.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
|
|
2274
|
+
}
|
|
2275
|
+
function isRecord2(value) {
|
|
2276
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2277
|
+
}
|
|
2278
|
+
function resolveConfigPath(homeDir) {
|
|
2279
|
+
return path8.join(homeDir, ".poe-code", "config.json");
|
|
2280
|
+
}
|
|
2281
|
+
function resolveProjectConfigPath(cwd) {
|
|
2282
|
+
return path8.join(cwd, ".poe-code", "config.json");
|
|
2283
|
+
}
|
|
2284
|
+
var EMPTY_DOCUMENT = `${JSON.stringify({}, null, 2)}
|
|
2285
|
+
`;
|
|
2286
|
+
|
|
2287
|
+
// packages/poe-code-config/src/resolve.ts
|
|
2288
|
+
function resolveScope(schema, fileValues, env = {}) {
|
|
2289
|
+
const resolved = {};
|
|
2290
|
+
for (const key of Object.keys(schema)) {
|
|
2291
|
+
const field = schema[key];
|
|
2292
|
+
const envValue = resolveEnvValue(field, env, key);
|
|
2293
|
+
const fileValue = resolveFileValue(field, fileValues?.[key], key);
|
|
2294
|
+
resolved[key] = envValue ?? fileValue ?? field.default;
|
|
2295
|
+
}
|
|
2296
|
+
return resolved;
|
|
2297
|
+
}
|
|
2298
|
+
function resolveEnvValue(field, env, key) {
|
|
2299
|
+
if (!field.env) {
|
|
2300
|
+
return void 0;
|
|
2301
|
+
}
|
|
2302
|
+
const raw = env[field.env];
|
|
2303
|
+
if (raw === void 0) {
|
|
2304
|
+
return void 0;
|
|
2305
|
+
}
|
|
2306
|
+
return coerceValue(field, raw, key);
|
|
2307
|
+
}
|
|
2308
|
+
function resolveFileValue(field, value, key) {
|
|
2309
|
+
return coerceValue(field, value, key);
|
|
2310
|
+
}
|
|
2311
|
+
function coerceValue(field, value, key) {
|
|
2312
|
+
switch (field.type) {
|
|
2313
|
+
case "string":
|
|
2314
|
+
return typeof value === "string" ? value : void 0;
|
|
2315
|
+
case "number":
|
|
2316
|
+
return coerceNumber(value);
|
|
2317
|
+
case "boolean":
|
|
2318
|
+
return coerceBoolean(value);
|
|
2319
|
+
case "json":
|
|
2320
|
+
return coerceJson(field, value, key);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function coerceNumber(value) {
|
|
2324
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2325
|
+
return value;
|
|
2326
|
+
}
|
|
2327
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2328
|
+
return void 0;
|
|
2329
|
+
}
|
|
2330
|
+
const parsed = Number(value);
|
|
2331
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2332
|
+
}
|
|
2333
|
+
function coerceBoolean(value) {
|
|
2334
|
+
if (typeof value === "boolean") {
|
|
2335
|
+
return value;
|
|
2336
|
+
}
|
|
2337
|
+
if (value === "true" || value === "1") {
|
|
2338
|
+
return true;
|
|
2339
|
+
}
|
|
2340
|
+
if (value === "false" || value === "0") {
|
|
2341
|
+
return false;
|
|
2342
|
+
}
|
|
2343
|
+
return void 0;
|
|
2344
|
+
}
|
|
2345
|
+
function coerceJson(field, value, key) {
|
|
2346
|
+
if (value === void 0) {
|
|
2347
|
+
return void 0;
|
|
2348
|
+
}
|
|
2349
|
+
const parsedValue = parseJsonValue(value, key);
|
|
2350
|
+
try {
|
|
2351
|
+
return field.parse(parsedValue);
|
|
2352
|
+
} catch (error2) {
|
|
2353
|
+
const message2 = error2 instanceof Error ? error2.message : "Invalid JSON value.";
|
|
2354
|
+
throw new Error(`Invalid config value for "${key}": ${message2}`);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
function parseJsonValue(value, key) {
|
|
2358
|
+
if (typeof value !== "string") {
|
|
2359
|
+
return value;
|
|
2360
|
+
}
|
|
2361
|
+
try {
|
|
2362
|
+
return JSON.parse(value);
|
|
2363
|
+
} catch {
|
|
2364
|
+
throw new Error(`Invalid config value for "${key}": expected valid JSON.`);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
// packages/poe-code-config/src/inspect.ts
|
|
2369
|
+
import path9 from "node:path";
|
|
2370
|
+
var EMPTY_DOCUMENT2 = `${JSON.stringify({}, null, 2)}
|
|
2371
|
+
`;
|
|
2372
|
+
|
|
2373
|
+
// packages/poe-code-config/src/state/index.ts
|
|
2374
|
+
import os2 from "node:os";
|
|
2375
|
+
|
|
2376
|
+
// packages/poe-code-config/src/state/jobs.ts
|
|
2377
|
+
import path10 from "node:path";
|
|
2378
|
+
|
|
2379
|
+
// packages/poe-code-config/src/state/fs.ts
|
|
2380
|
+
import * as nodeFs2 from "node:fs/promises";
|
|
2381
|
+
|
|
2382
|
+
// packages/poe-code-config/src/state/templates.ts
|
|
2383
|
+
import path11 from "node:path";
|
|
2384
|
+
|
|
2385
|
+
// packages/agent-harness-tools/src/execution-env.ts
|
|
2386
|
+
var executionEnvFactories = /* @__PURE__ */ new Map();
|
|
2387
|
+
function registerExecutionEnvFactory(factory) {
|
|
2388
|
+
executionEnvFactories.set(factory.type, factory);
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
// packages/agent-harness-tools/src/workspace-transfer.ts
|
|
2392
|
+
import { createHash } from "node:crypto";
|
|
2393
|
+
import { promises as nodeFs3 } from "node:fs";
|
|
2394
|
+
import path12 from "node:path";
|
|
2395
|
+
|
|
2396
|
+
// packages/process-runner/src/docker/context.ts
|
|
2397
|
+
import { execSync } from "node:child_process";
|
|
2398
|
+
function detectContext() {
|
|
2399
|
+
try {
|
|
2400
|
+
const output = execSync("colima list --json", {
|
|
2401
|
+
encoding: "utf-8",
|
|
2402
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
2403
|
+
});
|
|
2404
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
2405
|
+
for (const line of lines) {
|
|
2406
|
+
const profile = JSON.parse(line);
|
|
2407
|
+
if (profile.status === "Running" && profile.runtime === "docker") {
|
|
2408
|
+
const name = profile.name ?? profile.profile;
|
|
2409
|
+
if (!name) {
|
|
2410
|
+
continue;
|
|
2411
|
+
}
|
|
2412
|
+
return name === "default" ? "colima" : `colima-${name}`;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
} catch {
|
|
2416
|
+
return null;
|
|
2417
|
+
}
|
|
2418
|
+
return null;
|
|
2419
|
+
}
|
|
2420
|
+
function buildContextArgs(engine, context) {
|
|
2421
|
+
if (engine === "docker" && context) {
|
|
2422
|
+
return ["--context", context];
|
|
2423
|
+
}
|
|
2424
|
+
return [];
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
// packages/process-runner/src/docker/engine.ts
|
|
2428
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
2429
|
+
function detectEngine() {
|
|
2430
|
+
if (isEngineAvailable("docker")) {
|
|
2431
|
+
return "docker";
|
|
2432
|
+
}
|
|
2433
|
+
if (isEngineAvailable("podman")) {
|
|
2434
|
+
return "podman";
|
|
2435
|
+
}
|
|
2436
|
+
throw new Error(
|
|
2437
|
+
"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"
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2440
|
+
function isEngineAvailable(engine) {
|
|
2441
|
+
try {
|
|
2442
|
+
execSync2(`${engine} --version`, {
|
|
2443
|
+
stdio: "ignore"
|
|
2444
|
+
});
|
|
2445
|
+
return true;
|
|
2446
|
+
} catch {
|
|
2447
|
+
return false;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
// packages/process-runner/src/docker/docker-runner.ts
|
|
2452
|
+
import * as childProcess from "node:child_process";
|
|
2453
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2454
|
+
|
|
2455
|
+
// packages/process-runner/src/docker/args.ts
|
|
2456
|
+
import path13 from "node:path";
|
|
2457
|
+
function buildDockerRunArgs(input) {
|
|
2458
|
+
const args = [input.engine];
|
|
2459
|
+
if (input.engine === "docker" && input.context) {
|
|
2460
|
+
args.push("--context", input.context);
|
|
2461
|
+
}
|
|
2462
|
+
args.push("run");
|
|
2463
|
+
if (input.rm) {
|
|
2464
|
+
args.push("--rm");
|
|
2465
|
+
}
|
|
2466
|
+
if (input.detached) {
|
|
2467
|
+
args.push("-d");
|
|
2468
|
+
}
|
|
2469
|
+
if (input.interactive) {
|
|
2470
|
+
args.push("-i");
|
|
2471
|
+
}
|
|
2472
|
+
if (input.tty) {
|
|
2473
|
+
args.push("-t");
|
|
2474
|
+
}
|
|
2475
|
+
args.push("--name", input.containerName);
|
|
2476
|
+
if (input.cwd !== void 0) {
|
|
2477
|
+
args.push("-w", input.cwd);
|
|
2478
|
+
}
|
|
2479
|
+
for (const [key, value] of Object.entries(input.env ?? {})) {
|
|
2480
|
+
args.push("-e", `${key}=${value}`);
|
|
2481
|
+
}
|
|
2482
|
+
for (const mount of input.mounts) {
|
|
2483
|
+
const volume = `${path13.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
|
|
2484
|
+
args.push("-v", volume);
|
|
2485
|
+
}
|
|
2486
|
+
for (const port of input.ports) {
|
|
2487
|
+
const mapping = `${port.host}:${port.container}${port.protocol === void 0 || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
|
|
2488
|
+
args.push("-p", mapping);
|
|
2489
|
+
}
|
|
2490
|
+
if (input.network !== void 0) {
|
|
2491
|
+
args.push("--network", input.network);
|
|
2492
|
+
}
|
|
2493
|
+
args.push(...input.extraArgs, input.image, input.command, ...input.args);
|
|
2494
|
+
return args;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
2498
|
+
import { createHash as createHash2, randomBytes as randomBytes3 } from "node:crypto";
|
|
2499
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2500
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
2501
|
+
import { tmpdir } from "node:os";
|
|
2502
|
+
import path14 from "node:path";
|
|
2503
|
+
|
|
2504
|
+
// packages/process-runner/src/host/host-runner.ts
|
|
2505
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
2506
|
+
function createHostRunner(options = {}) {
|
|
2507
|
+
const detached = options.detached === true;
|
|
2508
|
+
return {
|
|
2509
|
+
name: "host",
|
|
2510
|
+
exec(spec) {
|
|
2511
|
+
const stdinMode = spec.stdin ?? "ignore";
|
|
2512
|
+
const stdoutMode = spec.stdout ?? "pipe";
|
|
2513
|
+
const stderrMode = spec.stderr ?? "pipe";
|
|
2514
|
+
const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
|
|
2515
|
+
const child = spawnChildProcess(spec.command, spec.args ?? [], {
|
|
2516
|
+
cwd: spec.cwd,
|
|
2517
|
+
env: spec.env,
|
|
2518
|
+
stdio,
|
|
2519
|
+
...detached ? { detached: true } : {}
|
|
2520
|
+
});
|
|
2521
|
+
if (detached) {
|
|
2522
|
+
child.unref();
|
|
2523
|
+
}
|
|
2524
|
+
const kill = (signal) => {
|
|
2525
|
+
if (detached && process.platform !== "win32" && child.pid !== void 0) {
|
|
2526
|
+
process.kill(-child.pid, signal);
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
child.kill(signal);
|
|
2530
|
+
};
|
|
2531
|
+
let settled = false;
|
|
2532
|
+
let resolveResult = null;
|
|
2533
|
+
const result = new Promise((resolve2) => {
|
|
2534
|
+
resolveResult = resolve2;
|
|
2535
|
+
});
|
|
2536
|
+
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
2537
|
+
kill("SIGTERM");
|
|
2538
|
+
});
|
|
2539
|
+
child.once("close", (code) => {
|
|
2540
|
+
if (settled) return;
|
|
2541
|
+
settled = true;
|
|
2542
|
+
cleanupAbort();
|
|
2543
|
+
resolveResult?.({ exitCode: code ?? 1 });
|
|
2544
|
+
});
|
|
2545
|
+
child.once("error", () => {
|
|
2546
|
+
if (settled) return;
|
|
2547
|
+
settled = true;
|
|
2548
|
+
cleanupAbort();
|
|
2549
|
+
resolveResult?.({ exitCode: 1 });
|
|
2550
|
+
});
|
|
2551
|
+
return {
|
|
2552
|
+
pid: child.pid ?? null,
|
|
2553
|
+
stdin: child.stdin,
|
|
2554
|
+
stdout: child.stdout,
|
|
2555
|
+
stderr: child.stderr,
|
|
2556
|
+
result,
|
|
2557
|
+
kill
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2562
|
+
function bindAbortSignal(signal, onAbort) {
|
|
2563
|
+
if (signal === void 0) {
|
|
2564
|
+
return () => {
|
|
2565
|
+
};
|
|
2566
|
+
}
|
|
2567
|
+
if (signal.aborted) {
|
|
2568
|
+
onAbort();
|
|
2569
|
+
return () => {
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2573
|
+
return () => {
|
|
2574
|
+
signal.removeEventListener("abort", onAbort);
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
2579
|
+
var containerCommand = ["sh", "-c", "while :; do sleep 3600; done"];
|
|
2580
|
+
var dockerExecutionEnvFactory = {
|
|
2581
|
+
type: "docker",
|
|
2582
|
+
supportsDetach: true,
|
|
2583
|
+
async open(spec) {
|
|
2584
|
+
const runtime = parseDockerRuntime(spec.runtime);
|
|
2585
|
+
const runner = spec.hostRunner ?? createHostRunner();
|
|
2586
|
+
const engine = runtime.engine ?? detectEngine();
|
|
2587
|
+
const context = detectContext();
|
|
2588
|
+
const image = await resolveImage({
|
|
2589
|
+
spec,
|
|
2590
|
+
runtime,
|
|
2591
|
+
runner,
|
|
2592
|
+
engine,
|
|
2593
|
+
context
|
|
2594
|
+
});
|
|
2595
|
+
const containerName = createContainerName();
|
|
2596
|
+
const runArgs = buildDockerRunArgs({
|
|
2597
|
+
engine,
|
|
2598
|
+
context,
|
|
2599
|
+
image,
|
|
2600
|
+
command: containerCommand[0],
|
|
2601
|
+
args: containerCommand.slice(1),
|
|
2602
|
+
cwd: void 0,
|
|
2603
|
+
env: void 0,
|
|
2604
|
+
mounts: runtime.mounts ?? [],
|
|
2605
|
+
ports: [],
|
|
2606
|
+
network: runtime.network,
|
|
2607
|
+
containerName,
|
|
2608
|
+
detached: true,
|
|
2609
|
+
interactive: true,
|
|
2610
|
+
tty: false,
|
|
2611
|
+
rm: false,
|
|
2612
|
+
extraArgs: runtime.extra_args ?? []
|
|
2613
|
+
});
|
|
2614
|
+
const [command, ...args] = runArgs;
|
|
2615
|
+
const id = (await runAndRead(runner, { command, args, stdout: "pipe", stderr: "pipe" })).trim();
|
|
2616
|
+
return createDockerEnv({
|
|
2617
|
+
id,
|
|
2618
|
+
spec,
|
|
2619
|
+
runner,
|
|
2620
|
+
engine,
|
|
2621
|
+
context
|
|
2622
|
+
});
|
|
2623
|
+
},
|
|
2624
|
+
async attach(envId, context) {
|
|
2625
|
+
const engine = detectEngine();
|
|
2626
|
+
return createDockerEnv({
|
|
2627
|
+
id: envId,
|
|
2628
|
+
spec: createAttachedSpec(context?.cwd),
|
|
2629
|
+
runner: createHostRunner(),
|
|
2630
|
+
engine,
|
|
2631
|
+
context: detectContext(),
|
|
2632
|
+
attachedJobId: context?.jobId
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
function createDockerEnv(input) {
|
|
2637
|
+
const containerRef = input.id;
|
|
2638
|
+
return {
|
|
2639
|
+
id: containerRef,
|
|
2640
|
+
job: input.attachedJobId === void 0 ? null : createContainerJob(containerRef, input.runner, input.engine, input.context, input.attachedJobId),
|
|
2641
|
+
async uploadWorkspace() {
|
|
2642
|
+
const tempDir = mkdtempSync(path14.join(tmpdir(), "poe-docker-upload-"));
|
|
2643
|
+
const archivePath = path14.join(tempDir, "workspace.tar");
|
|
2644
|
+
try {
|
|
2645
|
+
const excludeArgs = input.spec.uploadIgnoreFiles.flatMap((ignored) => [
|
|
2646
|
+
"--exclude",
|
|
2647
|
+
ignored
|
|
2648
|
+
]);
|
|
2649
|
+
const tarArgs = [...excludeArgs, "-cf", archivePath, "-C", input.spec.cwd, "."];
|
|
2650
|
+
await runOrThrow(input.runner, {
|
|
2651
|
+
command: "tar",
|
|
2652
|
+
args: tarArgs,
|
|
2653
|
+
stdout: "pipe",
|
|
2654
|
+
stderr: "pipe"
|
|
2655
|
+
});
|
|
2656
|
+
await runOrThrow(input.runner, {
|
|
2657
|
+
command: input.engine,
|
|
2658
|
+
args: [
|
|
2659
|
+
...buildContextArgs(input.engine, input.context),
|
|
2660
|
+
"cp",
|
|
2661
|
+
archivePath,
|
|
2662
|
+
`${containerRef}:/tmp/poe-workspace-upload.tar`
|
|
2663
|
+
],
|
|
2664
|
+
stdout: "pipe",
|
|
2665
|
+
stderr: "pipe"
|
|
2666
|
+
});
|
|
2667
|
+
await runOrThrow(input.runner, {
|
|
2668
|
+
command: input.engine,
|
|
2669
|
+
args: [
|
|
2670
|
+
...buildContextArgs(input.engine, input.context),
|
|
2671
|
+
"exec",
|
|
2672
|
+
containerRef,
|
|
2673
|
+
"sh",
|
|
2674
|
+
"-c",
|
|
2675
|
+
`mkdir -p ${shellQuote(input.spec.cwd)} && tar -xf /tmp/poe-workspace-upload.tar -C ${shellQuote(input.spec.cwd)}`
|
|
2676
|
+
],
|
|
2677
|
+
stdout: "pipe",
|
|
2678
|
+
stderr: "pipe"
|
|
2679
|
+
});
|
|
2680
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
2681
|
+
} finally {
|
|
2682
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
2683
|
+
}
|
|
2684
|
+
},
|
|
2685
|
+
async downloadWorkspace(opts) {
|
|
2686
|
+
const tempDir = mkdtempSync(path14.join(tmpdir(), "poe-docker-download-"));
|
|
2687
|
+
const archivePath = path14.join(tempDir, "workspace.tar");
|
|
2688
|
+
try {
|
|
2689
|
+
await runOrThrow(input.runner, {
|
|
2690
|
+
command: input.engine,
|
|
2691
|
+
args: [
|
|
2692
|
+
...buildContextArgs(input.engine, input.context),
|
|
2693
|
+
"exec",
|
|
2694
|
+
containerRef,
|
|
2695
|
+
"sh",
|
|
2696
|
+
"-c",
|
|
2697
|
+
`tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote(input.spec.cwd)} .`
|
|
2698
|
+
],
|
|
2699
|
+
stdout: "pipe",
|
|
2700
|
+
stderr: "pipe"
|
|
2701
|
+
});
|
|
2702
|
+
await runOrThrow(input.runner, {
|
|
2703
|
+
command: input.engine,
|
|
2704
|
+
args: [
|
|
2705
|
+
...buildContextArgs(input.engine, input.context),
|
|
2706
|
+
"cp",
|
|
2707
|
+
`${containerRef}:/tmp/poe-workspace-download.tar`,
|
|
2708
|
+
archivePath
|
|
2709
|
+
],
|
|
2710
|
+
stdout: "pipe",
|
|
2711
|
+
stderr: "pipe"
|
|
2712
|
+
});
|
|
2713
|
+
const extractMode = opts.conflictPolicy === "refuse" ? "-xkf" : "-xf";
|
|
2714
|
+
await runOrThrow(input.runner, {
|
|
2715
|
+
command: "tar",
|
|
2716
|
+
args: [extractMode, archivePath, "-C", input.spec.cwd],
|
|
2717
|
+
stdout: "pipe",
|
|
2718
|
+
stderr: "pipe"
|
|
2719
|
+
});
|
|
2720
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
2721
|
+
} finally {
|
|
2722
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
2723
|
+
}
|
|
2724
|
+
},
|
|
2725
|
+
exec(spec) {
|
|
2726
|
+
return input.runner.exec({
|
|
2727
|
+
command: input.engine,
|
|
2728
|
+
args: [
|
|
2729
|
+
...buildContextArgs(input.engine, input.context),
|
|
2730
|
+
"exec",
|
|
2731
|
+
...spec.stdin === "pipe" || spec.stdin === "inherit" ? ["-i"] : [],
|
|
2732
|
+
...spec.tty === true ? ["-t"] : [],
|
|
2733
|
+
...spec.cwd !== void 0 ? ["-w", spec.cwd] : [],
|
|
2734
|
+
...buildEnvArgs(spec.env),
|
|
2735
|
+
containerRef,
|
|
2736
|
+
spec.command,
|
|
2737
|
+
...spec.args ?? []
|
|
2738
|
+
],
|
|
2739
|
+
stdin: spec.stdin,
|
|
2740
|
+
stdout: spec.stdout,
|
|
2741
|
+
stderr: spec.stderr,
|
|
2742
|
+
tty: spec.tty
|
|
2743
|
+
});
|
|
2744
|
+
},
|
|
2745
|
+
async detach() {
|
|
2746
|
+
return createContainerJob(containerRef, input.runner, input.engine, input.context);
|
|
2747
|
+
},
|
|
2748
|
+
shell() {
|
|
2749
|
+
const shellSpec = input.spec.shellSpec;
|
|
2750
|
+
return this.exec({
|
|
2751
|
+
command: shellSpec?.command ?? input.spec.env.SHELL ?? "sh",
|
|
2752
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
2753
|
+
cwd: input.spec.cwd,
|
|
2754
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env,
|
|
2755
|
+
stdin: "inherit",
|
|
2756
|
+
stdout: "inherit",
|
|
2757
|
+
stderr: "inherit",
|
|
2758
|
+
tty: true
|
|
2759
|
+
});
|
|
2760
|
+
},
|
|
2761
|
+
async close() {
|
|
2762
|
+
await runOrThrow(input.runner, {
|
|
2763
|
+
command: input.engine,
|
|
2764
|
+
args: [...buildContextArgs(input.engine, input.context), "rm", "-f", containerRef],
|
|
2765
|
+
stdout: "pipe",
|
|
2766
|
+
stderr: "pipe"
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
async function resolveImage(input) {
|
|
2772
|
+
if (input.runtime.image !== void 0) {
|
|
2773
|
+
return input.runtime.image;
|
|
2774
|
+
}
|
|
2775
|
+
const result = await buildDockerRuntimeTemplate({
|
|
2776
|
+
cwd: input.spec.cwd,
|
|
2777
|
+
runtime: input.runtime,
|
|
2778
|
+
state: input.spec.state,
|
|
2779
|
+
runner: input.runner
|
|
2780
|
+
});
|
|
2781
|
+
return result.image;
|
|
2782
|
+
}
|
|
2783
|
+
async function buildDockerRuntimeTemplate(input) {
|
|
2784
|
+
const runner = input.runner ?? createHostRunner();
|
|
2785
|
+
const engine = input.runtime.engine ?? detectEngine();
|
|
2786
|
+
const context = detectContext();
|
|
2787
|
+
const dockerfilePath = path14.resolve(
|
|
2788
|
+
input.cwd,
|
|
2789
|
+
input.runtime.dockerfile ?? path14.join(".poe-code", "Dockerfile")
|
|
2790
|
+
);
|
|
2791
|
+
const buildContext = path14.resolve(input.cwd, input.runtime.build_context ?? ".");
|
|
2792
|
+
const dockerfileBytes = await readFile2(dockerfilePath);
|
|
2793
|
+
const hash = hashDockerTemplate(dockerfileBytes, input.runtime.build_args ?? {});
|
|
2794
|
+
const cached2 = input.force ? null : await input.state?.templates.get("docker", hash);
|
|
2795
|
+
if (cached2?.image !== void 0) {
|
|
2796
|
+
return {
|
|
2797
|
+
backend: "docker",
|
|
2798
|
+
hash,
|
|
2799
|
+
image: cached2.image,
|
|
2800
|
+
cached: true
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
const image = `poe-code/local:${hash}`;
|
|
2804
|
+
await buildImage({
|
|
2805
|
+
runner,
|
|
2806
|
+
engine,
|
|
2807
|
+
context,
|
|
2808
|
+
image,
|
|
2809
|
+
dockerfilePath,
|
|
2810
|
+
buildContext,
|
|
2811
|
+
buildArgs: input.runtime.build_args ?? {}
|
|
2812
|
+
});
|
|
2813
|
+
await input.state?.templates.put("docker", {
|
|
2814
|
+
hash,
|
|
2815
|
+
image,
|
|
2816
|
+
runtime_type: "docker",
|
|
2817
|
+
dockerfile_path: dockerfilePath,
|
|
2818
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2819
|
+
});
|
|
2820
|
+
return {
|
|
2821
|
+
backend: "docker",
|
|
2822
|
+
hash,
|
|
2823
|
+
image,
|
|
2824
|
+
cached: false
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
function hashDockerTemplate(dockerfileBytes, buildArgs) {
|
|
2828
|
+
const hash = createHash2("sha256");
|
|
2829
|
+
hash.update(dockerfileBytes);
|
|
2830
|
+
hash.update("\0");
|
|
2831
|
+
for (const [key, value] of sortedBuildArgs(buildArgs)) {
|
|
2832
|
+
hash.update(key);
|
|
2833
|
+
hash.update("=");
|
|
2834
|
+
hash.update(value);
|
|
2835
|
+
hash.update("\0");
|
|
2836
|
+
}
|
|
2837
|
+
return hash.digest("hex");
|
|
2838
|
+
}
|
|
2839
|
+
async function buildImage(input) {
|
|
2840
|
+
await runOrThrow(input.runner, {
|
|
2841
|
+
command: input.engine,
|
|
2842
|
+
args: [
|
|
2843
|
+
...buildContextArgs(input.engine, input.context),
|
|
2844
|
+
"build",
|
|
2845
|
+
"--tag",
|
|
2846
|
+
input.image,
|
|
2847
|
+
"-f",
|
|
2848
|
+
input.dockerfilePath,
|
|
2849
|
+
...sortedBuildArgs(input.buildArgs).flatMap(([key, value]) => [
|
|
2850
|
+
"--build-arg",
|
|
2851
|
+
`${key}=${value}`
|
|
2852
|
+
]),
|
|
2853
|
+
input.buildContext
|
|
2854
|
+
],
|
|
2855
|
+
stdout: "pipe",
|
|
2856
|
+
stderr: "pipe"
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
function parseDockerRuntime(runtime) {
|
|
2860
|
+
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
|
|
2861
|
+
throw new Error("docker runtime must be an object");
|
|
2862
|
+
}
|
|
2863
|
+
const record = runtime;
|
|
2864
|
+
if (record.type !== "docker") {
|
|
2865
|
+
throw new Error('docker runtime type must be "docker"');
|
|
2866
|
+
}
|
|
2867
|
+
return record;
|
|
2868
|
+
}
|
|
2869
|
+
async function runAndRead(runner, spec) {
|
|
2870
|
+
const handle = runner.exec(spec);
|
|
2871
|
+
const stdout = readStream(handle.stdout);
|
|
2872
|
+
const stderr = readStream(handle.stderr);
|
|
2873
|
+
const result = await handle.result;
|
|
2874
|
+
const output = await stdout;
|
|
2875
|
+
if (result.exitCode !== 0) {
|
|
2876
|
+
const errorOutput = await stderr;
|
|
2877
|
+
throw new Error(
|
|
2878
|
+
`Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${errorOutput ? `
|
|
2879
|
+
${errorOutput}` : ""}`
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
return output;
|
|
2883
|
+
}
|
|
2884
|
+
async function runOrThrow(runner, spec) {
|
|
2885
|
+
await runAndRead(runner, spec);
|
|
2886
|
+
}
|
|
2887
|
+
async function readStream(stream) {
|
|
2888
|
+
if (stream === null) {
|
|
2889
|
+
return "";
|
|
2890
|
+
}
|
|
2891
|
+
stream.setEncoding("utf8");
|
|
2892
|
+
const chunks = [];
|
|
2893
|
+
for await (const chunk of stream) {
|
|
2894
|
+
chunks.push(String(chunk));
|
|
2895
|
+
}
|
|
2896
|
+
return chunks.join("");
|
|
2897
|
+
}
|
|
2898
|
+
function sortedBuildArgs(buildArgs) {
|
|
2899
|
+
return Object.entries(buildArgs).sort(([left], [right]) => left.localeCompare(right));
|
|
2900
|
+
}
|
|
2901
|
+
function buildEnvArgs(env) {
|
|
2902
|
+
if (env === void 0) {
|
|
2903
|
+
return [];
|
|
2904
|
+
}
|
|
2905
|
+
return Object.entries(env).flatMap(([key, value]) => ["-e", `${key}=${value}`]);
|
|
2906
|
+
}
|
|
2907
|
+
function createContainerName() {
|
|
2908
|
+
return `poe-env-${randomBytes3(6).toString("hex")}`;
|
|
2909
|
+
}
|
|
2910
|
+
function createContainerJob(containerId, runner, engine, context, jobId = containerId) {
|
|
2911
|
+
return {
|
|
2912
|
+
id: jobId,
|
|
2913
|
+
envId: containerId,
|
|
2914
|
+
tool: "docker",
|
|
2915
|
+
argv: ["attach", containerId],
|
|
2916
|
+
async status() {
|
|
2917
|
+
const handle = runner.exec({
|
|
2918
|
+
command: engine,
|
|
2919
|
+
args: [
|
|
2920
|
+
...buildContextArgs(engine, context),
|
|
2921
|
+
"inspect",
|
|
2922
|
+
"-f",
|
|
2923
|
+
"{{.State.Status}}",
|
|
2924
|
+
containerId
|
|
2925
|
+
],
|
|
2926
|
+
stdout: "pipe",
|
|
2927
|
+
stderr: "pipe"
|
|
2928
|
+
});
|
|
2929
|
+
const stdout = await readStream(handle.stdout);
|
|
2930
|
+
const result = await handle.result;
|
|
2931
|
+
if (result.exitCode !== 0) {
|
|
2932
|
+
return "lost";
|
|
2933
|
+
}
|
|
2934
|
+
return stdout.trim() === "running" ? "running" : "exited";
|
|
2935
|
+
},
|
|
2936
|
+
async *stream(opts) {
|
|
2937
|
+
const handle = runner.exec({
|
|
2938
|
+
command: engine,
|
|
2939
|
+
args: [
|
|
2940
|
+
...buildContextArgs(engine, context),
|
|
2941
|
+
"exec",
|
|
2942
|
+
containerId,
|
|
2943
|
+
"sh",
|
|
2944
|
+
"-c",
|
|
2945
|
+
`test -f ${shellQuote(`/tmp/poe-jobs/${jobId}.log`)} && tail -c +${(opts?.sinceByte ?? 0) + 1} ${shellQuote(`/tmp/poe-jobs/${jobId}.log`)} || true`
|
|
2946
|
+
],
|
|
2947
|
+
stdout: "pipe",
|
|
2948
|
+
stderr: "pipe"
|
|
2949
|
+
});
|
|
2950
|
+
const stdout = await readStream(handle.stdout);
|
|
2951
|
+
await handle.result;
|
|
2952
|
+
if (stdout.length > 0) {
|
|
2953
|
+
yield { byteOffset: opts?.sinceByte ?? 0, data: stdout };
|
|
2954
|
+
}
|
|
2955
|
+
},
|
|
2956
|
+
async wait() {
|
|
2957
|
+
const handle = runner.exec({
|
|
2958
|
+
command: engine,
|
|
2959
|
+
args: [...buildContextArgs(engine, context), "wait", containerId],
|
|
2960
|
+
stdout: "pipe",
|
|
2961
|
+
stderr: "pipe"
|
|
2962
|
+
});
|
|
2963
|
+
const stdout = await readStream(handle.stdout);
|
|
2964
|
+
const result = await handle.result;
|
|
2965
|
+
return { exitCode: Number.parseInt(stdout.trim(), 10) || result.exitCode };
|
|
2966
|
+
},
|
|
2967
|
+
async kill(signal) {
|
|
2968
|
+
const args = signal === void 0 || signal === "SIGTERM" ? ["stop", containerId] : ["kill", ...signal === "SIGKILL" ? [] : [`--signal=${signal}`], containerId];
|
|
2969
|
+
await runOrThrow(runner, {
|
|
2970
|
+
command: engine,
|
|
2971
|
+
args: [...buildContextArgs(engine, context), ...args],
|
|
2972
|
+
stdout: "pipe",
|
|
2973
|
+
stderr: "pipe"
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
function createAttachedSpec(cwd = "/workspace") {
|
|
2979
|
+
return {
|
|
2980
|
+
cwd,
|
|
2981
|
+
runtime: {
|
|
2982
|
+
type: "docker",
|
|
2983
|
+
image: "attached",
|
|
2984
|
+
build_args: {},
|
|
2985
|
+
mounts: []
|
|
2986
|
+
},
|
|
2987
|
+
env: {},
|
|
2988
|
+
uploadIgnoreFiles: [],
|
|
2989
|
+
jobLabel: {
|
|
2990
|
+
tool: "docker",
|
|
2991
|
+
argv: []
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
function shellQuote(value) {
|
|
2996
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
// packages/process-runner/src/host/host-execution-env.ts
|
|
3000
|
+
var hostExecutionEnvFactory = {
|
|
3001
|
+
type: "host",
|
|
3002
|
+
supportsDetach: false,
|
|
3003
|
+
async open(openSpec) {
|
|
3004
|
+
return {
|
|
3005
|
+
id: "host",
|
|
3006
|
+
job: null,
|
|
3007
|
+
async uploadWorkspace() {
|
|
3008
|
+
return {
|
|
3009
|
+
files: 0,
|
|
3010
|
+
bytes: 0,
|
|
3011
|
+
skipped: []
|
|
3012
|
+
};
|
|
3013
|
+
},
|
|
3014
|
+
async downloadWorkspace() {
|
|
3015
|
+
return {
|
|
3016
|
+
files: 0,
|
|
3017
|
+
bytes: 0,
|
|
3018
|
+
conflicts: []
|
|
3019
|
+
};
|
|
3020
|
+
},
|
|
3021
|
+
exec(spec) {
|
|
3022
|
+
return createHostRunner().exec(spec);
|
|
3023
|
+
},
|
|
3024
|
+
async detach() {
|
|
3025
|
+
throw new Error("host runtime does not support detach because host has no addressable env");
|
|
3026
|
+
},
|
|
3027
|
+
shell() {
|
|
3028
|
+
const shellSpec = openSpec.shellSpec;
|
|
3029
|
+
return createHostRunner().exec({
|
|
3030
|
+
command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
3031
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
3032
|
+
cwd: openSpec.cwd,
|
|
3033
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
|
|
3034
|
+
stdin: "inherit",
|
|
3035
|
+
stdout: "inherit",
|
|
3036
|
+
stderr: "inherit",
|
|
3037
|
+
tty: true
|
|
3038
|
+
});
|
|
3039
|
+
},
|
|
3040
|
+
async close() {
|
|
3041
|
+
}
|
|
3042
|
+
};
|
|
3043
|
+
},
|
|
3044
|
+
async attach() {
|
|
3045
|
+
throw new Error("host runtime does not support reattach");
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
|
|
3049
|
+
// packages/process-runner/src/testing/mock-runner.ts
|
|
3050
|
+
import { Readable, Writable } from "node:stream";
|
|
3051
|
+
|
|
3052
|
+
// packages/runner-e2b/src/factory.ts
|
|
3053
|
+
import path18 from "node:path";
|
|
3054
|
+
|
|
3055
|
+
// packages/runner-e2b/src/sdk.ts
|
|
3056
|
+
import { Template, Sandbox } from "e2b";
|
|
3057
|
+
async function createSandbox(opts) {
|
|
3058
|
+
return Sandbox.create(opts.templateId, {
|
|
3059
|
+
apiKey: opts.apiKey,
|
|
3060
|
+
envs: opts.env,
|
|
3061
|
+
...opts.timeoutMinutes === void 0 ? {} : { timeoutMs: opts.timeoutMinutes * 6e4 }
|
|
3062
|
+
});
|
|
3063
|
+
}
|
|
3064
|
+
async function connectSandbox(id, apiKey) {
|
|
3065
|
+
return Sandbox.connect(id, apiKey === void 0 ? void 0 : { apiKey });
|
|
3066
|
+
}
|
|
3067
|
+
async function buildTemplate(opts) {
|
|
3068
|
+
const template = Template({ fileContextPath: opts.buildContext }).fromDockerfile(
|
|
3069
|
+
opts.dockerfilePath
|
|
3070
|
+
);
|
|
3071
|
+
if (opts.fromTemplate !== void 0 && opts.fromTemplate.length > 0) {
|
|
3072
|
+
template.fromTemplate(opts.fromTemplate);
|
|
3073
|
+
}
|
|
3074
|
+
const result = await Template.build(template, opts.name, {
|
|
3075
|
+
apiKey: opts.apiKey,
|
|
3076
|
+
...opts.cpu === void 0 ? {} : { cpuCount: opts.cpu },
|
|
3077
|
+
...opts.memoryMb === void 0 ? {} : { memoryMB: opts.memoryMb },
|
|
3078
|
+
...opts.onLog ? { onBuildLogs: opts.onLog } : {}
|
|
3079
|
+
});
|
|
3080
|
+
return { templateId: result.templateId };
|
|
3081
|
+
}
|
|
3082
|
+
function toArrayBuffer(buffer) {
|
|
3083
|
+
const output = new ArrayBuffer(buffer.byteLength);
|
|
3084
|
+
new Uint8Array(output).set(buffer);
|
|
3085
|
+
return output;
|
|
3086
|
+
}
|
|
3087
|
+
async function readableToString(stream) {
|
|
3088
|
+
if (stream === null) {
|
|
3089
|
+
return "";
|
|
3090
|
+
}
|
|
3091
|
+
stream.setEncoding("utf8");
|
|
3092
|
+
const chunks = [];
|
|
3093
|
+
for await (const chunk of stream) {
|
|
3094
|
+
chunks.push(String(chunk));
|
|
3095
|
+
}
|
|
3096
|
+
return chunks.join("");
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
// packages/runner-e2b/src/template-build.ts
|
|
3100
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3101
|
+
import { readdir, readFile as readFile3 } from "node:fs/promises";
|
|
3102
|
+
import path15 from "node:path";
|
|
3103
|
+
var BUILD_LOG_TAIL_SIZE = 30;
|
|
3104
|
+
async function buildE2bRuntimeTemplate(input) {
|
|
3105
|
+
const dockerfileBytes = await readFile3(input.dockerfilePath);
|
|
3106
|
+
const buildContextFiles = await readBuildContextFiles(input.buildContext);
|
|
3107
|
+
const hash = hashTemplate(dockerfileBytes, buildContextFiles, input.runtime.build_args);
|
|
3108
|
+
const cached2 = input.force === true ? null : await input.state?.templates.get("e2b", hash);
|
|
3109
|
+
if (cached2?.template_id !== void 0) {
|
|
3110
|
+
return { backend: "e2b", hash, templateId: cached2.template_id, cached: true };
|
|
3111
|
+
}
|
|
3112
|
+
const tail = [];
|
|
3113
|
+
const onLog = (entry) => {
|
|
3114
|
+
tail.push(entry.message);
|
|
3115
|
+
if (tail.length > BUILD_LOG_TAIL_SIZE) {
|
|
3116
|
+
tail.shift();
|
|
3117
|
+
}
|
|
3118
|
+
input.onLog?.(entry);
|
|
3119
|
+
};
|
|
3120
|
+
let built;
|
|
3121
|
+
try {
|
|
3122
|
+
built = await buildTemplate({
|
|
3123
|
+
apiKey: input.apiKey,
|
|
3124
|
+
name: `poe-code-${hash.slice(0, 32)}`,
|
|
3125
|
+
dockerfilePath: input.dockerfilePath,
|
|
3126
|
+
buildContext: input.buildContext,
|
|
3127
|
+
cpu: input.runtime.cpu,
|
|
3128
|
+
memoryMb: input.runtime.memory_mb,
|
|
3129
|
+
fromTemplate: input.runtime.from_template,
|
|
3130
|
+
onLog
|
|
3131
|
+
});
|
|
3132
|
+
} catch (error2) {
|
|
3133
|
+
throw decorateBuildError(error2, tail);
|
|
3134
|
+
}
|
|
3135
|
+
await input.state?.templates.put("e2b", {
|
|
3136
|
+
hash,
|
|
3137
|
+
template_id: built.templateId,
|
|
3138
|
+
runtime_type: "e2b",
|
|
3139
|
+
dockerfile_path: input.dockerfilePath,
|
|
3140
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3141
|
+
});
|
|
3142
|
+
return { backend: "e2b", hash, templateId: built.templateId, cached: false };
|
|
3143
|
+
}
|
|
3144
|
+
function decorateBuildError(error2, tail) {
|
|
3145
|
+
const original = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
3146
|
+
if (tail.length === 0) {
|
|
3147
|
+
return original;
|
|
3148
|
+
}
|
|
3149
|
+
const decorated = new Error(`${original.message}
|
|
3150
|
+
|
|
3151
|
+
Last build output:
|
|
3152
|
+
${tail.join("\n")}`);
|
|
3153
|
+
decorated.stack = original.stack;
|
|
3154
|
+
decorated.cause = original;
|
|
3155
|
+
return decorated;
|
|
3156
|
+
}
|
|
3157
|
+
function hashTemplate(dockerfileBytes, buildContextFiles, buildArgs) {
|
|
3158
|
+
const hash = createHash3("sha256");
|
|
3159
|
+
hash.update(dockerfileBytes);
|
|
3160
|
+
hash.update("\0");
|
|
3161
|
+
for (const file of buildContextFiles) {
|
|
3162
|
+
hash.update(file.relativePath);
|
|
3163
|
+
hash.update("\0");
|
|
3164
|
+
hash.update(file.bytes);
|
|
3165
|
+
hash.update("\0");
|
|
3166
|
+
}
|
|
3167
|
+
for (const [key, value] of Object.entries(buildArgs).sort(
|
|
3168
|
+
([left], [right]) => left.localeCompare(right)
|
|
3169
|
+
)) {
|
|
3170
|
+
hash.update(key);
|
|
3171
|
+
hash.update("=");
|
|
3172
|
+
hash.update(value);
|
|
3173
|
+
hash.update("\0");
|
|
3174
|
+
}
|
|
3175
|
+
return hash.digest("hex");
|
|
3176
|
+
}
|
|
3177
|
+
async function readBuildContextFiles(buildContext) {
|
|
3178
|
+
const files = [];
|
|
3179
|
+
await collectBuildContextFiles(buildContext, "", files);
|
|
3180
|
+
return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
3181
|
+
}
|
|
3182
|
+
async function collectBuildContextFiles(buildContext, relativeDir, files) {
|
|
3183
|
+
const absoluteDir = path15.join(buildContext, relativeDir);
|
|
3184
|
+
const entries = await readdir(absoluteDir, { withFileTypes: true });
|
|
3185
|
+
for (const entry of entries) {
|
|
3186
|
+
const relativePath = path15.join(relativeDir, entry.name);
|
|
3187
|
+
if (entry.isDirectory()) {
|
|
3188
|
+
await collectBuildContextFiles(buildContext, relativePath, files);
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
if (!entry.isFile()) {
|
|
3192
|
+
continue;
|
|
3193
|
+
}
|
|
3194
|
+
files.push({
|
|
3195
|
+
relativePath: relativePath.split(path15.sep).join("/"),
|
|
3196
|
+
bytes: await readFile3(path15.join(buildContext, relativePath))
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// packages/runner-e2b/src/opened-env.ts
|
|
3202
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
|
|
3203
|
+
import { readFile as readFile4, writeFile } from "node:fs/promises";
|
|
3204
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
3205
|
+
import path17 from "node:path";
|
|
3206
|
+
import { PassThrough, Writable as Writable2 } from "node:stream";
|
|
3207
|
+
|
|
3208
|
+
// packages/runner-e2b/src/job-handle.ts
|
|
3209
|
+
import path16 from "node:path";
|
|
3210
|
+
var JOB_DIR2 = "/tmp/poe-jobs";
|
|
3211
|
+
function createE2bJobHandle(input) {
|
|
3212
|
+
const fs = createE2bLogStreamFs(input.sandbox);
|
|
3213
|
+
return {
|
|
3214
|
+
id: input.jobId,
|
|
3215
|
+
envId: input.envId,
|
|
3216
|
+
tool: input.tool,
|
|
3217
|
+
argv: input.argv,
|
|
3218
|
+
async status() {
|
|
3219
|
+
const exit = await readExitCode(input.sandbox, input.jobId);
|
|
3220
|
+
if (exit !== null) {
|
|
3221
|
+
return "exited";
|
|
3222
|
+
}
|
|
3223
|
+
const processes = await input.sandbox.commands.list();
|
|
3224
|
+
const isRunning = input.pid === void 0 ? processes.some((process2) => processMentionsJob(process2, input.jobId)) : processes.some((process2) => process2.pid === input.pid);
|
|
3225
|
+
return isRunning ? "running" : "lost";
|
|
3226
|
+
},
|
|
3227
|
+
stream(opts = {}) {
|
|
3228
|
+
return streamLogFile({ fs }, input.jobId, opts);
|
|
3229
|
+
},
|
|
3230
|
+
async wait() {
|
|
3231
|
+
const result = await waitForExit({ fs }, input.jobId);
|
|
3232
|
+
const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
|
|
3233
|
+
if (preserveMs > 0) {
|
|
3234
|
+
await input.sandbox.setTimeout(preserveMs);
|
|
3235
|
+
}
|
|
3236
|
+
return result;
|
|
3237
|
+
},
|
|
3238
|
+
async kill() {
|
|
3239
|
+
const pids = input.pid === void 0 ? (await input.sandbox.commands.list()).filter((process2) => processMentionsJob(process2, input.jobId)).map((process2) => process2.pid) : [input.pid];
|
|
3240
|
+
await Promise.all(pids.map((pid) => input.sandbox.commands.kill(pid)));
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
function createE2bLogStreamFs(sandbox) {
|
|
3245
|
+
return {
|
|
3246
|
+
promises: {
|
|
3247
|
+
async readFile(filePath) {
|
|
3248
|
+
return Buffer.from(await sandbox.files.read(filePath, { format: "bytes" }));
|
|
3249
|
+
},
|
|
3250
|
+
async stat(filePath) {
|
|
3251
|
+
const result = await sandbox.commands.run(
|
|
3252
|
+
`stat -c %Y ${shellQuote2(filePath)} 2>/dev/null || stat -f %m ${shellQuote2(filePath)}`
|
|
3253
|
+
);
|
|
3254
|
+
if (!("stdout" in result)) {
|
|
3255
|
+
throw new Error(`Unable to stat ${filePath}`);
|
|
3256
|
+
}
|
|
3257
|
+
const seconds = Number(result.stdout?.trim());
|
|
3258
|
+
if (!Number.isFinite(seconds)) {
|
|
3259
|
+
throw new Error(`Unable to stat ${filePath}`);
|
|
3260
|
+
}
|
|
3261
|
+
return { mtimeMs: seconds * 1e3 };
|
|
3262
|
+
}
|
|
3263
|
+
},
|
|
3264
|
+
watch(filePath, listener) {
|
|
3265
|
+
let closed = false;
|
|
3266
|
+
let stop = null;
|
|
3267
|
+
void sandbox.files.watchDir(path16.dirname(filePath), listener, { recursive: false }).then((handle) => {
|
|
3268
|
+
if (closed) {
|
|
3269
|
+
void handle.stop();
|
|
3270
|
+
return;
|
|
3271
|
+
}
|
|
3272
|
+
stop = () => {
|
|
3273
|
+
void handle.stop();
|
|
3274
|
+
};
|
|
3275
|
+
});
|
|
3276
|
+
return {
|
|
3277
|
+
close() {
|
|
3278
|
+
closed = true;
|
|
3279
|
+
stop?.();
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
function processMentionsJob(process2, jobId) {
|
|
3286
|
+
const needle = `/tmp/poe-jobs/${jobId}`;
|
|
3287
|
+
return process2.cmd.includes(needle) || process2.args.some((arg) => arg.includes(needle));
|
|
3288
|
+
}
|
|
3289
|
+
function shellQuote2(value) {
|
|
3290
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
3291
|
+
}
|
|
3292
|
+
async function readExitCode(sandbox, jobId) {
|
|
3293
|
+
try {
|
|
3294
|
+
const contents = await sandbox.files.read(`${JOB_DIR2}/${jobId}.exit`);
|
|
3295
|
+
const exitCode = Number(contents.trim());
|
|
3296
|
+
return Number.isInteger(exitCode) ? exitCode : null;
|
|
3297
|
+
} catch {
|
|
3298
|
+
return null;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
// packages/runner-e2b/src/opened-env.ts
|
|
3303
|
+
var REMOTE_COMMAND_STDERR_TAIL_SIZE = 30;
|
|
3304
|
+
function createOpenedE2bEnv(input) {
|
|
3305
|
+
const hostRunner = input.spec.hostRunner ?? createHostRunner();
|
|
3306
|
+
const hostWorkspaceDir = path17.resolve(input.spec.cwd);
|
|
3307
|
+
const sandboxWorkspaceDir = normalizeSandboxWorkspaceDir(input.runtime.workspace_dir);
|
|
3308
|
+
let lastProcess = null;
|
|
3309
|
+
let detachedJobContext = null;
|
|
3310
|
+
const mapWorkspaceCwd = (cwd) => {
|
|
3311
|
+
if (cwd === void 0) {
|
|
3312
|
+
return void 0;
|
|
3313
|
+
}
|
|
3314
|
+
if (path17.isAbsolute(cwd) && path17.resolve(cwd) === hostWorkspaceDir) {
|
|
3315
|
+
return sandboxWorkspaceDir;
|
|
3316
|
+
}
|
|
3317
|
+
return cwd;
|
|
3318
|
+
};
|
|
3319
|
+
const attachedJobId = input.spec.detachedJobId;
|
|
3320
|
+
const env = {
|
|
3321
|
+
id: input.sandbox.sandboxId,
|
|
3322
|
+
job: attachedJobId ? createE2bJobHandle({
|
|
3323
|
+
sandbox: input.sandbox,
|
|
3324
|
+
envId: input.sandbox.sandboxId,
|
|
3325
|
+
jobId: attachedJobId,
|
|
3326
|
+
tool: input.spec.jobLabel.tool,
|
|
3327
|
+
argv: input.spec.jobLabel.argv,
|
|
3328
|
+
preserveAfterExitHours: input.runtime.preserve_after_exit_hours ?? 24
|
|
3329
|
+
}) : null,
|
|
3330
|
+
fs: createE2bLogStreamFs(input.sandbox),
|
|
3331
|
+
setDetachedJobContext(context) {
|
|
3332
|
+
detachedJobContext = context;
|
|
3333
|
+
},
|
|
3334
|
+
async uploadWorkspace() {
|
|
3335
|
+
if (input.spec.runner?.sync === "none") {
|
|
3336
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
3337
|
+
}
|
|
3338
|
+
const tempDir = mkdtempSync2(path17.join(tmpdir2(), "poe-e2b-upload-"));
|
|
3339
|
+
const archivePath = path17.join(tempDir, "workspace.tar");
|
|
3340
|
+
try {
|
|
3341
|
+
await runOrThrow2(hostRunner, {
|
|
3342
|
+
command: "tar",
|
|
3343
|
+
args: [
|
|
3344
|
+
...input.spec.uploadIgnoreFiles.flatMap((ignored) => ["--exclude", ignored]),
|
|
3345
|
+
"-cf",
|
|
3346
|
+
archivePath,
|
|
3347
|
+
"-C",
|
|
3348
|
+
input.spec.cwd,
|
|
3349
|
+
"."
|
|
3350
|
+
],
|
|
3351
|
+
stdout: "pipe",
|
|
3352
|
+
stderr: "pipe"
|
|
3353
|
+
});
|
|
3354
|
+
await input.sandbox.files.write(
|
|
3355
|
+
"/tmp/poe-workspace-upload.tar",
|
|
3356
|
+
toArrayBuffer(await readFile4(archivePath))
|
|
3357
|
+
);
|
|
3358
|
+
await runRemoteOrThrow(
|
|
3359
|
+
input.sandbox,
|
|
3360
|
+
createUploadWorkspaceCommand(sandboxWorkspaceDir)
|
|
3361
|
+
);
|
|
3362
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
3363
|
+
} finally {
|
|
3364
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
3365
|
+
}
|
|
3366
|
+
},
|
|
3367
|
+
async downloadWorkspace(opts) {
|
|
3368
|
+
if (input.spec.runner?.sync === "upload" || input.spec.runner?.sync === "none") {
|
|
3369
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
3370
|
+
}
|
|
3371
|
+
const tempDir = mkdtempSync2(path17.join(tmpdir2(), "poe-e2b-download-"));
|
|
3372
|
+
const archivePath = path17.join(tempDir, "workspace.tar");
|
|
3373
|
+
try {
|
|
3374
|
+
await runRemoteOrThrow(
|
|
3375
|
+
input.sandbox,
|
|
3376
|
+
`tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote3(sandboxWorkspaceDir)} .`
|
|
3377
|
+
);
|
|
3378
|
+
const archive = await input.sandbox.files.read("/tmp/poe-workspace-download.tar", {
|
|
3379
|
+
format: "bytes"
|
|
3380
|
+
});
|
|
3381
|
+
await writeFile(archivePath, Buffer.from(archive));
|
|
3382
|
+
await runOrThrow2(hostRunner, {
|
|
3383
|
+
command: "tar",
|
|
3384
|
+
args: [
|
|
3385
|
+
opts.conflictPolicy === "refuse" ? "-xkf" : "-xf",
|
|
3386
|
+
archivePath,
|
|
3387
|
+
"-C",
|
|
3388
|
+
input.spec.cwd
|
|
3389
|
+
],
|
|
3390
|
+
stdout: "pipe",
|
|
3391
|
+
stderr: "pipe"
|
|
3392
|
+
});
|
|
3393
|
+
return { files: 0, bytes: archive.byteLength, conflicts: [] };
|
|
3394
|
+
} finally {
|
|
3395
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
3396
|
+
}
|
|
3397
|
+
},
|
|
3398
|
+
exec(spec) {
|
|
3399
|
+
const handle = runE2bCommand(input.sandbox, {
|
|
3400
|
+
...spec,
|
|
3401
|
+
cwd: mapWorkspaceCwd(spec.cwd),
|
|
3402
|
+
env: resolveSandboxCommandEnv(spec.env)
|
|
3403
|
+
});
|
|
3404
|
+
lastProcess = { started: handle.started };
|
|
3405
|
+
return handle;
|
|
3406
|
+
},
|
|
3407
|
+
async detach() {
|
|
3408
|
+
if (detachedJobContext === null) {
|
|
3409
|
+
throw new Error("Cannot detach E2B environment before a job context is registered.");
|
|
3410
|
+
}
|
|
3411
|
+
if (lastProcess === null) {
|
|
3412
|
+
throw new Error("Cannot detach E2B environment before a command is running.");
|
|
3413
|
+
}
|
|
3414
|
+
const command = await lastProcess.started;
|
|
3415
|
+
const preserveAfterExitHours = input.runtime.preserve_after_exit_hours ?? 24;
|
|
3416
|
+
const preserveMs = preserveAfterExitHours * 60 * 60 * 1e3;
|
|
3417
|
+
if (preserveMs > 0) {
|
|
3418
|
+
await input.sandbox.setTimeout(preserveMs);
|
|
3419
|
+
}
|
|
3420
|
+
return createE2bJobHandle({
|
|
3421
|
+
sandbox: input.sandbox,
|
|
3422
|
+
envId: input.sandbox.sandboxId,
|
|
3423
|
+
jobId: detachedJobContext.id,
|
|
3424
|
+
tool: detachedJobContext.tool,
|
|
3425
|
+
argv: detachedJobContext.argv,
|
|
3426
|
+
pid: command.pid,
|
|
3427
|
+
preserveAfterExitHours
|
|
3428
|
+
});
|
|
3429
|
+
},
|
|
3430
|
+
shell() {
|
|
3431
|
+
const shellSpec = input.spec.shellSpec;
|
|
3432
|
+
const command = shellSpec?.command ?? input.spec.env.SHELL ?? "sh";
|
|
3433
|
+
return runE2bPty(input.sandbox, {
|
|
3434
|
+
command,
|
|
3435
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
3436
|
+
cwd: mapWorkspaceCwd(shellSpec?.cwd ?? input.spec.cwd),
|
|
3437
|
+
env: resolveSandboxCommandEnv(
|
|
3438
|
+
shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env
|
|
3439
|
+
),
|
|
3440
|
+
stdin: "inherit",
|
|
3441
|
+
stdout: "inherit",
|
|
3442
|
+
stderr: "inherit",
|
|
3443
|
+
tty: true
|
|
3444
|
+
});
|
|
3445
|
+
},
|
|
3446
|
+
async close() {
|
|
3447
|
+
await input.sandbox.kill();
|
|
3448
|
+
}
|
|
3449
|
+
};
|
|
3450
|
+
return env;
|
|
3451
|
+
}
|
|
3452
|
+
function runE2bCommand(sandbox, spec) {
|
|
3453
|
+
const stdout = spec.stdout === "inherit" ? null : new PassThrough();
|
|
3454
|
+
const stderr = spec.stderr === "inherit" ? null : new PassThrough();
|
|
3455
|
+
let e2bHandle = null;
|
|
3456
|
+
const command = shellCommand([spec.command, ...spec.args ?? []]);
|
|
3457
|
+
const started = sandbox.commands.run(command, {
|
|
3458
|
+
background: true,
|
|
3459
|
+
cwd: spec.cwd,
|
|
3460
|
+
envs: spec.env,
|
|
3461
|
+
stdin: spec.stdin === "pipe",
|
|
3462
|
+
onStdout(data) {
|
|
3463
|
+
stdout?.write(data);
|
|
3464
|
+
if (spec.stdout === "inherit") {
|
|
3465
|
+
process.stdout.write(data);
|
|
3466
|
+
}
|
|
3467
|
+
},
|
|
3468
|
+
onStderr(data) {
|
|
3469
|
+
stderr?.write(data);
|
|
3470
|
+
if (spec.stderr === "inherit") {
|
|
3471
|
+
process.stderr.write(data);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
});
|
|
3475
|
+
const stdin = spec.stdin === "pipe" ? new Writable2({
|
|
3476
|
+
write(chunk, _encoding, callback) {
|
|
3477
|
+
started.then(
|
|
3478
|
+
(handle) => sandbox.commands.sendStdin(
|
|
3479
|
+
handle.pid,
|
|
3480
|
+
Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))
|
|
3481
|
+
)
|
|
3482
|
+
).then(() => callback(), callback);
|
|
3483
|
+
},
|
|
3484
|
+
final(callback) {
|
|
3485
|
+
if (sandbox.commands.closeStdin === void 0) {
|
|
3486
|
+
callback();
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3489
|
+
started.then((handle) => sandbox.commands.closeStdin(handle.pid)).then(() => callback(), callback);
|
|
3490
|
+
}
|
|
3491
|
+
}) : null;
|
|
3492
|
+
const result = started.then((handle) => {
|
|
3493
|
+
e2bHandle = handle;
|
|
3494
|
+
return handle.wait();
|
|
3495
|
+
}).then(
|
|
3496
|
+
(result2) => {
|
|
3497
|
+
stdout?.end();
|
|
3498
|
+
stderr?.end();
|
|
3499
|
+
return { exitCode: result2.exitCode ?? 0 };
|
|
3500
|
+
},
|
|
3501
|
+
(error2) => {
|
|
3502
|
+
stdout?.end();
|
|
3503
|
+
stderr?.end();
|
|
3504
|
+
if (isExitError(error2)) {
|
|
3505
|
+
return { exitCode: error2.exitCode };
|
|
3506
|
+
}
|
|
3507
|
+
return { exitCode: 1 };
|
|
3508
|
+
}
|
|
3509
|
+
);
|
|
3510
|
+
return {
|
|
3511
|
+
get pid() {
|
|
3512
|
+
return e2bHandle?.pid ?? null;
|
|
3513
|
+
},
|
|
3514
|
+
stdin,
|
|
3515
|
+
stdout,
|
|
3516
|
+
stderr,
|
|
3517
|
+
result,
|
|
3518
|
+
kill() {
|
|
3519
|
+
void e2bHandle?.kill();
|
|
3520
|
+
},
|
|
3521
|
+
get e2bHandle() {
|
|
3522
|
+
return e2bHandle;
|
|
3523
|
+
},
|
|
3524
|
+
started
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
function runE2bPty(sandbox, spec) {
|
|
3528
|
+
const stdout = new PassThrough();
|
|
3529
|
+
let handleRef = null;
|
|
3530
|
+
const stdin = new Writable2({
|
|
3531
|
+
write(chunk, _encoding, callback) {
|
|
3532
|
+
if (handleRef === null) {
|
|
3533
|
+
callback(new Error("E2B PTY stdin is not ready."));
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
sandbox.pty.sendInput(handleRef.pid, Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))).then(() => callback(), callback);
|
|
3537
|
+
}
|
|
3538
|
+
});
|
|
3539
|
+
const started = sandbox.pty.create({
|
|
3540
|
+
cols: process.stdout.columns || 80,
|
|
3541
|
+
rows: process.stdout.rows || 24,
|
|
3542
|
+
cwd: spec.cwd,
|
|
3543
|
+
envs: spec.env,
|
|
3544
|
+
onData(data) {
|
|
3545
|
+
stdout.write(Buffer.from(data));
|
|
3546
|
+
if (spec.stdout === "inherit") {
|
|
3547
|
+
process.stdout.write(Buffer.from(data));
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
});
|
|
3551
|
+
const result = started.then((handle) => {
|
|
3552
|
+
handleRef = handle;
|
|
3553
|
+
return handle.wait();
|
|
3554
|
+
}).then(
|
|
3555
|
+
(result2) => {
|
|
3556
|
+
stdout.end();
|
|
3557
|
+
return { exitCode: result2.exitCode ?? 0 };
|
|
3558
|
+
},
|
|
3559
|
+
() => {
|
|
3560
|
+
stdout.end();
|
|
3561
|
+
return { exitCode: 1 };
|
|
3562
|
+
}
|
|
3563
|
+
);
|
|
3564
|
+
return {
|
|
3565
|
+
get pid() {
|
|
3566
|
+
return handleRef?.pid ?? null;
|
|
3567
|
+
},
|
|
3568
|
+
stdin: spec.stdin === "inherit" ? process.stdin : stdin,
|
|
3569
|
+
stdout: spec.stdout === "inherit" ? null : stdout,
|
|
3570
|
+
stderr: null,
|
|
3571
|
+
result,
|
|
3572
|
+
kill() {
|
|
3573
|
+
void (handleRef === null ? void 0 : sandbox.pty.kill(handleRef.pid));
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
}
|
|
3577
|
+
async function runRemoteOrThrow(sandbox, command) {
|
|
3578
|
+
const stdoutTail = createLineTail(REMOTE_COMMAND_STDERR_TAIL_SIZE);
|
|
3579
|
+
const stderrTail = createLineTail(REMOTE_COMMAND_STDERR_TAIL_SIZE);
|
|
3580
|
+
let result;
|
|
3581
|
+
try {
|
|
3582
|
+
result = await sandbox.commands.run(command, {
|
|
3583
|
+
onStdout(data) {
|
|
3584
|
+
stdoutTail.push(data);
|
|
3585
|
+
},
|
|
3586
|
+
onStderr(data) {
|
|
3587
|
+
stderrTail.push(data);
|
|
3588
|
+
}
|
|
3589
|
+
});
|
|
3590
|
+
} catch (error2) {
|
|
3591
|
+
appendRemoteCommandOutput(error2, stdoutTail, stderrTail);
|
|
3592
|
+
if (isCommandExitError(error2)) {
|
|
3593
|
+
throw decorateRemoteCommandError(error2, command, stderrTail.values());
|
|
3594
|
+
}
|
|
3595
|
+
throw error2;
|
|
3596
|
+
}
|
|
3597
|
+
appendRemoteCommandOutput(result, stdoutTail, stderrTail);
|
|
3598
|
+
if ("exitCode" in result && result.exitCode !== 0) {
|
|
3599
|
+
throw decorateRemoteCommandError(
|
|
3600
|
+
new Error(`E2B command failed with exit code ${result.exitCode}`),
|
|
3601
|
+
command,
|
|
3602
|
+
stderrTail.values()
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
function appendRemoteCommandOutput(source, stdoutTail, stderrTail) {
|
|
3607
|
+
if (!source || typeof source !== "object") {
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
const output = source;
|
|
3611
|
+
if (typeof output.stdout === "string") {
|
|
3612
|
+
stdoutTail.push(output.stdout);
|
|
3613
|
+
}
|
|
3614
|
+
if (typeof output.stderr === "string") {
|
|
3615
|
+
stderrTail.push(output.stderr);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
function decorateRemoteCommandError(error2, command, stderrTail) {
|
|
3619
|
+
const original = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
3620
|
+
const tail = stderrTail.length === 0 ? "" : `
|
|
3621
|
+
|
|
3622
|
+
Last stderr output:
|
|
3623
|
+
${stderrTail.join("\n")}`;
|
|
3624
|
+
const decorated = new Error(`E2B command failed: ${command}
|
|
3625
|
+
${original.message}${tail}`);
|
|
3626
|
+
decorated.stack = original.stack;
|
|
3627
|
+
decorated.cause = original;
|
|
3628
|
+
return decorated;
|
|
3629
|
+
}
|
|
3630
|
+
function createLineTail(maxLines) {
|
|
3631
|
+
const lines = [];
|
|
3632
|
+
let pending = "";
|
|
3633
|
+
const appendLine = (line) => {
|
|
3634
|
+
lines.push(trimTrailingCarriageReturn(line));
|
|
3635
|
+
while (lines.length > maxLines) {
|
|
3636
|
+
lines.shift();
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
return {
|
|
3640
|
+
push(chunk) {
|
|
3641
|
+
pending += chunk;
|
|
3642
|
+
const parts = pending.split("\n");
|
|
3643
|
+
pending = parts.pop() ?? "";
|
|
3644
|
+
for (const line of parts) {
|
|
3645
|
+
appendLine(line);
|
|
3646
|
+
}
|
|
3647
|
+
},
|
|
3648
|
+
values() {
|
|
3649
|
+
const output = [...lines];
|
|
3650
|
+
if (pending.length > 0) {
|
|
3651
|
+
output.push(trimTrailingCarriageReturn(pending));
|
|
3652
|
+
}
|
|
3653
|
+
return output.slice(-maxLines);
|
|
3654
|
+
}
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
function trimTrailingCarriageReturn(value) {
|
|
3658
|
+
return value.endsWith("\r") ? value.slice(0, -1) : value;
|
|
3659
|
+
}
|
|
3660
|
+
async function runOrThrow2(runner, spec) {
|
|
3661
|
+
const handle = runner.exec(spec);
|
|
3662
|
+
const stderr = readableToString(handle.stderr);
|
|
3663
|
+
const result = await handle.result;
|
|
3664
|
+
if (result.exitCode !== 0) {
|
|
3665
|
+
throw new Error(
|
|
3666
|
+
`Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}
|
|
3667
|
+
${await stderr}`
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
function shellCommand(argv) {
|
|
3672
|
+
return argv.map(shellQuote3).join(" ");
|
|
3673
|
+
}
|
|
3674
|
+
function shellQuote3(value) {
|
|
3675
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
3676
|
+
}
|
|
3677
|
+
function createUploadWorkspaceCommand(sandboxWorkspaceDir) {
|
|
3678
|
+
const quotedWorkspaceDir = shellQuote3(sandboxWorkspaceDir);
|
|
3679
|
+
return [
|
|
3680
|
+
`mkdir -p ${quotedWorkspaceDir} || { command -v sudo >/dev/null 2>&1 && sudo mkdir -p ${quotedWorkspaceDir} && sudo chown "$(id -u):$(id -g)" ${quotedWorkspaceDir}; }`,
|
|
3681
|
+
`test -w ${quotedWorkspaceDir} && tar -xf /tmp/poe-workspace-upload.tar -C ${quotedWorkspaceDir}`
|
|
3682
|
+
].join("\n");
|
|
3683
|
+
}
|
|
3684
|
+
function resolveSandboxCommandEnv(env) {
|
|
3685
|
+
if (env === void 0) {
|
|
3686
|
+
return void 0;
|
|
3687
|
+
}
|
|
3688
|
+
return {
|
|
3689
|
+
...env,
|
|
3690
|
+
HOME: "/home/user"
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function normalizeSandboxWorkspaceDir(workspaceDir) {
|
|
3694
|
+
const resolvedWorkspaceDir = workspaceDir ?? "/workspace";
|
|
3695
|
+
if (!path17.posix.isAbsolute(resolvedWorkspaceDir)) {
|
|
3696
|
+
throw new Error("E2B runtime workspace_dir must be an absolute sandbox path.");
|
|
3697
|
+
}
|
|
3698
|
+
let normalized = path17.posix.normalize(resolvedWorkspaceDir);
|
|
3699
|
+
while (normalized.length > 1 && normalized.endsWith("/")) {
|
|
3700
|
+
normalized = normalized.slice(0, -1);
|
|
3701
|
+
}
|
|
3702
|
+
return normalized;
|
|
3703
|
+
}
|
|
3704
|
+
function isExitError(error2) {
|
|
3705
|
+
return Boolean(
|
|
3706
|
+
error2 && typeof error2 === "object" && typeof error2.exitCode === "number"
|
|
3707
|
+
);
|
|
3708
|
+
}
|
|
3709
|
+
function isCommandExitError(error2) {
|
|
3710
|
+
return isExitError(error2) || Boolean(
|
|
3711
|
+
error2 && typeof error2 === "object" && error2.name === "CommandExitError"
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3715
|
+
// packages/runner-e2b/src/auth-scope.ts
|
|
3716
|
+
import os4 from "node:os";
|
|
3717
|
+
import { promises as nodeFs4 } from "node:fs";
|
|
3718
|
+
var e2bAuthScope = defineScope("e2b", {
|
|
3719
|
+
api_key: {
|
|
3720
|
+
type: "string",
|
|
3721
|
+
default: "",
|
|
3722
|
+
doc: "E2B API key",
|
|
3723
|
+
env: "E2B_API_KEY"
|
|
3724
|
+
}
|
|
3725
|
+
});
|
|
3726
|
+
async function resolveE2bApiKey(input) {
|
|
3727
|
+
const homeDir = input.homeDir ?? os4.homedir();
|
|
3728
|
+
const fs = input.fs ?? nodeFs4;
|
|
3729
|
+
const env = input.env ?? process.env;
|
|
3730
|
+
const document = await readMergedDocument(
|
|
3731
|
+
fs,
|
|
3732
|
+
resolveConfigPath(homeDir),
|
|
3733
|
+
resolveProjectConfigPath(input.cwd)
|
|
3734
|
+
);
|
|
3735
|
+
const resolved = resolveScope(e2bAuthScope.schema, document.e2b, env);
|
|
3736
|
+
if (resolved.api_key.length === 0) {
|
|
3737
|
+
throw new Error(
|
|
3738
|
+
"No E2B API key. Set E2B_API_KEY or e2b.api_key in ~/.poe-code/config.json."
|
|
3739
|
+
);
|
|
3740
|
+
}
|
|
3741
|
+
return resolved.api_key;
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
// packages/runner-e2b/src/factory.ts
|
|
3745
|
+
var e2bExecutionEnvFactory = {
|
|
3746
|
+
type: "e2b",
|
|
3747
|
+
supportsDetach: true,
|
|
3748
|
+
async open(spec) {
|
|
3749
|
+
const runtime = parseE2bRuntime(spec.runtime);
|
|
3750
|
+
const runtimeCwd = spec.runtimeCwd ?? spec.cwd;
|
|
3751
|
+
const apiKey = await resolveE2bApiKey({ cwd: runtimeCwd });
|
|
3752
|
+
const templateId = runtime.template_id ?? (await buildE2bRuntimeTemplate({
|
|
3753
|
+
runtime,
|
|
3754
|
+
dockerfilePath: path18.resolve(
|
|
3755
|
+
runtimeCwd,
|
|
3756
|
+
runtime.dockerfile ?? path18.join(".poe-code", "Dockerfile")
|
|
3757
|
+
),
|
|
3758
|
+
buildContext: path18.resolve(runtimeCwd, runtime.build_context ?? "."),
|
|
3759
|
+
state: spec.state,
|
|
3760
|
+
apiKey
|
|
3761
|
+
})).templateId;
|
|
3762
|
+
const sandbox = await createSandbox({
|
|
3763
|
+
apiKey,
|
|
3764
|
+
templateId,
|
|
3765
|
+
env: spec.env,
|
|
3766
|
+
timeoutMinutes: runtime.timeout_minutes
|
|
3767
|
+
});
|
|
3768
|
+
return createOpenedE2bEnv({ sandbox, spec, runtime });
|
|
3769
|
+
},
|
|
3770
|
+
async attach(envId, context) {
|
|
3771
|
+
const cwd = context?.cwd ?? process.cwd();
|
|
3772
|
+
const apiKey = await resolveE2bApiKey({ cwd });
|
|
3773
|
+
const sandbox = await connectSandbox(envId, apiKey);
|
|
3774
|
+
return createOpenedE2bEnv({
|
|
3775
|
+
sandbox,
|
|
3776
|
+
spec: {
|
|
3777
|
+
cwd: context?.cwd ?? "/workspace",
|
|
3778
|
+
runtime: {
|
|
3779
|
+
type: "e2b",
|
|
3780
|
+
build_args: {},
|
|
3781
|
+
mounts: [],
|
|
3782
|
+
workspace_dir: "/workspace",
|
|
3783
|
+
preserve_after_exit_hours: 24
|
|
3784
|
+
},
|
|
3785
|
+
env: {},
|
|
3786
|
+
uploadIgnoreFiles: [],
|
|
3787
|
+
jobLabel: { tool: context?.tool ?? "e2b", argv: context?.argv ?? [] },
|
|
3788
|
+
...context?.jobId ? { detachedJobId: context.jobId } : {}
|
|
3789
|
+
},
|
|
3790
|
+
runtime: {
|
|
3791
|
+
type: "e2b",
|
|
3792
|
+
build_args: {},
|
|
3793
|
+
mounts: [],
|
|
3794
|
+
workspace_dir: "/workspace",
|
|
3795
|
+
preserve_after_exit_hours: 24
|
|
3796
|
+
}
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3799
|
+
};
|
|
3800
|
+
function parseE2bRuntime(runtime) {
|
|
3801
|
+
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
|
|
3802
|
+
throw new Error("e2b runtime must be an object");
|
|
3803
|
+
}
|
|
3804
|
+
const record = runtime;
|
|
3805
|
+
if (record.type !== "e2b") {
|
|
3806
|
+
throw new Error('e2b runtime type must be "e2b"');
|
|
3807
|
+
}
|
|
3808
|
+
return record;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
// packages/runner-e2b/src/index.ts
|
|
3812
|
+
var e2bExecutionEnvFactory2 = e2bExecutionEnvFactory;
|
|
3813
|
+
|
|
3814
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
3815
|
+
registerExecutionEnvFactory(hostExecutionEnvFactory);
|
|
3816
|
+
registerExecutionEnvFactory(dockerExecutionEnvFactory);
|
|
3817
|
+
registerExecutionEnvFactory(e2bExecutionEnvFactory2);
|
|
3818
|
+
if (isVitest()) {
|
|
3819
|
+
registerExecutionEnvFactory(createTestHostExecutionEnvFactory());
|
|
3820
|
+
}
|
|
3821
|
+
function isVitest() {
|
|
3822
|
+
return process.env.VITEST !== void 0 || process.env.VITEST_POOL_ID !== void 0;
|
|
3823
|
+
}
|
|
3824
|
+
function createTestHostExecutionEnvFactory() {
|
|
3825
|
+
return {
|
|
3826
|
+
type: "host",
|
|
3827
|
+
supportsDetach: false,
|
|
3828
|
+
open: ((openSpec) => {
|
|
3829
|
+
return {
|
|
3830
|
+
id: "host",
|
|
3831
|
+
job: null,
|
|
3832
|
+
async uploadWorkspace() {
|
|
3833
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
3834
|
+
},
|
|
3835
|
+
async downloadWorkspace() {
|
|
3836
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
3837
|
+
},
|
|
3838
|
+
exec(spec) {
|
|
3839
|
+
return runHost(spawnChildProcess2, spec);
|
|
3840
|
+
},
|
|
3841
|
+
async detach() {
|
|
3842
|
+
throw new Error(
|
|
3843
|
+
"host runtime does not support detach because host has no addressable env"
|
|
3844
|
+
);
|
|
3845
|
+
},
|
|
3846
|
+
shell() {
|
|
3847
|
+
return runHost(spawnChildProcess2, {
|
|
3848
|
+
command: openSpec.shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
3849
|
+
args: openSpec.shellSpec?.args,
|
|
3850
|
+
cwd: openSpec.cwd,
|
|
3851
|
+
env: openSpec.shellSpec && "env" in openSpec.shellSpec ? openSpec.shellSpec.env : openSpec.env,
|
|
3852
|
+
stdin: "inherit",
|
|
3853
|
+
stdout: "inherit",
|
|
3854
|
+
stderr: "inherit",
|
|
3855
|
+
tty: true
|
|
3856
|
+
});
|
|
3857
|
+
},
|
|
3858
|
+
async close() {
|
|
3859
|
+
}
|
|
3860
|
+
};
|
|
3861
|
+
}),
|
|
3862
|
+
async attach() {
|
|
3863
|
+
throw new Error("host runtime does not support reattach");
|
|
3864
|
+
}
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
function runHost(spawnProcess, spec) {
|
|
3868
|
+
const stdin = spec.stdin ?? "ignore";
|
|
3869
|
+
const stdout = spec.stdout ?? "pipe";
|
|
3870
|
+
const stderr = spec.stderr ?? "pipe";
|
|
3871
|
+
const stdio = stdin === "inherit" && stdout === "inherit" && stderr === "inherit" ? "inherit" : [stdin, stdout, stderr];
|
|
3872
|
+
const child = spawnProcess(spec.command, spec.args ?? [], {
|
|
3873
|
+
cwd: spec.cwd,
|
|
3874
|
+
env: spec.env,
|
|
3875
|
+
stdio
|
|
3876
|
+
});
|
|
3877
|
+
const result = new Promise((resolve2) => {
|
|
3878
|
+
child.once("close", (code) => {
|
|
3879
|
+
resolve2({ exitCode: code ?? 1 });
|
|
3880
|
+
});
|
|
3881
|
+
child.once("error", () => {
|
|
3882
|
+
resolve2({ exitCode: 1 });
|
|
3883
|
+
});
|
|
3884
|
+
});
|
|
3885
|
+
const kill = (signal) => {
|
|
3886
|
+
child.kill(signal);
|
|
3887
|
+
};
|
|
3888
|
+
if (spec.signal?.aborted) {
|
|
3889
|
+
kill("SIGTERM");
|
|
3890
|
+
} else {
|
|
3891
|
+
spec.signal?.addEventListener("abort", () => kill("SIGTERM"), { once: true });
|
|
3892
|
+
}
|
|
3893
|
+
return {
|
|
3894
|
+
pid: child.pid ?? null,
|
|
3895
|
+
stdin: child.stdin,
|
|
3896
|
+
stdout: child.stdout,
|
|
3897
|
+
stderr: child.stderr,
|
|
3898
|
+
result,
|
|
3899
|
+
kill
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
// packages/agent-spawn/src/run-command.ts
|
|
3904
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3905
|
+
|
|
3906
|
+
// packages/agent-spawn/src/types.ts
|
|
3907
|
+
function resolveModeConfig(modeConfig) {
|
|
3908
|
+
if (Array.isArray(modeConfig)) {
|
|
3909
|
+
return { args: modeConfig };
|
|
3910
|
+
}
|
|
3911
|
+
return {
|
|
3912
|
+
args: modeConfig.args ?? [],
|
|
3913
|
+
env: modeConfig.env && Object.keys(modeConfig.env).length > 0 ? modeConfig.env : void 0
|
|
3914
|
+
};
|
|
3915
|
+
}
|
|
3916
|
+
|
|
3917
|
+
// packages/agent-spawn/src/configs/mcp.ts
|
|
3918
|
+
function toJsonMcpServers(servers) {
|
|
3919
|
+
const out = {};
|
|
3920
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
3921
|
+
const mapped = { command: server.command };
|
|
3922
|
+
if (server.args && server.args.length > 0) {
|
|
3923
|
+
mapped.args = server.args;
|
|
3924
|
+
}
|
|
3925
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
3926
|
+
mapped.env = server.env;
|
|
3927
|
+
}
|
|
3928
|
+
if (server.timeout !== void 0) {
|
|
3929
|
+
mapped.timeout = server.timeout;
|
|
3930
|
+
}
|
|
3931
|
+
out[name] = mapped;
|
|
3932
|
+
}
|
|
3933
|
+
return out;
|
|
3934
|
+
}
|
|
3935
|
+
function toTomlString(value) {
|
|
3936
|
+
return JSON.stringify(value);
|
|
3937
|
+
}
|
|
3938
|
+
function toTomlArray(values) {
|
|
3939
|
+
const serialized = values.map((value) => toTomlString(value));
|
|
3940
|
+
return `[${serialized.join(", ")}]`;
|
|
3941
|
+
}
|
|
3942
|
+
function toTomlInlineTable(values) {
|
|
3943
|
+
const parts = [];
|
|
3944
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3945
|
+
parts.push(`${JSON.stringify(key)}=${toTomlString(value)}`);
|
|
3946
|
+
}
|
|
3947
|
+
return `{${parts.join(", ")}}`;
|
|
3948
|
+
}
|
|
3949
|
+
function serializeJsonMcpArgs(servers) {
|
|
3950
|
+
return ["--mcp-config", JSON.stringify({ mcpServers: toJsonMcpServers(servers) })];
|
|
3951
|
+
}
|
|
3952
|
+
function serializeOpenCodeMcpEnv(servers) {
|
|
3953
|
+
const mcp = {};
|
|
3954
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
3955
|
+
const entry = { type: "local", command: [server.command, ...server.args ?? []] };
|
|
3956
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
3957
|
+
entry.environment = server.env;
|
|
3958
|
+
}
|
|
3959
|
+
mcp[name] = entry;
|
|
3960
|
+
}
|
|
3961
|
+
return { OPENCODE_CONFIG_CONTENT: JSON.stringify({ mcp }) };
|
|
3962
|
+
}
|
|
3963
|
+
function serializeCodexMcpArgs(servers) {
|
|
3964
|
+
const args = [];
|
|
3965
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
3966
|
+
const prefix = `mcp_servers.${name}`;
|
|
3967
|
+
args.push("-c", `${prefix}.command=${toTomlString(server.command)}`);
|
|
3968
|
+
if (server.args && server.args.length > 0) {
|
|
3969
|
+
args.push("-c", `${prefix}.args=${toTomlArray(server.args)}`);
|
|
3970
|
+
}
|
|
3971
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
3972
|
+
args.push("-c", `${prefix}.env=${toTomlInlineTable(server.env)}`);
|
|
3973
|
+
}
|
|
3974
|
+
if (server.timeout !== void 0) {
|
|
3975
|
+
args.push("-c", `${prefix}.timeout=${server.timeout}`);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
return args;
|
|
3979
|
+
}
|
|
3980
|
+
function serializeGooseMcpArgs(servers) {
|
|
3981
|
+
return Object.values(servers).flatMap((server) => [
|
|
3982
|
+
"--with-extension",
|
|
3983
|
+
[server.command, ...server.args ?? []].join(" ")
|
|
3984
|
+
]);
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
// packages/agent-spawn/src/configs/claude-code.ts
|
|
3988
|
+
var claudeCodeSpawnConfig = {
|
|
3989
|
+
kind: "cli",
|
|
3990
|
+
agentId: "claude-code",
|
|
3991
|
+
// ACP adapter support: yes (adapter: "claude")
|
|
3992
|
+
adapter: "claude",
|
|
3993
|
+
promptFlag: "-p",
|
|
3994
|
+
modelFlag: "--model",
|
|
3995
|
+
modelStripProviderPrefix: true,
|
|
3996
|
+
modelTransform: (model) => model.replaceAll(".", "-"),
|
|
3997
|
+
defaultArgs: [
|
|
3998
|
+
"--output-format",
|
|
3999
|
+
"stream-json",
|
|
4000
|
+
"--verbose"
|
|
4001
|
+
],
|
|
4002
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
4003
|
+
modes: {
|
|
4004
|
+
yolo: ["--dangerously-skip-permissions"],
|
|
4005
|
+
edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
|
|
4006
|
+
read: ["--permission-mode", "plan"]
|
|
4007
|
+
},
|
|
4008
|
+
stdinMode: {
|
|
4009
|
+
omitPrompt: true,
|
|
4010
|
+
extraArgs: ["--input-format", "text"]
|
|
4011
|
+
},
|
|
4012
|
+
interactive: {
|
|
4013
|
+
defaultArgs: []
|
|
4014
|
+
},
|
|
4015
|
+
resumeCommand: (threadId) => ["--resume", threadId]
|
|
4016
|
+
};
|
|
4017
|
+
|
|
4018
|
+
// packages/agent-spawn/src/configs/codex.ts
|
|
4019
|
+
var codexSpawnConfig = {
|
|
4020
|
+
kind: "cli",
|
|
4021
|
+
agentId: "codex",
|
|
4022
|
+
// ACP adapter support: yes (adapter: "codex")
|
|
4023
|
+
adapter: "codex",
|
|
4024
|
+
promptFlag: "exec",
|
|
4025
|
+
modelFlag: "--model",
|
|
4026
|
+
modelStripProviderPrefix: true,
|
|
4027
|
+
defaultArgs: ["--skip-git-repo-check", "--json"],
|
|
4028
|
+
mcpArgs: serializeCodexMcpArgs,
|
|
4029
|
+
mcpArgsBeforeCommand: true,
|
|
4030
|
+
modes: {
|
|
4031
|
+
yolo: ["-s", "danger-full-access"],
|
|
4032
|
+
edit: ["-s", "workspace-write"],
|
|
4033
|
+
read: ["-s", "read-only"]
|
|
4034
|
+
},
|
|
4035
|
+
stdinMode: {
|
|
4036
|
+
omitPrompt: true,
|
|
4037
|
+
extraArgs: ["-"]
|
|
4038
|
+
},
|
|
4039
|
+
interactive: {
|
|
4040
|
+
defaultArgs: ["-a", "never"]
|
|
4041
|
+
},
|
|
4042
|
+
resumeCommand: (threadId, cwd) => ["resume", "-C", cwd, threadId]
|
|
4043
|
+
};
|
|
4044
|
+
|
|
4045
|
+
// packages/agent-spawn/src/configs/opencode.ts
|
|
4046
|
+
var openCodeSpawnConfig = {
|
|
4047
|
+
kind: "cli",
|
|
4048
|
+
agentId: "opencode",
|
|
4049
|
+
// ACP adapter support: yes (adapter: "opencode").
|
|
4050
|
+
// OpenCode's `--format json` emits NDJSON events with `{ type, sessionID, part }`
|
|
4051
|
+
// (no `{ event, ... }` field), so it needs the OpenCode adapter (not "native").
|
|
4052
|
+
adapter: "opencode",
|
|
4053
|
+
promptFlag: "run",
|
|
4054
|
+
modelFlag: "--model",
|
|
4055
|
+
modelStripProviderPrefix: false,
|
|
4056
|
+
modelTransform: (model) => {
|
|
4057
|
+
return model.startsWith("poe/") ? model : `poe/${model}`;
|
|
4058
|
+
},
|
|
4059
|
+
defaultArgs: ["--format", "json"],
|
|
4060
|
+
modes: {
|
|
4061
|
+
yolo: [],
|
|
4062
|
+
edit: [],
|
|
4063
|
+
read: ["--agent", "plan"]
|
|
4064
|
+
},
|
|
4065
|
+
interactive: {
|
|
4066
|
+
defaultArgs: [],
|
|
4067
|
+
promptFlag: "--prompt"
|
|
4068
|
+
},
|
|
4069
|
+
resumeCommand: (threadId, cwd) => [cwd, "--session", threadId],
|
|
4070
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
4071
|
+
};
|
|
4072
|
+
var openCodeAcpSpawnConfig = {
|
|
4073
|
+
kind: "acp",
|
|
4074
|
+
agentId: "opencode",
|
|
4075
|
+
acpArgs: ["acp"],
|
|
4076
|
+
skipAuth: true,
|
|
4077
|
+
mcpEnv: serializeOpenCodeMcpEnv
|
|
4078
|
+
};
|
|
4079
|
+
|
|
4080
|
+
// packages/agent-spawn/src/configs/kimi.ts
|
|
4081
|
+
var kimiSpawnConfig = {
|
|
4082
|
+
kind: "cli",
|
|
4083
|
+
agentId: "kimi",
|
|
4084
|
+
// ACP adapter support: yes (adapter: "kimi").
|
|
4085
|
+
// Kimi's `--output-format stream-json` emits OpenAI-style `{ role, content }` JSON
|
|
4086
|
+
// (no `{ event, ... }` field), so it needs the Kimi adapter (not "native").
|
|
4087
|
+
adapter: "kimi",
|
|
4088
|
+
promptFlag: "-p",
|
|
4089
|
+
modelStripProviderPrefix: true,
|
|
4090
|
+
defaultArgs: ["--print", "--output-format", "stream-json"],
|
|
4091
|
+
mcpArgs: serializeJsonMcpArgs,
|
|
4092
|
+
modes: {
|
|
4093
|
+
yolo: ["--yolo"],
|
|
4094
|
+
edit: [],
|
|
4095
|
+
read: []
|
|
4096
|
+
},
|
|
4097
|
+
stdinMode: {
|
|
4098
|
+
omitPrompt: true,
|
|
4099
|
+
extraArgs: ["--input-format", "stream-json"]
|
|
4100
|
+
},
|
|
4101
|
+
interactive: {
|
|
4102
|
+
defaultArgs: [],
|
|
4103
|
+
promptFlag: "-p"
|
|
4104
|
+
},
|
|
4105
|
+
resumeCommand: (threadId, cwd) => ["--session", threadId, "--work-dir", cwd]
|
|
4106
|
+
};
|
|
4107
|
+
var kimiAcpSpawnConfig = {
|
|
4108
|
+
kind: "acp",
|
|
4109
|
+
agentId: "kimi",
|
|
4110
|
+
acpArgs: ["acp"]
|
|
4111
|
+
};
|
|
4112
|
+
|
|
4113
|
+
// packages/agent-spawn/src/configs/goose.ts
|
|
4114
|
+
var gooseFileSecretsEnv = { GOOSE_DISABLE_KEYRING: "1" };
|
|
4115
|
+
var gooseSpawnConfig = {
|
|
4116
|
+
kind: "cli",
|
|
4117
|
+
agentId: "goose",
|
|
4118
|
+
adapter: "native",
|
|
4119
|
+
promptFlag: "--text",
|
|
4120
|
+
modelFlag: "--model",
|
|
4121
|
+
modelStripProviderPrefix: false,
|
|
4122
|
+
defaultArgs: ["run", "--output-format", "stream-json"],
|
|
4123
|
+
defaultArgsPosition: "beforePrompt",
|
|
4124
|
+
mcpArgs: serializeGooseMcpArgs,
|
|
4125
|
+
mcpArgsPosition: "beforePrompt",
|
|
4126
|
+
modes: {
|
|
4127
|
+
yolo: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "auto" } },
|
|
4128
|
+
edit: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "smart_approve" } },
|
|
4129
|
+
read: { env: { ...gooseFileSecretsEnv, GOOSE_MODE: "chat" } }
|
|
4130
|
+
},
|
|
4131
|
+
stdinMode: {
|
|
4132
|
+
omitPrompt: true,
|
|
4133
|
+
extraArgs: ["--instructions", "-"]
|
|
4134
|
+
},
|
|
4135
|
+
interactive: {
|
|
4136
|
+
defaultArgs: ["session"],
|
|
4137
|
+
defaultArgsPosition: "beforePrompt"
|
|
4138
|
+
},
|
|
4139
|
+
resumeCommand: () => ["run", "--resume", "--text", "continue"]
|
|
4140
|
+
};
|
|
4141
|
+
var gooseAcpSpawnConfig = {
|
|
4142
|
+
kind: "acp",
|
|
4143
|
+
agentId: "goose",
|
|
4144
|
+
acpArgs: ["acp"],
|
|
4145
|
+
env: gooseFileSecretsEnv,
|
|
4146
|
+
skipAuth: true
|
|
4147
|
+
};
|
|
4148
|
+
|
|
4149
|
+
// packages/agent-spawn/src/configs/index.ts
|
|
4150
|
+
var allSpawnConfigs = [
|
|
4151
|
+
claudeCodeSpawnConfig,
|
|
4152
|
+
codexSpawnConfig,
|
|
4153
|
+
openCodeSpawnConfig,
|
|
4154
|
+
kimiSpawnConfig,
|
|
4155
|
+
gooseSpawnConfig
|
|
4156
|
+
];
|
|
4157
|
+
var lookup2 = /* @__PURE__ */ new Map();
|
|
4158
|
+
for (const config of allSpawnConfigs) {
|
|
4159
|
+
lookup2.set(config.agentId, config);
|
|
4160
|
+
}
|
|
4161
|
+
var acpLookup = /* @__PURE__ */ new Map();
|
|
4162
|
+
acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
|
|
4163
|
+
acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
|
|
4164
|
+
acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
|
|
4165
|
+
function getSpawnConfig(input) {
|
|
4166
|
+
const resolvedId = resolveAgentId(input);
|
|
4167
|
+
if (!resolvedId) {
|
|
4168
|
+
return void 0;
|
|
4169
|
+
}
|
|
4170
|
+
return lookup2.get(resolvedId);
|
|
4171
|
+
}
|
|
4172
|
+
function listMcpSupportedAgents() {
|
|
4173
|
+
const supported = [];
|
|
4174
|
+
for (const config of allSpawnConfigs) {
|
|
4175
|
+
if (config.kind !== "cli" || typeof config.mcpArgs !== "function" && typeof config.mcpEnv !== "function") {
|
|
4176
|
+
continue;
|
|
4177
|
+
}
|
|
4178
|
+
supported.push(config.agentId);
|
|
4179
|
+
}
|
|
4180
|
+
return supported;
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
// packages/agent-spawn/src/spawn.ts
|
|
4184
|
+
import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
|
|
4185
|
+
import path19 from "node:path";
|
|
4186
|
+
|
|
4187
|
+
// packages/agent-spawn/src/configs/resolve-config.ts
|
|
4188
|
+
function resolveConfig(agentId) {
|
|
4189
|
+
const resolvedAgentId = resolveAgentId(agentId);
|
|
4190
|
+
if (!resolvedAgentId) {
|
|
4191
|
+
throw new Error(`Unknown agent "${agentId}".`);
|
|
4192
|
+
}
|
|
4193
|
+
const agentDefinition = allAgents.find((agent) => agent.id === resolvedAgentId);
|
|
4194
|
+
if (!agentDefinition) {
|
|
4195
|
+
throw new Error(`Unknown agent "${agentId}".`);
|
|
4196
|
+
}
|
|
4197
|
+
const spawnConfig = getSpawnConfig(resolvedAgentId);
|
|
4198
|
+
const binaryName = agentDefinition.binaryName;
|
|
4199
|
+
return { agentId: resolvedAgentId, binaryName, spawnConfig };
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
// packages/agent-spawn/src/mcp-args.ts
|
|
4203
|
+
function hasMcpServers(servers) {
|
|
4204
|
+
if (!servers) {
|
|
4205
|
+
return false;
|
|
4206
|
+
}
|
|
4207
|
+
return Object.keys(servers).length > 0;
|
|
4208
|
+
}
|
|
4209
|
+
function getMcpArgs(config, servers) {
|
|
4210
|
+
if (!hasMcpServers(servers)) {
|
|
4211
|
+
return [];
|
|
4212
|
+
}
|
|
4213
|
+
if (!config.mcpArgs && !config.mcpEnv) {
|
|
4214
|
+
throw new Error(formatUnsupportedMcpSpawnMessage(config.agentId));
|
|
4215
|
+
}
|
|
4216
|
+
if (!config.mcpArgs) {
|
|
4217
|
+
return [];
|
|
4218
|
+
}
|
|
4219
|
+
return config.mcpArgs(servers);
|
|
4220
|
+
}
|
|
4221
|
+
function formatUnsupportedMcpSpawnMessage(agentId) {
|
|
4222
|
+
const supported = listMcpSupportedAgents();
|
|
4223
|
+
const supportedText = supported.length > 0 ? supported.join(", ") : "(none)";
|
|
4224
|
+
return `Agent "${agentId}" does not support MCP servers at spawn time.
|
|
4225
|
+
Agents with spawn-time MCP support: ${supportedText}`;
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
// packages/agent-spawn/src/model-utils.ts
|
|
4229
|
+
function stripModelNamespace(model) {
|
|
4230
|
+
const slashIndex = model.indexOf("/");
|
|
4231
|
+
return slashIndex === -1 ? model : model.slice(slashIndex + 1);
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
// packages/agent-spawn/src/spawn.ts
|
|
4235
|
+
function resolveCliConfig(agentId) {
|
|
4236
|
+
const resolved = resolveConfig(agentId);
|
|
4237
|
+
if (!resolved.spawnConfig) {
|
|
4238
|
+
throw new Error(`Agent "${resolved.agentId}" has no spawn config.`);
|
|
4239
|
+
}
|
|
4240
|
+
if (resolved.spawnConfig.kind !== "cli") {
|
|
4241
|
+
throw new Error(`Agent "${resolved.agentId}" does not support CLI spawn.`);
|
|
4242
|
+
}
|
|
4243
|
+
if (!resolved.binaryName) {
|
|
4244
|
+
throw new Error(`Agent "${resolved.agentId}" has no binaryName.`);
|
|
4245
|
+
}
|
|
4246
|
+
return {
|
|
4247
|
+
agentId: resolved.agentId,
|
|
4248
|
+
binaryName: resolved.binaryName,
|
|
4249
|
+
spawnConfig: resolved.spawnConfig
|
|
4250
|
+
};
|
|
4251
|
+
}
|
|
4252
|
+
function getDefaultArgsPosition(config) {
|
|
4253
|
+
return config.defaultArgsPosition ?? "afterPrompt";
|
|
4254
|
+
}
|
|
4255
|
+
function getMcpArgsPosition(config) {
|
|
4256
|
+
if (config.mcpArgsPosition) {
|
|
4257
|
+
return config.mcpArgsPosition;
|
|
4258
|
+
}
|
|
4259
|
+
return config.mcpArgsBeforeCommand ? "beforeCommand" : "afterCommand";
|
|
4260
|
+
}
|
|
4261
|
+
function buildCliArgs(config, options, stdinMode) {
|
|
4262
|
+
const mcpArgs = getMcpArgs(config, options.mcpServers);
|
|
4263
|
+
const defaultArgsPosition = getDefaultArgsPosition(config);
|
|
4264
|
+
const mcpArgsPosition = getMcpArgsPosition(config);
|
|
4265
|
+
const args = [];
|
|
4266
|
+
if (mcpArgsPosition === "beforeCommand") {
|
|
4267
|
+
args.push(...mcpArgs);
|
|
4268
|
+
}
|
|
4269
|
+
if (defaultArgsPosition === "beforePrompt") {
|
|
4270
|
+
args.push(...config.defaultArgs);
|
|
4271
|
+
}
|
|
4272
|
+
if (mcpArgsPosition === "beforePrompt") {
|
|
4273
|
+
args.push(...mcpArgs);
|
|
4274
|
+
}
|
|
4275
|
+
if (stdinMode) {
|
|
4276
|
+
args.push(
|
|
4277
|
+
config.promptFlag,
|
|
4278
|
+
...stdinMode.omitPrompt ? [] : [options.prompt],
|
|
4279
|
+
...stdinMode.extraArgs
|
|
4280
|
+
);
|
|
4281
|
+
} else {
|
|
4282
|
+
args.push(config.promptFlag, options.prompt);
|
|
4283
|
+
}
|
|
4284
|
+
if (options.model && config.modelFlag) {
|
|
4285
|
+
let model = config.modelStripProviderPrefix ? stripModelNamespace(options.model) : options.model;
|
|
4286
|
+
if (config.modelTransform) model = config.modelTransform(model);
|
|
4287
|
+
args.push(config.modelFlag, model);
|
|
4288
|
+
}
|
|
4289
|
+
if (defaultArgsPosition === "afterPrompt") {
|
|
4290
|
+
args.push(...config.defaultArgs);
|
|
4291
|
+
}
|
|
4292
|
+
if (mcpArgsPosition === "afterCommand") {
|
|
4293
|
+
args.push(...mcpArgs);
|
|
4294
|
+
}
|
|
4295
|
+
const mode = resolveModeConfig(config.modes[options.mode ?? "yolo"]);
|
|
4296
|
+
args.push(...mode.args);
|
|
4297
|
+
if (options.args && options.args.length > 0) {
|
|
4298
|
+
args.push(...options.args);
|
|
4299
|
+
}
|
|
4300
|
+
return { args, env: mode.env };
|
|
4301
|
+
}
|
|
4302
|
+
function buildSpawnArgs(agentId, options) {
|
|
4303
|
+
const { binaryName, spawnConfig } = resolveCliConfig(agentId);
|
|
4304
|
+
const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
|
|
4305
|
+
const result = buildCliArgs(spawnConfig, options, stdinMode);
|
|
4306
|
+
return { binaryName, args: result.args, env: result.env };
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
// packages/design-system/src/tokens/colors.ts
|
|
4310
|
+
import chalk from "chalk";
|
|
4311
|
+
var dark = {
|
|
4312
|
+
header: (text4) => chalk.magentaBright.bold(text4),
|
|
4313
|
+
divider: (text4) => chalk.dim(text4),
|
|
4314
|
+
prompt: (text4) => chalk.cyan(text4),
|
|
4315
|
+
number: (text4) => chalk.cyanBright(text4),
|
|
4316
|
+
intro: (text4) => chalk.bgMagenta.white(` Poe - ${text4} `),
|
|
4317
|
+
resolvedSymbol: chalk.magenta("\u25C7"),
|
|
4318
|
+
errorSymbol: chalk.red("\u25A0"),
|
|
4319
|
+
accent: (text4) => chalk.cyan(text4),
|
|
4320
|
+
muted: (text4) => chalk.dim(text4),
|
|
4321
|
+
success: (text4) => chalk.green(text4),
|
|
4322
|
+
warning: (text4) => chalk.yellow(text4),
|
|
4323
|
+
error: (text4) => chalk.red(text4),
|
|
4324
|
+
info: (text4) => chalk.magenta(text4),
|
|
4325
|
+
badge: (text4) => chalk.bgYellow.black(` ${text4} `)
|
|
4326
|
+
};
|
|
4327
|
+
var light = {
|
|
4328
|
+
header: (text4) => chalk.hex("#a200ff").bold(text4),
|
|
4329
|
+
divider: (text4) => chalk.hex("#666666")(text4),
|
|
4330
|
+
prompt: (text4) => chalk.hex("#006699").bold(text4),
|
|
4331
|
+
number: (text4) => chalk.hex("#0077cc").bold(text4),
|
|
4332
|
+
intro: (text4) => chalk.bgHex("#a200ff").white(` Poe - ${text4} `),
|
|
4333
|
+
resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
|
|
4334
|
+
errorSymbol: chalk.hex("#cc0000")("\u25A0"),
|
|
4335
|
+
accent: (text4) => chalk.hex("#006699").bold(text4),
|
|
4336
|
+
muted: (text4) => chalk.hex("#666666")(text4),
|
|
4337
|
+
success: (text4) => chalk.hex("#008800")(text4),
|
|
4338
|
+
warning: (text4) => chalk.hex("#cc6600")(text4),
|
|
4339
|
+
error: (text4) => chalk.hex("#cc0000")(text4),
|
|
4340
|
+
info: (text4) => chalk.hex("#a200ff")(text4),
|
|
4341
|
+
badge: (text4) => chalk.bgHex("#cc6600").white(` ${text4} `)
|
|
4342
|
+
};
|
|
4343
|
+
|
|
4344
|
+
// packages/design-system/src/tokens/typography.ts
|
|
4345
|
+
import chalk2 from "chalk";
|
|
4346
|
+
|
|
4347
|
+
// packages/design-system/src/components/text.ts
|
|
4348
|
+
import chalk3 from "chalk";
|
|
4349
|
+
|
|
4350
|
+
// packages/design-system/src/internal/output-format.ts
|
|
4351
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4352
|
+
var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
|
|
4353
|
+
var formatStorage = new AsyncLocalStorage();
|
|
4354
|
+
var cached;
|
|
4355
|
+
function resolveOutputFormat(env = process.env) {
|
|
4356
|
+
const scoped = formatStorage.getStore();
|
|
4357
|
+
if (scoped) {
|
|
4358
|
+
return scoped;
|
|
4359
|
+
}
|
|
4360
|
+
if (cached) {
|
|
4361
|
+
return cached;
|
|
4362
|
+
}
|
|
4363
|
+
const raw = env.OUTPUT_FORMAT?.toLowerCase();
|
|
4364
|
+
cached = VALID_FORMATS.has(raw) ? raw : "terminal";
|
|
4365
|
+
return cached;
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
// packages/design-system/src/internal/theme-detect.ts
|
|
4369
|
+
function detectThemeFromEnv(env) {
|
|
4370
|
+
const apple = env.APPLE_INTERFACE_STYLE;
|
|
4371
|
+
if (typeof apple === "string") {
|
|
4372
|
+
return apple.toLowerCase() === "dark" ? "dark" : "light";
|
|
4373
|
+
}
|
|
4374
|
+
const vscodeKind = env.VSCODE_COLOR_THEME_KIND;
|
|
4375
|
+
if (typeof vscodeKind === "string") {
|
|
4376
|
+
const normalized = vscodeKind.toLowerCase();
|
|
4377
|
+
if (normalized.includes("light")) {
|
|
4378
|
+
return "light";
|
|
4379
|
+
}
|
|
4380
|
+
if (normalized.includes("dark")) {
|
|
4381
|
+
return "dark";
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
const colorFGBG = env.COLORFGBG;
|
|
4385
|
+
if (typeof colorFGBG === "string") {
|
|
4386
|
+
const parts = colorFGBG.split(";").map((part) => Number.parseInt(part, 10));
|
|
4387
|
+
const background = parts.at(-1);
|
|
4388
|
+
if (Number.isFinite(background)) {
|
|
4389
|
+
return background >= 8 ? "light" : "dark";
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
return void 0;
|
|
4393
|
+
}
|
|
4394
|
+
function resolveThemeName(env = process.env) {
|
|
4395
|
+
const raw = (env.POE_CODE_THEME ?? env.POE_THEME)?.toLowerCase();
|
|
4396
|
+
if (raw === "light" || raw === "dark") {
|
|
4397
|
+
return raw;
|
|
4398
|
+
}
|
|
4399
|
+
const detected = detectThemeFromEnv(env);
|
|
4400
|
+
if (detected) {
|
|
4401
|
+
return detected;
|
|
4402
|
+
}
|
|
4403
|
+
return "dark";
|
|
4404
|
+
}
|
|
4405
|
+
var cachedTheme;
|
|
4406
|
+
function getTheme(env) {
|
|
4407
|
+
if (cachedTheme) {
|
|
4408
|
+
return cachedTheme;
|
|
4409
|
+
}
|
|
4410
|
+
const themeName = resolveThemeName(env);
|
|
4411
|
+
cachedTheme = themeName === "light" ? light : dark;
|
|
4412
|
+
return cachedTheme;
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
// packages/design-system/src/components/symbols.ts
|
|
4416
|
+
import chalk4 from "chalk";
|
|
4417
|
+
var symbols = {
|
|
4418
|
+
get info() {
|
|
4419
|
+
const format = resolveOutputFormat();
|
|
4420
|
+
if (format === "json") return "info";
|
|
4421
|
+
if (format === "markdown") return "(i)";
|
|
4422
|
+
return chalk4.magenta("\u25CF");
|
|
4423
|
+
},
|
|
4424
|
+
get success() {
|
|
4425
|
+
const format = resolveOutputFormat();
|
|
4426
|
+
if (format === "json") return "success";
|
|
4427
|
+
if (format === "markdown") return "[ok]";
|
|
4428
|
+
return chalk4.magenta("\u25C6");
|
|
4429
|
+
},
|
|
4430
|
+
get resolved() {
|
|
4431
|
+
const format = resolveOutputFormat();
|
|
4432
|
+
if (format === "json") return "resolved";
|
|
4433
|
+
if (format === "markdown") return ">";
|
|
4434
|
+
return getTheme().resolvedSymbol;
|
|
4435
|
+
},
|
|
4436
|
+
get errorResolved() {
|
|
4437
|
+
const format = resolveOutputFormat();
|
|
4438
|
+
if (format === "json") return "error";
|
|
4439
|
+
if (format === "markdown") return "[!]";
|
|
4440
|
+
return getTheme().errorSymbol;
|
|
4441
|
+
},
|
|
4442
|
+
get bar() {
|
|
4443
|
+
const format = resolveOutputFormat();
|
|
4444
|
+
if (format === "json") return "";
|
|
4445
|
+
if (format === "markdown") return "|";
|
|
4446
|
+
return "\u2502";
|
|
4447
|
+
},
|
|
4448
|
+
cornerTopRight: "\u256E",
|
|
4449
|
+
cornerBottomRight: "\u256F",
|
|
4450
|
+
get warning() {
|
|
4451
|
+
const format = resolveOutputFormat();
|
|
4452
|
+
if (format === "json") return "warning";
|
|
4453
|
+
if (format === "markdown") return "[!]";
|
|
4454
|
+
return "\u25B2";
|
|
4455
|
+
},
|
|
4456
|
+
get active() {
|
|
4457
|
+
const format = resolveOutputFormat();
|
|
4458
|
+
if (format === "json") return "active";
|
|
4459
|
+
if (format === "markdown") return "[x]";
|
|
4460
|
+
return "\u25C6";
|
|
4461
|
+
},
|
|
4462
|
+
get inactive() {
|
|
4463
|
+
const format = resolveOutputFormat();
|
|
4464
|
+
if (format === "json") return "inactive";
|
|
4465
|
+
if (format === "markdown") return "[ ]";
|
|
4466
|
+
return "\u25CB";
|
|
4467
|
+
}
|
|
4468
|
+
};
|
|
4469
|
+
|
|
4470
|
+
// packages/design-system/src/components/logger.ts
|
|
4471
|
+
import chalk6 from "chalk";
|
|
4472
|
+
|
|
4473
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
4474
|
+
import chalk5 from "chalk";
|
|
4475
|
+
|
|
4476
|
+
// packages/design-system/src/internal/strip-ansi.ts
|
|
4477
|
+
function stripAnsi(value) {
|
|
4478
|
+
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
4479
|
+
}
|
|
4480
|
+
|
|
4481
|
+
// packages/design-system/src/prompts/primitives/log.ts
|
|
4482
|
+
function writeTerminalMessage(msg, {
|
|
4483
|
+
symbol = chalk5.gray("\u2502"),
|
|
4484
|
+
secondarySymbol = chalk5.gray("\u2502"),
|
|
4485
|
+
spacing: spacing2 = 1,
|
|
4486
|
+
withGuide = true
|
|
4487
|
+
} = {}) {
|
|
4488
|
+
const lines = [];
|
|
4489
|
+
const showGuide = withGuide !== false;
|
|
4490
|
+
const contentLines = msg.split("\n");
|
|
4491
|
+
const prefix = showGuide ? `${symbol} ` : "";
|
|
4492
|
+
const continuationPrefix = showGuide ? `${secondarySymbol} ` : "";
|
|
4493
|
+
const emptyGuide = showGuide ? secondarySymbol : "";
|
|
4494
|
+
for (let index = 0; index < spacing2; index += 1) {
|
|
4495
|
+
lines.push(emptyGuide);
|
|
4496
|
+
}
|
|
4497
|
+
if (contentLines.length === 0) {
|
|
4498
|
+
process.stdout.write("\n");
|
|
4499
|
+
return;
|
|
4500
|
+
}
|
|
4501
|
+
const [firstLine = "", ...continuationLines] = contentLines;
|
|
4502
|
+
if (firstLine.length > 0) {
|
|
4503
|
+
lines.push(`${prefix}${firstLine}`);
|
|
4504
|
+
} else {
|
|
4505
|
+
lines.push(showGuide ? symbol : "");
|
|
4506
|
+
}
|
|
4507
|
+
for (const line of continuationLines) {
|
|
4508
|
+
if (line.length > 0) {
|
|
4509
|
+
lines.push(`${continuationPrefix}${line}`);
|
|
4510
|
+
continue;
|
|
4511
|
+
}
|
|
4512
|
+
lines.push(emptyGuide);
|
|
4513
|
+
}
|
|
4514
|
+
process.stdout.write(`${lines.join("\n")}
|
|
4515
|
+
`);
|
|
4516
|
+
}
|
|
4517
|
+
function message(msg, options) {
|
|
4518
|
+
const format = resolveOutputFormat();
|
|
4519
|
+
if (format === "markdown") {
|
|
4520
|
+
process.stdout.write(`- ${stripAnsi(msg)}
|
|
4521
|
+
`);
|
|
4522
|
+
return;
|
|
4523
|
+
}
|
|
4524
|
+
if (format === "json") {
|
|
4525
|
+
process.stdout.write(
|
|
4526
|
+
`${JSON.stringify({ level: "message", message: stripAnsi(msg) })}
|
|
4527
|
+
`
|
|
4528
|
+
);
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
writeTerminalMessage(msg, options);
|
|
4532
|
+
}
|
|
4533
|
+
function info(msg) {
|
|
4534
|
+
const format = resolveOutputFormat();
|
|
4535
|
+
if (format === "markdown") {
|
|
4536
|
+
process.stdout.write(`- **info:** ${stripAnsi(msg)}
|
|
4537
|
+
`);
|
|
4538
|
+
return;
|
|
4539
|
+
}
|
|
4540
|
+
if (format === "json") {
|
|
4541
|
+
process.stdout.write(
|
|
4542
|
+
`${JSON.stringify({ level: "info", message: stripAnsi(msg) })}
|
|
4543
|
+
`
|
|
4544
|
+
);
|
|
4545
|
+
return;
|
|
4546
|
+
}
|
|
4547
|
+
message(msg, { symbol: symbols.info });
|
|
4548
|
+
}
|
|
4549
|
+
function success(msg) {
|
|
4550
|
+
const format = resolveOutputFormat();
|
|
4551
|
+
if (format === "markdown") {
|
|
4552
|
+
process.stdout.write(`- **success:** ${stripAnsi(msg)}
|
|
4553
|
+
`);
|
|
4554
|
+
return;
|
|
4555
|
+
}
|
|
4556
|
+
if (format === "json") {
|
|
4557
|
+
process.stdout.write(
|
|
4558
|
+
`${JSON.stringify({ level: "success", message: stripAnsi(msg) })}
|
|
4559
|
+
`
|
|
4560
|
+
);
|
|
4561
|
+
return;
|
|
4562
|
+
}
|
|
4563
|
+
message(msg, { symbol: symbols.success });
|
|
4564
|
+
}
|
|
4565
|
+
function warn(msg) {
|
|
4566
|
+
const format = resolveOutputFormat();
|
|
4567
|
+
if (format === "markdown") {
|
|
4568
|
+
process.stdout.write(`- **warning:** ${stripAnsi(msg)}
|
|
4569
|
+
`);
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
if (format === "json") {
|
|
4573
|
+
process.stdout.write(
|
|
4574
|
+
`${JSON.stringify({ level: "warn", message: stripAnsi(msg) })}
|
|
4575
|
+
`
|
|
4576
|
+
);
|
|
4577
|
+
return;
|
|
4578
|
+
}
|
|
4579
|
+
message(msg, { symbol: chalk5.yellow("\u25B2") });
|
|
4580
|
+
}
|
|
4581
|
+
function error(msg) {
|
|
4582
|
+
const format = resolveOutputFormat();
|
|
4583
|
+
if (format === "markdown") {
|
|
4584
|
+
process.stdout.write(`- **error:** ${stripAnsi(msg)}
|
|
4585
|
+
`);
|
|
4586
|
+
return;
|
|
4587
|
+
}
|
|
4588
|
+
if (format === "json") {
|
|
4589
|
+
process.stdout.write(
|
|
4590
|
+
`${JSON.stringify({ level: "error", message: stripAnsi(msg) })}
|
|
4591
|
+
`
|
|
4592
|
+
);
|
|
4593
|
+
return;
|
|
4594
|
+
}
|
|
4595
|
+
message(msg, { symbol: chalk5.red("\u25A0") });
|
|
4596
|
+
}
|
|
4597
|
+
var log = {
|
|
4598
|
+
info,
|
|
4599
|
+
success,
|
|
4600
|
+
message,
|
|
4601
|
+
warn,
|
|
4602
|
+
error
|
|
4603
|
+
};
|
|
4604
|
+
|
|
4605
|
+
// packages/design-system/src/components/logger.ts
|
|
4606
|
+
function createLogger(emitter) {
|
|
4607
|
+
const emit = (level, message2) => {
|
|
4608
|
+
if (emitter) {
|
|
4609
|
+
emitter(message2);
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
4612
|
+
if (level === "success") {
|
|
4613
|
+
log.success(message2);
|
|
4614
|
+
return;
|
|
4615
|
+
}
|
|
4616
|
+
if (level === "warn") {
|
|
4617
|
+
log.warn(message2);
|
|
4618
|
+
return;
|
|
4619
|
+
}
|
|
4620
|
+
if (level === "error") {
|
|
4621
|
+
log.error(message2);
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
log.info(message2);
|
|
4625
|
+
};
|
|
4626
|
+
return {
|
|
4627
|
+
info(message2) {
|
|
4628
|
+
emit("info", message2);
|
|
4629
|
+
},
|
|
4630
|
+
success(message2) {
|
|
4631
|
+
emit("success", message2);
|
|
4632
|
+
},
|
|
4633
|
+
warn(message2) {
|
|
4634
|
+
emit("warn", message2);
|
|
4635
|
+
},
|
|
4636
|
+
error(message2) {
|
|
4637
|
+
emit("error", message2);
|
|
4638
|
+
},
|
|
4639
|
+
resolved(label, value) {
|
|
4640
|
+
if (emitter) {
|
|
4641
|
+
emitter(`${label}: ${value}`);
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4644
|
+
log.message(`${label}
|
|
4645
|
+
${value}`, { symbol: symbols.resolved });
|
|
4646
|
+
},
|
|
4647
|
+
errorResolved(label, value) {
|
|
4648
|
+
if (emitter) {
|
|
4649
|
+
emitter(`${label}: ${value}`);
|
|
4650
|
+
return;
|
|
4651
|
+
}
|
|
4652
|
+
log.message(`${label}
|
|
4653
|
+
${value}`, { symbol: symbols.errorResolved });
|
|
4654
|
+
},
|
|
4655
|
+
message(message2, symbol) {
|
|
4656
|
+
if (emitter) {
|
|
4657
|
+
emitter(message2);
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
log.message(message2, { symbol: symbol ?? chalk6.gray("\u2502") });
|
|
4661
|
+
}
|
|
4662
|
+
};
|
|
4663
|
+
}
|
|
4664
|
+
var logger = createLogger();
|
|
4665
|
+
|
|
4666
|
+
// packages/design-system/src/components/table.ts
|
|
4667
|
+
import { Table } from "console-table-printer";
|
|
4668
|
+
|
|
4669
|
+
// packages/design-system/src/acp/components.ts
|
|
4670
|
+
import chalk7 from "chalk";
|
|
4671
|
+
|
|
4672
|
+
// packages/design-system/src/acp/writer.ts
|
|
4673
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
4674
|
+
var storage = new AsyncLocalStorage2();
|
|
4675
|
+
|
|
4676
|
+
// packages/design-system/src/acp/components.ts
|
|
4677
|
+
var AGENT_PREFIX = `${chalk7.green.bold("\u2713")} agent: `;
|
|
4678
|
+
|
|
4679
|
+
// packages/design-system/src/dashboard/buffer.ts
|
|
4680
|
+
import chalk8 from "chalk";
|
|
4681
|
+
|
|
4682
|
+
// packages/design-system/src/dashboard/terminal.ts
|
|
4683
|
+
import readline from "node:readline";
|
|
4684
|
+
import { PassThrough as PassThrough2 } from "node:stream";
|
|
4685
|
+
|
|
4686
|
+
// packages/design-system/src/prompts/index.ts
|
|
4687
|
+
import chalk15 from "chalk";
|
|
4688
|
+
import * as clack from "@clack/prompts";
|
|
4689
|
+
|
|
4690
|
+
// packages/design-system/src/prompts/primitives/cancel.ts
|
|
4691
|
+
import chalk9 from "chalk";
|
|
4692
|
+
import { isCancel } from "@clack/prompts";
|
|
4693
|
+
|
|
4694
|
+
// packages/design-system/src/prompts/primitives/intro.ts
|
|
4695
|
+
import chalk10 from "chalk";
|
|
4696
|
+
|
|
4697
|
+
// packages/design-system/src/prompts/primitives/note.ts
|
|
4698
|
+
import chalk11 from "chalk";
|
|
4699
|
+
|
|
4700
|
+
// packages/design-system/src/prompts/primitives/outro.ts
|
|
4701
|
+
import chalk12 from "chalk";
|
|
4702
|
+
|
|
4703
|
+
// packages/design-system/src/prompts/primitives/spinner.ts
|
|
4704
|
+
import chalk14 from "chalk";
|
|
4705
|
+
|
|
4706
|
+
// packages/design-system/src/static/spinner.ts
|
|
4707
|
+
import chalk13 from "chalk";
|
|
4708
|
+
|
|
4709
|
+
// packages/design-system/src/static/menu.ts
|
|
4710
|
+
import chalk16 from "chalk";
|
|
4711
|
+
|
|
4712
|
+
// packages/agent-spawn/src/autonomous.ts
|
|
4713
|
+
var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
4714
|
+
|
|
4715
|
+
// packages/agent-spawn/src/acp/replay.ts
|
|
4716
|
+
import path20 from "node:path";
|
|
4717
|
+
import { homedir as homedir2 } from "node:os";
|
|
4718
|
+
import { open as open2, readdir as readdir2 } from "node:fs/promises";
|
|
4719
|
+
import { createInterface } from "node:readline";
|
|
4720
|
+
|
|
4721
|
+
// packages/poe-acp-client/src/acp-client.ts
|
|
4722
|
+
import { isAbsolute } from "node:path";
|
|
4723
|
+
|
|
4724
|
+
// packages/poe-acp-client/src/acp-transport.ts
|
|
4725
|
+
import {
|
|
4726
|
+
spawn as spawnChildProcess3
|
|
4727
|
+
} from "node:child_process";
|
|
4728
|
+
|
|
4729
|
+
// packages/poe-acp-client/src/run-report.ts
|
|
4730
|
+
import * as fsPromises2 from "node:fs/promises";
|
|
4731
|
+
import { homedir } from "node:os";
|
|
4732
|
+
import { join } from "node:path";
|
|
4733
|
+
|
|
4734
|
+
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
4735
|
+
import path21 from "node:path";
|
|
4736
|
+
import { homedir as homedir3 } from "node:os";
|
|
4737
|
+
import { mkdir, open as open3 } from "node:fs/promises";
|
|
4738
|
+
|
|
4739
|
+
// src/utils/command-checks.ts
|
|
4740
|
+
function formatCommandRunnerResult(result) {
|
|
4741
|
+
const stdout = result.stdout.length > 0 ? result.stdout : "<empty>";
|
|
4742
|
+
const stderr = result.stderr.length > 0 ? result.stderr : "<empty>";
|
|
4743
|
+
return `stdout:
|
|
4744
|
+
${stdout}
|
|
4745
|
+
stderr:
|
|
4746
|
+
${stderr}`;
|
|
4747
|
+
}
|
|
4748
|
+
function createSpawnHealthCheck(agentId, options) {
|
|
4749
|
+
const prompt = `Output exactly: ${options.expectedOutput}`;
|
|
4750
|
+
const { binaryName, args, env: modeEnv } = buildSpawnArgs(agentId, {
|
|
4751
|
+
prompt,
|
|
4752
|
+
model: options.model,
|
|
4753
|
+
mode: "yolo"
|
|
4754
|
+
});
|
|
4755
|
+
return {
|
|
4756
|
+
id: `${agentId}-cli-health`,
|
|
4757
|
+
description: `spawn ${agentId} (expecting "${options.expectedOutput}")`,
|
|
4758
|
+
async run(context) {
|
|
4759
|
+
if (context.isDryRun) {
|
|
4760
|
+
context.logDryRun?.(
|
|
4761
|
+
`Dry run: ${[binaryName, ...args].join(" ")} (expecting "${options.expectedOutput}")`
|
|
4762
|
+
);
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
const result = modeEnv ? await context.runCommand(binaryName, args, { env: modeEnv }) : await context.runCommand(binaryName, args);
|
|
4766
|
+
if (result.exitCode !== 0) {
|
|
4767
|
+
throw new Error(
|
|
4768
|
+
`spawn ${agentId} failed with exit code ${result.exitCode}.
|
|
4769
|
+
${formatCommandRunnerResult(result)}`
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
if (!result.stdout.includes(options.expectedOutput)) {
|
|
4773
|
+
throw new Error(
|
|
4774
|
+
`spawn ${agentId}: expected "${options.expectedOutput}" in stdout.
|
|
4775
|
+
${formatCommandRunnerResult(result)}`
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
function createBinaryExistsCheck(binaryName, id, description) {
|
|
4782
|
+
return {
|
|
4783
|
+
id,
|
|
4784
|
+
description,
|
|
4785
|
+
async run({ runCommand: runCommand2 }) {
|
|
4786
|
+
for (const detector of createBinaryExistsDetectors(binaryName)) {
|
|
4787
|
+
const result = await runCommand2(detector.command, detector.args);
|
|
4788
|
+
if (detector.validate(result)) {
|
|
4789
|
+
return;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
throw new Error(`${binaryName} CLI binary not found on PATH.`);
|
|
4793
|
+
}
|
|
4794
|
+
};
|
|
4795
|
+
}
|
|
4796
|
+
|
|
2163
4797
|
// src/services/service-install.ts
|
|
2164
4798
|
async function runServiceInstall(definition, context) {
|
|
2165
4799
|
const checkContext = {
|