poe-code 3.0.202 → 3.0.204
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/braintrust.d.ts +3 -0
- package/dist/cli/commands/braintrust.js +77 -0
- package/dist/cli/commands/braintrust.js.map +1 -0
- package/dist/cli/commands/configure.d.ts +1 -0
- package/dist/cli/commands/configure.js +197 -0
- package/dist/cli/commands/configure.js.map +1 -1
- package/dist/cli/commands/experiment.js +51 -7
- package/dist/cli/commands/experiment.js.map +1 -1
- package/dist/cli/commands/harness.d.ts +3 -0
- package/dist/cli/commands/harness.js +260 -0
- package/dist/cli/commands/harness.js.map +1 -0
- package/dist/cli/commands/pipeline.js +58 -24
- package/dist/cli/commands/pipeline.js.map +1 -1
- package/dist/cli/commands/ralph.js +19 -7
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/runtime/build.d.ts +7 -0
- package/dist/cli/commands/runtime/build.js +128 -0
- package/dist/cli/commands/runtime/build.js.map +1 -0
- package/dist/cli/commands/runtime/index.d.ts +3 -0
- package/dist/cli/commands/runtime/index.js +14 -0
- package/dist/cli/commands/runtime/index.js.map +1 -0
- package/dist/cli/commands/runtime/init.d.ts +7 -0
- package/dist/cli/commands/runtime/init.js +39 -0
- package/dist/cli/commands/runtime/init.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/attach.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/attach.js +35 -0
- package/dist/cli/commands/runtime/jobs/attach.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/index.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/index.js +16 -0
- package/dist/cli/commands/runtime/jobs/index.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/logs.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/logs.js +27 -0
- package/dist/cli/commands/runtime/jobs/logs.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/ls.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/ls.js +60 -0
- package/dist/cli/commands/runtime/jobs/ls.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/sandbox.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/sandbox.js +15 -0
- package/dist/cli/commands/runtime/jobs/sandbox.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/shared.d.ts +22 -0
- package/dist/cli/commands/runtime/jobs/shared.js +124 -0
- package/dist/cli/commands/runtime/jobs/shared.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/stop.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/stop.js +31 -0
- package/dist/cli/commands/runtime/jobs/stop.js.map +1 -0
- package/dist/cli/commands/runtime/jobs/sync.d.ts +3 -0
- package/dist/cli/commands/runtime/jobs/sync.js +25 -0
- package/dist/cli/commands/runtime/jobs/sync.js.map +1 -0
- package/dist/cli/commands/runtime/shared.d.ts +20 -0
- package/dist/cli/commands/runtime/shared.js +69 -0
- package/dist/cli/commands/runtime/shared.js.map +1 -0
- package/dist/cli/commands/runtime/templates/clear.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/clear.js +53 -0
- package/dist/cli/commands/runtime/templates/clear.js.map +1 -0
- package/dist/cli/commands/runtime/templates/index.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/index.js +10 -0
- package/dist/cli/commands/runtime/templates/index.js.map +1 -0
- package/dist/cli/commands/runtime/templates/ls.d.ts +3 -0
- package/dist/cli/commands/runtime/templates/ls.js +52 -0
- package/dist/cli/commands/runtime/templates/ls.js.map +1 -0
- package/dist/cli/commands/runtime-options.d.ts +11 -0
- package/dist/cli/commands/runtime-options.js +26 -0
- package/dist/cli/commands/runtime-options.js.map +1 -0
- package/dist/cli/commands/spawn.js +36 -7
- package/dist/cli/commands/spawn.js.map +1 -1
- package/dist/cli/program.js +17 -1
- package/dist/cli/program.js.map +1 -1
- package/dist/index.js +70879 -47381
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +3486 -852
- package/dist/providers/claude-code.js.map +4 -4
- package/dist/providers/codex.js +3486 -852
- package/dist/providers/codex.js.map +4 -4
- package/dist/providers/goose.js +3432 -798
- package/dist/providers/goose.js.map +4 -4
- package/dist/providers/kimi.js +3486 -852
- package/dist/providers/kimi.js.map +4 -4
- package/dist/providers/opencode.js +3486 -852
- package/dist/providers/opencode.js.map +4 -4
- package/dist/providers/poe-agent.js +20641 -17147
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/providers/spawn-options.d.ts +10 -1
- package/dist/sdk/experiment.js +6 -0
- package/dist/sdk/experiment.js.map +1 -1
- package/dist/sdk/ralph.js +108 -11
- package/dist/sdk/ralph.js.map +1 -1
- package/dist/sdk/spawn.js +27 -4
- package/dist/sdk/spawn.js.map +1 -1
- package/dist/sdk/types.d.ts +23 -1
- package/dist/utils/command-checks.js +2 -29
- package/dist/utils/command-checks.js.map +1 -1
- package/package.json +12 -7
- package/packages/design-system/dist/components/help-formatter-plain.d.ts +4 -0
- package/packages/design-system/dist/components/help-formatter-plain.js +132 -0
- package/packages/design-system/dist/components/help-formatter.d.ts +13 -0
- package/packages/design-system/dist/components/help-formatter.js +116 -7
- package/packages/design-system/dist/components/index.d.ts +2 -2
- package/packages/design-system/dist/components/index.js +1 -1
- package/packages/design-system/dist/components/text.d.ts +1 -0
- package/packages/design-system/dist/components/text.js +8 -0
- package/packages/design-system/dist/index.d.ts +3 -2
- package/packages/design-system/dist/index.js +2 -1
- package/packages/memory/dist/index.js +3406 -387
- package/packages/memory/dist/index.js.map +4 -4
- package/packages/superintendent/dist/commands/run.d.ts +45 -0
- package/packages/superintendent/dist/commands/run.js +133 -38
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +36 -0
- package/packages/superintendent/dist/runtime/agent-runner.d.ts +31 -0
- package/packages/superintendent/dist/runtime/agent-runner.js +121 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +7 -1
- package/packages/superintendent/dist/runtime/loop.js +3 -11
- package/packages/superintendent/dist/runtime/run-builder.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-builder.js +3 -25
- package/packages/superintendent/dist/runtime/run-inspector.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-inspector.js +3 -25
- package/packages/superintendent/dist/runtime/run-owner-review.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-owner-review.js +3 -25
- package/packages/superintendent/dist/runtime/run-superintendent.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-superintendent.js +3 -25
|
@@ -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,
|
|
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,
|
|
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,
|
|
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(
|
|
666
|
+
const fullPath = buildPath(path42, key);
|
|
453
667
|
if (isPlainObject(winningValue)) {
|
|
454
|
-
const merged = mergeObjectLayers(objectLayers, [...
|
|
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(
|
|
480
|
-
return [...
|
|
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(
|
|
1115
|
-
const ext = getExtension(
|
|
1328
|
+
function detectFormat2(path42) {
|
|
1329
|
+
const ext = getExtension(path42);
|
|
1116
1330
|
return extensionMap[ext];
|
|
1117
1331
|
}
|
|
1118
|
-
function getExtension(
|
|
1119
|
-
const lastDot =
|
|
1332
|
+
function getExtension(path42) {
|
|
1333
|
+
const lastDot = path42.lastIndexOf(".");
|
|
1120
1334
|
if (lastDot === -1) {
|
|
1121
1335
|
return "";
|
|
1122
1336
|
}
|
|
1123
|
-
return
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
1943
|
-
|
|
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
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
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
|
|
1966
|
-
|
|
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
|
-
|
|
1970
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2345
|
+
process.kill(pid, 0);
|
|
2346
|
+
return true;
|
|
1980
2347
|
} catch (error2) {
|
|
1981
|
-
|
|
1982
|
-
throw error2;
|
|
1983
|
-
}
|
|
2348
|
+
return !hasErrorCode(error2, "ESRCH");
|
|
1984
2349
|
}
|
|
1985
2350
|
}
|
|
1986
|
-
function
|
|
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
|
-
|
|
2011
|
-
|
|
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
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
3195
|
+
function sleep2(ms) {
|
|
2388
3196
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2389
3197
|
}
|
|
2390
|
-
function
|
|
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
|
|
3217
|
+
function isPidRunning2(pid) {
|
|
2410
3218
|
try {
|
|
2411
3219
|
process.kill(pid, 0);
|
|
2412
3220
|
return true;
|
|
2413
3221
|
} catch (error2) {
|
|
2414
|
-
return !
|
|
3222
|
+
return !hasErrorCode3(error2, "ESRCH");
|
|
2415
3223
|
}
|
|
2416
3224
|
}
|
|
2417
|
-
async function
|
|
3225
|
+
async function removeLockFile2(fs14, lockPath) {
|
|
2418
3226
|
try {
|
|
2419
3227
|
await fs14.unlink(lockPath);
|
|
2420
3228
|
} catch (error2) {
|
|
2421
|
-
if (!
|
|
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 (
|
|
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 ??
|
|
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 ??
|
|
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
|
|
3259
|
+
await removeLockFile2(fs14, lockPath);
|
|
2452
3260
|
}
|
|
2453
3261
|
} catch (error2) {
|
|
2454
|
-
if (!
|
|
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
|
|
3271
|
+
await removeLockFile2(fs14, lockPath);
|
|
2464
3272
|
continue;
|
|
2465
3273
|
}
|
|
2466
3274
|
if (attempt < retries) {
|
|
2467
|
-
await
|
|
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
|
|
3232
|
-
|
|
3233
|
-
// packages/agent-spawn/src/
|
|
3234
|
-
import { spawn } from "node:child_process";
|
|
3235
|
-
|
|
3236
|
-
// packages/agent-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
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
|
-
|
|
3243
|
-
|
|
6209
|
+
...env,
|
|
6210
|
+
HOME: "/home/user"
|
|
3244
6211
|
};
|
|
3245
6212
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
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
|
-
|
|
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/
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
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
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
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
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
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
|
-
|
|
3329
|
-
|
|
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
|
-
|
|
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/
|
|
3360
|
-
var
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
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
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6827
|
+
async function spawn3(agentId, options, context) {
|
|
3785
6828
|
if (options.signal?.aborted) {
|
|
3786
|
-
throw
|
|
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
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
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
|
|
6914
|
+
return path31.join(options.logDir, options.logFileName);
|
|
3890
6915
|
}
|
|
3891
6916
|
function openSpawnLog(filePath) {
|
|
3892
6917
|
try {
|
|
3893
|
-
mkdirSync(
|
|
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
|
|
7346
|
+
import path32 from "node:path";
|
|
4325
7347
|
import { homedir as homedir2 } from "node:os";
|
|
4326
|
-
import { open as open2, readdir as
|
|
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
|
|
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
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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(
|
|
7510
|
+
const indexMdBytes = await fs11.readFile(path35.join(root, MEMORY_INDEX_RELPATH));
|
|
4492
7511
|
const configOptions = {
|
|
4493
7512
|
fs: fs11,
|
|
4494
|
-
filePath:
|
|
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
|
-
|
|
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
|
|
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
|
|
5343
|
-
import
|
|
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
|
|
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
|
|
5377
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8647
|
+
return path38.join(homeDir, configPath.slice(2));
|
|
5629
8648
|
}
|
|
5630
|
-
return
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
9058
|
+
import path41 from "node:path";
|
|
6040
9059
|
function openMemory(opts) {
|
|
6041
|
-
if (!
|
|
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;
|