@smthrs/std 1.0.0-rc.3 → 1.0.0-rc.4
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/README.md +35 -35
- package/dist/cjs/Bash.d.ts +76 -0
- package/dist/cjs/Bash.d.ts.map +1 -1
- package/dist/cjs/Bash.js +20 -9
- package/dist/cjs/Bash.js.map +2 -2
- package/dist/cjs/internal/GitWorktree.d.ts +11 -1
- package/dist/cjs/internal/GitWorktree.d.ts.map +1 -1
- package/dist/cjs/internal/GitWorktree.js +7 -1
- package/dist/cjs/internal/GitWorktree.js.map +2 -2
- package/dist/esm/Bash.d.ts +76 -0
- package/dist/esm/Bash.d.ts.map +1 -1
- package/dist/esm/Bash.js +46 -10
- package/dist/esm/Bash.js.map +1 -1
- package/dist/esm/internal/GitWorktree.d.ts +11 -1
- package/dist/esm/internal/GitWorktree.d.ts.map +1 -1
- package/dist/esm/internal/GitWorktree.js +8 -1
- package/dist/esm/internal/GitWorktree.js.map +1 -1
- package/docs/concepts/flows-and-handlers.md +2 -2
- package/package.json +6 -6
- package/src/Bash.ts +67 -9
- package/src/internal/GitWorktree.ts +20 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/internal/GitWorktree.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Detached checkouts shared by checkpoint reads and baseline test runs.\n *\n * ## Host git never reads a file the agent can write that names a program\n *\n * The repository is the workspace's, and an agent can write its `.git`: a\n * containerised agent writes it through the bind mount. Git reads programs out\n * of that directory in more places than any list of `-c` overrides can close.\n * Hooks and `core.fsmonitor` can be overridden, but a filter driver\n * (`filter.<name>.clean`/`.smudge` in `.git/config`, selected by\n * `.git/info/attributes`) cannot: git has no switch that ignores repository\n * config or `$GIT_DIR/info/attributes`, and overriding each driver by name\n * races an agent that keeps rewriting the file.\n *\n * So the git that records and checks out trees runs against a **shadow**\n * `GIT_DIR`: a bare repository this module creates under the host's temporary\n * directory for one operation and deletes afterwards. Its config, hooks and\n * `info/attributes` are the ones `git init --template=` writes, which is none.\n * `GIT_CONFIG_NOSYSTEM` and `GIT_CONFIG_GLOBAL=/dev/null` drop the host's own\n * config files too, so the only settings are the ones on the command line.\n * `GIT_OBJECT_DIRECTORY` points at the workspace's object store, because objects\n * are data: git hashes and inflates them and never runs anything they say.\n *\n * What still touches the workspace repository is plain data:\n *\n * - `rev-parse` in the workspace, which resolves refs and paths and runs no\n * hook, filter, fsmonitor or other configured program;\n * - `git config --file <workspace config>`, which reads or writes that one file\n * and follows no `include`;\n * - a copy of the workspace index into the shadow, so the recorded tree holds\n * what the agent staged without the harness writing the agent's index; and\n * - a handful of `core.*` settings that change how bytes are hashed\n * ({@link dataSettings}), each accepted only as a bare word.\n *\n * A materialized checkout gets its own standalone `.git`, written by this\n * module as files, so a container can still run git in it: `HEAD` names the\n * commit, `objects/info/alternates` points back at the workspace's objects by a\n * relative path, and `index` is the one the checkout was written from. Host git\n * never uses that directory; it is the container's.\n *\n * @since 1.0.0\n */\n\nimport type { ChildProcessSpawner } from \"@smthrs/kernel/ChildProcessSpawner\"\nimport { Effect } from \"effect\"\nimport * as StdError from \"../StdError.ts\"\nimport * as Exec from \"./Exec.ts\"\n\nconst failed = (message: string, code: StdError.Code = \"command_failed\") => new StdError.StdError({ code, message })\n\n/**\n * Settings that stop the workspace repository from naming a program for the\n * `rev-parse` this module runs in it. `rev-parse` runs none of them; these keep\n * that true if a later call is added beside it.\n */\nconst hostGitSettings = [\"-c\", \"core.hooksPath=/dev/null\", \"-c\", \"core.fsmonitor=false\"] as const\n\n/** Host config files git would otherwise read, removed from every shadow command. */\nconst isolated = { GIT_CONFIG_NOSYSTEM: \"1\", GIT_CONFIG_GLOBAL: \"/dev/null\" } as const\n\n/**\n * Workspace settings that change how a tree is hashed or compared, carried\n * onto the shadow so a checkpoint records what the agent's own git would.\n * None of them names a program.\n */\nconst dataSettings = /^core\\.(autocrlf|eol|safecrlf|filemode|symlinks|ignorecase|precomposeunicode)$/\n\n/** A setting value this module will repeat to git: a bare word, never a command. */\nconst bareWord = /^[A-Za-z0-9_-]+$/\n\nconst run = (command: string, args: ReadonlyArray<string>, env?: Readonly<Record<string, string>>) =>\n Exec.exec(command, { args, ...(env === undefined ? {} : { env }) }).pipe(\n Effect.mapError((error) => failed(`${command} could not run: ${error.message}`))\n )\n\n/**\n * Runs host git in the workspace at `root`, for `rev-parse` only.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst git = (root: string, args: ReadonlyArray<string>) => run(\"git\", [...hostGitSettings, \"-C\", root, ...args])\n\nconst resolveCommit = (root: string, refs: ReadonlyArray<string>) =>\n Effect.gen(function*() {\n for (const ref of refs) {\n const answer = yield* git(root, [\"rev-parse\", \"--verify\", \"--quiet\", `${ref}^{commit}`])\n const commit = answer.stdout.trim()\n if (answer.exitCode === 0 && commit !== \"\") return { ref, commit }\n }\n return yield* Effect.fail(failed(`No commit resolves from ${refs.join(\" or \")} in ${root}.`, \"not_found\"))\n })\n\n/**\n * Where one workspace repository keeps what the shadow needs, as its own git\n * reports it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Repository {\n /** The root the caller named, without a trailing slash. */\n readonly root: string\n /** The same directory, as git canonicalizes it. */\n readonly toplevel: string\n /** The common git directory, which holds config and objects. */\n readonly common: string\n /** The workspace index. */\n readonly index: string\n readonly objectFormat: string\n /** {@link dataSettings} as `-c` arguments. */\n readonly settings: ReadonlyArray<string>\n}\n\n/**\n * Reads or writes the workspace's config file as a file, never as a repository.\n *\n * `--git-dir=/dev/null` stops git discovering any repository around the host's\n * working directory, and `--file` reads the one file without `include`s.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst config = (repository: Pick<Repository, \"common\">, args: ReadonlyArray<string>) =>\n run(\"git\", [\"--git-dir=/dev/null\", \"config\", \"--file\", `${repository.common}/config`, ...args], isolated)\n\nconst open = (root: string): Effect.Effect<Repository, StdError.StdError, ChildProcessSpawner> =>\n Effect.gen(function*() {\n const stripped = root.replace(/\\/+$/, \"\")\n const answer = yield* git(stripped, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--show-toplevel\",\n \"--git-common-dir\",\n \"--git-path\",\n \"index\",\n \"--show-object-format\"\n ])\n const [toplevel, common, index, objectFormat] = answer.stdout.split(\"\\n\").map((line) => line.trim())\n if (\n answer.exitCode !== 0 || toplevel === undefined || toplevel === \"\" || common === undefined || common === \"\" ||\n index === undefined || index === \"\" || objectFormat === undefined || !bareWord.test(objectFormat)\n ) {\n return yield* Effect.fail(failed(`${stripped} is not a git work tree: ${answer.stderr.trim()}`))\n }\n const found = yield* config({ common }, [\"--get-regexp\", dataSettings.source])\n const settings = found.exitCode === 0\n ? found.stdout.split(\"\\n\").flatMap((line) => {\n const [key, value] = line.trim().split(/\\s+/)\n return key !== undefined && value !== undefined && dataSettings.test(key) && bareWord.test(value)\n ? [\"-c\", `${key}=${value}`]\n : []\n })\n : []\n return { root: stripped, toplevel, common, index, objectFormat, settings }\n })\n\nconst temporaryRoot = (): string => {\n const configured = globalThis.process?.env?.TMPDIR?.replace(/\\/+$/, \"\")\n return configured === undefined || configured === \"\" ? \"/tmp\" : configured\n}\n\nconst remove = (path: string) =>\n run(\"rm\", [\"-rf\", \"--\", path]).pipe(\n Effect.flatMap((removed) => removed.exitCode === 0 ? Effect.void : Effect.fail(failed(removed.stderr.trim())))\n )\n\n/**\n * A harness-owned `GIT_DIR` over one workspace's objects.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Shadow {\n readonly path: string\n readonly repository: Repository\n /** Runs host git with the shadow as `GIT_DIR`, and `workTree` as its work tree when given. */\n readonly git: (\n args: ReadonlyArray<string>,\n workTree?: string\n ) => Effect.Effect<Exec.ExecResult, StdError.StdError, ChildProcessSpawner>\n /** Copies the workspace index into the shadow, so the shadow never writes the agent's own. */\n readonly adoptIndex: Effect.Effect<void, StdError.StdError, ChildProcessSpawner>\n}\n\nconst shadowOf = (repository: Repository, path: string): Shadow => ({\n path,\n repository,\n git: (args, workTree) =>\n run(\n \"git\",\n [\n `--git-dir=${path}`,\n ...(workTree === undefined ? [] : [`--work-tree=${workTree}`]),\n ...hostGitSettings,\n ...repository.settings,\n ...args\n ],\n { ...isolated, GIT_OBJECT_DIRECTORY: `${repository.common}/objects` }\n ),\n adoptIndex: run(\"sh\", [\n \"-c\",\n \"set -C; if [ -e \\\"$1\\\" ]; then cat -- \\\"$1\\\" > \\\"$2\\\"; fi\",\n \"sh\",\n repository.index,\n `${path}/index`\n ]).pipe(\n Effect.flatMap((copied) =>\n copied.exitCode === 0\n ? Effect.void\n : Effect.fail(failed(`Could not read the workspace index: ${copied.stderr.trim()}`))\n )\n )\n})\n\n/**\n * Runs `use` with a fresh {@link Shadow} over the repository at `root`, and\n * deletes the shadow however `use` ends.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst withShadow = <A, E, R>(\n root: string | Repository,\n use: (shadow: Shadow) => Effect.Effect<A, E, R>\n): Effect.Effect<A, E | StdError.StdError, R | ChildProcessSpawner> =>\n Effect.gen(function*() {\n const repository = typeof root === \"string\" ? yield* open(root) : root\n const path = `${temporaryRoot()}/smithers-git-${globalThis.crypto.randomUUID()}`\n return yield* Effect.acquireUseRelease(\n Effect.succeed(path),\n () =>\n Effect.gen(function*() {\n const created = yield* run(\n \"git\",\n [\"init\", \"--quiet\", \"--bare\", \"--template=\", `--object-format=${repository.objectFormat}`, path],\n isolated\n )\n if (created.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not create a shadow repository: ${created.stderr.trim()}`))\n }\n return yield* use(shadowOf(repository, path))\n }),\n () =>\n remove(path).pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"could not remove a shadow repository\").pipe(\n Effect.annotateLogs({ path, reason: error.message })\n )\n )\n )\n )\n })\n\n/** `to` relative to `from`, both absolute POSIX paths. */\nconst relative = (from: string, to: string): string => {\n const source = from.split(\"/\").filter((part) => part !== \"\")\n const target = to.split(\"/\").filter((part) => part !== \"\")\n let shared = 0\n while (shared < source.length && shared < target.length && source[shared] === target[shared]) shared++\n return [...source.slice(shared).map(() => \"..\"), ...target.slice(shared)].join(\"/\") || \".\"\n}\n\n/**\n * The config of a checkout's own `.git`, for the container's git.\n *\n * Only {@link dataSettings}, already restricted to bare words, reach it.\n */\nconst checkoutConfig = (repository: Repository): string => {\n const sha1 = repository.objectFormat === \"sha1\"\n const lines = [\n \"[core]\",\n `\\trepositoryformatversion = ${sha1 ? 0 : 1}`,\n \"\\tbare = false\",\n ...repository.settings.filter((_, index) => index % 2 === 1).map((setting) => {\n const [key, value] = setting.split(\"=\")\n return `\\t${key?.slice(\"core.\".length)} = ${value}`\n }),\n ...(sha1 ? [] : [\"[extensions]\", `\\tobjectformat = ${repository.objectFormat}`])\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n\n/** Fails with `message` and the command's stderr unless it exited 0. */\nconst succeeded = (result: Exec.ExecResult, message: string) =>\n result.exitCode === 0 ? Effect.void : Effect.fail(failed(`${message}: ${result.stderr.trim()}`))\n\n/**\n * Measures which staging directory shares the filesystem of the checkout's\n * parent `$1`: the shadow `$2`, the workspace root's parent `$3`, or the host\n * cache directory `$4`. Prints one line each for the parent, the shadow, the\n * root's parent (`unwritable` when the host user cannot create a stage there)\n * and the cache (empty unless both others missed and it could be created).\n * `stat -c` is GNU and BusyBox, `stat -f` is BSD; an empty line means neither\n * answered.\n */\nconst measure = [\n \"set -e\",\n \"mkdir -p -- \\\"$1\\\"\",\n \"dev() { stat -c %d -- \\\"$1\\\" 2>/dev/null || stat -f %d -- \\\"$1\\\" 2>/dev/null || echo; }\",\n \"t=$(dev \\\"$1\\\")\",\n \"s=$(dev \\\"$2\\\")\",\n \"if [ -w \\\"$3\\\" ]; then b=$(dev \\\"$3\\\"); else b=unwritable; fi\",\n \"c=\",\n \"if [ -n \\\"$t\\\" ] && [ \\\"$t\\\" != \\\"$s\\\" ] && [ \\\"$t\\\" != \\\"$b\\\" ] && [ -n \\\"$4\\\" ] && mkdir -p -m 700 -- \\\"$4\\\" 2>/dev/null; then c=$(dev \\\"$4\\\"); fi\",\n \"printf '%s\\\\n' \\\"$t\\\" \\\"$s\\\" \\\"$b\\\" \\\"$c\\\"\"\n].join(\"; \")\n\n/**\n * The host user's cache directory for checkout stages: `$XDG_CACHE_HOME`, or\n * `~/.cache`, under `smithers`. Empty when neither is an absolute path.\n */\nconst cacheRoot = (): string => {\n const env = globalThis.process?.env\n const xdg = env?.XDG_CACHE_HOME?.replace(/\\/+$/, \"\")\n if (xdg !== undefined && xdg.startsWith(\"/\")) return `${xdg}/smithers`\n const home = env?.HOME?.replace(/\\/+$/, \"\")\n return home !== undefined && home.startsWith(\"/\") ? `${home}/.cache/smithers` : \"\"\n}\n\n/**\n * Moves the staged checkout `$1` into the directory `$2` under the name `$3`\n * in one `rename`, then proves the directory there is the one that was staged.\n * The staging directory is on the same filesystem, so `mv` renames and never\n * copies.\n *\n * The shell first enters `$2` by its physical path and refuses unless that\n * lies under the real path of the workspace root `$4`. The rename then names\n * the target relative to that directory, so a parent the agent swaps for a\n * symlink, before or after the check, cannot carry the checkout out of the\n * workspace.\n */\nconst publish = [\n \"set -e\",\n \"root=$(cd -P -- \\\"$4\\\" && pwd -P)\",\n \"cd -P -- \\\"$2\\\"\",\n \"here=$(pwd -P)\",\n \"case \\\"$here\\\" in \\\"$root\\\"|\\\"$root\\\"/*) ;; *) echo \\\"$2 resolves to $here, outside the workspace $root\\\" >&2; exit 1 ;; esac\",\n \"if [ -e \\\"./$3\\\" ] || [ -L \\\"./$3\\\" ]; then echo \\\"$2/$3 already exists\\\" >&2; exit 1; fi\",\n \"staged=$(ls -di -- \\\"$1\\\" | awk '{print $1}')\",\n \"mv -- \\\"$1\\\" \\\"./$3\\\"\",\n \"if [ -L \\\"./$3\\\" ] || [ ! -d \\\"./$3\\\" ]; then echo \\\"$2/$3 was replaced while it was written\\\" >&2; exit 1; fi\",\n \"moved=$(ls -di -- \\\"./$3\\\" | awk '{print $1}')\",\n \"if [ \\\"$staged\\\" != \\\"$moved\\\" ]; then echo \\\"$2/$3 was replaced while it was written\\\" >&2; exit 1; fi\"\n].join(\"; \")\n\n/**\n * Checks `commit`'s tree out at the new directory `host`, with a standalone\n * `.git` the container's git can use.\n *\n * Host git never writes into an agent-writable directory. The tree and its\n * `.git` are written into a staging directory the agent cannot reach: inside\n * the shadow, or, when the host's temporary directory is another filesystem,\n * beside the workspace root or under the host user's cache directory. One\n * `rename` then gives the finished directory its name under the workspace. A\n * path the agent swaps for a symlink cannot redirect a write: a symlink at\n * `host` fails the call before the rename, and a parent that resolves outside\n * the workspace root fails it before the rename too.\n */\nconst checkout = (shadow: Shadow, host: string, commit: string) =>\n Effect.gen(function*() {\n const repository = shadow.repository\n const parent = host.slice(0, host.lastIndexOf(\"/\"))\n const name = host.slice(host.lastIndexOf(\"/\") + 1)\n const beside = repository.root.slice(0, repository.root.lastIndexOf(\"/\")) || \"/\"\n const cache = cacheRoot()\n const stage = `checkout-${globalThis.crypto.randomUUID()}`\n const besideStage = `${beside.replace(/\\/+$/, \"\")}/.smithers-${stage}`\n const cacheStage = `${cache}/${stage}`\n const measured = yield* run(\"sh\", [\"-c\", measure, \"sh\", parent, shadow.path, beside, cache])\n yield* succeeded(measured, `Could not check out ${commit}`)\n const [target, inShadow, besideRoot, inCache] = measured.stdout.split(\"\\n\").map((line) => line.trim())\n const scratch = target === undefined || target === \"\" || target === inShadow\n ? undefined\n : target === besideRoot\n ? besideStage\n : target === inCache\n ? cacheStage\n : undefined\n const base = target === undefined || target === \"\"\n ? undefined\n : target === inShadow\n ? shadow.path\n : scratch\n if (base === undefined) {\n const unwritable = besideRoot === \"unwritable\" ? \" (not writable by this user)\" : \"\"\n return yield* Effect.fail(\n failed(\n `Could not check out ${commit}: neither ${shadow.path} nor ${beside}${unwritable} nor ${\n cache === \"\" ? \"a cache directory\" : cache\n } is on the filesystem of ${parent}`\n )\n )\n }\n const staged = `${base}/${name}`\n const objects = relative(\n `${repository.toplevel}${host.slice(repository.root.length)}/.git/objects`,\n `${repository.common}/objects`\n )\n const write = Effect.gen(function*() {\n const prepared = yield* run(\"sh\", [\n \"-c\",\n [\n \"set -eC\",\n ...(scratch !== undefined ? [\"mkdir -m 700 -- \\\"$1\\\"\"] : []),\n \"mkdir -- \\\"$2\\\" \\\"$2/.git\\\" \\\"$2/.git/objects\\\" \\\"$2/.git/objects/info\\\" \\\"$2/.git/refs\\\"\",\n \"printf '%s\\\\n' \\\"$3\\\" > \\\"$2/.git/HEAD\\\"\",\n \"printf '%s\\\\n' \\\"$4\\\" > \\\"$2/.git/objects/info/alternates\\\"\",\n \"printf '%s' \\\"$5\\\" > \\\"$2/.git/config\\\"\"\n ].join(\"; \"),\n \"sh\",\n base,\n staged,\n commit,\n objects,\n checkoutConfig(repository)\n ])\n yield* succeeded(prepared, `Could not check out ${commit}`)\n const written = yield* shadow.git([\"read-tree\", \"--reset\", \"-u\", commit], staged)\n yield* succeeded(written, `Could not check out ${commit}`)\n const indexed = yield* run(\"sh\", [\n \"-c\",\n \"set -C; cat -- \\\"$1\\\" > \\\"$2\\\"\",\n \"sh\",\n `${shadow.path}/index`,\n `${staged}/.git/index`\n ])\n yield* succeeded(indexed, `Could not check out ${commit}`)\n const moved = yield* run(\"sh\", [\"-c\", publish, \"sh\", staged, parent, name, repository.root])\n yield* succeeded(moved, `Could not check out ${commit}`)\n })\n return yield* scratch !== undefined\n ? write.pipe(Effect.ensuring(remove(scratch).pipe(Effect.ignore)))\n : write\n })\n\nconst withDetachedWorktree = <A, E, R>(\n root: string,\n prefix: string,\n commit: string,\n use: (host: string) => Effect.Effect<A, E, R>\n): Effect.Effect<A, E | StdError.StdError, R | ChildProcessSpawner> =>\n Effect.gen(function*() {\n // Allocate on execution, including when the same Effect is run twice.\n // Never remove an existing checkout: another call may still be using it.\n const host = `${root.replace(/\\/+$/, \"\")}/${prefix}-${globalThis.crypto.randomUUID()}`\n return yield* Effect.acquireUseRelease(\n Effect.succeed(host),\n () =>\n Effect.gen(function*() {\n yield* withShadow(root, (shadow) => checkout(shadow, host, commit))\n return yield* use(host)\n }),\n // A checkout left behind is a stale copy of the repository inside the\n // workspace. Walks prune it, but the operator must still hear about it.\n () =>\n remove(host).pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"could not remove a scratch checkout\").pipe(\n Effect.annotateLogs({ path: host, reason: error.message })\n )\n )\n )\n )\n })\n\n/**\n * Internal Git operations used by both checkpoint and baseline leases.\n *\n * @category utilities\n * @since 1.0.0\n */\nexport const GitWorktree = { config, git, open, resolveCommit, withDetachedWorktree, withShadow }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Detached checkouts shared by checkpoint reads and baseline test runs.\n *\n * ## Host git never reads a file the agent can write that names a program\n *\n * The repository is the workspace's, and an agent can write its `.git`: a\n * containerised agent writes it through the bind mount. Git reads programs out\n * of that directory in more places than any list of `-c` overrides can close.\n * Hooks and `core.fsmonitor` can be overridden, but a filter driver\n * (`filter.<name>.clean`/`.smudge` in `.git/config`, selected by\n * `.git/info/attributes`) cannot: git has no switch that ignores repository\n * config or `$GIT_DIR/info/attributes`, and overriding each driver by name\n * races an agent that keeps rewriting the file.\n *\n * So the git that records and checks out trees runs against a **shadow**\n * `GIT_DIR`: a bare repository this module creates under the host's temporary\n * directory for one operation and deletes afterwards. Its config, hooks and\n * `info/attributes` are the ones `git init --template=` writes, which is none.\n * `GIT_CONFIG_NOSYSTEM` and `GIT_CONFIG_GLOBAL=/dev/null` drop the host's own\n * config files too, so the only settings are the ones on the command line.\n * `GIT_OBJECT_DIRECTORY` points at the workspace's object store, because objects\n * are data: git hashes and inflates them and never runs anything they say.\n *\n * What still touches the workspace repository is plain data:\n *\n * - `rev-parse` in the workspace, which resolves refs and paths and runs no\n * hook, filter, fsmonitor or other configured program;\n * - `git config --file <workspace config>`, which reads or writes that one file\n * and follows no `include`;\n * - a copy of the workspace index into the shadow, so the recorded tree holds\n * what the agent staged without the harness writing the agent's index; and\n * - a handful of `core.*` settings that change how bytes are hashed\n * ({@link dataSettings}), each accepted only as a bare word.\n *\n * A materialized checkout gets its own standalone `.git`, written by this\n * module as files, so a container can still run git in it: `HEAD` names the\n * commit, `objects/info/alternates` points back at the workspace's objects by a\n * relative path, and `index` is the one the checkout was written from. Host git\n * never uses that directory; it is the container's.\n *\n * @since 1.0.0\n */\n\nimport type { ChildProcessSpawner } from \"@smthrs/kernel/ChildProcessSpawner\"\nimport { Effect } from \"effect\"\nimport * as StdError from \"../StdError.ts\"\nimport * as Exec from \"./Exec.ts\"\n\nconst failed = (message: string, code: StdError.Code = \"command_failed\") => new StdError.StdError({ code, message })\n\n/**\n * Settings that stop the workspace repository from naming a program for the\n * `rev-parse` this module runs in it. `rev-parse` runs none of them; these keep\n * that true if a later call is added beside it.\n */\nconst hostGitSettings = [\"-c\", \"core.hooksPath=/dev/null\", \"-c\", \"core.fsmonitor=false\"] as const\n\n/** Host config files git would otherwise read, removed from every shadow command. */\nconst isolated = { GIT_CONFIG_NOSYSTEM: \"1\", GIT_CONFIG_GLOBAL: \"/dev/null\" } as const\n\n/**\n * Workspace settings that change how a tree is hashed or compared, carried\n * onto the shadow so a checkpoint records what the agent's own git would.\n * None of them names a program.\n */\nconst dataSettings = /^core\\.(autocrlf|eol|safecrlf|filemode|symlinks|ignorecase|precomposeunicode)$/\n\n/** A setting value this module will repeat to git: a bare word, never a command. */\nconst bareWord = /^[A-Za-z0-9_-]+$/\n\nconst run = (command: string, args: ReadonlyArray<string>, env?: Readonly<Record<string, string>>) =>\n Exec.exec(command, { args, ...(env === undefined ? {} : { env }) }).pipe(\n Effect.mapError((error) => failed(`${command} could not run: ${error.message}`))\n )\n\n/**\n * Runs host git in the workspace at `root`, for `rev-parse` only.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst git = (root: string, args: ReadonlyArray<string>) => run(\"git\", [...hostGitSettings, \"-C\", root, ...args])\n\nconst resolveCommit = (root: string, refs: ReadonlyArray<string>) =>\n Effect.gen(function*() {\n for (const ref of refs) {\n const answer = yield* git(root, [\"rev-parse\", \"--verify\", \"--quiet\", `${ref}^{commit}`])\n const commit = answer.stdout.trim()\n if (answer.exitCode === 0 && commit !== \"\") return { ref, commit }\n }\n return yield* Effect.fail(failed(`No commit resolves from ${refs.join(\" or \")} in ${root}.`, \"not_found\"))\n })\n\n/**\n * Where one workspace repository keeps what the shadow needs, as its own git\n * reports it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Repository {\n /** The root the caller named, without a trailing slash. */\n readonly root: string\n /** The same directory, as git canonicalizes it. */\n readonly toplevel: string\n /** The common git directory, which holds config and objects. */\n readonly common: string\n /** The workspace index. */\n readonly index: string\n readonly objectFormat: string\n /** {@link dataSettings} as `-c` arguments. */\n readonly settings: ReadonlyArray<string>\n}\n\n/**\n * Reads or writes the workspace's config file as a file, never as a repository.\n *\n * `--git-dir=/dev/null` stops git discovering any repository around the host's\n * working directory, and `--file` reads the one file without `include`s.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst config = (repository: Pick<Repository, \"common\">, args: ReadonlyArray<string>) =>\n run(\"git\", [\"--git-dir=/dev/null\", \"config\", \"--file\", `${repository.common}/config`, ...args], isolated)\n\nconst open = (root: string): Effect.Effect<Repository, StdError.StdError, ChildProcessSpawner> =>\n Effect.gen(function*() {\n const stripped = root.replace(/\\/+$/, \"\")\n const answer = yield* git(stripped, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--show-toplevel\",\n \"--git-common-dir\",\n \"--git-path\",\n \"index\",\n \"--show-object-format\"\n ])\n const [toplevel, common, index, objectFormat] = answer.stdout.split(\"\\n\").map((line) => line.trim())\n if (\n answer.exitCode !== 0 || toplevel === undefined || toplevel === \"\" || common === undefined || common === \"\" ||\n index === undefined || index === \"\" || objectFormat === undefined || !bareWord.test(objectFormat)\n ) {\n return yield* Effect.fail(failed(`${stripped} is not a git work tree: ${answer.stderr.trim()}`))\n }\n const found = yield* config({ common }, [\"--get-regexp\", dataSettings.source])\n const settings = found.exitCode === 0\n ? found.stdout.split(\"\\n\").flatMap((line) => {\n const [key, value] = line.trim().split(/\\s+/)\n return key !== undefined && value !== undefined && dataSettings.test(key) && bareWord.test(value)\n ? [\"-c\", `${key}=${value}`]\n : []\n })\n : []\n return { root: stripped, toplevel, common, index, objectFormat, settings }\n })\n\nconst temporaryRoot = (): string => {\n const configured = globalThis.process?.env?.TMPDIR?.replace(/\\/+$/, \"\")\n return configured === undefined || configured === \"\" ? \"/tmp\" : configured\n}\n\nconst remove = (path: string) =>\n run(\"rm\", [\"-rf\", \"--\", path]).pipe(\n Effect.flatMap((removed) => removed.exitCode === 0 ? Effect.void : Effect.fail(failed(removed.stderr.trim())))\n )\n\n/**\n * A harness-owned `GIT_DIR` over one workspace's objects.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Shadow {\n readonly path: string\n readonly repository: Repository\n /** Runs host git with the shadow as `GIT_DIR`, and `workTree` as its work tree when given. */\n readonly git: (\n args: ReadonlyArray<string>,\n workTree?: string\n ) => Effect.Effect<Exec.ExecResult, StdError.StdError, ChildProcessSpawner>\n /**\n * Copies the workspace index into the shadow, with its modification time, so\n * the shadow never writes the agent's own.\n *\n * The time is part of what git reads. It trusts an entry's recorded stat\n * unless the entry is at least as new as the index file, in whole seconds;\n * a copy stamped at the moment of copying made every entry look older, and a\n * same-size edit from the second the index was written was recorded as the\n * committed bytes (#3431). The time is taken before the bytes are read, so\n * an index rewritten meanwhile leaves the copy looking older, never newer.\n */\n readonly adoptIndex: Effect.Effect<void, StdError.StdError, ChildProcessSpawner>\n}\n\n/** The `adoptIndex` script: `$1` is the workspace index, `$2` the shadow's. */\nconst adoptIndexScript = [\n \"set -C\",\n \"if [ -e \\\"$1\\\" ]\",\n \"then : > \\\"$2.at\\\" && touch -r \\\"$1\\\" -- \\\"$2.at\\\" && cat -- \\\"$1\\\" > \\\"$2\\\" && touch -r \\\"$2.at\\\" -- \\\"$2\\\" && rm -f -- \\\"$2.at\\\"\",\n \"fi\"\n].join(\"; \")\n\nconst shadowOf = (repository: Repository, path: string): Shadow => ({\n path,\n repository,\n git: (args, workTree) =>\n run(\n \"git\",\n [\n `--git-dir=${path}`,\n ...(workTree === undefined ? [] : [`--work-tree=${workTree}`]),\n ...hostGitSettings,\n ...repository.settings,\n ...args\n ],\n { ...isolated, GIT_OBJECT_DIRECTORY: `${repository.common}/objects` }\n ),\n adoptIndex: run(\"sh\", [\n \"-c\",\n adoptIndexScript,\n \"sh\",\n repository.index,\n `${path}/index`\n ]).pipe(\n Effect.flatMap((copied) =>\n copied.exitCode === 0\n ? Effect.void\n : Effect.fail(failed(`Could not read the workspace index: ${copied.stderr.trim()}`))\n )\n )\n})\n\n/**\n * Runs `use` with a fresh {@link Shadow} over the repository at `root`, and\n * deletes the shadow however `use` ends.\n *\n * @category utilities\n * @since 1.0.0\n */\nconst withShadow = <A, E, R>(\n root: string | Repository,\n use: (shadow: Shadow) => Effect.Effect<A, E, R>\n): Effect.Effect<A, E | StdError.StdError, R | ChildProcessSpawner> =>\n Effect.gen(function*() {\n const repository = typeof root === \"string\" ? yield* open(root) : root\n const path = `${temporaryRoot()}/smithers-git-${globalThis.crypto.randomUUID()}`\n return yield* Effect.acquireUseRelease(\n Effect.succeed(path),\n () =>\n Effect.gen(function*() {\n const created = yield* run(\n \"git\",\n [\"init\", \"--quiet\", \"--bare\", \"--template=\", `--object-format=${repository.objectFormat}`, path],\n isolated\n )\n if (created.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not create a shadow repository: ${created.stderr.trim()}`))\n }\n return yield* use(shadowOf(repository, path))\n }),\n () =>\n remove(path).pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"could not remove a shadow repository\").pipe(\n Effect.annotateLogs({ path, reason: error.message })\n )\n )\n )\n )\n })\n\n/** `to` relative to `from`, both absolute POSIX paths. */\nconst relative = (from: string, to: string): string => {\n const source = from.split(\"/\").filter((part) => part !== \"\")\n const target = to.split(\"/\").filter((part) => part !== \"\")\n let shared = 0\n while (shared < source.length && shared < target.length && source[shared] === target[shared]) shared++\n return [...source.slice(shared).map(() => \"..\"), ...target.slice(shared)].join(\"/\") || \".\"\n}\n\n/**\n * The config of a checkout's own `.git`, for the container's git.\n *\n * Only {@link dataSettings}, already restricted to bare words, reach it.\n */\nconst checkoutConfig = (repository: Repository): string => {\n const sha1 = repository.objectFormat === \"sha1\"\n const lines = [\n \"[core]\",\n `\\trepositoryformatversion = ${sha1 ? 0 : 1}`,\n \"\\tbare = false\",\n ...repository.settings.filter((_, index) => index % 2 === 1).map((setting) => {\n const [key, value] = setting.split(\"=\")\n return `\\t${key?.slice(\"core.\".length)} = ${value}`\n }),\n ...(sha1 ? [] : [\"[extensions]\", `\\tobjectformat = ${repository.objectFormat}`])\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n\n/** Fails with `message` and the command's stderr unless it exited 0. */\nconst succeeded = (result: Exec.ExecResult, message: string) =>\n result.exitCode === 0 ? Effect.void : Effect.fail(failed(`${message}: ${result.stderr.trim()}`))\n\n/**\n * Measures which staging directory shares the filesystem of the checkout's\n * parent `$1`: the shadow `$2`, the workspace root's parent `$3`, or the host\n * cache directory `$4`. Prints one line each for the parent, the shadow, the\n * root's parent (`unwritable` when the host user cannot create a stage there)\n * and the cache (empty unless both others missed and it could be created).\n * `stat -c` is GNU and BusyBox, `stat -f` is BSD; an empty line means neither\n * answered.\n */\nconst measure = [\n \"set -e\",\n \"mkdir -p -- \\\"$1\\\"\",\n \"dev() { stat -c %d -- \\\"$1\\\" 2>/dev/null || stat -f %d -- \\\"$1\\\" 2>/dev/null || echo; }\",\n \"t=$(dev \\\"$1\\\")\",\n \"s=$(dev \\\"$2\\\")\",\n \"if [ -w \\\"$3\\\" ]; then b=$(dev \\\"$3\\\"); else b=unwritable; fi\",\n \"c=\",\n \"if [ -n \\\"$t\\\" ] && [ \\\"$t\\\" != \\\"$s\\\" ] && [ \\\"$t\\\" != \\\"$b\\\" ] && [ -n \\\"$4\\\" ] && mkdir -p -m 700 -- \\\"$4\\\" 2>/dev/null; then c=$(dev \\\"$4\\\"); fi\",\n \"printf '%s\\\\n' \\\"$t\\\" \\\"$s\\\" \\\"$b\\\" \\\"$c\\\"\"\n].join(\"; \")\n\n/**\n * The host user's cache directory for checkout stages: `$XDG_CACHE_HOME`, or\n * `~/.cache`, under `smithers`. Empty when neither is an absolute path.\n */\nconst cacheRoot = (): string => {\n const env = globalThis.process?.env\n const xdg = env?.XDG_CACHE_HOME?.replace(/\\/+$/, \"\")\n if (xdg !== undefined && xdg.startsWith(\"/\")) return `${xdg}/smithers`\n const home = env?.HOME?.replace(/\\/+$/, \"\")\n return home !== undefined && home.startsWith(\"/\") ? `${home}/.cache/smithers` : \"\"\n}\n\n/**\n * Moves the staged checkout `$1` into the directory `$2` under the name `$3`\n * in one `rename`, then proves the directory there is the one that was staged.\n * The staging directory is on the same filesystem, so `mv` renames and never\n * copies.\n *\n * The shell first enters `$2` by its physical path and refuses unless that\n * lies under the real path of the workspace root `$4`. The rename then names\n * the target relative to that directory, so a parent the agent swaps for a\n * symlink, before or after the check, cannot carry the checkout out of the\n * workspace.\n */\nconst publish = [\n \"set -e\",\n \"root=$(cd -P -- \\\"$4\\\" && pwd -P)\",\n \"cd -P -- \\\"$2\\\"\",\n \"here=$(pwd -P)\",\n \"case \\\"$here\\\" in \\\"$root\\\"|\\\"$root\\\"/*) ;; *) echo \\\"$2 resolves to $here, outside the workspace $root\\\" >&2; exit 1 ;; esac\",\n \"if [ -e \\\"./$3\\\" ] || [ -L \\\"./$3\\\" ]; then echo \\\"$2/$3 already exists\\\" >&2; exit 1; fi\",\n \"staged=$(ls -di -- \\\"$1\\\" | awk '{print $1}')\",\n \"mv -- \\\"$1\\\" \\\"./$3\\\"\",\n \"if [ -L \\\"./$3\\\" ] || [ ! -d \\\"./$3\\\" ]; then echo \\\"$2/$3 was replaced while it was written\\\" >&2; exit 1; fi\",\n \"moved=$(ls -di -- \\\"./$3\\\" | awk '{print $1}')\",\n \"if [ \\\"$staged\\\" != \\\"$moved\\\" ]; then echo \\\"$2/$3 was replaced while it was written\\\" >&2; exit 1; fi\"\n].join(\"; \")\n\n/**\n * Checks `commit`'s tree out at the new directory `host`, with a standalone\n * `.git` the container's git can use.\n *\n * Host git never writes into an agent-writable directory. The tree and its\n * `.git` are written into a staging directory the agent cannot reach: inside\n * the shadow, or, when the host's temporary directory is another filesystem,\n * beside the workspace root or under the host user's cache directory. One\n * `rename` then gives the finished directory its name under the workspace. A\n * path the agent swaps for a symlink cannot redirect a write: a symlink at\n * `host` fails the call before the rename, and a parent that resolves outside\n * the workspace root fails it before the rename too.\n */\nconst checkout = (shadow: Shadow, host: string, commit: string) =>\n Effect.gen(function*() {\n const repository = shadow.repository\n const parent = host.slice(0, host.lastIndexOf(\"/\"))\n const name = host.slice(host.lastIndexOf(\"/\") + 1)\n const beside = repository.root.slice(0, repository.root.lastIndexOf(\"/\")) || \"/\"\n const cache = cacheRoot()\n const stage = `checkout-${globalThis.crypto.randomUUID()}`\n const besideStage = `${beside.replace(/\\/+$/, \"\")}/.smithers-${stage}`\n const cacheStage = `${cache}/${stage}`\n const measured = yield* run(\"sh\", [\"-c\", measure, \"sh\", parent, shadow.path, beside, cache])\n yield* succeeded(measured, `Could not check out ${commit}`)\n const [target, inShadow, besideRoot, inCache] = measured.stdout.split(\"\\n\").map((line) => line.trim())\n const scratch = target === undefined || target === \"\" || target === inShadow\n ? undefined\n : target === besideRoot\n ? besideStage\n : target === inCache\n ? cacheStage\n : undefined\n const base = target === undefined || target === \"\"\n ? undefined\n : target === inShadow\n ? shadow.path\n : scratch\n if (base === undefined) {\n const unwritable = besideRoot === \"unwritable\" ? \" (not writable by this user)\" : \"\"\n return yield* Effect.fail(\n failed(\n `Could not check out ${commit}: neither ${shadow.path} nor ${beside}${unwritable} nor ${\n cache === \"\" ? \"a cache directory\" : cache\n } is on the filesystem of ${parent}`\n )\n )\n }\n const staged = `${base}/${name}`\n const objects = relative(\n `${repository.toplevel}${host.slice(repository.root.length)}/.git/objects`,\n `${repository.common}/objects`\n )\n const write = Effect.gen(function*() {\n const prepared = yield* run(\"sh\", [\n \"-c\",\n [\n \"set -eC\",\n ...(scratch !== undefined ? [\"mkdir -m 700 -- \\\"$1\\\"\"] : []),\n \"mkdir -- \\\"$2\\\" \\\"$2/.git\\\" \\\"$2/.git/objects\\\" \\\"$2/.git/objects/info\\\" \\\"$2/.git/refs\\\"\",\n \"printf '%s\\\\n' \\\"$3\\\" > \\\"$2/.git/HEAD\\\"\",\n \"printf '%s\\\\n' \\\"$4\\\" > \\\"$2/.git/objects/info/alternates\\\"\",\n \"printf '%s' \\\"$5\\\" > \\\"$2/.git/config\\\"\"\n ].join(\"; \"),\n \"sh\",\n base,\n staged,\n commit,\n objects,\n checkoutConfig(repository)\n ])\n yield* succeeded(prepared, `Could not check out ${commit}`)\n const written = yield* shadow.git([\"read-tree\", \"--reset\", \"-u\", commit], staged)\n yield* succeeded(written, `Could not check out ${commit}`)\n const indexed = yield* run(\"sh\", [\n \"-c\",\n \"set -C; cat -- \\\"$1\\\" > \\\"$2\\\"\",\n \"sh\",\n `${shadow.path}/index`,\n `${staged}/.git/index`\n ])\n yield* succeeded(indexed, `Could not check out ${commit}`)\n const moved = yield* run(\"sh\", [\"-c\", publish, \"sh\", staged, parent, name, repository.root])\n yield* succeeded(moved, `Could not check out ${commit}`)\n })\n return yield* scratch !== undefined\n ? write.pipe(Effect.ensuring(remove(scratch).pipe(Effect.ignore)))\n : write\n })\n\nconst withDetachedWorktree = <A, E, R>(\n root: string,\n prefix: string,\n commit: string,\n use: (host: string) => Effect.Effect<A, E, R>\n): Effect.Effect<A, E | StdError.StdError, R | ChildProcessSpawner> =>\n Effect.gen(function*() {\n // Allocate on execution, including when the same Effect is run twice.\n // Never remove an existing checkout: another call may still be using it.\n const host = `${root.replace(/\\/+$/, \"\")}/${prefix}-${globalThis.crypto.randomUUID()}`\n return yield* Effect.acquireUseRelease(\n Effect.succeed(host),\n () =>\n Effect.gen(function*() {\n yield* withShadow(root, (shadow) => checkout(shadow, host, commit))\n return yield* use(host)\n }),\n // A checkout left behind is a stale copy of the repository inside the\n // workspace. Walks prune it, but the operator must still hear about it.\n () =>\n remove(host).pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"could not remove a scratch checkout\").pipe(\n Effect.annotateLogs({ path: host, reason: error.message })\n )\n )\n )\n )\n })\n\n/**\n * Internal Git operations used by both checkpoint and baseline leases.\n *\n * @category utilities\n * @since 1.0.0\n */\nexport const GitWorktree = { config, git, open, resolveCommit, withDetachedWorktree, withShadow }\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CA,oBAAuB;AACvB,eAA0B;AAC1B,WAAsB;AAEtB,MAAM,SAAS,CAAC,SAAiB,OAAsB,qBAAqB,IAAI,SAAS,SAAS,EAAE,MAAM,QAAQ,CAAC;AAOnH,MAAM,kBAAkB,CAAC,MAAM,4BAA4B,MAAM,sBAAsB;AAGvF,MAAM,WAAW,EAAE,qBAAqB,KAAK,mBAAmB,YAAY;AAO5E,MAAM,eAAe;AAGrB,MAAM,WAAW;AAEjB,MAAM,MAAM,CAAC,SAAiB,MAA6B,QACzD,KAAK,KAAK,SAAS,EAAE,MAAM,GAAI,QAAQ,SAAY,CAAC,IAAI,EAAE,IAAI,EAAG,CAAC,EAAE;AAAA,EAClE,qBAAO,SAAS,CAAC,UAAU,OAAO,GAAG,OAAO,mBAAmB,MAAM,OAAO,EAAE,CAAC;AACjF;AAQF,MAAM,MAAM,CAAC,MAAc,SAAgC,IAAI,OAAO,CAAC,GAAG,iBAAiB,MAAM,MAAM,GAAG,IAAI,CAAC;AAE/G,MAAM,gBAAgB,CAAC,MAAc,SACnC,qBAAO,IAAI,aAAY;AACrB,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,OAAO,IAAI,MAAM,CAAC,aAAa,YAAY,WAAW,GAAG,GAAG,WAAW,CAAC;AACvF,UAAM,SAAS,OAAO,OAAO,KAAK;AAClC,QAAI,OAAO,aAAa,KAAK,WAAW,GAAI,QAAO,EAAE,KAAK,OAAO;AAAA,EACnE;AACA,SAAO,OAAO,qBAAO,KAAK,OAAO,2BAA2B,KAAK,KAAK,MAAM,CAAC,OAAO,IAAI,KAAK,WAAW,CAAC;AAC3G,CAAC;AAgCH,MAAM,SAAS,CAAC,YAAwC,SACtD,IAAI,OAAO,CAAC,uBAAuB,UAAU,UAAU,GAAG,WAAW,MAAM,WAAW,GAAG,IAAI,GAAG,QAAQ;AAE1G,MAAM,OAAO,CAAC,SACZ,qBAAO,IAAI,aAAY;AACrB,QAAM,WAAW,KAAK,QAAQ,QAAQ,EAAE;AACxC,QAAM,SAAS,OAAO,IAAI,UAAU;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,CAAC,UAAU,QAAQ,OAAO,YAAY,IAAI,OAAO,OAAO,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACnG,MACE,OAAO,aAAa,KAAK,aAAa,UAAa,aAAa,MAAM,WAAW,UAAa,WAAW,MACzG,UAAU,UAAa,UAAU,MAAM,iBAAiB,UAAa,CAAC,SAAS,KAAK,YAAY,GAChG;AACA,WAAO,OAAO,qBAAO,KAAK,OAAO,GAAG,QAAQ,4BAA4B,OAAO,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,EACjG;AACA,QAAM,QAAQ,OAAO,OAAO,EAAE,OAAO,GAAG,CAAC,gBAAgB,aAAa,MAAM,CAAC;AAC7E,QAAM,WAAW,MAAM,aAAa,IAChC,MAAM,OAAO,MAAM,IAAI,EAAE,QAAQ,CAAC,SAAS;AAC3C,UAAM,CAAC,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,MAAM,KAAK;AAC5C,WAAO,QAAQ,UAAa,UAAU,UAAa,aAAa,KAAK,GAAG,KAAK,SAAS,KAAK,KAAK,IAC5F,CAAC,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE,IACxB,CAAC;AAAA,EACP,CAAC,IACC,CAAC;AACL,SAAO,EAAE,MAAM,UAAU,UAAU,QAAQ,OAAO,cAAc,SAAS;AAC3E,CAAC;AAEH,MAAM,gBAAgB,MAAc;AAClC,QAAM,aAAa,WAAW,SAAS,KAAK,QAAQ,QAAQ,QAAQ,EAAE;AACtE,SAAO,eAAe,UAAa,eAAe,KAAK,SAAS;AAClE;AAEA,MAAM,SAAS,CAAC,SACd,IAAI,MAAM,CAAC,OAAO,MAAM,IAAI,CAAC,EAAE;AAAA,EAC7B,qBAAO,QAAQ,CAAC,YAAY,QAAQ,aAAa,IAAI,qBAAO,OAAO,qBAAO,KAAK,OAAO,QAAQ,OAAO,KAAK,CAAC,CAAC,CAAC;AAC/G;AA+BF,MAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,MAAM,WAAW,CAAC,YAAwB,UAA0B;AAAA,EAClE;AAAA,EACA;AAAA,EACA,KAAK,CAAC,MAAM,aACV;AAAA,IACE;AAAA,IACA;AAAA,MACE,aAAa,IAAI;AAAA,MACjB,GAAI,aAAa,SAAY,CAAC,IAAI,CAAC,eAAe,QAAQ,EAAE;AAAA,MAC5D,GAAG;AAAA,MACH,GAAG,WAAW;AAAA,MACd,GAAG;AAAA,IACL;AAAA,IACA,EAAE,GAAG,UAAU,sBAAsB,GAAG,WAAW,MAAM,WAAW;AAAA,EACtE;AAAA,EACF,YAAY,IAAI,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG,IAAI;AAAA,EACT,CAAC,EAAE;AAAA,IACD,qBAAO;AAAA,MAAQ,CAAC,WACd,OAAO,aAAa,IAChB,qBAAO,OACP,qBAAO,KAAK,OAAO,uCAAuC,OAAO,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,IACvF;AAAA,EACF;AACF;AASA,MAAM,aAAa,CACjB,MACA,QAEA,qBAAO,IAAI,aAAY;AACrB,QAAM,aAAa,OAAO,SAAS,WAAW,OAAO,KAAK,IAAI,IAAI;AAClE,QAAM,OAAO,GAAG,cAAc,CAAC,iBAAiB,WAAW,OAAO,WAAW,CAAC;AAC9E,SAAO,OAAO,qBAAO;AAAA,IACnB,qBAAO,QAAQ,IAAI;AAAA,IACnB,MACE,qBAAO,IAAI,aAAY;AACrB,YAAM,UAAU,OAAO;AAAA,QACrB;AAAA,QACA,CAAC,QAAQ,WAAW,UAAU,eAAe,mBAAmB,WAAW,YAAY,IAAI,IAAI;AAAA,QAC/F;AAAA,MACF;AACA,UAAI,QAAQ,aAAa,GAAG;AAC1B,eAAO,OAAO,qBAAO,KAAK,OAAO,yCAAyC,QAAQ,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,MACpG;AACA,aAAO,OAAO,IAAI,SAAS,YAAY,IAAI,CAAC;AAAA,IAC9C,CAAC;AAAA,IACH,MACE,OAAO,IAAI,EAAE;AAAA,MACX,qBAAO;AAAA,QAAM,CAAC,UACZ,qBAAO,WAAW,sCAAsC,EAAE;AAAA,UACxD,qBAAO,aAAa,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACJ;AACF,CAAC;AAGH,MAAM,WAAW,CAAC,MAAc,OAAuB;AACrD,QAAM,SAAS,KAAK,MAAM,GAAG,EAAE,OAAO,CAAC,SAAS,SAAS,EAAE;AAC3D,QAAM,SAAS,GAAG,MAAM,GAAG,EAAE,OAAO,CAAC,SAAS,SAAS,EAAE;AACzD,MAAI,SAAS;AACb,SAAO,SAAS,OAAO,UAAU,SAAS,OAAO,UAAU,OAAO,MAAM,MAAM,OAAO,MAAM,EAAG;AAC9F,SAAO,CAAC,GAAG,OAAO,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,GAAG,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK;AACzF;AAOA,MAAM,iBAAiB,CAAC,eAAmC;AACzD,QAAM,OAAO,WAAW,iBAAiB;AACzC,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,8BAA+B,OAAO,IAAI,CAAC;AAAA,IAC3C;AAAA,IACA,GAAG,WAAW,SAAS,OAAO,CAAC,GAAG,UAAU,QAAQ,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY;AAC5E,YAAM,CAAC,KAAK,KAAK,IAAI,QAAQ,MAAM,GAAG;AACtC,aAAO,IAAK,KAAK,MAAM,QAAQ,MAAM,CAAC,MAAM,KAAK;AAAA,IACnD,CAAC;AAAA,IACD,GAAI,OAAO,CAAC,IAAI,CAAC,gBAAgB,mBAAoB,WAAW,YAAY,EAAE;AAAA,EAChF;AACA,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;AAGA,MAAM,YAAY,CAAC,QAAyB,YAC1C,OAAO,aAAa,IAAI,qBAAO,OAAO,qBAAO,KAAK,OAAO,GAAG,OAAO,KAAK,OAAO,OAAO,KAAK,CAAC,EAAE,CAAC;AAWjG,MAAM,UAAU;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAMX,MAAM,YAAY,MAAc;AAC9B,QAAM,MAAM,WAAW,SAAS;AAChC,QAAM,MAAM,KAAK,gBAAgB,QAAQ,QAAQ,EAAE;AACnD,MAAI,QAAQ,UAAa,IAAI,WAAW,GAAG,EAAG,QAAO,GAAG,GAAG;AAC3D,QAAM,OAAO,KAAK,MAAM,QAAQ,QAAQ,EAAE;AAC1C,SAAO,SAAS,UAAa,KAAK,WAAW,GAAG,IAAI,GAAG,IAAI,qBAAqB;AAClF;AAcA,MAAM,UAAU;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAeX,MAAM,WAAW,CAAC,QAAgB,MAAc,WAC9C,qBAAO,IAAI,aAAY;AACrB,QAAM,aAAa,OAAO;AAC1B,QAAM,SAAS,KAAK,MAAM,GAAG,KAAK,YAAY,GAAG,CAAC;AAClD,QAAM,OAAO,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;AACjD,QAAM,SAAS,WAAW,KAAK,MAAM,GAAG,WAAW,KAAK,YAAY,GAAG,CAAC,KAAK;AAC7E,QAAM,QAAQ,UAAU;AACxB,QAAM,QAAQ,YAAY,WAAW,OAAO,WAAW,CAAC;AACxD,QAAM,cAAc,GAAG,OAAO,QAAQ,QAAQ,EAAE,CAAC,cAAc,KAAK;AACpE,QAAM,aAAa,GAAG,KAAK,IAAI,KAAK;AACpC,QAAM,WAAW,OAAO,IAAI,MAAM,CAAC,MAAM,SAAS,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAK,CAAC;AAC3F,SAAO,UAAU,UAAU,uBAAuB,MAAM,EAAE;AAC1D,QAAM,CAAC,QAAQ,UAAU,YAAY,OAAO,IAAI,SAAS,OAAO,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACrG,QAAM,UAAU,WAAW,UAAa,WAAW,MAAM,WAAW,WAChE,SACA,WAAW,aACX,cACA,WAAW,UACX,aACA;AACJ,QAAM,OAAO,WAAW,UAAa,WAAW,KAC5C,SACA,WAAW,WACX,OAAO,OACP;AACJ,MAAI,SAAS,QAAW;AACtB,UAAM,aAAa,eAAe,eAAe,iCAAiC;AAClF,WAAO,OAAO,qBAAO;AAAA,MACnB;AAAA,QACE,uBAAuB,MAAM,aAAa,OAAO,IAAI,QAAQ,MAAM,GAAG,UAAU,QAC9E,UAAU,KAAK,sBAAsB,KACvC,4BAA4B,MAAM;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,GAAG,IAAI,IAAI,IAAI;AAC9B,QAAM,UAAU;AAAA,IACd,GAAG,WAAW,QAAQ,GAAG,KAAK,MAAM,WAAW,KAAK,MAAM,CAAC;AAAA,IAC3D,GAAG,WAAW,MAAM;AAAA,EACtB;AACA,QAAM,QAAQ,qBAAO,IAAI,aAAY;AACnC,UAAM,WAAW,OAAO,IAAI,MAAM;AAAA,MAChC;AAAA,MACA;AAAA,QACE;AAAA,QACA,GAAI,YAAY,SAAY,CAAC,sBAAwB,IAAI,CAAC;AAAA,QAC1D;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,UAAU;AAAA,IAC3B,CAAC;AACD,WAAO,UAAU,UAAU,uBAAuB,MAAM,EAAE;AAC1D,UAAM,UAAU,OAAO,OAAO,IAAI,CAAC,aAAa,WAAW,MAAM,MAAM,GAAG,MAAM;AAChF,WAAO,UAAU,SAAS,uBAAuB,MAAM,EAAE;AACzD,UAAM,UAAU,OAAO,IAAI,MAAM;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,OAAO,IAAI;AAAA,MACd,GAAG,MAAM;AAAA,IACX,CAAC;AACD,WAAO,UAAU,SAAS,uBAAuB,MAAM,EAAE;AACzD,UAAM,QAAQ,OAAO,IAAI,MAAM,CAAC,MAAM,SAAS,MAAM,QAAQ,QAAQ,MAAM,WAAW,IAAI,CAAC;AAC3F,WAAO,UAAU,OAAO,uBAAuB,MAAM,EAAE;AAAA,EACzD,CAAC;AACD,SAAO,OAAO,YAAY,SACtB,MAAM,KAAK,qBAAO,SAAS,OAAO,OAAO,EAAE,KAAK,qBAAO,MAAM,CAAC,CAAC,IAC/D;AACN,CAAC;AAEH,MAAM,uBAAuB,CAC3B,MACA,QACA,QACA,QAEA,qBAAO,IAAI,aAAY;AAGrB,QAAM,OAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,CAAC,IAAI,MAAM,IAAI,WAAW,OAAO,WAAW,CAAC;AACpF,SAAO,OAAO,qBAAO;AAAA,IACnB,qBAAO,QAAQ,IAAI;AAAA,IACnB,MACE,qBAAO,IAAI,aAAY;AACrB,aAAO,WAAW,MAAM,CAAC,WAAW,SAAS,QAAQ,MAAM,MAAM,CAAC;AAClE,aAAO,OAAO,IAAI,IAAI;AAAA,IACxB,CAAC;AAAA;AAAA;AAAA,IAGH,MACE,OAAO,IAAI,EAAE;AAAA,MACX,qBAAO;AAAA,QAAM,CAAC,UACZ,qBAAO,WAAW,qCAAqC,EAAE;AAAA,UACvD,qBAAO,aAAa,EAAE,MAAM,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACJ;AACF,CAAC;AAQI,MAAM,cAAc,EAAE,QAAQ,KAAK,MAAM,eAAe,sBAAsB,WAAW;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/esm/Bash.d.ts
CHANGED
|
@@ -281,6 +281,82 @@ export declare const run: (input: {
|
|
|
281
281
|
} | undefined;
|
|
282
282
|
readonly mutated?: boolean | undefined;
|
|
283
283
|
}, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | import("effect/Path").Path>;
|
|
284
|
+
/**
|
|
285
|
+
* What one call runs, after planning and before any transport: the program,
|
|
286
|
+
* its arguments (absent for a shell command line), standard input, the
|
|
287
|
+
* caller's environment, and the logical invocation as a person reads it.
|
|
288
|
+
*
|
|
289
|
+
* @category models
|
|
290
|
+
* @since 1.0.0
|
|
291
|
+
*/
|
|
292
|
+
export interface Invocation {
|
|
293
|
+
readonly file: string;
|
|
294
|
+
readonly args: ReadonlyArray<string> | undefined;
|
|
295
|
+
readonly stdin: string | undefined;
|
|
296
|
+
readonly env: Record<string, string> | undefined;
|
|
297
|
+
readonly quoted: string;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* The invocation `input` names, or the typed refusal explaining why it names
|
|
301
|
+
* none. The same plan {@link run} spawns; a terminal provider runs it instead.
|
|
302
|
+
*
|
|
303
|
+
* @category constructors
|
|
304
|
+
* @since 1.0.0
|
|
305
|
+
*/
|
|
306
|
+
export declare const invocation: (input: Input) => Invocation | StdError.StdError;
|
|
307
|
+
/**
|
|
308
|
+
* {@link run}'s contract through a registered terminal (T-TRM-05): the same
|
|
309
|
+
* plan, hermetic pre-check, bounded output and diagnostics refresh, with
|
|
310
|
+
* execution supplied by `execute` instead of a spawn on this host. A terminal
|
|
311
|
+
* has no container transport, so a containerised call is refused before it
|
|
312
|
+
* reaches `execute`.
|
|
313
|
+
*
|
|
314
|
+
* @category handlers
|
|
315
|
+
* @since 1.0.0
|
|
316
|
+
*/
|
|
317
|
+
export declare const inTerminal: <R>(execute: (input: Input) => Effect.Effect<Output, StdError.StdError, R>) => (input: {
|
|
318
|
+
readonly mode: "hermetic";
|
|
319
|
+
readonly writes: readonly string[];
|
|
320
|
+
readonly reads: readonly string[];
|
|
321
|
+
readonly cwd?: string | undefined;
|
|
322
|
+
readonly env?: {
|
|
323
|
+
readonly [x: string]: string;
|
|
324
|
+
} | undefined;
|
|
325
|
+
readonly stdin?: string | undefined;
|
|
326
|
+
readonly command?: string | undefined;
|
|
327
|
+
readonly args?: readonly string[] | undefined;
|
|
328
|
+
readonly timeoutMs?: number | undefined;
|
|
329
|
+
readonly script?: string | undefined;
|
|
330
|
+
readonly interpreter?: string | undefined;
|
|
331
|
+
readonly container?: string | undefined;
|
|
332
|
+
} | {
|
|
333
|
+
readonly mode?: "unhermetic";
|
|
334
|
+
readonly cwd?: string | undefined;
|
|
335
|
+
readonly env?: {
|
|
336
|
+
readonly [x: string]: string;
|
|
337
|
+
} | undefined;
|
|
338
|
+
readonly stdin?: string | undefined;
|
|
339
|
+
readonly command?: string | undefined;
|
|
340
|
+
readonly args?: readonly string[] | undefined;
|
|
341
|
+
readonly timeoutMs?: number | undefined;
|
|
342
|
+
readonly script?: string | undefined;
|
|
343
|
+
readonly interpreter?: string | undefined;
|
|
344
|
+
readonly container?: string | undefined;
|
|
345
|
+
}) => Effect.Effect<{
|
|
346
|
+
readonly stderr: string;
|
|
347
|
+
readonly exitCode: number;
|
|
348
|
+
readonly stdout: string;
|
|
349
|
+
readonly stdoutTruncated: boolean;
|
|
350
|
+
readonly stderrTruncated: boolean;
|
|
351
|
+
readonly stdoutDroppedBytes: number;
|
|
352
|
+
readonly stderrDroppedBytes: number;
|
|
353
|
+
readonly invalidProbe?: {
|
|
354
|
+
readonly reason: "unknown-command" | "unknown-test" | "unknown-path" | "unknown-module" | "unknown-environment";
|
|
355
|
+
readonly evidence: string;
|
|
356
|
+
readonly message: string;
|
|
357
|
+
} | undefined;
|
|
358
|
+
readonly mutated?: boolean | undefined;
|
|
359
|
+
}, StdError.StdError, import("effect/Path").Path | R>;
|
|
284
360
|
/**
|
|
285
361
|
* The refusal a sealed host answers a call outside its container with.
|
|
286
362
|
*
|
package/dist/esm/Bash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Bash.d.ts","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,mBAAmB,MAAM,oCAAoC,CAAA;AAC9E,OAAO,KAAK,IAAI,MAAM,qBAAqB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAQvC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAGzC;;;;;GAKG;AACH,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,yMACgL,CAAA;AAExM;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,SAAU,CAAA;AAmCzC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;IAiChB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;EA6BjB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,4CAKlB,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,+CAajC,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAkC,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAsF,CAAA;AAUvG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAIf,CAAA;
|
|
1
|
+
{"version":3,"file":"Bash.d.ts","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,mBAAmB,MAAM,oCAAoC,CAAA;AAC9E,OAAO,KAAK,IAAI,MAAM,qBAAqB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAQvC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAGzC;;;;;GAKG;AACH,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,yMACgL,CAAA;AAExM;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,SAAU,CAAA;AAmCzC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;IAiChB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;EA6BjB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,4CAKlB,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,+CAajC,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAkC,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAsF,CAAA;AAUvG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAIf,CAAA;AA2LV;;;;;;;;GAQG;AACH,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2FAwCd,CAAA;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,UAAU,GAAG,QAAQ,CAAC,QAAuB,CAAA;AAEvF;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAC1B,SAAS,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qDAapE,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,GAAG,SAAS,KAAG,QAAQ,CAAC,QAMxF,CAAA;AAEJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,GAAI,UAAU,MAAM,MAErC,OAAO,KAAK,KACX,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,mBAAmB,GAAG,IAAI,CAAC,IAAI,CACO,CAAA"}
|
package/dist/esm/Bash.js
CHANGED
|
@@ -307,13 +307,25 @@ const plan = (input) => {
|
|
|
307
307
|
quoted: [interpreter, ...args].join(" ")
|
|
308
308
|
};
|
|
309
309
|
};
|
|
310
|
+
/**
|
|
311
|
+
* The plan, refused when its input names no one invocation or when a hermetic
|
|
312
|
+
* call's paths fall outside its declared envelope. Spawned and terminal
|
|
313
|
+
* execution share this one check.
|
|
314
|
+
*/
|
|
315
|
+
const precheck = (input, path) => {
|
|
316
|
+
const intent = plan(input);
|
|
317
|
+
if (intent instanceof StdError.StdError || input.mode !== "hermetic")
|
|
318
|
+
return intent;
|
|
319
|
+
// A planned call carries exactly one of command or script.
|
|
320
|
+
return outsideEnvelope(input, input.command ?? input.script, path) ?? intent;
|
|
321
|
+
};
|
|
310
322
|
/** The plan as the host will spawn it, once the container transport has had it. */
|
|
311
323
|
const routed = (plan, input, transport) => {
|
|
312
324
|
if (input.container === undefined)
|
|
313
325
|
return Effect.succeed(plan);
|
|
314
326
|
if (Option.isNone(transport))
|
|
315
327
|
return Effect.fail(Container.unavailable(input.container));
|
|
316
|
-
return Effect.map(transport.value.exec(request(plan, input)), (routing) => ({
|
|
328
|
+
return Effect.map(transport.value.exec(request(plan, input, input.container)), (routing) => ({
|
|
317
329
|
file: routing.file,
|
|
318
330
|
args: routing.args,
|
|
319
331
|
stdin: plan.stdin,
|
|
@@ -343,8 +355,8 @@ const routed = (plan, input, transport) => {
|
|
|
343
355
|
* still arrives on the inherited standard input and no argument is ever
|
|
344
356
|
* re-parsed as shell text.
|
|
345
357
|
*/
|
|
346
|
-
const request = (plan, input) => ({
|
|
347
|
-
container
|
|
358
|
+
const request = (plan, input, container) => ({
|
|
359
|
+
container,
|
|
348
360
|
file: "bash",
|
|
349
361
|
args: plan.args === undefined
|
|
350
362
|
? ["-lc", plan.file]
|
|
@@ -396,15 +408,9 @@ const fingerprint = (input, transport) => Effect.gen(function* () {
|
|
|
396
408
|
* @since 1.0.0
|
|
397
409
|
*/
|
|
398
410
|
export const run = Effect.fn("Bash.run")(function* (input) {
|
|
399
|
-
const
|
|
400
|
-
const intent = plan(input);
|
|
411
|
+
const intent = precheck(input, yield* Path.Path);
|
|
401
412
|
if (intent instanceof StdError.StdError)
|
|
402
413
|
return yield* Effect.fail(intent);
|
|
403
|
-
if (input.mode === "hermetic") {
|
|
404
|
-
const violation = outsideEnvelope(input, input.command ?? input.script ?? "", path);
|
|
405
|
-
if (violation !== undefined)
|
|
406
|
-
return yield* Effect.fail(violation);
|
|
407
|
-
}
|
|
408
414
|
// The working directory belongs to the container. The transport supplies
|
|
409
415
|
// any environment overrides its host process needs to forward into it.
|
|
410
416
|
const contained = input.container !== undefined;
|
|
@@ -441,6 +447,36 @@ export const run = Effect.fn("Bash.run")(function* (input) {
|
|
|
441
447
|
...(mutated === undefined ? {} : { mutated })
|
|
442
448
|
};
|
|
443
449
|
});
|
|
450
|
+
/**
|
|
451
|
+
* The invocation `input` names, or the typed refusal explaining why it names
|
|
452
|
+
* none. The same plan {@link run} spawns; a terminal provider runs it instead.
|
|
453
|
+
*
|
|
454
|
+
* @category constructors
|
|
455
|
+
* @since 1.0.0
|
|
456
|
+
*/
|
|
457
|
+
export const invocation = (input) => plan(input);
|
|
458
|
+
/**
|
|
459
|
+
* {@link run}'s contract through a registered terminal (T-TRM-05): the same
|
|
460
|
+
* plan, hermetic pre-check, bounded output and diagnostics refresh, with
|
|
461
|
+
* execution supplied by `execute` instead of a spawn on this host. A terminal
|
|
462
|
+
* has no container transport, so a containerised call is refused before it
|
|
463
|
+
* reaches `execute`.
|
|
464
|
+
*
|
|
465
|
+
* @category handlers
|
|
466
|
+
* @since 1.0.0
|
|
467
|
+
*/
|
|
468
|
+
export const inTerminal = (execute) => Effect.fn("Bash.inTerminal")(function* (input) {
|
|
469
|
+
const intent = precheck(input, yield* Path.Path);
|
|
470
|
+
if (intent instanceof StdError.StdError)
|
|
471
|
+
return yield* Effect.fail(intent);
|
|
472
|
+
if (input.container !== undefined)
|
|
473
|
+
return yield* Effect.fail(Container.unavailable(input.container));
|
|
474
|
+
const output = yield* execute(input);
|
|
475
|
+
// The terminal ran on this workspace: files a language server holds may
|
|
476
|
+
// have changed, exactly as after a spawn here.
|
|
477
|
+
yield* Diagnostics.refresh;
|
|
478
|
+
return output;
|
|
479
|
+
});
|
|
444
480
|
/**
|
|
445
481
|
* The refusal a sealed host answers a call outside its container with.
|
|
446
482
|
*
|
package/dist/esm/Bash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Bash.js","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash flow declaration and portable handler.\n *\n * Two ways in, and the difference is where the payload is parsed. `command` is\n * a line for the platform shell, exactly as before. `script` is program text\n * delivered to an interpreter on standard input, as data: nothing quotes it,\n * escapes it, or terminates it with a heredoc marker, so the class of failure\n * that produced `docker exec c bash -lc '…python - <<EOF…'` cannot occur. Its\n * `args` reach the script as arguments rather than as interpolated text.\n *\n * `container` names a container the host knows how to reach, and the argv is\n * built by the injected {@link Container} transport rather than typed by the\n * caller. A host that binds no transport refuses such a call by saying so; the\n * requirement is optional here precisely so a host without containers is not\n * forced to declare one.\n *\n * @since 1.0.0\n */\n\nimport type * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Path from \"@smthrs/kernel/Path\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as Container from \"./Container.ts\"\nimport { capability, envelope } from \"./internal/Declaration.ts\"\nimport * as Diagnostics from \"./internal/Diagnostics.ts\"\nimport { outsideEnvelope } from \"./internal/EnvelopePrecheck.ts\"\nimport * as Exec from \"./internal/Exec.ts\"\nimport { MAX_SHELL_OUTPUT_BYTES, truncateBytes } from \"./internal/Text.ts\"\nimport * as Probe from \"./Probe.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TreeFingerprint from \"./TreeFingerprint.ts\"\n\n/**\n * Registry name for the bash flow.\n *\n * @category identifiers\n * @since 1.0.0\n */\nexport const name = \"bash\"\n\n/**\n * Model-facing description of the bash flow.\n *\n * Capped at 200 characters and read every frame, which is why the\n * invalid-probe contract is not restated here: the cell contract teaches it\n * once, and a result that has one explains itself.\n *\n * @category descriptions\n * @since 1.0.0\n */\nexport const description =\n \"Run a shell command, or an interpreter over a script passed as data instead of quoted into a line. container routes it through the host's container transport. mode:hermetic pre-checks path tokens.\"\n\n/**\n * Default command timeout in milliseconds.\n *\n * Ten minutes lets full builds and test suites finish while ensuring a stalled\n * process eventually releases the flow.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const DEFAULT_TIMEOUT_MS = 600_000\n\nconst Command = Schema.optional(Schema.String.annotate({\n description: \"Shell command line to execute; give this or script, never both\"\n}))\nconst Script = Schema.optional(Schema.String.annotate({\n description: \"Program text delivered to the interpreter on stdin as data, so it needs no quoting or heredoc\"\n}))\nconst Interpreter = Schema.optional(Schema.String.annotate({\n description:\n \"Program that reads script on stdin; defaults to bash. python, python3, sh, zsh, node, ruby and perl are known\"\n}))\nconst Args = Schema.optional(\n Schema.Array(Schema.String).annotate({\n description: \"Arguments passed to the script as data, never interpolated into it\"\n })\n)\nconst Stdin = Schema.optional(Schema.String.annotate({\n description: \"Text written to the command's standard input; use script instead when the text is the program\"\n}))\nconst ContainerName = Schema.optional(Schema.String.annotate({\n description: \"Run inside this container through the host's container transport; requires mode:unhermetic\"\n}))\nconst Cwd = Schema.optional(Schema.String).annotate({\n description: \"Working directory for the command\",\n message: \"cwd must be a path string. For a checkpoint, pass { at: ctx.base } as the third ctx.call argument.\"\n})\nconst Env = Schema.optional(\n Schema.Record(Schema.String, Schema.String).annotate({ description: \"Environment variables for the command\" })\n)\nconst TimeoutMs = Schema.optional(Schema.Number).annotate({\n description: \"Wall-clock timeout in milliseconds; defaults to 600000 ms\",\n message: \"timeoutMs must be a number of milliseconds.\"\n})\n\n/**\n * Input schema for the bash flow.\n *\n * The mode named `hermetic` requires explicit read and write envelopes for a\n * lexical pre-check; it does not by itself confine the process. Unhermetic mode\n * deliberately omits those declarations so the distinction stays visible in\n * the input shape.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Input = Schema.Union([\n Schema.Struct({\n mode: Schema.Literal(\"hermetic\").annotate({\n description: \"Lexically pre-check explicit path tokens against declared reads and writes; does not sandbox\"\n }),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n reads: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may read\" }),\n writes: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may write\" }),\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n }),\n Schema.Struct({\n // Omitted means unhermetic: it is the envelope `effects` already declares\n // for every call, so the default grants nothing a named mode would not.\n mode: Schema.Literal(\"unhermetic\").annotate({\n description: \"Run without a declared filesystem envelope as an irreversible effect; the default\"\n }).pipe(Schema.optionalKey, Schema.withDecodingDefaultKey(Effect.succeed(\"unhermetic\" as const))),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n })\n])\n\n/**\n * Decoded input accepted by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Output schema for the bash flow.\n *\n * `invalidProbe` is present when the shell itself refused to start the\n * command, which its exit code says outright: 127 for a program it could not\n * find, 126 for one it could not execute. Only that. Reading a runner's own\n * wording is a judgment rather than a fact, and `Probe.classify` makes it with\n * Jev for the `test` flow, whose caller is asking about a suite; a shell call\n * is any command at all and stays free of the gateway. See `Probe`.\n *\n * The `<stream>Truncated` flags are a wire convention, not a display detail.\n * A truncated capture is a fragment of what the process printed, and the\n * harness reads these flags to refuse a later write of those exact bytes —\n * `@smthrs/harness/TruncatedOutput` is the consuming half. Renaming a flag, or\n * dropping it when the capture is cut, disarms that guard silently.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Output = Schema.Struct({\n exitCode: Schema.Number.annotate({ description: \"Command exit code, including non-zero codes\" }),\n stdout: Schema.String.annotate({\n description: \"Captured stdout; only the tail, and not the whole output, when stdoutTruncated is true\"\n }),\n stderr: Schema.String.annotate({\n description: \"Captured stderr; only the tail, and not the whole output, when stderrTruncated is true\"\n }),\n stdoutTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stdout exceeded the capture limit, leaving stdout a fragment that must not be written to a file\"\n }),\n stderrTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stderr exceeded the capture limit, leaving stderr a fragment that must not be written to a file\"\n }),\n stdoutDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stdout\" }),\n stderrDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stderr\" }),\n invalidProbe: Schema.optional(\n Probe.InvalidProbe.annotate({\n description: \"Reserved for a confirmed launch failure; an exit code alone does not establish one\"\n })\n ),\n mutated: Schema.optional(\n Schema.Boolean.annotate({\n description:\n \"Present for a containerised command whose working directory was measured before and after it: true when the tree changed, false when it held still. Absent when the tree was not measured, which is never read as unchanged.\"\n })\n )\n})\n\n/**\n * Decoded output returned by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Output = typeof Output.Type\n\n/**\n * Static conservative effect envelope for the bash flow.\n *\n * The declaration is the registry-time worst case because the mode is not\n * known until an invocation is decoded.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effects = envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n})\n\n/**\n * Narrows the effect envelope for a decoded invocation.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effectsFor = (input: Input) =>\n input.mode === \"hermetic\"\n ? envelope({\n tier: \"irreversible\",\n mode: \"hermetic\",\n reads: input.reads,\n writes: input.writes\n })\n : envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n })\n\n/**\n * Capabilities required by the bash flow.\n *\n * @category capabilities\n * @since 1.0.0\n */\nexport const capabilities = [capability(\"proc:spawn\", \"*\")]\n\n/**\n * Declaration-only bash flow.\n *\n * @category flows\n * @since 1.0.0\n */\nexport const flow = { name, description, input: Input, output: Output, capabilities, effects } as const\n\n/*\n * `bash` declares no activity on purpose. What a shell call does is in the\n * command, not in the declaration: the same flow runs a test suite, a type\n * check, and `cat`. A reader that recognises a direct runner says so from the\n * recorded command; everything else stays unknown, which is what an absent\n * activity means.\n */\n\n/**\n * How one recorded call to this flow reads: the verb for each settlement, the\n * input field that is its subject, and the measured output fields a one-line\n * summary may count.\n *\n * Display metadata only, governed by `@smthrs/registry`\n * `Descriptor.CallPresentation` and checked where `StandardFlows` binds it.\n *\n * @category presentation\n * @since 1.0.0-rc.0\n */\nexport const presentation = {\n verb: { pending: \"running\", success: \"ran\", failure: \"failed to run\" },\n subject: \"command\",\n result: \"command\"\n} as const\n\n/**\n * Select standard input explicitly. Shells need `--` before script arguments\n * so even an argument such as `-c` cannot replace the stdin program.\n * Other supported interpreters treat arguments after `-` as program data.\n */\nconst stdinArguments = (interpreter: string): ReadonlyArray<string> | undefined => {\n switch (interpreter) {\n case \"bash\":\n case \"zsh\":\n case \"sh\":\n case \"dash\":\n return [\"-s\", \"--\"]\n case \"python\":\n case \"python3\":\n case \"node\":\n case \"ruby\":\n case \"perl\":\n return [\"-\"]\n default:\n return undefined\n }\n}\n\n/** Whether a script is shell text the lexical pre-check can actually read. */\nconst shells = new Set([\"bash\", \"zsh\", \"sh\", \"dash\"])\n\n/** What one invocation runs, before any container transport rewrites it. */\ninterface Plan {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\nconst invalid = (message: string): StdError.StdError => new StdError.StdError({ code: \"invalid_input\", message })\n\n/** The invocation an input names, or the failure explaining why it names none. */\nconst plan = (input: Input): Plan | StdError.StdError => {\n const interpreter = input.interpreter ?? \"bash\"\n if (input.command !== undefined && input.script !== undefined) {\n return invalid(\"Give command or script, never both: a script is program text, a command is a shell line\")\n }\n if (input.command === undefined && input.script === undefined) {\n return invalid(\"Give either command (a shell line) or script (program text run by interpreter)\")\n }\n if (input.script !== undefined && input.stdin !== undefined) {\n return invalid(\"A script already arrives on standard input, so it cannot also carry stdin\")\n }\n if (input.command !== undefined && input.args !== undefined) {\n return invalid(\"args belong to a script; a command carries its own arguments in the line\")\n }\n if (input.command !== undefined && input.interpreter !== undefined) {\n return invalid(\"interpreter runs a script; a command runs in the platform shell\")\n }\n if (input.mode === \"hermetic\" && input.container !== undefined) {\n return invalid(\n \"A container has its own filesystem, so declared reads and writes cannot describe it. Use mode:unhermetic for a containerised command\"\n )\n }\n if (input.mode === \"hermetic\" && input.script !== undefined && !shells.has(interpreter)) {\n return invalid(\n `The hermetic pre-check reads shell text, and ${interpreter} is not a shell. Run this script with mode:unhermetic, or express it as shell`\n )\n }\n if (input.command !== undefined) {\n return { file: input.command, args: undefined, stdin: input.stdin, env: input.env, quoted: input.command }\n }\n const selector = stdinArguments(interpreter)\n if (selector === undefined) {\n return invalid(`Unsupported script interpreter: ${interpreter}`)\n }\n const args = [...selector, ...(input.args ?? [])]\n return {\n file: interpreter,\n args,\n stdin: input.script,\n env: input.env,\n quoted: [interpreter, ...args].join(\" \")\n }\n}\n\n/** The plan as the host will spawn it, once the container transport has had it. */\nconst routed = (\n plan: Plan,\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<Plan, StdError.StdError> => {\n if (input.container === undefined) return Effect.succeed(plan)\n if (Option.isNone(transport)) return Effect.fail(Container.unavailable(input.container))\n return Effect.map(\n transport.value.exec(request(plan, input)),\n (routing): Plan => ({\n file: routing.file,\n args: routing.args,\n stdin: plan.stdin,\n env: routing.env,\n // Diagnostics describe the logical invocation, not transport arguments.\n quoted: plan.quoted\n })\n )\n}\n\n/**\n * One containerised request, always through a login shell.\n *\n * A container's environment is what its profile makes it. The images this\n * harness runs against activate the project's interpreter — a conda env, a\n * virtualenv, a modified `PATH` — from `/etc/profile.d`, so a program spawned\n * directly by `docker exec` gets a different Python from the one that owns the\n * repository's dependencies. That is measured, not theoretical: r90 typed\n * `docker exec … bash -lc '…'` by hand and got the activation for free, r91\n * routed `interpreter: \"python3\"` straight at the container and filled its\n * traces with `ModuleNotFoundError: No module named 'numpy'` and\n * `exec: \"/usr/local/bin/python\": no such file or directory`. Thirty of 45\n * instances then spent 138 cells scanning `/opt` for the real interpreter.\n *\n * So the login shell is the transport's, not the caller's. A command line is\n * one argument to `bash -lc`, as before. A program run under an interpreter\n * becomes `bash -lc 'exec \"$@\"' bash <interpreter> <args…>`: the shell reads\n * the profile, then `exec` replaces it with the interpreter, so the script\n * still arrives on the inherited standard input and no argument is ever\n * re-parsed as shell text.\n */\nconst request = (plan: Plan, input: Input): Container.Request => ({\n container: input.container ?? \"\",\n file: \"bash\",\n args: plan.args === undefined\n ? [\"-lc\", plan.file]\n : [\"-lc\", `exec \"$@\"`, \"bash\", plan.file, ...plan.args],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n ...(input.env === undefined ? {} : { env: input.env }),\n stdin: plan.stdin !== undefined\n})\n\n/** How long one fingerprint of the container's working directory may take. */\nconst FINGERPRINT_TIMEOUT_MS = 60_000\n\n/**\n * Measures the tree a containerised command is about to run in, or just ran in.\n *\n * The host's workspace walk cannot see a container's filesystem, so the\n * measurement is taken where the tree is: `TreeFingerprint.script` runs\n * through the same transport as the command, in the same working directory,\n * and answers with one checksum. Every way this can fail — no transport, a\n * container without `find` or `stat`, a timeout, a bounded listing — is read\n * as \"unmeasured\" and reported as an absent `mutated`, never as a tree that\n * held still. The time it costs is bounded by the command's own timeout so a\n * stalled container cannot spend more waiting to be measured than it may\n * spend running.\n */\nconst fingerprint = (\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<TreeFingerprint.Measurement | undefined, never, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n if (input.container === undefined || Option.isNone(transport)) return undefined\n const routing = yield* transport.value.exec({\n container: input.container,\n file: \"sh\",\n args: [\"-c\", TreeFingerprint.script()],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n stdin: false\n })\n const result = yield* Exec.exec(routing.file, {\n ...(routing.env === undefined ? {} : { env: routing.env }),\n timeoutMs: Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, FINGERPRINT_TIMEOUT_MS),\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n args: routing.args\n })\n return result.exitCode === 0 ? TreeFingerprint.parse(result.stdout) : undefined\n }).pipe(Effect.orElseSucceed(() => undefined))\n\n/**\n * Executes a shell command through the permission-aware kernel service.\n *\n * Non-zero exit codes remain successful values. Only timeout, host, permission,\n * and declared-envelope pre-check failures use the typed error channel.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const run = Effect.fn(\"Bash.run\")(function*(\n input: Input\n): Effect.fn.Return<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> {\n const path = yield* Path.Path\n const intent = plan(input)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n if (input.mode === \"hermetic\") {\n const violation = outsideEnvelope(input, input.command ?? input.script ?? \"\", path)\n if (violation !== undefined) return yield* Effect.fail(violation)\n }\n // The working directory belongs to the container. The transport supplies\n // any environment overrides its host process needs to forward into it.\n const contained = input.container !== undefined\n const transport = yield* Effect.serviceOption(Container.Container)\n const spawned = yield* routed(intent, input, transport)\n\n // The tree the command runs in, before it runs. Measured in the container\n // because nothing on this host can see it; see `TreeFingerprint`.\n const before = yield* fingerprint(input, transport)\n const result = yield* Exec.exec(spawned.file, {\n ...(input.cwd === undefined || contained ? {} : { cwd: input.cwd }),\n ...(spawned.env === undefined ? {} : { env: spawned.env }),\n timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n ...(spawned.args === undefined ? {} : { args: spawned.args }),\n ...(spawned.stdin === undefined ? {} : { stdin: spawned.stdin })\n }).pipe(Effect.mapError((error) => Exec.toStdError(spawned.quoted, error)))\n const mutated = TreeFingerprint.moved(before, yield* fingerprint(input, transport))\n // A command on this host may have changed files a language server holds\n // open; a container's files are not the ones it was sent.\n if (!contained) yield* Diagnostics.refresh\n const stdout = truncateBytes(result.stdout, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n const stderr = truncateBytes(result.stderr, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n // Exit codes alone cannot establish whether the shell refused to launch a\n // command or a launched runner chose that status after executing tests.\n return {\n exitCode: result.exitCode,\n stdout: stdout.text,\n stderr: stderr.text,\n stdoutTruncated: result.stdoutDroppedBytes > 0 || stdout.truncated,\n stderrTruncated: result.stderrDroppedBytes > 0 || stderr.truncated,\n stdoutDroppedBytes: result.stdoutDroppedBytes + stdout.droppedBytes,\n stderrDroppedBytes: result.stderrDroppedBytes + stderr.droppedBytes,\n ...(mutated === undefined ? {} : { mutated })\n }\n})\n\n/**\n * The refusal a sealed host answers a call outside its container with.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const outsideContainer = (sealedTo: string, requested: string | undefined): StdError.StdError =>\n new StdError.StdError({\n code: \"outside_container\",\n message: requested === undefined\n ? `This host runs commands only inside container \"${sealedTo}\". Name it: { mode: \"unhermetic\", container: \"${sealedTo}\", command: \"...\" }.`\n : `This host runs commands only inside container \"${sealedTo}\", not \"${requested}\".`\n })\n\n/**\n * {@link run}, sealed to one container.\n *\n * A host that must not be reachable from the cell (a benchmark host holding\n * other tasks' tests and reference solutions, say) binds this instead of\n * {@link run}: a call naming `sealedTo` runs as usual, and every other call,\n * including one with no container that would run on the host, fails with\n * `outside_container` before anything is spawned.\n *\n * @category handlers\n * @since 1.0.0-rc.1\n */\nexport const sealed = (sealedTo: string) =>\n(\n input: Input\n): Effect.Effect<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> =>\n input.container === sealedTo ? run(input) : Effect.fail(outsideContainer(sealedTo, input.container))\n"]}
|
|
1
|
+
{"version":3,"file":"Bash.js","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash flow declaration and portable handler.\n *\n * Two ways in, and the difference is where the payload is parsed. `command` is\n * a line for the platform shell, exactly as before. `script` is program text\n * delivered to an interpreter on standard input, as data: nothing quotes it,\n * escapes it, or terminates it with a heredoc marker, so the class of failure\n * that produced `docker exec c bash -lc '…python - <<EOF…'` cannot occur. Its\n * `args` reach the script as arguments rather than as interpolated text.\n *\n * `container` names a container the host knows how to reach, and the argv is\n * built by the injected {@link Container} transport rather than typed by the\n * caller. A host that binds no transport refuses such a call by saying so; the\n * requirement is optional here precisely so a host without containers is not\n * forced to declare one.\n *\n * @since 1.0.0\n */\n\nimport type * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Path from \"@smthrs/kernel/Path\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as Container from \"./Container.ts\"\nimport { capability, envelope } from \"./internal/Declaration.ts\"\nimport * as Diagnostics from \"./internal/Diagnostics.ts\"\nimport { outsideEnvelope } from \"./internal/EnvelopePrecheck.ts\"\nimport * as Exec from \"./internal/Exec.ts\"\nimport { MAX_SHELL_OUTPUT_BYTES, truncateBytes } from \"./internal/Text.ts\"\nimport * as Probe from \"./Probe.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TreeFingerprint from \"./TreeFingerprint.ts\"\n\n/**\n * Registry name for the bash flow.\n *\n * @category identifiers\n * @since 1.0.0\n */\nexport const name = \"bash\"\n\n/**\n * Model-facing description of the bash flow.\n *\n * Capped at 200 characters and read every frame, which is why the\n * invalid-probe contract is not restated here: the cell contract teaches it\n * once, and a result that has one explains itself.\n *\n * @category descriptions\n * @since 1.0.0\n */\nexport const description =\n \"Run a shell command, or an interpreter over a script passed as data instead of quoted into a line. container routes it through the host's container transport. mode:hermetic pre-checks path tokens.\"\n\n/**\n * Default command timeout in milliseconds.\n *\n * Ten minutes lets full builds and test suites finish while ensuring a stalled\n * process eventually releases the flow.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const DEFAULT_TIMEOUT_MS = 600_000\n\nconst Command = Schema.optional(Schema.String.annotate({\n description: \"Shell command line to execute; give this or script, never both\"\n}))\nconst Script = Schema.optional(Schema.String.annotate({\n description: \"Program text delivered to the interpreter on stdin as data, so it needs no quoting or heredoc\"\n}))\nconst Interpreter = Schema.optional(Schema.String.annotate({\n description:\n \"Program that reads script on stdin; defaults to bash. python, python3, sh, zsh, node, ruby and perl are known\"\n}))\nconst Args = Schema.optional(\n Schema.Array(Schema.String).annotate({\n description: \"Arguments passed to the script as data, never interpolated into it\"\n })\n)\nconst Stdin = Schema.optional(Schema.String.annotate({\n description: \"Text written to the command's standard input; use script instead when the text is the program\"\n}))\nconst ContainerName = Schema.optional(Schema.String.annotate({\n description: \"Run inside this container through the host's container transport; requires mode:unhermetic\"\n}))\nconst Cwd = Schema.optional(Schema.String).annotate({\n description: \"Working directory for the command\",\n message: \"cwd must be a path string. For a checkpoint, pass { at: ctx.base } as the third ctx.call argument.\"\n})\nconst Env = Schema.optional(\n Schema.Record(Schema.String, Schema.String).annotate({ description: \"Environment variables for the command\" })\n)\nconst TimeoutMs = Schema.optional(Schema.Number).annotate({\n description: \"Wall-clock timeout in milliseconds; defaults to 600000 ms\",\n message: \"timeoutMs must be a number of milliseconds.\"\n})\n\n/**\n * Input schema for the bash flow.\n *\n * The mode named `hermetic` requires explicit read and write envelopes for a\n * lexical pre-check; it does not by itself confine the process. Unhermetic mode\n * deliberately omits those declarations so the distinction stays visible in\n * the input shape.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Input = Schema.Union([\n Schema.Struct({\n mode: Schema.Literal(\"hermetic\").annotate({\n description: \"Lexically pre-check explicit path tokens against declared reads and writes; does not sandbox\"\n }),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n reads: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may read\" }),\n writes: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may write\" }),\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n }),\n Schema.Struct({\n // Omitted means unhermetic: it is the envelope `effects` already declares\n // for every call, so the default grants nothing a named mode would not.\n mode: Schema.Literal(\"unhermetic\").annotate({\n description: \"Run without a declared filesystem envelope as an irreversible effect; the default\"\n }).pipe(Schema.optionalKey, Schema.withDecodingDefaultKey(Effect.succeed(\"unhermetic\" as const))),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n })\n])\n\n/**\n * Decoded input accepted by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Output schema for the bash flow.\n *\n * `invalidProbe` is present when the shell itself refused to start the\n * command, which its exit code says outright: 127 for a program it could not\n * find, 126 for one it could not execute. Only that. Reading a runner's own\n * wording is a judgment rather than a fact, and `Probe.classify` makes it with\n * Jev for the `test` flow, whose caller is asking about a suite; a shell call\n * is any command at all and stays free of the gateway. See `Probe`.\n *\n * The `<stream>Truncated` flags are a wire convention, not a display detail.\n * A truncated capture is a fragment of what the process printed, and the\n * harness reads these flags to refuse a later write of those exact bytes —\n * `@smthrs/harness/TruncatedOutput` is the consuming half. Renaming a flag, or\n * dropping it when the capture is cut, disarms that guard silently.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Output = Schema.Struct({\n exitCode: Schema.Number.annotate({ description: \"Command exit code, including non-zero codes\" }),\n stdout: Schema.String.annotate({\n description: \"Captured stdout; only the tail, and not the whole output, when stdoutTruncated is true\"\n }),\n stderr: Schema.String.annotate({\n description: \"Captured stderr; only the tail, and not the whole output, when stderrTruncated is true\"\n }),\n stdoutTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stdout exceeded the capture limit, leaving stdout a fragment that must not be written to a file\"\n }),\n stderrTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stderr exceeded the capture limit, leaving stderr a fragment that must not be written to a file\"\n }),\n stdoutDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stdout\" }),\n stderrDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stderr\" }),\n invalidProbe: Schema.optional(\n Probe.InvalidProbe.annotate({\n description: \"Reserved for a confirmed launch failure; an exit code alone does not establish one\"\n })\n ),\n mutated: Schema.optional(\n Schema.Boolean.annotate({\n description:\n \"Present for a containerised command whose working directory was measured before and after it: true when the tree changed, false when it held still. Absent when the tree was not measured, which is never read as unchanged.\"\n })\n )\n})\n\n/**\n * Decoded output returned by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Output = typeof Output.Type\n\n/**\n * Static conservative effect envelope for the bash flow.\n *\n * The declaration is the registry-time worst case because the mode is not\n * known until an invocation is decoded.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effects = envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n})\n\n/**\n * Narrows the effect envelope for a decoded invocation.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effectsFor = (input: Input) =>\n input.mode === \"hermetic\"\n ? envelope({\n tier: \"irreversible\",\n mode: \"hermetic\",\n reads: input.reads,\n writes: input.writes\n })\n : envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n })\n\n/**\n * Capabilities required by the bash flow.\n *\n * @category capabilities\n * @since 1.0.0\n */\nexport const capabilities = [capability(\"proc:spawn\", \"*\")]\n\n/**\n * Declaration-only bash flow.\n *\n * @category flows\n * @since 1.0.0\n */\nexport const flow = { name, description, input: Input, output: Output, capabilities, effects } as const\n\n/*\n * `bash` declares no activity on purpose. What a shell call does is in the\n * command, not in the declaration: the same flow runs a test suite, a type\n * check, and `cat`. A reader that recognises a direct runner says so from the\n * recorded command; everything else stays unknown, which is what an absent\n * activity means.\n */\n\n/**\n * How one recorded call to this flow reads: the verb for each settlement, the\n * input field that is its subject, and the measured output fields a one-line\n * summary may count.\n *\n * Display metadata only, governed by `@smthrs/registry`\n * `Descriptor.CallPresentation` and checked where `StandardFlows` binds it.\n *\n * @category presentation\n * @since 1.0.0-rc.0\n */\nexport const presentation = {\n verb: { pending: \"running\", success: \"ran\", failure: \"failed to run\" },\n subject: \"command\",\n result: \"command\"\n} as const\n\n/**\n * Select standard input explicitly. Shells need `--` before script arguments\n * so even an argument such as `-c` cannot replace the stdin program.\n * Other supported interpreters treat arguments after `-` as program data.\n */\nconst stdinArguments = (interpreter: string): ReadonlyArray<string> | undefined => {\n switch (interpreter) {\n case \"bash\":\n case \"zsh\":\n case \"sh\":\n case \"dash\":\n return [\"-s\", \"--\"]\n case \"python\":\n case \"python3\":\n case \"node\":\n case \"ruby\":\n case \"perl\":\n return [\"-\"]\n default:\n return undefined\n }\n}\n\n/** Whether a script is shell text the lexical pre-check can actually read. */\nconst shells = new Set([\"bash\", \"zsh\", \"sh\", \"dash\"])\n\n/** What one invocation runs, before any container transport rewrites it. */\ninterface Plan {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\nconst invalid = (message: string): StdError.StdError => new StdError.StdError({ code: \"invalid_input\", message })\n\n/** The invocation an input names, or the failure explaining why it names none. */\nconst plan = (input: Input): Plan | StdError.StdError => {\n const interpreter = input.interpreter ?? \"bash\"\n if (input.command !== undefined && input.script !== undefined) {\n return invalid(\"Give command or script, never both: a script is program text, a command is a shell line\")\n }\n if (input.command === undefined && input.script === undefined) {\n return invalid(\"Give either command (a shell line) or script (program text run by interpreter)\")\n }\n if (input.script !== undefined && input.stdin !== undefined) {\n return invalid(\"A script already arrives on standard input, so it cannot also carry stdin\")\n }\n if (input.command !== undefined && input.args !== undefined) {\n return invalid(\"args belong to a script; a command carries its own arguments in the line\")\n }\n if (input.command !== undefined && input.interpreter !== undefined) {\n return invalid(\"interpreter runs a script; a command runs in the platform shell\")\n }\n if (input.mode === \"hermetic\" && input.container !== undefined) {\n return invalid(\n \"A container has its own filesystem, so declared reads and writes cannot describe it. Use mode:unhermetic for a containerised command\"\n )\n }\n if (input.mode === \"hermetic\" && input.script !== undefined && !shells.has(interpreter)) {\n return invalid(\n `The hermetic pre-check reads shell text, and ${interpreter} is not a shell. Run this script with mode:unhermetic, or express it as shell`\n )\n }\n if (input.command !== undefined) {\n return { file: input.command, args: undefined, stdin: input.stdin, env: input.env, quoted: input.command }\n }\n const selector = stdinArguments(interpreter)\n if (selector === undefined) {\n return invalid(`Unsupported script interpreter: ${interpreter}`)\n }\n const args = [...selector, ...(input.args ?? [])]\n return {\n file: interpreter,\n args,\n stdin: input.script,\n env: input.env,\n quoted: [interpreter, ...args].join(\" \")\n }\n}\n\n/**\n * The plan, refused when its input names no one invocation or when a hermetic\n * call's paths fall outside its declared envelope. Spawned and terminal\n * execution share this one check.\n */\nconst precheck = (input: Input, path: Path.Path): Plan | StdError.StdError => {\n const intent = plan(input)\n if (intent instanceof StdError.StdError || input.mode !== \"hermetic\") return intent\n // A planned call carries exactly one of command or script.\n return outsideEnvelope(input, input.command ?? input.script!, path) ?? intent\n}\n\n/** The plan as the host will spawn it, once the container transport has had it. */\nconst routed = (\n plan: Plan,\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<Plan, StdError.StdError> => {\n if (input.container === undefined) return Effect.succeed(plan)\n if (Option.isNone(transport)) return Effect.fail(Container.unavailable(input.container))\n return Effect.map(\n transport.value.exec(request(plan, input, input.container)),\n (routing): Plan => ({\n file: routing.file,\n args: routing.args,\n stdin: plan.stdin,\n env: routing.env,\n // Diagnostics describe the logical invocation, not transport arguments.\n quoted: plan.quoted\n })\n )\n}\n\n/**\n * One containerised request, always through a login shell.\n *\n * A container's environment is what its profile makes it. The images this\n * harness runs against activate the project's interpreter — a conda env, a\n * virtualenv, a modified `PATH` — from `/etc/profile.d`, so a program spawned\n * directly by `docker exec` gets a different Python from the one that owns the\n * repository's dependencies. That is measured, not theoretical: r90 typed\n * `docker exec … bash -lc '…'` by hand and got the activation for free, r91\n * routed `interpreter: \"python3\"` straight at the container and filled its\n * traces with `ModuleNotFoundError: No module named 'numpy'` and\n * `exec: \"/usr/local/bin/python\": no such file or directory`. Thirty of 45\n * instances then spent 138 cells scanning `/opt` for the real interpreter.\n *\n * So the login shell is the transport's, not the caller's. A command line is\n * one argument to `bash -lc`, as before. A program run under an interpreter\n * becomes `bash -lc 'exec \"$@\"' bash <interpreter> <args…>`: the shell reads\n * the profile, then `exec` replaces it with the interpreter, so the script\n * still arrives on the inherited standard input and no argument is ever\n * re-parsed as shell text.\n */\nconst request = (plan: Plan, input: Input, container: string): Container.Request => ({\n container,\n file: \"bash\",\n args: plan.args === undefined\n ? [\"-lc\", plan.file]\n : [\"-lc\", `exec \"$@\"`, \"bash\", plan.file, ...plan.args],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n ...(input.env === undefined ? {} : { env: input.env }),\n stdin: plan.stdin !== undefined\n})\n\n/** How long one fingerprint of the container's working directory may take. */\nconst FINGERPRINT_TIMEOUT_MS = 60_000\n\n/**\n * Measures the tree a containerised command is about to run in, or just ran in.\n *\n * The host's workspace walk cannot see a container's filesystem, so the\n * measurement is taken where the tree is: `TreeFingerprint.script` runs\n * through the same transport as the command, in the same working directory,\n * and answers with one checksum. Every way this can fail — no transport, a\n * container without `find` or `stat`, a timeout, a bounded listing — is read\n * as \"unmeasured\" and reported as an absent `mutated`, never as a tree that\n * held still. The time it costs is bounded by the command's own timeout so a\n * stalled container cannot spend more waiting to be measured than it may\n * spend running.\n */\nconst fingerprint = (\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<TreeFingerprint.Measurement | undefined, never, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n if (input.container === undefined || Option.isNone(transport)) return undefined\n const routing = yield* transport.value.exec({\n container: input.container,\n file: \"sh\",\n args: [\"-c\", TreeFingerprint.script()],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n stdin: false\n })\n const result = yield* Exec.exec(routing.file, {\n ...(routing.env === undefined ? {} : { env: routing.env }),\n timeoutMs: Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, FINGERPRINT_TIMEOUT_MS),\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n args: routing.args\n })\n return result.exitCode === 0 ? TreeFingerprint.parse(result.stdout) : undefined\n }).pipe(Effect.orElseSucceed(() => undefined))\n\n/**\n * Executes a shell command through the permission-aware kernel service.\n *\n * Non-zero exit codes remain successful values. Only timeout, host, permission,\n * and declared-envelope pre-check failures use the typed error channel.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const run = Effect.fn(\"Bash.run\")(function*(\n input: Input\n): Effect.fn.Return<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n // The working directory belongs to the container. The transport supplies\n // any environment overrides its host process needs to forward into it.\n const contained = input.container !== undefined\n const transport = yield* Effect.serviceOption(Container.Container)\n const spawned = yield* routed(intent, input, transport)\n\n // The tree the command runs in, before it runs. Measured in the container\n // because nothing on this host can see it; see `TreeFingerprint`.\n const before = yield* fingerprint(input, transport)\n const result = yield* Exec.exec(spawned.file, {\n ...(input.cwd === undefined || contained ? {} : { cwd: input.cwd }),\n ...(spawned.env === undefined ? {} : { env: spawned.env }),\n timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n ...(spawned.args === undefined ? {} : { args: spawned.args }),\n ...(spawned.stdin === undefined ? {} : { stdin: spawned.stdin })\n }).pipe(Effect.mapError((error) => Exec.toStdError(spawned.quoted, error)))\n const mutated = TreeFingerprint.moved(before, yield* fingerprint(input, transport))\n // A command on this host may have changed files a language server holds\n // open; a container's files are not the ones it was sent.\n if (!contained) yield* Diagnostics.refresh\n const stdout = truncateBytes(result.stdout, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n const stderr = truncateBytes(result.stderr, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n // Exit codes alone cannot establish whether the shell refused to launch a\n // command or a launched runner chose that status after executing tests.\n return {\n exitCode: result.exitCode,\n stdout: stdout.text,\n stderr: stderr.text,\n stdoutTruncated: result.stdoutDroppedBytes > 0 || stdout.truncated,\n stderrTruncated: result.stderrDroppedBytes > 0 || stderr.truncated,\n stdoutDroppedBytes: result.stdoutDroppedBytes + stdout.droppedBytes,\n stderrDroppedBytes: result.stderrDroppedBytes + stderr.droppedBytes,\n ...(mutated === undefined ? {} : { mutated })\n }\n})\n\n/**\n * What one call runs, after planning and before any transport: the program,\n * its arguments (absent for a shell command line), standard input, the\n * caller's environment, and the logical invocation as a person reads it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Invocation {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\n/**\n * The invocation `input` names, or the typed refusal explaining why it names\n * none. The same plan {@link run} spawns; a terminal provider runs it instead.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const invocation = (input: Input): Invocation | StdError.StdError => plan(input)\n\n/**\n * {@link run}'s contract through a registered terminal (T-TRM-05): the same\n * plan, hermetic pre-check, bounded output and diagnostics refresh, with\n * execution supplied by `execute` instead of a spawn on this host. A terminal\n * has no container transport, so a containerised call is refused before it\n * reaches `execute`.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const inTerminal = <R>(\n execute: (input: Input) => Effect.Effect<Output, StdError.StdError, R>\n) =>\n Effect.fn(\"Bash.inTerminal\")(function*(\n input: Input\n ): Effect.fn.Return<Output, StdError.StdError, R | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n if (input.container !== undefined) return yield* Effect.fail(Container.unavailable(input.container))\n const output = yield* execute(input)\n // The terminal ran on this workspace: files a language server holds may\n // have changed, exactly as after a spawn here.\n yield* Diagnostics.refresh\n return output\n })\n\n/**\n * The refusal a sealed host answers a call outside its container with.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const outsideContainer = (sealedTo: string, requested: string | undefined): StdError.StdError =>\n new StdError.StdError({\n code: \"outside_container\",\n message: requested === undefined\n ? `This host runs commands only inside container \"${sealedTo}\". Name it: { mode: \"unhermetic\", container: \"${sealedTo}\", command: \"...\" }.`\n : `This host runs commands only inside container \"${sealedTo}\", not \"${requested}\".`\n })\n\n/**\n * {@link run}, sealed to one container.\n *\n * A host that must not be reachable from the cell (a benchmark host holding\n * other tasks' tests and reference solutions, say) binds this instead of\n * {@link run}: a call naming `sealedTo` runs as usual, and every other call,\n * including one with no container that would run on the host, fails with\n * `outside_container` before anything is spawned.\n *\n * @category handlers\n * @since 1.0.0-rc.1\n */\nexport const sealed = (sealedTo: string) =>\n(\n input: Input\n): Effect.Effect<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> =>\n input.container === sealedTo ? run(input) : Effect.fail(outsideContainer(sealedTo, input.container))\n"]}
|
|
@@ -75,7 +75,17 @@ export interface Shadow {
|
|
|
75
75
|
readonly repository: Repository;
|
|
76
76
|
/** Runs host git with the shadow as `GIT_DIR`, and `workTree` as its work tree when given. */
|
|
77
77
|
readonly git: (args: ReadonlyArray<string>, workTree?: string) => Effect.Effect<Exec.ExecResult, StdError.StdError, ChildProcessSpawner>;
|
|
78
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Copies the workspace index into the shadow, with its modification time, so
|
|
80
|
+
* the shadow never writes the agent's own.
|
|
81
|
+
*
|
|
82
|
+
* The time is part of what git reads. It trusts an entry's recorded stat
|
|
83
|
+
* unless the entry is at least as new as the index file, in whole seconds;
|
|
84
|
+
* a copy stamped at the moment of copying made every entry look older, and a
|
|
85
|
+
* same-size edit from the second the index was written was recorded as the
|
|
86
|
+
* committed bytes (#3431). The time is taken before the bytes are read, so
|
|
87
|
+
* an index rewritten meanwhile leaves the copy looking older, never newer.
|
|
88
|
+
*/
|
|
79
89
|
readonly adoptIndex: Effect.Effect<void, StdError.StdError, ChildProcessSpawner>;
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitWorktree.d.ts","sourceRoot":"","sources":["../../../src/internal/GitWorktree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AA+CjC;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACzC;AAuDD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,8FAA8F;IAC9F,QAAQ,CAAC,GAAG,EAAE,CACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;IAC3E
|
|
1
|
+
{"version":3,"file":"GitWorktree.d.ts","sourceRoot":"","sources":["../../../src/internal/GitWorktree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AA+CjC;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACzC;AAuDD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,8FAA8F;IAC9F,QAAQ,CAAC,GAAG,EAAE,CACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;IAC3E;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;CACjF;AAmSD;;;;;GAKG;AACH,eAAO,MAAM,WAAW;yBA/WI,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,aAAa,CAAC,MAAM,CAAC;gBA1ChE,MAAM,QAAQ,aAAa,CAAC,MAAM,CAAC;iBA6ClC,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;0BA3CjE,MAAM,QAAQ,aAAa,CAAC,MAAM,CAAC;;;;2BAmXlC,CAAC,EAAE,CAAC,EAAE,CAAC,QAC7B,MAAM,UACJ,MAAM,UACN,MAAM,OACT,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,mBAAmB,CAAC;iBA3N/C,CAAC,EAAE,CAAC,EAAE,CAAC,QACnB,MAAM,GAAG,UAAU,OACpB,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,mBAAmB,CAAC;CAuP8B,CAAA"}
|
|
@@ -119,6 +119,13 @@ const temporaryRoot = () => {
|
|
|
119
119
|
return configured === undefined || configured === "" ? "/tmp" : configured;
|
|
120
120
|
};
|
|
121
121
|
const remove = (path) => run("rm", ["-rf", "--", path]).pipe(Effect.flatMap((removed) => removed.exitCode === 0 ? Effect.void : Effect.fail(failed(removed.stderr.trim()))));
|
|
122
|
+
/** The `adoptIndex` script: `$1` is the workspace index, `$2` the shadow's. */
|
|
123
|
+
const adoptIndexScript = [
|
|
124
|
+
"set -C",
|
|
125
|
+
"if [ -e \"$1\" ]",
|
|
126
|
+
"then : > \"$2.at\" && touch -r \"$1\" -- \"$2.at\" && cat -- \"$1\" > \"$2\" && touch -r \"$2.at\" -- \"$2\" && rm -f -- \"$2.at\"",
|
|
127
|
+
"fi"
|
|
128
|
+
].join("; ");
|
|
122
129
|
const shadowOf = (repository, path) => ({
|
|
123
130
|
path,
|
|
124
131
|
repository,
|
|
@@ -131,7 +138,7 @@ const shadowOf = (repository, path) => ({
|
|
|
131
138
|
], { ...isolated, GIT_OBJECT_DIRECTORY: `${repository.common}/objects` }),
|
|
132
139
|
adoptIndex: run("sh", [
|
|
133
140
|
"-c",
|
|
134
|
-
|
|
141
|
+
adoptIndexScript,
|
|
135
142
|
"sh",
|
|
136
143
|
repository.index,
|
|
137
144
|
`${path}/index`
|