poe-code 8.0.0 → 9.0.0
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/harness.js +12 -3
- package/dist/cli/commands/harness.js.map +1 -1
- package/dist/index.js +1515 -712
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +15 -2
- package/packages/safejs/dist/chunks/chunk-4YIDX44G.js +40 -0
- package/packages/safejs/dist/chunks/chunk-4YIDX44G.js.map +7 -0
- package/packages/safejs/dist/chunks/chunk-55ZK3CR7.js +7802 -0
- package/packages/safejs/dist/chunks/chunk-55ZK3CR7.js.map +7 -0
- package/packages/safejs/dist/chunks/chunk-GQBEEFVQ.js +28798 -0
- package/packages/safejs/dist/chunks/chunk-GQBEEFVQ.js.map +7 -0
- package/packages/safejs/dist/chunks/chunk-NI4WFFTL.js +5701 -0
- package/packages/safejs/dist/chunks/chunk-NI4WFFTL.js.map +7 -0
- package/packages/safejs/dist/chunks/inMemory-RLZ4QGDZ.js +48 -0
- package/packages/safejs/dist/chunks/inMemory-RLZ4QGDZ.js.map +7 -0
- package/packages/safejs/dist/chunks/server-3SNT3RE2.js +12518 -0
- package/packages/safejs/dist/chunks/server-3SNT3RE2.js.map +7 -0
- package/packages/safejs/dist/chunks/types-BTF37PZ6.js +348 -0
- package/packages/safejs/dist/chunks/types-BTF37PZ6.js.map +7 -0
- package/packages/safejs/dist/cli.d.ts +2 -0
- package/packages/safejs/dist/cli.js +550 -491
- package/packages/safejs/dist/cli.js.map +7 -0
- package/packages/safejs/dist/core.js +14 -4
- package/packages/safejs/dist/core.js.map +7 -0
- package/packages/safejs/dist/index.d.ts +3 -0
- package/packages/safejs/dist/index.js +791 -30
- package/packages/safejs/dist/index.js.map +7 -0
- package/packages/safejs/dist/interp/host-bridge.d.ts +2 -0
- package/packages/safejs/dist/interp/host-bridge.js +20 -9
- package/packages/safejs/dist/interp/host-call.d.ts +5 -0
- package/packages/safejs/dist/interp/host-call.js +48 -3
- package/packages/safejs/dist/interp/resources.d.ts +7 -0
- package/packages/safejs/dist/interp/resources.js +36 -0
- package/packages/safejs/dist/modules/mcp-transport.d.ts +26 -0
- package/packages/safejs/dist/modules/mcp-transport.js +259 -0
- package/packages/safejs/dist/modules/mcp.d.ts +15 -1
- package/packages/safejs/dist/modules/mcp.js +107 -0
- package/packages/safejs/dist/run.js +6 -3
- package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safejs/dist/snapshot/dump-format.js +1 -1
- package/packages/safejs/dist/snapshot/replay-data.d.ts +1 -0
- package/packages/safejs/dist/snapshot/replay-data.js +3 -1
- package/packages/safejs/dist/snapshot/replay-inputs.d.ts +2 -2
- package/packages/safejs/dist/snapshot/replay-inputs.js +2 -1
|
@@ -1,30 +1,791 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
import {
|
|
2
|
+
AgentSpawnError,
|
|
3
|
+
SPAWN_MODES,
|
|
4
|
+
containsPath,
|
|
5
|
+
countLineBreaks,
|
|
6
|
+
createLintModulesFromRuntimeRegistry,
|
|
7
|
+
createSpawnUsageAccumulator,
|
|
8
|
+
extractBlock,
|
|
9
|
+
formatInterpreterError,
|
|
10
|
+
isSamePath,
|
|
11
|
+
makeAgentModule,
|
|
12
|
+
makeFailModule,
|
|
13
|
+
makeFsModule,
|
|
14
|
+
makeHarnessModule,
|
|
15
|
+
makeLogModule,
|
|
16
|
+
makeMcpModule,
|
|
17
|
+
makeMetricModule,
|
|
18
|
+
parseMcpConfig,
|
|
19
|
+
resolveCanonicalPath,
|
|
20
|
+
runWithSpawnUsageAccumulator,
|
|
21
|
+
splitFrontmatter,
|
|
22
|
+
supportsSpawnMode
|
|
23
|
+
} from "./chunks/chunk-55ZK3CR7.js";
|
|
24
|
+
import {
|
|
25
|
+
Budget,
|
|
26
|
+
FileSnapshotBackend,
|
|
27
|
+
HostCallResumabilityError,
|
|
28
|
+
HostOperationResumePolicyError,
|
|
29
|
+
SandboxError,
|
|
30
|
+
SnapshotValidationError,
|
|
31
|
+
createReplayableRandom,
|
|
32
|
+
createSeededRandom,
|
|
33
|
+
declareHostOperation,
|
|
34
|
+
deepCopyFromSandbox,
|
|
35
|
+
deepCopyToSandbox,
|
|
36
|
+
dump,
|
|
37
|
+
hasOwnErrorCode,
|
|
38
|
+
lint,
|
|
39
|
+
noopOtelSink,
|
|
40
|
+
parse,
|
|
41
|
+
parseModule,
|
|
42
|
+
registerPendingHostCallPolicy,
|
|
43
|
+
restore,
|
|
44
|
+
run
|
|
45
|
+
} from "./chunks/chunk-GQBEEFVQ.js";
|
|
46
|
+
import "./chunks/chunk-4YIDX44G.js";
|
|
47
|
+
|
|
48
|
+
// packages/safejs/src/runner/run-harness.ts
|
|
49
|
+
import { readFile, stat } from "node:fs/promises";
|
|
50
|
+
import { extname } from "node:path";
|
|
51
|
+
function getOwnEntry(record, key) {
|
|
52
|
+
return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
|
|
53
|
+
}
|
|
54
|
+
function createHarnessMeta(filepath, frontmatter) {
|
|
55
|
+
return {
|
|
56
|
+
filepath,
|
|
57
|
+
kind: getOwnEntry(frontmatter, "kind"),
|
|
58
|
+
version: getOwnEntry(frontmatter, "version")
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function assertFrontmatterAgentModes(frontmatter) {
|
|
62
|
+
const agents = getOwnEntry(frontmatter, "agents");
|
|
63
|
+
if (typeof agents !== "object" || agents === null || Array.isArray(agents)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const [name, definition] of Object.entries(agents)) {
|
|
67
|
+
if (typeof definition !== "object" || definition === null || Array.isArray(definition)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const record = definition;
|
|
71
|
+
const agent = getOwnEntry(record, "agent");
|
|
72
|
+
const mode = getOwnEntry(record, "mode");
|
|
73
|
+
if (typeof agent !== "string" || typeof mode !== "string") {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!SPAWN_MODES.includes(mode)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!supportsSpawnMode(agent, mode)) {
|
|
80
|
+
throw new Error(`Harness agent "${name}": agent "${agent}" does not support mode "${mode}".`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
var LintError = class extends Error {
|
|
85
|
+
diagnostics;
|
|
86
|
+
constructor(diagnostics) {
|
|
87
|
+
super(formatLintErrorMessage(diagnostics));
|
|
88
|
+
this.name = "LintError";
|
|
89
|
+
this.diagnostics = diagnostics;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
async function runHarness(filepath, options) {
|
|
93
|
+
const rawSource = stripByteOrderMark(await readHarnessFile(filepath));
|
|
94
|
+
const { executableSource, frontmatter, isRawScript } = loadExecutableSource(filepath, rawSource);
|
|
95
|
+
assertFrontmatterAgentModes(frontmatter);
|
|
96
|
+
const meta = createHarnessMeta(filepath, frontmatter);
|
|
97
|
+
const modules = excludeHarnessModule(options.modulesFor(frontmatter, meta), isRawScript);
|
|
98
|
+
const diagnostics = lint(executableSource, {
|
|
99
|
+
filename: filepath,
|
|
100
|
+
modules: createLintModulesFromRuntimeRegistry(modules)
|
|
101
|
+
});
|
|
102
|
+
const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
103
|
+
if (lintErrors.length > 0) {
|
|
104
|
+
throw new LintError(lintErrors);
|
|
105
|
+
}
|
|
106
|
+
return runHarnessSource(executableSource, {
|
|
107
|
+
entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : void 0,
|
|
108
|
+
filename: filepath,
|
|
109
|
+
modules,
|
|
110
|
+
otelSink: options.otelSink,
|
|
111
|
+
signal: options.signal,
|
|
112
|
+
snapshotBackend: options.snapshotBackend,
|
|
113
|
+
snapshotIntervalMs: options.snapshotIntervalMs,
|
|
114
|
+
snapshotPath: options.snapshotPath
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async function runHarnessPair(filepath, options) {
|
|
118
|
+
const pair = resolveHarnessPair(filepath);
|
|
119
|
+
const [rawMarkdown, rawScript] = await Promise.all([
|
|
120
|
+
readHarnessFile(pair.markdownPath),
|
|
121
|
+
readHarnessFile(pair.scriptPath)
|
|
122
|
+
]);
|
|
123
|
+
const { frontmatter, body } = splitFrontmatter(stripByteOrderMark(rawMarkdown));
|
|
124
|
+
assertFrontmatterAgentModes(frontmatter);
|
|
125
|
+
const executableSource = stripByteOrderMark(rawScript);
|
|
126
|
+
const meta = createHarnessMeta(pair.markdownPath, frontmatter);
|
|
127
|
+
const modules = options.modulesFor(frontmatter, meta);
|
|
128
|
+
const diagnostics = lint(executableSource, {
|
|
129
|
+
allowedExportNames: ["schema"],
|
|
130
|
+
defaultExport: {
|
|
131
|
+
parameters: ["frontmatter"],
|
|
132
|
+
required: true
|
|
133
|
+
},
|
|
134
|
+
filename: pair.scriptPath,
|
|
135
|
+
frontmatterFields: Object.keys(frontmatter),
|
|
136
|
+
modules: createLintModulesFromRuntimeRegistry(modules)
|
|
137
|
+
});
|
|
138
|
+
const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
139
|
+
if (lintErrors.length > 0) {
|
|
140
|
+
throw new LintError(lintErrors);
|
|
141
|
+
}
|
|
142
|
+
return runHarnessSource(executableSource, {
|
|
143
|
+
entryPointArgs: [frontmatter],
|
|
144
|
+
filename: pair.scriptPath,
|
|
145
|
+
importMeta: {
|
|
146
|
+
body,
|
|
147
|
+
filepath: pair.markdownPath,
|
|
148
|
+
kind: meta.kind,
|
|
149
|
+
version: meta.version
|
|
150
|
+
},
|
|
151
|
+
modules,
|
|
152
|
+
otelSink: options.otelSink,
|
|
153
|
+
signal: options.signal,
|
|
154
|
+
snapshotBackend: options.snapshotBackend,
|
|
155
|
+
snapshotIntervalMs: options.snapshotIntervalMs,
|
|
156
|
+
snapshotPath: options.snapshotPath
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async function readHarnessFile(filepath) {
|
|
160
|
+
try {
|
|
161
|
+
const stats = await stat(filepath);
|
|
162
|
+
if (!stats.isFile()) {
|
|
163
|
+
throw new Error(`Harness path must point to a file: ${filepath}`);
|
|
164
|
+
}
|
|
165
|
+
return await readFile(filepath, "utf8");
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (hasOwnErrorCode(error, "ENOENT") || hasOwnErrorCode(error, "ENOTDIR")) {
|
|
168
|
+
throw new Error(`Harness file not found: ${filepath}`);
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function loadExecutableSource(filepath, source) {
|
|
174
|
+
if (isRawScriptPath(filepath)) {
|
|
175
|
+
if (source.length === 0) {
|
|
176
|
+
throw new Error(`No code block found in empty harness file: ${filepath}`);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
executableSource: source,
|
|
180
|
+
frontmatter: {},
|
|
181
|
+
isRawScript: true
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const { frontmatter, body } = splitFrontmatter(source);
|
|
185
|
+
if (source.length === 0 || body.trim().length === 0) {
|
|
186
|
+
throw new Error(`No code block found in empty harness file: ${filepath}`);
|
|
187
|
+
}
|
|
188
|
+
const bodyStartOffset = source.length - body.length;
|
|
189
|
+
const { source: executableBlock, startOffset } = extractBlock(
|
|
190
|
+
body,
|
|
191
|
+
countLineBreaks(source, 0, bodyStartOffset) + 1
|
|
192
|
+
);
|
|
193
|
+
const absoluteLineOffset = countLineBreaks(source, 0, bodyStartOffset + startOffset);
|
|
194
|
+
return {
|
|
195
|
+
executableSource: createLineOffsetSource(stripHashbang(executableBlock), absoluteLineOffset),
|
|
196
|
+
frontmatter,
|
|
197
|
+
isRawScript: false
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function resolveHarnessPair(filepath) {
|
|
201
|
+
const extension = extname(filepath);
|
|
202
|
+
const basename = extension.length === 0 ? filepath : filepath.slice(0, -extension.length);
|
|
203
|
+
if (isRawScriptExtension(extension)) {
|
|
204
|
+
return {
|
|
205
|
+
markdownPath: `${basename}.md`,
|
|
206
|
+
scriptPath: filepath
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
markdownPath: filepath,
|
|
211
|
+
scriptPath: `${basename}.ajs`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function isRawScriptPath(filepath) {
|
|
215
|
+
return isRawScriptExtension(extname(filepath));
|
|
216
|
+
}
|
|
217
|
+
function isRawScriptExtension(extension) {
|
|
218
|
+
return extension === ".safejs" || extension === ".ajs";
|
|
219
|
+
}
|
|
220
|
+
async function runHarnessSource(executableSource, options) {
|
|
221
|
+
try {
|
|
222
|
+
return await run(executableSource, options);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (isSetupError(error)) {
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
...isAbortError(error) ? { aborted: true } : {},
|
|
229
|
+
error,
|
|
230
|
+
ok: false
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function isSetupError(error) {
|
|
235
|
+
return error instanceof Error && error.name === "ParseError";
|
|
236
|
+
}
|
|
237
|
+
function isAbortError(error) {
|
|
238
|
+
if (error instanceof Error) {
|
|
239
|
+
return error.name === "AbortError";
|
|
240
|
+
}
|
|
241
|
+
return typeof error === "object" && error !== null && Object.prototype.hasOwnProperty.call(error, "name") && error.name === "AbortError";
|
|
242
|
+
}
|
|
243
|
+
function hasDefaultExport(source, filename) {
|
|
244
|
+
return parseModule(source, filename).body.some(
|
|
245
|
+
(statement) => statement.type === "ExportDefaultDeclaration"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
function excludeHarnessModule(modules, isRawScript) {
|
|
249
|
+
if (!isRawScript) {
|
|
250
|
+
return modules;
|
|
251
|
+
}
|
|
252
|
+
if (modules instanceof Map) {
|
|
253
|
+
const rawModules2 = new Map(modules);
|
|
254
|
+
rawModules2.delete("harness");
|
|
255
|
+
return rawModules2;
|
|
256
|
+
}
|
|
257
|
+
const rawModules = {};
|
|
258
|
+
for (const [moduleName, moduleExports] of Object.entries(modules)) {
|
|
259
|
+
if (moduleName !== "harness") {
|
|
260
|
+
rawModules[moduleName] = moduleExports;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return rawModules;
|
|
264
|
+
}
|
|
265
|
+
function stripByteOrderMark(source) {
|
|
266
|
+
return source.startsWith("\uFEFF") ? source.slice(1) : source;
|
|
267
|
+
}
|
|
268
|
+
function stripHashbang(source) {
|
|
269
|
+
if (!source.startsWith("#!")) {
|
|
270
|
+
return source;
|
|
271
|
+
}
|
|
272
|
+
let end = 2;
|
|
273
|
+
while (end < source.length && source[end] !== "\n" && source[end] !== "\r") end += 1;
|
|
274
|
+
return `${" ".repeat(end)}${source.slice(end)}`;
|
|
275
|
+
}
|
|
276
|
+
function createLineOffsetSource(source, lineOffset) {
|
|
277
|
+
return `${"\n".repeat(Math.max(lineOffset, 0))}${source}`;
|
|
278
|
+
}
|
|
279
|
+
function formatLintErrorMessage(diagnostics) {
|
|
280
|
+
if (diagnostics.length === 0) {
|
|
281
|
+
return "Script lint failed.";
|
|
282
|
+
}
|
|
283
|
+
return diagnostics.map(
|
|
284
|
+
(diagnostic) => `${diagnostic.filename}:${diagnostic.line}:${diagnostic.column} ${diagnostic.code} ${diagnostic.message}`
|
|
285
|
+
).join("\n");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// packages/safejs/src/loader/find-exported.ts
|
|
289
|
+
function findExportedConstInitializer(module, name) {
|
|
290
|
+
for (const statement of module.body) {
|
|
291
|
+
if (statement.type === "ExportDefaultDeclaration") {
|
|
292
|
+
if (name === "default") {
|
|
293
|
+
return statement.declaration;
|
|
294
|
+
}
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (statement.type !== "ExportNamedDeclaration") {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const declaration = statement.declaration;
|
|
301
|
+
if (declaration.kind !== "const") {
|
|
302
|
+
throw new Error(`Exported '${name}' must be declared with const.`);
|
|
303
|
+
}
|
|
304
|
+
if (declaration.declarations.length !== 1) {
|
|
305
|
+
throw new Error("Exported const declarations must contain exactly one declarator.");
|
|
306
|
+
}
|
|
307
|
+
const declarator = declaration.declarations[0];
|
|
308
|
+
if (declarator.id.type !== "Identifier") {
|
|
309
|
+
throw new Error("Destructured exported const bindings are not supported.");
|
|
310
|
+
}
|
|
311
|
+
if (declarator.id.name !== name) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (declarator.init === void 0) {
|
|
315
|
+
throw new Error(`Exported const '${name}' must have an initializer.`);
|
|
316
|
+
}
|
|
317
|
+
return declarator.init;
|
|
318
|
+
}
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// packages/safejs/src/modules/env.ts
|
|
323
|
+
function makeEnvModule(allowList) {
|
|
324
|
+
const allowedNames = normalizeAllowList(allowList);
|
|
325
|
+
return {
|
|
326
|
+
get(name) {
|
|
327
|
+
const normalizedName = readNonEmptyString(name, "Environment variable name");
|
|
328
|
+
if (!allowedNames.has(normalizedName)) {
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
331
|
+
return process.env[normalizedName];
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function normalizeAllowList(allowList) {
|
|
336
|
+
if (!Array.isArray(allowList)) {
|
|
337
|
+
throw new Error("Environment allow list must be an array of non-empty strings.");
|
|
338
|
+
}
|
|
339
|
+
return new Set(
|
|
340
|
+
allowList.map((entry, index) => readNonEmptyString(entry, `Environment allow list[${index}]`))
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
function readNonEmptyString(value, label) {
|
|
344
|
+
if (typeof value !== "string") {
|
|
345
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
346
|
+
}
|
|
347
|
+
const normalizedValue = value.trim();
|
|
348
|
+
if (normalizedValue.length === 0) {
|
|
349
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
350
|
+
}
|
|
351
|
+
return normalizedValue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// packages/safejs/src/modules/git.ts
|
|
355
|
+
import { execFile } from "node:child_process";
|
|
356
|
+
import { mkdir, readlink, realpath, rm, stat as stat2 } from "node:fs/promises";
|
|
357
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
358
|
+
var GIT_EXEC_MAX_BUFFER = 10 * 1024 * 1024;
|
|
359
|
+
var SAVEPOINT_REF_PREFIX = "refs/poe-code/checkpoints/";
|
|
360
|
+
function makeGitModule(cwd) {
|
|
361
|
+
const normalizedCwd = readNonEmptyString2(cwd, "Git module cwd");
|
|
362
|
+
return {
|
|
363
|
+
async head() {
|
|
364
|
+
return runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
|
|
365
|
+
},
|
|
366
|
+
async checkpoint() {
|
|
367
|
+
const currentHead = await runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
|
|
368
|
+
const status = await runGitAndTrim(normalizedCwd, ["status", "--porcelain"]);
|
|
369
|
+
if (status.length === 0) {
|
|
370
|
+
return {
|
|
371
|
+
head: currentHead
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const stashRef = createSavepointRef();
|
|
375
|
+
const stashMessage = `poe-code checkpoint ${stashRef}`;
|
|
376
|
+
await runGit(normalizedCwd, [
|
|
377
|
+
"stash",
|
|
378
|
+
"push",
|
|
379
|
+
"--include-untracked",
|
|
380
|
+
"--message",
|
|
381
|
+
stashMessage
|
|
382
|
+
]);
|
|
383
|
+
try {
|
|
384
|
+
const stashOid = await runGitAndTrim(normalizedCwd, ["rev-parse", "stash@{0}"]);
|
|
385
|
+
await runGit(normalizedCwd, ["update-ref", stashRef, stashOid]);
|
|
386
|
+
await runGit(normalizedCwd, ["stash", "apply", "--index", stashRef]);
|
|
387
|
+
await runGit(normalizedCwd, ["stash", "drop", "stash@{0}"]);
|
|
388
|
+
return {
|
|
389
|
+
head: currentHead,
|
|
390
|
+
stashRef
|
|
391
|
+
};
|
|
392
|
+
} catch (error) {
|
|
393
|
+
await cleanupFailedCheckpoint(normalizedCwd, stashRef);
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
async commit(options) {
|
|
398
|
+
const normalizedOptions = normalizeCommitOptions(options);
|
|
399
|
+
if (normalizedOptions.files === void 0) {
|
|
400
|
+
await runGit(normalizedCwd, ["add", "--all"]);
|
|
401
|
+
await runGit(normalizedCwd, ["commit", "--message", normalizedOptions.message]);
|
|
402
|
+
} else {
|
|
403
|
+
await runGit(normalizedCwd, ["add", "--", ...normalizedOptions.files]);
|
|
404
|
+
await runGit(normalizedCwd, [
|
|
405
|
+
"commit",
|
|
406
|
+
"--message",
|
|
407
|
+
normalizedOptions.message,
|
|
408
|
+
"--",
|
|
409
|
+
...normalizedOptions.files
|
|
410
|
+
]);
|
|
411
|
+
}
|
|
412
|
+
return runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
|
|
413
|
+
},
|
|
414
|
+
async revert(savepoint) {
|
|
415
|
+
const normalizedSavepoint = normalizeSavepoint(savepoint);
|
|
416
|
+
try {
|
|
417
|
+
await runGit(normalizedCwd, ["reset", "--hard", normalizedSavepoint.head]);
|
|
418
|
+
await runGit(normalizedCwd, ["clean", "--force", "-d"]);
|
|
419
|
+
if (normalizedSavepoint.stashRef !== void 0) {
|
|
420
|
+
await runGit(normalizedCwd, ["stash", "apply", "--index", normalizedSavepoint.stashRef]);
|
|
421
|
+
}
|
|
422
|
+
} finally {
|
|
423
|
+
if (normalizedSavepoint.stashRef !== void 0) {
|
|
424
|
+
await deleteSavepointRef(normalizedCwd, normalizedSavepoint.stashRef);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
async diff() {
|
|
429
|
+
const { stdout } = await runGit(normalizedCwd, ["diff", "HEAD", "--"]);
|
|
430
|
+
return stdout;
|
|
431
|
+
},
|
|
432
|
+
async worktreeCreate(branch, options) {
|
|
433
|
+
const normalizedBranch = readNonEmptyString2(branch, "Git worktree branch");
|
|
434
|
+
const normalizedOptions = normalizeWorktreeCreateOptions(options);
|
|
435
|
+
const repoRoot = await getRepoRoot(normalizedCwd);
|
|
436
|
+
const worktreePath = await resolveWorktreePath(
|
|
437
|
+
repoRoot,
|
|
438
|
+
normalizedOptions.path ?? createDefaultWorktreePath(repoRoot, normalizedBranch),
|
|
439
|
+
"Git worktree path"
|
|
440
|
+
);
|
|
441
|
+
if (await gitRefExists(normalizedCwd, `refs/heads/${normalizedBranch}`)) {
|
|
442
|
+
throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);
|
|
443
|
+
}
|
|
444
|
+
await mkdir(dirname(worktreePath), { recursive: true });
|
|
445
|
+
try {
|
|
446
|
+
await runGit(normalizedCwd, [
|
|
447
|
+
"worktree",
|
|
448
|
+
"add",
|
|
449
|
+
"-b",
|
|
450
|
+
normalizedBranch,
|
|
451
|
+
worktreePath,
|
|
452
|
+
normalizedOptions.base
|
|
453
|
+
]);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
if (isBranchAlreadyExistsError(error, normalizedBranch)) {
|
|
456
|
+
throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);
|
|
457
|
+
}
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
path: worktreePath,
|
|
462
|
+
branch: normalizedBranch
|
|
463
|
+
};
|
|
464
|
+
},
|
|
465
|
+
async worktreeRemove(path) {
|
|
466
|
+
const repoRoot = await getRepoRoot(normalizedCwd);
|
|
467
|
+
const worktreePath = await resolveWorktreePath(repoRoot, path, "Git worktree path");
|
|
468
|
+
const worktrees = await listWorktrees(normalizedCwd);
|
|
469
|
+
if (!worktrees.some((worktree) => resolve(worktree.path) === worktreePath)) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
await runGit(normalizedCwd, ["worktree", "remove", "--force", worktreePath]);
|
|
473
|
+
await rm(worktreePath, { recursive: true, force: true });
|
|
474
|
+
},
|
|
475
|
+
async worktreeList() {
|
|
476
|
+
return listWorktrees(normalizedCwd);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function normalizeCommitOptions(options) {
|
|
481
|
+
if (!isRecord(options)) {
|
|
482
|
+
throw new Error("Git commit options must be an object.");
|
|
483
|
+
}
|
|
484
|
+
const files = getOwnProperty(options, "files");
|
|
485
|
+
return {
|
|
486
|
+
message: readNonEmptyString2(getOwnProperty(options, "message"), "Git commit options message"),
|
|
487
|
+
files: files === void 0 ? void 0 : readNonEmptyStringArray(files, "Git commit options files")
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function normalizeSavepoint(savepoint) {
|
|
491
|
+
if (!isRecord(savepoint)) {
|
|
492
|
+
throw new Error("Git savepoint must be an object.");
|
|
493
|
+
}
|
|
494
|
+
const stashRefValue = getOwnProperty(savepoint, "stashRef");
|
|
495
|
+
const stashRef = stashRefValue === void 0 ? void 0 : readNonEmptyString2(stashRefValue, "Git savepoint stashRef");
|
|
496
|
+
if (stashRef !== void 0 && !stashRef.startsWith(SAVEPOINT_REF_PREFIX)) {
|
|
497
|
+
throw new Error("Git savepoint stashRef must be a Poe checkpoint ref.");
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
head: readNonEmptyString2(getOwnProperty(savepoint, "head"), "Git savepoint head"),
|
|
501
|
+
stashRef
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function normalizeWorktreeCreateOptions(options) {
|
|
505
|
+
if (options === void 0) {
|
|
506
|
+
return {
|
|
507
|
+
base: "HEAD"
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (!isRecord(options)) {
|
|
511
|
+
throw new Error("Git worktree create options must be an object.");
|
|
512
|
+
}
|
|
513
|
+
const base = getOwnProperty(options, "base");
|
|
514
|
+
const path = getOwnProperty(options, "path");
|
|
515
|
+
return {
|
|
516
|
+
base: base === void 0 ? "HEAD" : readNonEmptyString2(base, "Git worktree create options base"),
|
|
517
|
+
path: path === void 0 ? void 0 : readNonEmptyString2(path, "Git worktree create options path")
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function createSavepointRef() {
|
|
521
|
+
return `${SAVEPOINT_REF_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
522
|
+
}
|
|
523
|
+
function createDefaultWorktreePath(repoRoot, branch) {
|
|
524
|
+
return resolve(repoRoot, ".poe-code", "worktrees", createSafeBranchDirectoryName(branch));
|
|
525
|
+
}
|
|
526
|
+
function createSafeBranchDirectoryName(branch) {
|
|
527
|
+
return encodeURIComponent(branch);
|
|
528
|
+
}
|
|
529
|
+
async function getRepoRoot(cwd) {
|
|
530
|
+
return runGitAndTrim(cwd, ["rev-parse", "--show-toplevel"]);
|
|
531
|
+
}
|
|
532
|
+
async function gitRefExists(cwd, ref) {
|
|
533
|
+
try {
|
|
534
|
+
await runGit(cwd, ["show-ref", "--verify", "--quiet", ref]);
|
|
535
|
+
return true;
|
|
536
|
+
} catch (error) {
|
|
537
|
+
void error;
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function listWorktrees(cwd) {
|
|
542
|
+
const { stdout } = await runGit(cwd, ["worktree", "list", "--porcelain"]);
|
|
543
|
+
return parseWorktreeList(stdout);
|
|
544
|
+
}
|
|
545
|
+
function parseWorktreeList(output) {
|
|
546
|
+
const worktrees = [];
|
|
547
|
+
let current = {};
|
|
548
|
+
const finishCurrent = () => {
|
|
549
|
+
if (current.path !== void 0 && current.branch !== void 0) {
|
|
550
|
+
worktrees.push({
|
|
551
|
+
path: current.path,
|
|
552
|
+
branch: current.branch
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
current = {};
|
|
556
|
+
};
|
|
557
|
+
for (const line of output.split("\n")) {
|
|
558
|
+
if (line.length === 0) {
|
|
559
|
+
finishCurrent();
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (line.startsWith("worktree ")) {
|
|
563
|
+
current.path = line.slice("worktree ".length);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (line.startsWith("branch ")) {
|
|
567
|
+
current.branch = parseBranchRef(line.slice("branch ".length));
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
finishCurrent();
|
|
571
|
+
return worktrees;
|
|
572
|
+
}
|
|
573
|
+
function parseBranchRef(ref) {
|
|
574
|
+
const branchPrefix = "refs/heads/";
|
|
575
|
+
return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;
|
|
576
|
+
}
|
|
577
|
+
async function resolveWorktreePath(repoRoot, path, label) {
|
|
578
|
+
const resolvedRoot = resolve(repoRoot);
|
|
579
|
+
const resolvedPath = isAbsolute(path) ? resolve(path) : resolve(resolvedRoot, path);
|
|
580
|
+
const canonicalRoot = await realpath(resolvedRoot);
|
|
581
|
+
const canonicalPath = await resolveCanonicalPath({ realpath, readlink }, resolvedPath);
|
|
582
|
+
const [inside, isRootItself] = await Promise.all([
|
|
583
|
+
containsPath(stat2, canonicalRoot, canonicalPath),
|
|
584
|
+
isSamePath(stat2, canonicalPath, canonicalRoot)
|
|
585
|
+
]);
|
|
586
|
+
if (!inside || isRootItself) {
|
|
587
|
+
throw new Error(`${label} must be inside the git repository.`);
|
|
588
|
+
}
|
|
589
|
+
return canonicalPath;
|
|
590
|
+
}
|
|
591
|
+
function isBranchAlreadyExistsError(error, branch) {
|
|
592
|
+
return error instanceof Error && error.message.includes(branch) && error.message.includes("already exists");
|
|
593
|
+
}
|
|
594
|
+
async function runGitAndTrim(cwd, args) {
|
|
595
|
+
const { stdout } = await runGit(cwd, args);
|
|
596
|
+
return stdout.trim();
|
|
597
|
+
}
|
|
598
|
+
async function runGit(cwd, args) {
|
|
599
|
+
return new Promise((resolve2, reject) => {
|
|
600
|
+
execFile(
|
|
601
|
+
"git",
|
|
602
|
+
args,
|
|
603
|
+
{
|
|
604
|
+
cwd,
|
|
605
|
+
encoding: "utf8",
|
|
606
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER
|
|
607
|
+
},
|
|
608
|
+
(error, stdout, stderr) => {
|
|
609
|
+
if (error) {
|
|
610
|
+
reject(
|
|
611
|
+
new Error(
|
|
612
|
+
`${formatGitCommand(args)} failed: ${pickGitFailureMessage(stdout, stderr, error)}`
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
resolve2({
|
|
618
|
+
stdout,
|
|
619
|
+
stderr
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
async function cleanupFailedCheckpoint(cwd, stashRef) {
|
|
626
|
+
await deleteSavepointRef(cwd, stashRef);
|
|
627
|
+
const restored = await tryRunGit(cwd, ["stash", "apply", "--index", "stash@{0}"]);
|
|
628
|
+
if (restored) {
|
|
629
|
+
await tryRunGit(cwd, ["stash", "drop", "stash@{0}"]);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async function deleteSavepointRef(cwd, stashRef) {
|
|
633
|
+
await tryRunGit(cwd, ["update-ref", "--delete", stashRef]);
|
|
634
|
+
}
|
|
635
|
+
async function tryRunGit(cwd, args) {
|
|
636
|
+
try {
|
|
637
|
+
await runGit(cwd, args);
|
|
638
|
+
return true;
|
|
639
|
+
} catch (error) {
|
|
640
|
+
void error;
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function formatGitCommand(args) {
|
|
645
|
+
return ["git", ...args].join(" ");
|
|
646
|
+
}
|
|
647
|
+
function pickGitFailureMessage(stdout, stderr, error) {
|
|
648
|
+
const stderrText = stderr.trim();
|
|
649
|
+
if (stderrText.length > 0) {
|
|
650
|
+
return stderrText;
|
|
651
|
+
}
|
|
652
|
+
const stdoutText = stdout.trim();
|
|
653
|
+
if (stdoutText.length > 0) {
|
|
654
|
+
return stdoutText;
|
|
655
|
+
}
|
|
656
|
+
return error.message;
|
|
657
|
+
}
|
|
658
|
+
function readNonEmptyString2(value, label) {
|
|
659
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
660
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
661
|
+
}
|
|
662
|
+
return value;
|
|
663
|
+
}
|
|
664
|
+
function readNonEmptyStringArray(value, label) {
|
|
665
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
666
|
+
throw new Error(`${label} must be a non-empty array of strings.`);
|
|
667
|
+
}
|
|
668
|
+
const entries = value.map((entry, index) => readNonEmptyString2(entry, `${label}[${index}]`));
|
|
669
|
+
return [...entries];
|
|
670
|
+
}
|
|
671
|
+
function isRecord(value) {
|
|
672
|
+
return typeof value === "object" && value !== null;
|
|
673
|
+
}
|
|
674
|
+
function getOwnProperty(value, name) {
|
|
675
|
+
return hasOwnProperty(value, name) ? value[name] : void 0;
|
|
676
|
+
}
|
|
677
|
+
function hasOwnProperty(value, name) {
|
|
678
|
+
return Object.prototype.hasOwnProperty.call(value, name);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// packages/safejs/src/modules/time.ts
|
|
682
|
+
function makeTimeModule(options = {}) {
|
|
683
|
+
const hasDeterministicRandom = options.random !== void 0 || options.seed !== void 0;
|
|
684
|
+
const random = options.random ?? (options.seed === void 0 ? () => Math.random() : createSeededRandom(options.seed).next);
|
|
685
|
+
const now = options.now ?? (() => Date.now());
|
|
686
|
+
const signal = options.signal;
|
|
687
|
+
async function sleep(ms) {
|
|
688
|
+
assertSleepDelay(ms);
|
|
689
|
+
return await new Promise((resolve2, reject) => {
|
|
690
|
+
if (signal?.aborted === true) {
|
|
691
|
+
reject(createSleepAbortError());
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const start = performance.now();
|
|
695
|
+
let timeout = setTimeout(resolveSleep, ms);
|
|
696
|
+
function cleanup() {
|
|
697
|
+
clearTimeout(timeout);
|
|
698
|
+
signal?.removeEventListener("abort", rejectSleep);
|
|
699
|
+
}
|
|
700
|
+
function resolveSleep() {
|
|
701
|
+
const remainingMs = ms - (performance.now() - start);
|
|
702
|
+
if (remainingMs > 0) {
|
|
703
|
+
timeout = setTimeout(resolveSleep, remainingMs);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
cleanup();
|
|
707
|
+
resolve2();
|
|
708
|
+
}
|
|
709
|
+
function rejectSleep() {
|
|
710
|
+
cleanup();
|
|
711
|
+
reject(createSleepAbortError());
|
|
712
|
+
}
|
|
713
|
+
signal?.addEventListener("abort", rejectSleep, { once: true });
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
random,
|
|
718
|
+
now,
|
|
719
|
+
sleep,
|
|
720
|
+
uuid: hasDeterministicRandom ? () => createRandomUuid(random) : () => crypto.randomUUID()
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function createRandomUuid(random) {
|
|
724
|
+
const bytes = Array.from({ length: 16 }, () => Math.floor(readRandomUnit(random()) * 256));
|
|
725
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
726
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
727
|
+
const hex = bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
728
|
+
return [
|
|
729
|
+
hex.slice(0, 8),
|
|
730
|
+
hex.slice(8, 12),
|
|
731
|
+
hex.slice(12, 16),
|
|
732
|
+
hex.slice(16, 20),
|
|
733
|
+
hex.slice(20)
|
|
734
|
+
].join("-");
|
|
735
|
+
}
|
|
736
|
+
function readRandomUnit(value) {
|
|
737
|
+
if (Number.isFinite(value) && value >= 0 && value < 1) {
|
|
738
|
+
return value;
|
|
739
|
+
}
|
|
740
|
+
throw new RangeError("time.uuid() random source must return a number in [0, 1).");
|
|
741
|
+
}
|
|
742
|
+
function assertSleepDelay(ms) {
|
|
743
|
+
if (Number.isFinite(ms) && ms >= 0) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
throw new RangeError("time.sleep(ms) requires a non-negative finite millisecond delay.");
|
|
747
|
+
}
|
|
748
|
+
function createSleepAbortError() {
|
|
749
|
+
return new Error("time.sleep aborted.");
|
|
750
|
+
}
|
|
751
|
+
export {
|
|
752
|
+
AgentSpawnError,
|
|
753
|
+
Budget,
|
|
754
|
+
FileSnapshotBackend,
|
|
755
|
+
HostCallResumabilityError,
|
|
756
|
+
HostOperationResumePolicyError,
|
|
757
|
+
SandboxError,
|
|
758
|
+
SnapshotValidationError,
|
|
759
|
+
createReplayableRandom,
|
|
760
|
+
createSpawnUsageAccumulator,
|
|
761
|
+
declareHostOperation,
|
|
762
|
+
deepCopyFromSandbox,
|
|
763
|
+
deepCopyToSandbox,
|
|
764
|
+
dump,
|
|
765
|
+
extractBlock,
|
|
766
|
+
findExportedConstInitializer,
|
|
767
|
+
formatInterpreterError,
|
|
768
|
+
lint,
|
|
769
|
+
makeAgentModule,
|
|
770
|
+
makeEnvModule,
|
|
771
|
+
makeFailModule,
|
|
772
|
+
makeFsModule,
|
|
773
|
+
makeGitModule,
|
|
774
|
+
makeHarnessModule,
|
|
775
|
+
makeLogModule,
|
|
776
|
+
makeMcpModule,
|
|
777
|
+
makeMetricModule,
|
|
778
|
+
makeTimeModule,
|
|
779
|
+
noopOtelSink,
|
|
780
|
+
parse,
|
|
781
|
+
parseMcpConfig,
|
|
782
|
+
parseModule,
|
|
783
|
+
registerPendingHostCallPolicy,
|
|
784
|
+
restore,
|
|
785
|
+
run,
|
|
786
|
+
runHarness,
|
|
787
|
+
runHarnessPair,
|
|
788
|
+
runWithSpawnUsageAccumulator,
|
|
789
|
+
splitFrontmatter
|
|
790
|
+
};
|
|
791
|
+
//# sourceMappingURL=index.js.map
|