@smthrs/std 1.0.0-rc.7 → 1.0.0-rc.9

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 (84) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +40 -36
  3. package/dist/cjs/Checkpoints.d.ts +37 -0
  4. package/dist/cjs/Checkpoints.d.ts.map +1 -1
  5. package/dist/cjs/Checkpoints.js +75 -0
  6. package/dist/cjs/Checkpoints.js.map +2 -2
  7. package/dist/cjs/PortableSearch.d.ts +13 -1
  8. package/dist/cjs/PortableSearch.d.ts.map +1 -1
  9. package/dist/cjs/PortableSearch.js +8 -1
  10. package/dist/cjs/PortableSearch.js.map +2 -2
  11. package/dist/cjs/RemoteSearch.d.ts +54 -0
  12. package/dist/cjs/RemoteSearch.d.ts.map +1 -0
  13. package/dist/cjs/RemoteSearch.js +134 -0
  14. package/dist/cjs/RemoteSearch.js.map +7 -0
  15. package/dist/cjs/index.d.ts +2 -0
  16. package/dist/cjs/index.d.ts.map +1 -1
  17. package/dist/cjs/index.js +2 -0
  18. package/dist/cjs/index.js.map +2 -2
  19. package/dist/cjs/internal/EnvelopePrecheck.d.ts +2 -0
  20. package/dist/cjs/internal/EnvelopePrecheck.d.ts.map +1 -1
  21. package/dist/cjs/internal/EnvelopePrecheck.js +235 -36
  22. package/dist/cjs/internal/EnvelopePrecheck.js.map +3 -3
  23. package/dist/cjs/internal/Exec.d.ts +12 -7
  24. package/dist/cjs/internal/Exec.d.ts.map +1 -1
  25. package/dist/cjs/internal/Exec.js +8 -4
  26. package/dist/cjs/internal/Exec.js.map +2 -2
  27. package/dist/cjs/internal/RemoteTree.d.ts +60 -0
  28. package/dist/cjs/internal/RemoteTree.d.ts.map +1 -0
  29. package/dist/cjs/internal/RemoteTree.js +123 -0
  30. package/dist/cjs/internal/RemoteTree.js.map +7 -0
  31. package/dist/cjs/internal/Tar.d.ts +23 -0
  32. package/dist/cjs/internal/Tar.d.ts.map +1 -0
  33. package/dist/cjs/internal/Tar.js +86 -0
  34. package/dist/cjs/internal/Tar.js.map +7 -0
  35. package/dist/esm/Checkpoints.d.ts +37 -0
  36. package/dist/esm/Checkpoints.d.ts.map +1 -1
  37. package/dist/esm/Checkpoints.js +85 -0
  38. package/dist/esm/Checkpoints.js.map +1 -1
  39. package/dist/esm/PortableSearch.d.ts +13 -1
  40. package/dist/esm/PortableSearch.d.ts.map +1 -1
  41. package/dist/esm/PortableSearch.js +16 -0
  42. package/dist/esm/PortableSearch.js.map +1 -1
  43. package/dist/esm/RemoteSearch.d.ts +54 -0
  44. package/dist/esm/RemoteSearch.d.ts.map +1 -0
  45. package/dist/esm/RemoteSearch.js +162 -0
  46. package/dist/esm/RemoteSearch.js.map +1 -0
  47. package/dist/esm/index.d.ts +2 -0
  48. package/dist/esm/index.d.ts.map +1 -1
  49. package/dist/esm/index.js +2 -0
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/internal/EnvelopePrecheck.d.ts +2 -0
  52. package/dist/esm/internal/EnvelopePrecheck.d.ts.map +1 -1
  53. package/dist/esm/internal/EnvelopePrecheck.js +321 -62
  54. package/dist/esm/internal/EnvelopePrecheck.js.map +1 -1
  55. package/dist/esm/internal/Exec.d.ts +12 -7
  56. package/dist/esm/internal/Exec.d.ts.map +1 -1
  57. package/dist/esm/internal/Exec.js +30 -9
  58. package/dist/esm/internal/Exec.js.map +1 -1
  59. package/dist/esm/internal/RemoteTree.d.ts +60 -0
  60. package/dist/esm/internal/RemoteTree.d.ts.map +1 -0
  61. package/dist/esm/internal/RemoteTree.js +132 -0
  62. package/dist/esm/internal/RemoteTree.js.map +1 -0
  63. package/dist/esm/internal/Tar.d.ts +23 -0
  64. package/dist/esm/internal/Tar.d.ts.map +1 -0
  65. package/dist/esm/internal/Tar.js +97 -0
  66. package/dist/esm/internal/Tar.js.map +1 -0
  67. package/docs/README.md +3 -1
  68. package/docs/api.md +48 -21
  69. package/docs/concepts/effects-and-capabilities.md +3 -1
  70. package/docs/guides/bind-the-standard-flows.md +3 -1
  71. package/docs/guides/pin-a-checkpoint.md +20 -0
  72. package/docs/guides/run-a-shell-command.md +4 -1
  73. package/docs/installation.md +13 -9
  74. package/docs/quickstart.md +1 -1
  75. package/docs/troubleshooting.md +20 -0
  76. package/package.json +16 -6
  77. package/src/Checkpoints.ts +137 -0
  78. package/src/PortableSearch.ts +20 -0
  79. package/src/RemoteSearch.ts +199 -0
  80. package/src/index.ts +3 -0
  81. package/src/internal/EnvelopePrecheck.ts +320 -64
  82. package/src/internal/Exec.ts +32 -8
  83. package/src/internal/RemoteTree.ts +159 -0
  84. package/src/internal/Tar.ts +95 -0
@@ -28,13 +28,15 @@ import { elideMiddle } from "./Text.js";
28
28
  * @category models
29
29
  * @since 1.0.0
30
30
  */
