poe-code 3.0.201 → 3.0.203
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/experiment.js +11 -4
- package/dist/cli/commands/experiment.js.map +1 -1
- package/dist/cli/commands/ralph.js +12 -5
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/runtime-options.d.ts +10 -0
- package/dist/cli/commands/runtime-options.js +23 -0
- package/dist/cli/commands/runtime-options.js.map +1 -0
- package/dist/cli/commands/spawn.js +9 -3
- package/dist/cli/commands/spawn.js.map +1 -1
- package/dist/index.js +22975 -20513
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +2741 -1706
- package/dist/providers/claude-code.js.map +4 -4
- package/dist/providers/codex.js +2770 -1735
- package/dist/providers/codex.js.map +4 -4
- package/dist/providers/goose.js +2640 -1605
- package/dist/providers/goose.js.map +4 -4
- package/dist/providers/kimi.js +2740 -1705
- package/dist/providers/kimi.js.map +4 -4
- package/dist/providers/opencode.js +2741 -1706
- package/dist/providers/opencode.js.map +4 -4
- package/dist/providers/poe-agent.js +19779 -17296
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/providers/spawn-options.d.ts +6 -0
- package/dist/sdk/experiment.js +5 -0
- package/dist/sdk/experiment.js.map +1 -1
- package/dist/sdk/ralph.js +5 -0
- package/dist/sdk/ralph.js.map +1 -1
- package/dist/sdk/spawn.js +17 -1
- package/dist/sdk/spawn.js.map +1 -1
- package/dist/sdk/types.d.ts +11 -0
- package/package.json +1 -1
- package/packages/memory/dist/index.js +2642 -444
- package/packages/memory/dist/index.js.map +4 -4
- package/packages/superintendent/dist/commands/run.d.ts +35 -0
- package/packages/superintendent/dist/commands/run.js +49 -1
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +30 -0
- package/packages/superintendent/dist/runtime/agent-runner.d.ts +30 -0
- package/packages/superintendent/dist/runtime/agent-runner.js +119 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +6 -1
- package/packages/superintendent/dist/runtime/loop.js +3 -11
- package/packages/superintendent/dist/runtime/run-builder.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-builder.js +3 -25
- package/packages/superintendent/dist/runtime/run-inspector.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-inspector.js +3 -25
- package/packages/superintendent/dist/runtime/run-owner-review.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-owner-review.js +3 -25
- package/packages/superintendent/dist/runtime/run-superintendent.d.ts +1 -0
- package/packages/superintendent/dist/runtime/run-superintendent.js +3 -25
|
@@ -35,7 +35,405 @@ function assertSafeRelPath(input) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// packages/memory/src/resolve-root.ts
|
|
38
|
-
import
|
|
38
|
+
import path11 from "node:path";
|
|
39
|
+
|
|
40
|
+
// packages/poe-code-config/src/runtime.ts
|
|
41
|
+
import { existsSync } from "node:fs";
|
|
42
|
+
import path2 from "node:path";
|
|
43
|
+
var defaultWorkspaceExclude = [
|
|
44
|
+
".git",
|
|
45
|
+
"node_modules",
|
|
46
|
+
"dist",
|
|
47
|
+
".turbo",
|
|
48
|
+
".next",
|
|
49
|
+
".poe-code/state.json"
|
|
50
|
+
];
|
|
51
|
+
var runtimeConfigScope = {
|
|
52
|
+
scope: "runtime",
|
|
53
|
+
schema: {
|
|
54
|
+
type: {
|
|
55
|
+
type: "string",
|
|
56
|
+
default: "host",
|
|
57
|
+
doc: "Runtime backend: host, docker, or e2b"
|
|
58
|
+
},
|
|
59
|
+
build_args: {
|
|
60
|
+
type: "json",
|
|
61
|
+
default: {},
|
|
62
|
+
parse: parseBuildArgs,
|
|
63
|
+
doc: "Build arguments passed to the runtime image build"
|
|
64
|
+
},
|
|
65
|
+
mounts: {
|
|
66
|
+
type: "json",
|
|
67
|
+
default: [],
|
|
68
|
+
parse: parseMounts,
|
|
69
|
+
doc: "Additional runtime mounts"
|
|
70
|
+
},
|
|
71
|
+
runner: {
|
|
72
|
+
type: "json",
|
|
73
|
+
default: createDefaultRunnerScope(),
|
|
74
|
+
parse: parseRunner,
|
|
75
|
+
doc: "Runner process and workspace transfer settings"
|
|
76
|
+
},
|
|
77
|
+
link: {
|
|
78
|
+
type: "string",
|
|
79
|
+
default: "",
|
|
80
|
+
doc: "Informational link for the runtime definition"
|
|
81
|
+
},
|
|
82
|
+
image: {
|
|
83
|
+
type: "string",
|
|
84
|
+
default: "",
|
|
85
|
+
doc: "Prebuilt Docker image"
|
|
86
|
+
},
|
|
87
|
+
dockerfile: {
|
|
88
|
+
type: "string",
|
|
89
|
+
default: "",
|
|
90
|
+
doc: "Path to the Dockerfile used for docker or e2b builds"
|
|
91
|
+
},
|
|
92
|
+
build_context: {
|
|
93
|
+
type: "string",
|
|
94
|
+
default: "",
|
|
95
|
+
doc: "Path to the Docker build context"
|
|
96
|
+
},
|
|
97
|
+
engine: {
|
|
98
|
+
type: "string",
|
|
99
|
+
default: "",
|
|
100
|
+
doc: "Container engine for Docker runtime"
|
|
101
|
+
},
|
|
102
|
+
network: {
|
|
103
|
+
type: "string",
|
|
104
|
+
default: "",
|
|
105
|
+
doc: "Docker network"
|
|
106
|
+
},
|
|
107
|
+
extra_args: {
|
|
108
|
+
type: "json",
|
|
109
|
+
default: void 0,
|
|
110
|
+
parse: parseOptionalStringArray,
|
|
111
|
+
doc: "Extra Docker runtime arguments"
|
|
112
|
+
},
|
|
113
|
+
template_id: {
|
|
114
|
+
type: "string",
|
|
115
|
+
default: "",
|
|
116
|
+
doc: "Prebuilt E2B template id"
|
|
117
|
+
},
|
|
118
|
+
cpu: {
|
|
119
|
+
type: "json",
|
|
120
|
+
default: void 0,
|
|
121
|
+
parse: parseOptionalNumber,
|
|
122
|
+
doc: "E2B CPU count"
|
|
123
|
+
},
|
|
124
|
+
memory_mb: {
|
|
125
|
+
type: "json",
|
|
126
|
+
default: void 0,
|
|
127
|
+
parse: parseOptionalNumber,
|
|
128
|
+
doc: "E2B memory in megabytes"
|
|
129
|
+
},
|
|
130
|
+
timeout_minutes: {
|
|
131
|
+
type: "json",
|
|
132
|
+
default: void 0,
|
|
133
|
+
parse: parseOptionalNumber,
|
|
134
|
+
doc: "E2B timeout in minutes"
|
|
135
|
+
},
|
|
136
|
+
preserve_after_exit_hours: {
|
|
137
|
+
type: "json",
|
|
138
|
+
default: void 0,
|
|
139
|
+
parse: parseOptionalNumber,
|
|
140
|
+
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
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function parseRunner(raw) {
|
|
150
|
+
if (raw === void 0) {
|
|
151
|
+
return createDefaultRunnerScope();
|
|
152
|
+
}
|
|
153
|
+
const record = asRecord(raw);
|
|
154
|
+
if (record === void 0) {
|
|
155
|
+
throw new Error("runner: expected an object.");
|
|
156
|
+
}
|
|
157
|
+
const uploadMaxFileMb = parseOptionalNumber(record.upload_max_file_mb, "runner.upload_max_file_mb") ?? 100;
|
|
158
|
+
if (uploadMaxFileMb <= 0) {
|
|
159
|
+
throw new Error("runner.upload_max_file_mb: expected a positive finite number.");
|
|
160
|
+
}
|
|
161
|
+
return omitUndefined({
|
|
162
|
+
detach: parseOptionalBoolean(record.detach, "runner.detach") ?? false,
|
|
163
|
+
upload_max_file_mb: uploadMaxFileMb,
|
|
164
|
+
download_conflict: parseDownloadConflict(record.download_conflict),
|
|
165
|
+
workspace: parseRunnerWorkspace(record.workspace)
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function parseRuntime(raw) {
|
|
169
|
+
if (raw === void 0) {
|
|
170
|
+
return {
|
|
171
|
+
type: "host",
|
|
172
|
+
build_args: {},
|
|
173
|
+
mounts: []
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const record = asRecord(raw);
|
|
177
|
+
if (record === void 0) {
|
|
178
|
+
throw new Error("runtime: expected an object.");
|
|
179
|
+
}
|
|
180
|
+
const type = parseRuntimeType(record.type);
|
|
181
|
+
const shared = parseSharedRuntimeFields(record);
|
|
182
|
+
if (type === "docker") {
|
|
183
|
+
return omitUndefined({
|
|
184
|
+
...shared,
|
|
185
|
+
type,
|
|
186
|
+
image: parseOptionalString(record.image),
|
|
187
|
+
dockerfile: parseOptionalString(record.dockerfile),
|
|
188
|
+
build_context: parseOptionalString(record.build_context),
|
|
189
|
+
engine: parseEngine(record.engine),
|
|
190
|
+
network: parseOptionalString(record.network),
|
|
191
|
+
extra_args: parseOptionalStringArray(record.extra_args)
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (type === "e2b") {
|
|
195
|
+
const preserveAfterExitHours = parseOptionalNumber(record.preserve_after_exit_hours) ?? 24;
|
|
196
|
+
if (preserveAfterExitHours < 0 || preserveAfterExitHours > 168) {
|
|
197
|
+
throw new Error("preserve_after_exit_hours: expected a number from 0 to 168.");
|
|
198
|
+
}
|
|
199
|
+
return omitUndefined({
|
|
200
|
+
...shared,
|
|
201
|
+
type,
|
|
202
|
+
template_id: parseOptionalString(record.template_id),
|
|
203
|
+
dockerfile: parseOptionalString(record.dockerfile),
|
|
204
|
+
build_context: parseOptionalString(record.build_context),
|
|
205
|
+
cpu: parseOptionalNumber(record.cpu),
|
|
206
|
+
memory_mb: parseOptionalNumber(record.memory_mb),
|
|
207
|
+
timeout_minutes: parseOptionalNumber(record.timeout_minutes),
|
|
208
|
+
preserve_after_exit_hours: preserveAfterExitHours,
|
|
209
|
+
api_key_env: parseOptionalString(record.api_key_env) ?? "E2B_API_KEY"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
...shared,
|
|
214
|
+
type
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function resolveRuntime({
|
|
218
|
+
cwd,
|
|
219
|
+
config
|
|
220
|
+
}) {
|
|
221
|
+
const runtime = config.runtime;
|
|
222
|
+
return runtimeResolvers[runtime.type]({ cwd, runtime });
|
|
223
|
+
}
|
|
224
|
+
var runtimeResolvers = {
|
|
225
|
+
host({ runtime }) {
|
|
226
|
+
return {
|
|
227
|
+
runtime,
|
|
228
|
+
runner: "host",
|
|
229
|
+
dockerfilePath: null,
|
|
230
|
+
buildContext: null
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
docker({ cwd, runtime }) {
|
|
234
|
+
const dockerRuntime = runtime;
|
|
235
|
+
const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, dockerRuntime);
|
|
236
|
+
if (dockerRuntime.image !== void 0) {
|
|
237
|
+
return {
|
|
238
|
+
runtime: dockerRuntime,
|
|
239
|
+
runner: "docker",
|
|
240
|
+
dockerfilePath: null,
|
|
241
|
+
buildContext: null
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (!existsSync(dockerfilePath)) {
|
|
245
|
+
throw new Error(`Docker runtime requires image or a Dockerfile at ${dockerfilePath}.`);
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
runtime: dockerRuntime,
|
|
249
|
+
runner: "docker",
|
|
250
|
+
dockerfilePath,
|
|
251
|
+
buildContext
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
e2b({ cwd, runtime }) {
|
|
255
|
+
const e2bRuntime = runtime;
|
|
256
|
+
const { dockerfilePath, buildContext } = resolveRuntimeBuildPaths(cwd, e2bRuntime);
|
|
257
|
+
if (e2bRuntime.template_id !== void 0) {
|
|
258
|
+
return {
|
|
259
|
+
runtime: e2bRuntime,
|
|
260
|
+
runner: "e2b",
|
|
261
|
+
dockerfilePath: null,
|
|
262
|
+
buildContext: null
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (!existsSync(dockerfilePath)) {
|
|
266
|
+
throw new Error(`E2B runtime requires template_id or a Dockerfile at ${dockerfilePath}.`);
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
runtime: e2bRuntime,
|
|
270
|
+
runner: "e2b",
|
|
271
|
+
dockerfilePath,
|
|
272
|
+
buildContext
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
function resolveRuntimeBuildPaths(cwd, runtime) {
|
|
277
|
+
return {
|
|
278
|
+
dockerfilePath: path2.resolve(cwd, runtime.dockerfile ?? path2.join(".poe-code", "Dockerfile")),
|
|
279
|
+
buildContext: path2.resolve(cwd, runtime.build_context ?? ".")
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function parseSharedRuntimeFields(record) {
|
|
283
|
+
return omitUndefined({
|
|
284
|
+
build_args: parseBuildArgs(record.build_args),
|
|
285
|
+
mounts: parseMounts(record.mounts),
|
|
286
|
+
link: parseOptionalString(record.link)
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function parseRuntimeType(value) {
|
|
290
|
+
if (value === void 0) {
|
|
291
|
+
return "host";
|
|
292
|
+
}
|
|
293
|
+
if (value === "host" || value === "docker" || value === "e2b") {
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
throw new Error('type: expected "host", "docker", or "e2b".');
|
|
297
|
+
}
|
|
298
|
+
function createDefaultRunnerScope() {
|
|
299
|
+
return {
|
|
300
|
+
detach: false,
|
|
301
|
+
upload_max_file_mb: 100,
|
|
302
|
+
download_conflict: "refuse",
|
|
303
|
+
workspace: {
|
|
304
|
+
exclude: [...defaultWorkspaceExclude]
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function parseRunnerWorkspace(value) {
|
|
309
|
+
if (value === void 0) {
|
|
310
|
+
return {
|
|
311
|
+
exclude: [...defaultWorkspaceExclude]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const record = asRecord(value);
|
|
315
|
+
if (record === void 0) {
|
|
316
|
+
throw new Error("runner.workspace: expected an object.");
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
exclude: parseOptionalStringArray(record.exclude, "runner.workspace.exclude") ?? [
|
|
320
|
+
...defaultWorkspaceExclude
|
|
321
|
+
]
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function parseDownloadConflict(value) {
|
|
325
|
+
if (value === void 0) {
|
|
326
|
+
return "refuse";
|
|
327
|
+
}
|
|
328
|
+
if (value === "refuse" || value === "overwrite") {
|
|
329
|
+
return value;
|
|
330
|
+
}
|
|
331
|
+
throw new Error('runner.download_conflict: expected "refuse" or "overwrite".');
|
|
332
|
+
}
|
|
333
|
+
function parseBuildArgs(value) {
|
|
334
|
+
if (value === void 0) {
|
|
335
|
+
return {};
|
|
336
|
+
}
|
|
337
|
+
const record = asRecord(value);
|
|
338
|
+
if (record === void 0) {
|
|
339
|
+
throw new Error("build_args: expected an object.");
|
|
340
|
+
}
|
|
341
|
+
const parsed = {};
|
|
342
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
343
|
+
if (typeof entry !== "string") {
|
|
344
|
+
throw new Error(`build_args.${key}: expected a string.`);
|
|
345
|
+
}
|
|
346
|
+
parsed[key] = entry;
|
|
347
|
+
}
|
|
348
|
+
return parsed;
|
|
349
|
+
}
|
|
350
|
+
function parseMounts(value) {
|
|
351
|
+
if (value === void 0) {
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
354
|
+
if (!Array.isArray(value)) {
|
|
355
|
+
throw new Error("mounts: expected an array.");
|
|
356
|
+
}
|
|
357
|
+
return value.map((entry, index) => {
|
|
358
|
+
const record = asRecord(entry);
|
|
359
|
+
if (record === void 0) {
|
|
360
|
+
throw new Error(`mounts[${index}]: expected an object.`);
|
|
361
|
+
}
|
|
362
|
+
const source = record.source;
|
|
363
|
+
const target = record.target;
|
|
364
|
+
if (typeof source !== "string") {
|
|
365
|
+
throw new Error(`mounts[${index}].source: expected a string.`);
|
|
366
|
+
}
|
|
367
|
+
if (typeof target !== "string") {
|
|
368
|
+
throw new Error(`mounts[${index}].target: expected a string.`);
|
|
369
|
+
}
|
|
370
|
+
return omitUndefined({
|
|
371
|
+
source,
|
|
372
|
+
target,
|
|
373
|
+
readonly: parseOptionalBoolean(record.readonly, `mounts[${index}].readonly`)
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function parseOptionalString(value) {
|
|
378
|
+
if (value === void 0) {
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
if (typeof value !== "string") {
|
|
382
|
+
throw new Error("expected a string.");
|
|
383
|
+
}
|
|
384
|
+
if (value.length === 0) {
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
function parseOptionalStringArray(value, key = "") {
|
|
390
|
+
if (value === void 0) {
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
if (!Array.isArray(value)) {
|
|
394
|
+
throw new Error(`${key ? `${key}: ` : ""}expected an array.`);
|
|
395
|
+
}
|
|
396
|
+
return value.map((entry, index) => {
|
|
397
|
+
if (typeof entry !== "string") {
|
|
398
|
+
throw new Error(`${key}[${index}]: expected a string.`);
|
|
399
|
+
}
|
|
400
|
+
return entry;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function parseEngine(value) {
|
|
404
|
+
const engine = parseOptionalString(value);
|
|
405
|
+
if (engine === void 0 || engine === "docker" || engine === "podman") {
|
|
406
|
+
return engine;
|
|
407
|
+
}
|
|
408
|
+
throw new Error('engine: expected "docker" or "podman".');
|
|
409
|
+
}
|
|
410
|
+
function parseOptionalNumber(value, key = "") {
|
|
411
|
+
if (value === void 0) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
415
|
+
throw new Error(`${key ? `${key}: ` : ""}expected a finite number.`);
|
|
416
|
+
}
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
function parseOptionalBoolean(value, key) {
|
|
420
|
+
if (value === void 0) {
|
|
421
|
+
return void 0;
|
|
422
|
+
}
|
|
423
|
+
if (typeof value !== "boolean") {
|
|
424
|
+
throw new Error(`${key}: expected a boolean.`);
|
|
425
|
+
}
|
|
426
|
+
return value;
|
|
427
|
+
}
|
|
428
|
+
function asRecord(value) {
|
|
429
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
430
|
+
return void 0;
|
|
431
|
+
}
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
function omitUndefined(value) {
|
|
435
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
436
|
+
}
|
|
39
437
|
|
|
40
438
|
// packages/poe-code-config/src/schema.ts
|
|
41
439
|
function defineScope(scope, schema) {
|
|
@@ -56,15 +454,15 @@ var planConfigScope = defineScope("plan", {
|
|
|
56
454
|
});
|
|
57
455
|
|
|
58
456
|
// packages/poe-code-config/src/store.ts
|
|
59
|
-
import
|
|
457
|
+
import path7 from "node:path";
|
|
60
458
|
|
|
61
459
|
// packages/config-extends/src/discover.ts
|
|
62
|
-
import
|
|
460
|
+
import path3 from "node:path";
|
|
63
461
|
async function findBase(name, bases, fs14) {
|
|
64
462
|
const checkedPaths = [];
|
|
65
463
|
for (const basePath of bases) {
|
|
66
464
|
for (const extension of [".md", ".yaml", ".yml", ".json"]) {
|
|
67
|
-
const filePath =
|
|
465
|
+
const filePath = path3.join(basePath, `${name}${extension}`);
|
|
68
466
|
checkedPaths.push(filePath);
|
|
69
467
|
try {
|
|
70
468
|
return {
|
|
@@ -88,7 +486,7 @@ function hasCode(error2, code) {
|
|
|
88
486
|
}
|
|
89
487
|
|
|
90
488
|
// packages/config-extends/src/parse.ts
|
|
91
|
-
import
|
|
489
|
+
import path4 from "node:path";
|
|
92
490
|
import matter from "gray-matter";
|
|
93
491
|
import { parse as parseYaml } from "yaml";
|
|
94
492
|
function parseDocument(content, filePath) {
|
|
@@ -106,7 +504,7 @@ function parseDocument(content, filePath) {
|
|
|
106
504
|
};
|
|
107
505
|
}
|
|
108
506
|
function detectFormat(content, filePath) {
|
|
109
|
-
const extension =
|
|
507
|
+
const extension = path4.extname(filePath).toLowerCase();
|
|
110
508
|
if (extension === ".md") {
|
|
111
509
|
return "markdown";
|
|
112
510
|
}
|
|
@@ -148,11 +546,11 @@ function stripBom(content) {
|
|
|
148
546
|
function mergeLayers(layers) {
|
|
149
547
|
return mergeObjectLayers(layers, []);
|
|
150
548
|
}
|
|
151
|
-
function mergeObjectLayers(layers,
|
|
549
|
+
function mergeObjectLayers(layers, path38) {
|
|
152
550
|
const data = {};
|
|
153
551
|
const sources = {};
|
|
154
552
|
for (const key of collectKeys(layers)) {
|
|
155
|
-
const resolved = resolveKey(layers, key,
|
|
553
|
+
const resolved = resolveKey(layers, key, path38);
|
|
156
554
|
if (resolved === void 0) {
|
|
157
555
|
continue;
|
|
158
556
|
}
|
|
@@ -170,7 +568,7 @@ function collectKeys(layers) {
|
|
|
170
568
|
}
|
|
171
569
|
return [...keys];
|
|
172
570
|
}
|
|
173
|
-
function resolveKey(layers, key,
|
|
571
|
+
function resolveKey(layers, key, path38) {
|
|
174
572
|
let winningSource;
|
|
175
573
|
let winningValue;
|
|
176
574
|
const objectLayers = [];
|
|
@@ -200,9 +598,9 @@ function resolveKey(layers, key, path30) {
|
|
|
200
598
|
if (winningSource === void 0) {
|
|
201
599
|
return void 0;
|
|
202
600
|
}
|
|
203
|
-
const fullPath = buildPath(
|
|
601
|
+
const fullPath = buildPath(path38, key);
|
|
204
602
|
if (isPlainObject(winningValue)) {
|
|
205
|
-
const merged = mergeObjectLayers(objectLayers, [...
|
|
603
|
+
const merged = mergeObjectLayers(objectLayers, [...path38, key]);
|
|
206
604
|
return {
|
|
207
605
|
value: merged.data,
|
|
208
606
|
sources: {
|
|
@@ -227,8 +625,8 @@ function isWinningCandidate(key, value) {
|
|
|
227
625
|
}
|
|
228
626
|
return true;
|
|
229
627
|
}
|
|
230
|
-
function buildPath(
|
|
231
|
-
return [...
|
|
628
|
+
function buildPath(path38, key) {
|
|
629
|
+
return [...path38, key].join(".");
|
|
232
630
|
}
|
|
233
631
|
function isPlainObject(value) {
|
|
234
632
|
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
@@ -252,7 +650,7 @@ function cloneValue(value) {
|
|
|
252
650
|
}
|
|
253
651
|
|
|
254
652
|
// packages/config-extends/src/resolve.ts
|
|
255
|
-
import
|
|
653
|
+
import path5 from "node:path";
|
|
256
654
|
var MAX_EXTENDS_DEPTH = 5;
|
|
257
655
|
var YIELD_TOKEN = "{{yield}}";
|
|
258
656
|
async function resolve(chain, options) {
|
|
@@ -350,7 +748,7 @@ Visited files:
|
|
|
350
748
|
);
|
|
351
749
|
}
|
|
352
750
|
const matchedBaseIndex = baseLayers.findIndex(
|
|
353
|
-
(layer) => layer.path ===
|
|
751
|
+
(layer) => layer.path === path5.dirname(discoveredBase.filePath)
|
|
354
752
|
);
|
|
355
753
|
if (matchedBaseIndex === -1) {
|
|
356
754
|
throw new Error(`Resolved base is outside configured base paths: ${discoveredBase.filePath}`);
|
|
@@ -464,7 +862,7 @@ function stripResolvedBasePrompts(layers, consumedBaseIndexes) {
|
|
|
464
862
|
});
|
|
465
863
|
}
|
|
466
864
|
function getBaseName(filePath) {
|
|
467
|
-
return
|
|
865
|
+
return path5.basename(filePath, path5.extname(filePath));
|
|
468
866
|
}
|
|
469
867
|
function shouldResolveBase(parsedDocument, autoExtend) {
|
|
470
868
|
return parsedDocument.extends || autoExtend === true && !parsedDocument.hasExtendsField;
|
|
@@ -862,20 +1260,20 @@ function getConfigFormat(pathOrFormat) {
|
|
|
862
1260
|
}
|
|
863
1261
|
return formatRegistry[formatName];
|
|
864
1262
|
}
|
|
865
|
-
function detectFormat2(
|
|
866
|
-
const ext = getExtension(
|
|
1263
|
+
function detectFormat2(path38) {
|
|
1264
|
+
const ext = getExtension(path38);
|
|
867
1265
|
return extensionMap[ext];
|
|
868
1266
|
}
|
|
869
|
-
function getExtension(
|
|
870
|
-
const lastDot =
|
|
1267
|
+
function getExtension(path38) {
|
|
1268
|
+
const lastDot = path38.lastIndexOf(".");
|
|
871
1269
|
if (lastDot === -1) {
|
|
872
1270
|
return "";
|
|
873
1271
|
}
|
|
874
|
-
return
|
|
1272
|
+
return path38.slice(lastDot).toLowerCase();
|
|
875
1273
|
}
|
|
876
1274
|
|
|
877
1275
|
// packages/config-mutations/src/execution/path-utils.ts
|
|
878
|
-
import
|
|
1276
|
+
import path6 from "node:path";
|
|
879
1277
|
function expandHome(targetPath, homeDir) {
|
|
880
1278
|
if (!targetPath?.startsWith("~")) {
|
|
881
1279
|
return targetPath;
|
|
@@ -892,7 +1290,7 @@ function expandHome(targetPath, homeDir) {
|
|
|
892
1290
|
remainder = remainder.slice(1);
|
|
893
1291
|
}
|
|
894
1292
|
}
|
|
895
|
-
return remainder.length === 0 ? homeDir :
|
|
1293
|
+
return remainder.length === 0 ? homeDir : path6.join(homeDir, remainder);
|
|
896
1294
|
}
|
|
897
1295
|
function validateHomePath(targetPath) {
|
|
898
1296
|
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
@@ -910,12 +1308,12 @@ function resolvePath(rawPath, homeDir, pathMapper) {
|
|
|
910
1308
|
if (!pathMapper) {
|
|
911
1309
|
return expanded;
|
|
912
1310
|
}
|
|
913
|
-
const rawDirectory =
|
|
1311
|
+
const rawDirectory = path6.dirname(expanded);
|
|
914
1312
|
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
915
1313
|
targetDirectory: rawDirectory
|
|
916
1314
|
});
|
|
917
|
-
const filename =
|
|
918
|
-
return filename.length === 0 ? mappedDirectory :
|
|
1315
|
+
const filename = path6.basename(expanded);
|
|
1316
|
+
return filename.length === 0 ? mappedDirectory : path6.join(mappedDirectory, filename);
|
|
919
1317
|
}
|
|
920
1318
|
|
|
921
1319
|
// packages/config-mutations/src/fs-utils.ts
|
|
@@ -1173,8 +1571,8 @@ async function applyChmod(mutation, context, options) {
|
|
|
1173
1571
|
};
|
|
1174
1572
|
}
|
|
1175
1573
|
try {
|
|
1176
|
-
const
|
|
1177
|
-
const currentMode = typeof
|
|
1574
|
+
const stat7 = await context.fs.stat(targetPath);
|
|
1575
|
+
const currentMode = typeof stat7.mode === "number" ? stat7.mode & 511 : null;
|
|
1178
1576
|
if (currentMode === mutation.mode) {
|
|
1179
1577
|
return {
|
|
1180
1578
|
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
@@ -1537,7 +1935,7 @@ async function readMergedDocument(fs14, globalPath, projectPath) {
|
|
|
1537
1935
|
},
|
|
1538
1936
|
{
|
|
1539
1937
|
source: "base",
|
|
1540
|
-
path:
|
|
1938
|
+
path: path7.dirname(globalPath)
|
|
1541
1939
|
}
|
|
1542
1940
|
],
|
|
1543
1941
|
{
|
|
@@ -1614,22 +2012,164 @@ function createResolvedConfigFs(fs14, globalPath, globalContent) {
|
|
|
1614
2012
|
};
|
|
1615
2013
|
}
|
|
1616
2014
|
async function recoverInvalidDocument(fs14, filePath, content) {
|
|
1617
|
-
await fs14.mkdir(
|
|
2015
|
+
await fs14.mkdir(path7.dirname(filePath), { recursive: true });
|
|
1618
2016
|
const backupPath = createInvalidBackupPath(filePath);
|
|
1619
2017
|
await fs14.writeFile(backupPath, content, { encoding: "utf8" });
|
|
1620
2018
|
await fs14.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
|
|
1621
2019
|
}
|
|
1622
2020
|
function createInvalidBackupPath(filePath) {
|
|
1623
|
-
const directory =
|
|
1624
|
-
const baseName =
|
|
1625
|
-
return
|
|
2021
|
+
const directory = path7.dirname(filePath);
|
|
2022
|
+
const baseName = path7.basename(filePath);
|
|
2023
|
+
return path7.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
|
|
1626
2024
|
}
|
|
1627
2025
|
function isRecord(value) {
|
|
1628
2026
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1629
2027
|
}
|
|
2028
|
+
function resolveConfigPath(homeDir) {
|
|
2029
|
+
return path7.join(homeDir, ".poe-code", "config.json");
|
|
2030
|
+
}
|
|
2031
|
+
function resolveProjectConfigPath(cwd) {
|
|
2032
|
+
return path7.join(cwd, ".poe-code", "config.json");
|
|
2033
|
+
}
|
|
1630
2034
|
var EMPTY_DOCUMENT = `${JSON.stringify({}, null, 2)}
|
|
1631
2035
|
`;
|
|
1632
2036
|
|
|
2037
|
+
// packages/poe-code-config/src/resolve.ts
|
|
2038
|
+
function resolveScope(schema, fileValues, env = {}) {
|
|
2039
|
+
const resolved = {};
|
|
2040
|
+
for (const key of Object.keys(schema)) {
|
|
2041
|
+
const field = schema[key];
|
|
2042
|
+
const envValue = resolveEnvValue(field, env, key);
|
|
2043
|
+
const fileValue = resolveFileValue(field, fileValues?.[key], key);
|
|
2044
|
+
resolved[key] = envValue ?? fileValue ?? field.default;
|
|
2045
|
+
}
|
|
2046
|
+
return resolved;
|
|
2047
|
+
}
|
|
2048
|
+
function resolveEnvValue(field, env, key) {
|
|
2049
|
+
if (!field.env) {
|
|
2050
|
+
return void 0;
|
|
2051
|
+
}
|
|
2052
|
+
const raw = env[field.env];
|
|
2053
|
+
if (raw === void 0) {
|
|
2054
|
+
return void 0;
|
|
2055
|
+
}
|
|
2056
|
+
return coerceValue(field, raw, key);
|
|
2057
|
+
}
|
|
2058
|
+
function resolveFileValue(field, value, key) {
|
|
2059
|
+
return coerceValue(field, value, key);
|
|
2060
|
+
}
|
|
2061
|
+
function coerceValue(field, value, key) {
|
|
2062
|
+
switch (field.type) {
|
|
2063
|
+
case "string":
|
|
2064
|
+
return typeof value === "string" ? value : void 0;
|
|
2065
|
+
case "number":
|
|
2066
|
+
return coerceNumber(value);
|
|
2067
|
+
case "boolean":
|
|
2068
|
+
return coerceBoolean(value);
|
|
2069
|
+
case "json":
|
|
2070
|
+
return coerceJson(field, value, key);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
function coerceNumber(value) {
|
|
2074
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2075
|
+
return value;
|
|
2076
|
+
}
|
|
2077
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2078
|
+
return void 0;
|
|
2079
|
+
}
|
|
2080
|
+
const parsed = Number(value);
|
|
2081
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2082
|
+
}
|
|
2083
|
+
function coerceBoolean(value) {
|
|
2084
|
+
if (typeof value === "boolean") {
|
|
2085
|
+
return value;
|
|
2086
|
+
}
|
|
2087
|
+
if (value === "true" || value === "1") {
|
|
2088
|
+
return true;
|
|
2089
|
+
}
|
|
2090
|
+
if (value === "false" || value === "0") {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
return void 0;
|
|
2094
|
+
}
|
|
2095
|
+
function coerceJson(field, value, key) {
|
|
2096
|
+
if (value === void 0) {
|
|
2097
|
+
return void 0;
|
|
2098
|
+
}
|
|
2099
|
+
const parsedValue = parseJsonValue(value, key);
|
|
2100
|
+
try {
|
|
2101
|
+
return field.parse(parsedValue);
|
|
2102
|
+
} catch (error2) {
|
|
2103
|
+
const message2 = error2 instanceof Error ? error2.message : "Invalid JSON value.";
|
|
2104
|
+
throw new Error(`Invalid config value for "${key}": ${message2}`);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
function parseJsonValue(value, key) {
|
|
2108
|
+
if (typeof value !== "string") {
|
|
2109
|
+
return value;
|
|
2110
|
+
}
|
|
2111
|
+
try {
|
|
2112
|
+
return JSON.parse(value);
|
|
2113
|
+
} catch {
|
|
2114
|
+
throw new Error(`Invalid config value for "${key}": expected valid JSON.`);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// packages/poe-code-config/src/merge.ts
|
|
2119
|
+
function deepMergeDocuments(base, override) {
|
|
2120
|
+
const merged = {};
|
|
2121
|
+
const scopes = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(override)]);
|
|
2122
|
+
for (const scope of scopes) {
|
|
2123
|
+
const baseScope = base[scope] ?? {};
|
|
2124
|
+
const overrideScope = override[scope] ?? {};
|
|
2125
|
+
const nextScope = mergeScope(scope, baseScope, overrideScope);
|
|
2126
|
+
if (Object.keys(nextScope).length > 0) {
|
|
2127
|
+
merged[scope] = nextScope;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return merged;
|
|
2131
|
+
}
|
|
2132
|
+
function mergeScope(scope, baseScope, overrideScope) {
|
|
2133
|
+
if (scope === "runtime") {
|
|
2134
|
+
return mergeRuntimeScope(baseScope, overrideScope);
|
|
2135
|
+
}
|
|
2136
|
+
const scopeEntries = Object.entries(overrideScope).filter(([, value]) => value !== void 0);
|
|
2137
|
+
return {
|
|
2138
|
+
...baseScope,
|
|
2139
|
+
...Object.fromEntries(scopeEntries)
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
function mergeRuntimeScope(baseScope, overrideScope, path38 = []) {
|
|
2143
|
+
const merged = {};
|
|
2144
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
|
|
2145
|
+
for (const key of keys) {
|
|
2146
|
+
const baseValue = baseScope[key];
|
|
2147
|
+
const overrideValue = overrideScope[key];
|
|
2148
|
+
if (overrideValue === void 0) {
|
|
2149
|
+
if (baseValue !== void 0) {
|
|
2150
|
+
merged[key] = baseValue;
|
|
2151
|
+
}
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
if (isRuntimeConcatenativeArray([...path38, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
|
|
2155
|
+
merged[key] = [...baseValue, ...overrideValue];
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
if (isRecord2(baseValue) && isRecord2(overrideValue)) {
|
|
2159
|
+
merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path38, key]);
|
|
2160
|
+
continue;
|
|
2161
|
+
}
|
|
2162
|
+
merged[key] = overrideValue;
|
|
2163
|
+
}
|
|
2164
|
+
return merged;
|
|
2165
|
+
}
|
|
2166
|
+
function isRuntimeConcatenativeArray(path38) {
|
|
2167
|
+
return path38.join(".") === "mounts" || path38.join(".") === "runner.workspace.exclude";
|
|
2168
|
+
}
|
|
2169
|
+
function isRecord2(value) {
|
|
2170
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2171
|
+
}
|
|
2172
|
+
|
|
1633
2173
|
// packages/poe-code-config/src/memory.ts
|
|
1634
2174
|
async function configuredMemoryRoot(options) {
|
|
1635
2175
|
return (await resolveMemoryConfig(options)).root;
|
|
@@ -1646,10 +2186,10 @@ async function cacheEnabled(options) {
|
|
|
1646
2186
|
}
|
|
1647
2187
|
async function resolveMemoryConfig(options) {
|
|
1648
2188
|
const document = await readMergedDocument(options.fs, options.filePath, options.projectFilePath);
|
|
1649
|
-
const memory =
|
|
1650
|
-
const cache =
|
|
1651
|
-
const mcp =
|
|
1652
|
-
const query =
|
|
2189
|
+
const memory = asRecord2(document.memory);
|
|
2190
|
+
const cache = asRecord2(memory?.cache);
|
|
2191
|
+
const mcp = asRecord2(memory?.mcp);
|
|
2192
|
+
const query = asRecord2(memory?.query);
|
|
1653
2193
|
return {
|
|
1654
2194
|
root: readString(memory?.root),
|
|
1655
2195
|
ingestAgent: readString(memory?.ingestAgent),
|
|
@@ -1659,7 +2199,7 @@ async function resolveMemoryConfig(options) {
|
|
|
1659
2199
|
defaultQueryBudget: readNumber(query?.defaultBudgetTokens) ?? 4096
|
|
1660
2200
|
};
|
|
1661
2201
|
}
|
|
1662
|
-
function
|
|
2202
|
+
function asRecord2(value) {
|
|
1663
2203
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1664
2204
|
return void 0;
|
|
1665
2205
|
}
|
|
@@ -1676,55 +2216,523 @@ function readBoolean(value) {
|
|
|
1676
2216
|
}
|
|
1677
2217
|
|
|
1678
2218
|
// packages/poe-code-config/src/inspect.ts
|
|
1679
|
-
import
|
|
2219
|
+
import path8 from "node:path";
|
|
1680
2220
|
var EMPTY_DOCUMENT2 = `${JSON.stringify({}, null, 2)}
|
|
1681
2221
|
`;
|
|
1682
2222
|
|
|
1683
|
-
// packages/
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
2223
|
+
// packages/poe-code-config/src/state/index.ts
|
|
2224
|
+
import os2 from "node:os";
|
|
2225
|
+
|
|
2226
|
+
// packages/poe-code-config/src/state/jobs.ts
|
|
2227
|
+
import path9 from "node:path";
|
|
2228
|
+
|
|
2229
|
+
// packages/file-lock/src/lock.ts
|
|
2230
|
+
import * as fsPromises from "node:fs/promises";
|
|
2231
|
+
import * as os from "node:os";
|
|
2232
|
+
var LockTimeoutError = class extends Error {
|
|
2233
|
+
constructor(message2) {
|
|
2234
|
+
super(message2);
|
|
2235
|
+
this.name = "LockTimeoutError";
|
|
1689
2236
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
2237
|
+
};
|
|
2238
|
+
function createAbortError() {
|
|
2239
|
+
const error2 = new Error("The operation was aborted.");
|
|
2240
|
+
error2.name = "AbortError";
|
|
2241
|
+
return error2;
|
|
2242
|
+
}
|
|
2243
|
+
function throwIfAborted(signal) {
|
|
2244
|
+
if (signal?.aborted) {
|
|
2245
|
+
throw createAbortError();
|
|
1697
2246
|
}
|
|
1698
|
-
return resolveMemoryRoot(options.cwd);
|
|
1699
2247
|
}
|
|
1700
|
-
function
|
|
1701
|
-
|
|
2248
|
+
function sleep(ms, signal) {
|
|
2249
|
+
if (!signal) {
|
|
2250
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2251
|
+
}
|
|
2252
|
+
if (signal.aborted) {
|
|
2253
|
+
return Promise.reject(createAbortError());
|
|
2254
|
+
}
|
|
2255
|
+
return new Promise((resolve2, reject) => {
|
|
2256
|
+
const timeoutId = setTimeout(() => {
|
|
2257
|
+
signal.removeEventListener("abort", onAbort);
|
|
2258
|
+
resolve2();
|
|
2259
|
+
}, ms);
|
|
2260
|
+
const onAbort = () => {
|
|
2261
|
+
clearTimeout(timeoutId);
|
|
2262
|
+
signal.removeEventListener("abort", onAbort);
|
|
2263
|
+
reject(createAbortError());
|
|
2264
|
+
};
|
|
2265
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2266
|
+
});
|
|
1702
2267
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
import path9 from "node:path";
|
|
1707
|
-
async function initMemory(root) {
|
|
1708
|
-
await fs.mkdir(path9.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
|
|
1709
|
-
await writeFileIfMissing(path9.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
|
|
1710
|
-
await writeFileIfMissing(path9.join(root, MEMORY_LOG_RELPATH), "");
|
|
2268
|
+
function backoff(attempt, minTimeout, maxTimeout) {
|
|
2269
|
+
const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
|
|
2270
|
+
return delay + Math.random() * delay * 0.1;
|
|
1711
2271
|
}
|
|
1712
|
-
|
|
2272
|
+
function hasErrorCode(error2, code) {
|
|
2273
|
+
return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
|
|
2274
|
+
}
|
|
2275
|
+
function hasAnyErrorCode(error2, codes) {
|
|
2276
|
+
return codes.some((code) => hasErrorCode(error2, code));
|
|
2277
|
+
}
|
|
2278
|
+
function isPidRunning(pid) {
|
|
2279
|
+
try {
|
|
2280
|
+
process.kill(pid, 0);
|
|
2281
|
+
return true;
|
|
2282
|
+
} catch (error2) {
|
|
2283
|
+
return !hasErrorCode(error2, "ESRCH");
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
function createDefaultFs() {
|
|
2287
|
+
return {
|
|
2288
|
+
open: (path38, flags) => fsPromises.open(path38, flags),
|
|
2289
|
+
readFile: (path38, encoding) => fsPromises.readFile(path38, encoding),
|
|
2290
|
+
stat: fsPromises.stat,
|
|
2291
|
+
unlink: fsPromises.unlink
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
async function removeLockFile(fs14, lockPath, signal) {
|
|
2295
|
+
for (let attempt = 0; attempt <= 4; attempt += 1) {
|
|
2296
|
+
throwIfAborted(signal);
|
|
2297
|
+
try {
|
|
2298
|
+
await fs14.unlink(lockPath);
|
|
2299
|
+
return;
|
|
2300
|
+
} catch (error2) {
|
|
2301
|
+
if (hasErrorCode(error2, "ENOENT")) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (!hasAnyErrorCode(error2, ["EPERM", "EBUSY"]) || attempt === 4) {
|
|
2305
|
+
throw error2;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
await sleep(25 * 2 ** attempt, signal);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
function parseLockMetadata(content) {
|
|
2312
|
+
try {
|
|
2313
|
+
const parsed = JSON.parse(content);
|
|
2314
|
+
if (!parsed || typeof parsed !== "object" || !("host" in parsed) || !("pid" in parsed)) {
|
|
2315
|
+
return void 0;
|
|
2316
|
+
}
|
|
2317
|
+
const { host, pid } = parsed;
|
|
2318
|
+
if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
|
|
2319
|
+
return {
|
|
2320
|
+
host,
|
|
2321
|
+
pid
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
} catch (ignoredError) {
|
|
2325
|
+
void ignoredError;
|
|
2326
|
+
}
|
|
2327
|
+
return void 0;
|
|
2328
|
+
}
|
|
2329
|
+
async function readLockMetadata(fs14, lockPath) {
|
|
2330
|
+
if (!fs14.readFile) {
|
|
2331
|
+
return void 0;
|
|
2332
|
+
}
|
|
2333
|
+
try {
|
|
2334
|
+
return parseLockMetadata(await fs14.readFile(lockPath, "utf8"));
|
|
2335
|
+
} catch (error2) {
|
|
2336
|
+
if (hasErrorCode(error2, "ENOENT")) {
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
return void 0;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
async function shouldReclaimLock(options) {
|
|
2343
|
+
const metadata = await readLockMetadata(options.fs, options.lockPath);
|
|
2344
|
+
if (metadata === null) {
|
|
2345
|
+
return "missing";
|
|
2346
|
+
}
|
|
2347
|
+
if (metadata?.host === os.hostname()) {
|
|
2348
|
+
return !options.isPidRunning(metadata.pid);
|
|
2349
|
+
}
|
|
2350
|
+
return Date.now() - options.stat.mtimeMs > options.staleMs;
|
|
2351
|
+
}
|
|
2352
|
+
async function writeLockMetadata(handle) {
|
|
2353
|
+
try {
|
|
2354
|
+
await handle.writeFile(
|
|
2355
|
+
JSON.stringify({ pid: process.pid, host: os.hostname(), acquiredAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
2356
|
+
{ encoding: "utf8" }
|
|
2357
|
+
);
|
|
2358
|
+
} catch (ignoredError) {
|
|
2359
|
+
void ignoredError;
|
|
2360
|
+
}
|
|
2361
|
+
try {
|
|
2362
|
+
await handle.close();
|
|
2363
|
+
} catch (ignoredError) {
|
|
2364
|
+
void ignoredError;
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
async function acquireFileLock(filePath, options = {}) {
|
|
2368
|
+
const fs14 = options.fs ?? createDefaultFs();
|
|
2369
|
+
const retries = options.retries ?? 20;
|
|
2370
|
+
const minTimeout = options.minTimeout ?? 25;
|
|
2371
|
+
const maxTimeout = options.maxTimeout ?? 250;
|
|
2372
|
+
const staleMs = options.staleMs ?? 1e3;
|
|
2373
|
+
const pidIsRunning = options.isPidRunning ?? isPidRunning;
|
|
2374
|
+
const lockPath = `${filePath}.lock`;
|
|
2375
|
+
let attempt = 0;
|
|
2376
|
+
while (attempt <= retries) {
|
|
2377
|
+
throwIfAborted(options.signal);
|
|
2378
|
+
try {
|
|
2379
|
+
const handle = await fs14.open(lockPath, "wx");
|
|
2380
|
+
await writeLockMetadata(handle);
|
|
2381
|
+
let released = false;
|
|
2382
|
+
return async () => {
|
|
2383
|
+
if (released) {
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
released = true;
|
|
2387
|
+
await removeLockFile(fs14, lockPath, options.signal);
|
|
2388
|
+
};
|
|
2389
|
+
} catch (error2) {
|
|
2390
|
+
if (!hasErrorCode(error2, "EEXIST")) {
|
|
2391
|
+
throw error2;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
let stat7;
|
|
2395
|
+
try {
|
|
2396
|
+
stat7 = await fs14.stat(lockPath);
|
|
2397
|
+
} catch (statError) {
|
|
2398
|
+
if (hasErrorCode(statError, "ENOENT")) {
|
|
2399
|
+
continue;
|
|
2400
|
+
}
|
|
2401
|
+
throw statError;
|
|
2402
|
+
}
|
|
2403
|
+
const reclaimLock = await shouldReclaimLock({
|
|
2404
|
+
fs: fs14,
|
|
2405
|
+
isPidRunning: pidIsRunning,
|
|
2406
|
+
lockPath,
|
|
2407
|
+
staleMs,
|
|
2408
|
+
stat: stat7
|
|
2409
|
+
});
|
|
2410
|
+
if (reclaimLock === "missing") {
|
|
2411
|
+
continue;
|
|
2412
|
+
}
|
|
2413
|
+
if (reclaimLock) {
|
|
2414
|
+
await removeLockFile(fs14, lockPath, options.signal);
|
|
2415
|
+
continue;
|
|
2416
|
+
}
|
|
2417
|
+
if (attempt >= retries) {
|
|
2418
|
+
break;
|
|
2419
|
+
}
|
|
2420
|
+
await sleep(backoff(attempt, minTimeout, maxTimeout), options.signal);
|
|
2421
|
+
attempt += 1;
|
|
2422
|
+
}
|
|
2423
|
+
throw new LockTimeoutError(`Failed to acquire lock on "${filePath}".`);
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
// packages/poe-code-config/src/state/fs.ts
|
|
2427
|
+
import * as nodeFs from "node:fs/promises";
|
|
2428
|
+
var defaultStateFs = nodeFs;
|
|
2429
|
+
function isNotFoundError(error2) {
|
|
2430
|
+
return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
// packages/poe-code-config/src/state/jobs.ts
|
|
2434
|
+
function createJobRegistry(homeDir, fs14 = defaultStateFs) {
|
|
2435
|
+
const jobsDir = path9.join(homeDir, ".poe-code", "state", "jobs");
|
|
2436
|
+
function jobPath(id) {
|
|
2437
|
+
assertSafeJobId(id);
|
|
2438
|
+
return path9.join(jobsDir, `${id}.json`);
|
|
2439
|
+
}
|
|
2440
|
+
async function get(id) {
|
|
2441
|
+
try {
|
|
2442
|
+
return parseJobEntry(await fs14.readFile(jobPath(id), "utf8"));
|
|
2443
|
+
} catch (error2) {
|
|
2444
|
+
if (isNotFoundError(error2)) {
|
|
2445
|
+
return null;
|
|
2446
|
+
}
|
|
2447
|
+
throw error2;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
async function put(entry) {
|
|
2451
|
+
assertJobEntry(entry);
|
|
2452
|
+
const filePath = jobPath(entry.id);
|
|
2453
|
+
await fs14.mkdir(jobsDir, { recursive: true });
|
|
2454
|
+
const release = await acquireFileLock(filePath, { fs: fs14 });
|
|
2455
|
+
try {
|
|
2456
|
+
await writeJobAtomically(filePath, entry);
|
|
2457
|
+
} finally {
|
|
2458
|
+
await release();
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
async function update(id, patch) {
|
|
2462
|
+
const filePath = jobPath(id);
|
|
2463
|
+
await fs14.mkdir(jobsDir, { recursive: true });
|
|
2464
|
+
const release = await acquireFileLock(filePath, { fs: fs14 });
|
|
2465
|
+
try {
|
|
2466
|
+
const current = await get(id);
|
|
2467
|
+
if (current === null) {
|
|
2468
|
+
return null;
|
|
2469
|
+
}
|
|
2470
|
+
const updated = {
|
|
2471
|
+
...current,
|
|
2472
|
+
...patch,
|
|
2473
|
+
id: current.id
|
|
2474
|
+
};
|
|
2475
|
+
assertJobEntry(updated);
|
|
2476
|
+
await writeJobAtomically(filePath, updated);
|
|
2477
|
+
return updated;
|
|
2478
|
+
} finally {
|
|
2479
|
+
await release();
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
async function list(filter = {}) {
|
|
2483
|
+
let entries;
|
|
2484
|
+
try {
|
|
2485
|
+
entries = await fs14.readdir(jobsDir);
|
|
2486
|
+
} catch (error2) {
|
|
2487
|
+
if (isNotFoundError(error2)) {
|
|
2488
|
+
return [];
|
|
2489
|
+
}
|
|
2490
|
+
throw error2;
|
|
2491
|
+
}
|
|
2492
|
+
const jobs = [];
|
|
2493
|
+
for (const entry of entries.sort()) {
|
|
2494
|
+
if (!entry.endsWith(".json")) {
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
const filePath = path9.join(jobsDir, entry);
|
|
2498
|
+
const stat7 = await fs14.stat(filePath);
|
|
2499
|
+
if (!stat7.isFile()) {
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
const job = parseJobEntry(await fs14.readFile(filePath, "utf8"));
|
|
2503
|
+
if (matchesFilter(job, filter)) {
|
|
2504
|
+
jobs.push(job);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
return jobs;
|
|
2508
|
+
}
|
|
2509
|
+
async function remove2(id) {
|
|
2510
|
+
const filePath = jobPath(id);
|
|
2511
|
+
try {
|
|
2512
|
+
await fs14.stat(jobsDir);
|
|
2513
|
+
} catch (error2) {
|
|
2514
|
+
if (isNotFoundError(error2)) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
throw error2;
|
|
2518
|
+
}
|
|
2519
|
+
const release = await acquireFileLock(filePath, { fs: fs14 });
|
|
2520
|
+
try {
|
|
2521
|
+
await fs14.unlink(filePath);
|
|
2522
|
+
} catch (error2) {
|
|
2523
|
+
if (!isNotFoundError(error2)) {
|
|
2524
|
+
throw error2;
|
|
2525
|
+
}
|
|
2526
|
+
} finally {
|
|
2527
|
+
await release();
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
async function writeJobAtomically(filePath, entry) {
|
|
2531
|
+
await fs14.mkdir(path9.dirname(filePath), { recursive: true });
|
|
2532
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
2533
|
+
try {
|
|
2534
|
+
await fs14.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
|
|
2535
|
+
`, {
|
|
2536
|
+
encoding: "utf8"
|
|
2537
|
+
});
|
|
2538
|
+
await fs14.rename(tempPath, filePath);
|
|
2539
|
+
} catch (error2) {
|
|
2540
|
+
await removeTempFile(tempPath);
|
|
2541
|
+
throw error2;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
async function removeTempFile(tempPath) {
|
|
2545
|
+
try {
|
|
2546
|
+
await fs14.unlink(tempPath);
|
|
2547
|
+
} catch (error2) {
|
|
2548
|
+
if (!isNotFoundError(error2)) {
|
|
2549
|
+
throw error2;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
return {
|
|
2554
|
+
get,
|
|
2555
|
+
put,
|
|
2556
|
+
update,
|
|
2557
|
+
list,
|
|
2558
|
+
remove: remove2
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
function assertSafeJobId(id) {
|
|
2562
|
+
if (id.length === 0 || id === "." || id === ".." || path9.isAbsolute(id) || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2563
|
+
throw new Error("Invalid job id.");
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
function assertJobEntry(entry) {
|
|
2567
|
+
if (!isJobEntry(entry)) {
|
|
2568
|
+
throw new Error("Invalid job entry.");
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
function matchesFilter(job, filter) {
|
|
2572
|
+
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);
|
|
2573
|
+
}
|
|
2574
|
+
function parseJobEntry(content) {
|
|
2575
|
+
const parsed = JSON.parse(content);
|
|
2576
|
+
if (!isJobEntry(parsed)) {
|
|
2577
|
+
throw new Error("Invalid job state file.");
|
|
2578
|
+
}
|
|
2579
|
+
return parsed;
|
|
2580
|
+
}
|
|
2581
|
+
function isJobEntry(value) {
|
|
2582
|
+
return isRecord3(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");
|
|
2583
|
+
}
|
|
2584
|
+
function isJobStatus(value) {
|
|
2585
|
+
return value === "pending" || value === "running" || value === "exited" || value === "killed" || value === "lost";
|
|
2586
|
+
}
|
|
2587
|
+
function isRecord3(value) {
|
|
2588
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// packages/poe-code-config/src/state/templates.ts
|
|
2592
|
+
import path10 from "node:path";
|
|
2593
|
+
function createTemplateRegistry(homeDir, fs14 = defaultStateFs) {
|
|
2594
|
+
const filePath = path10.join(homeDir, ".poe-code", "state", "templates.json");
|
|
2595
|
+
async function readState() {
|
|
2596
|
+
try {
|
|
2597
|
+
const raw = await fs14.readFile(filePath, "utf8");
|
|
2598
|
+
return normalizeTemplateState(JSON.parse(raw));
|
|
2599
|
+
} catch (error2) {
|
|
2600
|
+
if (isNotFoundError(error2)) {
|
|
2601
|
+
return createEmptyState();
|
|
2602
|
+
}
|
|
2603
|
+
throw error2;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
async function writeState(state) {
|
|
2607
|
+
await fs14.writeFile(filePath, `${JSON.stringify(state, null, 2)}
|
|
2608
|
+
`, {
|
|
2609
|
+
encoding: "utf8"
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
async function updateState(mutator) {
|
|
2613
|
+
await fs14.mkdir(path10.dirname(filePath), { recursive: true });
|
|
2614
|
+
const release = await acquireFileLock(filePath, { fs: fs14 });
|
|
2615
|
+
try {
|
|
2616
|
+
const state = await readState();
|
|
2617
|
+
mutator(state);
|
|
2618
|
+
await writeState(state);
|
|
2619
|
+
} finally {
|
|
2620
|
+
await release();
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
async function get(backend, hash) {
|
|
2624
|
+
const state = await readState();
|
|
2625
|
+
return state[backend][hash] ?? null;
|
|
2626
|
+
}
|
|
2627
|
+
async function put(backend, entry) {
|
|
2628
|
+
await updateState((state) => {
|
|
2629
|
+
state[backend][entry.hash] = entry;
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
async function remove2(backend, hash) {
|
|
2633
|
+
await updateState((state) => {
|
|
2634
|
+
delete state[backend][hash];
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
async function list(backend) {
|
|
2638
|
+
const state = await readState();
|
|
2639
|
+
const entries = backend === void 0 ? [...Object.values(state.docker), ...Object.values(state.e2b)] : Object.values(state[backend]);
|
|
2640
|
+
return entries.sort((left, right) => left.hash.localeCompare(right.hash));
|
|
2641
|
+
}
|
|
2642
|
+
return {
|
|
2643
|
+
get,
|
|
2644
|
+
put,
|
|
2645
|
+
remove: remove2,
|
|
2646
|
+
list
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function createEmptyState() {
|
|
2650
|
+
return {
|
|
2651
|
+
docker: {},
|
|
2652
|
+
e2b: {}
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
function normalizeTemplateState(value) {
|
|
2656
|
+
if (!isRecord4(value)) {
|
|
2657
|
+
return createEmptyState();
|
|
2658
|
+
}
|
|
2659
|
+
return {
|
|
2660
|
+
docker: normalizeTemplateEntries(value.docker),
|
|
2661
|
+
e2b: normalizeTemplateEntries(value.e2b)
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
function normalizeTemplateEntries(value) {
|
|
2665
|
+
if (!isRecord4(value)) {
|
|
2666
|
+
return {};
|
|
2667
|
+
}
|
|
2668
|
+
const entries = {};
|
|
2669
|
+
for (const [hash, entry] of Object.entries(value)) {
|
|
2670
|
+
if (isTemplateEntry(entry) && entry.hash === hash) {
|
|
2671
|
+
entries[hash] = entry;
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
return entries;
|
|
2675
|
+
}
|
|
2676
|
+
function isTemplateEntry(value) {
|
|
2677
|
+
return isRecord4(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");
|
|
2678
|
+
}
|
|
2679
|
+
function isRecord4(value) {
|
|
2680
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
// packages/poe-code-config/src/state/index.ts
|
|
2684
|
+
function createStateManager(homeDir, fs14) {
|
|
2685
|
+
return {
|
|
2686
|
+
templates: createTemplateRegistry(homeDir, fs14),
|
|
2687
|
+
jobs: createJobRegistry(homeDir, fs14)
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
// packages/memory/src/resolve-root.ts
|
|
2692
|
+
var MEMORY_ROOT_ENV_VAR = "POE_CODE_MEMORY_ROOT";
|
|
2693
|
+
async function resolveConfiguredMemoryRoot(options) {
|
|
2694
|
+
const envOverride = options.env[MEMORY_ROOT_ENV_VAR]?.trim();
|
|
2695
|
+
if (envOverride && envOverride.length > 0) {
|
|
2696
|
+
return resolveAgainstCwd(options.cwd, envOverride);
|
|
2697
|
+
}
|
|
2698
|
+
const configOverride = (await configuredMemoryRoot({
|
|
2699
|
+
fs: options.fs,
|
|
2700
|
+
filePath: options.configPath,
|
|
2701
|
+
projectFilePath: options.projectConfigPath
|
|
2702
|
+
}))?.trim();
|
|
2703
|
+
if (configOverride && configOverride.length > 0) {
|
|
2704
|
+
return resolveAgainstCwd(options.cwd, configOverride);
|
|
2705
|
+
}
|
|
2706
|
+
return resolveMemoryRoot(options.cwd);
|
|
2707
|
+
}
|
|
2708
|
+
function resolveAgainstCwd(cwd, value) {
|
|
2709
|
+
return path11.isAbsolute(value) ? value : path11.resolve(cwd, value);
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
// packages/memory/src/init.ts
|
|
2713
|
+
import * as fs from "node:fs/promises";
|
|
2714
|
+
import path12 from "node:path";
|
|
2715
|
+
async function initMemory(root) {
|
|
2716
|
+
await fs.mkdir(path12.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
|
|
2717
|
+
await writeFileIfMissing(path12.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
|
|
2718
|
+
await writeFileIfMissing(path12.join(root, MEMORY_LOG_RELPATH), "");
|
|
2719
|
+
}
|
|
2720
|
+
async function writeFileIfMissing(filePath, content) {
|
|
1713
2721
|
try {
|
|
1714
2722
|
await fs.writeFile(filePath, content, { encoding: "utf8", flag: "wx" });
|
|
1715
2723
|
} catch (error2) {
|
|
1716
|
-
if (!
|
|
2724
|
+
if (!hasErrorCode2(error2, "EEXIST")) {
|
|
1717
2725
|
throw error2;
|
|
1718
2726
|
}
|
|
1719
2727
|
}
|
|
1720
2728
|
}
|
|
1721
|
-
function
|
|
2729
|
+
function hasErrorCode2(error2, code) {
|
|
1722
2730
|
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
|
|
1723
2731
|
}
|
|
1724
2732
|
|
|
1725
2733
|
// packages/memory/src/pages.ts
|
|
1726
2734
|
import * as fs2 from "node:fs/promises";
|
|
1727
|
-
import
|
|
2735
|
+
import path13 from "node:path";
|
|
1728
2736
|
|
|
1729
2737
|
// packages/memory/src/frontmatter.ts
|
|
1730
2738
|
import { parse as parse5, stringify } from "yaml";
|
|
@@ -1870,7 +2878,7 @@ function parseYamlFrontmatter(yamlBlock) {
|
|
|
1870
2878
|
if (parsed === null) {
|
|
1871
2879
|
return {};
|
|
1872
2880
|
}
|
|
1873
|
-
if (!
|
|
2881
|
+
if (!isRecord5(parsed)) {
|
|
1874
2882
|
throw new Error("YAML frontmatter must parse to an object.");
|
|
1875
2883
|
}
|
|
1876
2884
|
return parsed;
|
|
@@ -1901,13 +2909,13 @@ function parseSources(value) {
|
|
|
1901
2909
|
if (typeof item === "string") {
|
|
1902
2910
|
return parseSourceRef(item);
|
|
1903
2911
|
}
|
|
1904
|
-
if (!
|
|
2912
|
+
if (!isRecord5(item)) {
|
|
1905
2913
|
throw new Error('Invalid "sources" frontmatter. Expected each source to be a string or object.');
|
|
1906
2914
|
}
|
|
1907
|
-
const
|
|
2915
|
+
const path38 = readRequiredString(item.path, "sources[].path");
|
|
1908
2916
|
const startLine = readOptionalPositiveInteger(item.startLine, "sources[].startLine");
|
|
1909
2917
|
const endLine = readOptionalPositiveInteger(item.endLine, "sources[].endLine");
|
|
1910
|
-
return parseSourceRef(serializeSourceRef({ path:
|
|
2918
|
+
return parseSourceRef(serializeSourceRef({ path: path38, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
|
|
1911
2919
|
});
|
|
1912
2920
|
}
|
|
1913
2921
|
function readOptionalString(value, field) {
|
|
@@ -1940,7 +2948,7 @@ function assertValidLineNumber(line, value) {
|
|
|
1940
2948
|
throw new Error(`Invalid source ref "${value}": line numbers must be positive integers.`);
|
|
1941
2949
|
}
|
|
1942
2950
|
}
|
|
1943
|
-
function
|
|
2951
|
+
function isRecord5(value) {
|
|
1944
2952
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1945
2953
|
}
|
|
1946
2954
|
|
|
@@ -1952,8 +2960,8 @@ async function listPages(root) {
|
|
|
1952
2960
|
}
|
|
1953
2961
|
async function readPage(root, relPath) {
|
|
1954
2962
|
const normalizedRelPath = assertMarkdownRelPath(relPath);
|
|
1955
|
-
const absPath =
|
|
1956
|
-
const [content,
|
|
2963
|
+
const absPath = path13.join(root, normalizedRelPath);
|
|
2964
|
+
const [content, stat7] = await Promise.all([fs2.readFile(absPath, "utf8"), fs2.stat(absPath)]);
|
|
1957
2965
|
try {
|
|
1958
2966
|
const parsed = parseFrontmatter(content);
|
|
1959
2967
|
return {
|
|
@@ -1961,7 +2969,7 @@ async function readPage(root, relPath) {
|
|
|
1961
2969
|
frontmatter: parsed.frontmatter,
|
|
1962
2970
|
body: parsed.body,
|
|
1963
2971
|
bytes: Buffer.byteLength(content),
|
|
1964
|
-
mtimeMs:
|
|
2972
|
+
mtimeMs: stat7.mtimeMs
|
|
1965
2973
|
};
|
|
1966
2974
|
} catch (error2) {
|
|
1967
2975
|
const message2 = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -1971,7 +2979,7 @@ async function readPage(root, relPath) {
|
|
|
1971
2979
|
frontmatter: {},
|
|
1972
2980
|
body: content,
|
|
1973
2981
|
bytes: Buffer.byteLength(content),
|
|
1974
|
-
mtimeMs:
|
|
2982
|
+
mtimeMs: stat7.mtimeMs
|
|
1975
2983
|
};
|
|
1976
2984
|
}
|
|
1977
2985
|
}
|
|
@@ -1981,7 +2989,7 @@ async function collectMarkdownRelPaths(root, startRelPath = "") {
|
|
|
1981
2989
|
return relPaths.sort((left, right) => left.localeCompare(right));
|
|
1982
2990
|
}
|
|
1983
2991
|
async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
|
|
1984
|
-
const absPath =
|
|
2992
|
+
const absPath = path13.join(root, currentRelPath);
|
|
1985
2993
|
let entryNames;
|
|
1986
2994
|
try {
|
|
1987
2995
|
entryNames = await fs2.readdir(absPath);
|
|
@@ -1992,8 +3000,8 @@ async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
|
|
|
1992
3000
|
throw error2;
|
|
1993
3001
|
}
|
|
1994
3002
|
for (const entryName of entryNames.sort((left, right) => left.localeCompare(right))) {
|
|
1995
|
-
const entryRelPath = currentRelPath.length === 0 ? entryName :
|
|
1996
|
-
const entryAbsPath =
|
|
3003
|
+
const entryRelPath = currentRelPath.length === 0 ? entryName : path13.posix.join(currentRelPath, entryName);
|
|
3004
|
+
const entryAbsPath = path13.join(root, entryRelPath);
|
|
1997
3005
|
const entryStat = await fs2.stat(entryAbsPath);
|
|
1998
3006
|
if (entryStat.isDirectory()) {
|
|
1999
3007
|
if (entryName === MEMORY_CACHE_DIR_RELPATH) {
|
|
@@ -2023,7 +3031,7 @@ function assertMarkdownRelPath(relPath) {
|
|
|
2023
3031
|
return normalizedRelPath;
|
|
2024
3032
|
}
|
|
2025
3033
|
function isMarkdownPath(relPath) {
|
|
2026
|
-
return
|
|
3034
|
+
return path13.posix.extname(relPath).toLowerCase() === ".md";
|
|
2027
3035
|
}
|
|
2028
3036
|
function isMissing(error2) {
|
|
2029
3037
|
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
@@ -2031,7 +3039,7 @@ function isMissing(error2) {
|
|
|
2031
3039
|
|
|
2032
3040
|
// packages/memory/src/search.ts
|
|
2033
3041
|
import * as fs3 from "node:fs/promises";
|
|
2034
|
-
import
|
|
3042
|
+
import path14 from "node:path";
|
|
2035
3043
|
async function searchMemory(root, query) {
|
|
2036
3044
|
const normalizedQuery = query.trim();
|
|
2037
3045
|
if (normalizedQuery.length === 0) {
|
|
@@ -2040,7 +3048,7 @@ async function searchMemory(root, query) {
|
|
|
2040
3048
|
const relPaths = await collectMarkdownRelPaths(root);
|
|
2041
3049
|
const hits = [];
|
|
2042
3050
|
for (const relPath of relPaths) {
|
|
2043
|
-
const content = await fs3.readFile(
|
|
3051
|
+
const content = await fs3.readFile(path14.join(root, relPath), "utf8");
|
|
2044
3052
|
if (content.length === 0) {
|
|
2045
3053
|
continue;
|
|
2046
3054
|
}
|
|
@@ -2061,7 +3069,7 @@ async function searchMemory(root, query) {
|
|
|
2061
3069
|
|
|
2062
3070
|
// packages/memory/src/status.ts
|
|
2063
3071
|
import * as fs4 from "node:fs/promises";
|
|
2064
|
-
import
|
|
3072
|
+
import path15 from "node:path";
|
|
2065
3073
|
async function statusOf(root) {
|
|
2066
3074
|
if (!await pathExists2(root)) {
|
|
2067
3075
|
return {
|
|
@@ -2078,9 +3086,9 @@ async function statusOf(root) {
|
|
|
2078
3086
|
let totalBytes = 0;
|
|
2079
3087
|
let lastWriteAtMs = Number.NEGATIVE_INFINITY;
|
|
2080
3088
|
for (const relPath of markdownRelPaths) {
|
|
2081
|
-
const
|
|
2082
|
-
totalBytes +=
|
|
2083
|
-
lastWriteAtMs = Math.max(lastWriteAtMs,
|
|
3089
|
+
const stat7 = await fs4.stat(path15.join(root, relPath));
|
|
3090
|
+
totalBytes += stat7.size;
|
|
3091
|
+
lastWriteAtMs = Math.max(lastWriteAtMs, stat7.mtimeMs);
|
|
2084
3092
|
}
|
|
2085
3093
|
return {
|
|
2086
3094
|
pageCount: pageRelPaths.length,
|
|
@@ -2103,26 +3111,26 @@ async function pathExists2(targetPath) {
|
|
|
2103
3111
|
|
|
2104
3112
|
// packages/memory/src/edit.ts
|
|
2105
3113
|
import * as fs7 from "node:fs/promises";
|
|
2106
|
-
import
|
|
3114
|
+
import path19 from "node:path";
|
|
2107
3115
|
|
|
2108
3116
|
// packages/memory/src/write.ts
|
|
2109
3117
|
import * as fs6 from "node:fs/promises";
|
|
2110
|
-
import
|
|
3118
|
+
import path18 from "node:path";
|
|
2111
3119
|
|
|
2112
3120
|
// packages/memory/src/lock.ts
|
|
2113
|
-
import * as
|
|
2114
|
-
import
|
|
2115
|
-
function
|
|
3121
|
+
import * as fsPromises2 from "node:fs/promises";
|
|
3122
|
+
import path16 from "node:path";
|
|
3123
|
+
function createDefaultFs2() {
|
|
2116
3124
|
return {
|
|
2117
|
-
readFile: (filePath, encoding) =>
|
|
2118
|
-
unlink:
|
|
2119
|
-
writeFile: (filePath, data, options) =>
|
|
3125
|
+
readFile: (filePath, encoding) => fsPromises2.readFile(filePath, encoding),
|
|
3126
|
+
unlink: fsPromises2.unlink,
|
|
3127
|
+
writeFile: (filePath, data, options) => fsPromises2.writeFile(filePath, data, options)
|
|
2120
3128
|
};
|
|
2121
3129
|
}
|
|
2122
|
-
function
|
|
3130
|
+
function sleep2(ms) {
|
|
2123
3131
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2124
3132
|
}
|
|
2125
|
-
function
|
|
3133
|
+
function hasErrorCode3(error2, code) {
|
|
2126
3134
|
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === code;
|
|
2127
3135
|
}
|
|
2128
3136
|
function lockDelay(attempt, minTimeoutMs, maxTimeoutMs) {
|
|
@@ -2141,19 +3149,19 @@ function parsePid(input) {
|
|
|
2141
3149
|
const pid = Number.parseInt(value, 10);
|
|
2142
3150
|
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
2143
3151
|
}
|
|
2144
|
-
function
|
|
3152
|
+
function isPidRunning2(pid) {
|
|
2145
3153
|
try {
|
|
2146
3154
|
process.kill(pid, 0);
|
|
2147
3155
|
return true;
|
|
2148
3156
|
} catch (error2) {
|
|
2149
|
-
return !
|
|
3157
|
+
return !hasErrorCode3(error2, "ESRCH");
|
|
2150
3158
|
}
|
|
2151
3159
|
}
|
|
2152
|
-
async function
|
|
3160
|
+
async function removeLockFile2(fs14, lockPath) {
|
|
2153
3161
|
try {
|
|
2154
3162
|
await fs14.unlink(lockPath);
|
|
2155
3163
|
} catch (error2) {
|
|
2156
|
-
if (!
|
|
3164
|
+
if (!hasErrorCode3(error2, "ENOENT")) {
|
|
2157
3165
|
throw error2;
|
|
2158
3166
|
}
|
|
2159
3167
|
}
|
|
@@ -2162,20 +3170,20 @@ async function readLockPid(fs14, lockPath) {
|
|
|
2162
3170
|
try {
|
|
2163
3171
|
return parsePid(await fs14.readFile(lockPath, "utf8"));
|
|
2164
3172
|
} catch (error2) {
|
|
2165
|
-
if (
|
|
3173
|
+
if (hasErrorCode3(error2, "ENOENT")) {
|
|
2166
3174
|
return null;
|
|
2167
3175
|
}
|
|
2168
3176
|
throw error2;
|
|
2169
3177
|
}
|
|
2170
3178
|
}
|
|
2171
3179
|
async function withLock(root, run, options = {}) {
|
|
2172
|
-
const fs14 = options.fs ??
|
|
2173
|
-
const lockPath =
|
|
3180
|
+
const fs14 = options.fs ?? createDefaultFs2();
|
|
3181
|
+
const lockPath = path16.join(root, MEMORY_LOCK_RELPATH);
|
|
2174
3182
|
const pid = options.pid ?? process.pid;
|
|
2175
3183
|
const retries = options.retries ?? 20;
|
|
2176
3184
|
const minTimeoutMs = options.minTimeoutMs ?? 25;
|
|
2177
3185
|
const maxTimeoutMs = options.maxTimeoutMs ?? 250;
|
|
2178
|
-
const pidIsRunning = options.isPidRunning ??
|
|
3186
|
+
const pidIsRunning = options.isPidRunning ?? isPidRunning2;
|
|
2179
3187
|
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
2180
3188
|
try {
|
|
2181
3189
|
await fs14.writeFile(lockPath, `${pid}
|
|
@@ -2183,10 +3191,10 @@ async function withLock(root, run, options = {}) {
|
|
|
2183
3191
|
try {
|
|
2184
3192
|
return await run();
|
|
2185
3193
|
} finally {
|
|
2186
|
-
await
|
|
3194
|
+
await removeLockFile2(fs14, lockPath);
|
|
2187
3195
|
}
|
|
2188
3196
|
} catch (error2) {
|
|
2189
|
-
if (!
|
|
3197
|
+
if (!hasErrorCode3(error2, "EEXIST")) {
|
|
2190
3198
|
throw error2;
|
|
2191
3199
|
}
|
|
2192
3200
|
}
|
|
@@ -2195,11 +3203,11 @@ async function withLock(root, run, options = {}) {
|
|
|
2195
3203
|
continue;
|
|
2196
3204
|
}
|
|
2197
3205
|
if (existingPid === void 0 || !pidIsRunning(existingPid)) {
|
|
2198
|
-
await
|
|
3206
|
+
await removeLockFile2(fs14, lockPath);
|
|
2199
3207
|
continue;
|
|
2200
3208
|
}
|
|
2201
3209
|
if (attempt < retries) {
|
|
2202
|
-
await
|
|
3210
|
+
await sleep2(lockDelay(attempt, minTimeoutMs, maxTimeoutMs));
|
|
2203
3211
|
}
|
|
2204
3212
|
}
|
|
2205
3213
|
throw new Error(`Failed to acquire memory lock at "${lockPath}".`);
|
|
@@ -2208,7 +3216,7 @@ async function withLock(root, run, options = {}) {
|
|
|
2208
3216
|
// packages/memory/src/reconcile.ts
|
|
2209
3217
|
import { createHash } from "node:crypto";
|
|
2210
3218
|
import * as fs5 from "node:fs/promises";
|
|
2211
|
-
import
|
|
3219
|
+
import path17 from "node:path";
|
|
2212
3220
|
|
|
2213
3221
|
// packages/memory/src/confidence.ts
|
|
2214
3222
|
var TAG_RE = /^<!--\s*memory:(?<verb>extracted|inferred|ambiguous)(?<rest>[^>]*?)-->\s*$/;
|
|
@@ -2420,7 +3428,7 @@ async function snapshot(root) {
|
|
|
2420
3428
|
await Promise.all(
|
|
2421
3429
|
(await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH)).map(async (relPath) => [
|
|
2422
3430
|
relPath,
|
|
2423
|
-
hashContent(await fs5.readFile(
|
|
3431
|
+
hashContent(await fs5.readFile(path17.join(root, relPath), "utf8"))
|
|
2424
3432
|
])
|
|
2425
3433
|
)
|
|
2426
3434
|
);
|
|
@@ -2431,7 +3439,7 @@ async function reconcile(root, before, _verb, detail) {
|
|
|
2431
3439
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2432
3440
|
const currentPages = await Promise.all(
|
|
2433
3441
|
(await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH)).map(async (relPath) => {
|
|
2434
|
-
const absPath =
|
|
3442
|
+
const absPath = path17.join(root, relPath);
|
|
2435
3443
|
const markdown = await fs5.readFile(absPath, "utf8");
|
|
2436
3444
|
const parsed = parsePageMarkdown(relPath, markdown);
|
|
2437
3445
|
const normalizedFrontmatter = withDenormalizedSources(parsed.frontmatter, parsed.body);
|
|
@@ -2453,7 +3461,7 @@ async function reconcile(root, before, _verb, detail) {
|
|
|
2453
3461
|
})
|
|
2454
3462
|
);
|
|
2455
3463
|
await Promise.all(
|
|
2456
|
-
currentPages.filter((page) => page.currentMarkdown !== page.nextMarkdown).map((page) => fs5.writeFile(
|
|
3464
|
+
currentPages.filter((page) => page.currentMarkdown !== page.nextMarkdown).map((page) => fs5.writeFile(path17.join(root, page.relPath), page.nextMarkdown, "utf8"))
|
|
2457
3465
|
);
|
|
2458
3466
|
const diff2 = diffSnapshots(before, await snapshot(root));
|
|
2459
3467
|
await writeIndex(root);
|
|
@@ -2483,7 +3491,7 @@ async function appendLogEntries(root, diff2, detail, timestamp = (/* @__PURE__ *
|
|
|
2483
3491
|
if (entries.length === 0) {
|
|
2484
3492
|
return;
|
|
2485
3493
|
}
|
|
2486
|
-
const logPath =
|
|
3494
|
+
const logPath = path17.join(root, MEMORY_LOG_RELPATH);
|
|
2487
3495
|
const existing = await fs5.readFile(logPath, "utf8");
|
|
2488
3496
|
const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
2489
3497
|
await fs5.writeFile(logPath, `${existing}${separator}${entries.join("\n")}
|
|
@@ -2507,7 +3515,7 @@ async function writeIndex(root) {
|
|
|
2507
3515
|
description: page.frontmatter.description ?? ""
|
|
2508
3516
|
}))
|
|
2509
3517
|
);
|
|
2510
|
-
await fs5.writeFile(
|
|
3518
|
+
await fs5.writeFile(path17.join(root, MEMORY_INDEX_RELPATH), index, "utf8");
|
|
2511
3519
|
}
|
|
2512
3520
|
function parsePageMarkdown(relPath, markdown) {
|
|
2513
3521
|
try {
|
|
@@ -2552,9 +3560,9 @@ async function writePage(root, relPath, body, opts) {
|
|
|
2552
3560
|
const pageRelPath = assertPageRelPath(relPath);
|
|
2553
3561
|
return withLock(root, async () => {
|
|
2554
3562
|
const before = await snapshot(root);
|
|
2555
|
-
await fs6.mkdir(
|
|
3563
|
+
await fs6.mkdir(path18.dirname(path18.join(root, pageRelPath)), { recursive: true });
|
|
2556
3564
|
await fs6.writeFile(
|
|
2557
|
-
|
|
3565
|
+
path18.join(root, pageRelPath),
|
|
2558
3566
|
serializeFrontmatter(opts.frontmatter ?? {}, body),
|
|
2559
3567
|
"utf8"
|
|
2560
3568
|
);
|
|
@@ -2565,8 +3573,8 @@ async function appendToPage(root, relPath, content, opts) {
|
|
|
2565
3573
|
const pageRelPath = assertPageRelPath(relPath);
|
|
2566
3574
|
return withLock(root, async () => {
|
|
2567
3575
|
const before = await snapshot(root);
|
|
2568
|
-
const pagePath =
|
|
2569
|
-
await fs6.mkdir(
|
|
3576
|
+
const pagePath = path18.join(root, pageRelPath);
|
|
3577
|
+
await fs6.mkdir(path18.dirname(pagePath), { recursive: true });
|
|
2570
3578
|
const existing = await readMarkdownIfPresent(pagePath);
|
|
2571
3579
|
const parsed = existing === void 0 ? { frontmatter: {}, body: "" } : parseFrontmatter(existing);
|
|
2572
3580
|
await fs6.writeFile(
|
|
@@ -2588,13 +3596,13 @@ async function removeChildren(directoryPath) {
|
|
|
2588
3596
|
if (entryName === MEMORY_LOCK_RELPATH) {
|
|
2589
3597
|
continue;
|
|
2590
3598
|
}
|
|
2591
|
-
const entryPath =
|
|
2592
|
-
const
|
|
2593
|
-
if (
|
|
3599
|
+
const entryPath = path18.join(directoryPath, entryName);
|
|
3600
|
+
const stat7 = await fs6.stat(entryPath);
|
|
3601
|
+
if (stat7.isDirectory()) {
|
|
2594
3602
|
await removeDirectory2(entryPath);
|
|
2595
3603
|
continue;
|
|
2596
3604
|
}
|
|
2597
|
-
if (
|
|
3605
|
+
if (stat7.isFile()) {
|
|
2598
3606
|
await fs6.unlink(entryPath);
|
|
2599
3607
|
}
|
|
2600
3608
|
}
|
|
@@ -2605,7 +3613,7 @@ async function removeDirectory2(directoryPath) {
|
|
|
2605
3613
|
}
|
|
2606
3614
|
function assertPageRelPath(relPath) {
|
|
2607
3615
|
const normalizedRelPath = assertSafeRelPath(relPath);
|
|
2608
|
-
if (!normalizedRelPath.startsWith(`${MEMORY_PAGES_DIR_RELPATH}/`) ||
|
|
3616
|
+
if (!normalizedRelPath.startsWith(`${MEMORY_PAGES_DIR_RELPATH}/`) || path18.posix.extname(normalizedRelPath).toLowerCase() !== ".md") {
|
|
2609
3617
|
throw new Error(`Expected a markdown page path under "${MEMORY_PAGES_DIR_RELPATH}/".`);
|
|
2610
3618
|
}
|
|
2611
3619
|
return normalizedRelPath;
|
|
@@ -2623,12 +3631,12 @@ async function readMarkdownIfPresent(filePath) {
|
|
|
2623
3631
|
|
|
2624
3632
|
// packages/memory/src/edit.ts
|
|
2625
3633
|
async function editPage(root, relPath, opts) {
|
|
2626
|
-
const pagePath =
|
|
3634
|
+
const pagePath = path19.join(root, relPath);
|
|
2627
3635
|
const original = await readIfPresent(pagePath);
|
|
2628
|
-
const tempRoot =
|
|
3636
|
+
const tempRoot = path19.join(root, ".tmp");
|
|
2629
3637
|
await fs7.mkdir(tempRoot, { recursive: true });
|
|
2630
|
-
const tempDir = await fs7.mkdtemp(
|
|
2631
|
-
const tempPath =
|
|
3638
|
+
const tempDir = await fs7.mkdtemp(path19.join(tempRoot, "poe-code-memory-edit-"));
|
|
3639
|
+
const tempPath = path19.join(tempDir, path19.basename(relPath));
|
|
2632
3640
|
try {
|
|
2633
3641
|
await fs7.writeFile(tempPath, original ?? "", "utf8");
|
|
2634
3642
|
await opts.launchEditor(tempPath);
|
|
@@ -2662,7 +3670,7 @@ async function readIfPresent(filePath) {
|
|
|
2662
3670
|
|
|
2663
3671
|
// packages/memory/src/audit.ts
|
|
2664
3672
|
import * as fs8 from "node:fs/promises";
|
|
2665
|
-
import
|
|
3673
|
+
import path20 from "node:path";
|
|
2666
3674
|
var DEFAULT_MIN_INFERRED_CONFIDENCE = 0.3;
|
|
2667
3675
|
var DEFAULT_REJECT_UNTAGGED = false;
|
|
2668
3676
|
var DEFAULT_UNTAGGED_BODY_THRESHOLD_CHARS = 200;
|
|
@@ -2717,10 +3725,10 @@ async function auditSourceRef(source, claimLineNumber, repoRoot, sourceCache) {
|
|
|
2717
3725
|
if (isUrlLike(source.path)) {
|
|
2718
3726
|
return void 0;
|
|
2719
3727
|
}
|
|
2720
|
-
if (
|
|
3728
|
+
if (path20.isAbsolute(source.path)) {
|
|
2721
3729
|
return `Claim on line ${claimLineNumber} cites "${serializeSourceRef(source)}", but source paths must be repo-relative or URLs.`;
|
|
2722
3730
|
}
|
|
2723
|
-
const absPath =
|
|
3731
|
+
const absPath = path20.resolve(repoRoot, source.path);
|
|
2724
3732
|
if (!isWithinRoot(repoRoot, absPath)) {
|
|
2725
3733
|
return `Claim on line ${claimLineNumber} cites "${serializeSourceRef(source)}", which resolves outside the repo root.`;
|
|
2726
3734
|
}
|
|
@@ -2786,14 +3794,14 @@ function isUrlLike(value) {
|
|
|
2786
3794
|
return /^[a-z][a-z\d+.-]*:\/\//i.test(value);
|
|
2787
3795
|
}
|
|
2788
3796
|
function isWithinRoot(root, absPath) {
|
|
2789
|
-
const relative =
|
|
2790
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3797
|
+
const relative = path20.relative(root, absPath);
|
|
3798
|
+
return relative === "" || !relative.startsWith("..") && !path20.isAbsolute(relative);
|
|
2791
3799
|
}
|
|
2792
3800
|
|
|
2793
3801
|
// packages/memory/src/cache.ts
|
|
2794
3802
|
import { createHash as createHash2 } from "node:crypto";
|
|
2795
3803
|
import * as fs9 from "node:fs/promises";
|
|
2796
|
-
import
|
|
3804
|
+
import path21 from "node:path";
|
|
2797
3805
|
function computeIngestKey(input) {
|
|
2798
3806
|
const hash = createHash2("sha256");
|
|
2799
3807
|
hash.update(input.sourceBytes);
|
|
@@ -2806,7 +3814,7 @@ function computeIngestKey(input) {
|
|
|
2806
3814
|
return hash.digest("hex");
|
|
2807
3815
|
}
|
|
2808
3816
|
async function readCacheEntry(root, key) {
|
|
2809
|
-
const cachePath =
|
|
3817
|
+
const cachePath = path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${key}.json`);
|
|
2810
3818
|
let raw;
|
|
2811
3819
|
try {
|
|
2812
3820
|
raw = await fs9.readFile(cachePath, "utf8");
|
|
@@ -2825,17 +3833,17 @@ async function readCacheEntry(root, key) {
|
|
|
2825
3833
|
}
|
|
2826
3834
|
}
|
|
2827
3835
|
async function writeCacheEntry(root, entry) {
|
|
2828
|
-
await fs9.mkdir(
|
|
3836
|
+
await fs9.mkdir(path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH), { recursive: true });
|
|
2829
3837
|
await fs9.writeFile(
|
|
2830
|
-
|
|
3838
|
+
path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH, `${entry.key}.json`),
|
|
2831
3839
|
`${JSON.stringify(entry)}
|
|
2832
3840
|
`,
|
|
2833
3841
|
"utf8"
|
|
2834
3842
|
);
|
|
2835
3843
|
}
|
|
2836
3844
|
async function clearCache(root, opts = {}) {
|
|
2837
|
-
const ingestDir =
|
|
2838
|
-
const cacheDir =
|
|
3845
|
+
const ingestDir = path21.join(root, MEMORY_INGEST_CACHE_DIR_RELPATH);
|
|
3846
|
+
const cacheDir = path21.join(root, MEMORY_CACHE_DIR_RELPATH);
|
|
2839
3847
|
const fileNames = await readCacheFileNames(ingestDir);
|
|
2840
3848
|
if (fileNames.length === 0) {
|
|
2841
3849
|
if (opts.olderThanMs === void 0) {
|
|
@@ -2855,7 +3863,7 @@ async function clearCache(root, opts = {}) {
|
|
|
2855
3863
|
if (entry === null || Date.parse(entry.ingestedAt) > cutoff) {
|
|
2856
3864
|
continue;
|
|
2857
3865
|
}
|
|
2858
|
-
await fs9.rm(
|
|
3866
|
+
await fs9.rm(path21.join(ingestDir, fileName), { force: true });
|
|
2859
3867
|
removed += 1;
|
|
2860
3868
|
}
|
|
2861
3869
|
await removeEmptyDirectory(ingestDir);
|
|
@@ -2911,7 +3919,7 @@ function expectStringArray(value, field) {
|
|
|
2911
3919
|
}
|
|
2912
3920
|
async function readCacheFileNames(ingestDir) {
|
|
2913
3921
|
try {
|
|
2914
|
-
return (await fs9.readdir(ingestDir)).filter((fileName) =>
|
|
3922
|
+
return (await fs9.readdir(ingestDir)).filter((fileName) => path21.posix.extname(fileName).toLowerCase() === ".json").sort((left, right) => left.localeCompare(right));
|
|
2915
3923
|
} catch (error2) {
|
|
2916
3924
|
if (isMissing3(error2)) {
|
|
2917
3925
|
return [];
|
|
@@ -2931,42 +3939,1406 @@ async function removeEmptyDirectory(directoryPath) {
|
|
|
2931
3939
|
}
|
|
2932
3940
|
throw error2;
|
|
2933
3941
|
}
|
|
2934
|
-
}
|
|
2935
|
-
function isMissing3(error2) {
|
|
2936
|
-
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
2937
|
-
}
|
|
3942
|
+
}
|
|
3943
|
+
function isMissing3(error2) {
|
|
3944
|
+
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
// packages/memory/src/cache.cli.ts
|
|
3948
|
+
import parseDuration from "parse-duration";
|
|
3949
|
+
async function runMemoryCacheStatus() {
|
|
3950
|
+
console.log("cache status not implemented yet");
|
|
3951
|
+
}
|
|
3952
|
+
async function runMemoryCacheClear(input) {
|
|
3953
|
+
if (!input.yes) {
|
|
3954
|
+
throw new Error("Refusing to clear cache without --yes.");
|
|
3955
|
+
}
|
|
3956
|
+
const olderThanMs = parseOlderThan(input.olderThan);
|
|
3957
|
+
const result = await clearCache(input.root, olderThanMs === void 0 ? {} : { olderThanMs });
|
|
3958
|
+
console.log(`removed ${result.removed} cache ${result.removed === 1 ? "entry" : "entries"}`);
|
|
3959
|
+
return result;
|
|
3960
|
+
}
|
|
3961
|
+
function parseOlderThan(value) {
|
|
3962
|
+
if (value === void 0) {
|
|
3963
|
+
return void 0;
|
|
3964
|
+
}
|
|
3965
|
+
const duration = parseDuration(value);
|
|
3966
|
+
if (duration === null || Number.isNaN(duration) || duration < 0) {
|
|
3967
|
+
throw new Error(`Invalid duration for --older-than: "${value}".`);
|
|
3968
|
+
}
|
|
3969
|
+
return duration;
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3972
|
+
// packages/memory/src/ingest.ts
|
|
3973
|
+
import * as fs11 from "node:fs/promises";
|
|
3974
|
+
import path31 from "node:path";
|
|
3975
|
+
|
|
3976
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
3977
|
+
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
3978
|
+
|
|
3979
|
+
// packages/agent-harness-tools/src/paths.ts
|
|
3980
|
+
import path22 from "node:path";
|
|
3981
|
+
|
|
3982
|
+
// packages/agent-defs/src/agents/claude-code.ts
|
|
3983
|
+
var claudeCodeAgent = {
|
|
3984
|
+
id: "claude-code",
|
|
3985
|
+
name: "claude-code",
|
|
3986
|
+
label: "Claude Code",
|
|
3987
|
+
summary: "Configure Claude Code to route through Poe.",
|
|
3988
|
+
aliases: ["claude"],
|
|
3989
|
+
binaryName: "claude",
|
|
3990
|
+
configPath: "~/.claude/settings.json",
|
|
3991
|
+
branding: {
|
|
3992
|
+
colors: {
|
|
3993
|
+
dark: "#C15F3C",
|
|
3994
|
+
light: "#C15F3C"
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
|
|
3999
|
+
// packages/agent-defs/src/agents/claude-desktop.ts
|
|
4000
|
+
var claudeDesktopAgent = {
|
|
4001
|
+
id: "claude-desktop",
|
|
4002
|
+
name: "claude-desktop",
|
|
4003
|
+
label: "Claude Desktop",
|
|
4004
|
+
summary: "Anthropic's official desktop application for Claude",
|
|
4005
|
+
configPath: "~/.claude/settings.json",
|
|
4006
|
+
branding: {
|
|
4007
|
+
colors: {
|
|
4008
|
+
dark: "#D97757",
|
|
4009
|
+
light: "#D97757"
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
};
|
|
4013
|
+
|
|
4014
|
+
// packages/agent-defs/src/agents/codex.ts
|
|
4015
|
+
var codexAgent = {
|
|
4016
|
+
id: "codex",
|
|
4017
|
+
name: "codex",
|
|
4018
|
+
label: "Codex",
|
|
4019
|
+
summary: "Configure Codex to use Poe as the model provider.",
|
|
4020
|
+
binaryName: "codex",
|
|
4021
|
+
configPath: "~/.codex/config.toml",
|
|
4022
|
+
branding: {
|
|
4023
|
+
colors: {
|
|
4024
|
+
dark: "#D5D9DF",
|
|
4025
|
+
light: "#7A7F86"
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
};
|
|
4029
|
+
|
|
4030
|
+
// packages/agent-defs/src/agents/opencode.ts
|
|
4031
|
+
var openCodeAgent = {
|
|
4032
|
+
id: "opencode",
|
|
4033
|
+
name: "opencode",
|
|
4034
|
+
label: "OpenCode CLI",
|
|
4035
|
+
summary: "Configure OpenCode CLI to use the Poe API.",
|
|
4036
|
+
binaryName: "opencode",
|
|
4037
|
+
configPath: "~/.config/opencode/config.json",
|
|
4038
|
+
branding: {
|
|
4039
|
+
colors: {
|
|
4040
|
+
dark: "#4A4F55",
|
|
4041
|
+
light: "#2F3338"
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
};
|
|
4045
|
+
|
|
4046
|
+
// packages/agent-defs/src/agents/kimi.ts
|
|
4047
|
+
var kimiAgent = {
|
|
4048
|
+
id: "kimi",
|
|
4049
|
+
name: "kimi",
|
|
4050
|
+
label: "Kimi",
|
|
4051
|
+
summary: "Configure Kimi CLI to use Poe API",
|
|
4052
|
+
aliases: ["kimi-cli"],
|
|
4053
|
+
binaryName: "kimi",
|
|
4054
|
+
configPath: "~/.kimi/config.toml",
|
|
4055
|
+
branding: {
|
|
4056
|
+
colors: {
|
|
4057
|
+
dark: "#7B68EE",
|
|
4058
|
+
light: "#6A5ACD"
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
};
|
|
4062
|
+
|
|
4063
|
+
// packages/agent-defs/src/agents/goose.ts
|
|
4064
|
+
var gooseAgent = {
|
|
4065
|
+
id: "goose",
|
|
4066
|
+
name: "goose",
|
|
4067
|
+
label: "Goose",
|
|
4068
|
+
summary: "Block's open-source AI agent with ACP support.",
|
|
4069
|
+
binaryName: "goose",
|
|
4070
|
+
configPath: "~/.config/goose/config.yaml",
|
|
4071
|
+
branding: {
|
|
4072
|
+
colors: {
|
|
4073
|
+
dark: "#FF6B35",
|
|
4074
|
+
light: "#E85D26"
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
};
|
|
4078
|
+
|
|
4079
|
+
// packages/agent-defs/src/agents/poe-agent.ts
|
|
4080
|
+
var poeAgentAgent = {
|
|
4081
|
+
id: "poe-agent",
|
|
4082
|
+
name: "poe-agent",
|
|
4083
|
+
label: "Poe Agent",
|
|
4084
|
+
summary: "Run one-shot prompts with the built-in Poe agent runtime.",
|
|
4085
|
+
configPath: "~/.poe-code/config.json",
|
|
4086
|
+
branding: {
|
|
4087
|
+
colors: {
|
|
4088
|
+
dark: "#A465F7",
|
|
4089
|
+
light: "#7A3FD3"
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
};
|
|
4093
|
+
|
|
4094
|
+
// packages/agent-defs/src/registry.ts
|
|
4095
|
+
var allAgents = [
|
|
4096
|
+
claudeCodeAgent,
|
|
4097
|
+
claudeDesktopAgent,
|
|
4098
|
+
codexAgent,
|
|
4099
|
+
openCodeAgent,
|
|
4100
|
+
kimiAgent,
|
|
4101
|
+
gooseAgent,
|
|
4102
|
+
poeAgentAgent
|
|
4103
|
+
];
|
|
4104
|
+
var lookup = /* @__PURE__ */ new Map();
|
|
4105
|
+
for (const agent of allAgents) {
|
|
4106
|
+
const values = [agent.id, agent.name, ...agent.aliases ?? []];
|
|
4107
|
+
for (const value of values) {
|
|
4108
|
+
const normalized = value.toLowerCase();
|
|
4109
|
+
if (!lookup.has(normalized)) {
|
|
4110
|
+
lookup.set(normalized, agent.id);
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function resolveAgentId(input) {
|
|
4115
|
+
if (!input) {
|
|
4116
|
+
return void 0;
|
|
4117
|
+
}
|
|
4118
|
+
return lookup.get(input.toLowerCase());
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
// packages/agent-harness-tools/src/select-agent.ts
|
|
4122
|
+
var loopAgents = allAgents.filter(
|
|
4123
|
+
(agent) => agent.binaryName !== void 0 || agent.id === "poe-agent"
|
|
4124
|
+
);
|
|
4125
|
+
var supportedAgents = loopAgents.map((agent) => agent.id).join(", ");
|
|
4126
|
+
|
|
4127
|
+
// packages/agent-harness-tools/src/run-logs.ts
|
|
4128
|
+
import path23 from "node:path";
|
|
4129
|
+
|
|
4130
|
+
// packages/agent-harness-tools/src/log-stream.ts
|
|
4131
|
+
import nodeFs2 from "node:fs";
|
|
4132
|
+
var JOB_DIR = "/tmp/poe-jobs";
|
|
4133
|
+
var POLL_INTERVAL_MS = 250;
|
|
4134
|
+
function wrapForLogTee(argv, jobId) {
|
|
4135
|
+
if (argv.length === 0) {
|
|
4136
|
+
throw new Error("wrapForLogTee requires argv to contain at least one argument");
|
|
4137
|
+
}
|
|
4138
|
+
const command = argv.map(shellQuote).join(" ");
|
|
4139
|
+
const logFile = shellQuote(jobLogPath(jobId));
|
|
4140
|
+
const exitFile = shellQuote(jobExitPath(jobId));
|
|
4141
|
+
const exitTmpFile = shellQuote(`${jobExitPath(jobId)}.tmp`);
|
|
4142
|
+
const script = [
|
|
4143
|
+
`mkdir -p ${shellQuote(JOB_DIR)}`,
|
|
4144
|
+
`({ (${command}); echo $? > ${exitTmpFile}; } 2>&1 | tee ${logFile}; mv ${exitTmpFile} ${exitFile})`
|
|
4145
|
+
].join(" && ");
|
|
4146
|
+
return ["sh", "-c", script];
|
|
4147
|
+
}
|
|
4148
|
+
async function waitForExit(env, jobId, opts = {}) {
|
|
4149
|
+
const fs14 = env.fs ?? nodeFs2;
|
|
4150
|
+
const file = jobExitPath(jobId);
|
|
4151
|
+
while (true) {
|
|
4152
|
+
throwIfAborted2(opts.signal);
|
|
4153
|
+
const contents = await readTextFileIfExists(fs14, file);
|
|
4154
|
+
if (contents !== null) {
|
|
4155
|
+
const text4 = contents.trim();
|
|
4156
|
+
const exitCode = Number(text4);
|
|
4157
|
+
if (text4.length === 0 || !Number.isInteger(exitCode)) {
|
|
4158
|
+
throw new Error(`Invalid exit code in ${file}: ${contents}`);
|
|
4159
|
+
}
|
|
4160
|
+
return { exitCode };
|
|
4161
|
+
}
|
|
4162
|
+
await sleep3(POLL_INTERVAL_MS, opts.signal);
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
function jobLogPath(jobId) {
|
|
4166
|
+
return `${JOB_DIR}/${jobId}.log`;
|
|
4167
|
+
}
|
|
4168
|
+
function jobExitPath(jobId) {
|
|
4169
|
+
return `${JOB_DIR}/${jobId}.exit`;
|
|
4170
|
+
}
|
|
4171
|
+
async function readTextFileIfExists(fs14, file) {
|
|
4172
|
+
const contents = await readFileIfExists2(fs14, file);
|
|
4173
|
+
return contents?.toString("utf8") ?? null;
|
|
4174
|
+
}
|
|
4175
|
+
async function readFileIfExists2(fs14, file) {
|
|
4176
|
+
try {
|
|
4177
|
+
const contents = await fs14.promises.readFile(file);
|
|
4178
|
+
return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
4179
|
+
} catch (error2) {
|
|
4180
|
+
if (isNodeError(error2) && error2.code === "ENOENT") {
|
|
4181
|
+
return null;
|
|
4182
|
+
}
|
|
4183
|
+
throw error2;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
function sleep3(ms, signal) {
|
|
4187
|
+
return new Promise((resolve2, reject) => {
|
|
4188
|
+
let timer = null;
|
|
4189
|
+
const abort = () => {
|
|
4190
|
+
if (timer !== null) {
|
|
4191
|
+
clearTimeout(timer);
|
|
4192
|
+
}
|
|
4193
|
+
reject(new Error("waitForExit aborted."));
|
|
4194
|
+
};
|
|
4195
|
+
if (signal?.aborted) {
|
|
4196
|
+
abort();
|
|
4197
|
+
return;
|
|
4198
|
+
}
|
|
4199
|
+
timer = setTimeout(() => {
|
|
4200
|
+
signal?.removeEventListener("abort", abort);
|
|
4201
|
+
resolve2();
|
|
4202
|
+
}, ms);
|
|
4203
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
function throwIfAborted2(signal) {
|
|
4207
|
+
if (signal?.aborted) {
|
|
4208
|
+
throw new Error("waitForExit aborted.");
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
function shellQuote(value) {
|
|
4212
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
4213
|
+
}
|
|
4214
|
+
function isNodeError(error2) {
|
|
4215
|
+
return error2 instanceof Error && "code" in error2;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
// packages/agent-harness-tools/src/run-poe-command.ts
|
|
4219
|
+
import { randomBytes } from "node:crypto";
|
|
4220
|
+
var ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
4221
|
+
async function runPoeCommand(opts) {
|
|
4222
|
+
const jobId = createUlid();
|
|
4223
|
+
const execution = opts.openSpec.execution;
|
|
4224
|
+
const wrapCommand = execution?.wrapForLogTee !== false;
|
|
4225
|
+
const pendingJob = opts.state.jobs.put({
|
|
4226
|
+
id: jobId,
|
|
4227
|
+
env_id: "",
|
|
4228
|
+
env_kind: opts.factory.type,
|
|
4229
|
+
tool: opts.openSpec.jobLabel.tool,
|
|
4230
|
+
argv: opts.openSpec.jobLabel.argv,
|
|
4231
|
+
cwd: opts.openSpec.cwd,
|
|
4232
|
+
started_at: "",
|
|
4233
|
+
status: "pending"
|
|
4234
|
+
});
|
|
4235
|
+
const opened = opts.factory.open(opts.openSpec);
|
|
4236
|
+
const env = isPromiseLike(opened) ? await opened : opened;
|
|
4237
|
+
let shouldClose = true;
|
|
4238
|
+
try {
|
|
4239
|
+
const upload = env.uploadWorkspace();
|
|
4240
|
+
const argv = wrapCommand ? wrapForLogTee(opts.openSpec.jobLabel.argv, jobId) : opts.openSpec.jobLabel.argv;
|
|
4241
|
+
const handle = execution?.tty ? env.shell() : env.exec({
|
|
4242
|
+
command: argv[0],
|
|
4243
|
+
args: argv.slice(1),
|
|
4244
|
+
cwd: opts.openSpec.cwd,
|
|
4245
|
+
env: execution && "env" in execution ? execution.env : opts.openSpec.env,
|
|
4246
|
+
stdin: execution?.stdin ?? "inherit",
|
|
4247
|
+
stdout: execution?.stdout ?? "pipe",
|
|
4248
|
+
stderr: execution?.stderr ?? "pipe",
|
|
4249
|
+
signal: opts.signal
|
|
4250
|
+
});
|
|
4251
|
+
if (execution?.input !== void 0) {
|
|
4252
|
+
handle.stdin?.setDefaultEncoding("utf8");
|
|
4253
|
+
handle.stdin?.end(execution.input);
|
|
4254
|
+
}
|
|
4255
|
+
const runningJob = Promise.all([pendingJob, upload]).then(
|
|
4256
|
+
() => opts.state.jobs.update(jobId, {
|
|
4257
|
+
status: "running",
|
|
4258
|
+
env_id: env.id,
|
|
4259
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4260
|
+
})
|
|
4261
|
+
);
|
|
4262
|
+
if (opts.detach) {
|
|
4263
|
+
await runningJob;
|
|
4264
|
+
shouldClose = false;
|
|
4265
|
+
return { kind: "detached", jobId, envId: env.id };
|
|
4266
|
+
}
|
|
4267
|
+
const result = await runSync({
|
|
4268
|
+
env,
|
|
4269
|
+
handle,
|
|
4270
|
+
jobId,
|
|
4271
|
+
openSpec: opts.openSpec,
|
|
4272
|
+
signal: opts.signal,
|
|
4273
|
+
wrapCommand
|
|
4274
|
+
});
|
|
4275
|
+
await runningJob;
|
|
4276
|
+
shouldClose = false;
|
|
4277
|
+
await opts.state.jobs.update(jobId, {
|
|
4278
|
+
status: "exited",
|
|
4279
|
+
exit_code: result.exitCode,
|
|
4280
|
+
exited_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4281
|
+
});
|
|
4282
|
+
return {
|
|
4283
|
+
kind: "sync",
|
|
4284
|
+
exitCode: result.exitCode,
|
|
4285
|
+
download: result.download,
|
|
4286
|
+
...result.stdout !== void 0 ? { stdout: result.stdout } : {},
|
|
4287
|
+
...result.stderr !== void 0 ? { stderr: result.stderr } : {}
|
|
4288
|
+
};
|
|
4289
|
+
} finally {
|
|
4290
|
+
if (shouldClose) {
|
|
4291
|
+
await env.close();
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
async function runSync(opts) {
|
|
4296
|
+
const execution = opts.openSpec.execution;
|
|
4297
|
+
const capture = execution?.captureOutput === true;
|
|
4298
|
+
const abort = createAbortSync(opts.signal, opts.handle, execution?.activityTimeoutMs);
|
|
4299
|
+
const streamState = capture ? captureRunStreams(opts.handle, execution, abort.resetActivityTimer) : pipeRunStreams(opts.handle);
|
|
4300
|
+
abort.resetActivityTimer();
|
|
4301
|
+
try {
|
|
4302
|
+
const { exitCode } = opts.wrapCommand ? await abort.waitForExit(opts.env, opts.jobId) : await abort.waitForHandle();
|
|
4303
|
+
const download = await opts.env.downloadWorkspace({
|
|
4304
|
+
conflictPolicy: opts.openSpec.runner?.download_conflict ?? "refuse"
|
|
4305
|
+
});
|
|
4306
|
+
await opts.env.close();
|
|
4307
|
+
return {
|
|
4308
|
+
exitCode,
|
|
4309
|
+
download,
|
|
4310
|
+
...capture ? { stdout: streamState.stdout(), stderr: streamState.stderr() } : {}
|
|
4311
|
+
};
|
|
4312
|
+
} finally {
|
|
4313
|
+
abort.dispose();
|
|
4314
|
+
streamState.dispose();
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
function pipeRunStreams(handle) {
|
|
4318
|
+
handle.stdout?.pipe(process.stdout, { end: false });
|
|
4319
|
+
handle.stderr?.pipe(process.stderr, { end: false });
|
|
4320
|
+
return {
|
|
4321
|
+
stdout: () => "",
|
|
4322
|
+
stderr: () => "",
|
|
4323
|
+
dispose() {
|
|
4324
|
+
handle.stdout?.unpipe(process.stdout);
|
|
4325
|
+
handle.stderr?.unpipe(process.stderr);
|
|
4326
|
+
}
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
function captureRunStreams(handle, execution, onActivity) {
|
|
4330
|
+
let stdout = "";
|
|
4331
|
+
let stderr = "";
|
|
4332
|
+
const listeners = [];
|
|
4333
|
+
const bind = (stream, onChunk) => {
|
|
4334
|
+
if (!stream) return;
|
|
4335
|
+
stream.setEncoding("utf8");
|
|
4336
|
+
const listener = (chunk) => {
|
|
4337
|
+
onActivity();
|
|
4338
|
+
onChunk(chunk.toString());
|
|
4339
|
+
};
|
|
4340
|
+
stream.on("data", listener);
|
|
4341
|
+
listeners.push(() => {
|
|
4342
|
+
stream.off("data", listener);
|
|
4343
|
+
});
|
|
4344
|
+
};
|
|
4345
|
+
bind(handle.stdout, (chunk) => {
|
|
4346
|
+
stdout += chunk;
|
|
4347
|
+
execution?.onStdout?.(chunk);
|
|
4348
|
+
});
|
|
4349
|
+
bind(handle.stderr, (chunk) => {
|
|
4350
|
+
stderr += chunk;
|
|
4351
|
+
execution?.onStderr?.(chunk);
|
|
4352
|
+
});
|
|
4353
|
+
return {
|
|
4354
|
+
stdout: () => stdout,
|
|
4355
|
+
stderr: () => stderr,
|
|
4356
|
+
dispose() {
|
|
4357
|
+
for (const remove2 of listeners) {
|
|
4358
|
+
remove2();
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
function createAbortSync(signal, handle, activityTimeoutMs) {
|
|
4364
|
+
let activityTimer;
|
|
4365
|
+
let timedOut = false;
|
|
4366
|
+
const resetActivityTimer = activityTimeoutMs ? () => {
|
|
4367
|
+
if (activityTimer) clearTimeout(activityTimer);
|
|
4368
|
+
activityTimer = setTimeout(() => {
|
|
4369
|
+
timedOut = true;
|
|
4370
|
+
handle.kill("SIGTERM");
|
|
4371
|
+
notifyAbort?.();
|
|
4372
|
+
}, activityTimeoutMs);
|
|
4373
|
+
} : () => {
|
|
4374
|
+
};
|
|
4375
|
+
let notifyAbort;
|
|
4376
|
+
if (signal === void 0) {
|
|
4377
|
+
return {
|
|
4378
|
+
waitForExit: (env, jobId) => waitForExit(toLogStreamEnv(env), jobId),
|
|
4379
|
+
waitForHandle: async () => {
|
|
4380
|
+
const result = await handle.result;
|
|
4381
|
+
if (timedOut) {
|
|
4382
|
+
throw createActivityTimeoutError(activityTimeoutMs);
|
|
4383
|
+
}
|
|
4384
|
+
return result;
|
|
4385
|
+
},
|
|
4386
|
+
resetActivityTimer,
|
|
4387
|
+
dispose() {
|
|
4388
|
+
if (activityTimer) clearTimeout(activityTimer);
|
|
4389
|
+
}
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
const exitWaitController = new AbortController();
|
|
4393
|
+
let aborted = signal.aborted;
|
|
4394
|
+
const abortedPromise = new Promise((resolve2) => {
|
|
4395
|
+
notifyAbort = resolve2;
|
|
4396
|
+
});
|
|
4397
|
+
const kill = () => {
|
|
4398
|
+
aborted = true;
|
|
4399
|
+
handle.kill("SIGTERM");
|
|
4400
|
+
notifyAbort?.();
|
|
4401
|
+
};
|
|
4402
|
+
if (signal.aborted) {
|
|
4403
|
+
kill();
|
|
4404
|
+
} else {
|
|
4405
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
4406
|
+
}
|
|
4407
|
+
return {
|
|
4408
|
+
async waitForExit(env, jobId) {
|
|
4409
|
+
if (aborted) {
|
|
4410
|
+
return handle.result;
|
|
4411
|
+
}
|
|
4412
|
+
const exit = waitForExit(toLogStreamEnv(env), jobId, {
|
|
4413
|
+
signal: exitWaitController.signal
|
|
4414
|
+
}).then(
|
|
4415
|
+
(value) => ({ kind: "exit", value }),
|
|
4416
|
+
(error2) => ({ kind: "error", error: error2 })
|
|
4417
|
+
);
|
|
4418
|
+
const result = await Promise.race([
|
|
4419
|
+
exit,
|
|
4420
|
+
abortedPromise.then(() => ({ kind: "abort" }))
|
|
4421
|
+
]);
|
|
4422
|
+
if (result.kind === "exit") {
|
|
4423
|
+
return result.value;
|
|
4424
|
+
}
|
|
4425
|
+
if (result.kind === "error") {
|
|
4426
|
+
throw result.error;
|
|
4427
|
+
}
|
|
4428
|
+
exitWaitController.abort();
|
|
4429
|
+
return handle.result;
|
|
4430
|
+
},
|
|
4431
|
+
async waitForHandle() {
|
|
4432
|
+
const result = await Promise.race([
|
|
4433
|
+
handle.result.then((value) => ({ kind: "exit", value })),
|
|
4434
|
+
abortedPromise.then(() => ({ kind: "abort" }))
|
|
4435
|
+
]);
|
|
4436
|
+
if (result.kind === "exit") {
|
|
4437
|
+
if (aborted) {
|
|
4438
|
+
throw createAbortError2();
|
|
4439
|
+
}
|
|
4440
|
+
if (timedOut) {
|
|
4441
|
+
throw createActivityTimeoutError(activityTimeoutMs);
|
|
4442
|
+
}
|
|
4443
|
+
return result.value;
|
|
4444
|
+
}
|
|
4445
|
+
if (timedOut) {
|
|
4446
|
+
throw createActivityTimeoutError(activityTimeoutMs);
|
|
4447
|
+
}
|
|
4448
|
+
throw createAbortError2();
|
|
4449
|
+
},
|
|
4450
|
+
resetActivityTimer,
|
|
4451
|
+
dispose() {
|
|
4452
|
+
if (activityTimer) clearTimeout(activityTimer);
|
|
4453
|
+
exitWaitController.abort();
|
|
4454
|
+
signal.removeEventListener("abort", kill);
|
|
4455
|
+
}
|
|
4456
|
+
};
|
|
4457
|
+
}
|
|
4458
|
+
function toLogStreamEnv(env) {
|
|
4459
|
+
const candidate = env;
|
|
4460
|
+
return candidate.fs === void 0 ? {} : { fs: candidate.fs };
|
|
4461
|
+
}
|
|
4462
|
+
function isPromiseLike(value) {
|
|
4463
|
+
return typeof value.then === "function";
|
|
4464
|
+
}
|
|
4465
|
+
function createAbortError2() {
|
|
4466
|
+
const error2 = new Error("Agent spawn aborted");
|
|
4467
|
+
error2.name = "AbortError";
|
|
4468
|
+
return error2;
|
|
4469
|
+
}
|
|
4470
|
+
function createActivityTimeoutError(timeoutMs) {
|
|
4471
|
+
const error2 = new Error(`Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`);
|
|
4472
|
+
error2.name = "ActivityTimeoutError";
|
|
4473
|
+
return error2;
|
|
4474
|
+
}
|
|
4475
|
+
function createUlid() {
|
|
4476
|
+
const time = BigInt(Date.now());
|
|
4477
|
+
const random = randomBytes(10);
|
|
4478
|
+
let randomValue = 0n;
|
|
4479
|
+
for (const byte of random) {
|
|
4480
|
+
randomValue = randomValue << 8n | BigInt(byte);
|
|
4481
|
+
}
|
|
4482
|
+
return encodeBase32(time, 10) + encodeBase32(randomValue, 16);
|
|
4483
|
+
}
|
|
4484
|
+
function encodeBase32(value, length) {
|
|
4485
|
+
const chars = Array.from({ length }, () => "0");
|
|
4486
|
+
let remaining = value;
|
|
4487
|
+
for (let index = length - 1; index >= 0; index -= 1) {
|
|
4488
|
+
chars[index] = ULID_ALPHABET[Number(remaining & 31n)];
|
|
4489
|
+
remaining >>= 5n;
|
|
4490
|
+
}
|
|
4491
|
+
return chars.join("");
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
// packages/agent-harness-tools/src/poe-command-execution.ts
|
|
4495
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
4496
|
+
import os3 from "node:os";
|
|
4497
|
+
|
|
4498
|
+
// packages/agent-harness-tools/src/execution-env.ts
|
|
4499
|
+
var executionEnvFactories = /* @__PURE__ */ new Map();
|
|
4500
|
+
function registerExecutionEnvFactory(factory) {
|
|
4501
|
+
executionEnvFactories.set(factory.type, factory);
|
|
4502
|
+
}
|
|
4503
|
+
function selectExecutionEnv(runtime) {
|
|
4504
|
+
const factory = executionEnvFactories.get(runtime.type);
|
|
4505
|
+
if (factory === void 0) {
|
|
4506
|
+
throw new Error(
|
|
4507
|
+
`No execution environment factory registered for runtime type "${runtime.type}".`
|
|
4508
|
+
);
|
|
4509
|
+
}
|
|
4510
|
+
return factory;
|
|
4511
|
+
}
|
|
4512
|
+
|
|
4513
|
+
// packages/agent-harness-tools/src/poe-command-execution.ts
|
|
4514
|
+
function resolvePoeCommandExecution(input) {
|
|
4515
|
+
const homeDir = input.context?.homeDir ?? os3.homedir();
|
|
4516
|
+
const config = applyRuntimeOverrides(loadRuntimeConfig(input.cwd, homeDir), input.runtime, input.cwd);
|
|
4517
|
+
const resolved = resolveRuntime({ cwd: input.cwd, config });
|
|
4518
|
+
const factory = selectExecutionEnv(resolved.runtime);
|
|
4519
|
+
const state = input.context?.state ?? loadState(homeDir);
|
|
4520
|
+
return {
|
|
4521
|
+
factory,
|
|
4522
|
+
detach: factory.supportsDetach === true && config.runner.detach,
|
|
4523
|
+
state,
|
|
4524
|
+
openSpec: {
|
|
4525
|
+
cwd: input.cwd,
|
|
4526
|
+
runtime: resolved.runtime,
|
|
4527
|
+
runner: config.runner,
|
|
4528
|
+
state,
|
|
4529
|
+
env: input.env,
|
|
4530
|
+
uploadIgnoreFiles: config.runner.workspace?.exclude ?? [],
|
|
4531
|
+
jobLabel: {
|
|
4532
|
+
tool: input.tool,
|
|
4533
|
+
argv: input.argv
|
|
4534
|
+
},
|
|
4535
|
+
...input.openSpec
|
|
4536
|
+
}
|
|
4537
|
+
};
|
|
4538
|
+
}
|
|
4539
|
+
function applyRuntimeOverrides(config, overrides, cwd = process.cwd()) {
|
|
4540
|
+
if (!overrides) {
|
|
4541
|
+
return config;
|
|
4542
|
+
}
|
|
4543
|
+
const runtime = parseRuntime({
|
|
4544
|
+
...config.runtime,
|
|
4545
|
+
...overrides.runtime !== void 0 ? { type: overrides.runtime } : {},
|
|
4546
|
+
...overrides.runtimeImage !== void 0 ? { image: overrides.runtimeImage } : {},
|
|
4547
|
+
...overrides.runtimeTemplate !== void 0 ? { template_id: overrides.runtimeTemplate } : {},
|
|
4548
|
+
...overrides.mountPoeCode === true ? { mounts: [...config.runtime.mounts, createPoeCodeMount(cwd)] } : {}
|
|
4549
|
+
});
|
|
4550
|
+
return {
|
|
4551
|
+
runtime,
|
|
4552
|
+
runner: {
|
|
4553
|
+
...config.runner,
|
|
4554
|
+
...overrides.detach === true ? { detach: true } : {}
|
|
4555
|
+
}
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
4558
|
+
function createPoeCodeMount(cwd) {
|
|
4559
|
+
return {
|
|
4560
|
+
source: cwd,
|
|
4561
|
+
target: "/usr/local/lib/poe-code",
|
|
4562
|
+
readonly: true
|
|
4563
|
+
};
|
|
4564
|
+
}
|
|
4565
|
+
function loadRuntimeConfig(cwd, homeDir) {
|
|
4566
|
+
const document = deepMergeDocuments(
|
|
4567
|
+
readConfigDocument(resolveConfigPath(homeDir)),
|
|
4568
|
+
readConfigDocument(resolveProjectConfigPath(cwd))
|
|
4569
|
+
);
|
|
4570
|
+
const runtimeScope = resolveScope(runtimeConfigScope.schema, document.runtime, process.env);
|
|
4571
|
+
return {
|
|
4572
|
+
runtime: parseRuntime(runtimeScope),
|
|
4573
|
+
runner: runtimeScope.runner
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
function readConfigDocument(filePath) {
|
|
4577
|
+
if (!existsSync2(filePath)) {
|
|
4578
|
+
return {};
|
|
4579
|
+
}
|
|
4580
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
4581
|
+
}
|
|
4582
|
+
function loadState(homeDir) {
|
|
4583
|
+
if (process.env.VITEST === "true") {
|
|
4584
|
+
return createMemoryStateManager();
|
|
4585
|
+
}
|
|
4586
|
+
return createStateManager(homeDir);
|
|
4587
|
+
}
|
|
4588
|
+
function createMemoryStateManager() {
|
|
4589
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
4590
|
+
return {
|
|
4591
|
+
templates: {
|
|
4592
|
+
async get() {
|
|
4593
|
+
return null;
|
|
4594
|
+
},
|
|
4595
|
+
async put() {
|
|
4596
|
+
},
|
|
4597
|
+
async remove() {
|
|
4598
|
+
},
|
|
4599
|
+
async list() {
|
|
4600
|
+
return [];
|
|
4601
|
+
}
|
|
4602
|
+
},
|
|
4603
|
+
jobs: {
|
|
4604
|
+
async get(id) {
|
|
4605
|
+
return jobs.get(id) ?? null;
|
|
4606
|
+
},
|
|
4607
|
+
async put(entry) {
|
|
4608
|
+
jobs.set(entry.id, entry);
|
|
4609
|
+
},
|
|
4610
|
+
async update(id, patch) {
|
|
4611
|
+
const current = jobs.get(id);
|
|
4612
|
+
if (!current) {
|
|
4613
|
+
return null;
|
|
4614
|
+
}
|
|
4615
|
+
const updated = { ...current, ...patch, id };
|
|
4616
|
+
jobs.set(id, updated);
|
|
4617
|
+
return updated;
|
|
4618
|
+
},
|
|
4619
|
+
async list(filter) {
|
|
4620
|
+
const entries = Array.from(jobs.values());
|
|
4621
|
+
if (!filter) {
|
|
4622
|
+
return entries;
|
|
4623
|
+
}
|
|
4624
|
+
return entries.filter(
|
|
4625
|
+
(entry) => Object.entries(filter).every(([key, value]) => entry[key] === value)
|
|
4626
|
+
);
|
|
4627
|
+
},
|
|
4628
|
+
async remove(id) {
|
|
4629
|
+
jobs.delete(id);
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
// packages/agent-harness-tools/src/workspace-transfer.ts
|
|
4636
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
4637
|
+
import { promises as nodeFs3 } from "node:fs";
|
|
4638
|
+
import path24 from "node:path";
|
|
4639
|
+
|
|
4640
|
+
// packages/process-runner/src/docker/context.ts
|
|
4641
|
+
import { execSync } from "node:child_process";
|
|
4642
|
+
function detectContext() {
|
|
4643
|
+
try {
|
|
4644
|
+
const output = execSync("colima list --json", {
|
|
4645
|
+
encoding: "utf-8",
|
|
4646
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
4647
|
+
});
|
|
4648
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
4649
|
+
for (const line of lines) {
|
|
4650
|
+
const profile = JSON.parse(line);
|
|
4651
|
+
if (profile.status === "Running" && profile.runtime === "docker") {
|
|
4652
|
+
const name = profile.name ?? profile.profile;
|
|
4653
|
+
if (!name) {
|
|
4654
|
+
continue;
|
|
4655
|
+
}
|
|
4656
|
+
return name === "default" ? "colima" : `colima-${name}`;
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
} catch {
|
|
4660
|
+
return null;
|
|
4661
|
+
}
|
|
4662
|
+
return null;
|
|
4663
|
+
}
|
|
4664
|
+
function buildContextArgs(engine, context) {
|
|
4665
|
+
if (engine === "docker" && context) {
|
|
4666
|
+
return ["--context", context];
|
|
4667
|
+
}
|
|
4668
|
+
return [];
|
|
4669
|
+
}
|
|
4670
|
+
|
|
4671
|
+
// packages/process-runner/src/docker/engine.ts
|
|
4672
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
4673
|
+
function detectEngine() {
|
|
4674
|
+
if (isEngineAvailable("docker")) {
|
|
4675
|
+
return "docker";
|
|
4676
|
+
}
|
|
4677
|
+
if (isEngineAvailable("podman")) {
|
|
4678
|
+
return "podman";
|
|
4679
|
+
}
|
|
4680
|
+
throw new Error(
|
|
4681
|
+
"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"
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
function isEngineAvailable(engine) {
|
|
4685
|
+
try {
|
|
4686
|
+
execSync2(`${engine} --version`, {
|
|
4687
|
+
stdio: "ignore"
|
|
4688
|
+
});
|
|
4689
|
+
return true;
|
|
4690
|
+
} catch {
|
|
4691
|
+
return false;
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
|
|
4695
|
+
// packages/process-runner/src/docker/docker-runner.ts
|
|
4696
|
+
import * as childProcess from "node:child_process";
|
|
4697
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4698
|
+
|
|
4699
|
+
// packages/process-runner/src/docker/args.ts
|
|
4700
|
+
import path25 from "node:path";
|
|
4701
|
+
function buildDockerRunArgs(input) {
|
|
4702
|
+
const args = [input.engine];
|
|
4703
|
+
if (input.engine === "docker" && input.context) {
|
|
4704
|
+
args.push("--context", input.context);
|
|
4705
|
+
}
|
|
4706
|
+
args.push("run");
|
|
4707
|
+
if (input.rm) {
|
|
4708
|
+
args.push("--rm");
|
|
4709
|
+
}
|
|
4710
|
+
if (input.detached) {
|
|
4711
|
+
args.push("-d");
|
|
4712
|
+
}
|
|
4713
|
+
if (input.interactive) {
|
|
4714
|
+
args.push("-i");
|
|
4715
|
+
}
|
|
4716
|
+
if (input.tty) {
|
|
4717
|
+
args.push("-t");
|
|
4718
|
+
}
|
|
4719
|
+
args.push("--name", input.containerName);
|
|
4720
|
+
if (input.cwd !== void 0) {
|
|
4721
|
+
args.push("-w", input.cwd);
|
|
4722
|
+
}
|
|
4723
|
+
for (const [key, value] of Object.entries(input.env ?? {})) {
|
|
4724
|
+
args.push("-e", `${key}=${value}`);
|
|
4725
|
+
}
|
|
4726
|
+
for (const mount of input.mounts) {
|
|
4727
|
+
const volume = `${path25.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
|
|
4728
|
+
args.push("-v", volume);
|
|
4729
|
+
}
|
|
4730
|
+
for (const port of input.ports) {
|
|
4731
|
+
const mapping = `${port.host}:${port.container}${port.protocol === void 0 || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
|
|
4732
|
+
args.push("-p", mapping);
|
|
4733
|
+
}
|
|
4734
|
+
if (input.network !== void 0) {
|
|
4735
|
+
args.push("--network", input.network);
|
|
4736
|
+
}
|
|
4737
|
+
args.push(...input.extraArgs, input.image, input.command, ...input.args);
|
|
4738
|
+
return args;
|
|
4739
|
+
}
|
|
4740
|
+
|
|
4741
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
4742
|
+
import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
|
|
4743
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
4744
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
4745
|
+
import { tmpdir } from "node:os";
|
|
4746
|
+
import path26 from "node:path";
|
|
4747
|
+
|
|
4748
|
+
// packages/process-runner/src/host/host-runner.ts
|
|
4749
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
4750
|
+
function createHostRunner(options = {}) {
|
|
4751
|
+
const detached = options.detached === true;
|
|
4752
|
+
return {
|
|
4753
|
+
name: "host",
|
|
4754
|
+
exec(spec) {
|
|
4755
|
+
const stdinMode = spec.stdin ?? "ignore";
|
|
4756
|
+
const stdoutMode = spec.stdout ?? "pipe";
|
|
4757
|
+
const stderrMode = spec.stderr ?? "pipe";
|
|
4758
|
+
const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
|
|
4759
|
+
const child = spawnChildProcess(spec.command, spec.args ?? [], {
|
|
4760
|
+
cwd: spec.cwd,
|
|
4761
|
+
env: spec.env,
|
|
4762
|
+
stdio,
|
|
4763
|
+
...detached ? { detached: true } : {}
|
|
4764
|
+
});
|
|
4765
|
+
if (detached) {
|
|
4766
|
+
child.unref();
|
|
4767
|
+
}
|
|
4768
|
+
const kill = (signal) => {
|
|
4769
|
+
if (detached && process.platform !== "win32" && child.pid !== void 0) {
|
|
4770
|
+
process.kill(-child.pid, signal);
|
|
4771
|
+
return;
|
|
4772
|
+
}
|
|
4773
|
+
child.kill(signal);
|
|
4774
|
+
};
|
|
4775
|
+
let settled = false;
|
|
4776
|
+
let resolveResult = null;
|
|
4777
|
+
const result = new Promise((resolve2) => {
|
|
4778
|
+
resolveResult = resolve2;
|
|
4779
|
+
});
|
|
4780
|
+
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
4781
|
+
kill("SIGTERM");
|
|
4782
|
+
});
|
|
4783
|
+
child.once("close", (code) => {
|
|
4784
|
+
if (settled) return;
|
|
4785
|
+
settled = true;
|
|
4786
|
+
cleanupAbort();
|
|
4787
|
+
resolveResult?.({ exitCode: code ?? 1 });
|
|
4788
|
+
});
|
|
4789
|
+
child.once("error", () => {
|
|
4790
|
+
if (settled) return;
|
|
4791
|
+
settled = true;
|
|
4792
|
+
cleanupAbort();
|
|
4793
|
+
resolveResult?.({ exitCode: 1 });
|
|
4794
|
+
});
|
|
4795
|
+
return {
|
|
4796
|
+
pid: child.pid ?? null,
|
|
4797
|
+
stdin: child.stdin,
|
|
4798
|
+
stdout: child.stdout,
|
|
4799
|
+
stderr: child.stderr,
|
|
4800
|
+
result,
|
|
4801
|
+
kill
|
|
4802
|
+
};
|
|
4803
|
+
}
|
|
4804
|
+
};
|
|
4805
|
+
}
|
|
4806
|
+
function bindAbortSignal(signal, onAbort) {
|
|
4807
|
+
if (signal === void 0) {
|
|
4808
|
+
return () => {
|
|
4809
|
+
};
|
|
4810
|
+
}
|
|
4811
|
+
if (signal.aborted) {
|
|
4812
|
+
onAbort();
|
|
4813
|
+
return () => {
|
|
4814
|
+
};
|
|
4815
|
+
}
|
|
4816
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4817
|
+
return () => {
|
|
4818
|
+
signal.removeEventListener("abort", onAbort);
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
// packages/process-runner/src/docker/docker-execution-env.ts
|
|
4823
|
+
var containerCommand = ["sh", "-c", "while :; do sleep 3600; done"];
|
|
4824
|
+
var dockerExecutionEnvFactory = {
|
|
4825
|
+
type: "docker",
|
|
4826
|
+
supportsDetach: true,
|
|
4827
|
+
async open(spec) {
|
|
4828
|
+
const runtime = parseDockerRuntime(spec.runtime);
|
|
4829
|
+
const runner = spec.hostRunner ?? createHostRunner();
|
|
4830
|
+
const engine = runtime.engine ?? detectEngine();
|
|
4831
|
+
const context = detectContext();
|
|
4832
|
+
const image = await resolveImage({
|
|
4833
|
+
spec,
|
|
4834
|
+
runtime,
|
|
4835
|
+
runner,
|
|
4836
|
+
engine,
|
|
4837
|
+
context
|
|
4838
|
+
});
|
|
4839
|
+
const containerName = createContainerName();
|
|
4840
|
+
const runArgs = buildDockerRunArgs({
|
|
4841
|
+
engine,
|
|
4842
|
+
context,
|
|
4843
|
+
image,
|
|
4844
|
+
command: containerCommand[0],
|
|
4845
|
+
args: containerCommand.slice(1),
|
|
4846
|
+
cwd: void 0,
|
|
4847
|
+
env: void 0,
|
|
4848
|
+
mounts: runtime.mounts ?? [],
|
|
4849
|
+
ports: [],
|
|
4850
|
+
network: runtime.network,
|
|
4851
|
+
containerName,
|
|
4852
|
+
detached: true,
|
|
4853
|
+
interactive: true,
|
|
4854
|
+
tty: false,
|
|
4855
|
+
rm: false,
|
|
4856
|
+
extraArgs: runtime.extra_args ?? []
|
|
4857
|
+
});
|
|
4858
|
+
const [command, ...args] = runArgs;
|
|
4859
|
+
const id = (await runAndRead(runner, { command, args, stdout: "pipe", stderr: "pipe" })).trim();
|
|
4860
|
+
return createDockerEnv({
|
|
4861
|
+
id,
|
|
4862
|
+
spec,
|
|
4863
|
+
runner,
|
|
4864
|
+
engine,
|
|
4865
|
+
context
|
|
4866
|
+
});
|
|
4867
|
+
},
|
|
4868
|
+
async attach(envId) {
|
|
4869
|
+
const engine = detectEngine();
|
|
4870
|
+
return createDockerEnv({
|
|
4871
|
+
id: envId,
|
|
4872
|
+
spec: createAttachedSpec(),
|
|
4873
|
+
runner: createHostRunner(),
|
|
4874
|
+
engine,
|
|
4875
|
+
context: detectContext()
|
|
4876
|
+
});
|
|
4877
|
+
}
|
|
4878
|
+
};
|
|
4879
|
+
function createDockerEnv(input) {
|
|
4880
|
+
const containerRef = input.id;
|
|
4881
|
+
return {
|
|
4882
|
+
id: containerRef,
|
|
4883
|
+
job: null,
|
|
4884
|
+
async uploadWorkspace() {
|
|
4885
|
+
const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-upload-"));
|
|
4886
|
+
const archivePath = path26.join(tempDir, "workspace.tar");
|
|
4887
|
+
try {
|
|
4888
|
+
const excludeArgs = input.spec.uploadIgnoreFiles.flatMap((ignored) => [
|
|
4889
|
+
"--exclude",
|
|
4890
|
+
ignored
|
|
4891
|
+
]);
|
|
4892
|
+
const tarArgs = [...excludeArgs, "-cf", archivePath, "-C", input.spec.cwd, "."];
|
|
4893
|
+
await runOrThrow(input.runner, {
|
|
4894
|
+
command: "tar",
|
|
4895
|
+
args: tarArgs,
|
|
4896
|
+
stdout: "pipe",
|
|
4897
|
+
stderr: "pipe"
|
|
4898
|
+
});
|
|
4899
|
+
await runOrThrow(input.runner, {
|
|
4900
|
+
command: input.engine,
|
|
4901
|
+
args: [
|
|
4902
|
+
...buildContextArgs(input.engine, input.context),
|
|
4903
|
+
"cp",
|
|
4904
|
+
archivePath,
|
|
4905
|
+
`${containerRef}:/tmp/poe-workspace-upload.tar`
|
|
4906
|
+
],
|
|
4907
|
+
stdout: "pipe",
|
|
4908
|
+
stderr: "pipe"
|
|
4909
|
+
});
|
|
4910
|
+
await runOrThrow(input.runner, {
|
|
4911
|
+
command: input.engine,
|
|
4912
|
+
args: [
|
|
4913
|
+
...buildContextArgs(input.engine, input.context),
|
|
4914
|
+
"exec",
|
|
4915
|
+
containerRef,
|
|
4916
|
+
"sh",
|
|
4917
|
+
"-c",
|
|
4918
|
+
`mkdir -p ${shellQuote2(input.spec.cwd)} && tar -xf /tmp/poe-workspace-upload.tar -C ${shellQuote2(input.spec.cwd)}`
|
|
4919
|
+
],
|
|
4920
|
+
stdout: "pipe",
|
|
4921
|
+
stderr: "pipe"
|
|
4922
|
+
});
|
|
4923
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
4924
|
+
} finally {
|
|
4925
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4926
|
+
}
|
|
4927
|
+
},
|
|
4928
|
+
async downloadWorkspace(opts) {
|
|
4929
|
+
const tempDir = mkdtempSync(path26.join(tmpdir(), "poe-docker-download-"));
|
|
4930
|
+
const archivePath = path26.join(tempDir, "workspace.tar");
|
|
4931
|
+
try {
|
|
4932
|
+
await runOrThrow(input.runner, {
|
|
4933
|
+
command: input.engine,
|
|
4934
|
+
args: [
|
|
4935
|
+
...buildContextArgs(input.engine, input.context),
|
|
4936
|
+
"exec",
|
|
4937
|
+
containerRef,
|
|
4938
|
+
"sh",
|
|
4939
|
+
"-c",
|
|
4940
|
+
`tar -cf /tmp/poe-workspace-download.tar -C ${shellQuote2(input.spec.cwd)} .`
|
|
4941
|
+
],
|
|
4942
|
+
stdout: "pipe",
|
|
4943
|
+
stderr: "pipe"
|
|
4944
|
+
});
|
|
4945
|
+
await runOrThrow(input.runner, {
|
|
4946
|
+
command: input.engine,
|
|
4947
|
+
args: [
|
|
4948
|
+
...buildContextArgs(input.engine, input.context),
|
|
4949
|
+
"cp",
|
|
4950
|
+
`${containerRef}:/tmp/poe-workspace-download.tar`,
|
|
4951
|
+
archivePath
|
|
4952
|
+
],
|
|
4953
|
+
stdout: "pipe",
|
|
4954
|
+
stderr: "pipe"
|
|
4955
|
+
});
|
|
4956
|
+
const extractMode = opts.conflictPolicy === "refuse" ? "-xkf" : "-xf";
|
|
4957
|
+
await runOrThrow(input.runner, {
|
|
4958
|
+
command: "tar",
|
|
4959
|
+
args: [extractMode, archivePath, "-C", input.spec.cwd],
|
|
4960
|
+
stdout: "pipe",
|
|
4961
|
+
stderr: "pipe"
|
|
4962
|
+
});
|
|
4963
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
4964
|
+
} finally {
|
|
4965
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4966
|
+
}
|
|
4967
|
+
},
|
|
4968
|
+
exec(spec) {
|
|
4969
|
+
return input.runner.exec({
|
|
4970
|
+
command: input.engine,
|
|
4971
|
+
args: [
|
|
4972
|
+
...buildContextArgs(input.engine, input.context),
|
|
4973
|
+
"exec",
|
|
4974
|
+
...spec.stdin === "pipe" || spec.stdin === "inherit" ? ["-i"] : [],
|
|
4975
|
+
...spec.tty === true ? ["-t"] : [],
|
|
4976
|
+
...spec.cwd !== void 0 ? ["-w", spec.cwd] : [],
|
|
4977
|
+
...buildEnvArgs(spec.env),
|
|
4978
|
+
containerRef,
|
|
4979
|
+
spec.command,
|
|
4980
|
+
...spec.args ?? []
|
|
4981
|
+
],
|
|
4982
|
+
stdin: spec.stdin,
|
|
4983
|
+
stdout: spec.stdout,
|
|
4984
|
+
stderr: spec.stderr,
|
|
4985
|
+
tty: spec.tty
|
|
4986
|
+
});
|
|
4987
|
+
},
|
|
4988
|
+
async detach() {
|
|
4989
|
+
return createContainerJob(containerRef, input.runner, input.engine, input.context);
|
|
4990
|
+
},
|
|
4991
|
+
shell() {
|
|
4992
|
+
const shellSpec = input.spec.shellSpec;
|
|
4993
|
+
return this.exec({
|
|
4994
|
+
command: shellSpec?.command ?? input.spec.env.SHELL ?? "sh",
|
|
4995
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
4996
|
+
cwd: input.spec.cwd,
|
|
4997
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : input.spec.env,
|
|
4998
|
+
stdin: "inherit",
|
|
4999
|
+
stdout: "inherit",
|
|
5000
|
+
stderr: "inherit",
|
|
5001
|
+
tty: true
|
|
5002
|
+
});
|
|
5003
|
+
},
|
|
5004
|
+
async close() {
|
|
5005
|
+
await runOrThrow(input.runner, {
|
|
5006
|
+
command: input.engine,
|
|
5007
|
+
args: [...buildContextArgs(input.engine, input.context), "rm", "-f", containerRef],
|
|
5008
|
+
stdout: "pipe",
|
|
5009
|
+
stderr: "pipe"
|
|
5010
|
+
});
|
|
5011
|
+
}
|
|
5012
|
+
};
|
|
5013
|
+
}
|
|
5014
|
+
async function resolveImage(input) {
|
|
5015
|
+
if (input.runtime.image !== void 0) {
|
|
5016
|
+
return input.runtime.image;
|
|
5017
|
+
}
|
|
5018
|
+
const dockerfilePath = path26.resolve(
|
|
5019
|
+
input.spec.cwd,
|
|
5020
|
+
input.runtime.dockerfile ?? path26.join(".poe-code", "Dockerfile")
|
|
5021
|
+
);
|
|
5022
|
+
const buildContext = path26.resolve(input.spec.cwd, input.runtime.build_context ?? ".");
|
|
5023
|
+
const dockerfileBytes = await readFile10(dockerfilePath);
|
|
5024
|
+
const hash = hashDockerTemplate(dockerfileBytes, input.runtime.build_args ?? {});
|
|
5025
|
+
const cached2 = await input.spec.state?.templates.get("docker", hash);
|
|
5026
|
+
if (cached2?.image !== void 0) {
|
|
5027
|
+
return cached2.image;
|
|
5028
|
+
}
|
|
5029
|
+
const image = `poe-code/local:${hash}`;
|
|
5030
|
+
await buildImage({
|
|
5031
|
+
runner: input.runner,
|
|
5032
|
+
engine: input.engine,
|
|
5033
|
+
context: input.context,
|
|
5034
|
+
image,
|
|
5035
|
+
dockerfilePath,
|
|
5036
|
+
buildContext,
|
|
5037
|
+
buildArgs: input.runtime.build_args ?? {}
|
|
5038
|
+
});
|
|
5039
|
+
await input.spec.state?.templates.put("docker", {
|
|
5040
|
+
hash,
|
|
5041
|
+
image,
|
|
5042
|
+
runtime_type: "docker",
|
|
5043
|
+
dockerfile_path: dockerfilePath,
|
|
5044
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5045
|
+
});
|
|
5046
|
+
return image;
|
|
5047
|
+
}
|
|
5048
|
+
function hashDockerTemplate(dockerfileBytes, buildArgs) {
|
|
5049
|
+
const hash = createHash4("sha256");
|
|
5050
|
+
hash.update(dockerfileBytes);
|
|
5051
|
+
hash.update("\0");
|
|
5052
|
+
for (const [key, value] of sortedBuildArgs(buildArgs)) {
|
|
5053
|
+
hash.update(key);
|
|
5054
|
+
hash.update("=");
|
|
5055
|
+
hash.update(value);
|
|
5056
|
+
hash.update("\0");
|
|
5057
|
+
}
|
|
5058
|
+
return hash.digest("hex");
|
|
5059
|
+
}
|
|
5060
|
+
async function buildImage(input) {
|
|
5061
|
+
await runOrThrow(input.runner, {
|
|
5062
|
+
command: input.engine,
|
|
5063
|
+
args: [
|
|
5064
|
+
...buildContextArgs(input.engine, input.context),
|
|
5065
|
+
"build",
|
|
5066
|
+
"--tag",
|
|
5067
|
+
input.image,
|
|
5068
|
+
"-f",
|
|
5069
|
+
input.dockerfilePath,
|
|
5070
|
+
...sortedBuildArgs(input.buildArgs).flatMap(([key, value]) => [
|
|
5071
|
+
"--build-arg",
|
|
5072
|
+
`${key}=${value}`
|
|
5073
|
+
]),
|
|
5074
|
+
input.buildContext
|
|
5075
|
+
],
|
|
5076
|
+
stdout: "pipe",
|
|
5077
|
+
stderr: "pipe"
|
|
5078
|
+
});
|
|
5079
|
+
}
|
|
5080
|
+
function parseDockerRuntime(runtime) {
|
|
5081
|
+
if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
|
|
5082
|
+
throw new Error("docker runtime must be an object");
|
|
5083
|
+
}
|
|
5084
|
+
const record = runtime;
|
|
5085
|
+
if (record.type !== "docker") {
|
|
5086
|
+
throw new Error('docker runtime type must be "docker"');
|
|
5087
|
+
}
|
|
5088
|
+
return record;
|
|
5089
|
+
}
|
|
5090
|
+
async function runAndRead(runner, spec) {
|
|
5091
|
+
const handle = runner.exec(spec);
|
|
5092
|
+
const stdout = readStream(handle.stdout);
|
|
5093
|
+
const stderr = readStream(handle.stderr);
|
|
5094
|
+
const result = await handle.result;
|
|
5095
|
+
const output = await stdout;
|
|
5096
|
+
if (result.exitCode !== 0) {
|
|
5097
|
+
const errorOutput = await stderr;
|
|
5098
|
+
throw new Error(
|
|
5099
|
+
`Command failed with exit code ${result.exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${errorOutput ? `
|
|
5100
|
+
${errorOutput}` : ""}`
|
|
5101
|
+
);
|
|
5102
|
+
}
|
|
5103
|
+
return output;
|
|
5104
|
+
}
|
|
5105
|
+
async function runOrThrow(runner, spec) {
|
|
5106
|
+
await runAndRead(runner, spec);
|
|
5107
|
+
}
|
|
5108
|
+
async function readStream(stream) {
|
|
5109
|
+
if (stream === null) {
|
|
5110
|
+
return "";
|
|
5111
|
+
}
|
|
5112
|
+
stream.setEncoding("utf8");
|
|
5113
|
+
const chunks = [];
|
|
5114
|
+
for await (const chunk of stream) {
|
|
5115
|
+
chunks.push(String(chunk));
|
|
5116
|
+
}
|
|
5117
|
+
return chunks.join("");
|
|
5118
|
+
}
|
|
5119
|
+
function sortedBuildArgs(buildArgs) {
|
|
5120
|
+
return Object.entries(buildArgs).sort(([left], [right]) => left.localeCompare(right));
|
|
5121
|
+
}
|
|
5122
|
+
function buildEnvArgs(env) {
|
|
5123
|
+
if (env === void 0) {
|
|
5124
|
+
return [];
|
|
5125
|
+
}
|
|
5126
|
+
return Object.entries(env).flatMap(([key, value]) => ["-e", `${key}=${value}`]);
|
|
5127
|
+
}
|
|
5128
|
+
function createContainerName() {
|
|
5129
|
+
return `poe-env-${randomBytes3(6).toString("hex")}`;
|
|
5130
|
+
}
|
|
5131
|
+
async function createContainerJob(containerId, runner, engine, context) {
|
|
5132
|
+
return {
|
|
5133
|
+
id: containerId,
|
|
5134
|
+
envId: containerId,
|
|
5135
|
+
tool: "docker",
|
|
5136
|
+
argv: ["attach", containerId],
|
|
5137
|
+
async status() {
|
|
5138
|
+
const handle = runner.exec({
|
|
5139
|
+
command: engine,
|
|
5140
|
+
args: [
|
|
5141
|
+
...buildContextArgs(engine, context),
|
|
5142
|
+
"inspect",
|
|
5143
|
+
"-f",
|
|
5144
|
+
"{{.State.Status}}",
|
|
5145
|
+
containerId
|
|
5146
|
+
],
|
|
5147
|
+
stdout: "pipe",
|
|
5148
|
+
stderr: "pipe"
|
|
5149
|
+
});
|
|
5150
|
+
const stdout = await readStream(handle.stdout);
|
|
5151
|
+
const result = await handle.result;
|
|
5152
|
+
if (result.exitCode !== 0) {
|
|
5153
|
+
return "lost";
|
|
5154
|
+
}
|
|
5155
|
+
return stdout.trim() === "running" ? "running" : "exited";
|
|
5156
|
+
},
|
|
5157
|
+
async *stream() {
|
|
5158
|
+
},
|
|
5159
|
+
async wait() {
|
|
5160
|
+
const handle = runner.exec({
|
|
5161
|
+
command: engine,
|
|
5162
|
+
args: [...buildContextArgs(engine, context), "wait", containerId],
|
|
5163
|
+
stdout: "pipe",
|
|
5164
|
+
stderr: "pipe"
|
|
5165
|
+
});
|
|
5166
|
+
const stdout = await readStream(handle.stdout);
|
|
5167
|
+
const result = await handle.result;
|
|
5168
|
+
return { exitCode: Number.parseInt(stdout.trim(), 10) || result.exitCode };
|
|
5169
|
+
},
|
|
5170
|
+
async kill(signal) {
|
|
5171
|
+
const args = signal === void 0 || signal === "SIGTERM" ? ["stop", containerId] : ["kill", ...signal === "SIGKILL" ? [] : [`--signal=${signal}`], containerId];
|
|
5172
|
+
await runOrThrow(runner, {
|
|
5173
|
+
command: engine,
|
|
5174
|
+
args: [...buildContextArgs(engine, context), ...args],
|
|
5175
|
+
stdout: "pipe",
|
|
5176
|
+
stderr: "pipe"
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
};
|
|
5180
|
+
}
|
|
5181
|
+
function createAttachedSpec() {
|
|
5182
|
+
return {
|
|
5183
|
+
cwd: "/workspace",
|
|
5184
|
+
runtime: {
|
|
5185
|
+
type: "docker",
|
|
5186
|
+
image: "attached",
|
|
5187
|
+
build_args: {},
|
|
5188
|
+
mounts: []
|
|
5189
|
+
},
|
|
5190
|
+
env: {},
|
|
5191
|
+
uploadIgnoreFiles: [],
|
|
5192
|
+
jobLabel: {
|
|
5193
|
+
tool: "docker",
|
|
5194
|
+
argv: []
|
|
5195
|
+
}
|
|
5196
|
+
};
|
|
5197
|
+
}
|
|
5198
|
+
function shellQuote2(value) {
|
|
5199
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
5200
|
+
}
|
|
5201
|
+
|
|
5202
|
+
// packages/process-runner/src/host/host-execution-env.ts
|
|
5203
|
+
var hostExecutionEnvFactory = {
|
|
5204
|
+
type: "host",
|
|
5205
|
+
supportsDetach: false,
|
|
5206
|
+
async open(openSpec) {
|
|
5207
|
+
return {
|
|
5208
|
+
id: "host",
|
|
5209
|
+
job: null,
|
|
5210
|
+
async uploadWorkspace() {
|
|
5211
|
+
return {
|
|
5212
|
+
files: 0,
|
|
5213
|
+
bytes: 0,
|
|
5214
|
+
skipped: []
|
|
5215
|
+
};
|
|
5216
|
+
},
|
|
5217
|
+
async downloadWorkspace() {
|
|
5218
|
+
return {
|
|
5219
|
+
files: 0,
|
|
5220
|
+
bytes: 0,
|
|
5221
|
+
conflicts: []
|
|
5222
|
+
};
|
|
5223
|
+
},
|
|
5224
|
+
exec(spec) {
|
|
5225
|
+
return createHostRunner().exec(spec);
|
|
5226
|
+
},
|
|
5227
|
+
async detach() {
|
|
5228
|
+
throw new Error("host runtime does not support detach because host has no addressable env");
|
|
5229
|
+
},
|
|
5230
|
+
shell() {
|
|
5231
|
+
const shellSpec = openSpec.shellSpec;
|
|
5232
|
+
return createHostRunner().exec({
|
|
5233
|
+
command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
5234
|
+
...shellSpec?.args ? { args: shellSpec.args } : {},
|
|
5235
|
+
cwd: openSpec.cwd,
|
|
5236
|
+
env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
|
|
5237
|
+
stdin: "inherit",
|
|
5238
|
+
stdout: "inherit",
|
|
5239
|
+
stderr: "inherit",
|
|
5240
|
+
tty: true
|
|
5241
|
+
});
|
|
5242
|
+
},
|
|
5243
|
+
async close() {
|
|
5244
|
+
}
|
|
5245
|
+
};
|
|
5246
|
+
},
|
|
5247
|
+
async attach() {
|
|
5248
|
+
throw new Error("host runtime does not support reattach");
|
|
5249
|
+
}
|
|
5250
|
+
};
|
|
2938
5251
|
|
|
2939
|
-
// packages/
|
|
2940
|
-
import
|
|
2941
|
-
|
|
2942
|
-
|
|
5252
|
+
// packages/process-runner/src/testing/mock-runner.ts
|
|
5253
|
+
import { Readable, Writable } from "node:stream";
|
|
5254
|
+
|
|
5255
|
+
// packages/agent-spawn/src/register-factories.ts
|
|
5256
|
+
registerExecutionEnvFactory(hostExecutionEnvFactory);
|
|
5257
|
+
registerExecutionEnvFactory(dockerExecutionEnvFactory);
|
|
5258
|
+
if (process.env.VITEST === "true") {
|
|
5259
|
+
registerExecutionEnvFactory(createTestHostExecutionEnvFactory());
|
|
2943
5260
|
}
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
5261
|
+
function createTestHostExecutionEnvFactory() {
|
|
5262
|
+
return {
|
|
5263
|
+
type: "host",
|
|
5264
|
+
supportsDetach: false,
|
|
5265
|
+
open: ((openSpec) => {
|
|
5266
|
+
return {
|
|
5267
|
+
id: "host",
|
|
5268
|
+
job: null,
|
|
5269
|
+
async uploadWorkspace() {
|
|
5270
|
+
return { files: 0, bytes: 0, skipped: [] };
|
|
5271
|
+
},
|
|
5272
|
+
async downloadWorkspace() {
|
|
5273
|
+
return { files: 0, bytes: 0, conflicts: [] };
|
|
5274
|
+
},
|
|
5275
|
+
exec(spec) {
|
|
5276
|
+
return runHost(spawnChildProcess2, spec);
|
|
5277
|
+
},
|
|
5278
|
+
async detach() {
|
|
5279
|
+
throw new Error(
|
|
5280
|
+
"host runtime does not support detach because host has no addressable env"
|
|
5281
|
+
);
|
|
5282
|
+
},
|
|
5283
|
+
shell() {
|
|
5284
|
+
return runHost(spawnChildProcess2, {
|
|
5285
|
+
command: openSpec.shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
5286
|
+
args: openSpec.shellSpec?.args,
|
|
5287
|
+
cwd: openSpec.cwd,
|
|
5288
|
+
env: openSpec.shellSpec && "env" in openSpec.shellSpec ? openSpec.shellSpec.env : openSpec.env,
|
|
5289
|
+
stdin: "inherit",
|
|
5290
|
+
stdout: "inherit",
|
|
5291
|
+
stderr: "inherit",
|
|
5292
|
+
tty: true
|
|
5293
|
+
});
|
|
5294
|
+
},
|
|
5295
|
+
async close() {
|
|
5296
|
+
}
|
|
5297
|
+
};
|
|
5298
|
+
}),
|
|
5299
|
+
async attach() {
|
|
5300
|
+
throw new Error("host runtime does not support reattach");
|
|
5301
|
+
}
|
|
5302
|
+
};
|
|
2952
5303
|
}
|
|
2953
|
-
function
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
|
|
5304
|
+
function runHost(spawnProcess, spec) {
|
|
5305
|
+
const stdin = spec.stdin ?? "ignore";
|
|
5306
|
+
const stdout = spec.stdout ?? "pipe";
|
|
5307
|
+
const stderr = spec.stderr ?? "pipe";
|
|
5308
|
+
const stdio = stdin === "inherit" && stdout === "inherit" && stderr === "inherit" ? "inherit" : [stdin, stdout, stderr];
|
|
5309
|
+
const child = spawnProcess(spec.command, spec.args ?? [], {
|
|
5310
|
+
cwd: spec.cwd,
|
|
5311
|
+
env: spec.env,
|
|
5312
|
+
stdio
|
|
5313
|
+
});
|
|
5314
|
+
const result = new Promise((resolve2) => {
|
|
5315
|
+
child.once("close", (code) => {
|
|
5316
|
+
resolve2({ exitCode: code ?? 1 });
|
|
5317
|
+
});
|
|
5318
|
+
child.once("error", () => {
|
|
5319
|
+
resolve2({ exitCode: 1 });
|
|
5320
|
+
});
|
|
5321
|
+
});
|
|
5322
|
+
const kill = (signal) => {
|
|
5323
|
+
child.kill(signal);
|
|
5324
|
+
};
|
|
5325
|
+
if (spec.signal?.aborted) {
|
|
5326
|
+
kill("SIGTERM");
|
|
5327
|
+
} else {
|
|
5328
|
+
spec.signal?.addEventListener("abort", () => kill("SIGTERM"), { once: true });
|
|
2960
5329
|
}
|
|
2961
|
-
return
|
|
5330
|
+
return {
|
|
5331
|
+
pid: child.pid ?? null,
|
|
5332
|
+
stdin: child.stdin,
|
|
5333
|
+
stdout: child.stdout,
|
|
5334
|
+
stderr: child.stderr,
|
|
5335
|
+
result,
|
|
5336
|
+
kill
|
|
5337
|
+
};
|
|
2962
5338
|
}
|
|
2963
5339
|
|
|
2964
|
-
// packages/memory/src/ingest.ts
|
|
2965
|
-
import * as fs11 from "node:fs/promises";
|
|
2966
|
-
import path23 from "node:path";
|
|
2967
|
-
|
|
2968
5340
|
// packages/agent-spawn/src/run-command.ts
|
|
2969
|
-
import { spawn } from "node:child_process";
|
|
5341
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2970
5342
|
|
|
2971
5343
|
// packages/agent-spawn/src/types.ts
|
|
2972
5344
|
function resolveModeConfig(modeConfig) {
|
|
@@ -2979,145 +5351,6 @@ function resolveModeConfig(modeConfig) {
|
|
|
2979
5351
|
};
|
|
2980
5352
|
}
|
|
2981
5353
|
|
|
2982
|
-
// packages/agent-defs/src/agents/claude-code.ts
|
|
2983
|
-
var claudeCodeAgent = {
|
|
2984
|
-
id: "claude-code",
|
|
2985
|
-
name: "claude-code",
|
|
2986
|
-
label: "Claude Code",
|
|
2987
|
-
summary: "Configure Claude Code to route through Poe.",
|
|
2988
|
-
aliases: ["claude"],
|
|
2989
|
-
binaryName: "claude",
|
|
2990
|
-
configPath: "~/.claude/settings.json",
|
|
2991
|
-
branding: {
|
|
2992
|
-
colors: {
|
|
2993
|
-
dark: "#C15F3C",
|
|
2994
|
-
light: "#C15F3C"
|
|
2995
|
-
}
|
|
2996
|
-
}
|
|
2997
|
-
};
|
|
2998
|
-
|
|
2999
|
-
// packages/agent-defs/src/agents/claude-desktop.ts
|
|
3000
|
-
var claudeDesktopAgent = {
|
|
3001
|
-
id: "claude-desktop",
|
|
3002
|
-
name: "claude-desktop",
|
|
3003
|
-
label: "Claude Desktop",
|
|
3004
|
-
summary: "Anthropic's official desktop application for Claude",
|
|
3005
|
-
configPath: "~/.claude/settings.json",
|
|
3006
|
-
branding: {
|
|
3007
|
-
colors: {
|
|
3008
|
-
dark: "#D97757",
|
|
3009
|
-
light: "#D97757"
|
|
3010
|
-
}
|
|
3011
|
-
}
|
|
3012
|
-
};
|
|
3013
|
-
|
|
3014
|
-
// packages/agent-defs/src/agents/codex.ts
|
|
3015
|
-
var codexAgent = {
|
|
3016
|
-
id: "codex",
|
|
3017
|
-
name: "codex",
|
|
3018
|
-
label: "Codex",
|
|
3019
|
-
summary: "Configure Codex to use Poe as the model provider.",
|
|
3020
|
-
binaryName: "codex",
|
|
3021
|
-
configPath: "~/.codex/config.toml",
|
|
3022
|
-
branding: {
|
|
3023
|
-
colors: {
|
|
3024
|
-
dark: "#D5D9DF",
|
|
3025
|
-
light: "#7A7F86"
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
};
|
|
3029
|
-
|
|
3030
|
-
// packages/agent-defs/src/agents/opencode.ts
|
|
3031
|
-
var openCodeAgent = {
|
|
3032
|
-
id: "opencode",
|
|
3033
|
-
name: "opencode",
|
|
3034
|
-
label: "OpenCode CLI",
|
|
3035
|
-
summary: "Configure OpenCode CLI to use the Poe API.",
|
|
3036
|
-
binaryName: "opencode",
|
|
3037
|
-
configPath: "~/.config/opencode/config.json",
|
|
3038
|
-
branding: {
|
|
3039
|
-
colors: {
|
|
3040
|
-
dark: "#4A4F55",
|
|
3041
|
-
light: "#2F3338"
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
};
|
|
3045
|
-
|
|
3046
|
-
// packages/agent-defs/src/agents/kimi.ts
|
|
3047
|
-
var kimiAgent = {
|
|
3048
|
-
id: "kimi",
|
|
3049
|
-
name: "kimi",
|
|
3050
|
-
label: "Kimi",
|
|
3051
|
-
summary: "Configure Kimi CLI to use Poe API",
|
|
3052
|
-
aliases: ["kimi-cli"],
|
|
3053
|
-
binaryName: "kimi",
|
|
3054
|
-
configPath: "~/.kimi/config.toml",
|
|
3055
|
-
branding: {
|
|
3056
|
-
colors: {
|
|
3057
|
-
dark: "#7B68EE",
|
|
3058
|
-
light: "#6A5ACD"
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
};
|
|
3062
|
-
|
|
3063
|
-
// packages/agent-defs/src/agents/goose.ts
|
|
3064
|
-
var gooseAgent = {
|
|
3065
|
-
id: "goose",
|
|
3066
|
-
name: "goose",
|
|
3067
|
-
label: "Goose",
|
|
3068
|
-
summary: "Block's open-source AI agent with ACP support.",
|
|
3069
|
-
binaryName: "goose",
|
|
3070
|
-
configPath: "~/.config/goose/config.yaml",
|
|
3071
|
-
branding: {
|
|
3072
|
-
colors: {
|
|
3073
|
-
dark: "#FF6B35",
|
|
3074
|
-
light: "#E85D26"
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
};
|
|
3078
|
-
|
|
3079
|
-
// packages/agent-defs/src/agents/poe-agent.ts
|
|
3080
|
-
var poeAgentAgent = {
|
|
3081
|
-
id: "poe-agent",
|
|
3082
|
-
name: "poe-agent",
|
|
3083
|
-
label: "Poe Agent",
|
|
3084
|
-
summary: "Run one-shot prompts with the built-in Poe agent runtime.",
|
|
3085
|
-
configPath: "~/.poe-code/config.json",
|
|
3086
|
-
branding: {
|
|
3087
|
-
colors: {
|
|
3088
|
-
dark: "#A465F7",
|
|
3089
|
-
light: "#7A3FD3"
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
};
|
|
3093
|
-
|
|
3094
|
-
// packages/agent-defs/src/registry.ts
|
|
3095
|
-
var allAgents = [
|
|
3096
|
-
claudeCodeAgent,
|
|
3097
|
-
claudeDesktopAgent,
|
|
3098
|
-
codexAgent,
|
|
3099
|
-
openCodeAgent,
|
|
3100
|
-
kimiAgent,
|
|
3101
|
-
gooseAgent,
|
|
3102
|
-
poeAgentAgent
|
|
3103
|
-
];
|
|
3104
|
-
var lookup = /* @__PURE__ */ new Map();
|
|
3105
|
-
for (const agent of allAgents) {
|
|
3106
|
-
const values = [agent.id, agent.name, ...agent.aliases ?? []];
|
|
3107
|
-
for (const value of values) {
|
|
3108
|
-
const normalized = value.toLowerCase();
|
|
3109
|
-
if (!lookup.has(normalized)) {
|
|
3110
|
-
lookup.set(normalized, agent.id);
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
function resolveAgentId(input) {
|
|
3115
|
-
if (!input) {
|
|
3116
|
-
return void 0;
|
|
3117
|
-
}
|
|
3118
|
-
return lookup.get(input.toLowerCase());
|
|
3119
|
-
}
|
|
3120
|
-
|
|
3121
5354
|
// packages/agent-spawn/src/configs/mcp.ts
|
|
3122
5355
|
function toJsonMcpServers(servers) {
|
|
3123
5356
|
const out = {};
|
|
@@ -3385,9 +5618,8 @@ function listMcpSupportedAgents() {
|
|
|
3385
5618
|
}
|
|
3386
5619
|
|
|
3387
5620
|
// packages/agent-spawn/src/spawn.ts
|
|
3388
|
-
import { spawn as spawnChildProcess } from "node:child_process";
|
|
3389
5621
|
import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
|
|
3390
|
-
import
|
|
5622
|
+
import path27 from "node:path";
|
|
3391
5623
|
|
|
3392
5624
|
// packages/agent-spawn/src/configs/resolve-config.ts
|
|
3393
5625
|
function resolveConfig(agentId) {
|
|
@@ -3437,18 +5669,11 @@ function stripModelNamespace(model) {
|
|
|
3437
5669
|
}
|
|
3438
5670
|
|
|
3439
5671
|
// packages/agent-spawn/src/spawn.ts
|
|
3440
|
-
function
|
|
5672
|
+
function createAbortError3() {
|
|
3441
5673
|
const error2 = new Error("Agent spawn aborted");
|
|
3442
5674
|
error2.name = "AbortError";
|
|
3443
5675
|
return error2;
|
|
3444
5676
|
}
|
|
3445
|
-
function createActivityTimeoutError(timeoutMs) {
|
|
3446
|
-
const error2 = new Error(
|
|
3447
|
-
`Agent spawn timed out after ${timeoutMs / 1e3}s of inactivity`
|
|
3448
|
-
);
|
|
3449
|
-
error2.name = "ActivityTimeoutError";
|
|
3450
|
-
return error2;
|
|
3451
|
-
}
|
|
3452
5677
|
function resolveCliConfig(agentId) {
|
|
3453
5678
|
const resolved = resolveConfig(agentId);
|
|
3454
5679
|
if (!resolved.spawnConfig) {
|
|
@@ -3516,9 +5741,9 @@ function buildCliArgs(config, options, stdinMode) {
|
|
|
3516
5741
|
}
|
|
3517
5742
|
return { args, env: mode.env };
|
|
3518
5743
|
}
|
|
3519
|
-
async function
|
|
5744
|
+
async function spawn3(agentId, options, context) {
|
|
3520
5745
|
if (options.signal?.aborted) {
|
|
3521
|
-
throw
|
|
5746
|
+
throw createAbortError3();
|
|
3522
5747
|
}
|
|
3523
5748
|
const { agentId: resolvedId, binaryName, spawnConfig } = resolveCliConfig(agentId);
|
|
3524
5749
|
const stdinMode = options.useStdin && spawnConfig.stdinMode ? spawnConfig.stdinMode : void 0;
|
|
@@ -3530,89 +5755,68 @@ async function spawn2(agentId, options, context) {
|
|
|
3530
5755
|
}
|
|
3531
5756
|
const logFilePath = resolveSpawnLogPath(options);
|
|
3532
5757
|
const logFd = logFilePath ? openSpawnLog(logFilePath) : void 0;
|
|
3533
|
-
const
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
}
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
child.kill("SIGTERM");
|
|
3567
|
-
}, options.activityTimeoutMs);
|
|
3568
|
-
} : void 0;
|
|
3569
|
-
resetActivityTimer?.();
|
|
3570
|
-
const cleanup = () => {
|
|
3571
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
3572
|
-
if (activityTimer) clearTimeout(activityTimer);
|
|
3573
|
-
};
|
|
3574
|
-
stdoutStream.setEncoding("utf8");
|
|
3575
|
-
stdoutStream.on("data", (chunk) => {
|
|
3576
|
-
stdout += chunk;
|
|
3577
|
-
resetActivityTimer?.();
|
|
3578
|
-
if (options.tee?.stdout) options.tee.stdout.write(chunk);
|
|
3579
|
-
appendSpawnLog(logFd, chunk);
|
|
3580
|
-
});
|
|
3581
|
-
stderrStream.setEncoding("utf8");
|
|
3582
|
-
stderrStream.on("data", (chunk) => {
|
|
3583
|
-
stderr += chunk;
|
|
3584
|
-
resetActivityTimer?.();
|
|
3585
|
-
if (options.tee?.stderr) options.tee.stderr.write(chunk);
|
|
3586
|
-
appendSpawnLog(logFd, chunk);
|
|
3587
|
-
});
|
|
3588
|
-
child.on("error", (error2) => {
|
|
3589
|
-
cleanup();
|
|
3590
|
-
closeSpawnLog(logFd);
|
|
3591
|
-
if (aborted) {
|
|
3592
|
-
reject(createAbortError());
|
|
3593
|
-
return;
|
|
5758
|
+
const processEnv = modeEnv ? { ...process.env, ...modeEnv } : void 0;
|
|
5759
|
+
const argv = [binaryName, ...spawnArgs];
|
|
5760
|
+
const execution = resolvePoeCommandExecution({
|
|
5761
|
+
cwd: options.cwd ?? process.cwd(),
|
|
5762
|
+
env: processEnv ?? process.env,
|
|
5763
|
+
argv,
|
|
5764
|
+
tool: resolvedId,
|
|
5765
|
+
runtime: {
|
|
5766
|
+
runtime: options.runtime,
|
|
5767
|
+
runtimeImage: options.runtimeImage,
|
|
5768
|
+
runtimeTemplate: options.runtimeTemplate,
|
|
5769
|
+
detach: options.detach,
|
|
5770
|
+
mountPoeCode: options.mountPoeCode
|
|
5771
|
+
},
|
|
5772
|
+
context,
|
|
5773
|
+
openSpec: {
|
|
5774
|
+
execution: {
|
|
5775
|
+
wrapForLogTee: false,
|
|
5776
|
+
stdin: stdinMode ? "pipe" : "inherit",
|
|
5777
|
+
stdout: "pipe",
|
|
5778
|
+
stderr: "pipe",
|
|
5779
|
+
env: processEnv,
|
|
5780
|
+
input: stdinMode ? options.prompt : void 0,
|
|
5781
|
+
captureOutput: true,
|
|
5782
|
+
activityTimeoutMs: options.activityTimeoutMs,
|
|
5783
|
+
onStdout(chunk) {
|
|
5784
|
+
options.tee?.stdout?.write(chunk);
|
|
5785
|
+
appendSpawnLog(logFd, chunk);
|
|
5786
|
+
},
|
|
5787
|
+
onStderr(chunk) {
|
|
5788
|
+
options.tee?.stderr?.write(chunk);
|
|
5789
|
+
appendSpawnLog(logFd, chunk);
|
|
5790
|
+
}
|
|
3594
5791
|
}
|
|
3595
|
-
|
|
5792
|
+
}
|
|
5793
|
+
});
|
|
5794
|
+
try {
|
|
5795
|
+
const result = await runPoeCommand({
|
|
5796
|
+
factory: execution.factory,
|
|
5797
|
+
openSpec: execution.openSpec,
|
|
5798
|
+
detach: execution.detach,
|
|
5799
|
+
state: execution.state,
|
|
5800
|
+
signal: options.signal
|
|
3596
5801
|
});
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
return;
|
|
3603
|
-
}
|
|
3604
|
-
if (timedOut) {
|
|
3605
|
-
reject(createActivityTimeoutError(options.activityTimeoutMs));
|
|
3606
|
-
return;
|
|
3607
|
-
}
|
|
3608
|
-
resolve2({
|
|
3609
|
-
stdout,
|
|
3610
|
-
stderr,
|
|
3611
|
-
exitCode: code ?? 1,
|
|
5802
|
+
if (result.kind === "detached") {
|
|
5803
|
+
return {
|
|
5804
|
+
stdout: "",
|
|
5805
|
+
stderr: "",
|
|
5806
|
+
exitCode: 0,
|
|
3612
5807
|
...logFilePath ? { logFile: logFilePath } : {}
|
|
3613
|
-
}
|
|
3614
|
-
}
|
|
3615
|
-
|
|
5808
|
+
};
|
|
5809
|
+
}
|
|
5810
|
+
const captured = result;
|
|
5811
|
+
return {
|
|
5812
|
+
stdout: captured.stdout ?? "",
|
|
5813
|
+
stderr: captured.stderr ?? "",
|
|
5814
|
+
exitCode: result.exitCode,
|
|
5815
|
+
...logFilePath ? { logFile: logFilePath } : {}
|
|
5816
|
+
};
|
|
5817
|
+
} finally {
|
|
5818
|
+
closeSpawnLog(logFd);
|
|
5819
|
+
}
|
|
3616
5820
|
}
|
|
3617
5821
|
function resolveSpawnLogPath(options) {
|
|
3618
5822
|
if (options.logPath) {
|
|
@@ -3621,11 +5825,11 @@ function resolveSpawnLogPath(options) {
|
|
|
3621
5825
|
if (!options.logDir || !options.logFileName) {
|
|
3622
5826
|
return void 0;
|
|
3623
5827
|
}
|
|
3624
|
-
return
|
|
5828
|
+
return path27.join(options.logDir, options.logFileName);
|
|
3625
5829
|
}
|
|
3626
5830
|
function openSpawnLog(filePath) {
|
|
3627
5831
|
try {
|
|
3628
|
-
mkdirSync(
|
|
5832
|
+
mkdirSync(path27.dirname(filePath), { recursive: true });
|
|
3629
5833
|
return openSync(filePath, "a");
|
|
3630
5834
|
} catch {
|
|
3631
5835
|
return void 0;
|
|
@@ -3646,9 +5850,6 @@ function closeSpawnLog(fd) {
|
|
|
3646
5850
|
}
|
|
3647
5851
|
}
|
|
3648
5852
|
|
|
3649
|
-
// packages/agent-spawn/src/spawn-interactive.ts
|
|
3650
|
-
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
3651
|
-
|
|
3652
5853
|
// packages/design-system/src/tokens/colors.ts
|
|
3653
5854
|
import chalk from "chalk";
|
|
3654
5855
|
var dark = {
|
|
@@ -4056,9 +6257,9 @@ import chalk16 from "chalk";
|
|
|
4056
6257
|
var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
4057
6258
|
|
|
4058
6259
|
// packages/agent-spawn/src/acp/replay.ts
|
|
4059
|
-
import
|
|
6260
|
+
import path28 from "node:path";
|
|
4060
6261
|
import { homedir as homedir2 } from "node:os";
|
|
4061
|
-
import { open, readdir as readdir4 } from "node:fs/promises";
|
|
6262
|
+
import { open as open2, readdir as readdir4 } from "node:fs/promises";
|
|
4062
6263
|
import { createInterface } from "node:readline";
|
|
4063
6264
|
|
|
4064
6265
|
// packages/poe-acp-client/src/acp-client.ts
|
|
@@ -4070,21 +6271,18 @@ import {
|
|
|
4070
6271
|
} from "node:child_process";
|
|
4071
6272
|
|
|
4072
6273
|
// packages/poe-acp-client/src/run-report.ts
|
|
4073
|
-
import * as
|
|
6274
|
+
import * as fsPromises3 from "node:fs/promises";
|
|
4074
6275
|
import { homedir } from "node:os";
|
|
4075
6276
|
import { join } from "node:path";
|
|
4076
6277
|
|
|
4077
|
-
// packages/agent-spawn/src/acp/spawn.ts
|
|
4078
|
-
import { spawn as spawnChildProcess4 } from "node:child_process";
|
|
4079
|
-
|
|
4080
6278
|
// packages/agent-spawn/src/acp/middlewares/spawn-log.ts
|
|
4081
|
-
import
|
|
6279
|
+
import path29 from "node:path";
|
|
4082
6280
|
import { homedir as homedir3 } from "node:os";
|
|
4083
|
-
import { mkdir as mkdir5, open as
|
|
6281
|
+
import { mkdir as mkdir5, open as open3 } from "node:fs/promises";
|
|
4084
6282
|
|
|
4085
6283
|
// packages/memory/src/tokens.ts
|
|
4086
6284
|
import * as fs10 from "node:fs/promises";
|
|
4087
|
-
import
|
|
6285
|
+
import path30 from "node:path";
|
|
4088
6286
|
|
|
4089
6287
|
// packages/tokenfill/dist/tokenizer.js
|
|
4090
6288
|
import { get_encoding } from "tiktoken";
|
|
@@ -4125,7 +6323,7 @@ function countTokens(text4) {
|
|
|
4125
6323
|
}
|
|
4126
6324
|
|
|
4127
6325
|
// packages/tokenfill/dist/corpus.js
|
|
4128
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
6326
|
+
import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
4129
6327
|
import { dirname, join as join2 } from "node:path";
|
|
4130
6328
|
import { fileURLToPath } from "node:url";
|
|
4131
6329
|
var CORPUS_ARTICLE_SEPARATOR = "\n\n";
|
|
@@ -4138,7 +6336,7 @@ function loadBuiltInCorpusArticles() {
|
|
|
4138
6336
|
if (corpusFileNames.length === 0) {
|
|
4139
6337
|
throw new Error(`No built-in corpus markdown files found in ${corpusDirectoryPath}`);
|
|
4140
6338
|
}
|
|
4141
|
-
return corpusFileNames.map((fileName) =>
|
|
6339
|
+
return corpusFileNames.map((fileName) => readFileSync2(join2(corpusDirectoryPath, fileName), "utf8").trim());
|
|
4142
6340
|
}
|
|
4143
6341
|
var BUILT_IN_CORPUS_ARTICLES = loadBuiltInCorpusArticles();
|
|
4144
6342
|
|
|
@@ -4168,11 +6366,11 @@ async function computeTokenStats(root) {
|
|
|
4168
6366
|
}
|
|
4169
6367
|
}
|
|
4170
6368
|
}
|
|
4171
|
-
const repoRoot =
|
|
6369
|
+
const repoRoot = path30.resolve(root, "..", "..");
|
|
4172
6370
|
let sourceTokens = 0;
|
|
4173
6371
|
const missingSources = [];
|
|
4174
6372
|
for (const sourcePath of sourcePaths) {
|
|
4175
|
-
const absPath =
|
|
6373
|
+
const absPath = path30.isAbsolute(sourcePath) ? sourcePath : path30.resolve(repoRoot, sourcePath);
|
|
4176
6374
|
try {
|
|
4177
6375
|
const content = await fs10.readFile(absPath, "utf8");
|
|
4178
6376
|
sourceTokens += countTokens(content);
|
|
@@ -4223,10 +6421,10 @@ function resolveRunners(overrides) {
|
|
|
4223
6421
|
async function ingest(root, opts, runners) {
|
|
4224
6422
|
const resolved = resolveRunners(runners);
|
|
4225
6423
|
const source = await materializeSource(opts.source);
|
|
4226
|
-
const indexMdBytes = await fs11.readFile(
|
|
6424
|
+
const indexMdBytes = await fs11.readFile(path31.join(root, MEMORY_INDEX_RELPATH));
|
|
4227
6425
|
const configOptions = {
|
|
4228
6426
|
fs: fs11,
|
|
4229
|
-
filePath:
|
|
6427
|
+
filePath: path31.join(inferRepoRoot(root), "poe-code.json")
|
|
4230
6428
|
};
|
|
4231
6429
|
const agentId = await resolveAgent(configOptions, opts.agent ?? null) ?? opts.agent ?? "claude-code";
|
|
4232
6430
|
const key = resolved.computeIngestKey({
|
|
@@ -4264,7 +6462,7 @@ async function ingest(root, opts, runners) {
|
|
|
4264
6462
|
let timeoutError;
|
|
4265
6463
|
try {
|
|
4266
6464
|
const result = await runWithTimeout(
|
|
4267
|
-
|
|
6465
|
+
spawn3(agentId, { prompt }),
|
|
4268
6466
|
opts.timeoutMs ?? await configuredTimeout(configOptions)
|
|
4269
6467
|
);
|
|
4270
6468
|
exitCode = result.exitCode;
|
|
@@ -4316,7 +6514,7 @@ async function materializeSource(source) {
|
|
|
4316
6514
|
throw new Error("URL ingest not implemented yet.");
|
|
4317
6515
|
}
|
|
4318
6516
|
function inferRepoRoot(root) {
|
|
4319
|
-
return
|
|
6517
|
+
return path31.resolve(root, "..", "..");
|
|
4320
6518
|
}
|
|
4321
6519
|
async function runWithTimeout(promise, timeoutMs) {
|
|
4322
6520
|
return await new Promise((resolve2, reject) => {
|
|
@@ -5074,8 +7272,8 @@ function printMcpConfig() {
|
|
|
5074
7272
|
}
|
|
5075
7273
|
|
|
5076
7274
|
// packages/agent-skill-config/src/configs.ts
|
|
5077
|
-
import
|
|
5078
|
-
import
|
|
7275
|
+
import os4 from "node:os";
|
|
7276
|
+
import path32 from "node:path";
|
|
5079
7277
|
var agentSkillConfigs = {
|
|
5080
7278
|
"claude-code": {
|
|
5081
7279
|
globalSkillDir: "~/.claude/skills",
|
|
@@ -5094,7 +7292,7 @@ var agentSkillConfigs = {
|
|
|
5094
7292
|
localSkillDir: ".agents/skills"
|
|
5095
7293
|
}
|
|
5096
7294
|
};
|
|
5097
|
-
var
|
|
7295
|
+
var supportedAgents2 = Object.keys(agentSkillConfigs);
|
|
5098
7296
|
function resolveAgentSupport(input, registry = agentSkillConfigs) {
|
|
5099
7297
|
const resolvedId = resolveAgentId(input);
|
|
5100
7298
|
if (!resolvedId) {
|
|
@@ -5108,8 +7306,8 @@ function resolveAgentSupport(input, registry = agentSkillConfigs) {
|
|
|
5108
7306
|
}
|
|
5109
7307
|
|
|
5110
7308
|
// packages/agent-skill-config/src/templates.ts
|
|
5111
|
-
import { readFile as
|
|
5112
|
-
import
|
|
7309
|
+
import { readFile as readFile13, stat as stat6 } from "node:fs/promises";
|
|
7310
|
+
import path33 from "node:path";
|
|
5113
7311
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5114
7312
|
|
|
5115
7313
|
// packages/agent-skill-config/src/apply.ts
|
|
@@ -5215,7 +7413,7 @@ var agentMcpConfigs = {
|
|
|
5215
7413
|
shape: "goose"
|
|
5216
7414
|
}
|
|
5217
7415
|
};
|
|
5218
|
-
var
|
|
7416
|
+
var supportedAgents3 = Object.keys(agentMcpConfigs);
|
|
5219
7417
|
function resolveAgentSupport2(input, registry = agentMcpConfigs) {
|
|
5220
7418
|
const resolvedId = resolveAgentId(input);
|
|
5221
7419
|
if (!resolvedId) {
|
|
@@ -5242,7 +7440,7 @@ function resolveConfigPath2(config, platform) {
|
|
|
5242
7440
|
}
|
|
5243
7441
|
|
|
5244
7442
|
// packages/agent-mcp-config/src/apply.ts
|
|
5245
|
-
import
|
|
7443
|
+
import path34 from "node:path";
|
|
5246
7444
|
import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
|
|
5247
7445
|
|
|
5248
7446
|
// packages/agent-mcp-config/src/shapes.ts
|
|
@@ -5334,7 +7532,7 @@ function getShapeTransformer(shape) {
|
|
|
5334
7532
|
|
|
5335
7533
|
// packages/agent-mcp-config/src/apply.ts
|
|
5336
7534
|
function getConfigDirectory(configPath) {
|
|
5337
|
-
return
|
|
7535
|
+
return path34.dirname(configPath);
|
|
5338
7536
|
}
|
|
5339
7537
|
var UnsupportedAgentError2 = class extends Error {
|
|
5340
7538
|
constructor(agentId) {
|
|
@@ -5360,9 +7558,9 @@ function expandHomePath(configPath, homeDir) {
|
|
|
5360
7558
|
return homeDir;
|
|
5361
7559
|
}
|
|
5362
7560
|
if (configPath.startsWith("~/")) {
|
|
5363
|
-
return
|
|
7561
|
+
return path34.join(homeDir, configPath.slice(2));
|
|
5364
7562
|
}
|
|
5365
|
-
return
|
|
7563
|
+
return path34.join(homeDir, configPath.slice(1));
|
|
5366
7564
|
}
|
|
5367
7565
|
function parseYamlDocument(content) {
|
|
5368
7566
|
if (content.trim() === "") {
|
|
@@ -5395,7 +7593,7 @@ async function writeYamlConfig(configPath, document, options) {
|
|
|
5395
7593
|
return;
|
|
5396
7594
|
}
|
|
5397
7595
|
const absolutePath = expandHomePath(configPath, options.homeDir);
|
|
5398
|
-
const configDir =
|
|
7596
|
+
const configDir = path34.dirname(absolutePath);
|
|
5399
7597
|
await options.fs.mkdir(configDir, { recursive: true });
|
|
5400
7598
|
await options.fs.writeFile(absolutePath, serializeYamlDocument(document), {
|
|
5401
7599
|
encoding: "utf8"
|
|
@@ -5573,7 +7771,7 @@ async function installMemory(options) {
|
|
|
5573
7771
|
|
|
5574
7772
|
// packages/memory/src/query.ts
|
|
5575
7773
|
import * as fs12 from "node:fs/promises";
|
|
5576
|
-
import
|
|
7774
|
+
import path35 from "node:path";
|
|
5577
7775
|
async function queryMemory(root, options) {
|
|
5578
7776
|
const pages = await listPages(root);
|
|
5579
7777
|
if (pages.length === 0) {
|
|
@@ -5587,11 +7785,11 @@ async function queryMemory(root, options) {
|
|
|
5587
7785
|
}
|
|
5588
7786
|
const configOptions = {
|
|
5589
7787
|
fs: fs12,
|
|
5590
|
-
filePath:
|
|
7788
|
+
filePath: path35.join(inferRepoRoot2(root), "poe-code.json")
|
|
5591
7789
|
};
|
|
5592
7790
|
const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
|
|
5593
7791
|
const context = await selectQueryContext(root, options.question, options.budget);
|
|
5594
|
-
const result = await
|
|
7792
|
+
const result = await spawn3(agentId, { prompt: context.prompt });
|
|
5595
7793
|
return {
|
|
5596
7794
|
answer: result.answer,
|
|
5597
7795
|
citations: result.citations,
|
|
@@ -5602,7 +7800,7 @@ async function queryMemory(root, options) {
|
|
|
5602
7800
|
}
|
|
5603
7801
|
async function selectQueryContext(root, question, budget) {
|
|
5604
7802
|
const [indexText, pages] = await Promise.all([
|
|
5605
|
-
fs12.readFile(
|
|
7803
|
+
fs12.readFile(path35.join(root, MEMORY_INDEX_RELPATH), "utf8"),
|
|
5606
7804
|
listPages(root)
|
|
5607
7805
|
]);
|
|
5608
7806
|
const indexTokens = countTokens(indexText);
|
|
@@ -5681,12 +7879,12 @@ function tokenize(text4) {
|
|
|
5681
7879
|
return text4.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length > 0);
|
|
5682
7880
|
}
|
|
5683
7881
|
function inferRepoRoot2(root) {
|
|
5684
|
-
return
|
|
7882
|
+
return path35.resolve(root, "..", "..");
|
|
5685
7883
|
}
|
|
5686
7884
|
|
|
5687
7885
|
// packages/memory/src/explain.ts
|
|
5688
7886
|
import * as fs13 from "node:fs/promises";
|
|
5689
|
-
import
|
|
7887
|
+
import path36 from "node:path";
|
|
5690
7888
|
async function explainPage(root, options) {
|
|
5691
7889
|
const targetPage = await readPageIfPresent(root, options.relPath);
|
|
5692
7890
|
if (targetPage === void 0) {
|
|
@@ -5709,10 +7907,10 @@ async function explainPage(root, options) {
|
|
|
5709
7907
|
}
|
|
5710
7908
|
const configOptions = {
|
|
5711
7909
|
fs: fs13,
|
|
5712
|
-
filePath:
|
|
7910
|
+
filePath: path36.join(inferRepoRoot3(root), "poe-code.json")
|
|
5713
7911
|
};
|
|
5714
7912
|
const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
|
|
5715
|
-
const response = await
|
|
7913
|
+
const response = await spawn3(agentId, { prompt });
|
|
5716
7914
|
return {
|
|
5717
7915
|
answer: response.answer,
|
|
5718
7916
|
citations: response.citations,
|
|
@@ -5758,7 +7956,7 @@ async function readPageIfPresent(root, relPath) {
|
|
|
5758
7956
|
}
|
|
5759
7957
|
}
|
|
5760
7958
|
function inferRepoRoot3(root) {
|
|
5761
|
-
return
|
|
7959
|
+
return path36.resolve(root, "..", "..");
|
|
5762
7960
|
}
|
|
5763
7961
|
|
|
5764
7962
|
// packages/memory/src/explain.cli.ts
|
|
@@ -5771,9 +7969,9 @@ async function runMemoryExplain(input) {
|
|
|
5771
7969
|
}
|
|
5772
7970
|
|
|
5773
7971
|
// packages/memory/src/handle.ts
|
|
5774
|
-
import
|
|
7972
|
+
import path37 from "node:path";
|
|
5775
7973
|
function openMemory(opts) {
|
|
5776
|
-
if (!
|
|
7974
|
+
if (!path37.isAbsolute(opts.root)) {
|
|
5777
7975
|
throw new Error(`openMemory: root must be absolute, got ${opts.root}`);
|
|
5778
7976
|
}
|
|
5779
7977
|
const root = opts.root;
|