poe-code 3.0.258 → 3.0.259-beta.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.
Files changed (76) hide show
  1. package/dist/cli/commands/gaslight.js +84 -1
  2. package/dist/cli/commands/gaslight.js.map +1 -1
  3. package/dist/cli/poe-theme.d.ts +1 -0
  4. package/dist/cli/poe-theme.js +5 -0
  5. package/dist/cli/poe-theme.js.map +1 -0
  6. package/dist/cli/program.js +1 -0
  7. package/dist/cli/program.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +2753 -1123
  10. package/dist/index.js.map +4 -4
  11. package/dist/metafile.json +1 -1
  12. package/dist/sdk/gaslight.d.ts +1 -1
  13. package/dist/sdk/gaslight.js +1 -1
  14. package/dist/sdk/gaslight.js.map +1 -1
  15. package/package.json +3 -1
  16. package/packages/agent-gaslight/dist/config.d.ts +5 -1
  17. package/packages/agent-gaslight/dist/config.js +32 -11
  18. package/packages/agent-gaslight/dist/index.d.ts +3 -2
  19. package/packages/agent-gaslight/dist/index.js +2 -1
  20. package/packages/agent-gaslight/dist/ingest.d.ts +2 -0
  21. package/packages/agent-gaslight/dist/ingest.js +486 -0
  22. package/packages/agent-gaslight/dist/run.js +1 -1
  23. package/packages/agent-gaslight/dist/types.d.ts +41 -6
  24. package/packages/agent-harness/dist/loader/run.js +6 -21
  25. package/packages/agent-script/dist/cli.d.ts +2 -3
  26. package/packages/agent-script/dist/cli.js +70 -36
  27. package/packages/agent-script/dist/example-runner.d.ts +2 -3
  28. package/packages/agent-script/dist/example-runner.js +69 -56
  29. package/packages/agent-script/dist/interp/exceptions.js +1 -1
  30. package/packages/agent-script/dist/interp/globals/object-array.d.ts +1 -1
  31. package/packages/agent-script/dist/interp/globals/object-array.js +39 -20
  32. package/packages/agent-script/dist/interp/host-bridge.js +1 -1
  33. package/packages/agent-script/dist/interp/interpreter.js +83 -17
  34. package/packages/agent-script/dist/interp/methods/array.js +25 -2
  35. package/packages/agent-script/dist/interp/methods/regex.js +1 -1
  36. package/packages/agent-script/dist/interp/promise-tracker.d.ts +16 -0
  37. package/packages/agent-script/dist/interp/promise-tracker.js +58 -0
  38. package/packages/agent-script/dist/interp/promise.js +38 -7
  39. package/packages/agent-script/dist/interp/scope.d.ts +1 -0
  40. package/packages/agent-script/dist/interp/scope.js +3 -0
  41. package/packages/agent-script/dist/interp/values.js +2 -0
  42. package/packages/agent-script/dist/lint/index.d.ts +2 -0
  43. package/packages/agent-script/dist/lint/index.js +2 -0
  44. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.d.ts +6 -1
  45. package/packages/agent-script/dist/lint/rules/AS-export-import-meta.js +33 -4
  46. package/packages/agent-script/dist/modules/agent.js +10 -1
  47. package/packages/agent-script/dist/modules/log.js +5 -1
  48. package/packages/agent-script/dist/modules/registry.js +9 -3
  49. package/packages/agent-script/dist/output-stream.d.ts +12 -0
  50. package/packages/agent-script/dist/output-stream.js +50 -0
  51. package/packages/agent-script/dist/parse/parser.d.ts +1 -1
  52. package/packages/agent-script/dist/parse/parser.js +151 -45
  53. package/packages/agent-script/dist/parse/tokenizer.js +26 -3
  54. package/packages/agent-script/dist/run.js +14 -3
  55. package/packages/agent-script/dist/runner/run-harness.js +28 -5
  56. package/packages/agent-traces/dist/collect.d.ts +4 -0
  57. package/packages/agent-traces/dist/collect.js +102 -0
  58. package/packages/agent-traces/dist/index.d.ts +4 -0
  59. package/packages/agent-traces/dist/index.js +3 -0
  60. package/packages/agent-traces/dist/jsonl.d.ts +2 -0
  61. package/packages/agent-traces/dist/jsonl.js +7 -0
  62. package/packages/agent-traces/dist/line-json.d.ts +4 -0
  63. package/packages/agent-traces/dist/line-json.js +40 -0
  64. package/packages/agent-traces/dist/readers/claude.d.ts +2 -0
  65. package/packages/agent-traces/dist/readers/claude.js +192 -0
  66. package/packages/agent-traces/dist/readers/codex.d.ts +2 -0
  67. package/packages/agent-traces/dist/readers/codex.js +266 -0
  68. package/packages/agent-traces/dist/readers/index.d.ts +5 -0
  69. package/packages/agent-traces/dist/readers/index.js +4 -0
  70. package/packages/agent-traces/dist/types.d.ts +84 -0
  71. package/packages/agent-traces/dist/types.js +1 -0
  72. package/packages/package-lint/dist/model.js +5 -1
  73. package/packages/package-lint/dist/source-imports.d.ts +11 -1
  74. package/packages/package-lint/dist/source-imports.js +30 -4
  75. package/packages/tiny-stdio-mcp-test-server/dist/cli.js +41 -0
  76. package/packages/tiny-stdio-mcp-test-server/dist/index.js +8 -0