31
- export const ExecErrorCode = Schema.Literals(["timeout", "spawn_error", "capture_overflow"]);
31
+ export const ExecErrorCode = Schema.Literals(["timeout", "spawn_error", "arguments_too_long", "capture_overflow"]);
32
32
  /**
33
33
  * A command that never produced an exit code.
34
34
  *
35
35
  * A non-zero exit is a successful {@link ExecResult}, not a failure; this
36
- * error is reserved for a command that could not start or was cut off by its
37
- * timeout, or whose captured output exceeded a refusing capture bound.
36
+ * error is reserved for a command that could not start (`arguments_too_long`
37
+ * when the operating system refused it for the size of its arguments) or was
38
+ * cut off by its timeout, or whose captured output exceeded a refusing capture
39
+ * bound.
38
40
  *
39
41
  * @category errors
40
42
  * @since 1.0.0
@@ -160,6 +162,20 @@ const capture = (stream, maxBytes, overflow) => maxBytes === undefined
160
162
  : overflow === "ends"
161
163
  ? boundedEnds(stream, maxBytes)
162
164
  : boundedTail(stream, maxBytes, overflow);
165
+ /**
166
+ * A command whose arguments exceed the operating system's limit fails `execve`
167
+ * with `E2BIG` before it starts: one argument over 128 KiB on Linux, all of
168
+ * them over 1 MiB on macOS. A heredoc in a command line, or a file's text
169
+ * passed as an argument, is one such argument. The platform error's own text
170
+ * repeats the whole command line, so this text is written here instead, and a
171
+ * caller may show it as it stands.
172
+ */
173
+ const argumentsTooLong = (error) => error.cause?.code === "E2BIG"
174
+ ? new ExecError({
175
+ code: "arguments_too_long",
176
+ message: "Command not started: its arguments exceed the operating system's size limit (E2BIG). Send long text on standard input or in a file, not in the command line or its arguments."
177
+ })
178
+ : undefined;
163
179
  const unbounded = (command, options) => Effect.gen(function* () {
164
180
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
165
181
  const settings = {
@@ -202,7 +218,7 @@ const unbounded = (command, options) => Effect.gen(function* () {
202
218
  stdoutDroppedBytes: stdout.droppedBytes,
203
219
  stderrDroppedBytes: stderr.droppedBytes
204
220
  };
205
- }).pipe(Effect.scoped, Effect.mapError((error) => error instanceof ExecError ? error : new ExecError({
221
+ }).pipe(Effect.scoped, Effect.mapError((error) => error instanceof ExecError ? error : argumentsTooLong(error) ?? new ExecError({
206
222
  code: "spawn_error",
207
223
  message: `exec: ${command}: ${error.message}`
208
224
  })));
@@ -232,18 +248,21 @@ export const exec = (command, options = {}) => options.timeoutMs === undefined
232
248
  *
233
249
  * A timeout keeps its own code, because "the command never finished" and "the
234
250
  * command never started" are different things to a caller deciding whether to
235
- * retry. Every other way of producing no exit code is a host failure.
251
+ * retry. Arguments the operating system refused for their size are the
252
+ * caller's own input, which a shell flow publishes, so they read as
253
+ * `invalid_input`. Every other way of producing no exit code is a host failure.
236
254
  *
237
255
  * @category errors
238
256
  * @since 1.0.0
239
257
  */
240
- export const toStdErrorCode = (error) => error.code === "timeout" ? "timeout" : "command_failed";
258
+ export const toStdErrorCode = (error) => error.code === "timeout" ? "timeout" : error.code === "arguments_too_long" ? "invalid_input" : "command_failed";
241
259
  /**
242
260
  * The model-facing failure one execution error becomes.
243
261
  *
244
262
  * A timeout names the command, which is what the caller asked for and all a
245
- * timeout has to say. Any other failure carries the message the execution
246
- * produced, which already names the command.
263
+ * timeout has to say. Arguments too long to start carry the reason and the
264
+ * fix, and nothing of the command. Any other failure carries the message the
265
+ * execution produced, which already names the command.
247
266
  *
248
267
  * @category errors
249
268
  * @since 1.0.0
@@ -252,7 +271,9 @@ export const toStdError = (command, error) => new StdError.StdError({
252
271
  code: toStdErrorCode(error),
253
272
  message: error.code === "timeout"
254
273
  ? `Command timed out: ${command}`
255
- : `Command failed to start: ${error.message}`,
274
+ : error.code === "arguments_too_long"
275
+ ? error.message
276
+ : `Command failed to start: ${error.message}`,
256
277
  ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })
257
278
  });
258
279
  //# sourceMappingURL=Exec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Exec.js","sourceRoot":"","sources":["../../../src/internal/Exec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Buffered shell execution over the permission-aware process spawner.\n *\n * `@smthrs/kernel` used to expose a `Shell` service whose `exec` returned a\n * buffered `{ stdout, stderr, exitCode }`. The kernel replaced it with\n * `ChildProcessSpawner`, Effect's own process boundary, which streams and has\n * no buffered form. This module is the buffered form the shell flows still\n * want, expressed in terms of the spawner so the `proc:spawn` capability check\n * and the command-line resource stay exactly where the kernel put them.\n *\n * Commands run through the platform shell (`shell: true`), matching the old\n * service: the flows take a command *line*, not an argv.\n *\n * @since 1.0.0\n */\n\nimport * as ChildProcessEnvironment from \"@smthrs/kernel/ChildProcessEnvironment\"\nimport * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Effect from \"effect/Effect\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as StdError from \"../StdError.ts\"\nimport { elideMiddle } from \"./Text.ts\"\n\n/**\n * Failure codes a buffered execution can report, matching the codes the\n * removed kernel `Shell` used so handler-level mapping is unchanged.\n *\n * @category models\n * @since 1.0.0\n */\nexport const ExecErrorCode = Schema.Literals([\"timeout\", \"spawn_error\", \"capture_overflow\"])\n\n/**\n * Failure codes a buffered execution can report.\n *\n * @category models\n * @since 1.0.0\n */\nexport type ExecErrorCode = typeof ExecErrorCode.Type\n\n/**\n * A command that never produced an exit code.\n *\n * A non-zero exit is a successful {@link ExecResult}, not a failure; this\n * error is reserved for a command that could not start or was cut off by its\n * timeout, or whose captured output exceeded a refusing capture bound.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class ExecError extends Schema.TaggedError<ExecError>()(\"@smthrs/std/ExecError\", {\n code: ExecErrorCode,\n message: Schema.String,\n /** The limit a `timeout` ran past, in milliseconds. */\n limitMillis: Schema.optional(Schema.Number)\n}) {}\n\n/**\n * The buffered result of one command.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecResult {\n readonly stdout: string\n readonly stderr: string\n readonly exitCode: number\n /** UTF-8 bytes of stdout dropped to stay inside the capture bound; see `overflow`. */\n readonly stdoutDroppedBytes: number\n /** UTF-8 bytes of stderr dropped to stay inside the capture bound; see `overflow`. */\n readonly stderrDroppedBytes: number\n}\n\n/**\n * Options accepted by {@link exec}.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecOptions {\n readonly cwd?: string | undefined\n readonly env?: Readonly<Record<string, string>> | undefined\n readonly timeoutMs?: number | undefined\n /**\n * The program's arguments. Present means an argv, absent means a command\n * line for the platform shell. An argv is how a payload reaches a program\n * without being quoted into a string: nothing between here and `execve`\n * re-parses it.\n */\n readonly args?: ReadonlyArray<string> | undefined\n /**\n * Text written to the program's standard input, as data. A script delivered\n * this way is never quoted, escaped, or heredoc-terminated, which is the\n * whole point: quoting corruption cost the measured SWE-bench program twelve\n * probe failures and one instance's most expensive frame.\n */\n readonly stdin?: string | undefined\n /**\n * Bytes retained per captured stream. A stream that prints more keeps what\n * `overflow` names and reports what it dropped, so a command printing\n * gigabytes costs a bounded amount of memory rather than the whole of what it\n * printed.\n *\n * Absent means unbounded, which is what a caller wants when it needs every\n * byte a tool produced (`rg --files`, `git worktree list`).\n */\n readonly maxCaptureBytes?: number | undefined\n /**\n * What a stream past the bound keeps. `tail`, the default, keeps its last\n * bytes. `ends` keeps its first and last halves and puts one line naming the\n * dropped bytes where the middle was. `refuse` fails if either stream exceeds\n * the bound.\n */\n readonly overflow?: Overflow | undefined\n}\n\ntype Overflow = \"tail\" | \"ends\" | \"refuse\"\n\n/** One captured stream: the text kept, and the bytes dropped to keep it bounded. */\ninterface Capture {\n readonly text: string\n readonly droppedBytes: number\n}\n\ninterface Tail {\n chunks: Array<Uint8Array>\n bytes: number\n dropped: number\n}\n\n/** A stream's first bytes and its last ones: what `overflow: \"ends\"` keeps. */\ninterface Ends {\n readonly head: Array<Uint8Array>\n headBytes: number\n readonly tail: Tail\n}\n\nconst decoder = new TextDecoder(\"utf-8\")\n\nconst emptyTail = (): Tail => ({ chunks: [], bytes: 0, dropped: 0 })\n\n/** A UTF-8 continuation byte is `10xxxxxx`; every other byte starts a character. */\nconst isContinuation = (byte: number): boolean => (byte & 0b1100_0000) === 0b1000_0000\n\nconst join = (chunks: ReadonlyArray<Uint8Array>, size: number): Uint8Array => {\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\n/** Appends one chunk and drops from the front whatever no longer fits. */\nconst keepTail = (state: Tail, chunk: Uint8Array, maxBytes: number): void => {\n if (chunk.byteLength === 0) return\n state.chunks.push(chunk)\n state.bytes += chunk.byteLength\n while (state.bytes > maxBytes && state.chunks.length > 0) {\n const excess = state.bytes - maxBytes\n const first = state.chunks[0]!\n if (first.byteLength <= excess) {\n state.chunks.shift()\n state.bytes -= first.byteLength\n state.dropped += first.byteLength\n } else {\n state.chunks[0] = first.subarray(excess)\n state.bytes -= excess\n state.dropped += excess\n }\n }\n}\n\n/**\n * Joins the retained chunks and decodes them, skipping the partial code point a\n * dropped head can leave behind.\n *\n * A code point is at most four bytes, so at most three leading continuation\n * bytes can precede the first whole character. Skipping them is counted as\n * dropped rather than decoded to a replacement character the process never\n * printed.\n */\nconst decodeTail = (state: Tail): Capture => {\n const bytes = join(state.chunks, state.bytes)\n if (state.dropped === 0) return { text: decoder.decode(bytes), droppedBytes: 0 }\n let start = 0\n while (start < bytes.byteLength && start < 3 && isContinuation(bytes[start]!)) start++\n return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start }\n}\n\n/**\n * The length of a cut head without the character the cut left unfinished.\n *\n * Its last character starts at the last byte that is not a continuation byte,\n * and that byte says how many bytes the character needs. One that needs more\n * than the head holds is dropped, for the same reason {@link decodeTail} skips\n * a partial code point.\n */\nconst wholeLength = (bytes: Uint8Array): number => {\n for (let back = 1; back <= Math.min(3, bytes.byteLength); back++) {\n const byte = bytes[bytes.byteLength - back]!\n if (isContinuation(byte)) continue\n const width = byte >= 0b1111_0000 ? 4 : byte >= 0b1110_0000 ? 3 : byte >= 0b1100_0000 ? 2 : 1\n return width > back ? bytes.byteLength - back : bytes.byteLength\n }\n return bytes.byteLength\n}\n\n/**\n * Decodes a stream kept by its ends. A stream that fit is decoded whole;\n * otherwise each end is cut on a character boundary and one line naming every\n * byte between them stands where the middle was.\n */\nconst decodeEnds = (state: Ends): Capture => {\n if (state.tail.dropped === 0) {\n const whole = join([...state.head, ...state.tail.chunks], state.headBytes + state.tail.bytes)\n return { text: decoder.decode(whole), droppedBytes: 0 }\n }\n const head = join(state.head, state.headBytes)\n const kept = wholeLength(head)\n const tail = decodeTail(state.tail)\n const droppedBytes = head.byteLength - kept + tail.droppedBytes\n return { text: elideMiddle(decoder.decode(head.subarray(0, kept)), droppedBytes, tail.text), droppedBytes }\n}\n\nconst boundedTail = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n Stream.runFold(stream, emptyTail, (state, chunk) => {\n keepTail(state, chunk, maxBytes)\n return state\n }).pipe(Effect.map((state) =>\n overflow === \"refuse\" && state.dropped > 0\n ? { text: \"\", droppedBytes: state.dropped }\n : decodeTail(state)\n ))\n\n/**\n * Keeps the first half of the bound and the last half. The head fills first,\n * and every byte after it passes through the tail, which drops from its front:\n * what falls out of the tail is the middle.\n */\nconst boundedEnds = <E>(stream: Stream.Stream<Uint8Array, E>, maxBytes: number): Effect.Effect<Capture, E> => {\n const headBytes = Math.floor(maxBytes / 2)\n return Stream.runFold(stream, (): Ends => ({ head: [], headBytes: 0, tail: emptyTail() }), (state, chunk) => {\n const taken = Math.min(headBytes - state.headBytes, chunk.byteLength)\n if (taken > 0) {\n state.head.push(chunk.subarray(0, taken))\n state.headBytes += taken\n }\n keepTail(state.tail, chunk.subarray(taken), maxBytes - headBytes)\n return state\n }).pipe(Effect.map(decodeEnds))\n}\n\nconst capture = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number | undefined,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n maxBytes === undefined\n ? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))\n : overflow === \"ends\"\n ? boundedEnds(stream, maxBytes)\n : boundedTail(stream, maxBytes, overflow)\n\nconst unbounded = (\n command: string,\n options: ExecOptions\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n const settings: ChildProcess.CommandOptions = {\n ...(options.args === undefined ? { shell: true } : {}),\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n env: ChildProcessEnvironment.make(process.env, options.env),\n extendEnv: false,\n ...(options.stdin === undefined\n ? {}\n : { stdin: Stream.make(new TextEncoder().encode(options.stdin)) })\n }\n const handle = yield* spawner.spawn(\n options.args === undefined\n ? ChildProcess.make(command, settings)\n : ChildProcess.make(command, [...options.args], settings)\n )\n // stdout, stderr, and the exit code have to be consumed concurrently: a\n // command that fills a pipe blocks until the pipe is drained, so waiting\n // for exit first would deadlock on any output larger than the buffer.\n const [stdout, stderr, exitCode] = yield* Effect.all(\n [\n capture(handle.stdout, options.maxCaptureBytes, options.overflow),\n capture(handle.stderr, options.maxCaptureBytes, options.overflow),\n handle.exitCode\n ],\n { concurrency: \"unbounded\" }\n )\n if (options.overflow === \"refuse\") {\n const overflowed = [\n stdout.droppedBytes > 0 ? \"stdout\" : undefined,\n stderr.droppedBytes > 0 ? \"stderr\" : undefined\n ]\n .filter((stream): stream is string => stream !== undefined)\n if (overflowed.length > 0) {\n return yield* Effect.fail(\n new ExecError({\n code: \"capture_overflow\",\n message: `${command} ${\n overflowed.join(\" and \")\n } exceeded the ${options.maxCaptureBytes}-byte capture cap; refusing partial output`\n })\n )\n }\n }\n return {\n stdout: stdout.text,\n stderr: stderr.text,\n exitCode,\n stdoutDroppedBytes: stdout.droppedBytes,\n stderrDroppedBytes: stderr.droppedBytes\n }\n }).pipe(\n Effect.scoped,\n Effect.mapError((error) =>\n error instanceof ExecError ? error : new ExecError({\n code: \"spawn_error\",\n message: `exec: ${command}: ${error.message}`\n })\n )\n )\n\n/**\n * Runs a command line through the platform shell and buffers its output.\n *\n * Cancellation is scope closure: the spawn is scoped, so an interrupted fiber\n * — including the one a `timeoutMs` interrupts — terminates the process rather\n * than leaking it.\n *\n * @category execution\n * @since 1.0.0\n */\nexport const exec = (\n command: string,\n options: ExecOptions = {}\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n options.timeoutMs === undefined\n ? unbounded(command, options)\n : Effect.timeoutOrElse(unbounded(command, options), {\n duration: options.timeoutMs,\n orElse: () =>\n Effect.fail(\n new ExecError({\n code: \"timeout\",\n message: `exec: \\`${command}\\` exceeded ${options.timeoutMs}ms`,\n limitMillis: options.timeoutMs\n })\n )\n })\n\n/**\n * The model-facing failure code a command that never produced an exit code\n * carries.\n *\n * A timeout keeps its own code, because \"the command never finished\" and \"the\n * command never started\" are different things to a caller deciding whether to\n * retry. Every other way of producing no exit code is a host failure.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdErrorCode = (error: ExecError): StdError.Code =>\n error.code === \"timeout\" ? \"timeout\" : \"command_failed\"\n\n/**\n * The model-facing failure one execution error becomes.\n *\n * A timeout names the command, which is what the caller asked for and all a\n * timeout has to say. Any other failure carries the message the execution\n * produced, which already names the command.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdError = (command: string, error: ExecError): StdError.StdError =>\n new StdError.StdError({\n code: toStdErrorCode(error),\n message: error.code === \"timeout\"\n ? `Command timed out: ${command}`\n : `Command failed to start: ${error.message}`,\n ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })\n })\n"]}
1
+ {"version":3,"file":"Exec.js","sourceRoot":"","sources":["../../../src/internal/Exec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Buffered shell execution over the permission-aware process spawner.\n *\n * `@smthrs/kernel` used to expose a `Shell` service whose `exec` returned a\n * buffered `{ stdout, stderr, exitCode }`. The kernel replaced it with\n * `ChildProcessSpawner`, Effect's own process boundary, which streams and has\n * no buffered form. This module is the buffered form the shell flows still\n * want, expressed in terms of the spawner so the `proc:spawn` capability check\n * and the command-line resource stay exactly where the kernel put them.\n *\n * Commands run through the platform shell (`shell: true`), matching the old\n * service: the flows take a command *line*, not an argv.\n *\n * @since 1.0.0\n */\n\nimport * as ChildProcessEnvironment from \"@smthrs/kernel/ChildProcessEnvironment\"\nimport * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Effect from \"effect/Effect\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as StdError from \"../StdError.ts\"\nimport { elideMiddle } from \"./Text.ts\"\n\n/**\n * Failure codes a buffered execution can report, matching the codes the\n * removed kernel `Shell` used so handler-level mapping is unchanged.\n *\n * @category models\n * @since 1.0.0\n */\nexport const ExecErrorCode = Schema.Literals([\"timeout\", \"spawn_error\", \"arguments_too_long\", \"capture_overflow\"])\n\n/**\n * Failure codes a buffered execution can report.\n *\n * @category models\n * @since 1.0.0\n */\nexport type ExecErrorCode = typeof ExecErrorCode.Type\n\n/**\n * A command that never produced an exit code.\n *\n * A non-zero exit is a successful {@link ExecResult}, not a failure; this\n * error is reserved for a command that could not start (`arguments_too_long`\n * when the operating system refused it for the size of its arguments) or was\n * cut off by its timeout, or whose captured output exceeded a refusing capture\n * bound.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class ExecError extends Schema.TaggedError<ExecError>()(\"@smthrs/std/ExecError\", {\n code: ExecErrorCode,\n message: Schema.String,\n /** The limit a `timeout` ran past, in milliseconds. */\n limitMillis: Schema.optional(Schema.Number)\n}) {}\n\n/**\n * The buffered result of one command.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecResult {\n readonly stdout: string\n readonly stderr: string\n readonly exitCode: number\n /** UTF-8 bytes of stdout dropped to stay inside the capture bound; see `overflow`. */\n readonly stdoutDroppedBytes: number\n /** UTF-8 bytes of stderr dropped to stay inside the capture bound; see `overflow`. */\n readonly stderrDroppedBytes: number\n}\n\n/**\n * Options accepted by {@link exec}.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface ExecOptions {\n readonly cwd?: string | undefined\n readonly env?: Readonly<Record<string, string>> | undefined\n readonly timeoutMs?: number | undefined\n /**\n * The program's arguments. Present means an argv, absent means a command\n * line for the platform shell. An argv is how a payload reaches a program\n * without being quoted into a string: nothing between here and `execve`\n * re-parses it.\n */\n readonly args?: ReadonlyArray<string> | undefined\n /**\n * Text written to the program's standard input, as data. A script delivered\n * this way is never quoted, escaped, or heredoc-terminated, which is the\n * whole point: quoting corruption cost the measured SWE-bench program twelve\n * probe failures and one instance's most expensive frame.\n */\n readonly stdin?: string | undefined\n /**\n * Bytes retained per captured stream. A stream that prints more keeps what\n * `overflow` names and reports what it dropped, so a command printing\n * gigabytes costs a bounded amount of memory rather than the whole of what it\n * printed.\n *\n * Absent means unbounded, which is what a caller wants when it needs every\n * byte a tool produced (`rg --files`, `git worktree list`).\n */\n readonly maxCaptureBytes?: number | undefined\n /**\n * What a stream past the bound keeps. `tail`, the default, keeps its last\n * bytes. `ends` keeps its first and last halves and puts one line naming the\n * dropped bytes where the middle was. `refuse` fails if either stream exceeds\n * the bound.\n */\n readonly overflow?: Overflow | undefined\n}\n\ntype Overflow = \"tail\" | \"ends\" | \"refuse\"\n\n/** One captured stream: the text kept, and the bytes dropped to keep it bounded. */\ninterface Capture {\n readonly text: string\n readonly droppedBytes: number\n}\n\ninterface Tail {\n chunks: Array<Uint8Array>\n bytes: number\n dropped: number\n}\n\n/** A stream's first bytes and its last ones: what `overflow: \"ends\"` keeps. */\ninterface Ends {\n readonly head: Array<Uint8Array>\n headBytes: number\n readonly tail: Tail\n}\n\nconst decoder = new TextDecoder(\"utf-8\")\n\nconst emptyTail = (): Tail => ({ chunks: [], bytes: 0, dropped: 0 })\n\n/** A UTF-8 continuation byte is `10xxxxxx`; every other byte starts a character. */\nconst isContinuation = (byte: number): boolean => (byte & 0b1100_0000) === 0b1000_0000\n\nconst join = (chunks: ReadonlyArray<Uint8Array>, size: number): Uint8Array => {\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\n/** Appends one chunk and drops from the front whatever no longer fits. */\nconst keepTail = (state: Tail, chunk: Uint8Array, maxBytes: number): void => {\n if (chunk.byteLength === 0) return\n state.chunks.push(chunk)\n state.bytes += chunk.byteLength\n while (state.bytes > maxBytes && state.chunks.length > 0) {\n const excess = state.bytes - maxBytes\n const first = state.chunks[0]!\n if (first.byteLength <= excess) {\n state.chunks.shift()\n state.bytes -= first.byteLength\n state.dropped += first.byteLength\n } else {\n state.chunks[0] = first.subarray(excess)\n state.bytes -= excess\n state.dropped += excess\n }\n }\n}\n\n/**\n * Joins the retained chunks and decodes them, skipping the partial code point a\n * dropped head can leave behind.\n *\n * A code point is at most four bytes, so at most three leading continuation\n * bytes can precede the first whole character. Skipping them is counted as\n * dropped rather than decoded to a replacement character the process never\n * printed.\n */\nconst decodeTail = (state: Tail): Capture => {\n const bytes = join(state.chunks, state.bytes)\n if (state.dropped === 0) return { text: decoder.decode(bytes), droppedBytes: 0 }\n let start = 0\n while (start < bytes.byteLength && start < 3 && isContinuation(bytes[start]!)) start++\n return { text: decoder.decode(bytes.subarray(start)), droppedBytes: state.dropped + start }\n}\n\n/**\n * The length of a cut head without the character the cut left unfinished.\n *\n * Its last character starts at the last byte that is not a continuation byte,\n * and that byte says how many bytes the character needs. One that needs more\n * than the head holds is dropped, for the same reason {@link decodeTail} skips\n * a partial code point.\n */\nconst wholeLength = (bytes: Uint8Array): number => {\n for (let back = 1; back <= Math.min(3, bytes.byteLength); back++) {\n const byte = bytes[bytes.byteLength - back]!\n if (isContinuation(byte)) continue\n const width = byte >= 0b1111_0000 ? 4 : byte >= 0b1110_0000 ? 3 : byte >= 0b1100_0000 ? 2 : 1\n return width > back ? bytes.byteLength - back : bytes.byteLength\n }\n return bytes.byteLength\n}\n\n/**\n * Decodes a stream kept by its ends. A stream that fit is decoded whole;\n * otherwise each end is cut on a character boundary and one line naming every\n * byte between them stands where the middle was.\n */\nconst decodeEnds = (state: Ends): Capture => {\n if (state.tail.dropped === 0) {\n const whole = join([...state.head, ...state.tail.chunks], state.headBytes + state.tail.bytes)\n return { text: decoder.decode(whole), droppedBytes: 0 }\n }\n const head = join(state.head, state.headBytes)\n const kept = wholeLength(head)\n const tail = decodeTail(state.tail)\n const droppedBytes = head.byteLength - kept + tail.droppedBytes\n return { text: elideMiddle(decoder.decode(head.subarray(0, kept)), droppedBytes, tail.text), droppedBytes }\n}\n\nconst boundedTail = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n Stream.runFold(stream, emptyTail, (state, chunk) => {\n keepTail(state, chunk, maxBytes)\n return state\n }).pipe(Effect.map((state) =>\n overflow === \"refuse\" && state.dropped > 0\n ? { text: \"\", droppedBytes: state.dropped }\n : decodeTail(state)\n ))\n\n/**\n * Keeps the first half of the bound and the last half. The head fills first,\n * and every byte after it passes through the tail, which drops from its front:\n * what falls out of the tail is the middle.\n */\nconst boundedEnds = <E>(stream: Stream.Stream<Uint8Array, E>, maxBytes: number): Effect.Effect<Capture, E> => {\n const headBytes = Math.floor(maxBytes / 2)\n return Stream.runFold(stream, (): Ends => ({ head: [], headBytes: 0, tail: emptyTail() }), (state, chunk) => {\n const taken = Math.min(headBytes - state.headBytes, chunk.byteLength)\n if (taken > 0) {\n state.head.push(chunk.subarray(0, taken))\n state.headBytes += taken\n }\n keepTail(state.tail, chunk.subarray(taken), maxBytes - headBytes)\n return state\n }).pipe(Effect.map(decodeEnds))\n}\n\nconst capture = <E>(\n stream: Stream.Stream<Uint8Array, E>,\n maxBytes: number | undefined,\n overflow: Overflow | undefined\n): Effect.Effect<Capture, E> =>\n maxBytes === undefined\n ? Effect.map(Stream.mkString(Stream.decodeText(stream)), (text) => ({ text, droppedBytes: 0 }))\n : overflow === \"ends\"\n ? boundedEnds(stream, maxBytes)\n : boundedTail(stream, maxBytes, overflow)\n\n/**\n * A command whose arguments exceed the operating system's limit fails `execve`\n * with `E2BIG` before it starts: one argument over 128 KiB on Linux, all of\n * them over 1 MiB on macOS. A heredoc in a command line, or a file's text\n * passed as an argument, is one such argument. The platform error's own text\n * repeats the whole command line, so this text is written here instead, and a\n * caller may show it as it stands.\n */\nconst argumentsTooLong = (error: { readonly cause?: unknown }): ExecError | undefined =>\n (error.cause as { readonly code?: unknown } | undefined)?.code === \"E2BIG\"\n ? new ExecError({\n code: \"arguments_too_long\",\n message:\n \"Command not started: its arguments exceed the operating system's size limit (E2BIG). Send long text on standard input or in a file, not in the command line or its arguments.\"\n })\n : undefined\n\nconst unbounded = (\n command: string,\n options: ExecOptions\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n const settings: ChildProcess.CommandOptions = {\n ...(options.args === undefined ? { shell: true } : {}),\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n env: ChildProcessEnvironment.make(process.env, options.env),\n extendEnv: false,\n ...(options.stdin === undefined\n ? {}\n : { stdin: Stream.make(new TextEncoder().encode(options.stdin)) })\n }\n const handle = yield* spawner.spawn(\n options.args === undefined\n ? ChildProcess.make(command, settings)\n : ChildProcess.make(command, [...options.args], settings)\n )\n // stdout, stderr, and the exit code have to be consumed concurrently: a\n // command that fills a pipe blocks until the pipe is drained, so waiting\n // for exit first would deadlock on any output larger than the buffer.\n const [stdout, stderr, exitCode] = yield* Effect.all(\n [\n capture(handle.stdout, options.maxCaptureBytes, options.overflow),\n capture(handle.stderr, options.maxCaptureBytes, options.overflow),\n handle.exitCode\n ],\n { concurrency: \"unbounded\" }\n )\n if (options.overflow === \"refuse\") {\n const overflowed = [\n stdout.droppedBytes > 0 ? \"stdout\" : undefined,\n stderr.droppedBytes > 0 ? \"stderr\" : undefined\n ]\n .filter((stream): stream is string => stream !== undefined)\n if (overflowed.length > 0) {\n return yield* Effect.fail(\n new ExecError({\n code: \"capture_overflow\",\n message: `${command} ${\n overflowed.join(\" and \")\n } exceeded the ${options.maxCaptureBytes}-byte capture cap; refusing partial output`\n })\n )\n }\n }\n return {\n stdout: stdout.text,\n stderr: stderr.text,\n exitCode,\n stdoutDroppedBytes: stdout.droppedBytes,\n stderrDroppedBytes: stderr.droppedBytes\n }\n }).pipe(\n Effect.scoped,\n Effect.mapError((error) =>\n error instanceof ExecError ? error : argumentsTooLong(error) ?? new ExecError({\n code: \"spawn_error\",\n message: `exec: ${command}: ${error.message}`\n })\n )\n )\n\n/**\n * Runs a command line through the platform shell and buffers its output.\n *\n * Cancellation is scope closure: the spawn is scoped, so an interrupted fiber\n * — including the one a `timeoutMs` interrupts — terminates the process rather\n * than leaking it.\n *\n * @category execution\n * @since 1.0.0\n */\nexport const exec = (\n command: string,\n options: ExecOptions = {}\n): Effect.Effect<ExecResult, ExecError, ChildProcessSpawner.ChildProcessSpawner> =>\n options.timeoutMs === undefined\n ? unbounded(command, options)\n : Effect.timeoutOrElse(unbounded(command, options), {\n duration: options.timeoutMs,\n orElse: () =>\n Effect.fail(\n new ExecError({\n code: \"timeout\",\n message: `exec: \\`${command}\\` exceeded ${options.timeoutMs}ms`,\n limitMillis: options.timeoutMs\n })\n )\n })\n\n/**\n * The model-facing failure code a command that never produced an exit code\n * carries.\n *\n * A timeout keeps its own code, because \"the command never finished\" and \"the\n * command never started\" are different things to a caller deciding whether to\n * retry. Arguments the operating system refused for their size are the\n * caller's own input, which a shell flow publishes, so they read as\n * `invalid_input`. Every other way of producing no exit code is a host failure.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdErrorCode = (error: ExecError): StdError.Code =>\n error.code === \"timeout\" ? \"timeout\" : error.code === \"arguments_too_long\" ? \"invalid_input\" : \"command_failed\"\n\n/**\n * The model-facing failure one execution error becomes.\n *\n * A timeout names the command, which is what the caller asked for and all a\n * timeout has to say. Arguments too long to start carry the reason and the\n * fix, and nothing of the command. Any other failure carries the message the\n * execution produced, which already names the command.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const toStdError = (command: string, error: ExecError): StdError.StdError =>\n new StdError.StdError({\n code: toStdErrorCode(error),\n message: error.code === \"timeout\"\n ? `Command timed out: ${command}`\n : error.code === \"arguments_too_long\"\n ? error.message\n : `Command failed to start: ${error.message}`,\n ...(error.limitMillis === undefined ? {} : { limitMillis: error.limitMillis })\n })\n"]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A tree another machine listed, served as a read-only `FileSystem`.
3
+ *
4
+ * `RemoteSearch` lists a root on the machine with `find` and brings back the
5
+ * bytes the search reads with `tar`; this module holds what came back and
6
+ * answers what the walk, the globs, and the matcher ask of a filesystem, so
7
+ * `PortableSearch` can search the copy as it searches a host.
8
+ *
9
+ * @since 1.0.0-rc.8
10
+ */
11
+ import type * as Path from "@smthrs/kernel/Path";
12
+ import * as FileSystem from "effect/FileSystem";
13
+ /**
14
+ * What one listed path is: `d` a directory, `u` a directory the machine's
15
+ * user may not list, `f` a file, `o` anything else, and a symbolic link to a
16
+ * directory (`D`), to a file (`F`), or to neither (`L`).
17
+ *
18
+ * @private
19
+ * @since 1.0.0-rc.8
20
+ */
21
+ export type Kind = "d" | "u" | "f" | "o" | "D" | "F" | "L";
22
+ /**
23
+ * One machine's listing of a root, and the bytes it brought back, by
24
+ * absolute path.
25
+ *
26
+ * @private
27
+ * @since 1.0.0-rc.8
28
+ */
29
+ export interface Tree {
30
+ readonly kinds: ReadonlyMap<string, Kind>;
31
+ readonly children: ReadonlyMap<string, ReadonlyArray<string>>;
32
+ readonly contents: ReadonlyMap<string, Uint8Array>;
33
+ }
34
+ /**
35
+ * The member names `tar` wrote for absolute paths, which it strips of their
36
+ * leading `/`, as those paths again.
37
+ *
38
+ * @private
39
+ * @since 1.0.0-rc.8
40
+ */
41
+ export declare const absolute: (members: ReadonlyMap<string, Uint8Array>) => ReadonlyMap<string, Uint8Array>;
42
+ /**
43
+ * Reads NUL-terminated `<kind><path>` records up to an `E` record, then the
44
+ * tar stream of `.gitignore` files after it. `root` is the listed root, which
45
+ * is nobody's child. `undefined` when the bytes are not that shape.
46
+ *
47
+ * @private
48
+ * @since 1.0.0-rc.8
49
+ */
50
+ export declare const parse: (bytes: Uint8Array, root: string, path: Path.Path) => Tree | undefined;
51
+ /**
52
+ * The tree as a read-only `FileSystem`, resolving paths with `path`. A file
53
+ * whose bytes did not come back is one the machine's user could not read,
54
+ * which the in-process peer skips exactly as it skips such a file on a host.
55
+ *
56
+ * @private
57
+ * @since 1.0.0-rc.8
58
+ */
59
+ export declare const fileSystem: (tree: Tree, path: Path.Path) => FileSystem.FileSystem;
60
+ //# sourceMappingURL=RemoteTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteTree.d.ts","sourceRoot":"","sources":["../../../src/internal/RemoteTree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,qBAAqB,CAAA;AAGhD,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAA;AAI/C;;;;;;;GAOG;AACH,MAAM,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACzC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7D,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACnD;AAID;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,KAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAC/B,CAAA;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,UAAU,EAAE,MAAM,MAAM,EAAE,MAAM,IAAI,CAAC,IAAI,KAAG,IAAI,GAAG,SAuB/E,CAAA;AAiCD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,KAAG,UAAU,CAAC,UAiCnE,CAAA"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * A tree another machine listed, served as a read-only `FileSystem`.
3
+ *
4
+ * `RemoteSearch` lists a root on the machine with `find` and brings back the
5
+ * bytes the search reads with `tar`; this module holds what came back and
6
+ * answers what the walk, the globs, and the matcher ask of a filesystem, so
7
+ * `PortableSearch` can search the copy as it searches a host.
8
+ *
9
+ * @since 1.0.0-rc.8
10
+ */
11
+ import { Effect, Option, Stream } from "effect";
12
+ import * as ByteSize from "effect/ByteSize";
13
+ import * as FileSystem from "effect/FileSystem";
14
+ import * as PlatformError from "effect/PlatformError";
15
+ import * as Tar from "./Tar.js";
16
+ const kinds = new Set(["d", "u", "f", "o", "D", "F", "L"]);
17
+ /**
18
+ * The member names `tar` wrote for absolute paths, which it strips of their
19
+ * leading `/`, as those paths again.
20
+ *
21
+ * @private
22
+ * @since 1.0.0-rc.8
23
+ */
24
+ export const absolute = (members) => new Map([...members].map(([name, bytes]) => [`/${name}`, bytes]));
25
+ /**
26
+ * Reads NUL-terminated `<kind><path>` records up to an `E` record, then the
27
+ * tar stream of `.gitignore` files after it. `root` is the listed root, which
28
+ * is nobody's child. `undefined` when the bytes are not that shape.
29
+ *
30
+ * @private
31
+ * @since 1.0.0-rc.8
32
+ */
33
+ export const parse = (bytes, root, path) => {
34
+ const decoder = new TextDecoder();
35
+ const listed = new Map();
36
+ const children = new Map();
37
+ let offset = 0;
38
+ for (;;) {
39
+ const end = bytes.indexOf(0, offset);
40
+ if (end === -1)
41
+ return undefined;
42
+ const record = decoder.decode(bytes.subarray(offset, end));
43
+ offset = end + 1;
44
+ if (record === "E")
45
+ break;
46
+ const kind = record.slice(0, 1);
47
+ if (!kinds.has(kind))
48
+ return undefined;
49
+ const entry = record.slice(1);
50
+ listed.set(entry, kind);
51
+ if (entry === root)
52
+ continue;
53
+ const parent = path.dirname(entry);
54
+ const siblings = children.get(parent);
55
+ if (siblings === undefined)
56
+ children.set(parent, [path.basename(entry)]);
57
+ else
58
+ siblings.push(path.basename(entry));
59
+ }
60
+ const ignores = Tar.files(bytes.subarray(offset));
61
+ return ignores === undefined ? undefined : { kinds: listed, children, contents: absolute(ignores) };
62
+ };
63
+ const refusal = (tag, method, path) => PlatformError.systemError({ _tag: tag, module: "FileSystem", method, pathOrDescriptor: path });
64
+ /** What `stat` says each kind is: links resolved, a dangling one absent. */
65
+ const types = {
66
+ d: "Directory",
67
+ u: "Directory",
68
+ D: "Directory",
69
+ f: "File",
70
+ F: "File",
71
+ o: "Unknown",
72
+ L: undefined
73
+ };
74
+ const info = (type, size) => ({
75
+ type,
76
+ size: ByteSize.bytes(BigInt(size)),
77
+ mtime: Option.none(),
78
+ atime: Option.none(),
79
+ birthtime: Option.none(),
80
+ dev: 0,
81
+ ino: Option.none(),
82
+ mode: 0,
83
+ nlink: Option.none(),
84
+ uid: Option.none(),
85
+ gid: Option.none(),
86
+ rdev: Option.none(),
87
+ blksize: Option.none(),
88
+ blocks: Option.none()
89
+ });
90
+ /**
91
+ * The tree as a read-only `FileSystem`, resolving paths with `path`. A file
92
+ * whose bytes did not come back is one the machine's user could not read,
93
+ * which the in-process peer skips exactly as it skips such a file on a host.
94
+ *
95
+ * @private
96
+ * @since 1.0.0-rc.8
97
+ */
98
+ export const fileSystem = (tree, path) => {
99
+ const kindOf = (value) => tree.kinds.get(path.resolve(value));
100
+ const readFile = (value) => {
101
+ const bytes = tree.contents.get(path.resolve(value));
102
+ if (bytes !== undefined)
103
+ return Effect.succeed(bytes);
104
+ const type = types[kindOf(value) ?? "L"];
105
+ return Effect.fail(refusal(type === "File" ? "PermissionDenied" : type === undefined ? "NotFound" : "BadResource", "readFile", value));
106
+ };
107
+ return FileSystem.makeNoop({
108
+ stat: (value) => {
109
+ const type = types[kindOf(value) ?? "L"];
110
+ return type === undefined
111
+ ? Effect.fail(refusal("NotFound", "stat", value))
112
+ : Effect.succeed(info(type, tree.contents.get(path.resolve(value))?.length ?? 0));
113
+ },
114
+ readDirectory: (value) => {
115
+ const kind = kindOf(value);
116
+ if (kind === "d")
117
+ return Effect.succeed([...tree.children.get(path.resolve(value)) ?? []]);
118
+ const tag = kind === "u" ? "PermissionDenied" : kind === undefined || kind === "L" ? "NotFound" : "BadResource";
119
+ return Effect.fail(refusal(tag, "readDirectory", value));
120
+ },
121
+ readLink: (value) => {
122
+ const kind = kindOf(value);
123
+ return kind === "D" || kind === "F" || kind === "L"
124
+ ? Effect.succeed("")
125
+ : Effect.fail(refusal(kind === undefined ? "NotFound" : "BadResource", "readLink", value));
126
+ },
127
+ readFile,
128
+ readFileString: (value) => Effect.map(readFile(value), (bytes) => new TextDecoder().decode(bytes)),
129
+ stream: (value) => Stream.fromEffect(readFile(value))
130
+ });
131
+ };
132
+ //# sourceMappingURL=RemoteTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteTree.js","sourceRoot":"","sources":["../../../src/internal/RemoteTree.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A tree another machine listed, served as a read-only `FileSystem`.\n *\n * `RemoteSearch` lists a root on the machine with `find` and brings back the\n * bytes the search reads with `tar`; this module holds what came back and\n * answers what the walk, the globs, and the matcher ask of a filesystem, so\n * `PortableSearch` can search the copy as it searches a host.\n *\n * @since 1.0.0-rc.8\n */\n\nimport type * as Path from \"@smthrs/kernel/Path\"\nimport { Effect, Option, Stream } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport * as FileSystem from \"effect/FileSystem\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport * as Tar from \"./Tar.ts\"\n\n/**\n * What one listed path is: `d` a directory, `u` a directory the machine's\n * user may not list, `f` a file, `o` anything else, and a symbolic link to a\n * directory (`D`), to a file (`F`), or to neither (`L`).\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport type Kind = \"d\" | \"u\" | \"f\" | \"o\" | \"D\" | \"F\" | \"L\"\n\n/**\n * One machine's listing of a root, and the bytes it brought back, by\n * absolute path.\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport interface Tree {\n readonly kinds: ReadonlyMap<string, Kind>\n readonly children: ReadonlyMap<string, ReadonlyArray<string>>\n readonly contents: ReadonlyMap<string, Uint8Array>\n}\n\nconst kinds: ReadonlySet<string> = new Set([\"d\", \"u\", \"f\", \"o\", \"D\", \"F\", \"L\"])\n\n/**\n * The member names `tar` wrote for absolute paths, which it strips of their\n * leading `/`, as those paths again.\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport const absolute = (members: ReadonlyMap<string, Uint8Array>): ReadonlyMap<string, Uint8Array> =>\n new Map([...members].map(([name, bytes]) => [`/${name}`, bytes]))\n\n/**\n * Reads NUL-terminated `<kind><path>` records up to an `E` record, then the\n * tar stream of `.gitignore` files after it. `root` is the listed root, which\n * is nobody's child. `undefined` when the bytes are not that shape.\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport const parse = (bytes: Uint8Array, root: string, path: Path.Path): Tree | undefined => {\n const decoder = new TextDecoder()\n const listed = new Map<string, Kind>()\n const children = new Map<string, Array<string>>()\n let offset = 0\n for (;;) {\n const end = bytes.indexOf(0, offset)\n if (end === -1) return undefined\n const record = decoder.decode(bytes.subarray(offset, end))\n offset = end + 1\n if (record === \"E\") break\n const kind = record.slice(0, 1)\n if (!kinds.has(kind)) return undefined\n const entry = record.slice(1)\n listed.set(entry, kind as Kind)\n if (entry === root) continue\n const parent = path.dirname(entry)\n const siblings = children.get(parent)\n if (siblings === undefined) children.set(parent, [path.basename(entry)])\n else siblings.push(path.basename(entry))\n }\n const ignores = Tar.files(bytes.subarray(offset))\n return ignores === undefined ? undefined : { kinds: listed, children, contents: absolute(ignores) }\n}\n\nconst refusal = (tag: PlatformError.SystemErrorTag, method: string, path: string): PlatformError.PlatformError =>\n PlatformError.systemError({ _tag: tag, module: \"FileSystem\", method, pathOrDescriptor: path })\n\n/** What `stat` says each kind is: links resolved, a dangling one absent. */\nconst types: Readonly<Record<Kind, FileSystem.File.Type | undefined>> = {\n d: \"Directory\",\n u: \"Directory\",\n D: \"Directory\",\n f: \"File\",\n F: \"File\",\n o: \"Unknown\",\n L: undefined\n}\n\nconst info = (type: FileSystem.File.Type, size: number): FileSystem.File.Info => ({\n type,\n size: ByteSize.bytes(BigInt(size)),\n mtime: Option.none(),\n atime: Option.none(),\n birthtime: Option.none(),\n dev: 0,\n ino: Option.none(),\n mode: 0,\n nlink: Option.none(),\n uid: Option.none(),\n gid: Option.none(),\n rdev: Option.none(),\n blksize: Option.none(),\n blocks: Option.none()\n})\n\n/**\n * The tree as a read-only `FileSystem`, resolving paths with `path`. A file\n * whose bytes did not come back is one the machine's user could not read,\n * which the in-process peer skips exactly as it skips such a file on a host.\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport const fileSystem = (tree: Tree, path: Path.Path): FileSystem.FileSystem => {\n const kindOf = (value: string) => tree.kinds.get(path.resolve(value))\n const readFile = (value: string) => {\n const bytes = tree.contents.get(path.resolve(value))\n if (bytes !== undefined) return Effect.succeed(bytes)\n const type = types[kindOf(value) ?? \"L\"]\n return Effect.fail(\n refusal(type === \"File\" ? \"PermissionDenied\" : type === undefined ? \"NotFound\" : \"BadResource\", \"readFile\", value)\n )\n }\n return FileSystem.makeNoop({\n stat: (value) => {\n const type = types[kindOf(value) ?? \"L\"]\n return type === undefined\n ? Effect.fail(refusal(\"NotFound\", \"stat\", value))\n : Effect.succeed(info(type, tree.contents.get(path.resolve(value))?.length ?? 0))\n },\n readDirectory: (value) => {\n const kind = kindOf(value)\n if (kind === \"d\") return Effect.succeed([...tree.children.get(path.resolve(value)) ?? []])\n const tag = kind === \"u\" ? \"PermissionDenied\" : kind === undefined || kind === \"L\" ? \"NotFound\" : \"BadResource\"\n return Effect.fail(refusal(tag, \"readDirectory\", value))\n },\n readLink: (value) => {\n const kind = kindOf(value)\n return kind === \"D\" || kind === \"F\" || kind === \"L\"\n ? Effect.succeed(\"\")\n : Effect.fail(refusal(kind === undefined ? \"NotFound\" : \"BadResource\", \"readLink\", value))\n },\n readFile,\n readFileString: (value) => Effect.map(readFile(value), (bytes) => new TextDecoder().decode(bytes)),\n stream: (value) => Stream.fromEffect(readFile(value))\n })\n}\n"]}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The regular files of a tar stream.
3
+ *
4
+ * `RemoteSearch` brings a machine's files back with its own `tar`, because
5
+ * every image ships one and it frames each file with its name and size. GNU
6
+ * and BusyBox `tar` write GNU long names, `bsdtar` writes POSIX extended
7
+ * headers, and `xargs` may start `tar` more than once, so the stream can hold
8
+ * several archives one after another. This reads all of them.
9
+ *
10
+ * @since 1.0.0-rc.8
11
+ */
12
+ /**
13
+ * Every regular file the stream holds, by member name, or `undefined` when
14
+ * the stream is not whole tar archives: a header whose checksum does not
15
+ * hold, a member cut short, or a last archive with no end. A `tar` that
16
+ * stopped between two members wrote no end, so its missing files are not
17
+ * mistaken for files it could not read.
18
+ *
19
+ * @private
20
+ * @since 1.0.0-rc.8
21
+ */
22
+ export declare const files: (stream: Uint8Array) => ReadonlyMap<string, Uint8Array> | undefined;
23
+ //# sourceMappingURL=Tar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tar.d.ts","sourceRoot":"","sources":["../../../src/internal/Tar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAsCH;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,QAAQ,UAAU,KAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAoC5E,CAAA"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The regular files of a tar stream.
3
+ *
4
+ * `RemoteSearch` brings a machine's files back with its own `tar`, because
5
+ * every image ships one and it frames each file with its name and size. GNU
6
+ * and BusyBox `tar` write GNU long names, `bsdtar` writes POSIX extended
7
+ * headers, and `xargs` may start `tar` more than once, so the stream can hold
8
+ * several archives one after another. This reads all of them.
9
+ *
10
+ * @since 1.0.0-rc.8
11
+ */
12
+ const decoder = new TextDecoder();
13
+ /** Bytes up to the first NUL, as text. */
14
+ const text = (bytes) => {
15
+ const end = bytes.indexOf(0);
16
+ return decoder.decode(end === -1 ? bytes : bytes.subarray(0, end));
17
+ };
18
+ /** An octal header field, or `undefined` when it holds anything else. */
19
+ const octal = (bytes) => {
20
+ const digits = text(bytes).trim();
21
+ return /^[0-7]+$/.test(digits) ? Number.parseInt(digits, 8) : undefined;
22
+ };
23
+ /** The header's checksum, computed with the checksum field read as spaces. */
24
+ const checksum = (header) => {
25
+ let sum = 0;
26
+ for (let index = 0; index < 512; index++)
27
+ sum += index >= 148 && index < 156 ? 32 : header[index];
28
+ return sum;
29
+ };
30
+ /** The `path` record of a POSIX extended header: `<length> <key>=<value>\n` records. */
31
+ const paxPath = (data) => {
32
+ let offset = 0;
33
+ let path;
34
+ while (offset < data.length) {
35
+ const space = data.indexOf(32, offset);
36
+ const length = space === -1 ? Number.NaN : Number(decoder.decode(data.subarray(offset, space)));
37
+ if (!Number.isSafeInteger(length) || length <= space - offset || offset + length > data.length)
38
+ return path;
39
+ const record = decoder.decode(data.subarray(space + 1, offset + length - 1));
40
+ if (record.startsWith("path="))
41
+ path = record.slice("path=".length);
42
+ offset += length;
43
+ }
44
+ return path;
45
+ };
46
+ /**
47
+ * Every regular file the stream holds, by member name, or `undefined` when
48
+ * the stream is not whole tar archives: a header whose checksum does not
49
+ * hold, a member cut short, or a last archive with no end. A `tar` that
50
+ * stopped between two members wrote no end, so its missing files are not
51
+ * mistaken for files it could not read.
52
+ *
53
+ * @private
54
+ * @since 1.0.0-rc.8
55
+ */
56
+ export const files = (stream) => {
57
+ const found = new Map();
58
+ let longName;
59
+ let extended;
60
+ let ended = true;
61
+ for (let offset = 0; offset + 512 <= stream.length;) {
62
+ const header = stream.subarray(offset, offset + 512);
63
+ offset += 512;
64
+ // Zero blocks end one archive; `xargs` may have started another.
65
+ ended = header.every((byte) => byte === 0);
66
+ if (ended)
67
+ continue;
68
+ const size = octal(header.subarray(124, 136));
69
+ if (size === undefined || octal(header.subarray(148, 156)) !== checksum(header))
70
+ return undefined;
71
+ if (offset + size > stream.length)
72
+ return undefined;
73
+ const data = stream.subarray(offset, offset + size);
74
+ offset += Math.ceil(size / 512) * 512;
75
+ const type = String.fromCharCode(header[156]);
76
+ if (type === "L") {
77
+ longName = text(data);
78
+ continue;
79
+ }
80
+ if (type === "x") {
81
+ extended = paxPath(data) ?? extended;
82
+ continue;
83
+ }
84
+ // A long link name and a global header describe no member of their own.
85
+ if (type === "K" || type === "g")
86
+ continue;
87
+ if (type === "0" || type === "\0" || type === "7") {
88
+ const prefix = decoder.decode(header.subarray(257, 263)) === "ustar\0" ? text(header.subarray(345, 500)) : "";
89
+ const short = text(header.subarray(0, 100));
90
+ found.set(extended ?? longName ?? (prefix === "" ? short : `${prefix}/${short}`), data.slice());
91
+ }
92
+ longName = undefined;
93
+ extended = undefined;
94
+ }
95
+ return ended ? found : undefined;
96
+ };
97
+ //# sourceMappingURL=Tar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tar.js","sourceRoot":"","sources":["../../../src/internal/Tar.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The regular files of a tar stream.\n *\n * `RemoteSearch` brings a machine's files back with its own `tar`, because\n * every image ships one and it frames each file with its name and size. GNU\n * and BusyBox `tar` write GNU long names, `bsdtar` writes POSIX extended\n * headers, and `xargs` may start `tar` more than once, so the stream can hold\n * several archives one after another. This reads all of them.\n *\n * @since 1.0.0-rc.8\n */\n\nconst decoder = new TextDecoder()\n\n/** Bytes up to the first NUL, as text. */\nconst text = (bytes: Uint8Array): string => {\n const end = bytes.indexOf(0)\n return decoder.decode(end === -1 ? bytes : bytes.subarray(0, end))\n}\n\n/** An octal header field, or `undefined` when it holds anything else. */\nconst octal = (bytes: Uint8Array): number | undefined => {\n const digits = text(bytes).trim()\n return /^[0-7]+$/.test(digits) ? Number.parseInt(digits, 8) : undefined\n}\n\n/** The header's checksum, computed with the checksum field read as spaces. */\nconst checksum = (header: Uint8Array): number => {\n let sum = 0\n for (let index = 0; index < 512; index++) sum += index >= 148 && index < 156 ? 32 : header[index]!\n return sum\n}\n\n/** The `path` record of a POSIX extended header: `<length> <key>=<value>\\n` records. */\nconst paxPath = (data: Uint8Array): string | undefined => {\n let offset = 0\n let path: string | undefined\n while (offset < data.length) {\n const space = data.indexOf(32, offset)\n const length = space === -1 ? Number.NaN : Number(decoder.decode(data.subarray(offset, space)))\n if (!Number.isSafeInteger(length) || length <= space - offset || offset + length > data.length) return path\n const record = decoder.decode(data.subarray(space + 1, offset + length - 1))\n if (record.startsWith(\"path=\")) path = record.slice(\"path=\".length)\n offset += length\n }\n return path\n}\n\n/**\n * Every regular file the stream holds, by member name, or `undefined` when\n * the stream is not whole tar archives: a header whose checksum does not\n * hold, a member cut short, or a last archive with no end. A `tar` that\n * stopped between two members wrote no end, so its missing files are not\n * mistaken for files it could not read.\n *\n * @private\n * @since 1.0.0-rc.8\n */\nexport const files = (stream: Uint8Array): ReadonlyMap<string, Uint8Array> | undefined => {\n const found = new Map<string, Uint8Array>()\n let longName: string | undefined\n let extended: string | undefined\n let ended = true\n for (let offset = 0; offset + 512 <= stream.length;) {\n const header = stream.subarray(offset, offset + 512)\n offset += 512\n // Zero blocks end one archive; `xargs` may have started another.\n ended = header.every((byte) => byte === 0)\n if (ended) continue\n const size = octal(header.subarray(124, 136))\n if (size === undefined || octal(header.subarray(148, 156)) !== checksum(header)) return undefined\n if (offset + size > stream.length) return undefined\n const data = stream.subarray(offset, offset + size)\n offset += Math.ceil(size / 512) * 512\n const type = String.fromCharCode(header[156]!)\n if (type === \"L\") {\n longName = text(data)\n continue\n }\n if (type === \"x\") {\n extended = paxPath(data) ?? extended\n continue\n }\n // A long link name and a global header describe no member of their own.\n if (type === \"K\" || type === \"g\") continue\n if (type === \"0\" || type === \"\\0\" || type === \"7\") {\n const prefix = decoder.decode(header.subarray(257, 263)) === \"ustar\\0\" ? text(header.subarray(345, 500)) : \"\"\n const short = text(header.subarray(0, 100))\n found.set(extended ?? longName ?? (prefix === \"\" ? short : `${prefix}/${short}`), data.slice())\n }\n longName = undefined\n extended = undefined\n }\n return ended ? found : undefined\n}\n"]}
package/docs/README.md CHANGED
@@ -42,7 +42,9 @@ the details that decide whether a model gets its next step right:
42
42
 
43
43
  ## Install
44
44
 
45
- Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
45
+ ```bash
46
+ npm install @smthrs/std@next effect
47
+ ```
46
48
 
47
49
  The handlers
48
50
  ask the host for services such as `FileSystem`, `Path`, and