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.
Files changed (119) hide show
  1. package/dist/cli/commands/braintrust.d.ts +3 -0
  2. package/dist/cli/commands/braintrust.js +77 -0
  3. package/dist/cli/commands/braintrust.js.map +1 -0
  4. package/dist/cli/commands/configure.d.ts +1 -0
  5. package/dist/cli/commands/configure.js +197 -0
  6. package/dist/cli/commands/configure.js.map +1 -1
  7. package/dist/cli/commands/experiment.js +51 -7
  8. package/dist/cli/commands/experiment.js.map +1 -1
  9. package/dist/cli/commands/harness.d.ts +3 -0
  10. package/dist/cli/commands/harness.js +260 -0
  11. package/dist/cli/commands/harness.js.map +1 -0
  12. package/dist/cli/commands/pipeline.js +58 -24
  13. package/dist/cli/commands/pipeline.js.map +1 -1
  14. package/dist/cli/commands/ralph.js +19 -7
  15. package/dist/cli/commands/ralph.js.map +1 -1
  16. package/dist/cli/commands/runtime/build.d.ts +7 -0
  17. package/dist/cli/commands/runtime/build.js +128 -0
  18. package/dist/cli/commands/runtime/build.js.map +1 -0
  19. package/dist/cli/commands/runtime/index.d.ts +3 -0
  20. package/dist/cli/commands/runtime/index.js +14 -0
  21. package/dist/cli/commands/runtime/index.js.map +1 -0
  22. package/dist/cli/commands/runtime/init.d.ts +7 -0
  23. package/dist/cli/commands/runtime/init.js +39 -0
  24. package/dist/cli/commands/runtime/init.js.map +1 -0
  25. package/dist/cli/commands/runtime/jobs/attach.d.ts +3 -0
  26. package/dist/cli/commands/runtime/jobs/attach.js +35 -0
  27. package/dist/cli/commands/runtime/jobs/attach.js.map +1 -0
  28. package/dist/cli/commands/runtime/jobs/index.d.ts +3 -0
  29. package/dist/cli/commands/runtime/jobs/index.js +16 -0
  30. package/dist/cli/commands/runtime/jobs/index.js.map +1 -0
  31. package/dist/cli/commands/runtime/jobs/logs.d.ts +3 -0
  32. package/dist/cli/commands/runtime/jobs/logs.js +27 -0
  33. package/dist/cli/commands/runtime/jobs/logs.js.map +1 -0
  34. package/dist/cli/commands/runtime/jobs/ls.d.ts +3 -0
  35. package/dist/cli/commands/runtime/jobs/ls.js +60 -0
  36. package/dist/cli/commands/runtime/jobs/ls.js.map +1 -0
  37. package/dist/cli/commands/runtime/jobs/sandbox.d.ts +3 -0
  38. package/dist/cli/commands/runtime/jobs/sandbox.js +15 -0
  39. package/dist/cli/commands/runtime/jobs/sandbox.js.map +1 -0
  40. package/dist/cli/commands/runtime/jobs/shared.d.ts +22 -0
  41. package/dist/cli/commands/runtime/jobs/shared.js +124 -0
  42. package/dist/cli/commands/runtime/jobs/shared.js.map +1 -0
  43. package/dist/cli/commands/runtime/jobs/stop.d.ts +3 -0
  44. package/dist/cli/commands/runtime/jobs/stop.js +31 -0
  45. package/dist/cli/commands/runtime/jobs/stop.js.map +1 -0
  46. package/dist/cli/commands/runtime/jobs/sync.d.ts +3 -0
  47. package/dist/cli/commands/runtime/jobs/sync.js +25 -0
  48. package/dist/cli/commands/runtime/jobs/sync.js.map +1 -0
  49. package/dist/cli/commands/runtime/shared.d.ts +20 -0
  50. package/dist/cli/commands/runtime/shared.js +69 -0
  51. package/dist/cli/commands/runtime/shared.js.map +1 -0
  52. package/dist/cli/commands/runtime/templates/clear.d.ts +3 -0
  53. package/dist/cli/commands/runtime/templates/clear.js +53 -0
  54. package/dist/cli/commands/runtime/templates/clear.js.map +1 -0
  55. package/dist/cli/commands/runtime/templates/index.d.ts +3 -0
  56. package/dist/cli/commands/runtime/templates/index.js +10 -0
  57. package/dist/cli/commands/runtime/templates/index.js.map +1 -0
  58. package/dist/cli/commands/runtime/templates/ls.d.ts +3 -0
  59. package/dist/cli/commands/runtime/templates/ls.js +52 -0
  60. package/dist/cli/commands/runtime/templates/ls.js.map +1 -0
  61. package/dist/cli/commands/runtime-options.d.ts +11 -0
  62. package/dist/cli/commands/runtime-options.js +26 -0
  63. package/dist/cli/commands/runtime-options.js.map +1 -0
  64. package/dist/cli/commands/spawn.js +36 -7
  65. package/dist/cli/commands/spawn.js.map +1 -1
  66. package/dist/cli/program.js +17 -1
  67. package/dist/cli/program.js.map +1 -1
  68. package/dist/index.js +70879 -47381
  69. package/dist/index.js.map +4 -4
  70. package/dist/providers/claude-code.js +3486 -852
  71. package/dist/providers/claude-code.js.map +4 -4
  72. package/dist/providers/codex.js +3486 -852
  73. package/dist/providers/codex.js.map +4 -4
  74. package/dist/providers/goose.js +3432 -798
  75. package/dist/providers/goose.js.map +4 -4
  76. package/dist/providers/kimi.js +3486 -852
  77. package/dist/providers/kimi.js.map +4 -4
  78. package/dist/providers/opencode.js +3486 -852
  79. package/dist/providers/opencode.js.map +4 -4
  80. package/dist/providers/poe-agent.js +20641 -17147
  81. package/dist/providers/poe-agent.js.map +4 -4
  82. package/dist/providers/spawn-options.d.ts +10 -1
  83. package/dist/sdk/experiment.js +6 -0
  84. package/dist/sdk/experiment.js.map +1 -1
  85. package/dist/sdk/ralph.js +108 -11
  86. package/dist/sdk/ralph.js.map +1 -1
  87. package/dist/sdk/spawn.js +27 -4
  88. package/dist/sdk/spawn.js.map +1 -1
  89. package/dist/sdk/types.d.ts +23 -1
  90. package/dist/utils/command-checks.js +2 -29
  91. package/dist/utils/command-checks.js.map +1 -1
  92. package/package.json +12 -7
  93. package/packages/design-system/dist/components/help-formatter-plain.d.ts +4 -0
  94. package/packages/design-system/dist/components/help-formatter-plain.js +132 -0
  95. package/packages/design-system/dist/components/help-formatter.d.ts +13 -0
  96. package/packages/design-system/dist/components/help-formatter.js +116 -7
  97. package/packages/design-system/dist/components/index.d.ts +2 -2
  98. package/packages/design-system/dist/components/index.js +1 -1
  99. package/packages/design-system/dist/components/text.d.ts +1 -0
  100. package/packages/design-system/dist/components/text.js +8 -0
  101. package/packages/design-system/dist/index.d.ts +3 -2
  102. package/packages/design-system/dist/index.js +2 -1
  103. package/packages/memory/dist/index.js +3406 -387
  104. package/packages/memory/dist/index.js.map +4 -4
  105. package/packages/superintendent/dist/commands/run.d.ts +45 -0
  106. package/packages/superintendent/dist/commands/run.js +133 -38
  107. package/packages/superintendent/dist/commands/superintendent-group.d.ts +36 -0
  108. package/packages/superintendent/dist/runtime/agent-runner.d.ts +31 -0
  109. package/packages/superintendent/dist/runtime/agent-runner.js +121 -0
  110. package/packages/superintendent/dist/runtime/loop.d.ts +7 -1
  111. package/packages/superintendent/dist/runtime/loop.js +3 -11
  112. package/packages/superintendent/dist/runtime/run-builder.d.ts +1 -0
  113. package/packages/superintendent/dist/runtime/run-builder.js +3 -25
  114. package/packages/superintendent/dist/runtime/run-inspector.d.ts +1 -0
  115. package/packages/superintendent/dist/runtime/run-inspector.js +3 -25
  116. package/packages/superintendent/dist/runtime/run-owner-review.d.ts +1 -0
  117. package/packages/superintendent/dist/runtime/run-owner-review.js +3 -25
  118. package/packages/superintendent/dist/runtime/run-superintendent.d.ts +1 -0
  119. package/packages/superintendent/dist/runtime/run-superintendent.js +3 -25
@@ -94,6 +94,11 @@ var runtimeConfigScope = {
94
94
  default: "",
95
95
  doc: "Path to the Docker build context"
96
96
  },
97
+ workspace_dir: {
98
+ type: "string",
99
+ default: "/workspace",
100
+ doc: "Sandbox-local workspace directory for E2B runtime upload, execution, and download"
101
+ },
97
102
  engine: {
98
103
  type: "string",
99
104
  default: "",
@@ -115,6 +120,11 @@ var runtimeConfigScope = {
115
120
  default: "",
116
121
  doc: "Prebuilt E2B template id"
117
122
  },
123
+ from_template: {
124
+ type: "string",
125
+ default: "",
126
+ doc: "Existing E2B template alias to extend instead of using the Dockerfile FROM image"
127
+ },
118
128
  cpu: {
119
129
  type: "json",
120
130
  default: void 0,
@@ -138,11 +148,6 @@ var runtimeConfigScope = {
138
148
  default: void 0,
139
149
  parse: parseOptionalNumber,
140
150
  doc: "Hours to keep an E2B sandbox alive after job exit"
141
- },
142
- api_key_env: {
143
- type: "string",
144
- default: "",
145
- doc: "Environment variable name containing the E2B API key"
146
151
  }
147
152
  }
148
153
  };
@@ -162,14 +167,158 @@ function parseRunner(raw) {
162
167
  detach: parseOptionalBoolean(record.detach, "runner.detach") ?? false,
163
168
  upload_max_file_mb: uploadMaxFileMb,
164
169
  download_conflict: parseDownloadConflict(record.download_conflict),
170
+ sync: parseRunnerSync(record.sync),
165
171
  workspace: parseRunnerWorkspace(record.workspace)
166
172
  });
167
173
  }
174
+ function parseRuntime(raw) {
175
+ if (raw === void 0) {
176
+ return {
177
+ type: "host",
178
+ build_args: {},
179
+ mounts: []
180
+ };
181
+ }
182
+ const record = asRecord(raw);
183
+ if (record === void 0) {
184
+ throw new Error("runtime: expected an object.");
185
+ }
186
+ const type = parseRuntimeType(record.type);
187
+ const shared = parseSharedRuntimeFields(record);
188
+ if (type === "docker") {
189
+ return omitUndefined({
190
+ ...shared,
191
+ type,
192
+ image: parseOptionalString(record.image),
193
+ dockerfile: parseOptionalString(record.dockerfile),
194
+ build_context: parseOptionalString(record.build_context),
195
+ engine: parseEngine(record.engine),
196
+ network: parseOptionalString(record.network),
197
+ extra_args: parseOptionalStringArray(record.extra_args)
198
+ });
199
+ }
200
+ if (type === "e2b") {
201
+ const preserveAfterExitHours = parseOptionalNumber(record.preserve_after_exit_hours) ?? 24;
202
+ if (preserveAfterExitHours < 0 || preserveAfterExitHours > 168) {
203
+ throw new Error("preserve_after_exit_hours: expected a number from 0 to 168.");
204
+ }
205
+ return omitUndefined({
206
+ ...shared,
207
+ type,
208
+ template_id: parseOptionalString(record.template_id),
209
+ from_template: parseOptionalString(record.from_template),
210
+ dockerfile: parseOptionalString(record.dockerfile),
211
+ build_context: parseOptionalString(record.build_context),
212
+ workspace_dir: parseWorkspaceDir(record.workspace_dir),
213
+ cpu: parseOptionalNumber(record.cpu),
214
+ memory_mb: parseOptionalNumber(record.memory_mb),
215
+ timeout_minutes: parseOptionalNumber(record.timeout_minutes),
216
+ preserve_after_exit_hours: preserveAfterExitHours
217
+ });
218
+ }
219
+ return {
220
+ ...shared,
221
+ type
222
+ };
223
+ }
224
+ function resolveRuntime({
225
+ cwd,
226
+ config
227
+ }) {
228
+ const runtime = config.runtime;
229
+ return runtimeResolvers[runtime.type]({ cwd, runtime });
230
+ }
231
+ var runtimeResolvers = {
232
+ host({ runtime }) {
233
+ return {
234
+ runtime,
235
+ runner: "host",
236
+ dockerfilePath: null,
237
+ buildContext: null
238
+ };
239
+ },
240
+ docker({ cwd, runtime }) {
241
+ const dockerRuntime = runtime;
242
+ const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, dockerRuntime);
243
+ if (dockerRuntime.image !== void 0) {
244
+ return {
245
+ runtime: dockerRuntime,
246
+ runner: "docker",
247
+ dockerfilePath: null,
248
+ buildContext: null
249
+ };
250
+ }
251
+ if (!existsSync(dockerfilePath)) {
252
+ throw new Error(`Docker runtime requires image or a Dockerfile at ${dockerfilePath}.`);
253
+ }
254
+ return {
255
+ runtime: dockerRuntime,
256
+ runner: "docker",
257
+ dockerfilePath,
258
+ buildContext
259
+ };
260
+ },
261
+ e2b({ cwd, runtime }) {
262
+ const e2bRuntime = runtime;
263
+ const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, e2bRuntime);
264
+ if (e2bRuntime.template_id !== void 0) {
265
+ return {
266
+ runtime: e2bRuntime,
267
+ runner: "e2b",
268
+ dockerfilePath: null,
269
+ buildContext: null
270
+ };
271
+ }
272
+ if (!existsSync(dockerfilePath)) {
273
+ throw new Error(`E2B runtime requires template_id or a Dockerfile at ${dockerfilePath}.`);
274
+ }
275
+ return {
276
+ runtime: e2bRuntime,
277
+ runner: "e2b",
278
+ dockerfilePath,
279
+ buildContext
280
+ };
281
+ }
282
+ };
283
+ function resolveRuntimeBuildPaths(cwd, runtime) {
284
+ return {
285
+ dockerfilePath: path2.resolve(cwd, runtime.dockerfile ?? path2.join(".poe-code", "Dockerfile")),
286
+ buildContext: path2.resolve(cwd, runtime.build_context ?? ".")
287
+ };
288
+ }
289
+ function parseSharedRuntimeFields(record) {
290
+ return omitUndefined({
291
+ build_args: parseBuildArgs(record.build_args),
292
+ mounts: parseMounts(record.mounts),
293
+ link: parseOptionalString(record.link)
294
+ });
295
+ }
296
+ function parseRuntimeType(value) {
297
+ if (value === void 0) {
298
+ return "host";
299
+ }
300
+ if (value === "host" || value === "docker" || value === "e2b") {
301
+ return value;
302
+ }
303
+ throw new Error('type: expected "host", "docker", or "e2b".');
304
+ }
305
+ function parseWorkspaceDir(value) {
306
+ const workspaceDir = parseOptionalString(value) ?? "/workspace";
307
+ if (!path2.posix.isAbsolute(workspaceDir)) {
308
+ throw new Error("workspace_dir: expected an absolute sandbox path.");
309
+ }
310
+ let normalized = path2.posix.normalize(workspaceDir);
311
+ while (normalized.length > 1 && normalized.endsWith("/")) {
312
+ normalized = normalized.slice(0, -1);
313
+ }
314
+ return normalized;
315
+ }
168
316
  function createDefaultRunnerScope() {
169
317
  return {
170
318
  detach: false,
171
319
  upload_max_file_mb: 100,
172
320
  download_conflict: "refuse",
321
+ sync: "both",
173
322
  workspace: {
174
323
  exclude: [...defaultWorkspaceExclude]
175
324
  }
@@ -200,6 +349,15 @@ function parseDownloadConflict(value) {
200
349
  }
201
350
  throw new Error('runner.download_conflict: expected "refuse" or "overwrite".');
202
351
  }
352
+ function parseRunnerSync(value) {
353
+ if (value === void 0) {
354
+ return "both";
355
+ }
356
+ if (value === "both" || value === "upload" || value === "none") {
357
+ return value;
358
+ }
359
+ throw new Error('runner.sync: expected "both", "upload", or "none".');
360
+ }
203
361
  function parseBuildArgs(value) {
204
362
  if (value === void 0) {
205
363
  return {};
@@ -244,6 +402,18 @@ function parseMounts(value) {
244
402
  });
245
403
  });
246
404
  }
405
+ function parseOptionalString(value) {
406
+ if (value === void 0) {
407
+ return void 0;
408
+ }
409
+ if (typeof value !== "string") {
410
+ throw new Error("expected a string.");
411
+ }
412
+ if (value.length === 0) {
413
+ return void 0;
414
+ }
415
+ return value;
416
+ }
247
417
  function parseOptionalStringArray(value, key = "") {
248
418
  if (value === void 0) {
249
419
  return void 0;
@@ -258,6 +428,13 @@ function parseOptionalStringArray(value, key = "") {
258
428
  return entry;
259
429
  });
260
430
  }
