@coryrylan/cradle 0.0.0 → 1.2.0

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.
@@ -45,7 +45,7 @@ export interface ProfileSpec {
45
45
  * `extends default`, pulls the `node_runtime` group, and grants mise/pi/say
46
46
  * paths) merged with this run's grants — the target cwd, the agent folder, the
47
47
  * state dir, the linked git dir when cwd is a worktree/submodule checkout, and
48
- * the agent's own `sandbox/nono.json` entries. Regenerated every run into the
48
+ * the agent's own path and direct-child Unix socket grants from `sandbox/nono.json`. Regenerated every run into the
49
49
  * state dir, so there is no shared global profile: each agent's permissions
50
50
  * are fully described by its own directory.
51
51
  *
@@ -0,0 +1,115 @@
1
+ import type { AgentNetwork, AgentSandboxGrants } from '../agent/folder.js';
2
+ /**
3
+ * Guest `localhost` resolves to the microVM itself, not the host — host
4
+ * loopback is reachable at this gateway, which sbx routes through the policy
5
+ * proxy. Used both to rewrite `allowDomain` localhost entries (see
6
+ * `composeSbxPolicyArgvs`) and caller-supplied base URLs (see
7
+ * `rewriteLocalhostBaseUrl`).
8
+ */
9
+ export declare const SBX_HOST_GATEWAY = "host.docker.internal";
10
+ /** A single sbx mount: a host path (preserved verbatim in the guest) and its access mode. */
11
+ export interface SbxMount {
12
+ readonly path: string;
13
+ readonly readonly: boolean;
14
+ }
15
+ /**
16
+ * Everything `composeSbxCreateArgv`, `composeSbxPolicyArgvs`,
17
+ * `composeSbxProvisionArgv`, and `composeSbxExecArgv` need — the sbx analog of
18
+ * nono's `ProfileSpec`.
19
+ */
20
+ export interface SbxSpec {
21
+ /** Resolved path, or bare `sbx` for dry-run previews. */
22
+ readonly sbxBin: string;
23
+ /** Sandbox name from `sbxSandboxName()`. */
24
+ readonly name: string;
25
+ /** The target project — always the first (rw) workspace mount. */
26
+ readonly cwd: string;
27
+ /** Host home dir — for the exec `HOME` override and the `~/.pi/agent` mount. */
28
+ readonly home: string;
29
+ /** From `composeSbxMounts()`; cwd first. Fixed at sandbox creation time. */
30
+ readonly mounts: readonly SbxMount[];
31
+ /** Precedence-resolved network posture — the same shape nono consumes. */
32
+ readonly network?: AgentNetwork;
33
+ /** Host pi version to pin in-guest; `null` = install only if missing. */
34
+ readonly piVersion: string | null;
35
+ /** Pass `-t` on exec — set when a TTY is present. */
36
+ readonly tty: boolean;
37
+ }
38
+ /** Inputs to `composeSbxMounts` — everything that can contribute a guest mount. */
39
+ export interface SbxMountContext {
40
+ readonly cwd: string;
41
+ readonly agentDir: string;
42
+ readonly stateDir: string;
43
+ readonly home: string;
44
+ readonly grants: AgentSandboxGrants;
45
+ /**
46
+ * The real git dir when `cwd` is a linked worktree or submodule checkout
47
+ * (see `agent/linked-git-dir.ts`) — mirrors nono's `ProfileSpec.linkedGitDir`.
48
+ * Absent for regular repos, where `.git` already sits inside the cwd mount.
49
+ */
50
+ readonly linkedGitDir?: string;
51
+ }
52
+ /**
53
+ * Build this run's guest mount list: cwd (rw), the agent folder (ro), the
54
+ * state dir (rw), `~/.pi/agent` (rw — pi's auth/settings store; the exec
55
+ * `HOME` override in `composeSbxExecArgv` makes pi find it there), the linked
56
+ * git dir when present (rw), then the agent's own `sandbox/nono.json` grants
57
+ * (`read` ro, `write`/`allow` rw), each expanded against `home`. Duplicate
58
+ * paths collapse to one entry — rw wins over ro, first-occurrence order
59
+ * otherwise — since sbx takes a flat mount list, not layered grants.
60
+ */
61
+ export declare function composeSbxMounts(ctx: SbxMountContext): SbxMount[];
62
+ /**
63
+ * `cradle-<stateDirBasename>-<hash8>` — hash8 is the first 8 hex chars of a
64
+ * sha256 over the order-insensitive sorted `path:ro|rw` strings, mirroring
65
+ * `agentId` (`agent/state.ts`). Mounts are fixed at sbx creation time, so this
66
+ * name keys the mount set: a changed mount set yields a new sandbox name
67
+ * instead of silently attaching to (and running with) a stale mount list.
68
+ */
69
+ export declare function sbxSandboxName(stateDirBasename: string, mounts: readonly SbxMount[]): string;
70
+ /** `sbx create shell <path>[:ro]... --name <name> -q`. `spec.mounts` already carries cwd first. */
71
+ export declare function composeSbxCreateArgv(spec: SbxSpec): string[];
72
+ /**
73
+ * Per-sandbox network policy argvs (idempotent re-adds — safe to run every
74
+ * launch). `[]` when `spec.network` is absent. A `block: true` posture emits a
75
+ * single deny-all — `allowDomain` is ignored under block, matching nono's
76
+ * block-wins precedence, since a same-scope deny always beats an allow.
77
+ * Otherwise, a non-empty `allowDomain` emits a single allow argv with every
78
+ * expanded resource comma-joined (see `composeAllowResources`); an empty or
79
+ * absent `allowDomain` under a non-blocking posture emits nothing.
80
+ * `openPort`/`listenPort`/`networkProfile` have no sbx policy equivalent —
81
+ * see `sbxNetworkWarnings` for their disclosures.
82
+ */
83
+ export declare function composeSbxPolicyArgvs(spec: SbxSpec): string[][];
84
+ /**
85
+ * Warn-and-drop disclosures for `AgentNetwork` fields sbx cannot enforce the
86
+ * way nono does. Returns `[]` when nothing applies (including when `network`
87
+ * is absent) — this module never throws, it only reports what it dropped.
88
+ */
89
+ export declare function sbxNetworkWarnings(network: AgentNetwork | undefined): string[];
90
+ /** Warn-and-drop disclosure for the one `AgentSandboxGrants` field sbx cannot honor: Unix sockets cannot cross the VM boundary. */
91
+ export declare function sbxGrantWarnings(grants: AgentSandboxGrants): string[];
92
+ /**
93
+ * `sbx exec <name> bash -lc <script>` running an idempotent pi install: with
94
+ * `spec.piVersion` pinned, reinstalls only on a version mismatch (`pi
95
+ * --version` compared against the pin); with `null`, installs only if `pi` is
96
+ * missing from PATH entirely, otherwise leaving whatever version is already
97
+ * there.
98
+ */
99
+ export declare function composeSbxProvisionArgv(spec: SbxSpec): string[];
100
+ /**
101
+ * `sbx exec -i [-t] -e HOME=<home> -w <cwd> <name> <piArgv...>` — docker-exec
102
+ * semantics. `-t` rides only when `spec.tty` is set (a TTY is present);
103
+ * forcing it without one breaks non-interactive callers.
104
+ */
105
+ export declare function composeSbxExecArgv(spec: SbxSpec, piArgv: readonly string[]): string[];
106
+ /** True when `stderr` reports the sandbox name is already taken — callers treat that as attach, not failure. */
107
+ export declare function isSbxAlreadyExistsError(stderr: string): boolean;
108
+ /**
109
+ * Rewrite a `localhost`/`127.0.0.1` base URL to `SBX_HOST_GATEWAY`, preserving
110
+ * scheme, port, and path — a host service bound to loopback is unreachable
111
+ * from inside the sbx guest microVM under its own name, but the gateway
112
+ * routes through the policy proxy back to the host. Any other host, and a
113
+ * parse failure, pass through unchanged.
114
+ */
115
+ export declare function rewriteLocalhostBaseUrl(baseUrl: string): string;
@@ -16,6 +16,17 @@ export declare function killOn(proc: Killable, signal: ForwardableSignal): () =>
16
16
  * sandboxed-run `MISE_CACHE_DIR`, see `agent/launch.ts`'s `composeEnv`).