@@ -1,10 +1,6 @@
1
1
  import type { SpawnMode, SpawnOptions, SpawnResult, SpawnUsage } from "../../agent-spawn/dist/index.js";
2
- export interface GaslightFileSystem {
3
- readFile(path: string, encoding: BufferEncoding): Promise<string>;
4
- stat(path: string): Promise<{
5
- isFile(): boolean;
6
- }>;
7
- }
2
+ import type { AgentTraceFileSystem, AgentTraceSource, CollectHumanPromptsOptions, CollectHumanPromptsResult } from "../../agent-traces/dist/index.js";
3
+ export type GaslightFileSystem = AgentTraceFileSystem;
8
4
  export type GaslightSpawn = (agent: string, options: SpawnOptions) => Promise<SpawnResult>;
9
5
  export type GaslightEvent = {
10
6
  type: "round.started";
@@ -33,6 +29,7 @@ export interface GaslightOptions {
33
29
  mode?: Exclude<SpawnMode, "auto">;
34
30
  cwd?: string;
35
31
  homeDir?: string;
32
+ configPath?: string;
36
33
  prompt?: string;
37
34
  followups?: string[];
38
35
  onEvent?: (event: GaslightEvent) => void;
@@ -45,3 +42,41 @@ export interface GaslightConfig {
45
42
  followups: string[];
46
43
  path: string;
47
44
  }
45
+ export type GaslightCollectHumanPrompts = (options: CollectHumanPromptsOptions) => Promise<CollectHumanPromptsResult>;
46
+ export type GaslightIngestEvent = {
47
+ type: "traces.discovered";
48
+ count: number;
49
+ } | {
50
+ type: "prompts.extracted";
51
+ traces: number;
52
+ prompts: number;
53
+ } | {
54
+ type: "analysis.started";
55
+ agent: string;
56
+ dataPath: string;
57
+ } | {
58
+ type: "config.written";
59
+ path: string;
60
+ };
61
+ export interface GaslightIngestOptions {
62
+ sources?: AgentTraceSource[];
63
+ analysisAgent: string;
64
+ model?: string;
65
+ cwd?: string;
66
+ homeDir?: string;
67
+ since?: string | Date;
68
+ limit?: number;
69
+ allWorkspaces?: boolean;
70
+ outputPath?: string;
71
+ keepDataPath?: string;
72
+ onEvent?: (event: GaslightIngestEvent) => void;
73
+ fs?: GaslightFileSystem;
74
+ spawn?: GaslightSpawn;
75
+ collectHumanPrompts?: GaslightCollectHumanPrompts;
76
+ }
77
+ export interface GaslightIngestResult {
78
+ outputPath: string;
79
+ dataPath: string;
80
+ promptCount: number;
81
+ traceCount: number;
82
+ }
@@ -3,7 +3,7 @@ import { lstat, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promi
3
3
  import os from "node:os";
4
4
  import { dirname, join, parse, resolve, sep } from "node:path";
5
5
  import { resolveRunLogDir } from "@poe-code/agent-harness-tools";
6
- import { lint, FileSnapshotBackend, createSpawnUsageAccumulator, makeTimeModule, parseModule, run, splitFrontmatter, runWithSpawnUsageAccumulator } from "@poe-code/agent-script";
6
+ import { lint, FileSnapshotBackend, createSpawnUsageAccumulator, makeTimeModule, run, splitFrontmatter, runWithSpawnUsageAccumulator } from "@poe-code/agent-script";
7
7
  import { hasOwnErrorCode } from "../error-codes.js";
8
8
  import { makeSchemaModule } from "../modules/schema.js";
9
9
  import { extractSchema } from "./extract-schema.js";
@@ -82,6 +82,10 @@ export async function runHarnessPair(mdPath, options) {
82
82
  const lintOptions = {
83
83
  allowedExportNames: ["schema"],
84
84
  allowedGlobals: options.allowedGlobals,
85
+ defaultExport: {
86
+ parameters: ["frontmatter"],
87
+ required: true
88
+ },
85
89
  filename: pair.ajsPath,
86
90
  frontmatterFields: readSchemaTopLevelFields(schema),
87
91
  modules: createLintModules(modules)
@@ -96,8 +100,7 @@ export async function runHarnessPair(mdPath, options) {
96
100
  }
97
101
  }
98
102
  const diagnostics = [
99
- ...(Array.isArray(lintDiagnostics) ? lintDiagnostics : lintDiagnostics.diagnostics),
100
- ...missingDefaultExportDiagnostics(executableSource, pair.ajsPath)
103
+ ...(Array.isArray(lintDiagnostics) ? lintDiagnostics : lintDiagnostics.diagnostics)
101
104
  ];
102
105
  options.onDiagnostics?.(diagnostics);
103
106
  throwOnLintErrors(diagnostics);
@@ -144,24 +147,6 @@ function throwOnLintErrors(diagnostics) {
144
147
  throw new LintError(errors);
145
148
  }
146
149
  }
147
- function missingDefaultExportDiagnostics(source, filename) {
148
- const module = parseModule(source, filename);
149
- const hasDefaultExport = module.body.some((statement) => statement.type === "ExportDefaultDeclaration");
150
- if (hasDefaultExport) {
151
- return [];
152
- }
153
- return [
154
- {
155
- code: "AS-EXPORT-DEFAULT-MISSING",
156
- severity: "error",
157
- message: "Module must export a default entry point.",
158
- filename,
159
- line: module.span.start.line,
160
- column: module.span.start.column,
161
- span: module.span
162
- }
163
- ];
164
- }
165
150
  function readSchemaTopLevelFields(schema) {
166
151
  if (schema?.kind !== "object") {
167
152
  return undefined;
@@ -1,7 +1,6 @@
1
+ import { type OutputStream } from "./output-stream.js";
1
2
  import type { ModuleRegistry } from "./modules/registry.js";
2
- type CliStream = {
3
- write(chunk: string): void;
4
- };
3
+ type CliStream = OutputStream;
5
4
  type CliProcess = Pick<NodeJS.Process, "off" | "on">;
6
5
  type FileStats = {
7
6
  isFile(): boolean;
@@ -1,6 +1,6 @@
1
1
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
2
  import path, { dirname, extname } from "node:path";
3
- import { inspect } from "node:util";
3
+ import { formatWithOptions } from "node:util";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { hasOwnErrorCode } from "./error-codes.js";
6
6
  import { formatInterpreterError } from "./error/format.js";
@@ -15,6 +15,7 @@ import { makeFailModule } from "./modules/fail.js";
15
15
  import { makeHarnessModule } from "./modules/harness.js";
16
16
  import { makeLogModule } from "./modules/log.js";
17
17
  import { makeMetricModule } from "./modules/metric.js";
18
+ import { createBrokenPipeState, createSafeOutputStream, withBrokenPipeGuard } from "./output-stream.js";
18
19
  import { parseModule } from "./parse/parser.js";
19
20
  import { restore } from "./restore.js";
20
21
  import { run } from "./run.js";
@@ -24,38 +25,57 @@ const EXIT_PARSE = 2;
24
25
  const EXIT_BUDGET = 3;
25
26
  const EXIT_SIGINT = 130;
26
27
  export async function runCli(argv, options = {}) {
27
- const stdout = options.stdout ?? process.stdout;
28
- const stderr = options.stderr ?? process.stderr;
29
- const cwd = options.cwd ?? process.cwd();
30
- try {
31
- if (argv.includes("--help") || argv.includes("-h")) {
32
- stdout.write(`${createUsage()}\n`);
33
- return 0;
28
+ const brokenPipe = createBrokenPipeState();
29
+ const stdout = createSafeOutputStream(options.stdout ?? process.stdout, brokenPipe);
30
+ const stderr = createSafeOutputStream(options.stderr ?? process.stderr, brokenPipe);
31
+ return withBrokenPipeGuard([options.stdout ?? process.stdout, options.stderr ?? process.stderr], brokenPipe, async () => {
32
+ try {
33
+ if (argv.includes("--help") || argv.includes("-h")) {
34
+ stdout.write(`${createUsage()}\n`);
35
+ return 0;
36
+ }
37
+ const parsed = parseArgs(argv);
38
+ if (parsed.filepath === undefined) {
39
+ stderr.write(`${createUsage()}\n`);
40
+ return brokenPipe.closed ? 0 : EXIT_RUNTIME;
41
+ }
42
+ const cwd = options.cwd ?? readCurrentWorkingDirectory();
43
+ const filepath = path.resolve(cwd, parsed.filepath);
44
+ await assertHarnessFile({
45
+ displayPath: parsed.filepath,
46
+ filepath,
47
+ statFile: options.stat ?? stat
48
+ });
49
+ return await runScriptFile(filepath, parsed, {
50
+ cwd,
51
+ modulesFor: options.modulesFor,
52
+ process: options.process ?? process,
53
+ readFile: options.readFile ?? readFile,
54
+ brokenPipe,
55
+ stderr,
56
+ stdout,
57
+ writeFile: options.writeFile ?? writeFile
58
+ });
34
59
  }
35
- const parsed = parseArgs(argv);
36
- if (parsed.filepath === undefined) {
37
- stderr.write(`${createUsage()}\n`);
38
- return EXIT_RUNTIME;
60
+ catch (error) {
61
+ if (brokenPipe.closed) {
62
+ return 0;
63
+ }
64
+ stderr.write(`${readErrorMessage(error)}\n`);
65
+ return brokenPipe.closed
66
+ ? 0
67
+ : error instanceof CliExitError
68
+ ? error.exitCode
69
+ : exitCodeForError(error);
39
70
  }
40
- const filepath = path.resolve(cwd, parsed.filepath);
41
- await assertHarnessFile({
42
- displayPath: parsed.filepath,
43
- filepath,
44
- statFile: options.stat ?? stat
45
- });
46
- return await runScriptFile(filepath, parsed, {
47
- cwd,
48
- modulesFor: options.modulesFor,
49
- process: options.process ?? process,
50
- readFile: options.readFile ?? readFile,
51
- stderr,
52
- stdout,
53
- writeFile: options.writeFile ?? writeFile
54
- });
71
+ });
72
+ }
73
+ function readCurrentWorkingDirectory() {
74
+ try {
75
+ return process.cwd();
55
76
  }
56
77
  catch (error) {
57
- stderr.write(`${readErrorMessage(error)}\n`);
58
- return error instanceof CliExitError ? error.exitCode : exitCodeForError(error);
78
+ throw new Error(`Unable to resolve current working directory: ${readErrorMessage(error)}`);
59
79
  }
60
80
  }
61
81
  function parseArgs(argv) {
@@ -167,11 +187,19 @@ async function runScriptFile(filepath, parsed, options) {
167
187
  const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
168
188
  if (lintErrors.length > 0) {
169
189
  options.stderr.write(`Lint failed:\n${formatDiagnostics(lintErrors)}\n`);
170
- return lintErrors.some((diagnostic) => diagnostic.code === "AS001") ? EXIT_PARSE : EXIT_RUNTIME;
190
+ if (options.brokenPipe.closed) {
191
+ return 0;
192
+ }
193
+ return lintErrors.some((diagnostic) => diagnostic.code === "AS001")
194
+ ? EXIT_PARSE
195
+ : EXIT_RUNTIME;
171
196
  }
172
197
  const lintWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
173
198
  if (lintWarnings.length > 0) {
174
199
  options.stderr.write(`Lint warnings:\n${formatDiagnostics(lintWarnings)}\n`);
200
+ if (options.brokenPipe.closed) {
201
+ return 0;
202
+ }
175
203
  }
176
204
  const snapshot = await readRestoreSnapshot(parsed.restorePath, options);
177
205
  if (snapshot !== undefined) {
@@ -204,6 +232,9 @@ async function runScriptFile(filepath, parsed, options) {
204
232
  });
205
233
  const result = await runPromise;
206
234
  await signalSnapshotWrite;
235
+ if (options.brokenPipe.closed) {
236
+ return 0;
237
+ }
207
238
  if (parsed.snapshotPath !== undefined) {
208
239
  await writeSnapshot(parsed.snapshotPath, await dump(result), options);
209
240
  }
@@ -212,18 +243,24 @@ async function runScriptFile(filepath, parsed, options) {
212
243
  filename: filepath,
213
244
  source: executableSource
214
245
  })}\n`);
246
+ if (options.brokenPipe.closed) {
247
+ return 0;
248
+ }
215
249
  return interrupted ? EXIT_SIGINT : exitCodeForError(result.error);
216
250
  }
217
251
  options.stdout.write(`${JSON.stringify({ ok: true, returnValue: result.returnValue })}\n`);
218
- return interrupted ? EXIT_SIGINT : 0;
252
+ return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : 0;
219
253
  }
220
254
  catch (error) {
221
255
  await signalSnapshotWrite;
256
+ if (options.brokenPipe.closed) {
257
+ return 0;
258
+ }
222
259
  options.stderr.write(`${formatInterpreterError(error, {
223
260
  filename: filepath,
224
261
  source: executableSource
225
262
  })}\n`);
226
- return interrupted ? EXIT_SIGINT : exitCodeForError(error);
263
+ return options.brokenPipe.closed ? 0 : interrupted ? EXIT_SIGINT : exitCodeForError(error);
227
264
  }
228
265
  finally {
229
266
  options.process.off("SIGINT", onSigint);
@@ -367,10 +404,7 @@ function createConsoleSink(stdout, stderr) {
367
404
  };
368
405
  }
369
406
  function formatConsoleArgs(args) {
370
- return args.map(formatConsoleArg).join(" ");
371
- }
372
- function formatConsoleArg(value) {
373
- return typeof value === "string" ? value : inspect(value, { colors: false, depth: 4 });
407
+ return formatWithOptions({ colors: false, depth: 4 }, ...args);
374
408
  }
375
409
  function createUsage() {
376
410
  return [
@@ -1,6 +1,5 @@
1
- type CliStream = {
2
- write(chunk: string): void;
3
- };
1
+ import { type OutputStream } from "./output-stream.js";
2
+ type CliStream = OutputStream;
4
3
  export type ReadMarkdownFile = (filepath: string, encoding: "utf8") => Promise<string>;
5
4
  export type WriteMarkdownFile = (filepath: string, source: string, options: {
6
5
  encoding: "utf8";
@@ -9,6 +9,7 @@ import { makeFailModule } from "./modules/fail.js";
9
9
  import { makeHarnessModule } from "./modules/harness.js";
10
10
  import { makeLogModule } from "./modules/log.js";
11
11
  import { makeMetricModule } from "./modules/metric.js";
12
+ import { createBrokenPipeState, createSafeOutputStream, withBrokenPipeGuard } from "./output-stream.js";
12
13
  import { parseModule } from "./parse/parser.js";
13
14
  import { run } from "./run.js";
14
15
  async function main(argv) {
@@ -20,68 +21,80 @@ async function main(argv) {
20
21
  return await runExampleFile(filepath, { fix });
21
22
  }
22
23
  export async function runExampleFile(filepath, options = {}) {
23
- const stdout = options.stdout ?? process.stdout;
24
- const stderr = options.stderr ?? process.stderr;
24
+ const brokenPipe = createBrokenPipeState();
25
+ const stdout = createSafeOutputStream(options.stdout ?? process.stdout, brokenPipe);
26
+ const stderr = createSafeOutputStream(options.stderr ?? process.stderr, brokenPipe);
25
27
  const readMarkdownFile = options.readFile ?? readFile;
26
28
  const writeMarkdownFile = options.writeFile ?? writeFile;
27
- try {
28
- const rawSource = await readMarkdownFile(filepath, "utf8");
29
- const loaded = loadExecutableSource(rawSource);
30
- const { frontmatter, hasScriptBlock } = loaded;
31
- let executableSource = loaded.executableSource;
32
- const meta = {
33
- filepath,
34
- kind: frontmatter.kind,
35
- version: frontmatter.version
36
- };
37
- const runtime = createExampleRuntime(frontmatter, meta, stdout);
38
- if (!hasScriptBlock) {
39
- const returnValue = await runDemoFallback(frontmatter, runtime);
40
- stdout.write(`${JSON.stringify({ ok: true, returnValue })}\n`);
29
+ return withBrokenPipeGuard([options.stdout ?? process.stdout, options.stderr ?? process.stderr], brokenPipe, async () => {
30
+ try {
31
+ const rawSource = await readMarkdownFile(filepath, "utf8");
32
+ const loaded = loadExecutableSource(rawSource);
33
+ const { frontmatter, hasScriptBlock } = loaded;
34
+ let executableSource = loaded.executableSource;
35
+ const meta = {
36
+ filepath,
37
+ kind: frontmatter.kind,
38
+ version: frontmatter.version
39
+ };
40
+ const runtime = createExampleRuntime(frontmatter, meta, stdout);
41
+ if (!hasScriptBlock) {
42
+ const returnValue = await runDemoFallback(frontmatter, runtime);
43
+ stdout.write(`${JSON.stringify({ ok: true, returnValue })}\n`);
44
+ return 0;
45
+ }
46
+ const lintOptions = {
47
+ allowedExportNames: ["schema"],
48
+ filename: filepath,
49
+ modules: createLintModulesFromRuntimeRegistry(runtime.registry)
50
+ };
51
+ const lintResult = options.fix
52
+ ? lint(executableSource, { ...lintOptions, fix: true })
53
+ : lint(executableSource, lintOptions);
54
+ const diagnostics = Array.isArray(lintResult) ? lintResult : lintResult.diagnostics;
55
+ if (!Array.isArray(lintResult)) {
56
+ executableSource = lintResult.fixed;
57
+ if (lintResult.fixed !== loaded.executableSource) {
58
+ await writeMarkdownFile(filepath, replaceExecutableSource(rawSource, loaded, lintResult.fixed), {
59
+ encoding: "utf8"
60
+ });
61
+ }
62
+ }
63
+ const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
64
+ if (lintErrors.length > 0) {
65
+ stderr.write(`Lint failed:\n${formatDiagnostics(lintErrors)}\n`);
66
+ return brokenPipe.closed ? 0 : 1;
67
+ }
68
+ const lintWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
69
+ if (lintWarnings.length > 0) {
70
+ stderr.write(`Lint warnings:\n${formatDiagnostics(lintWarnings)}\n`);
71
+ if (brokenPipe.closed) {
72
+ return 0;
73
+ }
74
+ }
75
+ const result = await run(executableSource, {
76
+ entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : undefined,
77
+ filename: filepath,
78
+ modules: runtime.registry
79
+ });
80
+ if (brokenPipe.closed) {
81
+ return 0;
82
+ }
83
+ if (!result.ok) {
84
+ stderr.write(`${readErrorMessage(result.error)}\n`);
85
+ return brokenPipe.closed ? 0 : 1;
86
+ }
87
+ stdout.write(`${JSON.stringify({ ok: true, returnValue: result.returnValue })}\n`);
41
88
  return 0;
42
89
  }
43
- const lintOptions = {
44
- allowedExportNames: ["schema"],
45
- filename: filepath,
46
- modules: createLintModulesFromRuntimeRegistry(runtime.registry)
47
- };
48
- const lintResult = options.fix
49
- ? lint(executableSource, { ...lintOptions, fix: true })
50
- : lint(executableSource, lintOptions);
51
- const diagnostics = Array.isArray(lintResult) ? lintResult : lintResult.diagnostics;
52
- if (!Array.isArray(lintResult)) {
53
- executableSource = lintResult.fixed;
54
- if (lintResult.fixed !== loaded.executableSource) {
55
- await writeMarkdownFile(filepath, replaceExecutableSource(rawSource, loaded, lintResult.fixed), {
56
- encoding: "utf8"
57
- });
90
+ catch (error) {
91
+ if (brokenPipe.closed) {
92
+ return 0;
58
93
  }
94
+ stderr.write(`${readErrorMessage(error)}\n`);
95
+ return brokenPipe.closed ? 0 : 1;
59
96
  }
60
- const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
61
- if (lintErrors.length > 0) {
62
- stderr.write(`Lint failed:\n${formatDiagnostics(lintErrors)}\n`);
63
- return 1;
64
- }
65
- const lintWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
66
- if (lintWarnings.length > 0) {
67
- stderr.write(`Lint warnings:\n${formatDiagnostics(lintWarnings)}\n`);
68
- }
69
- const result = await run(executableSource, {
70
- entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : undefined,
71
- filename: filepath,
72
- modules: runtime.registry
73
- });
74
- if (!result.ok) {
75
- stderr.write(`${readErrorMessage(result.error)}\n`);
76
- return 1;
77
- }
78
- stdout.write(`${JSON.stringify({ ok: true, returnValue: result.returnValue })}\n`);
79
- return 0;
80
- }
81
- catch (error) {
82
- stderr.write(`${readErrorMessage(error)}\n`);
83
- return 1;
84
- }
97
+ });
85
98
  }
86
99
  function loadExecutableSource(source) {
87
100
  const { frontmatter, body } = splitFrontmatter(source);
@@ -315,7 +315,7 @@ function getPatternBindingNames(pattern) {
315
315
  async function bindPattern(pattern, value, context, evaluateNode) {
316
316
  switch (pattern.type) {
317
317
  case "Identifier":
318
- context.scope.declare(pattern.name, "const", value);
318
+ context.scope.declare(pattern.name, "let", value);
319
319
  return { ok: true };
320
320
  case "MemberExpression":
321
321
  throw new TypeError("Catch bindings do not support member expressions.");
@@ -2,7 +2,7 @@ import type { Budget } from "../budget.js";
2
2
  import { type SandboxClosure, type SandboxObject } from "../values.js";
3
3
  export type ObjectArrayGlobals = {
4
4
  Object: SandboxObject;
5
- Array: SandboxObject;
5
+ Array: SandboxClosure;
6
6
  String: SandboxClosure;
7
7
  Number: SandboxClosure;
8
8
  Boolean: SandboxClosure;
@@ -45,20 +45,25 @@ export function createObjectArrayGlobals(options) {
45
45
  name: "assign"
46
46
  })
47
47
  },
48
- Array: {
49
- isArray: createSandboxClosure({
50
- call: ([value]) => Array.isArray(value),
51
- name: "isArray"
52
- }),
53
- from: createSandboxClosure({
54
- call: (args) => arrayFromSandboxValues(args, options.budget),
55
- name: "from"
56
- }),
57
- of: createSandboxClosure({
58
- call: (args) => budgetSandboxValue(Reflect.apply(Array.of, Array, [...args]), options.budget),
59
- name: "of"
60
- })
61
- },
48
+ Array: createSandboxClosure({
49
+ call: (args) => createArrayFromConstructorArgs(args, options.budget),
50
+ construct: (args) => createArrayFromConstructorArgs(args, options.budget),
51
+ name: "Array",
52
+ properties: {
53
+ isArray: createSandboxClosure({
54
+ call: ([value]) => Array.isArray(value),
55
+ name: "isArray"
56
+ }),
57
+ from: createSandboxClosure({
58
+ call: (args) => arrayFromSandboxValues(args, options.budget),
59
+ name: "from"
60
+ }),
61
+ of: createSandboxClosure({
62
+ call: (args) => budgetSandboxValue(Reflect.apply(Array.of, Array, [...args]), options.budget),
63
+ name: "of"
64
+ })
65
+ }
66
+ }),
62
67
  String: createSandboxClosure({
63
68
  call: ([value]) => options.budget.allocateString(String(value)),
64
69
  name: "String",
@@ -152,13 +157,13 @@ function isAssignableSandboxTarget(value) {
152
157
  async function arrayFromSandboxValues(args, budget) {
153
158
  const [items, mapFn] = args;
154
159
  const iterator = getSandboxIterator(items);
155
- if (iterator?.generator !== true && (mapFn === undefined || !isSandboxClosure(mapFn))) {
156
- return budgetSandboxValue(Reflect.apply(Array.from, Array, [...args]), budget);
157
- }
158
- const values = iterator?.generator === true
159
- ? await collectIteratorValues(iterator)
160
- : Reflect.apply(Array.from, Array, [items]);
160
+ const values = iterator === undefined
161
+ ? Reflect.apply(Array.from, Array, [items])
162
+ : await collectIteratorValues(iterator);
161
163
  if (mapFn === undefined || !isSandboxClosure(mapFn)) {
164
+ if (mapFn !== undefined) {
165
+ throw new TypeError("Array.from mapping callback must be a function.");
166
+ }
162
167
  return budgetSandboxValue(values, budget);
163
168
  }
164
169
  const mappedValues = [];
@@ -168,6 +173,20 @@ async function arrayFromSandboxValues(args, budget) {
168
173
  }
169
174
  return budgetSandboxValue(mappedValues, budget);
170
175
  }
176
+ function createArrayFromConstructorArgs(args, budget) {
177
+ if (args.length !== 1) {
178
+ return budgetSandboxValue(Reflect.apply(Array, Array, [...args]), budget);
179
+ }
180
+ const [lengthOrValue] = args;
181
+ if (typeof lengthOrValue !== "number") {
182
+ return budgetSandboxValue([lengthOrValue], budget);
183
+ }
184
+ if (!Number.isInteger(lengthOrValue) || lengthOrValue < 0 || lengthOrValue > 0xffffffff) {
185
+ throw new RangeError("Invalid array length.");
186
+ }
187
+ budget.allocateArrayLength(lengthOrValue);
188
+ return new Array(lengthOrValue);
189
+ }
171
190
  async function collectIteratorValues(iterator) {
172
191
  const values = [];
173
192
  while (true) {
@@ -475,7 +475,7 @@ function readStringProperty(value, key) {
475
475
  }
476
476
  function copyFunctionProperties(callable, stackFrames, options, state, path) {
477
477
  const properties = {};
478
- for (const key of Object.keys(callable)) {
478
+ for (const key of Object.getOwnPropertyNames(callable)) {
479
479
  const descriptor = Object.getOwnPropertyDescriptor(callable, key);
480
480
  if (descriptor === undefined) {
481
481
  continue;