@smthrs/std 1.0.0-rc.8 → 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.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -1
- package/dist/cjs/PortableSearch.d.ts +13 -1
- package/dist/cjs/PortableSearch.d.ts.map +1 -1
- package/dist/cjs/PortableSearch.js +8 -1
- package/dist/cjs/PortableSearch.js.map +2 -2
- package/dist/cjs/RemoteSearch.d.ts +54 -0
- package/dist/cjs/RemoteSearch.d.ts.map +1 -0
- package/dist/cjs/RemoteSearch.js +134 -0
- package/dist/cjs/RemoteSearch.js.map +7 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/internal/EnvelopePrecheck.d.ts.map +1 -1
- package/dist/cjs/internal/EnvelopePrecheck.js +21 -5
- package/dist/cjs/internal/EnvelopePrecheck.js.map +2 -2
- package/dist/cjs/internal/RemoteTree.d.ts +60 -0
- package/dist/cjs/internal/RemoteTree.d.ts.map +1 -0
- package/dist/cjs/internal/RemoteTree.js +123 -0
- package/dist/cjs/internal/RemoteTree.js.map +7 -0
- package/dist/cjs/internal/Tar.d.ts +23 -0
- package/dist/cjs/internal/Tar.d.ts.map +1 -0
- package/dist/cjs/internal/Tar.js +86 -0
- package/dist/cjs/internal/Tar.js.map +7 -0
- package/dist/esm/PortableSearch.d.ts +13 -1
- package/dist/esm/PortableSearch.d.ts.map +1 -1
- package/dist/esm/PortableSearch.js +16 -0
- package/dist/esm/PortableSearch.js.map +1 -1
- package/dist/esm/RemoteSearch.d.ts +54 -0
- package/dist/esm/RemoteSearch.d.ts.map +1 -0
- package/dist/esm/RemoteSearch.js +162 -0
- package/dist/esm/RemoteSearch.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/EnvelopePrecheck.d.ts.map +1 -1
- package/dist/esm/internal/EnvelopePrecheck.js +29 -5
- package/dist/esm/internal/EnvelopePrecheck.js.map +1 -1
- package/dist/esm/internal/RemoteTree.d.ts +60 -0
- package/dist/esm/internal/RemoteTree.d.ts.map +1 -0
- package/dist/esm/internal/RemoteTree.js +132 -0
- package/dist/esm/internal/RemoteTree.js.map +1 -0
- package/dist/esm/internal/Tar.d.ts +23 -0
- package/dist/esm/internal/Tar.d.ts.map +1 -0
- package/dist/esm/internal/Tar.js +97 -0
- package/dist/esm/internal/Tar.js.map +1 -0
- package/docs/api.md +28 -4
- package/docs/concepts/effects-and-capabilities.md +3 -1
- package/docs/guides/bind-the-standard-flows.md +3 -1
- package/docs/guides/run-a-shell-command.md +4 -1
- package/docs/troubleshooting.md +8 -0
- package/package.json +16 -6
- package/src/PortableSearch.ts +20 -0
- package/src/RemoteSearch.ts +199 -0
- package/src/index.ts +3 -0
- package/src/internal/EnvelopePrecheck.ts +29 -5
- package/src/internal/RemoteTree.ts +159 -0
- package/src/internal/Tar.ts +95 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvelopePrecheck.js","sourceRoot":"","sources":["../../../src/internal/EnvelopePrecheck.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The hermetic pre-check: a lexical scan of shell text for the paths it names,\n * and the refusal when one of them falls outside the declared envelope.\n *\n * The scan is fail-closed over explicit path tokens and nothing more. It\n * cannot prove confinement — a shell can name a path no lexer sees, through a\n * variable, a substitution, or a program that opens a file of its own — so a\n * kernel or host sandbox must eventually enforce and report the complete read\n * and write sets.\n *\n * It lives beside the flow rather than inside it because it is its own\n * concept, with its own vocabulary (Access, PathReference) and its own\n * security history: three independent bypasses, each sufficient on its own to\n * defeat it, are closed here.\n *\n * @since 1.0.0\n */\n\nimport type * as Path from \"@smthrs/kernel/Path\"\nimport * as StdError from \"../StdError.ts\"\nimport { withinEnvelope } from \"./Paths.ts\"\n\n/**\n * The filesystem envelope one invocation declared.\n *\n * Structural rather than the hermetic `Bash.Input` itself: the scan needs the\n * three fields and none of the rest, and depending on the flow module would\n * make the flow and its pre-check import each other.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Envelope {\n readonly cwd?: string | undefined\n readonly reads: ReadonlyArray<string>\n readonly writes: ReadonlyArray<string>\n}\n\n/**\n * How a command touches a path it names.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Access = \"read\" | \"write\"\n\n/**\n * One path a command names, and how it touches it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface PathReference {\n readonly access: Access\n readonly value: string\n}\n\nconst commandSeparators = new Set([\"&&\", \"||\", \";\", \";;\", \";&\", \";;&\", \"|\", \"|&\", \"&\", \"\\n\", \"(\", \")\"])\nconst writeRedirections = new Set([\">\", \">>\", \">|\", \"&>\", \"&>>\", \">&\", \"<>\"])\nconst readRedirections = new Set([\"<\", \"<&\"])\n// A here-document delimiter and a here-string are text, not file names.\nconst textRedirections = new Set([\"<<\", \"<<-\", \"<<<\"])\nconst isRedirection = (token: Token): boolean =>\n token.operator &&\n (writeRedirections.has(token.text) || readRedirections.has(token.text) || textRedirections.has(token.text))\n// Longest first, so `&>>` is not read as `&` then `>>`.\nconst operators = [\n \";;&\",\n \"&>>\",\n \"<<<\",\n \"<<-\",\n \";;\",\n \";&\",\n \"&&\",\n \"||\",\n \"|&\",\n \"&>\",\n \">>\",\n \">|\",\n \">&\",\n \"<>\",\n \"<<\",\n \"<&\",\n \"\\n\",\n \";\",\n \"|\",\n \"&\",\n \"(\",\n \")\",\n \">\",\n \"<\"\n]\n// Reserved words and grouping braces open or close a compound command; the\n// command they wrap stands after them.\nconst reservedWords = new Set([\n \"!\",\n \"{\",\n \"}\",\n \"case\",\n \"do\",\n \"done\",\n \"elif\",\n \"else\",\n \"esac\",\n \"fi\",\n \"for\",\n \"function\",\n \"if\",\n \"in\",\n \"select\",\n \"then\",\n \"until\",\n \"while\"\n])\nconst writeCommands = new Set([\"chmod\", \"chown\", \"mkdir\", \"rm\", \"rmdir\", \"tee\", \"touch\", \"truncate\", \"unlink\"])\nconst destinationCommands = new Set([\"cp\", \"install\", \"mv\"])\nconst prefixWrappers = new Set([\"env\", \"nice\", \"time\", \"sudo\", \"nohup\", \"xargs\", \"command\", \"exec\"])\n\n/**\n * One lexical unit of shell text: an operator, or a word with its quotes\n * removed. A command substitution inside a word stands as `$()` in the word;\n * its body is scanned as a command of its own.\n */\ninterface Token {\n readonly operator: boolean\n readonly text: string\n}\n\ninterface Lexed {\n readonly tokens: ReadonlyArray<Token>\n readonly substitutions: ReadonlyArray<string>\n}\n\n// The end of a `$(` body that starts at `from`: the index of its closing\n// parenthesis, skipping quoted spans and nested parentheses. An unclosed body\n// runs to the end of the text, which is how a fail-closed scan must read it.\n// Substitutions and quotes nest without limit in a shell, but each level\n// costs this scan a pass over the rest of the text and a stack frame. Fifty\n// thousand `$(` used to run for half a minute and then overflow the stack, so\n// past this depth the text is refused rather than scanned.\nconst maxNesting = 32\n\nconst tooDeep = (): StdError.StdError =>\n new StdError.StdError({\n code: \"invalid_input\",\n message: `Command nests substitutions or quotes deeper than ${maxNesting} levels`\n })\n\nconst substitutionEnd = (text: string, from: number, nesting: number): number => {\n if (nesting > maxNesting) throw tooDeep()\n let depth = 1\n let index = from\n while (index < text.length) {\n const char = text[index]!\n if (char === \"\\\\\") {\n index += 2\n } else if (char === \"'\") {\n const close = text.indexOf(\"'\", index + 1)\n index = close === -1 ? text.length : close + 1\n } else if (char === \"\\\"\") {\n index = doubleQuoteEnd(text, index + 1, nesting + 1) + 1\n } else if (char === \"`\") {\n index = backtickEnd(text, index + 1) + 1\n } else {\n if (char === \"(\") depth++\n if (char === \")\" && --depth === 0) return index\n index++\n }\n }\n return text.length\n}\n\nconst backtickEnd = (text: string, from: number): number => {\n let index = from\n while (index < text.length && text[index] !== \"`\") index += text[index] === \"\\\\\" ? 2 : 1\n return Math.min(index, text.length)\n}\n\nconst doubleQuoteEnd = (text: string, from: number, nesting: number): number => {\n if (nesting > maxNesting) throw tooDeep()\n let index = from\n while (index < text.length && text[index] !== \"\\\"\") {\n const char = text[index]!\n if (char === \"\\\\\") index += 2\n else if (char === \"`\") index = backtickEnd(text, index + 1) + 1\n else if (char === \"$\" && text[index + 1] === \"(\") index = substitutionEnd(text, index + 2, nesting + 1) + 1\n else index++\n }\n return Math.min(index, text.length)\n}\n\n// A hand-rolled lexer rather than one regular expression: a word is any run\n// of bare characters, quoted spans, and substitutions, and a substitution can\n// hold quotes and operators of its own. Splitting on operator characters\n// alone let `echo \"$(rm /work/target)\"` hide a delete inside one quoted word,\n// read `>|` as a pipe that turned the target into the next command's name,\n// and read `(rm /work/target)` as a command named `(rm`.\n//\n// Quotes are removed from a word's text, because `\"/outs\"ide/x` names\n// `/outside/x`; keeping the quotes made it a relative path under the\n// working directory.\n// Sticky, so the match starts at `lastIndex` without copying the rest of the\n// text for every word.\nconst descriptorPattern = /\\d+(?=[<>])/y\n\nconst lex = (command: string, nesting: number): Lexed => {\n const tokens: Array<Token> = []\n const substitutions: Array<string> = []\n let index = 0\n const substitution = (from: number, to: number): string => {\n substitutions.push(command.slice(from, to))\n return \"$()\"\n }\n while (index < command.length) {\n const char = command[index]!\n // Every blank the word loop stops at must be skipped here, or the lexer\n // would push an empty word without advancing.\n if (char !== \"\\n\" && /\\s/.test(char)) {\n index++\n continue\n }\n if (char === \"\\\\\" && command[index + 1] === \"\\n\") {\n index += 2\n continue\n }\n if (char === \"#\") {\n while (index < command.length && command[index] !== \"\\n\") index++\n continue\n }\n // A file-descriptor number belongs to the redirection after it: in\n // `2>/dev/null rm /work/target` the command is `rm`, not `2`.\n descriptorPattern.lastIndex = index\n const descriptor = descriptorPattern.exec(command)\n if (descriptor !== null) {\n index += descriptor[0].length\n continue\n }\n const operator = operators.find((candidate) => command.startsWith(candidate, index))\n if (operator !== undefined) {\n tokens.push({ operator: true, text: operator })\n index += operator.length\n continue\n }\n let text = \"\"\n while (index < command.length) {\n const next = command[index]!\n if (/[\\s<>;|&()]/.test(next)) break\n if (next === \"\\\\\") {\n // A backslash escapes shell syntax. Before any other character it is\n // kept, so `C:\\work\\input` stays the path it names on Windows rather\n // than collapsing to a word the scan cannot see as a path.\n const escaped = command[index + 1] ?? \"\"\n if (escaped !== \"\\n\") text += /[\\s<>;|&()'\"`$\\\\#]/.test(escaped) ? escaped : `\\\\${escaped}`\n index += 2\n } else if (next === \"'\") {\n const close = command.indexOf(\"'\", index + 1)\n const end = close === -1 ? command.length : close\n text += command.slice(index + 1, end)\n index = end + 1\n } else if (next === \"\\\"\") {\n const end = doubleQuoteEnd(command, index + 1, nesting + 1)\n let inner = index + 1\n while (inner < end) {\n const quoted = command[inner]!\n if (quoted === \"\\\\\" && /[$`\"\\\\\\n]/.test(command[inner + 1] ?? \"\")) {\n if (command[inner + 1] !== \"\\n\") text += command[inner + 1]\n inner += 2\n } else if (quoted === \"`\") {\n const close = backtickEnd(command, inner + 1)\n text += substitution(inner + 1, close)\n inner = close + 1\n } else if (quoted === \"$\" && command[inner + 1] === \"(\") {\n const close = substitutionEnd(command, inner + 2, nesting + 2)\n text += substitution(inner + 2, close)\n inner = close + 1\n } else {\n text += quoted\n inner++\n }\n }\n index = end + 1\n } else if (next === \"`\") {\n const close = backtickEnd(command, index + 1)\n text += substitution(index + 1, close)\n index = close + 1\n } else if (next === \"$\" && command[index + 1] === \"(\") {\n const close = substitutionEnd(command, index + 2, nesting + 1)\n text += substitution(index + 2, close)\n index = close + 1\n } else {\n text += next\n index++\n }\n }\n tokens.push({ operator: false, text })\n }\n return { tokens, substitutions }\n}\n\n// A parenthesis is an operator to the lexer, so one left in a word was quoted\n// and is part of the name; only a trailing comma is punctuation.\nconst pathValue = (token: string): string => token.replace(/,+$/, \"\")\n\nconst isPathToken = (path: Path.Path, token: string): boolean =>\n token === \".\" ||\n token === \"..\" ||\n token.startsWith(\"./\") ||\n token.startsWith(\"../\") ||\n path.isAbsolute(token) ||\n /^[A-Za-z]:[\\\\/]/.test(token) ||\n token.includes(\"/\")\n\nconst segmentReferences = (path: Path.Path, tokens: ReadonlyArray<Token>): ReadonlyArray<PathReference> => {\n // The word after a redirection operator is its target, never the command.\n const isTarget = (index: number): boolean => index > 0 && isRedirection(tokens[index - 1]!)\n const isWord = (index: number): boolean => !tokens[index]!.operator && !isTarget(index)\n const name = (index: number): string => path.basename(pathValue(tokens[index]!.text))\n const hasWrapper = tokens.some((_, index) => isWord(index) && prefixWrappers.has(name(index)))\n let commandIndex = tokens.findIndex((token, index) =>\n isWord(index) &&\n !token.text.includes(\"=\") &&\n !prefixWrappers.has(name(index)) &&\n !reservedWords.has(token.text) &&\n !token.text.startsWith(\"-\") &&\n token.text !== \"\"\n )\n if (hasWrapper) {\n // Wrapper options can carry their own values. Prefer a later known\n // mutating command so an option value cannot lend it a read classification.\n const mutatingIndex = tokens.findIndex((_, index) =>\n isWord(index) && (writeCommands.has(name(index)) || destinationCommands.has(name(index)))\n )\n if (mutatingIndex !== -1) commandIndex = mutatingIndex\n }\n\n const command = commandIndex === -1 ? \"\" : name(commandIndex)\n const candidates: Array<{ readonly index: number; readonly value: string }> = []\n for (let index = 0; index < tokens.length; index++) {\n const token = tokens[index]!\n if (index === commandIndex || token.operator || (!isTarget(index) && token.text.startsWith(\"-\"))) continue\n if (isTarget(index) && textRedirections.has(tokens[index - 1]!.text)) continue\n const value = pathValue(token.text)\n if (value !== \"\" && isPathToken(path, value)) candidates.push({ index, value })\n }\n\n const destinationIndex = destinationCommands.has(command)\n ? candidates.filter(({ index }) => !isTarget(index)).at(-1)?.index\n : undefined\n\n return candidates.map(({ index, value }) => {\n const access: Access = isTarget(index)\n ? writeRedirections.has(tokens[index - 1]!.text) ? \"write\" : \"read\"\n : writeCommands.has(command) || destinationIndex === index\n ? \"write\"\n : \"read\"\n return { access, value }\n })\n}\n\nconst references = (path: Path.Path, command: string, nesting: number): ReadonlyArray<PathReference> => {\n const { substitutions, tokens } = lex(command, nesting)\n const found: Array<PathReference> = []\n let segment: Array<Token> = []\n for (const token of tokens) {\n if (token.operator && commandSeparators.has(token.text)) {\n found.push(...segmentReferences(path, segment))\n segment = []\n } else {\n segment.push(token)\n }\n }\n found.push(...segmentReferences(path, segment))\n // A substitution runs its body as a command; its paths count like any other.\n for (const body of substitutions) found.push(...references(path, body, nesting + 1))\n return found\n}\n\n/**\n * Every path token the shell text names, one segment at a time.\n *\n * @throws {StdError.StdError} `invalid_input` when substitutions or quotes nest\n * deeper than the scan reads.\n * @category pre-check\n * @since 1.0.0\n */\nexport const commandReferences = (path: Path.Path, command: string): ReadonlyArray<PathReference> =>\n references(path, command, 0)\n\nconst resolvePath = (\n path: Path.Path,\n cwd: string,\n value: string\n): string => path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value)\n\nconst isDeclared = (\n path: Path.Path,\n cwd: string,\n declared: ReadonlyArray<string>,\n source: string\n): boolean =>\n withinEnvelope(\n declared.map((entry) => resolvePath(path, cwd, entry)),\n resolvePath(path, cwd, source)\n )\n\n/**\n * The refusal shell text earns by naming a path outside its declared\n * envelope, or `undefined` when every path it names is declared.\n *\n * @category pre-check\n * @since 1.0.0\n */\nexport const outsideEnvelope = (\n input: Envelope,\n text: string,\n path: Path.Path\n): StdError.StdError | undefined => {\n const base = path.resolve(\".\")\n const cwd = path.resolve(input.cwd ?? \".\")\n // Passing the default explicitly is not a declaration the caller owes. A\n // working directory is where the command runs, not a file it reads, and\n // `cwd: \".\"` names exactly what omitting `cwd` names — so refusing it asked\n // the caller to list the workspace root among its reads to say nothing at\n // all. Agents hit this constantly: one SWE-bench run spent ten of its\n // forty-eight tool calls re-issuing the same command after\n // \"Working directory is outside declared reads: <the workspace root>\". A\n // cwd that points somewhere else is still declared or refused.\n if (input.cwd !== undefined && cwd !== base && !isDeclared(path, cwd, input.reads, cwd)) {\n return new StdError.StdError({\n code: \"outside_declared_reads\",\n message: `Working directory is outside declared reads: ${cwd}`,\n path: cwd\n })\n }\n\n // The Shell contract currently has no sandbox or access-reporting surface.\n // This lexical scan is intentionally only a fail-closed pre-check for\n // explicit path tokens. It cannot prove confinement; a kernel/host sandbox\n // must eventually enforce and report the complete read and write sets.\n let named: ReadonlyArray<PathReference>\n try {\n named = commandReferences(path, text)\n } catch {\n // The depth bound is the one failure the scan raises, and a text the scan\n // cannot read is refused whatever the reason, never run unchecked.\n return tooDeep()\n }\n for (const reference of named) {\n const resolved = resolvePath(path, cwd, reference.value)\n // /dev/* is the process plumbing every shell one-liner leans on:\n // `2>/dev/null`, `cat /dev/stdin`. Normalize first so a dot-dot segment\n // cannot disguise an ordinary filesystem path as process plumbing.\n if (resolved.startsWith(\"/dev/\")) continue\n const declared = reference.access === \"write\" ? input.writes : input.reads\n if (!isDeclared(path, cwd, declared, resolved)) {\n const code = reference.access === \"write\" ? \"outside_declared_writes\" : \"outside_declared_reads\"\n return new StdError.StdError({\n code,\n message: `Command path is outside declared ${reference.access}s: ${resolved}`,\n path: resolved\n })\n }\n }\n return undefined\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EnvelopePrecheck.js","sourceRoot":"","sources":["../../../src/internal/EnvelopePrecheck.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The hermetic pre-check: a lexical scan of shell text for the paths it names,\n * and the refusal when one of them falls outside the declared envelope.\n *\n * The scan is fail-closed over explicit path tokens and nothing more. It\n * cannot prove confinement — a shell can name a path no lexer sees, through a\n * variable, a substitution, or a program that opens a file of its own — so a\n * kernel or host sandbox must eventually enforce and report the complete read\n * and write sets.\n *\n * It lives beside the flow rather than inside it because it is its own\n * concept, with its own vocabulary (Access, PathReference) and its own\n * security history: three independent bypasses, each sufficient on its own to\n * defeat it, are closed here.\n *\n * @since 1.0.0\n */\n\nimport type * as Path from \"@smthrs/kernel/Path\"\nimport * as StdError from \"../StdError.ts\"\nimport { withinEnvelope } from \"./Paths.ts\"\n\n/**\n * The filesystem envelope one invocation declared.\n *\n * Structural rather than the hermetic `Bash.Input` itself: the scan needs the\n * three fields and none of the rest, and depending on the flow module would\n * make the flow and its pre-check import each other.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Envelope {\n readonly cwd?: string | undefined\n readonly reads: ReadonlyArray<string>\n readonly writes: ReadonlyArray<string>\n}\n\n/**\n * How a command touches a path it names.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Access = \"read\" | \"write\"\n\n/**\n * One path a command names, and how it touches it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface PathReference {\n readonly access: Access\n readonly value: string\n}\n\nconst commandSeparators = new Set([\"&&\", \"||\", \";\", \";;\", \";&\", \";;&\", \"|\", \"|&\", \"&\", \"\\n\", \"(\", \")\"])\nconst writeRedirections = new Set([\">\", \">>\", \">|\", \"&>\", \"&>>\", \">&\", \"<>\"])\nconst readRedirections = new Set([\"<\", \"<&\"])\n// A here-document delimiter and a here-string are text, not file names.\nconst textRedirections = new Set([\"<<\", \"<<-\", \"<<<\"])\nconst isRedirection = (token: Token): boolean =>\n token.operator &&\n (writeRedirections.has(token.text) || readRedirections.has(token.text) || textRedirections.has(token.text))\n// Longest first, so `&>>` is not read as `&` then `>>`.\nconst operators = [\n \";;&\",\n \"&>>\",\n \"<<<\",\n \"<<-\",\n \";;\",\n \";&\",\n \"&&\",\n \"||\",\n \"|&\",\n \"&>\",\n \">>\",\n \">|\",\n \">&\",\n \"<>\",\n \"<<\",\n \"<&\",\n \"\\n\",\n \";\",\n \"|\",\n \"&\",\n \"(\",\n \")\",\n \">\",\n \"<\"\n]\n// Reserved words and grouping braces open or close a compound command; the\n// command they wrap stands after them.\nconst reservedWords = new Set([\n \"!\",\n \"{\",\n \"}\",\n \"case\",\n \"do\",\n \"done\",\n \"elif\",\n \"else\",\n \"esac\",\n \"fi\",\n \"for\",\n \"function\",\n \"if\",\n \"in\",\n \"select\",\n \"then\",\n \"until\",\n \"while\"\n])\nconst writeCommands = new Set([\"chmod\", \"chown\", \"mkdir\", \"rm\", \"rmdir\", \"tee\", \"touch\", \"truncate\", \"unlink\"])\nconst destinationCommands = new Set([\"cp\", \"install\", \"mv\"])\nconst prefixWrappers = new Set([\"env\", \"nice\", \"time\", \"sudo\", \"nohup\", \"xargs\", \"command\", \"exec\"])\n// A shell runs the argument of its `-c` flag as shell text of its own.\nconst shells = new Set([\"ash\", \"bash\", \"dash\", \"fish\", \"ksh\", \"mksh\", \"sh\", \"zsh\"])\nconst commandFlag = /^-[A-Za-z]*c[A-Za-z]*$/\n\n/**\n * One lexical unit of shell text: an operator, or a word with its quotes\n * removed. A command substitution inside a word stands as `$()` in the word;\n * its body is scanned as a command of its own.\n */\ninterface Token {\n readonly operator: boolean\n readonly text: string\n}\n\ninterface Lexed {\n readonly tokens: ReadonlyArray<Token>\n readonly substitutions: ReadonlyArray<string>\n}\n\n// The end of a `$(` body that starts at `from`: the index of its closing\n// parenthesis, skipping quoted spans and nested parentheses. An unclosed body\n// runs to the end of the text, which is how a fail-closed scan must read it.\n// Substitutions and quotes nest without limit in a shell, but each level\n// costs this scan a pass over the rest of the text and a stack frame. Fifty\n// thousand `$(` used to run for half a minute and then overflow the stack, so\n// past this depth the text is refused rather than scanned.\nconst maxNesting = 32\n\nconst tooDeep = (): StdError.StdError =>\n new StdError.StdError({\n code: \"invalid_input\",\n message: `Command nests substitutions or quotes deeper than ${maxNesting} levels`\n })\n\nconst substitutionEnd = (text: string, from: number, nesting: number): number => {\n if (nesting > maxNesting) throw tooDeep()\n let depth = 1\n let index = from\n while (index < text.length) {\n const char = text[index]!\n if (char === \"\\\\\") {\n index += 2\n } else if (char === \"'\") {\n const close = text.indexOf(\"'\", index + 1)\n index = close === -1 ? text.length : close + 1\n } else if (char === \"\\\"\") {\n index = doubleQuoteEnd(text, index + 1, nesting + 1) + 1\n } else if (char === \"`\") {\n index = backtickEnd(text, index + 1) + 1\n } else {\n if (char === \"(\") depth++\n if (char === \")\" && --depth === 0) return index\n index++\n }\n }\n return text.length\n}\n\nconst backtickEnd = (text: string, from: number): number => {\n let index = from\n while (index < text.length && text[index] !== \"`\") index += text[index] === \"\\\\\" ? 2 : 1\n return Math.min(index, text.length)\n}\n\nconst doubleQuoteEnd = (text: string, from: number, nesting: number): number => {\n if (nesting > maxNesting) throw tooDeep()\n let index = from\n while (index < text.length && text[index] !== \"\\\"\") {\n const char = text[index]!\n if (char === \"\\\\\") index += 2\n else if (char === \"`\") index = backtickEnd(text, index + 1) + 1\n else if (char === \"$\" && text[index + 1] === \"(\") index = substitutionEnd(text, index + 2, nesting + 1) + 1\n else index++\n }\n return Math.min(index, text.length)\n}\n\n// A hand-rolled lexer rather than one regular expression: a word is any run\n// of bare characters, quoted spans, and substitutions, and a substitution can\n// hold quotes and operators of its own. Splitting on operator characters\n// alone let `echo \"$(rm /work/target)\"` hide a delete inside one quoted word,\n// read `>|` as a pipe that turned the target into the next command's name,\n// and read `(rm /work/target)` as a command named `(rm`.\n//\n// Quotes are removed from a word's text, because `\"/outs\"ide/x` names\n// `/outside/x`; keeping the quotes made it a relative path under the\n// working directory.\n// Sticky, so the match starts at `lastIndex` without copying the rest of the\n// text for every word.\nconst descriptorPattern = /\\d+(?=[<>])/y\n\nconst lex = (command: string, nesting: number): Lexed => {\n const tokens: Array<Token> = []\n const substitutions: Array<string> = []\n let index = 0\n const substitution = (from: number, to: number): string => {\n substitutions.push(command.slice(from, to))\n return \"$()\"\n }\n while (index < command.length) {\n const char = command[index]!\n // Every blank the word loop stops at must be skipped here, or the lexer\n // would push an empty word without advancing.\n if (char !== \"\\n\" && /\\s/.test(char)) {\n index++\n continue\n }\n if (char === \"\\\\\" && command[index + 1] === \"\\n\") {\n index += 2\n continue\n }\n if (char === \"#\") {\n while (index < command.length && command[index] !== \"\\n\") index++\n continue\n }\n // A file-descriptor number belongs to the redirection after it: in\n // `2>/dev/null rm /work/target` the command is `rm`, not `2`.\n descriptorPattern.lastIndex = index\n const descriptor = descriptorPattern.exec(command)\n if (descriptor !== null) {\n index += descriptor[0].length\n continue\n }\n const operator = operators.find((candidate) => command.startsWith(candidate, index))\n if (operator !== undefined) {\n tokens.push({ operator: true, text: operator })\n index += operator.length\n continue\n }\n let text = \"\"\n while (index < command.length) {\n const next = command[index]!\n if (/[\\s<>;|&()]/.test(next)) break\n if (next === \"\\\\\") {\n // A backslash escapes shell syntax. Before any other character it is\n // kept, so `C:\\work\\input` stays the path it names on Windows rather\n // than collapsing to a word the scan cannot see as a path.\n const escaped = command[index + 1] ?? \"\"\n if (escaped !== \"\\n\") text += /[\\s<>;|&()'\"`$\\\\#]/.test(escaped) ? escaped : `\\\\${escaped}`\n index += 2\n } else if (next === \"'\") {\n const close = command.indexOf(\"'\", index + 1)\n const end = close === -1 ? command.length : close\n text += command.slice(index + 1, end)\n index = end + 1\n } else if (next === \"\\\"\") {\n const end = doubleQuoteEnd(command, index + 1, nesting + 1)\n let inner = index + 1\n while (inner < end) {\n const quoted = command[inner]!\n if (quoted === \"\\\\\" && /[$`\"\\\\\\n]/.test(command[inner + 1] ?? \"\")) {\n if (command[inner + 1] !== \"\\n\") text += command[inner + 1]\n inner += 2\n } else if (quoted === \"`\") {\n const close = backtickEnd(command, inner + 1)\n text += substitution(inner + 1, close)\n inner = close + 1\n } else if (quoted === \"$\" && command[inner + 1] === \"(\") {\n const close = substitutionEnd(command, inner + 2, nesting + 2)\n text += substitution(inner + 2, close)\n inner = close + 1\n } else {\n text += quoted\n inner++\n }\n }\n index = end + 1\n } else if (next === \"`\") {\n const close = backtickEnd(command, index + 1)\n text += substitution(index + 1, close)\n index = close + 1\n } else if (next === \"$\" && command[index + 1] === \"(\") {\n const close = substitutionEnd(command, index + 2, nesting + 1)\n text += substitution(index + 2, close)\n index = close + 1\n } else {\n text += next\n index++\n }\n }\n tokens.push({ operator: false, text })\n }\n return { tokens, substitutions }\n}\n\n// A parenthesis is an operator to the lexer, so one left in a word was quoted\n// and is part of the name; only a trailing comma is punctuation.\nconst pathValue = (token: string): string => token.replace(/,+$/, \"\")\n\nconst isPathToken = (path: Path.Path, token: string): boolean =>\n token === \".\" ||\n token === \"..\" ||\n token.startsWith(\"./\") ||\n token.startsWith(\"../\") ||\n path.isAbsolute(token) ||\n /^[A-Za-z]:[\\\\/]/.test(token) ||\n token.includes(\"/\")\n\nconst segmentReferences = (\n path: Path.Path,\n tokens: ReadonlyArray<Token>,\n nesting: number\n): ReadonlyArray<PathReference> => {\n // The word after a redirection operator is its target, never the command.\n const isTarget = (index: number): boolean => index > 0 && isRedirection(tokens[index - 1]!)\n const isWord = (index: number): boolean => !tokens[index]!.operator && !isTarget(index)\n const name = (index: number): string => path.basename(pathValue(tokens[index]!.text))\n const hasWrapper = tokens.some((_, index) => isWord(index) && prefixWrappers.has(name(index)))\n let commandIndex = tokens.findIndex((token, index) =>\n isWord(index) &&\n !token.text.includes(\"=\") &&\n !prefixWrappers.has(name(index)) &&\n !reservedWords.has(token.text) &&\n !token.text.startsWith(\"-\") &&\n token.text !== \"\"\n )\n if (hasWrapper) {\n // Wrapper options can carry their own values. Prefer a later known\n // mutating command so an option value cannot lend it a read classification.\n const mutatingIndex = tokens.findIndex((_, index) =>\n isWord(index) && (writeCommands.has(name(index)) || destinationCommands.has(name(index)))\n )\n if (mutatingIndex !== -1) commandIndex = mutatingIndex\n }\n\n const command = commandIndex === -1 ? \"\" : name(commandIndex)\n // Shell text handed to `eval` or to a shell's `-c` flag is a command, not a\n // path: reading `sh -c 'rm /work/target'` as one quoted word let a delete\n // pass as a read of a file named `rm /work/target`.\n const scripts = new Set<number>()\n if (command === \"eval\") {\n for (let index = commandIndex + 1; index < tokens.length; index++) if (isWord(index)) scripts.add(index)\n } else if (shells.has(command)) {\n const flag = tokens.findIndex((token, index) =>\n index > commandIndex && isWord(index) && commandFlag.test(token.text)\n )\n if (flag !== -1 && flag + 1 < tokens.length && isWord(flag + 1)) scripts.add(flag + 1)\n }\n const candidates: Array<{ readonly index: number; readonly value: string }> = []\n for (let index = 0; index < tokens.length; index++) {\n const token = tokens[index]!\n if (index === commandIndex || scripts.has(index) || token.operator) continue\n if (!isTarget(index) && token.text.startsWith(\"-\")) continue\n if (isTarget(index) && textRedirections.has(tokens[index - 1]!.text)) continue\n const value = pathValue(token.text)\n if (value !== \"\" && isPathToken(path, value)) candidates.push({ index, value })\n }\n\n const destinationIndex = destinationCommands.has(command)\n ? candidates.filter(({ index }) => !isTarget(index)).at(-1)?.index\n : undefined\n\n const script = [...scripts].map((index) => tokens[index]!.text).join(\" \")\n const nested = script === \"\" ? [] : references(path, script, nesting + 1)\n const named = candidates.map(({ index, value }): PathReference => {\n const access: Access = isTarget(index)\n ? writeRedirections.has(tokens[index - 1]!.text) ? \"write\" : \"read\"\n : writeCommands.has(command) || destinationIndex === index\n ? \"write\"\n : \"read\"\n return { access, value }\n })\n return [...named, ...nested]\n}\n\nconst references = (path: Path.Path, command: string, nesting: number): ReadonlyArray<PathReference> => {\n if (nesting > maxNesting) throw tooDeep()\n const { substitutions, tokens } = lex(command, nesting)\n const found: Array<PathReference> = []\n let segment: Array<Token> = []\n for (const token of tokens) {\n if (token.operator && commandSeparators.has(token.text)) {\n found.push(...segmentReferences(path, segment, nesting))\n segment = []\n } else {\n segment.push(token)\n }\n }\n found.push(...segmentReferences(path, segment, nesting))\n // A substitution runs its body as a command; its paths count like any other.\n for (const body of substitutions) found.push(...references(path, body, nesting + 1))\n return found\n}\n\n/**\n * Every path token the shell text names, one segment at a time.\n *\n * @throws {StdError.StdError} `invalid_input` when substitutions or quotes nest\n * deeper than the scan reads.\n * @category pre-check\n * @since 1.0.0\n */\nexport const commandReferences = (path: Path.Path, command: string): ReadonlyArray<PathReference> =>\n references(path, command, 0)\n\nconst resolvePath = (\n path: Path.Path,\n cwd: string,\n value: string\n): string => path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value)\n\nconst isDeclared = (\n path: Path.Path,\n cwd: string,\n declared: ReadonlyArray<string>,\n source: string\n): boolean =>\n withinEnvelope(\n declared.map((entry) => resolvePath(path, cwd, entry)),\n resolvePath(path, cwd, source)\n )\n\n/**\n * The refusal shell text earns by naming a path outside its declared\n * envelope, or `undefined` when every path it names is declared.\n *\n * @category pre-check\n * @since 1.0.0\n */\nexport const outsideEnvelope = (\n input: Envelope,\n text: string,\n path: Path.Path\n): StdError.StdError | undefined => {\n const base = path.resolve(\".\")\n const cwd = path.resolve(input.cwd ?? \".\")\n // Passing the default explicitly is not a declaration the caller owes. A\n // working directory is where the command runs, not a file it reads, and\n // `cwd: \".\"` names exactly what omitting `cwd` names — so refusing it asked\n // the caller to list the workspace root among its reads to say nothing at\n // all. Agents hit this constantly: one SWE-bench run spent ten of its\n // forty-eight tool calls re-issuing the same command after\n // \"Working directory is outside declared reads: <the workspace root>\". A\n // cwd that points somewhere else is still declared or refused.\n if (input.cwd !== undefined && cwd !== base && !isDeclared(path, cwd, input.reads, cwd)) {\n return new StdError.StdError({\n code: \"outside_declared_reads\",\n message: `Working directory is outside declared reads: ${cwd}`,\n path: cwd\n })\n }\n\n // The Shell contract currently has no sandbox or access-reporting surface.\n // This lexical scan is intentionally only a fail-closed pre-check for\n // explicit path tokens. It cannot prove confinement; a kernel/host sandbox\n // must eventually enforce and report the complete read and write sets.\n let named: ReadonlyArray<PathReference>\n try {\n named = commandReferences(path, text)\n } catch {\n // The depth bound is the one failure the scan raises, and a text the scan\n // cannot read is refused whatever the reason, never run unchecked.\n return tooDeep()\n }\n for (const reference of named) {\n const resolved = resolvePath(path, cwd, reference.value)\n // /dev/* is the process plumbing every shell one-liner leans on:\n // `2>/dev/null`, `cat /dev/stdin`. Normalize first so a dot-dot segment\n // cannot disguise an ordinary filesystem path as process plumbing.\n if (resolved.startsWith(\"/dev/\")) continue\n const declared = reference.access === \"write\" ? input.writes : input.reads\n if (!isDeclared(path, cwd, declared, resolved)) {\n const code = reference.access === \"write\" ? \"outside_declared_writes\" : \"outside_declared_reads\"\n return new StdError.StdError({\n code,\n message: `Command path is outside declared ${reference.access}s: ${resolved}`,\n path: resolved\n })\n }\n }\n return undefined\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/api.md
CHANGED
|
@@ -180,10 +180,14 @@ resolves its own services when it is built.
|
|
|
180
180
|
The in-process peer. Walks the injected `FileSystem`, so it needs no external
|
|
181
181
|
binary and is browser-safe.
|
|
182
182
|
|
|
183
|
-
| Export
|
|
184
|
-
|
|
|
185
|
-
| `make`
|
|
186
|
-
| `layer`
|
|
183
|
+
| Export | Type |
|
|
184
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
185
|
+
| `make` | `(services: Context<FileSystem \| Path>) => Search.Search` |
|
|
186
|
+
| `layer` | `Layer<Search.Search, never, FileSystem \| Path>` |
|
|
187
|
+
| `searched` | `(input: Pick<GrepInput, "root" \| "hidden" \| "noIgnore" \| "globs">) => Effect<ReadonlyArray<string>, StdError, FileSystem \| Path>` |
|
|
188
|
+
|
|
189
|
+
`searched` names the files `grep` reads for an input, in the order it reads
|
|
190
|
+
them; `RemoteSearch` fetches exactly those.
|
|
187
191
|
|
|
188
192
|
## NativeSearch
|
|
189
193
|
|
|
@@ -201,6 +205,26 @@ start `rg` is `provider_unavailable`. The default layer uses the host environmen
|
|
|
201
205
|
allowlist. `make` accepts explicit environment declarations as its second
|
|
202
206
|
argument; those names override the allowlist.
|
|
203
207
|
|
|
208
|
+
## RemoteSearch
|
|
209
|
+
|
|
210
|
+
The peer for a tree on another machine, such as a container or a sandbox
|
|
211
|
+
machine. It lists the root with the machine's `find`, brings back its
|
|
212
|
+
`.gitignore` files and then the files the search reads with `tar`, and lets
|
|
213
|
+
`PortableSearch` answer over that copy, so every result, notice, and refusal is
|
|
214
|
+
the in-process peer's. Each search is two round trips through the machine's
|
|
215
|
+
spawner, whatever the tree's size.
|
|
216
|
+
|
|
217
|
+
| Export | Type | Meaning |
|
|
218
|
+
| ---------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
219
|
+
| `maxBytes` | `268_435_456` | The most file content one search brings back; past it, `response_too_large`. |
|
|
220
|
+
| `make` | `(services: Context<ChildProcessSpawner \| Path>, options?: { maxBytes?: number }) => Search.Search` | The spawner runs on the machine; `Path` resolves a relative root there. |
|
|
221
|
+
|
|
222
|
+
The machine needs `sh`, `find`, `xargs`, `tar`, `head`, and `base64`; GNU,
|
|
223
|
+
BusyBox, and BSD userlands all have them, and a machine without one refuses
|
|
224
|
+
with `command_failed` naming it. The program goes in on standard input and
|
|
225
|
+
the answer comes back as base64, so a transport whose argv is bounded or whose
|
|
226
|
+
output channel carries only text answers intact.
|
|
227
|
+
|
|
204
228
|
## SearchContract
|
|
205
229
|
|
|
206
230
|
The validation and matching rules both peers build on, exported so a third peer
|
|
@@ -112,7 +112,9 @@ handler checks the command against them before spawning anything. Read what the
|
|
|
112
112
|
check does, because the name promises more than it delivers.
|
|
113
113
|
|
|
114
114
|
The check is **lexical**. It tokenizes the command line, keeping quoted spans
|
|
115
|
-
whole, treats each physical line as its own command,
|
|
115
|
+
whole, treats each physical line as its own command, reads the body of a
|
|
116
|
+
command substitution, the argument of a shell's `-c` flag, and the arguments of
|
|
117
|
+
`eval` as commands of their own, classifies each path token
|
|
116
118
|
as a read or a write from the command that governs it, resolves both the token
|
|
117
119
|
and every declaration to a canonical absolute path, and refuses the call with
|
|
118
120
|
`outside_declared_reads` or `outside_declared_writes` when one falls outside.
|
|
@@ -36,7 +36,9 @@ const host = Layer.mergeAll(
|
|
|
36
36
|
|
|
37
37
|
Swap `NativeSearch.layer` for `PortableSearch.layer` to search in process with
|
|
38
38
|
no external binary. Both satisfy the same `Search` service, and nothing above
|
|
39
|
-
the layer changes.
|
|
39
|
+
the layer changes. When the files live on another machine, such as a container
|
|
40
|
+
or a sandbox machine, `RemoteSearch.make` over that machine's spawner and `Path`
|
|
41
|
+
answers in two round trips where the portable peer would ask once per file.
|
|
40
42
|
|
|
41
43
|
A host that cannot serve a service binds its refusal layer instead of leaving it
|
|
42
44
|
out, so the failure names the missing service rather than a missing context:
|
|
@@ -91,7 +91,10 @@ tracked in [#1790](https://github.com/smithersai/smithers/issues/1790).
|
|
|
91
91
|
A host sealed to one container is the exception. `SMITHERS_BASH_CONTAINER` names
|
|
92
92
|
the container, and `bash` and `test` then run in it through the `docker` CLI.
|
|
93
93
|
Every other container, and a call that names none, fails with
|
|
94
|
-
`outside_container`. The
|
|
94
|
+
`outside_container`. The file flows (`read`, `edit`, `apply_patch`, `grep` and
|
|
95
|
+
the rest) act on the container's files through the same transport, resolving a
|
|
96
|
+
relative path under `SMITHERS_TEST_CWD`, and `grep` and `glob` answer through
|
|
97
|
+
`RemoteSearch`. The transport process runs outside the OS sandbox, which
|
|
95
98
|
denies the daemon's socket, and receives the host's `DOCKER_HOST`,
|
|
96
99
|
`DOCKER_CONTEXT`, `DOCKER_CONFIG`, `DOCKER_TLS_VERIFY`, and `DOCKER_CERT_PATH`,
|
|
97
100
|
so it reaches a rootless daemon.
|