17
17
  */
18
18
  export declare function runForeground(argv: readonly string[], env?: Record<string, string>): Promise<number>;
19
+ /**
20
+ * Run a setup command silently, capturing stderr for the caller's error
21
+ * message — the sbx create/policy/provision sequence, see
22
+ * `commands/run.ts`'s `MaterializeDeps.run`. Never throws on a non-zero
23
+ * exit: the caller decides which failures matter (an sbx create name
24
+ * collision means attach, not failure).
25
+ */
26
+ export declare function runCapture(argv: readonly string[]): Promise<{
27
+ exitCode: number;
28
+ stderr: string;
29
+ }>;
19
30
  /** Run a package install (e.g. `npm install`) in `cwd`, inheriting output; throws on non-zero exit. */
20
31
  export declare function runInstall(command: readonly string[], cwd: string): Promise<void>;
21
32
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coryrylan/cradle",
3
- "version": "0.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A runtime for portable agents defined as folders — launches the pi coding agent configured from an agent folder, sandboxed with nono",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -220,14 +220,17 @@
220
220
  }
221
221
  },
222
222
  "dependencies": {
223
- "yargs": "18.0.0"
223
+ "yargs": "^18.0.0"
224
224
  },
225
225
  "devDependencies": {
226
- "@eslint/js": "catalog:",
227
- "@types/bun": "catalog:",
226
+ "@coryrylan/tools": "1.1.0",
227
+ "@eslint/js": "10.0.1",
228
+ "@eslint/json": "1.2.0",
229
+ "@types/bun": "1.3.14",
228
230
  "@types/yargs": "17.0.35",
229
- "eslint": "catalog:",
230
- "typescript": "catalog:",
231
- "typescript-eslint": "catalog:"
231
+ "eslint": "10.6.0",
232
+ "eslint-plugin-jsdoc": "63.2.0",
233
+ "typescript": "6.0.3",
234
+ "typescript-eslint": "8.62.1"
232
235
  }
233
236
  }