firedrill-run 3.0.2 → 3.0.3

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,7 +1,7 @@
1
1
  {
2
2
  "schema": "firedrill.cli-assembly.v1",
3
3
  "package": "firedrill-run",
4
- "version": "3.0.2",
4
+ "version": "3.0.3",
5
5
  "entry": "cli/src/main.js",
6
6
  "helpers": [
7
7
  "cli/runtime/source_snapshot.py",
@@ -2,13 +2,88 @@ import { constants } from 'node:fs';
2
2
  import { lstat, open } from 'node:fs/promises';
3
3
  import { resolve } from 'node:path';
4
4
  import { CliError } from './http.js';
5
+ /** Consume a pipe once without leaving an abort waiting for another byte. */
6
+ async function stdinInput(signal, maximumBytes) {
7
+ signal.throwIfAborted();
8
+ const input = process.stdin;
9
+ if (input.isTTY || input.readableEncoding !== null) {
10
+ throw new CliError('request_stdin_invalid', 'Pipe one UTF-8 JSON object into --file -. Nothing was submitted.', 2);
11
+ }
12
+ const chunks = await new Promise((accept, reject) => {
13
+ const collected = [];
14
+ let size = 0;
15
+ let settled = false;
16
+ const invalid = () => new CliError('request_stdin_invalid', 'Standard input must contain one JSON object within transport capacity. Nothing was submitted.', 2);
17
+ const finish = (error) => {
18
+ if (settled)
19
+ return;
20
+ settled = true;
21
+ input.pause();
22
+ input.removeListener('data', data);
23
+ input.removeListener('end', end);
24
+ input.removeListener('error', failed);
25
+ input.removeListener('close', closed);
26
+ signal.removeEventListener('abort', aborted);
27
+ if (error !== undefined)
28
+ reject(error);
29
+ else
30
+ accept(collected);
31
+ };
32
+ const data = (chunk) => {
33
+ if (!Buffer.isBuffer(chunk)) {
34
+ finish(invalid());
35
+ return;
36
+ }
37
+ size += chunk.length;
38
+ if (size > maximumBytes) {
39
+ finish(invalid());
40
+ return;
41
+ }
42
+ collected.push(Buffer.from(chunk));
43
+ };
44
+ const end = () => finish();
45
+ const failed = () => finish(invalid());
46
+ const closed = () => { if (!input.readableEnded)
47
+ finish(invalid()); };
48
+ const aborted = () => finish(signal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
49
+ input.on('data', data);
50
+ input.once('end', end);
51
+ input.once('error', failed);
52
+ input.once('close', closed);
53
+ signal.addEventListener('abort', aborted, { once: true });
54
+ // EOF or cancellation can precede listener installation. Never wait for an
55
+ // event that has already happened, including an idle pipe on cancellation.
56
+ if (signal.aborted)
57
+ aborted();
58
+ else if (input.readableEnded)
59
+ end();
60
+ else if (input.destroyed)
61
+ failed();
62
+ else
63
+ input.resume();
64
+ });
65
+ signal.throwIfAborted();
66
+ try {
67
+ const raw = new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks));
68
+ const value = JSON.parse(raw);
69
+ if (!value || typeof value !== 'object' || Array.isArray(value))
70
+ throw new Error();
71
+ return raw;
72
+ }
73
+ catch {
74
+ throw new CliError('request_stdin_invalid', 'Standard input is not a UTF-8 JSON object. Nothing was submitted.', 2);
75
+ }
76
+ }
5
77
  /** Read one bounded, unchanged JSON object without rewriting number literals. */
6
78
  export async function requestInput(path, signal, maximumBytes = 8 * 1024 * 1024) {
79
+ signal.throwIfAborted();
7
80
  if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 2 || maximumBytes > 8 * 1024 * 1024) {
8
81
  throw new CliError('request_capacity_invalid', 'Choose a supported request capacity.', 2);
9
82
  }
10
83
  if (!path)
11
84
  return '{}';
85
+ if (path === '-')
86
+ return stdinInput(signal, maximumBytes);
12
87
  const before = await lstat(resolve(path));
13
88
  if (!before.isFile() || before.isSymbolicLink() || before.size > maximumBytes) {
14
89
  throw new CliError('request_file_invalid', 'Choose a regular JSON file within transport capacity.', 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firedrill-run",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Local AI agent simulations with cloud-hosted worlds and tools",
5
5
  "type": "module",
6
6
  "bin": {