431
+ function parseEngine(value) {
432
+ const engine = parseOptionalString(value);
433
+ if (engine === void 0 || engine === "docker" || engine === "podman") {
434
+ return engine;
435
+ }
436
+ throw new Error('engine: expected "docker" or "podman".');
437
+ }
261
438
  function parseOptionalNumber(value, key = "") {
262
439
  if (value === void 0) {
263
440
  return void 0;
@@ -293,6 +470,43 @@ function defineScope(scope, schema) {
293
470
  schema
294
471
  };
295
472
  }
473
+ var integrationsConfigScope = defineScope("integrations", {
474
+ braintrust: {
475
+ type: "json",
476
+ default: {
477
+ enabled: false
478
+ },
479
+ parse: parseBraintrustIntegrationConfig,
480
+ doc: "Braintrust integration configuration"
481
+ }
482
+ });
483
+ function parseBraintrustIntegrationConfig(value) {
484
+ if (!isRecord(value)) {
485
+ throw new Error("expected an object");
486
+ }
487
+ const enabled = value.enabled === void 0 ? false : value.enabled;
488
+ if (typeof enabled !== "boolean") {
489
+ throw new Error("enabled must be a boolean");
490
+ }
491
+ return {
492
+ enabled,
493
+ ...optionalStringEntry("apiKey", value.apiKey),
494
+ ...optionalStringEntry("apiUrl", value.apiUrl),
495
+ ...optionalStringEntry("project", value.project)
496
+ };
497
+ }
498
+ function optionalStringEntry(key, value) {
499
+ if (value === void 0) {
500
+ return {};
501
+ }
502
+ if (typeof value !== "string") {
503
+ throw new Error(`${key} must be a string`);
504
+ }
505
+ return { [key]: value };
506
+ }
507
+ function isRecord(value) {
508
+ return typeof value === "object" && value !== null && !Array.isArray(value);
509
+ }
296
510
 
297
511
  // packages/poe-code-config/src/plan-scope.ts
298
512
  var planConfigScope = defineScope("plan", {
@@ -397,11 +611,11 @@ function stripBom(content) {
397
611
  function mergeLayers(layers) {
398
612
  return mergeObjectLayers(layers, []);
399
613
  }
400
- function mergeObjectLayers(layers, path33) {
614
+ function mergeObjectLayers(layers, path42) {
401
615
  const data = {};
402
616
  const sources = {};
403
617
  for (const key of collectKeys(layers)) {
404
- const resolved = resolveKey(layers, key, path33);
618
+ const resolved = resolveKey(layers, key, path42);
405
619
  if (resolved === void 0) {
406
620
  continue;
407
621
  }
@@ -419,7 +633,7 @@ function collectKeys(layers) {
419
633
  }
420
634
  return [...keys];
421
635
  }
422
- function resolveKey(layers, key, path33) {
636
+ function resolveKey(layers, key, path42) {
423
637
  let winningSource;
424
638
  let winningValue;
425
639
  const objectLayers = [];
@@ -449,9 +663,9 @@ function resolveKey(layers, key, path33) {
449
663
  if (winningSource === void 0) {
450
664
  return void 0;
451
665
  }
452
- const fullPath = buildPath(path33, key);
666
+ const fullPath = buildPath(path42, key);
453
667
  if (isPlainObject(winningValue)) {
454
- const merged = mergeObjectLayers(objectLayers, [...path33, key]);
668
+ const merged = mergeObjectLayers(objectLayers, [...path42, key]);
455
669
  return {
456
670
  value: merged.data,
457
671
  sources: {
@@ -476,8 +690,8 @@ function isWinningCandidate(key, value) {
476
690
  }
477
691
  return true;
478
692
  }
479
- function buildPath(path33, key) {
480
- return [...path33, key].join(".");
693
+ function buildPath(path42, key) {
694
+ return [...path42, key].join(".");
481
695
  }
482
696
  function isPlainObject(value) {
483
697
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -1111,16 +1325,16 @@ function getConfigFormat(pathOrFormat) {
1111
1325
  }
1112
1326
  return formatRegistry[formatName];
1113
1327
  }
1114
- function detectFormat2(path33) {
1115
- const ext = getExtension(path33);
1328
+ function detectFormat2(path42) {
1329
+ const ext = getExtension(path42);
1116
1330
  return extensionMap[ext];
1117
1331
  }
1118
- function getExtension(path33) {
1119
- const lastDot = path33.lastIndexOf(".");
1332
+ function getExtension(path42) {
1333
+ const lastDot = path42.lastIndexOf(".");
1120
1334
  if (lastDot === -1) {
1121
1335
  return "";
1122
1336
  }
1123
- return path33.slice(lastDot).toLowerCase();
1337
+ return path42.slice(lastDot).toLowerCase();
1124
1338
  }
1125
1339
 
1126
1340
  // packages/config-mutations/src/execution/path-utils.ts
@@ -1828,7 +2042,7 @@ async function parseStoredDocument(fs14, filePath, raw) {
1828
2042
  }
1829
2043
  }
1830
2044
  function normalizeDocument(value) {
1831
- if (!isRecord(value)) {
2045
+ if (!isRecord2(value)) {
1832
2046
  return {};
1833
2047
  }
1834
2048
  const document = {};
@@ -1841,7 +2055,7 @@ function normalizeDocument(value) {
1841
2055
  return document;
1842
2056
  }
1843
2057
  function normalizeScopeValues(value) {
1844
- if (!isRecord(value)) {
2058
+ if (!isRecord2(value)) {
1845
2059
  return {};
1846
2060
  }
1847
2061
  const normalized = {};
@@ -1873,12 +2087,154 @@ function createInvalidBackupPath(filePath) {
1873
2087
  const baseName = path7.basename(filePath);
1874
2088
  return path7.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
1875
2089
  }
1876
- function isRecord(value) {
2090
+ function isRecord2(value) {
1877
2091
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1878
2092
  }
2093
+ function resolveConfigPath(homeDir) {
2094
+ return path7.join(homeDir, ".poe-code", "config.json");
2095
+ }
2096
+ function resolveProjectConfigPath(cwd) {
2097
+ return path7.join(cwd, ".poe-code", "config.json");
2098
+ }
1879
2099
  var EMPTY_DOCUMENT = `${JSON.stringify({}, null, 2)}
1880
2100
  `;
1881
2101
 
2102
+ // packages/poe-code-config/src/resolve.ts
2103
+ function resolveScope(schema, fileValues, env = {}) {
2104
+ const resolved = {};
2105
+ for (const key of Object.keys(schema)) {
2106
+ const field = schema[key];
2107
+ const envValue = resolveEnvValue(field, env, key);
2108
+ const fileValue = resolveFileValue(field, fileValues?.[key], key);
2109
+ resolved[key] = envValue ?? fileValue ?? field.default;
2110
+ }
2111
+ return resolved;
2112
+ }
2113
+ function resolveEnvValue(field, env, key) {
2114
+ if (!field.env) {
2115
+ return void 0;
2116
+ }
2117
+ const raw = env[field.env];
2118
+ if (raw === void 0) {
2119
+ return void 0;
2120
+ }
2121
+ return coerceValue(field, raw, key);
2122
+ }
2123
+ function resolveFileValue(field, value, key) {
2124
+ return coerceValue(field, value, key);
2125
+ }
2126
+ function coerceValue(field, value, key) {
2127
+ switch (field.type) {
2128
+ case "string":
2129
+ return typeof value === "string" ? value : void 0;
2130
+ case "number":
2131
+ return coerceNumber(value);
2132
+ case "boolean":
2133
+ return coerceBoolean(value);
2134
+ case "json":
2135
+ return coerceJson(field, value, key);
2136
+ }
2137
+ }
2138
+ function coerceNumber(value) {
2139
+ if (typeof value === "number" && Number.isFinite(value)) {
2140
+ return value;
2141
+ }
2142
+ if (typeof value !== "string" || value.length === 0) {
2143
+ return void 0;
2144
+ }
2145
+ const parsed = Number(value);
2146
+ return Number.isNaN(parsed) ? void 0 : parsed;
2147
+ }
2148
+ function coerceBoolean(value) {
2149
+ if (typeof value === "boolean") {
2150
+ return value;
2151
+ }
2152
+ if (value === "true" || value === "1") {
2153
+ return true;
2154
+ }
2155
+ if (value === "false" || value === "0") {
2156
+ return false;
2157
+ }
2158
+ return void 0;
2159
+ }
2160
+ function coerceJson(field, value, key) {
2161
+ if (value === void 0) {
2162
+ return void 0;
2163
+ }
2164
+ const parsedValue = parseJsonValue(value, key);
2165
+ try {
2166
+ return field.parse(parsedValue);
2167
+ } catch (error2) {
2168
+ const message2 = error2 instanceof Error ? error2.message : "Invalid JSON value.";
2169
+ throw new Error(`Invalid config value for "${key}": ${message2}`);
2170
+ }
2171
+ }
2172
+ function parseJsonValue(value, key) {
2173
+ if (typeof value !== "string") {
2174
+ return value;
2175
+ }
2176
+ try {
2177
+ return JSON.parse(value);
2178
+ } catch {
2179
+ throw new Error(`Invalid config value for "${key}": expected valid JSON.`);
2180
+ }
2181
+ }
2182
+
2183
+ // packages/poe-code-config/src/merge.ts
2184
+ function deepMergeDocuments(base, override) {
2185
+ const merged = {};
2186
+ const scopes = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(override)]);
2187
+ for (const scope of scopes) {
2188
+ const baseScope = base[scope] ?? {};
2189
+ const overrideScope = override[scope] ?? {};
2190
+ const nextScope = mergeScope(scope, baseScope, overrideScope);
2191
+ if (Object.keys(nextScope).length > 0) {
2192
+ merged[scope] = nextScope;
2193
+ }
2194
+ }
2195
+ return merged;
2196
+ }
2197
+ function mergeScope(scope, baseScope, overrideScope) {
2198
+ if (scope === "runtime") {
2199
+ return mergeRuntimeScope(baseScope, overrideScope);
2200
+ }
2201
+ const scopeEntries = Object.entries(overrideScope).filter(([, value]) => value !== void 0);
2202
+ return {
2203
+ ...baseScope,
2204
+ ...Object.fromEntries(scopeEntries)
2205
+ };
2206
+ }
2207
+ function mergeRuntimeScope(baseScope, overrideScope, path42 = []) {
2208
+ const merged = {};
2209
+ const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
2210
+ for (const key of keys) {
2211
+ const baseValue = baseScope[key];
2212
+ const overrideValue = overrideScope[key];
2213
+ if (overrideValue === void 0) {
2214
+ if (baseValue !== void 0) {
2215
+ merged[key] = baseValue;
2216
+ }
2217
+ continue;
2218
+ }
2219
+ if (isRuntimeConcatenativeArray([...path42, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
2220
+ merged[key] = [...baseValue, ...overrideValue];
2221
+ continue;
2222
+ }
2223
+ if (isRecord3(baseValue) && isRecord3(overrideValue)) {
2224
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path42, key]);
2225
+ continue;
2226
+ }
2227
+ merged[key] = overrideValue;
2228
+ }
2229
+ return merged;
2230
+ }
2231
+ function isRuntimeConcatenativeArray(path42) {
2232
+ return path42.join(".") === "mounts" || path42.join(".") === "runner.workspace.exclude";
2233
+ }
2234
+ function isRecord3(value) {
2235
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2236
+ }
2237
+
1882
2238
  // packages/poe-code-config/src/memory.ts
1883
2239
  async function configuredMemoryRoot(options) {
1884
2240
  return (await resolveMemoryConfig(options)).root;
@@ -1938,93 +2294,545 @@ import path9 from "node:path";
1938
2294
  // packages/file-lock/src/lock.ts
1939
2295
  import * as fsPromises from "node:fs/promises";
1940
2296
  import * as os from "node:os";
1941
-
1942
- // packages/poe-code-config/src/state/fs.ts
1943
- import * as nodeFs from "node:fs/promises";
1944
-
1945
- // packages/poe-code-config/src/state/templates.ts
1946
- import path10 from "node:path";
1947
-
1948
- // packages/memory/src/resolve-root.ts
1949
- var MEMORY_ROOT_ENV_VAR = "POE_CODE_MEMORY_ROOT";
1950
- async function resolveConfiguredMemoryRoot(options) {
1951
- const envOverride = options.env[MEMORY_ROOT_ENV_VAR]?.trim();
1952
- if (envOverride && envOverride.length > 0) {
1953
- return resolveAgainstCwd(options.cwd, envOverride);
2297
+ var LockTimeoutError = class extends Error {
2298
+ constructor(message2) {
2299
+ super(message2);
2300
+ this.name = "LockTimeoutError";
1954
2301
  }
1955
- const configOverride = (await configuredMemoryRoot({
1956
- fs: options.fs,
1957
- filePath: options.configPath,
1958
- projectFilePath: options.projectConfigPath
1959
- }))?.trim();
1960
- if (configOverride && configOverride.length > 0) {
1961
- return resolveAgainstCwd(options.cwd, configOverride);
2302
+ };
2303
+ function createAbortError() {
2304
+ const error2 = new Error("The operation was aborted.");
2305
+ error2.name = "AbortError";
2306
+ return error2;
2307
+ }
2308
+ function throwIfAborted(signal) {
2309
+ if (signal?.aborted) {
2310
+ throw createAbortError();
1962
2311
  }
1963
- return resolveMemoryRoot(options.cwd);
1964
2312
  }
1965
- function resolveAgainstCwd(cwd, value) {
1966
- return path11.isAbsolute(value) ? value : path11.resolve(cwd, value);
2313
+ function sleep(ms, signal) {
2314
+ if (!signal) {
2315
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2316
+ }
2317
+ if (signal.aborted) {
2318
+ return Promise.reject(createAbortError());
2319
+ }
2320
+ return new Promise((resolve2, reject) => {
2321
+ const timeoutId = setTimeout(() => {
2322
+ signal.removeEventListener("abort", onAbort);
2323
+ resolve2();
2324
+ }, ms);
2325
+ const onAbort = () => {
2326
+ clearTimeout(timeoutId);
2327
+ signal.removeEventListener("abort", onAbort);
2328
+ reject(createAbortError());
2329
+ };
2330
+ signal.addEventListener("abort", onAbort, { once: true });
2331
+ });
1967
2332
  }
1968
-
1969
- // packages/memory/src/init.ts
1970
- import * as fs from "node:fs/promises";
1971
- import path12 from "node:path";
1972
- async function initMemory(root) {
1973
- await fs.mkdir(path12.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
1974
- await writeFileIfMissing(path12.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
1975
- await writeFileIfMissing(path12.join(root, MEMORY_LOG_RELPATH), "");
2333
+ function backoff(attempt, minTimeout, maxTimeout) {
2334
+ const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
2335
+ return delay + Math.random() * delay * 0.1;
1976
2336
  }
1977
- async function writeFileIfMissing(filePath, content) {
2337
+ function hasErrorCode(error2, code) {
2338
+ return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
2339
+ }
2340
+ function hasAnyErrorCode(error2, codes) {
2341
+ return codes.some((code) => hasErrorCode(error2, code));
2342
+ }
2343
+ function isPidRunning(pid) {
1978
2344
  try {
1979
- await fs.writeFile(filePath, content, { encoding: "utf8", flag: "wx" });
2345
+ process.kill(pid, 0);
2346
+ return true;
1980
2347
  } catch (error2) {
1981
- if (!hasErrorCode(error2, "EEXIST")) {
1982
- throw error2;
1983
- }
2348
+ return !hasErrorCode(error2, "ESRCH");
1984
2349
  }
1985
2350
  }
1986
- function hasErrorCode(error2, code) {
1987
- return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
1988
- }
1989
-
1990
- // packages/memory/src/pages.ts
1991
- import * as fs2 from "node:fs/promises";
1992
- import path13 from "node:path";
1993
-
1994
- // packages/memory/src/frontmatter.ts
1995
- import { parse as parse5, stringify } from "yaml";
1996
- function parseFrontmatter(markdown) {
1997
- const content = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
1998
- const openingLineBreak = readOpeningLineBreak(content);
1999
- if (openingLineBreak === void 0) {
2000
- return {
2001
- frontmatter: {},
2002
- body: markdown
2003
- };
2004
- }
2005
- const frontmatterStart = 3 + openingLineBreak.length;
2006
- const closingFenceIndex = findClosingFence(content, frontmatterStart);
2007
- const yamlBlock = content.slice(frontmatterStart, closingFenceIndex);
2008
- const bodyStart = closingFenceIndex + 4;
2351
+ function createDefaultFs() {
2009
2352
  return {
2010
- frontmatter: parsePageFrontmatter(parseYamlFrontmatter(yamlBlock)),
2011
- body: readBody(content, bodyStart)
2353
+ open: (path42, flags) => fsPromises.open(path42, flags),
2354
+ readFile: (path42, encoding) => fsPromises.readFile(path42, encoding),
2355
+ stat: fsPromises.stat,
2356
+ unlink: fsPromises.unlink
2012
2357
  };
2013
2358
  }
2014
- function serializeFrontmatter(frontmatter, body) {
2015
- const serialized = {
2016
- ...frontmatter.name === void 0 ? {} : { name: frontmatter.name },
2017
- ...frontmatter.description === void 0 ? {} : { description: frontmatter.description },
2018
- ...frontmatter.lastTouchedAt === void 0 ? {} : { last_touched_at: frontmatter.lastTouchedAt },
2019
- ...frontmatter.sources === void 0 || frontmatter.sources.length === 0 ? {} : { sources: frontmatter.sources.map((source) => serializeSourceRef(source)) }
2020
- };
2021
- if (Object.keys(serialized).length === 0) {
2022
- return body;
2023
- }
2024
- return `---
2025
- ${stringify(serialized).trimEnd()}
2026
- ---
2027
- ${body}`;
2359
+ async function removeLockFile(fs14, lockPath, signal) {
2360
+ for (let attempt = 0; attempt <= 4; attempt += 1) {
2361
+ throwIfAborted(signal);
2362
+ try {
2363
+ await fs14.unlink(lockPath);
2364
+ return;
2365
+ } catch (error2) {
2366
+ if (hasErrorCode(error2, "ENOENT")) {
2367
+ return;
2368
+ }
2369
+ if (!hasAnyErrorCode(error2, ["EPERM", "EBUSY"]) || attempt === 4) {
2370
+ throw error2;
2371
+ }
2372
+ }
2373
+ await sleep(25 * 2 ** attempt, signal);
2374
+ }
2375
+ }
2376
+ function parseLockMetadata(content) {
2377
+ try {
2378
+ const parsed = JSON.parse(content);
2379
+ if (!parsed || typeof parsed !== "object" || !("host" in parsed) || !("pid" in parsed)) {
2380
+ return void 0;
2381
+ }
2382
+ const { host, pid } = parsed;
2383
+ if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
2384
+ return {
2385
+ host,
2386
+ pid
2387
+ };
2388
+ }
2389
+ } catch (ignoredError) {
2390
+ void ignoredError;
2391
+ }
2392
+ return void 0;
2393
+ }
2394
+ async function readLockMetadata(fs14, lockPath) {
2395
+ if (!fs14.readFile) {
2396
+ return void 0;
2397
+ }
2398
+ try {
2399
+ return parseLockMetadata(await fs14.readFile(lockPath, "utf8"));
2400
+ } catch (error2) {
2401
+ if (hasErrorCode(error2, "ENOENT")) {
2402
+ return null;
2403
+ }
2404
+ return void 0;
2405
+ }
2406
+ }
2407
+ async function shouldReclaimLock(options) {
2408
+ const metadata = await readLockMetadata(options.fs, options.lockPath);
2409
+ if (metadata === null) {
2410
+ return "missing";
2411
+ }
2412
+ if (metadata?.host === os.hostname()) {
2413
+ return !options.isPidRunning(metadata.pid);
2414
+ }
2415
+ return Date.now() - options.stat.mtimeMs > options.staleMs;
2416
+ }
2417
+ async function writeLockMetadata(handle) {
2418
+ try {
2419
+ await handle.writeFile(
2420
+ JSON.stringify({ pid: process.pid, host: os.hostname(), acquiredAt: (/* @__PURE__ */ new Date()).toISOString() }),
2421
+ { encoding: "utf8" }
2422
+ );
2423
+ } catch (ignoredError) {
2424
+ void ignoredError;
2425
+ }
2426
+ try {
2427
+ await handle.close();
2428
+ } catch (ignoredError) {
2429
+ void ignoredError;
2430
+ }
2431
+ }
2432
+ async function acquireFileLock(filePath, options = {}) {
2433
+ const fs14 = options.fs ?? createDefaultFs();
2434
+ const retries = options.retries ?? 20;
2435
+ const minTimeout = options.minTimeout ?? 25;
2436
+ const maxTimeout = options.maxTimeout ?? 250;
2437
+ const staleMs = options.staleMs ?? 1e3;
2438
+ const pidIsRunning = options.isPidRunning ?? isPidRunning;
2439
+ const lockPath = `${filePath}.lock`;
2440
+ let attempt = 0;
2441
+ while (attempt <= retries) {
2442
+ throwIfAborted(options.signal);
2443
+ try {
2444
+ const handle = await fs14.open(lockPath, "wx");
2445
+ await writeLockMetadata(handle);
2446
+ let released = false;
2447
+ return async () => {
2448
+ if (released) {
2449
+ return;
2450
+ }
2451
+ released = true;
2452
+ await removeLockFile(fs14, lockPath, options.signal);
2453
+ };
2454
+ } catch (error2) {
2455
+ if (!hasErrorCode(error2, "EEXIST")) {
2456
+ throw error2;
2457
+ }
2458
+ }
2459
+ let stat7;
2460
+ try {
2461
+ stat7 = await fs14.stat(lockPath);
2462
+ } catch (statError) {
2463
+ if (hasErrorCode(statError, "ENOENT")) {
2464
+ continue;
2465
+ }
2466
+ throw statError;
2467
+ }
2468
+ const reclaimLock = await shouldReclaimLock({
2469
+ fs: fs14,
2470
+ isPidRunning: pidIsRunning,
2471
+ lockPath,
2472
+ staleMs,
2473
+ stat: stat7
2474
+ });
2475
+ if (reclaimLock === "missing") {
2476
+ continue;
2477
+ }
2478
+ if (reclaimLock) {
2479
+ await removeLockFile(fs14, lockPath, options.signal);
2480
+ continue;
2481
+ }
2482
+ if (attempt >= retries) {
2483
+ break;
2484
+ }
2485
+ await sleep(backoff(attempt, minTimeout, maxTimeout), options.signal);
2486
+ attempt += 1;
2487
+ }
2488
+ throw new LockTimeoutError(`Failed to acquire lock on "${filePath}".`);
2489
+ }
2490
+
2491
+ // packages/poe-code-config/src/state/fs.ts
2492
+ import * as nodeFs from "node:fs/promises";
2493
+ var defaultStateFs = nodeFs;
2494
+ function isNotFoundError(error2) {
2495
+ return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
2496
+ }
2497
+
2498
+ // packages/poe-code-config/src/state/jobs.ts
2499
+ function createJobRegistry(homeDir, fs14 = defaultStateFs) {
2500
+ const jobsDir = path9.join(homeDir, ".poe-code", "state", "jobs");
2501
+ function jobPath(id) {
2502
+ assertSafeJobId(id);
2503
+ return path9.join(jobsDir, `${id}.json`);
2504
+ }
2505
+ async function get(id) {
2506
+ try {
2507
+ return parseJobEntry(await fs14.readFile(jobPath(id), "utf8"));
2508
+ } catch (error2) {
2509
+ if (isNotFoundError(error2)) {
2510
+ return null;
2511
+ }
2512
+ throw error2;
2513
+ }
2514
+ }
2515
+ async function put(entry) {
2516
+ assertJobEntry(entry);
2517
+ const filePath = jobPath(entry.id);
2518
+ await fs14.mkdir(jobsDir, { recursive: true });
2519
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2520
+ try {
2521
+ await writeJobAtomically(filePath, entry);
2522
+ } finally {
2523
+ await release();
2524
+ }
2525
+ }
2526
+ async function update(id, patch) {
2527
+ const filePath = jobPath(id);
2528
+ await fs14.mkdir(jobsDir, { recursive: true });
2529
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2530
+ try {
2531
+ const current = await get(id);
2532
+ if (current === null) {
2533
+ return null;
2534
+ }
2535
+ const updated = {
2536
+ ...current,
2537
+ ...patch,
2538
+ id: current.id
2539
+ };
2540
+ assertJobEntry(updated);
2541
+ await writeJobAtomically(filePath, updated);
2542
+ return updated;
2543
+ } finally {
2544
+ await release();
2545
+ }
2546
+ }
2547
+ async function list(filter = {}) {
2548
+ let entries;
2549
+ try {
2550
+ entries = await fs14.readdir(jobsDir);
2551
+ } catch (error2) {
2552
+ if (isNotFoundError(error2)) {
2553
+ return [];
2554
+ }
2555
+ throw error2;
2556
+ }
2557
+ const jobs = [];
2558
+ for (const entry of entries.sort()) {
2559
+ if (!entry.endsWith(".json")) {
2560
+ continue;
2561
+ }
2562
+ const filePath = path9.join(jobsDir, entry);
2563
+ const stat7 = await fs14.stat(filePath);
2564
+ if (!stat7.isFile()) {
2565
+ continue;
2566
+ }
2567
+ const job = parseJobEntry(await fs14.readFile(filePath, "utf8"));
2568
+ if (matchesFilter(job, filter)) {
2569
+ jobs.push(job);
2570
+ }
2571
+ }
2572
+ return jobs;
2573
+ }
2574
+ async function remove2(id) {
2575
+ const filePath = jobPath(id);
2576
+ try {
2577
+ await fs14.stat(jobsDir);
2578
+ } catch (error2) {
2579
+ if (isNotFoundError(error2)) {
2580
+ return;
2581
+ }
2582
+ throw error2;
2583
+ }
2584
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2585
+ try {
2586
+ await fs14.unlink(filePath);
2587
+ } catch (error2) {
2588
+ if (!isNotFoundError(error2)) {
2589
+ throw error2;
2590
+ }
2591
+ } finally {
2592
+ await release();
2593
+ }
2594
+ }
2595
+ async function writeJobAtomically(filePath, entry) {
2596
+ await fs14.mkdir(path9.dirname(filePath), { recursive: true });
2597
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
2598
+ try {
2599
+ await fs14.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
2600
+ `, {
2601
+ encoding: "utf8"
2602
+ });
2603
+ await fs14.rename(tempPath, filePath);
2604
+ } catch (error2) {
2605
+ await removeTempFile(tempPath);
2606
+ throw error2;
2607
+ }
2608
+ }
2609
+ async function removeTempFile(tempPath) {
2610
+ try {
2611
+ await fs14.unlink(tempPath);
2612
+ } catch (error2) {
2613
+ if (!isNotFoundError(error2)) {
2614
+ throw error2;
2615
+ }
2616
+ }
2617
+ }
2618
+ return {
2619
+ get,
2620
+ put,
2621
+ update,
2622
+ list,
2623
+ remove: remove2
2624
+ };
2625
+ }
2626
+ function assertSafeJobId(id) {
2627
+ if (id.length === 0 || id === "." || id === ".." || path9.isAbsolute(id) || id.includes("/") || id.includes("\\") || id.includes("\0")) {
2628
+ throw new Error("Invalid job id.");
2629
+ }
2630
+ }
2631
+ function assertJobEntry(entry) {
2632
+ if (!isJobEntry(entry)) {
2633
+ throw new Error("Invalid job entry.");
2634
+ }
2635
+ }
2636
+ function matchesFilter(job, filter) {
2637
+ return (filter.env_id === void 0 || job.env_id === filter.env_id) && (filter.env_kind === void 0 || job.env_kind === filter.env_kind) && (filter.tool === void 0 || job.tool === filter.tool) && (filter.status === void 0 || job.status === filter.status);
2638
+ }
2639
+ function parseJobEntry(content) {
2640
+ const parsed = JSON.parse(content);
2641
+ if (!isJobEntry(parsed)) {
2642
+ throw new Error("Invalid job state file.");
2643
+ }
2644
+ return parsed;
2645
+ }
2646
+ function isJobEntry(value) {
2647
+ return isRecord4(value) && typeof value.id === "string" && typeof value.env_id === "string" && typeof value.env_kind === "string" && typeof value.tool === "string" && Array.isArray(value.argv) && value.argv.every((arg) => typeof arg === "string") && typeof value.cwd === "string" && typeof value.started_at === "string" && isJobStatus(value.status) && (value.exit_code === void 0 || typeof value.exit_code === "number") && (value.exited_at === void 0 || typeof value.exited_at === "string") && (value.log_file === void 0 || typeof value.log_file === "string");
2648
+ }
2649
+ function isJobStatus(value) {
2650
+ return value === "pending" || value === "running" || value === "exited" || value === "killed" || value === "lost";
2651
+ }
2652
+ function isRecord4(value) {
2653
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2654
+ }
2655
+
2656
+ // packages/poe-code-config/src/state/templates.ts
2657
+ import path10 from "node:path";
2658
+ function createTemplateRegistry(homeDir, fs14 = defaultStateFs) {
2659
+ const filePath = path10.join(homeDir, ".poe-code", "state", "templates.json");
2660
+ async function readState() {
2661
+ try {
2662
+ const raw = await fs14.readFile(filePath, "utf8");
2663
+ return normalizeTemplateState(JSON.parse(raw));
2664
+ } catch (error2) {
2665
+ if (isNotFoundError(error2)) {
2666
+ return createEmptyState();
2667
+ }
2668
+ throw error2;
2669
+ }
2670
+ }
2671
+ async function writeState(state) {
2672
+ await fs14.writeFile(filePath, `${JSON.stringify(state, null, 2)}
2673
+ `, {
2674
+ encoding: "utf8"
2675
+ });
2676
+ }
2677
+ async function updateState(mutator) {
2678
+ await fs14.mkdir(path10.dirname(filePath), { recursive: true });
2679
+ const release = await acquireFileLock(filePath, { fs: fs14 });
2680
+ try {
2681
+ const state = await readState();
2682
+ mutator(state);
2683
+ await writeState(state);
2684
+ } finally {
2685
+ await release();
2686
+ }
2687
+ }
2688
+ async function get(backend, hash) {
2689
+ const state = await readState();
2690
+ return state[backend][hash] ?? null;
2691
+ }
2692
+ async function put(backend, entry) {
2693
+ await updateState((state) => {
2694
+ state[backend][entry.hash] = entry;
2695
+ });
2696
+ }
2697
+ async function remove2(backend, hash) {
2698
+ await updateState((state) => {
2699
+ delete state[backend][hash];
2700
+ });
2701
+ }
2702
+ async function list(backend) {
2703
+ const state = await readState();
2704
+ const entries = backend === void 0 ? [...Object.values(state.docker), ...Object.values(state.e2b)] : Object.values(state[backend]);
2705
+ return entries.sort((left, right) => left.hash.localeCompare(right.hash));
2706
+ }
2707
+ return {
2708
+ get,
2709
+ put,
2710
+ remove: remove2,
2711
+ list
2712
+ };
2713
+ }
2714
+ function createEmptyState() {
2715
+ return {
2716
+ docker: {},
2717
+ e2b: {}
2718
+ };
2719
+ }
2720
+ function normalizeTemplateState(value) {
2721
+ if (!isRecord5(value)) {
2722
+ return createEmptyState();
2723
+ }
2724
+ return {
2725
+ docker: normalizeTemplateEntries(value.docker),
2726
+ e2b: normalizeTemplateEntries(value.e2b)
2727
+ };
2728
+ }
2729
+ function normalizeTemplateEntries(value) {
2730
+ if (!isRecord5(value)) {
2731
+ return {};
2732
+ }
2733
+ const entries = {};
2734
+ for (const [hash, entry] of Object.entries(value)) {
2735
+ if (isTemplateEntry(entry) && entry.hash === hash) {
2736
+ entries[hash] = entry;
2737
+ }
2738
+ }
2739
+ return entries;
2740
+ }
2741
+ function isTemplateEntry(value) {
2742
+ return isRecord5(value) && typeof value.hash === "string" && typeof value.runtime_type === "string" && typeof value.dockerfile_path === "string" && typeof value.built_at === "string" && (value.template_id === void 0 || typeof value.template_id === "string") && (value.image === void 0 || typeof value.image === "string");
2743
+ }
2744
+ function isRecord5(value) {
2745
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2746
+ }
2747
+
2748
+ // packages/poe-code-config/src/state/index.ts
2749
+ function createStateManager(homeDir, fs14) {
2750
+ return {
2751
+ templates: createTemplateRegistry(homeDir, fs14),
2752
+ jobs: createJobRegistry(homeDir, fs14)
2753
+ };
2754
+ }
2755
+
2756
+ // packages/memory/src/resolve-root.ts
2757
+ var MEMORY_ROOT_ENV_VAR = "POE_CODE_MEMORY_ROOT";
2758
+ async function resolveConfiguredMemoryRoot(options) {
2759
+ const envOverride = options.env[MEMORY_ROOT_ENV_VAR]?.trim();
2760
+ if (envOverride && envOverride.length > 0) {
2761
+ return resolveAgainstCwd(options.cwd, envOverride);
2762
+ }
2763
+ const configOverride = (await configuredMemoryRoot({
2764
+ fs: options.fs,
2765
+ filePath: options.configPath,
2766
+ projectFilePath: options.projectConfigPath
2767
+ }))?.trim();
2768
+ if (configOverride && configOverride.length > 0) {
2769
+ return resolveAgainstCwd(options.cwd, configOverride);
2770
+ }
2771
+ return resolveMemoryRoot(options.cwd);
2772
+ }
2773
+ function resolveAgainstCwd(cwd, value) {
2774
+ return path11.isAbsolute(value) ? value : path11.resolve(cwd, value);
2775
+ }
2776
+
2777
+ // packages/memory/src/init.ts
2778
+ import * as fs from "node:fs/promises";
2779
+ import path12 from "node:path";
2780
+ async function initMemory(root) {
2781
+ await fs.mkdir(path12.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
2782
+ await writeFileIfMissing(path12.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
2783
+ await writeFileIfMissing(path12.join(root, MEMORY_LOG_RELPATH), "");
2784
+ }
2785
+ async function writeFileIfMissing(filePath, content) {
2786
+ try {
2787
+ await fs.writeFile(filePath, content, { encoding: "utf8", flag: "wx" });
2788
+ } catch (error2) {
2789
+ if (!hasErrorCode2(error2, "EEXIST")) {
2790
+ throw error2;
2791
+ }
2792
+ }
2793
+ }
2794
+ function hasErrorCode2(error2, code) {
2795
+ return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
2796
+ }
2797
+
2798
+ // packages/memory/src/pages.ts
2799
+ import * as fs2 from "node:fs/promises";
2800
+ import path13 from "node:path";
2801
+
2802
+ // packages/memory/src/frontmatter.ts
2803
+ import { parse as parse5, stringify } from "yaml";
2804
+ function parseFrontmatter(markdown) {
2805
+ const content = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
2806
+ const openingLineBreak = readOpeningLineBreak(content);
2807
+ if (openingLineBreak === void 0) {
2808
+ return {
2809
+ frontmatter: {},
2810
+ body: markdown
2811
+ };
2812
+ }
2813
+ const frontmatterStart = 3 + openingLineBreak.length;
2814
+ const closingFenceIndex = findClosingFence(content, frontmatterStart);
2815
+ const yamlBlock = content.slice(frontmatterStart, closingFenceIndex);
2816
+ const bodyStart = closingFenceIndex + 4;
2817
+ return {
2818
+ frontmatter: parsePageFrontmatter(parseYamlFrontmatter(yamlBlock)),
2819
+ body: readBody(content, bodyStart)
2820
+ };
2821
+ }
2822
+ function serializeFrontmatter(frontmatter, body) {
2823
+ const serialized = {
2824
+ ...frontmatter.name === void 0 ? {} : { name: frontmatter.name },
2825
+ ...frontmatter.description === void 0 ? {} : { description: frontmatter.description },
2826
+ ...frontmatter.lastTouchedAt === void 0 ? {} : { last_touched_at: frontmatter.lastTouchedAt },
2827
+ ...frontmatter.sources === void 0 || frontmatter.sources.length === 0 ? {} : { sources: frontmatter.sources.map((source) => serializeSourceRef(source)) }
2828
+ };
2829
+ if (Object.keys(serialized).length === 0) {
2830
+ return body;
2831
+ }
2832
+ return `---
2833
+ ${stringify(serialized).trimEnd()}
2834
+ ---
2835
+ ${body}`;
2028
2836
  }
2029
2837
  function parseSourceRef(serialized) {
2030
2838
  const [rawPath, rawAnchor] = serialized.split("#", 2);
@@ -2135,7 +2943,7 @@ function parseYamlFrontmatter(yamlBlock) {
2135
2943
  if (parsed === null) {
2136
2944
  return {};
2137
2945
  }
2138
- if (!isRecord2(parsed)) {
2946
+ if (!isRecord6(parsed)) {
2139
2947
  throw new Error("YAML frontmatter must parse to an object.");
2140
2948
  }
2141
2949
  return parsed;
@@ -2166,13 +2974,13 @@ function parseSources(value) {
2166
2974
  if (typeof item === "string") {
2167
2975
  return parseSourceRef(item);
2168
2976
  }
2169
- if (!isRecord2(item)) {
2977
+ if (!isRecord6(item)) {
2170
2978
  throw new Error('Invalid "sources" frontmatter. Expected each source to be a string or object.');
2171
2979
  }
2172
- const path33 = readRequiredString(item.path, "sources[].path");
2980
+ const path42 = readRequiredString(item.path, "sources[].path");
2173
2981
  const startLine = readOptionalPositiveInteger(item.startLine, "sources[].startLine");
2174
2982
  const endLine = readOptionalPositiveInteger(item.endLine, "sources[].endLine");
2175
- return parseSourceRef(serializeSourceRef({ path: path33, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
2983
+ return parseSourceRef(serializeSourceRef({ path: path42, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
2176
2984
  });
2177
2985
  }
2178
2986
  function readOptionalString(value, field) {
@@ -2205,7 +3013,7 @@ function assertValidLineNumber(line, value) {
2205
3013
  throw new Error(`Invalid source ref "${value}": line numbers must be positive integers.`);
2206
3014
  }
2207
3015
  }
2208
- function isRecord2(value) {
3016
+ function isRecord6(value) {
2209
3017
  return typeof value === "object" && value !== null && !Array.isArray(value);
2210
3018
  }
2211
3019
 
@@ -2377,17 +3185,17 @@ import path18 from "node:path";
2377
3185
  // packages/memory/src/lock.ts
2378
3186
  import * as fsPromises2 from "node:fs/promises";
2379
3187
  import path16 from "node:path";
2380
- function createDefaultFs() {
3188
+ function createDefaultFs2() {
2381
3189
  return {
2382
3190
  readFile: (filePath, encoding) => fsPromises2.readFile(filePath, encoding),
2383
3191
  unlink: fsPromises2.unlink,
2384
3192
  writeFile: (filePath, data, options) => fsPromises2.writeFile(filePath, data, options)
2385
3193
  };
2386
3194
  }
2387
- function sleep(ms) {
3195
+ function sleep2(ms) {
2388
3196
  return new Promise((resolve2) => setTimeout(resolve2, ms));
2389
3197
  }
2390
- function hasErrorCode2(error2, code) {
3198
+ function hasErrorCode3(error2, code) {
2391
3199
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
2392
3200
  }
2393
3201
  function lockDelay(attempt, minTimeoutMs, maxTimeoutMs) {
@@ -2406,19 +3214,19 @@ function parsePid(input) {
2406
3214
  const pid = Number.parseInt(value, 10);
2407
3215
  return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
2408
3216
  }
2409
- function isPidRunning(pid) {
3217
+ function isPidRunning2(pid) {
2410
3218
  try {
2411
3219
  process.kill(pid, 0);
2412
3220
  return true;
2413
3221
  } catch (error2) {
2414
- return !hasErrorCode2(error2, "ESRCH");
3222
+ return !hasErrorCode3(error2, "ESRCH");
2415
3223
  }
2416
3224
  }
2417
- async function removeLockFile(fs14, lockPath) {
3225
+ async function removeLockFile2(fs14, lockPath) {
2418
3226
  try {
2419
3227
  await fs14.unlink(lockPath);
2420
3228
  } catch (error2) {
2421
- if (!hasErrorCode2(error2, "ENOENT")) {
3229
+ if (!hasErrorCode3(error2, "ENOENT")) {
2422
3230
  throw error2;
2423
3231
  }
2424
3232
  }
@@ -2427,20 +3235,20 @@ async function readLockPid(fs14, lockPath) {
2427
3235
  try {
2428
3236
  return parsePid(await fs14.readFile(lockPath, "utf8"));
2429
3237
  } catch (error2) {
2430
- if (hasErrorCode2(error2, "ENOENT")) {
3238
+ if (hasErrorCode3(error2, "ENOENT")) {
2431
3239
  return null;
2432
3240
  }
2433
3241
  throw error2;
2434
3242
  }
2435
3243
  }
2436
3244
  async function withLock(root, run, options = {}) {
2437
- const fs14 = options.fs ?? createDefaultFs();
3245
+ const fs14 = options.fs ?? createDefaultFs2();
2438
3246
  const lockPath = path16.join(root, MEMORY_LOCK_RELPATH);
2439
3247
  const pid = options.pid ?? process.pid;
2440
3248
  const retries = options.retries ?? 20;
2441
3249
  const minTimeoutMs = options.minTimeoutMs ?? 25;
2442
3250
  const maxTimeoutMs = options.maxTimeoutMs ?? 250;
2443
- const pidIsRunning = options.isPidRunning ?? isPidRunning;
3251
+ const pidIsRunning = options.isPidRunning ?? isPidRunning2;
2444
3252
  for (let attempt = 0; attempt <= retries; attempt += 1) {
2445
3253
  try {
2446
3254
  await fs14.writeFile(lockPath, `${pid}
@@ -2448,10 +3256,10 @@ async function withLock(root, run, options = {}) {
2448
3256
  try {
2449
3257
  return await run();
2450
3258
  } finally {
2451
- await removeLockFile(fs14, lockPath);
3259
+ await removeLockFile2(fs14, lockPath);
2452
3260
  }
2453
3261
  } catch (error2) {
2454
- if (!hasErrorCode2(error2, "EEXIST")) {
3262
+ if (!hasErrorCode3(error2, "EEXIST")) {
2455
3263
  throw error2;
2456
3264
  }
2457
3265
  }
@@ -2460,11 +3268,11 @@ async function withLock(root, run, options = {}) {
2460
3268
  continue;
2461
3269
  }
2462
3270
  if (existingPid === void 0 || !pidIsRunning(existingPid)) {
2463
- await removeLockFile(fs14, lockPath);
3271
+ await removeLockFile2(fs14, lockPath);
2464
3272
  continue;
2465
3273
  }
2466
3274
  if (attempt < retries) {
2467
- await sleep(lockDelay(attempt, minTimeoutMs, maxTimeoutMs));
3275
+ await sleep2(lockDelay(attempt, minTimeoutMs, maxTimeoutMs));
2468
3276
  }
2469
3277
  }
2470
3278
  throw new Error(`Failed to acquire memory lock at "${lockPath}".`);
@@ -3225,162 +4033,2405 @@ function parseOlderThan(value) {
3225
4033
  }
3226
4034
  return duration;
3227
4035
  }
3228
-
3229
- // packages/memory/src/ingest.ts
3230
- import * as fs11 from "node:fs/promises";
3231
- import path26 from "node:path";
3232
-
3233
- // packages/agent-spawn/src/run-command.ts
3234
- import { spawn } from "node:child_process";
3235
-
3236
- // packages/agent-spawn/src/types.ts
3237
- function resolveModeConfig(modeConfig) {
3238
- if (Array.isArray(modeConfig)) {
3239
- return { args: modeConfig };
4036
+
4037
+ // packages/memory/src/ingest.ts
4038
+ import * as fs11 from "node:fs/promises";
4039
+ import path35 from "node:path";
4040
+
4041
+ // packages/agent-spawn/src/register-factories.ts
4042
+ import { spawn as spawnChildProcess2 } from "node:child_process";
4043
+
4044
+ // packages/agent-harness-tools/src/paths.ts
4045
+ import path22 from "node:path";
4046
+
4047
+ // packages/agent-defs/src/agents/claude-code.ts
4048
+ var claudeCodeAgent = {
4049
+ id: "claude-code",
4050
+ name: "claude-code",
4051
+ label: "Claude Code",
4052
+ summary: "Configure Claude Code to route through Poe.",
4053
+ aliases: ["claude"],
4054
+ binaryName: "claude",
4055
+ configPath: "~/.claude/settings.json",
4056
+ branding: {
4057
+ colors: {
4058
+ dark: "#C15F3C",
4059
+ light: "#C15F3C"
4060
+ }
4061
+ }
4062
+ };
4063
+
4064
+ // packages/agent-defs/src/agents/claude-desktop.ts
4065
+ var claudeDesktopAgent = {
4066
+ id: "claude-desktop",
4067
+ name: "claude-desktop",
4068
+ label: "Claude Desktop",
4069
+ summary: "Anthropic's official desktop application for Claude",
4070
+ configPath: "~/.claude/settings.json",
4071
+ branding: {
4072
+ colors: {
4073
+ dark: "#D97757",
4074
+ light: "#D97757"
4075
+ }
4076
+ }
4077
+ };
4078
+
4079
+ // packages/agent-defs/src/agents/codex.ts
4080
+ var codexAgent = {
4081
+ id: "codex",
4082
+ name: "codex",
4083
+ label: "Codex",
4084
+ summary: "Configure Codex to use Poe as the model provider.",
4085
+ binaryName: "codex",
4086
+ configPath: "~/.codex/config.toml",
4087
+ branding: {
4088
+ colors: {
4089
+ dark: "#D5D9DF",
4090
+ light: "#7A7F86"
4091
+ }
4092
+ }
4093
+ };
4094
+
4095
+ // packages/agent-defs/src/agents/opencode.ts
4096
+ var openCodeAgent = {
4097
+ id: "opencode",
4098
+ name: "opencode",
4099
+ label: "OpenCode CLI",
4100
+ summary: "Configure OpenCode CLI to use the Poe API.",
4101
+ binaryName: "opencode",
4102
+ configPath: "~/.config/opencode/config.json",
4103
+ branding: {
4104
+ colors: {
4105
+ dark: "#4A4F55",
4106
+ light: "#2F3338"
4107
+ }
4108
+ }
4109
+ };
4110
+
4111
+ // packages/agent-defs/src/agents/kimi.ts
4112
+ var kimiAgent = {
4113
+ id: "kimi",
4114
+ name: "kimi",
4115
+ label: "Kimi",
4116
+ summary: "Configure Kimi CLI to use Poe API",
4117
+ aliases: ["kimi-cli"],
4118
+ binaryName: "kimi",
4119
+ configPath: "~/.kimi/config.toml",
4120
+ branding: {
4121
+ colors: {
4122
+ dark: "#7B68EE",
4123
+ light: "#6A5ACD"
4124
+ }
4125
+ }
4126
+ };
4127
+
4128
+ // packages/agent-defs/src/agents/goose.ts
4129
+ var gooseAgent = {
4130
+ id: "goose",
4131
+ name: "goose",
4132
+ label: "Goose",
4133
+ summary: "Block's open-source AI agent with ACP support.",
4134
+ binaryName: "goose",
4135
+ configPath: "~/.config/goose/config.yaml",
4136
+ branding: {
4137
+ colors: {
4138
+ dark: "#FF6B35",
4139
+ light: "#E85D26"
4140
+ }
4141
+ }
4142
+ };
4143
+
4144
+ // packages/agent-defs/src/agents/poe-agent.ts
4145
+ var poeAgentAgent = {
4146
+ id: "poe-agent",
4147
+ name: "poe-agent",
4148
+ label: "Poe Agent",
4149
+ summary: "Run one-shot prompts with the built-in Poe agent runtime.",
4150
+ configPath: "~/.poe-code/config.json",
4151
+ branding: {
4152
+ colors: {
4153
+ dark: "#A465F7",
4154
+ light: "#7A3FD3"
4155
+ }
4156
+ }
4157
+ };
4158
+
4159
+ // packages/agent-defs/src/registry.ts
4160
+ var allAgents = [
4161
+ claudeCodeAgent,
4162
+ claudeDesktopAgent,
4163
+ codexAgent,
4164
+ openCodeAgent,
4165
+ kimiAgent,
4166
+ gooseAgent,
4167
+ poeAgentAgent
4168
+ ];
4169
+ var lookup = /* @__PURE__ */ new Map();
4170
+ for (const agent of allAgents) {
4171
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
4172
+ for (const value of values) {
4173
+ const normalized = value.toLowerCase();
4174
+ if (!lookup.has(normalized)) {
4175
+ lookup.set(normalized, agent.id);
4176
+ }
4177
+ }
4178
+ }
4179
+ function resolveAgentId(input) {
4180
+ if (!input) {
4181
+ return void 0;
4182
+ }
4183
+ return lookup.get(input.toLowerCase());
4184
+ }
4185
+
4186
+ // packages/agent-harness-tools/src/select-agent.ts
4187
+ var loopAgents = allAgents.filter(
4188
+ (agent) => agent.binaryName !== void 0 || agent.id === "poe-agent"
4189
+ );
4190
+ var supportedAgents = loopAgents.map((agent) => agent.id).join(", ");
4191
+
4192
+ // packages/agent-harness-tools/src/run-logs.ts
4193
+ import path23 from "node:path";
4194
+
4195
+ // packages/agent-harness-tools/src/log-stream.ts
4196
+ import nodeFs2 from "node:fs";
4197
+ var JOB_DIR = "/tmp/poe-jobs";
4198
+ var POLL_INTERVAL_MS = 250;
4199
+ function wrapForLogTee(argv, jobId) {
4200
+ if (argv.length === 0) {
4201
+ throw new Error("wrapForLogTee requires argv to contain at least one argument");
4202
+ }
4203
+ const command = argv.map(shellQuote).join(" ");
4204
+ const logFile = shellQuote(jobLogPath(jobId));
4205
+ const exitFile = shellQuote(jobExitPath(jobId));
4206
+ const exitTmpFile = shellQuote(`${jobExitPath(jobId)}.tmp`);
4207
+ const script = [
4208
+ `mkdir -p ${shellQuote(JOB_DIR)}`,
4209
+ `({ (${command}); echo $? > ${exitTmpFile}; } 2>&1 | tee ${logFile}; mv ${exitTmpFile} ${exitFile})`
4210
+ ].join(" && ");
4211
+ return ["sh", "-c", script];
4212
+ }
4213
+ async function* streamLogFile(env, jobId, opts) {
4214
+ const fs14 = env.fs ?? nodeFs2;
4215
+ const file = jobLogPath(jobId);
4216
+ let byteOffset = opts.sinceByte ?? 0;
4217
+ while (true) {
4218
+ if (opts.since !== void 0 && !await wasModifiedSince(fs14, file, opts.since)) {
4219
+ await waitForLogChange(fs14, file);
4220
+ continue;
4221
+ }
4222
+ const result = await readLogChunk(fs14, file, byteOffset);
4223
+ if (result !== null) {
4224
+ byteOffset = result.nextByteOffset;
4225
+ yield result.chunk;
4226
+ continue;
4227
+ }
4228
+ await waitForLogChange(fs14, file);
4229
+ }
4230
+ }
4231
+ async function wasModifiedSince(fs14, file, since) {
4232
+ if (fs14.promises.stat === void 0) {
4233
+ return true;
4234
+ }
4235
+ try {
4236
+ const stat7 = await fs14.promises.stat(file);
4237
+ return stat7.mtimeMs >= since.getTime();
4238
+ } catch (error2) {
4239
+ if (isNodeError(error2) && error2.code === "ENOENT") {
4240
+ return false;
4241
+ }
4242
+ throw error2;
4243
+ }
4244
+ }
4245
+ async function waitForExit(env, jobId, opts = {}) {
4246
+ const fs14 = env.fs ?? nodeFs2;
4247
+ const file = jobExitPath(jobId);
4248
+ while (true) {
4249
+ throwIfAborted2(opts.signal);
4250
+ const contents = await readTextFileIfExists(fs14, file);
4251
+ if (contents !== null) {
4252
+ const text4 = contents.trim();
4253
+ const exitCode = Number(text4);
4254
+ if (text4.length === 0 || !Number.isInteger(exitCode)) {
4255
+ throw new Error(`Invalid exit code in ${file}: ${contents}`);
4256
+ }
4257
+ return { exitCode };
4258
+ }
4259
+ await sleep3(POLL_INTERVAL_MS, opts.signal);
4260
+ }
4261
+ }
4262
+ function jobLogPath(jobId) {
4263
+ return `${JOB_DIR}/${jobId}.log`;
4264
+ }
4265
+ function jobExitPath(jobId) {
4266
+ return `${JOB_DIR}/${jobId}.exit`;
4267
+ }
4268
+ async function readLogChunk(fs14, file, byteOffset) {
4269
+ const contents = await readFileIfExists2(fs14, file);
4270
+ if (contents === null || byteOffset >= contents.byteLength) {
4271
+ return null;
4272
+ }
4273
+ return {
4274
+ chunk: {
4275
+ byteOffset,
4276
+ data: contents.subarray(byteOffset).toString("utf8")
4277
+ },
4278
+ nextByteOffset: contents.byteLength
4279
+ };
4280
+ }
4281
+ async function readTextFileIfExists(fs14, file) {
4282
+ const contents = await readFileIfExists2(fs14, file);
4283
+ return contents?.toString("utf8") ?? null;
4284
+ }
4285
+ async function readFileIfExists2(fs14, file) {
4286
+ try {
4287
+ const contents = await fs14.promises.readFile(file);
4288
+ return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
4289
+ } catch (error2) {
4290
+ if (isNodeError(error2) && error2.code === "ENOENT") {
4291
+ return null;
4292
+ }
4293
+ throw error2;
4294
+ }
4295
+ }
4296
+ async function waitForLogChange(fs14, file) {
4297
+ const watch = fs14.watch;
4298
+ if (typeof watch !== "function") {
4299
+ await sleep3(POLL_INTERVAL_MS);
4300
+ return;
4301
+ }
4302
+ await new Promise((resolve2) => {
4303
+ let watcher = null;
4304
+ const timer = setTimeout(done, POLL_INTERVAL_MS);
4305
+ function done() {
4306
+ clearTimeout(timer);
4307
+ watcher?.close();
4308
+ resolve2();
4309
+ }
4310
+ try {
4311
+ watcher = watch(file, done);
4312
+ } catch {
4313
+ done();
4314
+ }
4315
+ });
4316
+ }
4317
+ function sleep3(ms, signal) {
4318
+ return new Promise((resolve2, reject) => {
4319
+ let timer = null;
4320
+ const abort = () => {
4321
+ if (timer !== null) {
4322
+ clearTimeout(timer);
4323
+ }
4324
+ reject(new Error("waitForExit aborted."));
4325
+ };
4326
+ if (signal?.aborted) {
4327
+ abort();
4328
+ return;
4329
+ }
4330
+ timer = setTimeout(() => {
4331
+ signal?.removeEventListener("abort", abort);
4332
+ resolve2();
4333
+ }, ms);
4334
+ signal?.addEventListener("abort", abort, { once: true });
4335
+ });
4336
+ }
4337
+ function throwIfAborted2(signal) {
4338
+ if (signal?.aborted) {
4339
+ throw new Error("waitForExit aborted.");
4340
+ }
4341
+ }
4342
+ function shellQuote(value) {
4343
+ return `'${value.replaceAll("'", "'\\''")}'`;
4344
+ }
4345
+ function isNodeError(error2) {
4346
+ return error2 instanceof Error && "code" in error2;
4347
+ }
4348
+
4349
+ // packages/agent-harness-tools/src/run-poe-command.ts
4350
+ import { randomBytes } from "node:crypto";
4351
+
4352
+ // packages/agent-harness-tools/src/binary-exists.ts
4353
+ function createBinaryExistsDetectors(binaryName) {
4354
+ const commonPaths = [
4355
+ `/usr/local/bin/${binaryName}`,
4356
+ `/usr/bin/${binaryName}`,
4357
+ `$HOME/.local/bin/${binaryName}`,
4358
+ `$HOME/.claude/local/bin/${binaryName}`
4359
+ ];
4360
+ return [
4361
+ {
4362
+ command: "which",
4363
+ args: [binaryName],
4364
+ validate: (result) => result.exitCode === 0
4365
+ },
4366
+ {
4367
+ command: "where",
4368
+ args: [binaryName],
4369
+ validate: (result) => result.exitCode === 0 && result.stdout.trim().length > 0
4370
+ },
4371
+ {
4372
+ command: "sh",
4373
+ args: ["-c", commonPaths.map((p) => `test -f "${p}"`).join(" || ")],
4374
+ validate: (result) => result.exitCode === 0
4375
+ }
4376
+ ];
4377
+ }
4378
+
4379
+ // packages/agent-harness-tools/src/run-poe-command.ts
4380
+ var ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
4381
+ async function runPoeCommand(opts) {
4382
+ const jobId = createUlid();
4383
+ const execution = opts.openSpec.execution;
4384
+ const wrapCommand = execution?.wrapForLogTee !== false;
4385
+ const pendingJob = opts.state.jobs.put({
4386
+ id: jobId,
4387
+ env_id: "",
4388
+ env_kind: opts.factory.type,
4389
+ tool: opts.openSpec.jobLabel.tool,
4390
+ argv: opts.openSpec.jobLabel.argv,
4391
+ cwd: opts.openSpec.cwd,
4392
+ started_at: "",
4393
+ status: "pending"
4394
+ });
4395
+ const opened = opts.factory.open(opts.openSpec);
4396
+ const env = isPromiseLike(opened) ? await opened : opened;
4397
+ let shouldClose = true;
4398
+ try {
4399
+ const upload = env.uploadWorkspace();
4400
+ await Promise.all([pendingJob, upload]);
4401
+ await configureE2bSpawnAgentIfAvailable({
4402
+ env,
4403
+ openSpec: opts.openSpec,
4404
+ factoryType: opts.factory.type
4405
+ });
4406
+ const argv = wrapCommand ? wrapForLogTee(opts.openSpec.jobLabel.argv, jobId) : opts.openSpec.jobLabel.argv;
4407
+ const handle = execution?.tty ? env.shell() : env.exec({
4408
+ command: argv[0],
4409
+ args: argv.slice(1),
4410
+ cwd: opts.openSpec.cwd,
4411
+ env: resolveExecutionEnv(opts.openSpec),
4412
+ stdin: execution?.stdin ?? "inherit",
4413
+ stdout: execution?.stdout ?? "pipe",
4414
+ stderr: execution?.stderr ?? "pipe",
4415
+ signal: opts.signal
4416
+ });
4417
+ if (execution?.input !== void 0) {
4418
+ handle.stdin?.setDefaultEncoding("utf8");
4419
+ handle.stdin?.end(execution.input);
4420
+ }
4421
+ const runningJob = opts.state.jobs.update(jobId, {
4422
+ status: "running",
4423
+ env_id: env.id,
4424
+ started_at: (/* @__PURE__ */ new Date()).toISOString()
4425
+ });
4426
+ if (opts.detach) {
4427
+ await runningJob;
4428
+ setDetachedJobContext(env, {
4429
+ id: jobId,
4430
+ tool: opts.openSpec.jobLabel.tool,
4431
+ argv: opts.openSpec.jobLabel.argv
4432
+ });
4433
+ await env.detach();
4434
+ shouldClose = false;
4435
+ return { kind: "detached", jobId, envId: env.id };
4436
+ }
4437
+ const result = await runSync({
4438
+ env,
4439
+ handle,
4440
+ jobId,
4441
+ openSpec: opts.openSpec,
4442
+ signal: opts.signal,
4443
+ wrapCommand
4444
+ });
4445
+ await runningJob;
4446
+ shouldClose = false;
4447
+ await opts.state.jobs.update(jobId, {
4448
+ status: "exited",
4449
+ exit_code: result.exitCode,
4450
+ exited_at: (/* @__PURE__ */ new Date()).toISOString()
4451
+ });
4452
+ return {
4453
+ kind: "sync",
4454
+ exitCode: result.exitCode,
4455
+ download: result.download,
4456
+ ...result.stdout !== void 0 ? { stdout: result.stdout } : {},
4457
+ ...result.stderr !== void 0 ? { stderr: result.stderr } : {}
4458
+ };
4459
+ } finally {
4460
+ if (shouldClose) {
4461
+ await env.close();
4462
+ }
4463
+ }
4464
+ }
4465
+ async function configureE2bSpawnAgentIfAvailable(opts) {
4466
+ if (opts.factoryType !== "e2b") {
4467
+ return;
4468
+ }
4469
+ const agentId = resolveAgentId(opts.openSpec.jobLabel.tool);
4470
+ const agent = allAgents.find((candidate) => candidate.id === agentId);
4471
+ const binaryName = agent?.binaryName;
4472
+ if (!agentId || !binaryName) {
4473
+ return;
4474
+ }
4475
+ const commandEnv = resolveExecutionEnv(opts.openSpec);
4476
+ const exists = await binaryExists(opts.env, {
4477
+ binaryName,
4478
+ cwd: opts.openSpec.cwd,
4479
+ env: commandEnv
4480
+ });
4481
+ if (!exists) {
4482
+ return;
4483
+ }
4484
+ const result = await runProbeCommand(opts.env, {
4485
+ command: "poe-code",
4486
+ args: ["configure", "--yes", "--provider", "poe", agentId],
4487
+ cwd: opts.openSpec.cwd,
4488
+ env: commandEnv
4489
+ });
4490
+ if (result.exitCode !== 0) {
4491
+ throw new Error(
4492
+ `Failed to configure ${agentId} for Poe inside E2B sandbox.
4493
+ ${formatProbeResult(result)}`
4494
+ );
4495
+ }
4496
+ }
4497
+ async function binaryExists(env, opts) {
4498
+ for (const detector of createBinaryExistsDetectors(opts.binaryName)) {
4499
+ const result = await runProbeCommand(env, {
4500
+ ...detector,
4501
+ cwd: opts.cwd,
4502
+ env: opts.env
4503
+ });
4504
+ if (detector.validate(result)) {
4505
+ return true;
4506
+ }
4507
+ }
4508
+ return false;
4509
+ }
4510
+ function resolveExecutionEnv(openSpec) {
4511
+ const execution = openSpec.execution;
4512
+ return execution?.env ?? openSpec.env;
4513
+ }
4514
+ async function runProbeCommand(env, spec) {
4515
+ const handle = env.exec({
4516
+ command: spec.command,
4517
+ args: spec.args,
4518
+ cwd: spec.cwd,
4519
+ env: spec.env,
4520
+ stdin: "ignore",
4521
+ stdout: "pipe",
4522
+ stderr: "pipe"
4523
+ });
4524
+ const stdout = readStream(handle.stdout);
4525
+ const stderr = readStream(handle.stderr);
4526
+ const result = await handle.result;
4527
+ return {
4528
+ exitCode: result.exitCode,
4529
+ stdout: await stdout,
4530
+ stderr: await stderr
4531
+ };
4532
+ }
4533
+ function readStream(stream) {
4534
+ if (!stream) {
4535
+ return Promise.resolve("");
4536
+ }
4537
+ return new Promise((resolve2, reject) => {
4538
+ let output = "";
4539
+ stream.setEncoding("utf8");
4540
+ stream.on("data", (chunk) => {
4541
+ output += chunk.toString();
4542
+ });
4543
+ stream.on("error", reject);
4544
+ stream.on("end", () => resolve2(output));
4545
+ });
4546
+ }
4547
+ function formatProbeResult(result) {
4548
+ return [
4549
+ `Exit code: ${result.exitCode}`,
4550
+ result.stdout.trim() ? `stdout:
4551
+ ${result.stdout.trim()}` : "",
4552
+ result.stderr.trim() ? `stderr:
4553
+ ${result.stderr.trim()}` : ""
4554
+ ].filter(Boolean).join("\n");
4555
+ }
4556
+ async function runSync(opts) {
4557
+ const execution = opts.openSpec.execution;
4558
+ const capture = execution?.captureOutput === true;
4559
+ const abort = createAbortSync(opts.signal, opts.handle, execution?.activityTimeoutMs);
4560
+ const streamState = capture ? captureRunStreams(opts.handle, execution, abort.resetActivityTimer) : pipeRunStreams(opts.handle);
4561
+ abort.resetActivityTimer();
4562
+ try {
4563
+ const { exitCode } = opts.wrapCommand ? await abort.waitForExit(opts.env, opts.jobId) : await abort.waitForHandle();
4564
+ const download = await opts.env.downloadWorkspace({
4565
+ conflictPolicy: opts.openSpec.runner?.download_conflict ?? "refuse"
4566
+ });
4567
+ if (opts.closeAfterDownload !== false) {
4568
+ await opts.env.close();
4569
+ }
4570
+ return {
4571
+ exitCode,
4572
+ download,
4573
+ ...capture ? { stdout: streamState.stdout(), stderr: streamState.stderr() } : {}
4574
+ };
4575
+ } finally {
4576
+ abort.dispose();
4577
+ streamState.dispose();
4578
+ }
4579
+ }
4580
+ function pipeRunStreams(handle) {
4581
+ handle.stdout?.pipe(process.stdout, { end: false });
4582
+ handle.stderr?.pipe(process.stderr, { end: false });
4583
+ return {
4584
+ stdout: () => "",
4585
+ stderr: () => "",
4586
+ dispose() {
4587
+ handle.stdout?.unpipe(process.stdout);
4588
+ handle.stderr?.unpipe(process.stderr);
4589
+ }
4590
+ };
4591
+ }
4592
+ function captureRunStreams(handle, execution, onActivity) {
4593
+ let stdout = "";
4594
+ let stderr = "";
4595
+ const listeners = [];
4596
+ const bind = (stream, onChunk) => {
4597
+ if (!stream) return;
4598
+ stream.setEncoding("utf8");
4599
+ const listener = (chunk) => {
4600
+ onActivity();
4601
+ onChunk(chunk.toString());
4602
+ };
4603
+ stream.on("data", listener);
4604
+ listeners.push(() => {
4605
+ stream.off("data", listener);
4606
+ });
4607
+ };
4608
+ bind(handle.stdout, (chunk) => {
4609
+ stdout += chunk;
4610
+ execution?.onStdout?.(chunk);
4611
+ });
4612
+ bind(handle.stderr, (chunk) => {
4613
+ stderr += chunk;
4614
+ execution?.onStderr?.(chunk);
4615
+ });
4616
+ return {
4617
+ stdout: () => stdout,
4618
+ stderr: () => stderr,
4619
+ dispose() {
4620
+ for (const remove2 of listeners) {
4621
+ remove2();
4622
+ }
4623
+ }
4624
+ };
4625
+ }
4626
+ function createAbortSync(signal, handle, activityTimeoutMs) {
4627
+ let activityTimer;
4628
+ let timedOut = false;
4629
+ const resetActivityTimer = activityTimeoutMs ? () => {
4630
+ if (activityTimer) clearTimeout(activityTimer);
4631
+ activityTimer = setTimeout(() => {
4632
+ timedOut = true;
4633
+ handle.kill("SIGTERM");
4634
+ notifyAbort?.();
4635
+ }, activityTimeoutMs);
4636
+ } : () => {
4637
+ };
4638
+ let notifyAbort;
4639
+ if (signal === void 0) {
4640
+ return {
4641
+ waitForExit: (env, jobId) => waitForExit(toLogStreamEnv(env), jobId),
4642
+ waitForHandle: async () => {
4643
+ const result = await handle.result;
4644
+ if (timedOut) {
4645
+ throw createActivityTimeoutError(activityTimeoutMs);
4646
+ }
4647
+ return result;
4648
+ },
4649
+ resetActivityTimer,
4650
+ dispose() {
4651
+ if (activityTimer) clearTimeout(activityTimer);
4652
+ }
4653
+ };
4654
+ }
4655
+ const exitWaitController = new AbortController();
4656
+ let aborted = signal.aborted;
4657
+ const abortedPromise = new Promise((resolve2) => {
4658
+ notifyAbort = resolve2;
4659
+ });
4660
+ const kill = () => {
4661
+ aborted = true;
4662
+ handle.kill("SIGTERM");
4663
+ notifyAbort?.();
4664
+ };
4665
+ if (signal.aborted) {
4666
+ kill();
4667
+ } else {
4668
+ signal.addEventListener("abort", kill, { once: true });
4669
+ }
4670
+ return {
4671
+ async waitForExit(env, jobId) {
4672
+ if (aborted) {
4673
+ return handle.result;
4674
+ }
4675
+ const exit = waitForExit(toLogStreamEnv(env), jobId, {
4676
+ signal: exitWaitController.signal
4677
+ }).then(
4678
+ (value) => ({ kind: "exit", value }),
4679
+ (error2) => ({ kind: "error", error: error2 })
4680
+ );
4681
+ const result = await Promise.race([
4682
+ exit,
4683
+ abortedPromise.then(() => ({ kind: "abort" }))
4684
+ ]);
4685
+ if (result.kind === "exit") {
4686
+ return result.value;
4687
+ }
4688
+ if (result.kind === "error") {
4689
+ throw result.error;
4690
+ }
4691
+ exitWaitController.abort();
4692
+ return handle.result;
4693
+ },
4694
+ async waitForHandle() {
4695
+ const result = await Promise.race([
4696
+ handle.result.then((value) => ({ kind: "exit", value })),
4697
+ abortedPromise.then(() => ({ kind: "abort" }))
4698
+ ]);
4699
+ if (result.kind === "exit") {
4700
+ if (aborted) {
4701
+ throw createAbortError2();
4702
+ }
4703
+ if (timedOut) {
4704
+ throw createActivityTimeoutError(activityTimeoutMs);
4705
+ }
4706
+ return result.value;
4707
+ }
4708
+ if (timedOut) {
4709
+ throw createActivityTimeoutError(activityTimeoutMs);
4710
+ }
4711
+ throw createAbortError2();
4712
+ },
4713
+ resetActivityTimer,
4714
+ dispose() {
4715
+ if (activityTimer) clearTimeout(activityTimer);
4716
+ exitWaitController.abort();
4717
+ signal.removeEventListener("abort", kill);
4718
+ }
4719
+ };
4720
+ }
4721
+ function toLogStreamEnv(env) {
4722
+ const candidate = env;
4723
+ return candidate.fs === void 0 ? {} : { fs: candidate.fs };
4724
+ }
4725
+ function setDetachedJobContext(env, context) {
4726
+ const candidate = env;
4727
+ candidate.setDetachedJobContext?.(context);
4728
+ }
4729
+ function isPromiseLike(value) {
4730
+ return typeof value.then === "function";
4731
+ }
4732
+ function createAbortError2() {
4733
+ const error2 = new Error("Agent spawn aborted");
4734
+ error2.name = "AbortError";
4735
+ return error2;
4736
+ }
4737
+ function createActivityTimeoutError(timeoutMs) {
4738
+ const error2 = new Error(`Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`);
4739
+ error2.name = "ActivityTimeoutError";
4740
+ return error2;
4741
+ }
4742
+ function createUlid() {
4743
+ const time = BigInt(Date.now());
4744
+ const random = randomBytes(10);
4745
+ let randomValue = 0n;
4746
+ for (const byte of random) {
4747
+ randomValue = randomValue << 8n | BigInt(byte);
4748
+ }
4749
+ return encodeBase32(time, 10) + encodeBase32(randomValue, 16);
4750
+ }
4751
+ function encodeBase32(value, length) {
4752
+ const chars = Array.from({ length }, () => "0");
4753
+ let remaining = value;
4754
+ for (let index = length - 1; index >= 0; index -= 1) {
4755
+ chars[index] = ULID_ALPHABET[Number(remaining & 31n)];
4756
+ remaining >>= 5n;
4757
+ }
4758
+ return chars.join("");
4759
+ }
4760
+
4761
+ // packages/agent-harness-tools/src/poe-command-execution.ts
4762
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
4763
+ import os3 from "node:os";
4764
+
4765
+ // packages/agent-harness-tools/src/execution-env.ts
4766
+ var executionEnvFactories = /* @__PURE__ */ new Map();
4767
+ function registerExecutionEnvFactory(factory) {
4768
+ executionEnvFactories.set(factory.type, factory);
4769
+ }
4770
+ function selectExecutionEnv(runtime) {
4771
+ return selectExecutionEnvFactory(runtime.type);
4772
+ }
4773
+ function selectExecutionEnvFactory(type) {
4774
+ const factory = executionEnvFactories.get(type);
4775
+ if (factory === void 0) {
4776
+ throw new Error(
4777
+ `No execution environment factory registered for runtime type "${type}".`
4778
+ );
4779
+ }
4780
+ return factory;
4781
+ }
4782
+
4783
+ // packages/agent-harness-tools/src/poe-command-execution.ts
4784
+ function resolvePoeCommandExecution(input) {
4785
+ const homeDir = input.context?.homeDir ?? os3.homedir();
4786
+ const runtimeConfigCwd = input.runtimeConfigCwd ?? input.cwd;
4787
+ const loaded = loadRuntimeConfig(runtimeConfigCwd, homeDir);
4788
+ const config = applyRuntimeOverrides(loaded, input.runtime, runtimeConfigCwd);
4789
+ const resolved = resolveRuntime({ cwd: runtimeConfigCwd, config });
4790
+ const factory = selectExecutionEnv(resolved.runtime);
4791
+ const state = input.context?.state ?? loadState(homeDir);
4792
+ return {
4793
+ factory,
4794
+ detach: factory.supportsDetach === true && config.runner.detach,
4795
+ state,
4796
+ openSpec: {
4797
+ cwd: input.cwd,
4798
+ runtimeCwd: runtimeConfigCwd,
4799
+ runtime: resolved.runtime,
4800
+ runner: config.runner,
4801
+ state,
4802
+ env: input.env,
4803
+ uploadIgnoreFiles: config.runner.workspace?.exclude ?? [],
4804
+ jobLabel: {
4805
+ tool: input.tool,
4806
+ argv: input.argv
4807
+ },
4808
+ ...input.openSpec
4809
+ }
4810
+ };
4811
+ }
4812
+ function applyRuntimeOverrides(config, overrides, cwd = process.cwd()) {
4813
+ if (!overrides) {
4814
+ return { runtime: config.runtime, runner: config.runner };
4815
+ }
4816
+ const base = "rawScope" in config && config.rawScope ? { ...config.rawScope } : { ...config.runtime };
4817
+ const runtime = parseRuntime({
4818
+ ...base,
4819
+ ...overrides.runtime !== void 0 ? { type: overrides.runtime } : {},
4820
+ ...overrides.runtimeImage !== void 0 ? { image: overrides.runtimeImage } : {},
4821
+ ...overrides.runtimeTemplate !== void 0 ? { template_id: overrides.runtimeTemplate } : {},
4822
+ ...overrides.mountPoeCode === true ? { mounts: [...config.runtime.mounts, createPoeCodeMount(cwd)] } : {}
4823
+ });
4824
+ return {
4825
+ runtime,
4826
+ runner: {
4827
+ ...config.runner,
4828
+ ...overrides.detach === true ? { detach: true } : {},
4829
+ ...overrides.runnerSync !== void 0 ? { sync: overrides.runnerSync } : {}
4830
+ }
4831
+ };
4832
+ }
4833
+ function createPoeCodeMount(cwd) {
4834
+ return {
4835
+ source: cwd,
4836
+ target: "/usr/local/lib/poe-code",
4837
+ readonly: true
4838
+ };
4839
+ }
4840
+ function loadRuntimeConfig(cwd, homeDir) {
4841
+ const document = deepMergeDocuments(
4842
+ readConfigDocument(resolveConfigPath(homeDir)),
4843
+ readConfigDocument(resolveProjectConfigPath(cwd))
4844
+ );
4845
+ const runtimeScope = resolveScope(runtimeConfigScope.schema, document.runtime, process.env);
4846
+ return {
4847
+ rawScope: { ...runtimeScope },
4848
+ runtime: parseRuntime(runtimeScope),
4849
+ runner: runtimeScope.runner
4850
+ };
4851
+ }
4852
+ function readConfigDocument(filePath) {
4853
+ if (!existsSync2(filePath)) {
4854
+ return {};
4855
+ }
4856
+ return JSON.parse(readFileSync(filePath, "utf8"));
4857
+ }
4858
+ function loadState(homeDir) {
4859
+ if (process.env.VITEST === "true") {
4860
+ return createMemoryStateManager();
4861
+ }
4862
+ return createStateManager(homeDir);
4863
+ }
4864
+ function createMemoryStateManager() {
4865
+ const jobs = /* @__PURE__ */ new Map();
4866
+ return {
4867
+ templates: {
4868
+ async get() {
4869
+ return null;
4870
+ },
4871
+ async put() {
4872
+ },
4873
+ async remove() {
4874
+ },
4875
+ async list() {
4876
+ return [];
4877
+ }
4878
+ },
4879
+ jobs: {
4880
+ async get(id) {
4881
+ return jobs.get(id) ?? null;
4882
+ },
4883
+ async put(entry) {
4884
+ jobs.set(entry.id, entry);
4885
+ },
4886
+ async update(id, patch) {
4887
+ const current = jobs.get(id);
4888
+ if (!current) {
4889
+ return null;
4890
+ }
4891
+ const updated = { ...current, ...patch, id };
4892
+ jobs.set(id, updated);
4893
+ return updated;
4894
+ },
4895
+ async list(filter) {
4896
+ const entries = Array.from(jobs.values());
4897
+ if (!filter) {
4898
+ return entries;
4899
+ }
4900
+ return entries.filter(
4901
+ (entry) => Object.entries(filter).every(([key, value]) => entry[key] === value)
4902
+ );
4903
+ },
4904
+ async remove(id) {
4905
+ jobs.delete(id);
4906
+ }
4907
+ }
4908
+ };
4909
+ }
4910
+
4911
+ // packages/agent-harness-tools/src/workspace-transfer.ts
4912
+ import { createHash as createHash3 } from "node:crypto";
4913
+ import { promises as nodeFs3 } from "node:fs";
4914
+ import path24 from "node:path";
4915
+
4916
+ // packages/process-runner/src/docker/context.ts
4917
+ import { execSync } from "node:child_process";
4918
+ function detectContext() {
4919
+ try {
4920
+ const output = execSync("colima list --json", {
4921
+ encoding: "utf-8",
4922
+ stdio: ["pipe", "pipe", "ignore"]
4923
+ });
4924
+ const lines = output.trim().split("\n").filter(Boolean);
4925
+ for (const line of lines) {
4926
+ const profile = JSON.parse(line);
4927
+ if (profile.status === "Running" && profile.runtime === "docker") {
4928
+ const name = profile.name ?? profile.profile;
4929
+ if (!name) {
4930
+ continue;
4931
+ }
4932
+ return name === "default" ? "colima" : `colima-${name}`;
4933
+ }
4934
+ }
4935
+ } catch {
4936
+ return null;
4937
+ }
4938
+ return null;
4939
+ }
4940
+ function buildContextArgs(engine, context) {
4941
+ if (engine === "docker" && context) {
4942
+ return ["--context", context];
4943
+ }
4944
+ return [];
4945
+ }
4946
+
4947
+ // packages/process-runner/src/docker/engine.ts
4948
+ import { execSync as execSync2 } from "node:child_process";
4949
+ function detectEngine() {
4950
+ if (isEngineAvailable("docker")) {
4951
+ return "docker";
4952
+ }
4953
+ if (isEngineAvailable("podman")) {
4954
+ return "podman";
4955
+ }
4956
+ throw new Error(
4957
+ "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"
4958
+ );
4959
+ }
4960
+ function isEngineAvailable(engine) {
4961
+ try {
4962
+ execSync2(`${engine} --version`, {
4963
+ stdio: "ignore"
4964
+ });
4965
+ return true;
4966
+ } catch {
4967
+ return false;
4968
+ }
4969
+ }
4970
+
4971
+ // packages/process-runner/src/docker/docker-runner.ts
4972
+ import * as childProcess from "node:child_process";
4973
+ import { randomBytes as randomBytes2 } from "node:crypto";
4974
+
4975
+ // packages/process-runner/src/docker/args.ts
4976
+ import path25 from "node:path";
4977
+ function buildDockerRunArgs(input) {
4978
+ const args = [input.engine];
4979
+ if (input.engine === "docker" && input.context) {
4980
+ args.push("--context", input.context);
4981
+ }
4982
+ args.push("run");
4983
+ if (input.rm) {
4984
+ args.push("--rm");
4985
+ }
4986
+ if (input.detached) {
4987
+ args.push("-d");
4988
+ }
4989
+ if (input.interactive) {
4990
+ args.push("-i");
4991
+ }
4992
+ if (input.tty) {
4993
+ args.push("-t");
4994
+ }
4995
+ args.push("--name", input.containerName);
4996
+ if (input.cwd !== void 0) {
4997
+ args.push("-w", input.cwd);
4998
+ }
4999
+ for (const [key, value] of Object.entries(input.env ?? {})) {
5000
+ args.push("-e", `${key}=${value}`);
5001
+ }
5002
+ for (const mount of input.mounts) {
5003
+ const volume = `${path25.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
5004
+ args.push("-v", volume);
5005
+ }
5006
+ for (const port of input.ports) {
5007
+ const mapping = `${port.host}:${port.container}${port.protocol === void 0 || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
5008
+ args.push("-p", mapping);
5009
+ }
5010
+ if (input.network !== void 0) {
5011
+ args.push("--network", input.network);
5012
+ }
5013
+ args.push(...input.extraArgs, input.image, input.command, ...input.args);
5014
+ return args;
5015
+ }
5016
+
5017
+ // packages/process-runner/src/docker/docker-execution-env.ts
5018
+ import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
5019
+ import { mkdtempSync, rmSync } from "node:fs";
5020
+ import { readFile as readFile10 } from "node:fs/promises";
5021
+ import { tmpdir } from "node:os";
5022
+ import path26 from "node:path";
5023
+
5024
+ // packages/process-runner/src/host/host-runner.ts
5025
+ import { spawn as spawnChildProcess } from "node:child_process";
5026
+ function createHostRunner(options = {}) {
5027
+ const detached = options.detached === true;
5028
+ return {
5029
+ name: "host",
5030
+ exec(spec) {
5031
+ const stdinMode = spec.stdin ?? "ignore";
5032
+ const stdoutMode = spec.stdout ?? "pipe";
5033
+ const stderrMode = spec.stderr ?? "pipe";
5034
+ const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
5035
+ const child = spawnChildProcess(spec.command, spec.args ?? [], {
5036
+ cwd: spec.cwd,
5037
+ env: spec.env,
5038
+ stdio,
5039
+ ...detached ? { detached: true } : {}
5040
+ });
5041
+ if (detached) {
5042
+ child.unref();
5043
+ }
5044
+ const kill = (signal) => {
5045
+ if (detached && process.platform !== "win32" && child.pid !== void 0) {
5046
+ process.kill(-child.pid, signal);
5047
+ return;
5048
+ }
5049
+ child.kill(signal);
5050
+ };
5051
+ let settled = false;
5052
+ let resolveResult = null;
5053
+ const result = new Promise((resolve2) => {
5054
+ resolveResult = resolve2;
5055
+ });
5056
+ const cleanupAbort = bindAbortSignal(spec.signal, () => {
5057
+ kill("SIGTERM");
5058
+ });
5059
+ child.once("close", (code) => {
5060
+ if (settled) return;
5061
+ settled = true;
5062
+ cleanupAbort();
5063
+ resolveResult?.({ exitCode: code ?? 1 });
5064
+ });
5065
+ child.once("error", () => {
5066
+ if (settled) return;
5067
+ settled = true;
5068
+ cleanupAbort();
5069
+ resolveResult?.({ exitCode: 1 });
5070
+ });
5071
+ return {
5072
+ pid: child.pid ?? null,
5073
+ stdin: child.stdin,
5074
+ stdout: child.stdout,
5075
+ stderr: child.stderr,
5076
+ result,
5077
+ kill
5078
+ };
5079
+ }
5080
+ };
5081
+ }
5082
+ function bindAbortSignal(signal, onAbort) {
5083
+ if (signal === void 0) {
5084
+ return () => {
5085
+ };
5086
+ }
5087
+ if (signal.aborted) {
5088
+ onAbort();
5089
+ return () => {
5090
+ };
5091
+ }
5092
+ signal.addEventListener("abort", onAbort, { once: true });
5093
+ return () => {
5094
+ signal.removeEventListener("abort", onAbort);
5095
+ };
5096
+ }
5097
+
5098
+ // packages/process-runner/src/docker/docker-execution-env.ts
5099
+ var containerCommand = ["sh", "-c", "while :; do sleep 3600; done"];
5100
+ var dockerExecutionEnvFactory = {
5101
+ type: "docker",
5102
+ supportsDetach: true,
5103
+ async open(spec) {
5104
+ const runtime = parseDockerRuntime(spec.runtime);
5105
+ const runner = spec.hostRunner ?? createHostRunner();
5106
+ const engine = runtime.engine ?? detectEngine();
5107
+ const context = detectContext();
5108
+ const image = await resolveImage({
5109
+ spec,
5110
+ runtime,
5111
+ runner,
5112
+ engine,
5113
+ context
5114
+ });
5115
+ const containerName = createContainerName();
5116
+ const runArgs = buildDockerRunArgs({
5117
+ engine,
5118
+ context,
5119
+ image,
5120
+ command: containerCommand[0],
5121
+ args: containerCommand.slice(1),
5122
+ cwd: void 0,
5123
+ env: void 0,
5124
+ mounts: runtime.mounts ?? [],
5125
+ ports: [],
5126
+ network: runtime.network,
5127
+ containerName,
5128
+ detached: true,
5129
+ interactive: true,
5130
+ tty: false,
5131
+ rm: false,
5132
+ extraArgs: runtime.extra_args ?? []
5133
+ });
5134
+ const [command, ...args] = runArgs;
5135
+ const id = (await runAndRead(runner, { command, args, stdout: "pipe", stderr: "pipe" })).trim();
5136
+ return createDockerEnv({
5137
+ id,
5138
+ spec,
5139
+ runner,
5140
+ engine,
5141
+ context
5142
+ });
5143
+ },
5144
+ async attach(envId, context) {
5145
+ const engine = detectEngine();
5146
+ return createDockerEnv({
5147
+ id: envId,
5148
+ spec: createAttachedSpec(context?.cwd),
5149
+ runner: createHostRunner(),
5150
+ engine,
5151
+ context: detectContext(),
5152
+ attachedJobId: context?.jobId
5153
+ });
5154
+ }
5155
+ };
5156
+ function createDockerEnv(input) {
5157
+ const containerRef = input.id;
5158
+ return {
5159
+ id: containerRef,
5160
+ job: input.attachedJobId === void 0 ? null : createContainerJob(containerRef, input.runner, input.engine, input.context, input.attachedJobId),
5161
+ async uploadWorkspace() {
5162
+ const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-upload-"));
5163
+ const archivePath = path26.join(tempDir, "workspace.tar");
5164
+ try {
5165
+ const excludeArgs = input.spec.uploadIgnoreFiles.flatMap((ignored) => [
5166
+ "--exclude",
5167
+ ignored
5168
+ ]);
5169
+ const tarArgs = [...excludeArgs, "-cf", archivePath, "-C", input.spec.cwd, "."];
5170
+ await runOrThrow(input.runner, {
5171
+ command: "tar",
5172
+ args: tarArgs,
5173
+ stdout: "pipe",
5174
+ stderr: "pipe"
5175
+ });
5176
+ await runOrThrow(input.runner, {
5177
+ command: input.engine,
5178
+ args: [
5179
+ ...buildContextArgs(input.engine, input.context),
5180
+ "cp",
5181
+ archivePath,
5182
+ `${containerRef}:/tmp/poe-workspace-upload.tar`
5183
+ ],
5184
+ stdout: "pipe",
5185
+ stderr: "pipe"
5186
+ });
5187
+ await runOrThrow(input.runner, {
5188
+ command: input.engine,
5189
+ args: [
5190
+ ...buildContextArgs(input.engine, input.context),
5191
+ "exec",
5192
+ containerRef,
5193
+ "sh",
5194
+ "-c",
5195
+ `mkdir -p ${shellQuote2(input.spec.cwd)} && tar -xf /tmp/poe-workspace-upload.tar -C ${shellQuote2(input.spec.cwd)}`
5196
+ ],
5197
+ stdout: "pipe",
5198
+ stderr: "pipe"
5199
+ });
5200
+ return { files: 0, bytes: 0, skipped: [] };
5201
+ } finally {
5202
+ rmSync(tempDir, { recursive: true, force: true });
5203
+ }
5204
+ },
5205
+ async downloadWorkspace(opts) {
5206
+ const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-download-"));
5207
+ const archivePath = path26.join(tempDir, "workspace.tar");
5208
+ try {
5209
+ await runOrThrow(input.runner, {
5210
+ command: input.engine,
5211
+ args: [
5212
+ ...buildContextArgs(input.engine, input.context),
5213
+ "exec",
5214
+ containerRef,
5215
+ "sh",
5216
+ "-c",
5217
+ `tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote2(input.spec.cwd)} .`
5218
+ ],
5219
+ stdout: "pipe",
5220
+ stderr: "pipe"
5221
+ });
5222
+ await runOrThrow(input.runner, {
5223
+ command: input.engine,
5224
+ args: [
5225
+ ...buildContextArgs(input.engine, input.context),
5226
+ "cp",
5227
+ `${containerRef}:/tmp/poe-workspace-download.tar`,
5228
+ archivePath
5229
+ ],
5230
+ stdout: "pipe",
5231
+ stderr: "pipe"
5232
+ });
5233
+ const extractMode = opts.conflictPolicy === "refuse" ? "-xkf" : "-xf";
5234
+ await runOrThrow(input.runner, {
5235
+ command: "tar",
5236
+ args: [extractMode, archivePath, "-C", input.spec.cwd],
5237
+ stdout: "pipe",
5238
+ stderr: "pipe"
5239
+ });
5240
+ return { files: 0, bytes: 0, conflicts: [] };
5241
+ } finally {
5242
+ rmSync(tempDir, { recursive: true, force: true });
5243
+ }
5244
+ },
5245
+ exec(spec) {
5246
+ return input.runner.exec({
5247
+ command: input.engine,
5248
+ args: [
5249
+ ...buildContextArgs(input.engine, input.context),
5250
+ "exec",
5251
+ ...spec.stdin === "pipe" || spec.stdin === "inherit" ? ["-i"] : [],
5252
+ ...spec.tty === true ? ["-t"] : [],
5253
+ ...spec.cwd !== void 0 ? ["-w", spec.cwd] : [],
5254
+ ...buildEnvArgs(spec.env),
5255
+ containerRef,
5256
+ spec.command,
5257
+ ...spec.args ?? []
5258
+ ],
5259
+ stdin: spec.stdin,
5260
+ stdout: spec.stdout,
5261
+ stderr: spec.stderr,
5262
+ tty: spec.tty
5263
+ });
5264
+ },
5265
+ async detach() {
5266
+ return createContainerJob(containerRef, input.runner, input.engine, input.context);
5267
+ },
5268
+ shell() {
5269
+ const shellSpec = input.spec.shellSpec;
5270
+ return this.exec({
5271
+ command: shellSpec?.command ?? input.spec.env.SHELL ?? "sh",
5272
+ ...shellSpec?.args ? { args: shellSpec.args } : {},
5273
+ cwd: input.spec.cwd,
5274
+ env: shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env,
5275
+ stdin: "inherit",
5276
+ stdout: "inherit",
5277
+ stderr: "inherit",
5278
+ tty: true
5279
+ });
5280
+ },
5281
+ async close() {
5282
+ await runOrThrow(input.runner, {
5283
+ command: input.engine,
5284
+ args: [...buildContextArgs(input.engine, input.context), "rm", "-f", containerRef],
5285
+ stdout: "pipe",
5286
+ stderr: "pipe"
5287
+ });
5288
+ }
5289
+ };
5290
+ }
5291
+ async function resolveImage(input) {
5292
+ if (input.runtime.image !== void 0) {
5293
+ return input.runtime.image;
5294
+ }
5295
+ const result = await buildDockerRuntimeTemplate({
5296
+ cwd: input.spec.cwd,
5297
+ runtime: input.runtime,
5298
+ state: input.spec.state,
5299
+ runner: input.runner
5300
+ });
5301
+ return result.image;
5302
+ }
5303
+ async function buildDockerRuntimeTemplate(input) {
5304
+ const runner = input.runner ?? createHostRunner();
5305
+ const engine = input.runtime.engine ?? detectEngine();
5306
+ const context = detectContext();
5307
+ const dockerfilePath = path26.resolve(
5308
+ input.cwd,
5309
+ input.runtime.dockerfile ?? path26.join(".poe-code", "Dockerfile")
5310
+ );
5311
+ const buildContext = path26.resolve(input.cwd, input.runtime.build_context ?? ".");
5312
+ const dockerfileBytes = await readFile10(dockerfilePath);
5313
+ const hash = hashDockerTemplate(dockerfileBytes, input.runtime.build_args ?? {});
5314
+ const cached2 = input.force ? null : await input.state?.templates.get("docker", hash);
5315
+ if (cached2?.image !== void 0) {
5316
+ return {
5317
+ backend: "docker",
5318
+ hash,
5319
+ image: cached2.image,
5320
+ cached: true
5321
+ };
5322
+ }
5323
+ const image = `poe-code/local:${hash}`;
5324
+ await buildImage({
5325
+ runner,
5326
+ engine,
5327
+ context,
5328
+ image,
5329
+ dockerfilePath,
5330
+ buildContext,
5331
+ buildArgs: input.runtime.build_args ?? {}
5332
+ });
5333
+ await input.state?.templates.put("docker", {
5334
+ hash,
5335
+ image,
5336
+ runtime_type: "docker",
5337
+ dockerfile_path: dockerfilePath,
5338
+ built_at: (/* @__PURE__ */ new Date()).toISOString()
5339
+ });
5340
+ return {
5341
+ backend: "docker",
5342
+ hash,
5343
+ image,
5344
+ cached: false
5345
+ };
5346
+ }
5347
+ function hashDockerTemplate(dockerfileBytes, buildArgs) {
5348
+ const hash = createHash4("sha256");
5349
+ hash.update(dockerfileBytes);
5350
+ hash.update("\0");
5351
+ for (const [key, value] of sortedBuildArgs(buildArgs)) {
5352
+ hash.update(key);
5353
+ hash.update("=");
5354
+ hash.update(value);
5355
+ hash.update("\0");
5356
+ }
5357
+ return hash.digest("hex");
5358
+ }
5359
+ async function buildImage(input) {
5360
+ await runOrThrow(input.runner, {
5361
+ command: input.engine,
5362
+ args: [
5363
+ ...buildContextArgs(input.engine, input.context),
5364
+ "build",
5365
+ "--tag",
5366
+ input.image,
5367
+ "-f",
5368
+ input.dockerfilePath,
5369
+ ...sortedBuildArgs(input.buildArgs).flatMap(([key, value]) => [
5370
+ "--build-arg",
5371
+ `${key}=${value}`
5372
+ ]),
5373
+ input.buildContext
5374
+ ],
5375
+ stdout: "pipe",
5376
+ stderr: "pipe"
5377
+ });
5378
+ }
5379
+ function parseDockerRuntime(runtime) {
5380
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
5381
+ throw new Error("docker runtime must be an object");
5382
+ }
5383
+ const record = runtime;
5384
+ if (record.type !== "docker") {
5385
+ throw new Error('docker runtime type must be "docker"');
5386
+ }
5387
+ return record;
5388
+ }
5389
+ async function runAndRead(runner, spec) {
5390
+ const handle = runner.exec(spec);
5391
+ const stdout = readStream2(handle.stdout);
5392
+ const stderr = readStream2(handle.stderr);
5393
+ const result = await handle.result;
5394
+ const output = await stdout;
5395
+ if (result.exitCode !== 0) {
5396
+ const errorOutput = await stderr;
5397
+ throw new Error(
5398
+ `Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${errorOutput ? `
5399
+ ${errorOutput}` : ""}`
5400
+ );
5401
+ }
5402
+ return output;
5403
+ }
5404
+ async function runOrThrow(runner, spec) {
5405
+ await runAndRead(runner, spec);
5406
+ }
5407
+ async function readStream2(stream) {
5408
+ if (stream === null) {
5409
+ return "";
5410
+ }
5411
+ stream.setEncoding("utf8");
5412
+ const chunks = [];
5413
+ for await (const chunk of stream) {
5414
+ chunks.push(String(chunk));
5415
+ }
5416
+ return chunks.join("");
5417
+ }
5418
+ function sortedBuildArgs(buildArgs) {
5419
+ return Object.entries(buildArgs).sort(([left], [right]) => left.localeCompare(right));
5420
+ }
5421
+ function buildEnvArgs(env) {
5422
+ if (env === void 0) {
5423
+ return [];
5424
+ }
5425
+ return Object.entries(env).flatMap(([key, value]) => ["-e", `${key}=${value}`]);
5426
+ }
5427
+ function createContainerName() {
5428
+ return `poe-env-${randomBytes3(6).toString("hex")}`;
5429
+ }
5430
+ function createContainerJob(containerId, runner, engine, context, jobId = containerId) {
5431
+ return {
5432
+ id: jobId,
5433
+ envId: containerId,
5434
+ tool: "docker",
5435
+ argv: ["attach", containerId],
5436
+ async status() {
5437
+ const handle = runner.exec({
5438
+ command: engine,
5439
+ args: [
5440
+ ...buildContextArgs(engine, context),
5441
+ "inspect",
5442
+ "-f",
5443
+ "{{.State.Status}}",
5444
+ containerId
5445
+ ],
5446
+ stdout: "pipe",
5447
+ stderr: "pipe"
5448
+ });
5449
+ const stdout = await readStream2(handle.stdout);
5450
+ const result = await handle.result;
5451
+ if (result.exitCode !== 0) {
5452
+ return "lost";
5453
+ }
5454
+ return stdout.trim() === "running" ? "running" : "exited";
5455
+ },
5456
+ async *stream(opts) {
5457
+ const handle = runner.exec({
5458
+ command: engine,
5459
+ args: [
5460
+ ...buildContextArgs(engine, context),
5461
+ "exec",
5462
+ containerId,
5463
+ "sh",
5464
+ "-c",
5465
+ `test -f ${shellQuote2(`/tmp/poe-jobs/${jobId}.log`)} && tail -c +${(opts?.sinceByte ?? 0) + 1} ${shellQuote2(`/tmp/poe-jobs/${jobId}.log`)} || true`
5466
+ ],
5467
+ stdout: "pipe",
5468
+ stderr: "pipe"
5469
+ });
5470
+ const stdout = await readStream2(handle.stdout);
5471
+ await handle.result;
5472
+ if (stdout.length > 0) {
5473
+ yield { byteOffset: opts?.sinceByte ?? 0, data: stdout };
5474
+ }
5475
+ },
5476
+ async wait() {
5477
+ const handle = runner.exec({
5478
+ command: engine,
5479
+ args: [...buildContextArgs(engine, context), "wait", containerId],
5480
+ stdout: "pipe",
5481
+ stderr: "pipe"
5482
+ });
5483
+ const stdout = await readStream2(handle.stdout);
5484
+ const result = await handle.result;
5485
+ return { exitCode: Number.parseInt(stdout.trim(), 10) || result.exitCode };
5486
+ },
5487
+ async kill(signal) {
5488
+ const args = signal === void 0 || signal === "SIGTERM" ? ["stop", containerId] : ["kill", ...signal === "SIGKILL" ? [] : [`--signal=${signal}`], containerId];
5489
+ await runOrThrow(runner, {
5490
+ command: engine,
5491
+ args: [...buildContextArgs(engine, context), ...args],
5492
+ stdout: "pipe",
5493
+ stderr: "pipe"
5494
+ });
5495
+ }
5496
+ };
5497
+ }
5498
+ function createAttachedSpec(cwd = "/workspace") {
5499
+ return {
5500
+ cwd,
5501
+ runtime: {
5502
+ type: "docker",
5503
+ image: "attached",
5504
+ build_args: {},
5505
+ mounts: []
5506
+ },
5507
+ env: {},
5508
+ uploadIgnoreFiles: [],
5509
+ jobLabel: {
5510
+ tool: "docker",
5511
+ argv: []
5512
+ }
5513
+ };
5514
+ }
5515
+ function shellQuote2(value) {
5516
+ return `'${value.replaceAll("'", "'\\''")}'`;
5517
+ }
5518
+
5519
+ // packages/process-runner/src/host/host-execution-env.ts
5520
+ var hostExecutionEnvFactory = {
5521
+ type: "host",
5522
+ supportsDetach: false,
5523
+ async open(openSpec) {
5524
+ return {
5525
+ id: "host",
5526
+ job: null,
5527
+ async uploadWorkspace() {
5528
+ return {
5529
+ files: 0,
5530
+ bytes: 0,
5531
+ skipped: []
5532
+ };
5533
+ },
5534
+ async downloadWorkspace() {
5535
+ return {
5536
+ files: 0,
5537
+ bytes: 0,
5538
+ conflicts: []
5539
+ };
5540
+ },
5541
+ exec(spec) {
5542
+ return createHostRunner().exec(spec);
5543
+ },
5544
+ async detach() {
5545
+ throw new Error("host runtime does not support detach because host has no addressable env");
5546
+ },
5547
+ shell() {
5548
+ const shellSpec = openSpec.shellSpec;
5549
+ return createHostRunner().exec({
5550
+ command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
5551
+ ...shellSpec?.args ? { args: shellSpec.args } : {},
5552
+ cwd: openSpec.cwd,
5553
+ env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
5554
+ stdin: "inherit",
5555
+ stdout: "inherit",
5556
+ stderr: "inherit",
5557
+ tty: true
5558
+ });
5559
+ },
5560
+ async close() {
5561
+ }
5562
+ };
5563
+ },
5564
+ async attach() {
5565
+ throw new Error("host runtime does not support reattach");
5566
+ }
5567
+ };
5568
+
5569
+ // packages/process-runner/src/testing/mock-runner.ts
5570
+ import { Readable, Writable } from "node:stream";
5571
+
5572
+ // packages/runner-e2b/src/factory.ts
5573
+ import path30 from "node:path";
5574
+
5575
+ // packages/runner-e2b/src/sdk.ts
5576
+ import { Template, Sandbox } from "e2b";
5577
+ async function createSandbox(opts) {
5578
+ return Sandbox.create(opts.templateId, {
5579
+ apiKey: opts.apiKey,
5580
+ envs: opts.env,
5581
+ ...opts.timeoutMinutes === void 0 ? {} : { timeoutMs: opts.timeoutMinutes * 6e4 }
5582
+ });
5583
+ }
5584
+ async function connectSandbox(id, apiKey) {
5585
+ return Sandbox.connect(id, apiKey === void 0 ? void 0 : { apiKey });
5586
+ }
5587
+ async function buildTemplate(opts) {
5588
+ const template = Template({ fileContextPath: opts.buildContext }).fromDockerfile(
5589
+ opts.dockerfilePath
5590
+ );
5591
+ if (opts.fromTemplate !== void 0 && opts.fromTemplate.length > 0) {
5592
+ template.fromTemplate(opts.fromTemplate);
5593
+ }
5594
+ const result = await Template.build(template, opts.name, {
5595
+ apiKey: opts.apiKey,
5596
+ ...opts.cpu === void 0 ? {} : { cpuCount: opts.cpu },
5597
+ ...opts.memoryMb === void 0 ? {} : { memoryMB: opts.memoryMb },
5598
+ ...opts.onLog ? { onBuildLogs: opts.onLog } : {}
5599
+ });
5600
+ return { templateId: result.templateId };
5601
+ }
5602
+ function toArrayBuffer(buffer) {
5603
+ const output = new ArrayBuffer(buffer.byteLength);
5604
+ new Uint8Array(output).set(buffer);
5605
+ return output;
5606
+ }
5607
+ async function readableToString(stream) {
5608
+ if (stream === null) {
5609
+ return "";
5610
+ }
5611
+ stream.setEncoding("utf8");
5612
+ const chunks = [];
5613
+ for await (const chunk of stream) {
5614
+ chunks.push(String(chunk));
5615
+ }
5616
+ return chunks.join("");
5617
+ }
5618
+
5619
+ // packages/runner-e2b/src/template-build.ts
5620
+ import { createHash as createHash5 } from "node:crypto";
5621
+ import { readdir as readdir4, readFile as readFile11 } from "node:fs/promises";
5622
+ import path27 from "node:path";
5623
+ var BUILD_LOG_TAIL_SIZE = 30;
5624
+ async function buildE2bRuntimeTemplate(input) {
5625
+ const dockerfileBytes = await readFile11(input.dockerfilePath);
5626
+ const buildContextFiles = await readBuildContextFiles(input.buildContext);
5627
+ const hash = hashTemplate(dockerfileBytes, buildContextFiles, input.runtime.build_args);
5628
+ const cached2 = input.force === true ? null : await input.state?.templates.get("e2b", hash);
5629
+ if (cached2?.template_id !== void 0) {
5630
+ return { backend: "e2b", hash, templateId: cached2.template_id, cached: true };
5631
+ }
5632
+ const tail = [];
5633
+ const onLog = (entry) => {
5634
+ tail.push(entry.message);
5635
+ if (tail.length > BUILD_LOG_TAIL_SIZE) {
5636
+ tail.shift();
5637
+ }
5638
+ input.onLog?.(entry);
5639
+ };
5640
+ let built;
5641
+ try {
5642
+ built = await buildTemplate({
5643
+ apiKey: input.apiKey,
5644
+ name: `poe-code-${hash.slice(0, 32)}`,
5645
+ dockerfilePath: input.dockerfilePath,
5646
+ buildContext: input.buildContext,
5647
+ cpu: input.runtime.cpu,
5648
+ memoryMb: input.runtime.memory_mb,
5649
+ fromTemplate: input.runtime.from_template,
5650
+ onLog
5651
+ });
5652
+ } catch (error2) {
5653
+ throw decorateBuildError(error2, tail);
5654
+ }
5655
+ await input.state?.templates.put("e2b", {
5656
+ hash,
5657
+ template_id: built.templateId,
5658
+ runtime_type: "e2b",
5659
+ dockerfile_path: input.dockerfilePath,
5660
+ built_at: (/* @__PURE__ */ new Date()).toISOString()
5661
+ });
5662
+ return { backend: "e2b", hash, templateId: built.templateId, cached: false };
5663
+ }
5664
+ function decorateBuildError(error2, tail) {
5665
+ const original = error2 instanceof Error ? error2 : new Error(String(error2));
5666
+ if (tail.length === 0) {
5667
+ return original;
5668
+ }
5669
+ const decorated = new Error(`${original.message}
5670
+
5671
+ Last build output:
5672
+ ${tail.join("\n")}`);
5673
+ decorated.stack = original.stack;
5674
+ decorated.cause = original;
5675
+ return decorated;
5676
+ }
5677
+ function hashTemplate(dockerfileBytes, buildContextFiles, buildArgs) {
5678
+ const hash = createHash5("sha256");
5679
+ hash.update(dockerfileBytes);
5680
+ hash.update("\0");
5681
+ for (const file of buildContextFiles) {
5682
+ hash.update(file.relativePath);
5683
+ hash.update("\0");
5684
+ hash.update(file.bytes);
5685
+ hash.update("\0");
5686
+ }
5687
+ for (const [key, value] of Object.entries(buildArgs).sort(
5688
+ ([left], [right]) => left.localeCompare(right)
5689
+ )) {
5690
+ hash.update(key);
5691
+ hash.update("=");
5692
+ hash.update(value);
5693
+ hash.update("\0");
5694
+ }
5695
+ return hash.digest("hex");
5696
+ }
5697
+ async function readBuildContextFiles(buildContext) {
5698
+ const files = [];
5699
+ await collectBuildContextFiles(buildContext, "", files);
5700
+ return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
5701
+ }
5702
+ async function collectBuildContextFiles(buildContext, relativeDir, files) {
5703
+ const absoluteDir = path27.join(buildContext, relativeDir);
5704
+ const entries = await readdir4(absoluteDir, { withFileTypes: true });
5705
+ for (const entry of entries) {
5706
+ const relativePath = path27.join(relativeDir, entry.name);
5707
+ if (entry.isDirectory()) {
5708
+ await collectBuildContextFiles(buildContext, relativePath, files);
5709
+ continue;
5710
+ }
5711
+ if (!entry.isFile()) {
5712
+ continue;
5713
+ }
5714
+ files.push({
5715
+ relativePath: relativePath.split(path27.sep).join("/"),
5716
+ bytes: await readFile11(path27.join(buildContext, relativePath))
5717
+ });
5718
+ }
5719
+ }
5720
+
5721
+ // packages/runner-e2b/src/opened-env.ts
5722
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
5723
+ import { readFile as readFile12, writeFile as writeFile7 } from "node:fs/promises";
5724
+ import { tmpdir as tmpdir2 } from "node:os";
5725
+ import path29 from "node:path";
5726
+ import { PassThrough, Writable as Writable2 } from "node:stream";
5727
+
5728
+ // packages/runner-e2b/src/job-handle.ts
5729
+ import path28 from "node:path";
5730
+ var JOB_DIR2 = "/tmp/poe-jobs";
5731
+ function createE2bJobHandle(input) {
5732
+ const fs14 = createE2bLogStreamFs(input.sandbox);
5733
+ return {
5734
+ id: input.jobId,
5735
+ envId: input.envId,
5736
+ tool: input.tool,
5737
+ argv: input.argv,
5738
+ async status() {
5739
+ const exit = await readExitCode(input.sandbox, input.jobId);
5740
+ if (exit !== null) {
5741
+ return "exited";
5742
+ }
5743
+ const processes = await input.sandbox.commands.list();
5744
+ const isRunning = input.pid === void 0 ? processes.some((process2) => processMentionsJob(process2, input.jobId)) : processes.some((process2) => process2.pid === input.pid);
5745
+ return isRunning ? "running" : "lost";
5746
+ },
5747
+ stream(opts = {}) {
5748
+ return streamLogFile({ fs: fs14 }, input.jobId, opts);
5749
+ },
5750
+ async wait() {
5751
+ const result = await waitForExit({ fs: fs14 }, input.jobId);
5752
+ const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
5753
+ if (preserveMs > 0) {
5754
+ await input.sandbox.setTimeout(preserveMs);
5755
+ }
5756
+ return result;
5757
+ },
5758
+ async kill() {
5759
+ const pids = input.pid === void 0 ? (await input.sandbox.commands.list()).filter((process2) => processMentionsJob(process2, input.jobId)).map((process2) => process2.pid) : [input.pid];
5760
+ await Promise.all(pids.map((pid) => input.sandbox.commands.kill(pid)));
5761
+ }
5762
+ };
5763
+ }
5764
+ function createE2bLogStreamFs(sandbox) {
5765
+ return {
5766
+ promises: {
5767
+ async readFile(filePath) {
5768
+ return Buffer.from(await sandbox.files.read(filePath, { format: "bytes" }));
5769
+ },
5770
+ async stat(filePath) {
5771
+ const result = await sandbox.commands.run(
5772
+ `stat -c %Y ${shellQuote3(filePath)} 2>/dev/null || stat -f %m ${shellQuote3(filePath)}`
5773
+ );
5774
+ if (!("stdout" in result)) {
5775
+ throw new Error(`Unable to stat ${filePath}`);
5776
+ }
5777
+ const seconds = Number(result.stdout?.trim());
5778
+ if (!Number.isFinite(seconds)) {
5779
+ throw new Error(`Unable to stat ${filePath}`);
5780
+ }
5781
+ return { mtimeMs: seconds * 1e3 };
5782
+ }
5783
+ },
5784
+ watch(filePath, listener) {
5785
+ let closed = false;
5786
+ let stop = null;
5787
+ void sandbox.files.watchDir(path28.dirname(filePath), listener, { recursive: false }).then((handle) => {
5788
+ if (closed) {
5789
+ void handle.stop();
5790
+ return;
5791
+ }
5792
+ stop = () => {
5793
+ void handle.stop();
5794
+ };
5795
+ });
5796
+ return {
5797
+ close() {
5798
+ closed = true;
5799
+ stop?.();
5800
+ }
5801
+ };
5802
+ }
5803
+ };
5804
+ }
5805
+ function processMentionsJob(process2, jobId) {
5806
+ const needle = `/tmp/poe-jobs/${jobId}`;
5807
+ return process2.cmd.includes(needle) || process2.args.some((arg) => arg.includes(needle));
5808
+ }
5809
+ function shellQuote3(value) {
5810
+ return `'${value.replaceAll("'", "'\\''")}'`;
5811
+ }
5812
+ async function readExitCode(sandbox, jobId) {
5813
+ try {
5814
+ const contents = await sandbox.files.read(`${JOB_DIR2}/${jobId}.exit`);
5815
+ const exitCode = Number(contents.trim());
5816
+ return Number.isInteger(exitCode) ? exitCode : null;
5817
+ } catch {
5818
+ return null;
5819
+ }
5820
+ }
5821
+
5822
+ // packages/runner-e2b/src/opened-env.ts
5823
+ var REMOTE_COMMAND_STDERR_TAIL_SIZE = 30;
5824
+ function createOpenedE2bEnv(input) {
5825
+ const hostRunner = input.spec.hostRunner ?? createHostRunner();
5826
+ const hostWorkspaceDir = path29.resolve(input.spec.cwd);
5827
+ const sandboxWorkspaceDir = normalizeSandboxWorkspaceDir(input.runtime.workspace_dir);
5828
+ let lastProcess = null;
5829
+ let detachedJobContext = null;
5830
+ const mapWorkspaceCwd = (cwd) => {
5831
+ if (cwd === void 0) {
5832
+ return void 0;
5833
+ }
5834
+ if (path29.isAbsolute(cwd) && path29.resolve(cwd) === hostWorkspaceDir) {
5835
+ return sandboxWorkspaceDir;
5836
+ }
5837
+ return cwd;
5838
+ };
5839
+ const attachedJobId = input.spec.detachedJobId;
5840
+ const env = {
5841
+ id: input.sandbox.sandboxId,
5842
+ job: attachedJobId ? createE2bJobHandle({
5843
+ sandbox: input.sandbox,
5844
+ envId: input.sandbox.sandboxId,
5845
+ jobId: attachedJobId,
5846
+ tool: input.spec.jobLabel.tool,
5847
+ argv: input.spec.jobLabel.argv,
5848
+ preserveAfterExitHours: input.runtime.preserve_after_exit_hours ?? 24
5849
+ }) : null,
5850
+ fs: createE2bLogStreamFs(input.sandbox),
5851
+ setDetachedJobContext(context) {
5852
+ detachedJobContext = context;
5853
+ },
5854
+ async uploadWorkspace() {
5855
+ if (input.spec.runner?.sync === "none") {
5856
+ return { files: 0, bytes: 0, skipped: [] };
5857
+ }
5858
+ const tempDir = mkdtempSync2(path29.join(tmpdir2(), "poe-e2b-upload-"));
5859
+ const archivePath = path29.join(tempDir, "workspace.tar");
5860
+ try {
5861
+ await runOrThrow2(hostRunner, {
5862
+ command: "tar",
5863
+ args: [
5864
+ ...input.spec.uploadIgnoreFiles.flatMap((ignored) => ["--exclude", ignored]),
5865
+ "-cf",
5866
+ archivePath,
5867
+ "-C",
5868
+ input.spec.cwd,
5869
+ "."
5870
+ ],
5871
+ stdout: "pipe",
5872
+ stderr: "pipe"
5873
+ });
5874
+ await input.sandbox.files.write(
5875
+ "/tmp/poe-workspace-upload.tar",
5876
+ toArrayBuffer(await readFile12(archivePath))
5877
+ );
5878
+ await runRemoteOrThrow(
5879
+ input.sandbox,
5880
+ createUploadWorkspaceCommand(sandboxWorkspaceDir)
5881
+ );
5882
+ return { files: 0, bytes: 0, skipped: [] };
5883
+ } finally {
5884
+ rmSync2(tempDir, { recursive: true, force: true });
5885
+ }
5886
+ },
5887
+ async downloadWorkspace(opts) {
5888
+ if (input.spec.runner?.sync === "upload" || input.spec.runner?.sync === "none") {
5889
+ return { files: 0, bytes: 0, conflicts: [] };
5890
+ }
5891
+ const tempDir = mkdtempSync2(path29.join(tmpdir2(), "poe-e2b-download-"));
5892
+ const archivePath = path29.join(tempDir, "workspace.tar");
5893
+ try {
5894
+ await runRemoteOrThrow(
5895
+ input.sandbox,
5896
+ `tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote4(sandboxWorkspaceDir)} .`
5897
+ );
5898
+ const archive = await input.sandbox.files.read("/tmp/poe-workspace-download.tar", {
5899
+ format: "bytes"
5900
+ });
5901
+ await writeFile7(archivePath, Buffer.from(archive));
5902
+ await runOrThrow2(hostRunner, {
5903
+ command: "tar",
5904
+ args: [
5905
+ opts.conflictPolicy === "refuse" ? "-xkf" : "-xf",
5906
+ archivePath,
5907
+ "-C",
5908
+ input.spec.cwd
5909
+ ],
5910
+ stdout: "pipe",
5911
+ stderr: "pipe"
5912
+ });
5913
+ return { files: 0, bytes: archive.byteLength, conflicts: [] };
5914
+ } finally {
5915
+ rmSync2(tempDir, { recursive: true, force: true });
5916
+ }
5917
+ },
5918
+ exec(spec) {
5919
+ const handle = runE2bCommand(input.sandbox, {
5920
+ ...spec,
5921
+ cwd: mapWorkspaceCwd(spec.cwd),
5922
+ env: resolveSandboxCommandEnv(spec.env)
5923
+ });
5924
+ lastProcess = { started: handle.started };
5925
+ return handle;
5926
+ },
5927
+ async detach() {
5928
+ if (detachedJobContext === null) {
5929
+ throw new Error("Cannot detach E2B environment before a job context is registered.");
5930
+ }
5931
+ if (lastProcess === null) {
5932
+ throw new Error("Cannot detach E2B environment before a command is running.");
5933
+ }
5934
+ const command = await lastProcess.started;
5935
+ const preserveAfterExitHours = input.runtime.preserve_after_exit_hours ?? 24;
5936
+ const preserveMs = preserveAfterExitHours * 60 * 60 * 1e3;
5937
+ if (preserveMs > 0) {
5938
+ await input.sandbox.setTimeout(preserveMs);
5939
+ }
5940
+ return createE2bJobHandle({
5941
+ sandbox: input.sandbox,
5942
+ envId: input.sandbox.sandboxId,
5943
+ jobId: detachedJobContext.id,
5944
+ tool: detachedJobContext.tool,
5945
+ argv: detachedJobContext.argv,
5946
+ pid: command.pid,
5947
+ preserveAfterExitHours
5948
+ });
5949
+ },
5950
+ shell() {
5951
+ const shellSpec = input.spec.shellSpec;
5952
+ const command = shellSpec?.command ?? input.spec.env.SHELL ?? "sh";
5953
+ return runE2bPty(input.sandbox, {
5954
+ command,
5955
+ ...shellSpec?.args ? { args: shellSpec.args } : {},
5956
+ cwd: mapWorkspaceCwd(shellSpec?.cwd ?? input.spec.cwd),
5957
+ env: resolveSandboxCommandEnv(
5958
+ shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env
5959
+ ),
5960
+ stdin: "inherit",
5961
+ stdout: "inherit",
5962
+ stderr: "inherit",
5963
+ tty: true
5964
+ });
5965
+ },
5966
+ async close() {
5967
+ await input.sandbox.kill();
5968
+ }
5969
+ };
5970
+ return env;
5971
+ }
5972
+ function runE2bCommand(sandbox, spec) {
5973
+ const stdout = spec.stdout === "inherit" ? null : new PassThrough();
5974
+ const stderr = spec.stderr === "inherit" ? null : new PassThrough();
5975
+ let e2bHandle = null;
5976
+ const command = shellCommand([spec.command, ...spec.args ?? []]);
5977
+ const started = sandbox.commands.run(command, {
5978
+ background: true,
5979
+ cwd: spec.cwd,
5980
+ envs: spec.env,
5981
+ stdin: spec.stdin === "pipe",
5982
+ onStdout(data) {
5983
+ stdout?.write(data);
5984
+ if (spec.stdout === "inherit") {
5985
+ process.stdout.write(data);
5986
+ }
5987
+ },
5988
+ onStderr(data) {
5989
+ stderr?.write(data);
5990
+ if (spec.stderr === "inherit") {
5991
+ process.stderr.write(data);
5992
+ }
5993
+ }
5994
+ });
5995
+ const stdin = spec.stdin === "pipe" ? new Writable2({
5996
+ write(chunk, _encoding, callback) {
5997
+ started.then(
5998
+ (handle) => sandbox.commands.sendStdin(
5999
+ handle.pid,
6000
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))
6001
+ )
6002
+ ).then(() => callback(), callback);
6003
+ },
6004
+ final(callback) {
6005
+ if (sandbox.commands.closeStdin === void 0) {
6006
+ callback();
6007
+ return;
6008
+ }
6009
+ started.then((handle) => sandbox.commands.closeStdin(handle.pid)).then(() => callback(), callback);
6010
+ }
6011
+ }) : null;
6012
+ const result = started.then((handle) => {
6013
+ e2bHandle = handle;
6014
+ return handle.wait();
6015
+ }).then(
6016
+ (result2) => {
6017
+ stdout?.end();
6018
+ stderr?.end();
6019
+ return { exitCode: result2.exitCode ?? 0 };
6020
+ },
6021
+ (error2) => {
6022
+ stdout?.end();
6023
+ stderr?.end();
6024
+ if (isExitError(error2)) {
6025
+ return { exitCode: error2.exitCode };
6026
+ }
6027
+ return { exitCode: 1 };
6028
+ }
6029
+ );
6030
+ return {
6031
+ get pid() {
6032
+ return e2bHandle?.pid ?? null;
6033
+ },
6034
+ stdin,
6035
+ stdout,
6036
+ stderr,
6037
+ result,
6038
+ kill() {
6039
+ void e2bHandle?.kill();
6040
+ },
6041
+ get e2bHandle() {
6042
+ return e2bHandle;
6043
+ },
6044
+ started
6045
+ };
6046
+ }
6047
+ function runE2bPty(sandbox, spec) {
6048
+ const stdout = new PassThrough();
6049
+ let handleRef = null;
6050
+ const stdin = new Writable2({
6051
+ write(chunk, _encoding, callback) {
6052
+ if (handleRef === null) {
6053
+ callback(new Error("E2B PTY stdin is not ready."));
6054
+ return;
6055
+ }
6056
+ sandbox.pty.sendInput(handleRef.pid, Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))).then(() => callback(), callback);
6057
+ }
6058
+ });
6059
+ const started = sandbox.pty.create({
6060
+ cols: process.stdout.columns || 80,
6061
+ rows: process.stdout.rows || 24,
6062
+ cwd: spec.cwd,
6063
+ envs: spec.env,
6064
+ onData(data) {
6065
+ stdout.write(Buffer.from(data));
6066
+ if (spec.stdout === "inherit") {
6067
+ process.stdout.write(Buffer.from(data));
6068
+ }
6069
+ }
6070
+ });
6071
+ const result = started.then((handle) => {
6072
+ handleRef = handle;
6073
+ return handle.wait();
6074
+ }).then(
6075
+ (result2) => {
6076
+ stdout.end();
6077
+ return { exitCode: result2.exitCode ?? 0 };
6078
+ },
6079
+ () => {
6080
+ stdout.end();
6081
+ return { exitCode: 1 };
6082
+ }
6083
+ );
6084
+ return {
6085
+ get pid() {
6086
+ return handleRef?.pid ?? null;
6087
+ },
6088
+ stdin: spec.stdin === "inherit" ? process.stdin : stdin,
6089
+ stdout: spec.stdout === "inherit" ? null : stdout,
6090
+ stderr: null,
6091
+ result,
6092
+ kill() {
6093
+ void (handleRef === null ? void 0 : sandbox.pty.kill(handleRef.pid));
6094
+ }
6095
+ };
6096
+ }
6097
+ async function runRemoteOrThrow(sandbox, command) {
6098
+ const stdoutTail = createLineTail(REMOTE_COMMAND_STDERR_TAIL_SIZE);
6099
+ const stderrTail = createLineTail(REMOTE_COMMAND_STDERR_TAIL_SIZE);
6100
+ let result;
6101
+ try {
6102
+ result = await sandbox.commands.run(command, {
6103
+ onStdout(data) {
6104
+ stdoutTail.push(data);
6105
+ },
6106
+ onStderr(data) {
6107
+ stderrTail.push(data);
6108
+ }
6109
+ });
6110
+ } catch (error2) {
6111
+ appendRemoteCommandOutput(error2, stdoutTail, stderrTail);
6112
+ if (isCommandExitError(error2)) {
6113
+ throw decorateRemoteCommandError(error2, command, stderrTail.values());
6114
+ }
6115
+ throw error2;
6116
+ }
6117
+ appendRemoteCommandOutput(result, stdoutTail, stderrTail);
6118
+ if ("exitCode" in result && result.exitCode !== 0) {
6119
+ throw decorateRemoteCommandError(
6120
+ new Error(`E2B command failed with exit code ${result.exitCode}`),
6121
+ command,
6122
+ stderrTail.values()
6123
+ );
6124
+ }
6125
+ }
6126
+ function appendRemoteCommandOutput(source, stdoutTail, stderrTail) {
6127
+ if (!source || typeof source !== "object") {
6128
+ return;
6129
+ }
6130
+ const output = source;
6131
+ if (typeof output.stdout === "string") {
6132
+ stdoutTail.push(output.stdout);
6133
+ }
6134
+ if (typeof output.stderr === "string") {
6135
+ stderrTail.push(output.stderr);
6136
+ }
6137
+ }
6138
+ function decorateRemoteCommandError(error2, command, stderrTail) {
6139
+ const original = error2 instanceof Error ? error2 : new Error(String(error2));
6140
+ const tail = stderrTail.length === 0 ? "" : `
6141
+
6142
+ Last stderr output:
6143
+ ${stderrTail.join("\n")}`;
6144
+ const decorated = new Error(`E2B command failed: ${command}
6145
+ ${original.message}${tail}`);
6146
+ decorated.stack = original.stack;
6147
+ decorated.cause = original;
6148
+ return decorated;
6149
+ }
6150
+ function createLineTail(maxLines) {
6151
+ const lines = [];
6152
+ let pending = "";
6153
+ const appendLine = (line) => {
6154
+ lines.push(trimTrailingCarriageReturn(line));
6155
+ while (lines.length > maxLines) {
6156
+ lines.shift();
6157
+ }
6158
+ };
6159
+ return {
6160
+ push(chunk) {
6161
+ pending += chunk;
6162
+ const parts = pending.split("\n");
6163
+ pending = parts.pop() ?? "";
6164
+ for (const line of parts) {
6165
+ appendLine(line);
6166
+ }
6167
+ },
6168
+ values() {
6169
+ const output = [...lines];
6170
+ if (pending.length > 0) {
6171
+ output.push(trimTrailingCarriageReturn(pending));
6172
+ }
6173
+ return output.slice(-maxLines);
6174
+ }
6175
+ };
6176
+ }
6177
+ function trimTrailingCarriageReturn(value) {
6178
+ return value.endsWith("\r") ? value.slice(0, -1) : value;
6179
+ }
6180
+ async function runOrThrow2(runner, spec) {
6181
+ const handle = runner.exec(spec);
6182
+ const stderr = readableToString(handle.stderr);
6183
+ const result = await handle.result;
6184
+ if (result.exitCode !== 0) {
6185
+ throw new Error(
6186
+ `Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}
6187
+ ${await stderr}`
6188
+ );
6189
+ }
6190
+ }
6191
+ function shellCommand(argv) {
6192
+ return argv.map(shellQuote4).join(" ");
6193
+ }
6194
+ function shellQuote4(value) {
6195
+ return `'${value.replaceAll("'", "'\\''")}'`;
6196
+ }
6197
+ function createUploadWorkspaceCommand(sandboxWorkspaceDir) {
6198
+ const quotedWorkspaceDir = shellQuote4(sandboxWorkspaceDir);
6199
+ return [
6200
+ `mkdir -p ${quotedWorkspaceDir} || { command -v sudo >/dev/null 2>&1 && sudo mkdir -p ${quotedWorkspaceDir} && sudo chown "$(id -u):$(id -g)" ${quotedWorkspaceDir}; }`,
6201
+ `test -w ${quotedWorkspaceDir} && tar -xf /tmp/poe-workspace-upload.tar -C ${quotedWorkspaceDir}`
6202
+ ].join("\n");
6203
+ }
6204
+ function resolveSandboxCommandEnv(env) {
6205
+ if (env === void 0) {
6206
+ return void 0;
3240
6207
  }
3241
6208
  return {
3242
- args: modeConfig.args ?? [],
3243
- env: modeConfig.env && Object.keys(modeConfig.env).length > 0 ? modeConfig.env : void 0
6209
+ ...env,
6210
+ HOME: "/home/user"
3244
6211
  };
3245
6212
  }
3246
-
3247
- // packages/agent-defs/src/agents/claude-code.ts
3248
- var claudeCodeAgent = {
3249
- id: "claude-code",
3250
- name: "claude-code",
3251
- label: "Claude Code",
3252
- summary: "Configure Claude Code to route through Poe.",
3253
- aliases: ["claude"],
3254
- binaryName: "claude",
3255
- configPath: "~/.claude/settings.json",
3256
- branding: {
3257
- colors: {
3258
- dark: "#C15F3C",
3259
- light: "#C15F3C"
3260
- }
6213
+ function normalizeSandboxWorkspaceDir(workspaceDir) {
6214
+ const resolvedWorkspaceDir = workspaceDir ?? "/workspace";
6215
+ if (!path29.posix.isAbsolute(resolvedWorkspaceDir)) {
6216
+ throw new Error("E2B runtime workspace_dir must be an absolute sandbox path.");
3261
6217
  }
3262
- };
3263
-
3264
- // packages/agent-defs/src/agents/claude-desktop.ts
3265
- var claudeDesktopAgent = {
3266
- id: "claude-desktop",
3267
- name: "claude-desktop",
3268
- label: "Claude Desktop",
3269
- summary: "Anthropic's official desktop application for Claude",
3270
- configPath: "~/.claude/settings.json",
3271
- branding: {
3272
- colors: {
3273
- dark: "#D97757",
3274
- light: "#D97757"
3275
- }
6218
+ let normalized = path29.posix.normalize(resolvedWorkspaceDir);
6219
+ while (normalized.length > 1 && normalized.endsWith("/")) {
6220
+ normalized = normalized.slice(0, -1);
3276
6221
  }
3277
- };
6222
+ return normalized;
6223
+ }
6224
+ function isExitError(error2) {
6225
+ return Boolean(
6226
+ error2 && typeof error2 === "object" && typeof error2.exitCode === "number"
6227
+ );
6228
+ }
6229
+ function isCommandExitError(error2) {
6230
+ return isExitError(error2) || Boolean(
6231
+ error2 && typeof error2 === "object" && error2.name === "CommandExitError"
6232
+ );
6233
+ }
3278
6234
 
3279
- // packages/agent-defs/src/agents/codex.ts
3280
- var codexAgent = {
3281
- id: "codex",
3282
- name: "codex",
3283
- label: "Codex",
3284
- summary: "Configure Codex to use Poe as the model provider.",
3285
- binaryName: "codex",
3286
- configPath: "~/.codex/config.toml",
3287
- branding: {
3288
- colors: {
3289
- dark: "#D5D9DF",
3290
- light: "#7A7F86"
3291
- }
6235
+ // packages/runner-e2b/src/auth-scope.ts
6236
+ import os4 from "node:os";
6237
+ import { promises as nodeFs4 } from "node:fs";
6238
+ var e2bAuthScope = defineScope("e2b", {
6239
+ api_key: {
6240
+ type: "string",
6241
+ default: "",
6242
+ doc: "E2B API key",
6243
+ env: "E2B_API_KEY"
3292
6244
  }
3293
- };
3294
-
3295
- // packages/agent-defs/src/agents/opencode.ts
3296
- var openCodeAgent = {
3297
- id: "opencode",
3298
- name: "opencode",
3299
- label: "OpenCode CLI",
3300
- summary: "Configure OpenCode CLI to use the Poe API.",
3301
- binaryName: "opencode",
3302
- configPath: "~/.config/opencode/config.json",
3303
- branding: {
3304
- colors: {
3305
- dark: "#4A4F55",
3306
- light: "#2F3338"
3307
- }
6245
+ });
6246
+ async function resolveE2bApiKey(input) {
6247
+ const homeDir = input.homeDir ?? os4.homedir();
6248
+ const fs14 = input.fs ?? nodeFs4;
6249
+ const env = input.env ?? process.env;
6250
+ const document = await readMergedDocument(
6251
+ fs14,
6252
+ resolveConfigPath(homeDir),
6253
+ resolveProjectConfigPath(input.cwd)
6254
+ );
6255
+ const resolved = resolveScope(e2bAuthScope.schema, document.e2b, env);
6256
+ if (resolved.api_key.length === 0) {
6257
+ throw new Error(
6258
+ "No E2B API key. Set E2B_API_KEY or e2b.api_key in ~/.poe-code/config.json."
6259
+ );
3308
6260
  }
3309
- };
3310
-
3311
- // packages/agent-defs/src/agents/kimi.ts
3312
- var kimiAgent = {
3313
- id: "kimi",
3314
- name: "kimi",
3315
- label: "Kimi",
3316
- summary: "Configure Kimi CLI to use Poe API",
3317
- aliases: ["kimi-cli"],
3318
- binaryName: "kimi",
3319
- configPath: "~/.kimi/config.toml",
3320
- branding: {
3321
- colors: {
3322
- dark: "#7B68EE",
3323
- light: "#6A5ACD"
3324
- }
6261
+ return resolved.api_key;
6262
+ }
6263
+
6264
+ // packages/runner-e2b/src/factory.ts
6265
+ var e2bExecutionEnvFactory = {
6266
+ type: "e2b",
6267
+ supportsDetach: true,
6268
+ async open(spec) {
6269
+ const runtime = parseE2bRuntime(spec.runtime);
6270
+ const runtimeCwd = spec.runtimeCwd ?? spec.cwd;
6271
+ const apiKey = await resolveE2bApiKey({ cwd: runtimeCwd });
6272
+ const templateId = runtime.template_id ?? (await buildE2bRuntimeTemplate({
6273
+ runtime,
6274
+ dockerfilePath: path30.resolve(
6275
+ runtimeCwd,
6276
+ runtime.dockerfile ?? path30.join(".poe-code", "Dockerfile")
6277
+ ),
6278
+ buildContext: path30.resolve(runtimeCwd, runtime.build_context ?? "."),
6279
+ state: spec.state,
6280
+ apiKey
6281
+ })).templateId;
6282
+ const sandbox = await createSandbox({
6283
+ apiKey,
6284
+ templateId,
6285
+ env: spec.env,
6286
+ timeoutMinutes: runtime.timeout_minutes
6287
+ });
6288
+ return createOpenedE2bEnv({ sandbox, spec, runtime });
6289
+ },
6290
+ async attach(envId, context) {
6291
+ const cwd = context?.cwd ?? process.cwd();
6292
+ const apiKey = await resolveE2bApiKey({ cwd });
6293
+ const sandbox = await connectSandbox(envId, apiKey);
6294
+ return createOpenedE2bEnv({
6295
+ sandbox,
6296
+ spec: {
6297
+ cwd: context?.cwd ?? "/workspace",
6298
+ runtime: {
6299
+ type: "e2b",
6300
+ build_args: {},
6301
+ mounts: [],
6302
+ workspace_dir: "/workspace",
6303
+ preserve_after_exit_hours: 24
6304
+ },
6305
+ env: {},
6306
+ uploadIgnoreFiles: [],
6307
+ jobLabel: { tool: context?.tool ?? "e2b", argv: context?.argv ?? [] },
6308
+ ...context?.jobId ? { detachedJobId: context.jobId } : {}
6309
+ },
6310
+ runtime: {
6311
+ type: "e2b",
6312
+ build_args: {},
6313
+ mounts: [],
6314
+ workspace_dir: "/workspace",
6315
+ preserve_after_exit_hours: 24
6316
+ }
6317
+ });
3325
6318
  }
3326
6319
  };
3327
-
3328
- // packages/agent-defs/src/agents/goose.ts
3329
- var gooseAgent = {
3330
- id: "goose",
3331
- name: "goose",
3332
- label: "Goose",
3333
- summary: "Block's open-source AI agent with ACP support.",
3334
- binaryName: "goose",
3335
- configPath: "~/.config/goose/config.yaml",
3336
- branding: {
3337
- colors: {
3338
- dark: "#FF6B35",
3339
- light: "#E85D26"
3340
- }
6320
+ function parseE2bRuntime(runtime) {
6321
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
6322
+ throw new Error("e2b runtime must be an object");
3341
6323
  }
3342
- };
3343
-
3344
- // packages/agent-defs/src/agents/poe-agent.ts
3345
- var poeAgentAgent = {
3346
- id: "poe-agent",
3347
- name: "poe-agent",
3348
- label: "Poe Agent",
3349
- summary: "Run one-shot prompts with the built-in Poe agent runtime.",
3350
- configPath: "~/.poe-code/config.json",
3351
- branding: {
3352
- colors: {
3353
- dark: "#A465F7",
3354
- light: "#7A3FD3"
3355
- }
6324
+ const record = runtime;
6325
+ if (record.type !== "e2b") {
6326
+ throw new Error('e2b runtime type must be "e2b"');
3356
6327
  }
3357
- };
6328
+ return record;
6329
+ }
3358
6330
 
3359
- // packages/agent-defs/src/registry.ts
3360
- var allAgents = [
3361
- claudeCodeAgent,
3362
- claudeDesktopAgent,
3363
- codexAgent,
3364
- openCodeAgent,
3365
- kimiAgent,
3366
- gooseAgent,
3367
- poeAgentAgent
3368
- ];
3369
- var lookup = /* @__PURE__ */ new Map();
3370
- for (const agent of allAgents) {
3371
- const values = [agent.id, agent.name, ...agent.aliases ?? []];
3372
- for (const value of values) {
3373
- const normalized = value.toLowerCase();
3374
- if (!lookup.has(normalized)) {
3375
- lookup.set(normalized, agent.id);
6331
+ // packages/runner-e2b/src/index.ts
6332
+ var e2bExecutionEnvFactory2 = e2bExecutionEnvFactory;
6333
+
6334
+ // packages/agent-spawn/src/register-factories.ts
6335
+ registerExecutionEnvFactory(hostExecutionEnvFactory);
6336
+ registerExecutionEnvFactory(dockerExecutionEnvFactory);
6337
+ registerExecutionEnvFactory(e2bExecutionEnvFactory2);
6338
+ if (isVitest()) {
6339
+ registerExecutionEnvFactory(createTestHostExecutionEnvFactory());
6340
+ }
6341
+ function isVitest() {
6342
+ return process.env.VITEST !== void 0 || process.env.VITEST_POOL_ID !== void 0;
6343
+ }
6344
+ function createTestHostExecutionEnvFactory() {
6345
+ return {
6346
+ type: "host",
6347
+ supportsDetach: false,
6348
+ open: ((openSpec) => {
6349
+ return {
6350
+ id: "host",
6351
+ job: null,
6352
+ async uploadWorkspace() {
6353
+ return { files: 0, bytes: 0, skipped: [] };
6354
+ },
6355
+ async downloadWorkspace() {
6356
+ return { files: 0, bytes: 0, conflicts: [] };
6357
+ },
6358
+ exec(spec) {
6359
+ return runHost(spawnChildProcess2, spec);
6360
+ },
6361
+ async detach() {
6362
+ throw new Error(
6363
+ "host runtime does not support detach because host has no addressable env"
6364
+ );
6365
+ },
6366
+ shell() {
6367
+ return runHost(spawnChildProcess2, {
6368
+ command: openSpec.shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
6369
+ args: openSpec.shellSpec?.args,
6370
+ cwd: openSpec.cwd,
6371
+ env: openSpec.shellSpec && "env" in openSpec.shellSpec ? openSpec.shellSpec.env : openSpec.env,
6372
+ stdin: "inherit",
6373
+ stdout: "inherit",
6374
+ stderr: "inherit",
6375
+ tty: true
6376
+ });
6377
+ },
6378
+ async close() {
6379
+ }
6380
+ };
6381
+ }),
6382
+ async attach() {
6383
+ throw new Error("host runtime does not support reattach");
3376
6384
  }
6385
+ };
6386
+ }
6387
+ function runHost(spawnProcess, spec) {
6388
+ const stdin = spec.stdin ?? "ignore";
6389
+ const stdout = spec.stdout ?? "pipe";
6390
+ const stderr = spec.stderr ?? "pipe";
6391
+ const stdio = stdin === "inherit" && stdout === "inherit" && stderr === "inherit" ? "inherit" : [stdin, stdout, stderr];
6392
+ const child = spawnProcess(spec.command, spec.args ?? [], {
6393
+ cwd: spec.cwd,
6394
+ env: spec.env,
6395
+ stdio
6396
+ });
6397
+ const result = new Promise((resolve2) => {
6398
+ child.once("close", (code) => {
6399
+ resolve2({ exitCode: code ?? 1 });
6400
+ });
6401
+ child.once("error", () => {
6402
+ resolve2({ exitCode: 1 });
6403
+ });
6404
+ });
6405
+ const kill = (signal) => {
6406
+ child.kill(signal);
6407
+ };
6408
+ if (spec.signal?.aborted) {
6409
+ kill("SIGTERM");
6410
+ } else {
6411
+ spec.signal?.addEventListener("abort", () => kill("SIGTERM"), { once: true });
3377
6412
  }
6413
+ return {
6414
+ pid: child.pid ?? null,
6415
+ stdin: child.stdin,
6416
+ stdout: child.stdout,
6417
+ stderr: child.stderr,
6418
+ result,
6419
+ kill
6420
+ };
3378
6421
  }
3379
- function resolveAgentId(input) {
3380
- if (!input) {
3381
- return void 0;
6422
+
6423
+ // packages/agent-spawn/src/run-command.ts
6424
+ import { spawn as spawn2 } from "node:child_process";
6425
+
6426
+ // packages/agent-spawn/src/types.ts
6427
+ function resolveModeConfig(modeConfig) {
6428
+ if (Array.isArray(modeConfig)) {
6429
+ return { args: modeConfig };
3382
6430
  }
3383
- return lookup.get(input.toLowerCase());
6431
+ return {
6432
+ args: modeConfig.args ?? [],
6433
+ env: modeConfig.env && Object.keys(modeConfig.env).length > 0 ? modeConfig.env : void 0
6434
+ };
3384
6435
  }
3385
6436
 
3386
6437
  // packages/agent-spawn/src/configs/mcp.ts
@@ -3650,9 +6701,8 @@ function listMcpSupportedAgents() {
3650
6701
  }
3651
6702
 
3652
6703
  // packages/agent-spawn/src/spawn.ts
3653
- import { spawn as spawnChildProcess } from "node:child_process";
3654
6704
  import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
3655
- import path22 from "node:path";
6705
+ import path31 from "node:path";
3656
6706
 
3657
6707
  // packages/agent-spawn/src/configs/resolve-config.ts
3658
6708
  function resolveConfig(agentId) {
@@ -3702,18 +6752,11 @@ function stripModelNamespace(model) {
3702
6752
  }
3703
6753
 
3704
6754
  // packages/agent-spawn/src/spawn.ts
3705
- function createAbortError() {
6755
+ function createAbortError3() {
3706
6756
  const error2 = new Error("Agent spawn aborted");
3707
6757
  error2.name = "AbortError";
3708
6758
  return error2;
3709
6759
  }
3710
- function createActivityTimeoutError(timeoutMs) {
3711
- const error2 = new Error(
3712
- `Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`
3713
- );
3714
- error2.name = "ActivityTimeoutError";
3715
- return error2;
3716
- }
3717
6760
  function resolveCliConfig(agentId) {
3718
6761
  const resolved = resolveConfig(agentId);
3719
6762
  if (!resolved.spawnConfig) {
@@ -3781,9 +6824,9 @@ function buildCliArgs(config, options, stdinMode) {
3781
6824
  }
3782
6825
  return { args, env: mode.env };
3783
6826
  }
3784
- async function spawn2(agentId, options, context) {
6827
+ async function spawn3(agentId, options, context) {
3785
6828
  if (options.signal?.aborted) {
3786
- throw createAbortError();
6829
+ throw createAbortError3();
3787
6830
  }
3788
6831
  const { agentId: resolvedId, binaryName, spawnConfig } = resolveCliConfig(agentId);
3789
6832
  const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
@@ -3795,89 +6838,71 @@ async function spawn2(agentId, options, context) {
3795
6838
  }
3796
6839
  const logFilePath = resolveSpawnLogPath(options);
3797
6840
  const logFd = logFilePath ? openSpawnLog(logFilePath) : void 0;
3798
- const child = spawnChildProcess(binaryName, spawnArgs, {
3799
- cwd: options.cwd,
3800
- stdio: [stdinMode ? "pipe" : "inherit", "pipe", "pipe"],
3801
- ...modeEnv ? { env: { ...process.env, ...modeEnv } } : {}
3802
- });
3803
- if (!child.stdout || !child.stderr) {
3804
- throw new Error(`Failed to spawn "${resolvedId}": missing stdio pipes.`);
3805
- }
3806
- const stdoutStream = child.stdout;
3807
- const stderrStream = child.stderr;
3808
- if (stdinMode) {
3809
- if (!child.stdin) {
3810
- throw new Error(`Failed to spawn "${resolvedId}": missing stdin pipe.`);
3811
- }
3812
- child.stdin.setDefaultEncoding("utf8");
3813
- child.stdin.write(options.prompt);
3814
- child.stdin.end();
3815
- }
3816
- return new Promise((resolve2, reject) => {
3817
- let stdout = "";
3818
- let stderr = "";
3819
- let aborted = false;
3820
- let timedOut = false;
3821
- const onAbort = () => {
3822
- aborted = true;
3823
- child.kill("SIGTERM");
3824
- };
3825
- options.signal?.addEventListener("abort", onAbort, { once: true });
3826
- let activityTimer;
3827
- const resetActivityTimer = options.activityTimeoutMs ? () => {
3828
- if (activityTimer) clearTimeout(activityTimer);
3829
- activityTimer = setTimeout(() => {
3830
- timedOut = true;
3831
- child.kill("SIGTERM");
3832
- }, options.activityTimeoutMs);
3833
- } : void 0;
3834
- resetActivityTimer?.();
3835
- const cleanup = () => {
3836
- options.signal?.removeEventListener("abort", onAbort);
3837
- if (activityTimer) clearTimeout(activityTimer);
3838
- };
3839
- stdoutStream.setEncoding("utf8");
3840
- stdoutStream.on("data", (chunk) => {
3841
- stdout += chunk;
3842
- resetActivityTimer?.();
3843
- if (options.tee?.stdout) options.tee.stdout.write(chunk);
3844
- appendSpawnLog(logFd, chunk);
3845
- });
3846
- stderrStream.setEncoding("utf8");
3847
- stderrStream.on("data", (chunk) => {
3848
- stderr += chunk;
3849
- resetActivityTimer?.();
3850
- if (options.tee?.stderr) options.tee.stderr.write(chunk);
3851
- appendSpawnLog(logFd, chunk);
3852
- });
3853
- child.on("error", (error2) => {
3854
- cleanup();
3855
- closeSpawnLog(logFd);
3856
- if (aborted) {
3857
- reject(createAbortError());
3858
- return;
6841
+ const processEnv = modeEnv ? { ...process.env, ...modeEnv } : void 0;
6842
+ const argv = [binaryName, ...spawnArgs];
6843
+ const execution = resolvePoeCommandExecution({
6844
+ cwd: options.cwd ?? process.cwd(),
6845
+ runtimeConfigCwd: options.runtimeConfigCwd,
6846
+ env: processEnv ?? process.env,
6847
+ argv,
6848
+ tool: resolvedId,
6849
+ runtime: {
6850
+ runtime: options.runtime,
6851
+ runtimeImage: options.runtimeImage,
6852
+ runtimeTemplate: options.runtimeTemplate,
6853
+ detach: options.detach,
6854
+ mountPoeCode: options.mountPoeCode,
6855
+ runnerSync: options.runnerSync
6856
+ },
6857
+ context,
6858
+ openSpec: {
6859
+ execution: {
6860
+ wrapForLogTee: false,
6861
+ stdin: stdinMode ? "pipe" : "inherit",
6862
+ stdout: "pipe",
6863
+ stderr: "pipe",
6864
+ env: processEnv,
6865
+ input: stdinMode ? options.prompt : void 0,
6866
+ captureOutput: true,
6867
+ activityTimeoutMs: options.activityTimeoutMs,
6868
+ onStdout(chunk) {
6869
+ options.tee?.stdout?.write(chunk);
6870
+ appendSpawnLog(logFd, chunk);
6871
+ },
6872
+ onStderr(chunk) {
6873
+ options.tee?.stderr?.write(chunk);
6874
+ appendSpawnLog(logFd, chunk);
6875
+ }
3859
6876
  }
3860
- reject(error2);
6877
+ }
6878
+ });
6879
+ try {
6880
+ const result = await runPoeCommand({
6881
+ factory: execution.factory,
6882
+ openSpec: execution.openSpec,
6883
+ detach: execution.detach,
6884
+ state: execution.state,
6885
+ signal: options.signal
3861
6886
  });
3862
- child.on("close", (code) => {
3863
- cleanup();
3864
- closeSpawnLog(logFd);
3865
- if (aborted) {
3866
- reject(createAbortError());
3867
- return;
3868
- }
3869
- if (timedOut) {
3870
- reject(createActivityTimeoutError(options.activityTimeoutMs));
3871
- return;
3872
- }
3873
- resolve2({
3874
- stdout,
3875
- stderr,
3876
- exitCode: code ?? 1,
6887
+ if (result.kind === "detached") {
6888
+ return {
6889
+ stdout: "",
6890
+ stderr: "",
6891
+ exitCode: 0,
6892
+ detached: { jobId: result.jobId, envId: result.envId },
3877
6893
  ...logFilePath ? { logFile: logFilePath } : {}
3878
- });
3879
- });
3880
- });
6894
+ };
6895
+ }
6896
+ const captured = result;
6897
+ return {
6898
+ stdout: captured.stdout ?? "",
6899
+ stderr: captured.stderr ?? "",
6900
+ exitCode: result.exitCode,
6901
+ ...logFilePath ? { logFile: logFilePath } : {}
6902
+ };
6903
+ } finally {
6904
+ closeSpawnLog(logFd);
6905
+ }
3881
6906
  }
3882
6907
  function resolveSpawnLogPath(options) {
3883
6908
  if (options.logPath) {
@@ -3886,11 +6911,11 @@ function resolveSpawnLogPath(options) {
3886
6911
  if (!options.logDir || !options.logFileName) {
3887
6912
  return void 0;
3888
6913
  }
3889
- return path22.join(options.logDir, options.logFileName);
6914
+ return path31.join(options.logDir, options.logFileName);
3890
6915
  }
3891
6916
  function openSpawnLog(filePath) {
3892
6917
  try {
3893
- mkdirSync(path22.dirname(filePath), { recursive: true });
6918
+ mkdirSync(path31.dirname(filePath), { recursive: true });
3894
6919
  return openSync(filePath, "a");
3895
6920
  } catch {
3896
6921
  return void 0;
@@ -3911,9 +6936,6 @@ function closeSpawnLog(fd) {
3911
6936
  }
3912
6937
  }
3913
6938
 
3914
- // packages/agent-spawn/src/spawn-interactive.ts
3915
- import { spawn as spawnChildProcess2 } from "node:child_process";
3916
-
3917
6939
  // packages/design-system/src/tokens/colors.ts
3918
6940
  import chalk from "chalk";
3919
6941
  var dark = {
@@ -4289,7 +7311,7 @@ import chalk8 from "chalk";
4289
7311
 
4290
7312
  // packages/design-system/src/dashboard/terminal.ts
4291
7313
  import readline from "node:readline";
4292
- import { PassThrough } from "node:stream";
7314
+ import { PassThrough as PassThrough2 } from "node:stream";
4293
7315
 
4294
7316
  // packages/design-system/src/prompts/index.ts
4295
7317
  import chalk15 from "chalk";
@@ -4321,9 +7343,9 @@ import chalk16 from "chalk";
4321
7343
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
4322
7344
 
4323
7345
  // packages/agent-spawn/src/acp/replay.ts
4324
- import path23 from "node:path";
7346
+ import path32 from "node:path";
4325
7347
  import { homedir as homedir2 } from "node:os";
4326
- import { open as open2, readdir as readdir4 } from "node:fs/promises";
7348
+ import { open as open2, readdir as readdir5 } from "node:fs/promises";
4327
7349
  import { createInterface } from "node:readline";
4328
7350
 
4329
7351
  // packages/poe-acp-client/src/acp-client.ts
@@ -4339,17 +7361,14 @@ import * as fsPromises3 from "node:fs/promises";
4339
7361
  import { homedir } from "node:os";
4340
7362
  import { join } from "node:path";
4341
7363
 
4342
- // packages/agent-spawn/src/acp/spawn.ts
4343
- import { spawn as spawnChildProcess4 } from "node:child_process";
4344
-
4345
7364
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
4346
- import path24 from "node:path";
7365
+ import path33 from "node:path";
4347
7366
  import { homedir as homedir3 } from "node:os";
4348
7367
  import { mkdir as mkdir5, open as open3 } from "node:fs/promises";
4349
7368
 
4350
7369
  // packages/memory/src/tokens.ts
4351
7370
  import * as fs10 from "node:fs/promises";
4352
- import path25 from "node:path";
7371
+ import path34 from "node:path";
4353
7372
 
4354
7373
  // packages/tokenfill/dist/tokenizer.js
4355
7374
  import { get_encoding } from "tiktoken";
@@ -4390,7 +7409,7 @@ function countTokens(text4) {
4390
7409
  }
4391
7410
 
4392
7411
  // packages/tokenfill/dist/corpus.js
4393
- import { readdirSync, readFileSync } from "node:fs";
7412
+ import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
4394
7413
  import { dirname, join as join2 } from "node:path";
4395
7414
  import { fileURLToPath } from "node:url";
4396
7415
  var CORPUS_ARTICLE_SEPARATOR = "\n\n";
@@ -4403,7 +7422,7 @@ function loadBuiltInCorpusArticles() {
4403
7422
  if (corpusFileNames.length === 0) {
4404
7423
  throw new Error(`No built-in corpus markdown files found in ${corpusDirectoryPath}`);
4405
7424
  }
4406
- return corpusFileNames.map((fileName) => readFileSync(join2(corpusDirectoryPath, fileName), "utf8").trim());
7425
+ return corpusFileNames.map((fileName) => readFileSync2(join2(corpusDirectoryPath, fileName), "utf8").trim());
4407
7426
  }
4408
7427
  var BUILT_IN_CORPUS_ARTICLES = loadBuiltInCorpusArticles();
4409
7428
 
@@ -4433,11 +7452,11 @@ async function computeTokenStats(root) {
4433
7452
  }
4434
7453
  }
4435
7454
  }
4436
- const repoRoot = path25.resolve(root, "..", "..");
7455
+ const repoRoot = path34.resolve(root, "..", "..");
4437
7456
  let sourceTokens = 0;
4438
7457
  const missingSources = [];
4439
7458
  for (const sourcePath of sourcePaths) {
4440
- const absPath = path25.isAbsolute(sourcePath) ? sourcePath : path25.resolve(repoRoot, sourcePath);
7459
+ const absPath = path34.isAbsolute(sourcePath) ? sourcePath : path34.resolve(repoRoot, sourcePath);
4441
7460
  try {
4442
7461
  const content = await fs10.readFile(absPath, "utf8");
4443
7462
  sourceTokens += countTokens(content);
@@ -4488,10 +7507,10 @@ function resolveRunners(overrides) {
4488
7507
  async function ingest(root, opts, runners) {
4489
7508
  const resolved = resolveRunners(runners);
4490
7509
  const source = await materializeSource(opts.source);
4491
- const indexMdBytes = await fs11.readFile(path26.join(root, MEMORY_INDEX_RELPATH));
7510
+ const indexMdBytes = await fs11.readFile(path35.join(root, MEMORY_INDEX_RELPATH));
4492
7511
  const configOptions = {
4493
7512
  fs: fs11,
4494
- filePath: path26.join(inferRepoRoot(root), "poe-code.json")
7513
+ filePath: path35.join(inferRepoRoot(root), "poe-code.json")
4495
7514
  };
4496
7515
  const agentId = await resolveAgent(configOptions, opts.agent ?? null) ?? opts.agent ?? "claude-code";
4497
7516
  const key = resolved.computeIngestKey({
@@ -4529,7 +7548,7 @@ async function ingest(root, opts, runners) {
4529
7548
  let timeoutError;
4530
7549
  try {
4531
7550
  const result = await runWithTimeout(
4532
- spawn2(agentId, { prompt }),
7551
+ spawn3(agentId, { prompt }),
4533
7552
  opts.timeoutMs ?? await configuredTimeout(configOptions)
4534
7553
  );
4535
7554
  exitCode = result.exitCode;
@@ -4581,7 +7600,7 @@ async function materializeSource(source) {
4581
7600
  throw new Error("URL ingest not implemented yet.");
4582
7601
  }
4583
7602
  function inferRepoRoot(root) {
4584
- return path26.resolve(root, "..", "..");
7603
+ return path35.resolve(root, "..", "..");
4585
7604
  }
4586
7605
  async function runWithTimeout(promise, timeoutMs) {
4587
7606
  return await new Promise((resolve2, reject) => {
@@ -5339,8 +8358,8 @@ function printMcpConfig() {
5339
8358
  }
5340
8359
 
5341
8360
  // packages/agent-skill-config/src/configs.ts
5342
- import os3 from "node:os";
5343
- import path27 from "node:path";
8361
+ import os5 from "node:os";
8362
+ import path36 from "node:path";
5344
8363
  var agentSkillConfigs = {
5345
8364
  "claude-code": {
5346
8365
  globalSkillDir: "~/.claude/skills",
@@ -5359,7 +8378,7 @@ var agentSkillConfigs = {
5359
8378
  localSkillDir: ".agents/skills"
5360
8379
  }
5361
8380
  };
5362
- var supportedAgents = Object.keys(agentSkillConfigs);
8381
+ var supportedAgents2 = Object.keys(agentSkillConfigs);
5363
8382
  function resolveAgentSupport(input, registry = agentSkillConfigs) {
5364
8383
  const resolvedId = resolveAgentId(input);
5365
8384
  if (!resolvedId) {
@@ -5373,8 +8392,8 @@ function resolveAgentSupport(input, registry = agentSkillConfigs) {
5373
8392
  }
5374
8393
 
5375
8394
  // packages/agent-skill-config/src/templates.ts
5376
- import { readFile as readFile12, stat as stat6 } from "node:fs/promises";
5377
- import path28 from "node:path";
8395
+ import { readFile as readFile15, stat as stat6 } from "node:fs/promises";
8396
+ import path37 from "node:path";
5378
8397
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5379
8398
 
5380
8399
  // packages/agent-skill-config/src/apply.ts
@@ -5480,7 +8499,7 @@ var agentMcpConfigs = {
5480
8499
  shape: "goose"
5481
8500
  }
5482
8501
  };
5483
- var supportedAgents2 = Object.keys(agentMcpConfigs);
8502
+ var supportedAgents3 = Object.keys(agentMcpConfigs);
5484
8503
  function resolveAgentSupport2(input, registry = agentMcpConfigs) {
5485
8504
  const resolvedId = resolveAgentId(input);
5486
8505
  if (!resolvedId) {
@@ -5507,7 +8526,7 @@ function resolveConfigPath2(config, platform) {
5507
8526
  }
5508
8527
 
5509
8528
  // packages/agent-mcp-config/src/apply.ts
5510
- import path29 from "node:path";
8529
+ import path38 from "node:path";
5511
8530
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
5512
8531
 
5513
8532
  // packages/agent-mcp-config/src/shapes.ts
@@ -5599,7 +8618,7 @@ function getShapeTransformer(shape) {
5599
8618
 
5600
8619
  // packages/agent-mcp-config/src/apply.ts
5601
8620
  function getConfigDirectory(configPath) {
5602
- return path29.dirname(configPath);
8621
+ return path38.dirname(configPath);
5603
8622
  }
5604
8623
  var UnsupportedAgentError2 = class extends Error {
5605
8624
  constructor(agentId) {
@@ -5625,9 +8644,9 @@ function expandHomePath(configPath, homeDir) {
5625
8644
  return homeDir;
5626
8645
  }
5627
8646
  if (configPath.startsWith("~/")) {
5628
- return path29.join(homeDir, configPath.slice(2));
8647
+ return path38.join(homeDir, configPath.slice(2));
5629
8648
  }
5630
- return path29.join(homeDir, configPath.slice(1));
8649
+ return path38.join(homeDir, configPath.slice(1));
5631
8650
  }
5632
8651
  function parseYamlDocument(content) {
5633
8652
  if (content.trim() === "") {
@@ -5660,7 +8679,7 @@ async function writeYamlConfig(configPath, document, options) {
5660
8679
  return;
5661
8680
  }
5662
8681
  const absolutePath = expandHomePath(configPath, options.homeDir);
5663
- const configDir = path29.dirname(absolutePath);
8682
+ const configDir = path38.dirname(absolutePath);
5664
8683
  await options.fs.mkdir(configDir, { recursive: true });
5665
8684
  await options.fs.writeFile(absolutePath, serializeYamlDocument(document), {
5666
8685
  encoding: "utf8"
@@ -5838,7 +8857,7 @@ async function installMemory(options) {
5838
8857
 
5839
8858
  // packages/memory/src/query.ts
5840
8859
  import * as fs12 from "node:fs/promises";
5841
- import path30 from "node:path";
8860
+ import path39 from "node:path";
5842
8861
  async function queryMemory(root, options) {
5843
8862
  const pages = await listPages(root);
5844
8863
  if (pages.length === 0) {
@@ -5852,11 +8871,11 @@ async function queryMemory(root, options) {
5852
8871
  }
5853
8872
  const configOptions = {
5854
8873
  fs: fs12,
5855
- filePath: path30.join(inferRepoRoot2(root), "poe-code.json")
8874
+ filePath: path39.join(inferRepoRoot2(root), "poe-code.json")
5856
8875
  };
5857
8876
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
5858
8877
  const context = await selectQueryContext(root, options.question, options.budget);
5859
- const result = await spawn2(agentId, { prompt: context.prompt });
8878
+ const result = await spawn3(agentId, { prompt: context.prompt });
5860
8879
  return {
5861
8880
  answer: result.answer,
5862
8881
  citations: result.citations,
@@ -5867,7 +8886,7 @@ async function queryMemory(root, options) {
5867
8886
  }
5868
8887
  async function selectQueryContext(root, question, budget) {
5869
8888
  const [indexText, pages] = await Promise.all([
5870
- fs12.readFile(path30.join(root, MEMORY_INDEX_RELPATH), "utf8"),
8889
+ fs12.readFile(path39.join(root, MEMORY_INDEX_RELPATH), "utf8"),
5871
8890
  listPages(root)
5872
8891
  ]);
5873
8892
  const indexTokens = countTokens(indexText);
@@ -5946,12 +8965,12 @@ function tokenize(text4) {
5946
8965
  return text4.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length > 0);
5947
8966
  }
5948
8967
  function inferRepoRoot2(root) {
5949
- return path30.resolve(root, "..", "..");
8968
+ return path39.resolve(root, "..", "..");
5950
8969
  }
5951
8970
 
5952
8971
  // packages/memory/src/explain.ts
5953
8972
  import * as fs13 from "node:fs/promises";
5954
- import path31 from "node:path";
8973
+ import path40 from "node:path";
5955
8974
  async function explainPage(root, options) {
5956
8975
  const targetPage = await readPageIfPresent(root, options.relPath);
5957
8976
  if (targetPage === void 0) {
@@ -5974,10 +8993,10 @@ async function explainPage(root, options) {
5974
8993
  }
5975
8994
  const configOptions = {
5976
8995
  fs: fs13,
5977
- filePath: path31.join(inferRepoRoot3(root), "poe-code.json")
8996
+ filePath: path40.join(inferRepoRoot3(root), "poe-code.json")
5978
8997
  };
5979
8998
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
5980
- const response = await spawn2(agentId, { prompt });
8999
+ const response = await spawn3(agentId, { prompt });
5981
9000
  return {
5982
9001
  answer: response.answer,
5983
9002
  citations: response.citations,
@@ -6023,7 +9042,7 @@ async function readPageIfPresent(root, relPath) {
6023
9042
  }
6024
9043
  }
6025
9044
  function inferRepoRoot3(root) {
6026
- return path31.resolve(root, "..", "..");
9045
+ return path40.resolve(root, "..", "..");
6027
9046
  }
6028
9047
 
6029
9048
  // packages/memory/src/explain.cli.ts
@@ -6036,9 +9055,9 @@ async function runMemoryExplain(input) {
6036
9055
  }
6037
9056
 
6038
9057
  // packages/memory/src/handle.ts
6039
- import path32 from "node:path";
9058
+ import path41 from "node:path";
6040
9059
  function openMemory(opts) {
6041
- if (!path32.isAbsolute(opts.root)) {
9060
+ if (!path41.isAbsolute(opts.root)) {
6042
9061
  throw new Error(`openMemory: root must be absolute, got ${opts.root}`);
6043
9062
  }
6044
9063
  const root = opts.root;