poe-code 8.0.0 → 8.0.1

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.
@@ -1,569 +1,604 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ countLineBreaks,
4
+ createBrokenPipeState,
5
+ createLintModulesFromRuntimeRegistry,
6
+ createSafeOutputStream,
7
+ extractBlock,
8
+ formatInterpreterError,
9
+ makeAgentModule,
10
+ makeFailModule,
11
+ makeFsModule,
12
+ makeHarnessModule,
13
+ makeLogModule,
14
+ makeMetricModule,
15
+ splitFrontmatter,
16
+ withBrokenPipeGuard
17
+ } from "./chunks/chunk-AVDRLE3Y.js";
18
+ import {
19
+ Budget,
20
+ SandboxError,
21
+ dump,
22
+ dumpCurrent,
23
+ hasOwnErrorCode,
24
+ lint,
25
+ parseModule,
26
+ replaceErrorStack,
27
+ restore,
28
+ run
29
+ } from "./chunks/chunk-ME5BTAZT.js";
30
+
31
+ // packages/safejs/src/cli.ts
2
32
  import { mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
3
33
  import path, { dirname, extname } from "node:path";
4
34
  import { formatWithOptions } from "node:util";
5
35
  import { fileURLToPath } from "node:url";
6
- import { hasOwnErrorCode } from "./error-codes.js";
7
- import { formatInterpreterError } from "./error/format.js";
8
- import { replaceErrorStack } from "./error/shape.js";
9
- import { Budget, SandboxError } from "./interp/budget.js";
10
- import { countLineBreaks, extractBlock } from "./loader/extract-block.js";
11
- import { splitFrontmatter } from "./loader/frontmatter.js";
12
- import { lint } from "./lint.js";
13
- import { createLintModulesFromRuntimeRegistry } from "./lint/runtime-modules.js";
14
- import { makeAgentModule } from "./modules/agent.js";
15
- import { makeFailModule } from "./modules/fail.js";
16
- import { makeFsModule } from "./modules/fs.js";
17
- import { makeHarnessModule } from "./modules/harness.js";
18
- import { makeLogModule } from "./modules/log.js";
19
- import { makeMetricModule } from "./modules/metric.js";
20
- import { createBrokenPipeState, createSafeOutputStream, withBrokenPipeGuard } from "./output-stream.js";
21
- import { parseModule } from "./parse/parser.js";
22
- import { restore } from "./restore.js";
23
- import { run } from "./run.js";
24
- import { dump, dumpCurrent } from "./snapshot/dump.js";
25
- const EXIT_RUNTIME = 1;
26
- const EXIT_PARSE = 2;
27
- const EXIT_BUDGET = 3;
28
- const EXIT_SIGINT = 130;
29
- export async function runCli(argv, options = {}) {
30
- const brokenPipe = createBrokenPipeState();
31
- const stdout = createSafeOutputStream(options.stdout ?? process.stdout, brokenPipe);
32
- const stderr = createSafeOutputStream(options.stderr ?? process.stderr, brokenPipe);
33
- return withBrokenPipeGuard([options.stdout ?? process.stdout, options.stderr ?? process.stderr], brokenPipe, async () => {
34
- try {
35
- if (argv.includes("--help") || argv.includes("-h")) {
36
- stdout.write(`${createUsage()}\n`);
37
- return 0;
38
- }
39
- const parsed = parseArgs(argv);
40
- if (parsed.filepath === undefined) {
41
- stderr.write(`${createUsage()}\n`);
42
- return brokenPipe.closed ? 0 : EXIT_RUNTIME;
43
- }
44
- const cwd = options.cwd ?? readCurrentWorkingDirectory();
45
- const filepath = path.resolve(cwd, parsed.filepath);
46
- await assertHarnessFile({
47
- displayPath: parsed.filepath,
48
- filepath,
49
- statFile: options.stat ?? stat
50
- });
51
- return await runScriptFile(filepath, parsed, {
52
- cwd,
53
- modulesFor: options.modulesFor,
54
- process: options.process ?? process,
55
- readFile: options.readFile ?? readFile,
56
- brokenPipe,
57
- stderr,
58
- stdout,
59
- writeFile: options.writeFile ?? writeFile
60
- });
36
+ var EXIT_RUNTIME = 1;
37
+ var EXIT_PARSE = 2;
38
+ var EXIT_BUDGET = 3;
39
+ var EXIT_SIGINT = 130;
40
+ async function runCli(argv, options = {}) {
41
+ const brokenPipe = createBrokenPipeState();
42
+ const stdout = createSafeOutputStream(options.stdout ?? process.stdout, brokenPipe);
43
+ const stderr = createSafeOutputStream(options.stderr ?? process.stderr, brokenPipe);
44
+ return withBrokenPipeGuard(
45
+ [options.stdout ?? process.stdout, options.stderr ?? process.stderr],
46
+ brokenPipe,
47
+ async () => {
48
+ try {
49
+ if (argv.includes("--help") || argv.includes("-h")) {
50
+ stdout.write(`${createUsage()}
51
+ `);
52
+ return 0;
61
53
  }
62
- catch (error) {
63
- if (brokenPipe.closed) {
64
- return 0;
65
- }
66
- stderr.write(`${readErrorMessage(error)}\n`);
67
- return brokenPipe.closed
68
- ? 0
69
- : error instanceof CliExitError
70
- ? error.exitCode
71
- : exitCodeForError(error);
54
+ const parsed = parseArgs(argv);
55
+ if (parsed.filepath === void 0) {
56
+ stderr.write(`${createUsage()}
57
+ `);
58
+ return brokenPipe.closed ? 0 : EXIT_RUNTIME;
72
59
  }
73
- });
60
+ const cwd = options.cwd ?? readCurrentWorkingDirectory();
61
+ const filepath = path.resolve(cwd, parsed.filepath);
62
+ await assertHarnessFile({
63
+ displayPath: parsed.filepath,
64
+ filepath,
65
+ statFile: options.stat ?? stat
66
+ });
67
+ return await runScriptFile(filepath, parsed, {
68
+ cwd,
69
+ modulesFor: options.modulesFor,
70
+ process: options.process ?? process,
71
+ readFile: options.readFile ?? readFile,
72
+ brokenPipe,
73
+ stderr,
74
+ stdout,
75
+ writeFile: options.writeFile ?? writeFile
76
+ });
77
+ } catch (error) {
78
+ if (brokenPipe.closed) {
79
+ return 0;
80
+ }
81
+ stderr.write(`${readErrorMessage(error)}
82
+ `);
83
+ return brokenPipe.closed ? 0 : error instanceof CliExitError ? error.exitCode : exitCodeForError(error);
84
+ }
85
+ }
86
+ );
74
87
  }
75
88
  function readCurrentWorkingDirectory() {
76
- try {
77
- return process.cwd();
78
- }
79
- catch (error) {
80
- throw new Error(`Unable to resolve current working directory: ${readErrorMessage(error)}`);
81
- }
89
+ try {
90
+ return process.cwd();
91
+ } catch (error) {
92
+ throw new Error(`Unable to resolve current working directory: ${readErrorMessage(error)}`);
93
+ }
82
94
  }
83
95
  function parseArgs(argv) {
84
- const parsed = {
85
- fix: false,
86
- fs: false
87
- };
88
- for (let index = 0; index < argv.length; index += 1) {
89
- const arg = argv[index];
90
- if (arg === "--fix") {
91
- parsed.fix = true;
92
- continue;
93
- }
94
- if (arg === "--fs") {
95
- parsed.fs = true;
96
- continue;
97
- }
98
- if (arg === "--fs-root") {
99
- parsed.fsRoot = readFlagValue(argv, index, arg);
100
- index += 1;
101
- continue;
102
- }
103
- if (arg === "--snapshot") {
104
- parsed.snapshotPath = readFlagValue(argv, index, arg);
105
- index += 1;
106
- continue;
107
- }
108
- if (arg === "--restore") {
109
- parsed.restorePath = readFlagValue(argv, index, arg);
110
- index += 1;
111
- continue;
112
- }
113
- if (arg === "--max-steps") {
114
- parsed.maxSteps = readPositiveInteger(readFlagValue(argv, index, arg), arg);
115
- index += 1;
116
- continue;
117
- }
118
- if (arg === "--data-size") {
119
- parsed.dataSize = readPositiveInteger(readFlagValue(argv, index, arg), arg);
120
- index += 1;
121
- continue;
122
- }
123
- if (arg.startsWith("-")) {
124
- throw new CliExitError(`Unknown flag: ${arg}`, EXIT_RUNTIME);
125
- }
126
- if (parsed.filepath !== undefined) {
127
- throw new CliExitError(`Unexpected argument: ${arg}`, EXIT_RUNTIME);
128
- }
129
- parsed.filepath = arg;
130
- }
131
- if (!parsed.fs && parsed.fsRoot !== undefined) {
132
- throw new CliExitError("--fs-root requires --fs. Pass --fs to give the script a filesystem confined to that root, or drop --fs-root.", EXIT_RUNTIME);
133
- }
134
- return parsed;
96
+ const parsed = {
97
+ fix: false,
98
+ fs: false
99
+ };
100
+ for (let index = 0; index < argv.length; index += 1) {
101
+ const arg = argv[index];
102
+ if (arg === "--fix") {
103
+ parsed.fix = true;
104
+ continue;
105
+ }
106
+ if (arg === "--fs") {
107
+ parsed.fs = true;
108
+ continue;
109
+ }
110
+ if (arg === "--fs-root") {
111
+ parsed.fsRoot = readFlagValue(argv, index, arg);
112
+ index += 1;
113
+ continue;
114
+ }
115
+ if (arg === "--snapshot") {
116
+ parsed.snapshotPath = readFlagValue(argv, index, arg);
117
+ index += 1;
118
+ continue;
119
+ }
120
+ if (arg === "--restore") {
121
+ parsed.restorePath = readFlagValue(argv, index, arg);
122
+ index += 1;
123
+ continue;
124
+ }
125
+ if (arg === "--max-steps") {
126
+ parsed.maxSteps = readPositiveInteger(readFlagValue(argv, index, arg), arg);
127
+ index += 1;
128
+ continue;
129
+ }
130
+ if (arg === "--data-size") {
131
+ parsed.dataSize = readPositiveInteger(readFlagValue(argv, index, arg), arg);
132
+ index += 1;
133
+ continue;
134
+ }
135
+ if (arg.startsWith("-")) {
136
+ throw new CliExitError(`Unknown flag: ${arg}`, EXIT_RUNTIME);
137
+ }
138
+ if (parsed.filepath !== void 0) {
139
+ throw new CliExitError(`Unexpected argument: ${arg}`, EXIT_RUNTIME);
140
+ }
141
+ parsed.filepath = arg;
142
+ }
143
+ if (!parsed.fs && parsed.fsRoot !== void 0) {
144
+ throw new CliExitError(
145
+ "--fs-root requires --fs. Pass --fs to give the script a filesystem confined to that root, or drop --fs-root.",
146
+ EXIT_RUNTIME
147
+ );
148
+ }
149
+ return parsed;
135
150
  }
136
151
  function readFlagValue(argv, index, flag) {
137
- const value = argv[index + 1];
138
- if (value === undefined || value.length === 0) {
139
- throw new CliExitError(`Missing value for ${flag}`, EXIT_RUNTIME);
140
- }
141
- return value;
152
+ const value = argv[index + 1];
153
+ if (value === void 0 || value.length === 0) {
154
+ throw new CliExitError(`Missing value for ${flag}`, EXIT_RUNTIME);
155
+ }
156
+ return value;
142
157
  }
143
158
  function readPositiveInteger(value, flag) {
144
- const parsed = Number(value);
145
- if (!Number.isInteger(parsed) || parsed <= 0) {
146
- throw new CliExitError(`${flag} must be a positive integer`, EXIT_RUNTIME);
147
- }
148
- return parsed;
159
+ const parsed = Number(value);
160
+ if (!Number.isInteger(parsed) || parsed <= 0) {
161
+ throw new CliExitError(`${flag} must be a positive integer`, EXIT_RUNTIME);
162
+ }
163
+ return parsed;
149
164
  }
150
165
  async function assertHarnessFile(input) {
151
- let stats;
152
- try {
153
- stats = await input.statFile(input.filepath);
154
- }
155
- catch (error) {
156
- if (hasErrorCode(error, "ENOENT")) {
157
- throw new CliExitError(`File not found: ${input.displayPath}`, EXIT_RUNTIME);
158
- }
159
- throw error;
160
- }
161
- if (!stats.isFile()) {
162
- throw new CliExitError(`Harness path must point to a file: ${input.displayPath}`, EXIT_RUNTIME);
163
- }
166
+ let stats;
167
+ try {
168
+ stats = await input.statFile(input.filepath);
169
+ } catch (error) {
170
+ if (hasErrorCode(error, "ENOENT")) {
171
+ throw new CliExitError(`File not found: ${input.displayPath}`, EXIT_RUNTIME);
172
+ }
173
+ throw error;
174
+ }
175
+ if (!stats.isFile()) {
176
+ throw new CliExitError(`Harness path must point to a file: ${input.displayPath}`, EXIT_RUNTIME);
177
+ }
164
178
  }
165
179
  async function runScriptFile(filepath, parsed, options) {
166
- const loaded = loadExecutableSource(filepath, await options.readFile(filepath, "utf8"));
167
- const meta = {
180
+ const loaded = loadExecutableSource(filepath, await options.readFile(filepath, "utf8"));
181
+ const meta = {
182
+ filepath,
183
+ kind: loaded.frontmatter.kind,
184
+ version: loaded.frontmatter.version
185
+ };
186
+ const runtime = createRuntime(loaded.frontmatter, meta, {
187
+ fs: parsed.fs ? { root: path.resolve(options.cwd, parsed.fsRoot ?? dirname(filepath)) } : void 0,
188
+ modulesFor: options.modulesFor,
189
+ stderr: options.stderr,
190
+ stdout: options.stdout
191
+ });
192
+ const modules = excludeHarnessModule(runtime.registry, loaded.isRawScript);
193
+ let executableSource = loaded.executableSource;
194
+ const lintResult = parsed.fix ? lint(executableSource, {
195
+ allowedExportNames: ["schema"],
196
+ filename: filepath,
197
+ fix: true,
198
+ fixRanges: loaded.fixRanges,
199
+ frontmatterFields: Object.keys(loaded.frontmatter),
200
+ modules: createLintModulesFromRuntimeRegistry(modules)
201
+ }) : lint(executableSource, {
202
+ allowedExportNames: ["schema"],
203
+ filename: filepath,
204
+ frontmatterFields: Object.keys(loaded.frontmatter),
205
+ modules: createLintModulesFromRuntimeRegistry(modules)
206
+ });
207
+ const diagnostics = Array.isArray(lintResult) ? lintResult : lintResult.diagnostics;
208
+ if (!Array.isArray(lintResult)) {
209
+ executableSource = lintResult.fixed;
210
+ if (lintResult.fixed !== loaded.executableSource) {
211
+ await options.writeFile(
168
212
  filepath,
169
- kind: loaded.frontmatter.kind,
170
- version: loaded.frontmatter.version
171
- };
172
- const runtime = createRuntime(loaded.frontmatter, meta, {
173
- fs: parsed.fs
174
- ? { root: path.resolve(options.cwd, parsed.fsRoot ?? dirname(filepath)) }
175
- : undefined,
176
- modulesFor: options.modulesFor,
177
- stderr: options.stderr,
178
- stdout: options.stdout
179
- });
180
- const modules = excludeHarnessModule(runtime.registry, loaded.isRawScript);
181
- let executableSource = loaded.executableSource;
182
- const lintResult = parsed.fix
183
- ? lint(executableSource, {
184
- allowedExportNames: ["schema"],
185
- filename: filepath,
186
- fix: true,
187
- fixRanges: loaded.fixRanges,
188
- frontmatterFields: Object.keys(loaded.frontmatter),
189
- modules: createLintModulesFromRuntimeRegistry(modules)
190
- })
191
- : lint(executableSource, {
192
- allowedExportNames: ["schema"],
193
- filename: filepath,
194
- frontmatterFields: Object.keys(loaded.frontmatter),
195
- modules: createLintModulesFromRuntimeRegistry(modules)
196
- });
197
- const diagnostics = Array.isArray(lintResult) ? lintResult : lintResult.diagnostics;
198
- if (!Array.isArray(lintResult)) {
199
- executableSource = lintResult.fixed;
200
- if (lintResult.fixed !== loaded.executableSource) {
201
- await options.writeFile(filepath, replaceExecutableSource(loaded.rawSource, loaded, lintResult.fixes), {
202
- encoding: "utf8"
203
- });
204
- }
205
- }
206
- const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
207
- if (lintErrors.length > 0) {
208
- options.stderr.write(`Lint failed:\n${formatDiagnostics(lintErrors)}\n`);
209
- if (options.brokenPipe.closed) {
210
- return 0;
211
- }
212
- return lintErrors.some((diagnostic) => diagnostic.code === "AS001") ? EXIT_PARSE : EXIT_RUNTIME;
213
- }
214
- const lintWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
215
- if (lintWarnings.length > 0) {
216
- options.stderr.write(`Lint warnings:\n${formatDiagnostics(lintWarnings)}\n`);
217
- if (options.brokenPipe.closed) {
218
- return 0;
219
- }
220
- }
221
- const snapshot = await readRestoreSnapshot(parsed.restorePath, options);
222
- if (snapshot !== undefined) {
223
- restore(snapshot, { source: executableSource });
224
- }
225
- const abortController = new AbortController();
226
- let interrupted = false;
227
- let runPromise;
228
- let signalSnapshotWrite;
229
- const onSigint = () => {
230
- interrupted = true;
231
- abortController.abort(createAbortError());
232
- options.stderr.write("Interrupted by SIGINT\n");
233
- if (parsed.snapshotPath !== undefined && runPromise !== undefined) {
234
- signalSnapshotWrite = writeCurrentSnapshot(runPromise, parsed.snapshotPath, options);
235
- }
236
- };
237
- options.process.on("SIGINT", onSigint);
238
- try {
239
- runPromise = run(executableSource, {
240
- budget: parsed.maxSteps === undefined && parsed.dataSize === undefined
241
- ? undefined
242
- : new Budget({ dataSize: parsed.dataSize, maxSteps: parsed.maxSteps }),
243
- entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : undefined,
244
- filename: filepath,
245
- modules,
246
- signal: abortController.signal,
247
- sink: createConsoleSink(options.stdout, options.stderr),
248
- snapshot
249
- });
250
- const result = await runPromise;
251
- await signalSnapshotWrite;
252
- if (options.brokenPipe.closed) {
253
- return 0;
254
- }
255
- if (parsed.snapshotPath !== undefined) {
256
- await writeSnapshot(parsed.snapshotPath, await dump(result), options);
257
- }
258
- if (!result.ok) {
259
- options.stderr.write(`${formatInterpreterError(result.error, {
260
- filename: filepath,
261
- source: executableSource
262
- })}\n`);
263
- if (options.brokenPipe.closed) {
264
- return 0;
265
- }
266
- return interrupted ? EXIT_SIGINT : exitCodeForError(result.error);
267
- }
268
- options.stdout.write(`${JSON.stringify({ ok: true, returnValue: result.returnValue })}\n`);
269
- return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : 0;
270
- }
271
- catch (error) {
272
- await signalSnapshotWrite;
273
- if (options.brokenPipe.closed) {
274
- return 0;
213
+ replaceExecutableSource(loaded.rawSource, loaded, lintResult.fixes),
214
+ {
215
+ encoding: "utf8"
275
216
  }
276
- options.stderr.write(`${formatInterpreterError(error, {
277
- filename: filepath,
278
- source: executableSource
279
- })}\n`);
280
- return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : exitCodeForError(error);
281
- }
282
- finally {
283
- options.process.off("SIGINT", onSigint);
284
- }
217
+ );
218
+ }
219
+ }
220
+ const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
221
+ if (lintErrors.length > 0) {
222
+ options.stderr.write(`Lint failed:
223
+ ${formatDiagnostics(lintErrors)}
224
+ `);
225
+ if (options.brokenPipe.closed) {
226
+ return 0;
227
+ }
228
+ return lintErrors.some((diagnostic) => diagnostic.code === "AS001") ? EXIT_PARSE : EXIT_RUNTIME;
229
+ }
230
+ const lintWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
231
+ if (lintWarnings.length > 0) {
232
+ options.stderr.write(`Lint warnings:
233
+ ${formatDiagnostics(lintWarnings)}
234
+ `);
235
+ if (options.brokenPipe.closed) {
236
+ return 0;
237
+ }
238
+ }
239
+ const snapshot = await readRestoreSnapshot(parsed.restorePath, options);
240
+ if (snapshot !== void 0) {
241
+ restore(snapshot, { source: executableSource });
242
+ }
243
+ const abortController = new AbortController();
244
+ let interrupted = false;
245
+ let runPromise;
246
+ let signalSnapshotWrite;
247
+ const onSigint = () => {
248
+ interrupted = true;
249
+ abortController.abort(createAbortError());
250
+ options.stderr.write("Interrupted by SIGINT\n");
251
+ if (parsed.snapshotPath !== void 0 && runPromise !== void 0) {
252
+ signalSnapshotWrite = writeCurrentSnapshot(runPromise, parsed.snapshotPath, options);
253
+ }
254
+ };
255
+ options.process.on("SIGINT", onSigint);
256
+ try {
257
+ runPromise = run(executableSource, {
258
+ budget: parsed.maxSteps === void 0 && parsed.dataSize === void 0 ? void 0 : new Budget({ dataSize: parsed.dataSize, maxSteps: parsed.maxSteps }),
259
+ entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : void 0,
260
+ filename: filepath,
261
+ modules,
262
+ signal: abortController.signal,
263
+ sink: createConsoleSink(options.stdout, options.stderr),
264
+ snapshot
265
+ });
266
+ const result = await runPromise;
267
+ await signalSnapshotWrite;
268
+ if (options.brokenPipe.closed) {
269
+ return 0;
270
+ }
271
+ if (parsed.snapshotPath !== void 0) {
272
+ await writeSnapshot(parsed.snapshotPath, await dump(result), options);
273
+ }
274
+ if (!result.ok) {
275
+ options.stderr.write(
276
+ `${formatInterpreterError(result.error, {
277
+ filename: filepath,
278
+ source: executableSource
279
+ })}
280
+ `
281
+ );
282
+ if (options.brokenPipe.closed) {
283
+ return 0;
284
+ }
285
+ return interrupted ? EXIT_SIGINT : exitCodeForError(result.error);
286
+ }
287
+ options.stdout.write(`${JSON.stringify({ ok: true, returnValue: result.returnValue })}
288
+ `);
289
+ return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : 0;
290
+ } catch (error) {
291
+ await signalSnapshotWrite;
292
+ if (options.brokenPipe.closed) {
293
+ return 0;
294
+ }
295
+ options.stderr.write(
296
+ `${formatInterpreterError(error, {
297
+ filename: filepath,
298
+ source: executableSource
299
+ })}
300
+ `
301
+ );
302
+ return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : exitCodeForError(error);
303
+ } finally {
304
+ options.process.off("SIGINT", onSigint);
305
+ }
285
306
  }
286
307
  function loadExecutableSource(filepath, rawSource) {
287
- const source = stripByteOrderMark(rawSource);
288
- const byteOrderMarkLength = rawSource.length - source.length;
289
- if (isRawScriptExtension(extname(filepath))) {
290
- return {
291
- sourceOffset: byteOrderMarkLength,
292
- executableSource: source,
293
- frontmatter: {},
294
- isRawScript: true,
295
- rawSource
296
- };
297
- }
298
- const { frontmatter, body } = splitFrontmatter(source);
299
- const bodyStartOffset = source.length - body.length;
300
- const executableBlock = extractBlock(body, countLineBreaks(source, 0, bodyStartOffset) + 1);
301
- const lineOffset = countLineBreaks(source, 0, bodyStartOffset + executableBlock.startOffset);
308
+ const source = stripByteOrderMark(rawSource);
309
+ const byteOrderMarkLength = rawSource.length - source.length;
310
+ if (isRawScriptExtension(extname(filepath))) {
302
311
  return {
303
- sourceOffset: byteOrderMarkLength + bodyStartOffset + executableBlock.startOffset - lineOffset,
304
- fixRanges: executableBlock.ranges.map(([start, end]) => [
305
- start - executableBlock.startOffset + lineOffset,
306
- end - executableBlock.startOffset + lineOffset
307
- ]),
308
- executableSource: createLineOffsetSource(executableBlock.source, lineOffset),
309
- frontmatter,
310
- isRawScript: false,
311
- rawSource
312
+ sourceOffset: byteOrderMarkLength,
313
+ executableSource: source,
314
+ frontmatter: {},
315
+ isRawScript: true,
316
+ rawSource
312
317
  };
318
+ }
319
+ const { frontmatter, body } = splitFrontmatter(source);
320
+ const bodyStartOffset = source.length - body.length;
321
+ const executableBlock = extractBlock(body, countLineBreaks(source, 0, bodyStartOffset) + 1);
322
+ const lineOffset = countLineBreaks(source, 0, bodyStartOffset + executableBlock.startOffset);
323
+ return {
324
+ sourceOffset: byteOrderMarkLength + bodyStartOffset + executableBlock.startOffset - lineOffset,
325
+ fixRanges: executableBlock.ranges.map(([start, end]) => [
326
+ start - executableBlock.startOffset + lineOffset,
327
+ end - executableBlock.startOffset + lineOffset
328
+ ]),
329
+ executableSource: createLineOffsetSource(executableBlock.source, lineOffset),
330
+ frontmatter,
331
+ isRawScript: false,
332
+ rawSource
333
+ };
313
334
  }
314
335
  function isRawScriptExtension(extension) {
315
- return extension === ".safejs" || extension === ".ajs";
336
+ return extension === ".safejs" || extension === ".ajs";
316
337
  }
317
338
  function replaceExecutableSource(source, loaded, fixes) {
318
- return fixes.reduce((result, fix) => `${result.slice(0, loaded.sourceOffset + fix.range[0])}${fix.replacement}${result.slice(loaded.sourceOffset + fix.range[1])}`, source);
339
+ return fixes.reduce(
340
+ (result, fix) => `${result.slice(0, loaded.sourceOffset + fix.range[0])}${fix.replacement}${result.slice(loaded.sourceOffset + fix.range[1])}`,
341
+ source
342
+ );
319
343
  }
320
344
  function createRuntime(frontmatter, meta, options) {
321
- if (options.modulesFor !== undefined) {
322
- return {
323
- registry: options.modulesFor(frontmatter, meta, {
324
- stderr: options.stderr,
325
- stdout: options.stdout
326
- })
327
- };
328
- }
329
- const state = {
330
- checkpointCount: 0,
331
- commitCount: 0,
332
- metricCalls: new Map(),
333
- spawnCount: 0
334
- };
335
- const harness = makeHarnessModule(frontmatter, meta);
336
- const agent = makeAgentModule(async (input) => {
337
- state.spawnCount += 1;
338
- return {
339
- exitCode: 0,
340
- stdout: "",
341
- stderr: "",
342
- summary: `${input.agent} handled ${summarizePrompt(input.prompt)}`,
343
- durationMs: 25 * state.spawnCount
344
- };
345
- });
346
- const git = {
347
- async checkpoint() {
348
- state.checkpointCount += 1;
349
- return {
350
- head: `head-${state.checkpointCount}`,
351
- stashRef: `savepoint-${state.checkpointCount}`
352
- };
353
- },
354
- async commit() {
355
- state.commitCount += 1;
356
- return `commit-${state.commitCount}`;
357
- },
358
- async revert() {
359
- return undefined;
360
- }
345
+ if (options.modulesFor !== void 0) {
346
+ return {
347
+ registry: options.modulesFor(frontmatter, meta, {
348
+ stderr: options.stderr,
349
+ stdout: options.stdout
350
+ })
361
351
  };
362
- const log = makeLogModule((entry) => {
363
- const normalized = normalizeLogEntry(entry);
364
- const stream = normalized.type === "error" ? options.stderr : options.stdout;
365
- stream.write(`${JSON.stringify(normalized)}\n`);
366
- });
367
- const metric = makeMetricModule(async (scriptName) => `${readMetricScore(scriptName, state)}\n`);
352
+ }
353
+ const state = {
354
+ checkpointCount: 0,
355
+ commitCount: 0,
356
+ metricCalls: /* @__PURE__ */ new Map(),
357
+ spawnCount: 0
358
+ };
359
+ const harness = makeHarnessModule(frontmatter, meta);
360
+ const agent = makeAgentModule(async (input) => {
361
+ state.spawnCount += 1;
368
362
  return {
369
- registry: {
370
- agent: toModuleExports(agent),
371
- fail: toModuleExports(new Map([["default", makeFailModule().default]])),
372
- // The one module here that is not a stub, so it is registered only when asked for.
373
- ...(options.fs === undefined ? {} : { fs: toModuleExports(makeFsModule(options.fs)) }),
374
- git: toModuleExports(new Map([
375
- ["checkpoint", git.checkpoint],
376
- ["commit", git.commit],
377
- ["revert", git.revert]
378
- ])),
379
- harness: toModuleExports(harness),
380
- log: toModuleExports(log),
381
- metric: toModuleExports(metric)
382
- }
363
+ exitCode: 0,
364
+ stdout: "",
365
+ stderr: "",
366
+ summary: `${input.agent} handled ${summarizePrompt(input.prompt)}`,
367
+ durationMs: 25 * state.spawnCount
383
368
  };
369
+ });
370
+ const git = {
371
+ async checkpoint() {
372
+ state.checkpointCount += 1;
373
+ return {
374
+ head: `head-${state.checkpointCount}`,
375
+ stashRef: `savepoint-${state.checkpointCount}`
376
+ };
377
+ },
378
+ async commit() {
379
+ state.commitCount += 1;
380
+ return `commit-${state.commitCount}`;
381
+ },
382
+ async revert() {
383
+ return void 0;
384
+ }
385
+ };
386
+ const log = makeLogModule((entry) => {
387
+ const normalized = normalizeLogEntry(entry);
388
+ const stream = normalized.type === "error" ? options.stderr : options.stdout;
389
+ stream.write(`${JSON.stringify(normalized)}
390
+ `);
391
+ });
392
+ const metric = makeMetricModule(async (scriptName) => `${readMetricScore(scriptName, state)}
393
+ `);
394
+ return {
395
+ registry: {
396
+ agent: toModuleExports(agent),
397
+ fail: toModuleExports(/* @__PURE__ */ new Map([["default", makeFailModule().default]])),
398
+ // The one module here that is not a stub, so it is registered only when asked for.
399
+ ...options.fs === void 0 ? {} : { fs: toModuleExports(makeFsModule(options.fs)) },
400
+ git: toModuleExports(
401
+ /* @__PURE__ */ new Map([
402
+ ["checkpoint", git.checkpoint],
403
+ ["commit", git.commit],
404
+ ["revert", git.revert]
405
+ ])
406
+ ),
407
+ harness: toModuleExports(harness),
408
+ log: toModuleExports(log),
409
+ metric: toModuleExports(metric)
410
+ }
411
+ };
384
412
  }
385
413
  async function readRestoreSnapshot(restorePath, options) {
386
- if (restorePath === undefined) {
387
- return undefined;
388
- }
389
- const snapshotPath = path.resolve(options.cwd, restorePath);
390
- let rawSnapshot;
391
- try {
392
- rawSnapshot = await options.readFile(snapshotPath, "utf8");
393
- }
394
- catch (error) {
395
- if (hasErrorCode(error, "ENOENT")) {
396
- throw new CliExitError(`Snapshot not found: ${restorePath}`, EXIT_RUNTIME);
397
- }
398
- throw error;
399
- }
400
- try {
401
- return JSON.parse(rawSnapshot);
402
- }
403
- catch (error) {
404
- throw new Error(`Failed to parse snapshot at ${restorePath}: ${readErrorMessage(error)}`);
405
- }
414
+ if (restorePath === void 0) {
415
+ return void 0;
416
+ }
417
+ const snapshotPath = path.resolve(options.cwd, restorePath);
418
+ let rawSnapshot;
419
+ try {
420
+ rawSnapshot = await options.readFile(snapshotPath, "utf8");
421
+ } catch (error) {
422
+ if (hasErrorCode(error, "ENOENT")) {
423
+ throw new CliExitError(`Snapshot not found: ${restorePath}`, EXIT_RUNTIME);
424
+ }
425
+ throw error;
426
+ }
427
+ try {
428
+ return JSON.parse(rawSnapshot);
429
+ } catch (error) {
430
+ throw new Error(`Failed to parse snapshot at ${restorePath}: ${readErrorMessage(error)}`);
431
+ }
406
432
  }
407
433
  async function writeCurrentSnapshot(result, snapshotPath, options) {
408
- try {
409
- await writeSnapshot(snapshotPath, await dumpCurrent(result), options);
410
- }
411
- catch (error) {
412
- options.stderr.write(`Failed to write snapshot at ${snapshotPath}: ${readErrorMessage(error)}\n`);
413
- }
434
+ try {
435
+ await writeSnapshot(snapshotPath, await dumpCurrent(result), options);
436
+ } catch (error) {
437
+ options.stderr.write(
438
+ `Failed to write snapshot at ${snapshotPath}: ${readErrorMessage(error)}
439
+ `
440
+ );
441
+ }
414
442
  }
415
443
  async function writeSnapshot(snapshotPath, content, options) {
416
- const resolvedPath = path.resolve(options.cwd, snapshotPath);
417
- await mkdir(dirname(resolvedPath), { recursive: true });
418
- await options.writeFile(resolvedPath, content, { encoding: "utf8" });
444
+ const resolvedPath = path.resolve(options.cwd, snapshotPath);
445
+ await mkdir(dirname(resolvedPath), { recursive: true });
446
+ await options.writeFile(resolvedPath, content, { encoding: "utf8" });
419
447
  }
420
448
  function createConsoleSink(stdout, stderr) {
421
- return {
422
- error(...args) {
423
- stderr.write(`${formatConsoleArgs(args)}\n`);
424
- },
425
- log(...args) {
426
- stdout.write(`${formatConsoleArgs(args)}\n`);
427
- }
428
- };
449
+ return {
450
+ error(...args) {
451
+ stderr.write(`${formatConsoleArgs(args)}
452
+ `);
453
+ },
454
+ log(...args) {
455
+ stdout.write(`${formatConsoleArgs(args)}
456
+ `);
457
+ }
458
+ };
429
459
  }
430
460
  function formatConsoleArgs(args) {
431
- return formatWithOptions({ colors: false, depth: 4 }, ...args);
461
+ return formatWithOptions({ colors: false, depth: 4 }, ...args);
432
462
  }
433
463
  function createUsage() {
434
- return [
435
- "Usage: poe-safejs [options] <script.md|script.safejs|script.ajs>",
436
- "",
437
- "Options:",
438
- " --fix apply lint fixes before running",
439
- " --fs register the fs module: a real filesystem, unlike the agent,",
440
- " git, and metric stubs this runner bundles",
441
- " --fs-root <path> directory --fs confines the script to (default: the script's",
442
- " directory)",
443
- " --snapshot <path> write the final snapshot, and best-effort snapshot on SIGINT",
444
- " --restore <path> restore from a snapshot before running",
445
- " --max-steps <n> cap interpreter step budget",
446
- " --data-size <n> cap retained sandbox data units",
447
- " -h, --help print this help",
448
- "",
449
- "Exit codes:",
450
- " 0 success",
451
- " 1 runtime, usage, file, or restore error",
452
- " 2 parse error",
453
- " 3 budget exceeded",
454
- " 130 interrupted by SIGINT"
455
- ].join("\n");
464
+ return [
465
+ "Usage: poe-safejs [options] <script.md|script.safejs|script.ajs>",
466
+ "",
467
+ "Options:",
468
+ " --fix apply lint fixes before running",
469
+ " --fs register the fs module: a real filesystem, unlike the agent,",
470
+ " git, and metric stubs this runner bundles",
471
+ " --fs-root <path> directory --fs confines the script to (default: the script's",
472
+ " directory)",
473
+ " --snapshot <path> write the final snapshot, and best-effort snapshot on SIGINT",
474
+ " --restore <path> restore from a snapshot before running",
475
+ " --max-steps <n> cap interpreter step budget",
476
+ " --data-size <n> cap retained sandbox data units",
477
+ " -h, --help print this help",
478
+ "",
479
+ "Exit codes:",
480
+ " 0 success",
481
+ " 1 runtime, usage, file, or restore error",
482
+ " 2 parse error",
483
+ " 3 budget exceeded",
484
+ " 130 interrupted by SIGINT"
485
+ ].join("\n");
456
486
  }
457
487
  function formatDiagnostics(diagnostics) {
458
- return diagnostics
459
- .map((diagnostic) => `${diagnostic.filename}:${diagnostic.line}:${diagnostic.column} ${diagnostic.code} ${diagnostic.message}`)
460
- .join("\n");
488
+ return diagnostics.map(
489
+ (diagnostic) => `${diagnostic.filename}:${diagnostic.line}:${diagnostic.column} ${diagnostic.code} ${diagnostic.message}`
490
+ ).join("\n");
461
491
  }
462
492
  function exitCodeForError(error) {
463
- if (error instanceof SandboxError && error.code === "budgetExceeded") {
464
- return EXIT_BUDGET;
465
- }
466
- if (isParseError(error)) {
467
- return EXIT_PARSE;
468
- }
469
- return EXIT_RUNTIME;
493
+ if (error instanceof SandboxError && error.code === "budgetExceeded") {
494
+ return EXIT_BUDGET;
495
+ }
496
+ if (isParseError(error)) {
497
+ return EXIT_PARSE;
498
+ }
499
+ return EXIT_RUNTIME;
470
500
  }
471
501
  function isParseError(error) {
472
- return error instanceof Error && error.name === "ParseError";
502
+ return error instanceof Error && error.name === "ParseError";
473
503
  }
474
504
  function hasDefaultExport(source, filename) {
475
- return parseModule(source, filename).body.some((statement) => statement.type === "ExportDefaultDeclaration");
505
+ return parseModule(source, filename).body.some(
506
+ (statement) => statement.type === "ExportDefaultDeclaration"
507
+ );
476
508
  }
477
509
  function excludeHarnessModule(modules, isRawScript) {
478
- if (!isRawScript) {
479
- return modules;
480
- }
481
- if (modules instanceof Map) {
482
- const rawModules = new Map(modules);
483
- rawModules.delete("harness");
484
- return rawModules;
485
- }
486
- const rawModules = Object.create(null);
487
- for (const [moduleName, moduleExports] of Object.entries(modules)) {
488
- if (moduleName !== "harness") {
489
- rawModules[moduleName] = moduleExports;
490
- }
491
- }
492
- return rawModules;
510
+ if (!isRawScript) {
511
+ return modules;
512
+ }
513
+ if (modules instanceof Map) {
514
+ const rawModules2 = new Map(modules);
515
+ rawModules2.delete("harness");
516
+ return rawModules2;
517
+ }
518
+ const rawModules = /* @__PURE__ */ Object.create(null);
519
+ for (const [moduleName, moduleExports] of Object.entries(modules)) {
520
+ if (moduleName !== "harness") {
521
+ rawModules[moduleName] = moduleExports;
522
+ }
523
+ }
524
+ return rawModules;
493
525
  }
494
526
  function stripByteOrderMark(source) {
495
- return source.startsWith("\uFEFF") ? source.slice(1) : source;
527
+ return source.startsWith("\uFEFF") ? source.slice(1) : source;
496
528
  }
497
529
  function createLineOffsetSource(source, lineOffset) {
498
- return `${"\n".repeat(Math.max(lineOffset, 0))}${source}`;
530
+ return `${"\n".repeat(Math.max(lineOffset, 0))}${source}`;
499
531
  }
500
532
  function normalizeLogEntry(entry) {
501
- return {
502
- ...entry,
503
- ts: "2026-04-29T00:00:00.000Z"
504
- };
533
+ return {
534
+ ...entry,
535
+ ts: "2026-04-29T00:00:00.000Z"
536
+ };
505
537
  }
506
538
  function summarizePrompt(prompt) {
507
- const normalizedPrompt = prompt.trim().replaceAll("\n", " ");
508
- return normalizedPrompt.length <= 48 ? normalizedPrompt : `${normalizedPrompt.slice(0, 45)}...`;
539
+ const normalizedPrompt = prompt.trim().replaceAll("\n", " ");
540
+ return normalizedPrompt.length <= 48 ? normalizedPrompt : `${normalizedPrompt.slice(0, 45)}...`;
509
541
  }
510
542
  function readMetricScore(scriptName, state) {
511
- const callCount = (state.metricCalls.get(scriptName) ?? 0) + 1;
512
- state.metricCalls.set(scriptName, callCount);
513
- if (scriptName === "tests" && callCount === 1) {
514
- return 10;
515
- }
516
- if (scriptName === "tests" && callCount === 2) {
517
- return 9;
518
- }
519
- return 9 + callCount;
543
+ const callCount = (state.metricCalls.get(scriptName) ?? 0) + 1;
544
+ state.metricCalls.set(scriptName, callCount);
545
+ if (scriptName === "tests" && callCount === 1) {
546
+ return 10;
547
+ }
548
+ if (scriptName === "tests" && callCount === 2) {
549
+ return 9;
550
+ }
551
+ return 9 + callCount;
520
552
  }
521
553
  function toModuleExports(value) {
522
- return value;
554
+ return value;
523
555
  }
524
556
  function createAbortError() {
525
- if (typeof DOMException === "function") {
526
- const error = new DOMException("This operation was aborted", "AbortError");
527
- replaceErrorStack(error);
528
- return error;
529
- }
530
- const error = new Error("This operation was aborted");
531
- error.name = "AbortError";
532
- replaceErrorStack(error);
533
- return error;
557
+ if (typeof DOMException === "function") {
558
+ const error2 = new DOMException("This operation was aborted", "AbortError");
559
+ replaceErrorStack(error2);
560
+ return error2;
561
+ }
562
+ const error = new Error("This operation was aborted");
563
+ error.name = "AbortError";
564
+ replaceErrorStack(error);
565
+ return error;
534
566
  }
535
567
  function hasErrorCode(error, code) {
536
- return hasOwnErrorCode(error, code);
568
+ return hasOwnErrorCode(error, code);
537
569
  }
538
570
  function readErrorMessage(error) {
539
- if (error instanceof Error) {
540
- return error.message;
541
- }
542
- return String(error);
543
- }
544
- class CliExitError extends Error {
545
- exitCode;
546
- constructor(message, exitCode) {
547
- super(message);
548
- this.name = "CliExitError";
549
- this.exitCode = exitCode;
550
- }
551
- }
571
+ if (error instanceof Error) {
572
+ return error.message;
573
+ }
574
+ return String(error);
575
+ }
576
+ var CliExitError = class extends Error {
577
+ exitCode;
578
+ constructor(message, exitCode) {
579
+ super(message);
580
+ this.name = "CliExitError";
581
+ this.exitCode = exitCode;
582
+ }
583
+ };
552
584
  async function isDirectExecution(entryPoint) {
553
- if (typeof entryPoint !== "string" || entryPoint.length === 0) {
554
- return false;
555
- }
556
- try {
557
- const [resolvedEntryPoint, resolvedModule] = await Promise.all([
558
- realpath(path.resolve(entryPoint)),
559
- realpath(fileURLToPath(import.meta.url))
560
- ]);
561
- return resolvedEntryPoint === resolvedModule;
562
- }
563
- catch {
564
- return false;
565
- }
585
+ if (typeof entryPoint !== "string" || entryPoint.length === 0) {
586
+ return false;
587
+ }
588
+ try {
589
+ const [resolvedEntryPoint, resolvedModule] = await Promise.all([
590
+ realpath(path.resolve(entryPoint)),
591
+ realpath(fileURLToPath(import.meta.url))
592
+ ]);
593
+ return resolvedEntryPoint === resolvedModule;
594
+ } catch {
595
+ return false;
596
+ }
566
597
  }
567
598
  if (await isDirectExecution(process.argv[1])) {
568
- process.exitCode = await runCli(process.argv.slice(2));
599
+ process.exitCode = await runCli(process.argv.slice(2));
569
600
  }
601
+ export {
602
+ runCli
603
+ };
604
+ //# sourceMappingURL=cli.js.map