agentic-pi 0.1.2 → 0.2.1
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 +43 -15
- package/dist/args.d.ts +34 -0
- package/dist/args.js +57 -0
- package/dist/args.js.map +1 -1
- package/dist/extensions/github/index.d.ts +8 -1
- package/dist/extensions/github/index.js +1 -0
- package/dist/extensions/github/index.js.map +1 -1
- package/dist/run.d.ts +30 -0
- package/dist/run.js +3 -0
- package/dist/run.js.map +1 -1
- package/dist/runner.js +78 -11
- package/dist/runner.js.map +1 -1
- package/dist/sandbox/gondolin.d.ts +65 -1
- package/dist/sandbox/gondolin.js +62 -2
- package/dist/sandbox/gondolin.js.map +1 -1
- package/dist/sandbox/images/loader.d.ts +63 -0
- package/dist/sandbox/images/loader.js +195 -0
- package/dist/sandbox/images/loader.js.map +1 -0
- package/dist/sandbox/images/manifest.d.ts +29 -0
- package/dist/sandbox/images/manifest.js +37 -0
- package/dist/sandbox/images/manifest.js.map +1 -0
- package/dist/sandbox/index.d.ts +32 -0
- package/dist/sandbox/index.js +6 -1
- package/dist/sandbox/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -169,16 +169,32 @@ Linux + KVM should be in the same ballpark. Numbers are reproducible
|
|
|
169
169
|
from `test/fixtures/phase3-smoke-sandbox-gondolin.jsonl`.
|
|
170
170
|
|
|
171
171
|
**Event stream.** A `sandbox_status` JSONL line is emitted right after
|
|
172
|
-
the session header
|
|
172
|
+
the session header. It carries an `envKeys` list (just the keys, never
|
|
173
|
+
the values) so consumers can verify which env vars were handed to the VM:
|
|
173
174
|
|
|
174
175
|
```jsonl
|
|
175
|
-
{"type":"sandbox_status","backend":"gondolin","status":{"backend":"gondolin","cwd":"/path/to/workspace","guestPath":"/workspace","createMs":47},"sessionId":"…","timestamp":"…"}
|
|
176
|
+
{"type":"sandbox_status","backend":"gondolin","status":{"backend":"gondolin","cwd":"/path/to/workspace","guestPath":"/workspace","createMs":47,"envKeys":["GH_TOKEN","GITHUB_TOKEN"]},"sessionId":"…","timestamp":"…"}
|
|
176
177
|
```
|
|
177
178
|
|
|
178
179
|
If `--sandbox none` (the default), the same line is still emitted with
|
|
179
180
|
`backend: "none"` so downstream consumers always know which mode the run
|
|
180
181
|
used.
|
|
181
182
|
|
|
183
|
+
**Passing env into the VM.** Use `--sandbox-env KEY=VAL` on the CLI
|
|
184
|
+
(repeatable), or `sandboxEnv: { KEY: "VAL" }` on the programmatic API.
|
|
185
|
+
The agent's `bash` calls see these as ordinary environment variables.
|
|
186
|
+
|
|
187
|
+
When `--profile <github>` is also active and the GitHub extension is
|
|
188
|
+
configured, agentic-pi automatically mints a short-lived installation
|
|
189
|
+
token via the configured auth backend (App JWT exchange, or static
|
|
190
|
+
`GITHUB_TOKEN` passthrough) and injects it as **both** `GITHUB_TOKEN`
|
|
191
|
+
and `GH_TOKEN`. Inside the VM, `git push`, `git fetch`, and `gh`
|
|
192
|
+
commands work without further setup.
|
|
193
|
+
|
|
194
|
+
The **App PEM is never copied into the VM** — only the resulting token,
|
|
195
|
+
which is short-lived. User-supplied `--sandbox-env GITHUB_TOKEN=…`
|
|
196
|
+
overrides the auto-injected value if you need to scope down further.
|
|
197
|
+
|
|
182
198
|
## When to use this
|
|
183
199
|
|
|
184
200
|
- You have an orchestrator that calls a coding agent once per workflow
|
|
@@ -237,6 +253,7 @@ GITHUB_TOKEN=ghp_…
|
|
|
237
253
|
| `--no-builtin-tools` | Disable Pi's `read,write,edit,bash,grep,find,ls`. |
|
|
238
254
|
| `--tools <a,b,c>` | Explicit tool allowlist (combined with profile if set). |
|
|
239
255
|
| `--sandbox <none\|gondolin>` | Route `read`/`write`/`edit`/`bash` through a sandbox backend. Default `none`. `gondolin` boots a QEMU micro-VM mounting cwd at `/workspace`. Requires QEMU on the host; native-only (not Docker-in-Docker). See section 8. |
|
|
256
|
+
| `--sandbox-env KEY=VAL` | Inject env var into the sandbox VM (repeatable). Ignored when `--sandbox=none`. Auto-injects a minted `GITHUB_TOKEN`/`GH_TOKEN` when `--profile` is also active. |
|
|
240
257
|
| `--dangerously-skip-permissions` | Accepted for caller-side compatibility. No-op. |
|
|
241
258
|
|
|
242
259
|
Reads the prompt from stdin. Emits JSONL on stdout. Exits 0 on `agent_end`,
|
|
@@ -283,6 +300,14 @@ const result = await run({
|
|
|
283
300
|
noSession: true,
|
|
284
301
|
cwd: "/path/to/workspace",
|
|
285
302
|
|
|
303
|
+
// Per-run env handed to the sandbox VM. Ignored when sandbox="none".
|
|
304
|
+
// When sandbox="gondolin" + profile is set, GITHUB_TOKEN/GH_TOKEN are
|
|
305
|
+
// auto-injected from a minted installation token — explicit values
|
|
306
|
+
// here win.
|
|
307
|
+
sandboxEnv: {
|
|
308
|
+
CI_BUILD_REF: process.env.GITHUB_SHA ?? "",
|
|
309
|
+
},
|
|
310
|
+
|
|
286
311
|
// Optional observability hooks. Both are pure callbacks — no I/O happens
|
|
287
312
|
// unless you do something with the values.
|
|
288
313
|
onEvent: (record) => myShim.writeJsonl(record),
|
|
@@ -382,19 +407,22 @@ agentic-pi publishes to npm via a GitHub Actions workflow using **npm
|
|
|
382
407
|
trusted publishing** (OIDC) — no `NPM_TOKEN` secret is needed in the
|
|
383
408
|
repo.
|
|
384
409
|
|
|
385
|
-
To cut a release
|
|
386
|
-
|
|
387
|
-
1. Bump `version` in `package.json` — `npm version patch`
|
|
388
|
-
commit and tag
|
|
389
|
-
2. Push the commit and tag: `git push --follow-tags`.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
410
|
+
To cut a release:
|
|
411
|
+
|
|
412
|
+
1. Bump `version` in `package.json` — `npm version patch` (or `minor` /
|
|
413
|
+
`major`) does the version bump, the commit, and the tag in one step.
|
|
414
|
+
2. Push the commit and tag: `git push --follow-tags`. CI runs against
|
|
415
|
+
the version-bump commit on `main`.
|
|
416
|
+
3. Create a GitHub Release on the new tag once CI is green:
|
|
417
|
+
`gh release create v0.2.0 --generate-notes` or via the web UI.
|
|
418
|
+
4. The `publish.yml` workflow runs on the `release: published` event —
|
|
419
|
+
this is the only auto-trigger. (CI does not re-run; `publish.yml`
|
|
420
|
+
re-validates type-check, build, and unit tests itself so nothing is
|
|
421
|
+
skipped.)
|
|
422
|
+
|
|
423
|
+
`publish.yml` also accepts a manual `workflow_dispatch` with an explicit
|
|
424
|
+
tag/ref — useful if a release-triggered run failed (network, OIDC config
|
|
425
|
+
not yet set up) and you want to retry without re-cutting the release.
|
|
398
426
|
|
|
399
427
|
The publish step fails if the tag (or the dispatch `ref` input) doesn't
|
|
400
428
|
match `package.json` version — there is no path that publishes a version
|
package/dist/args.d.ts
CHANGED
|
@@ -26,6 +26,40 @@ export interface RunConfig {
|
|
|
26
26
|
dangerouslySkipPermissions: boolean;
|
|
27
27
|
/** Sandbox backend for read/write/edit/bash. */
|
|
28
28
|
sandbox: "none" | "gondolin";
|
|
29
|
+
/**
|
|
30
|
+
* Image to boot when `sandbox === "gondolin"`. Resolved by the image
|
|
31
|
+
* loader:
|
|
32
|
+
* - `"default"` → bundled `agentic-pi-dev` manifest (auto-downloaded).
|
|
33
|
+
* - `"gondolin-builtin"` → gondolin's built-in `alpine-base:latest`.
|
|
34
|
+
* - absolute path → a local `gondolin build` output directory.
|
|
35
|
+
* Ignored when `sandbox === "none"`. Default: `"default"`.
|
|
36
|
+
*/
|
|
37
|
+
sandboxImage?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Environment variables to inject into the sandbox VM. Ignored when
|
|
40
|
+
* sandbox === "none" (Pi's host tools already inherit process.env).
|
|
41
|
+
* Use this to hand `GITHUB_TOKEN`, secrets, or workflow context to the
|
|
42
|
+
* agent's `bash` calls inside the VM.
|
|
43
|
+
*
|
|
44
|
+
* Set via `--sandbox-env KEY=VAL` (repeatable). When `--profile` is
|
|
45
|
+
* active and the GitHub extension is configured, a short-lived
|
|
46
|
+
* installation token is auto-injected as both `GITHUB_TOKEN` and
|
|
47
|
+
* `GH_TOKEN`. User-provided values override the auto-injected ones.
|
|
48
|
+
*/
|
|
49
|
+
sandboxEnv?: Record<string, string>;
|
|
50
|
+
/**
|
|
51
|
+
* HTTP egress allowlist for the sandbox VM. Without this, gondolin
|
|
52
|
+
* returns 502 to every outbound request from inside the VM —
|
|
53
|
+
* `git clone`, `git push`, `gh api`, `npm install`, `pip install` all
|
|
54
|
+
* fail. Default: a built-in GitHub-only list (github.com,
|
|
55
|
+
* api.github.com, codeload.github.com, objects.githubusercontent.com,
|
|
56
|
+
* raw.githubusercontent.com).
|
|
57
|
+
*
|
|
58
|
+
* Set explicit hosts via `--allow-host <host>` (repeatable) to extend
|
|
59
|
+
* or replace the default. Pass `--no-network` to disable HTTP egress
|
|
60
|
+
* entirely. Ignored when `sandbox === "none"`.
|
|
61
|
+
*/
|
|
62
|
+
allowedHttpHosts?: string[] | null;
|
|
29
63
|
}
|
|
30
64
|
export declare function printHelp(): void;
|
|
31
65
|
export declare function parseArgs(argv: string[]): RunConfig;
|
package/dist/args.js
CHANGED
|
@@ -24,6 +24,22 @@ Flags:
|
|
|
24
24
|
Default: none. 'gondolin' boots a per-run QEMU micro-VM
|
|
25
25
|
mounting the cwd at /workspace. Requires QEMU on host;
|
|
26
26
|
native only (Docker-in-Docker not viable; see SPIKE-gondolin.md).
|
|
27
|
+
--sandbox-env KEY=VAL Inject env var into the sandbox VM. Repeatable.
|
|
28
|
+
Ignored when --sandbox=none. When --profile is active,
|
|
29
|
+
GITHUB_TOKEN and GH_TOKEN are auto-injected from a minted
|
|
30
|
+
installation token (App PEM never enters the VM).
|
|
31
|
+
--allow-host <host> Add a host to the sandbox HTTP egress allowlist.
|
|
32
|
+
Repeatable. First explicit use replaces the default
|
|
33
|
+
GitHub-only allowlist; subsequent uses extend it.
|
|
34
|
+
Ignored when --sandbox=none.
|
|
35
|
+
--no-network Disable HTTP egress from the sandbox entirely.
|
|
36
|
+
Ignored when --sandbox=none.
|
|
37
|
+
--sandbox-image <name> Image to boot when --sandbox=gondolin. Values:
|
|
38
|
+
'default' (recommended) — bundled agentic-pi-dev image
|
|
39
|
+
with git/gh/node/python/rust baked in (auto-downloaded).
|
|
40
|
+
'gondolin-builtin' — stock alpine-base:latest, no extras.
|
|
41
|
+
<absolute path> — directory produced by 'gondolin build'.
|
|
42
|
+
Default: 'default'.
|
|
27
43
|
--dangerously-skip-permissions Accepted for compat; Pi has no permission prompts anyway
|
|
28
44
|
|
|
29
45
|
Reads the prompt from stdin. Emits Pi-native JSONL events on stdout, terminating
|
|
@@ -90,6 +106,47 @@ export function parseArgs(argv) {
|
|
|
90
106
|
config.sandbox = v;
|
|
91
107
|
break;
|
|
92
108
|
}
|
|
109
|
+
case "--sandbox-image": {
|
|
110
|
+
const v = next();
|
|
111
|
+
if (v.length === 0) {
|
|
112
|
+
throw new Error(`--sandbox-image requires a non-empty value`);
|
|
113
|
+
}
|
|
114
|
+
config.sandboxImage = v;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "--sandbox-env": {
|
|
118
|
+
const v = next();
|
|
119
|
+
const eq = v.indexOf("=");
|
|
120
|
+
if (eq < 1) {
|
|
121
|
+
throw new Error(`--sandbox-env must be KEY=VAL (got '${v}')`);
|
|
122
|
+
}
|
|
123
|
+
const key = v.slice(0, eq);
|
|
124
|
+
const val = v.slice(eq + 1);
|
|
125
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
126
|
+
throw new Error(`--sandbox-env KEY must match POSIX identifier rules (got '${key}')`);
|
|
127
|
+
}
|
|
128
|
+
config.sandboxEnv = { ...(config.sandboxEnv ?? {}), [key]: val };
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case "--allow-host": {
|
|
132
|
+
const v = next().trim();
|
|
133
|
+
if (!v)
|
|
134
|
+
throw new Error("--allow-host requires a non-empty host");
|
|
135
|
+
if (!/^[A-Za-z0-9.\-*]+$/.test(v)) {
|
|
136
|
+
throw new Error(`--allow-host must be a host pattern (got '${v}')`);
|
|
137
|
+
}
|
|
138
|
+
// First explicit --allow-host replaces the default GitHub list;
|
|
139
|
+
// subsequent ones extend. Pass --no-network first to start from
|
|
140
|
+
// an empty allowlist with HTTP enabled? No — --no-network sets
|
|
141
|
+
// null (HTTP disabled). To start from empty allow-list, pass an
|
|
142
|
+
// explicit `--allow-host` for whatever you want and nothing else.
|
|
143
|
+
const cur = Array.isArray(config.allowedHttpHosts) ? config.allowedHttpHosts : [];
|
|
144
|
+
config.allowedHttpHosts = [...cur, v];
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "--no-network":
|
|
148
|
+
config.allowedHttpHosts = null;
|
|
149
|
+
break;
|
|
93
150
|
case "-h":
|
|
94
151
|
case "--help":
|
|
95
152
|
printHelp();
|
package/dist/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* decide whether to surface a warning.
|
|
11
11
|
*/
|
|
12
12
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { type AuthFailureReason } from "./auth.js";
|
|
13
|
+
import { type AuthFailureReason, type GitHubAuth } from "./auth.js";
|
|
14
14
|
import { type GitAccessProfile } from "./profiles.js";
|
|
15
15
|
export { isGitAccessProfile, type GitAccessProfile } from "./profiles.js";
|
|
16
16
|
/** Why the extension didn't load tools. */
|
|
@@ -28,6 +28,13 @@ export interface GitHubExtensionResult {
|
|
|
28
28
|
message?: string;
|
|
29
29
|
/** Always echoed back so the consumer knows what they asked for. */
|
|
30
30
|
profile?: GitAccessProfile;
|
|
31
|
+
/**
|
|
32
|
+
* The auth backend that was constructed (App or static-token). Exposed
|
|
33
|
+
* so the runner can mint a short-lived installation token to inject
|
|
34
|
+
* into the sandbox VM as `GITHUB_TOKEN`. Only present when
|
|
35
|
+
* `status === "configured"`.
|
|
36
|
+
*/
|
|
37
|
+
auth?: GitHubAuth;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* Build the GitHub extension for a given profile.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/github/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/github/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,gBAAgB,EAA2C,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,kBAAkB,GAEnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAyB,MAAM,eAAe,CAAC;AA8B1E;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAoB;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;YACL,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,YAAY;SACrB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,2BAA2B,WAAW,uBAAuB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrG,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACrD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,OAAO;YACP,OAAO,EAAE,WAAW;SACrB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjE,OAAO;QACL,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,WAAW;QACpB,IAAI;KACL,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA6B;IAClE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,MAAM,CAAC,MAAM,KAAK,gBAAgB,IAAI,MAAM,CAAC,MAAM,KAAK,gBAAgB,CAAC;AAClF,CAAC"}
|
package/dist/run.d.ts
CHANGED
|
@@ -31,6 +31,15 @@ export interface RunOptions {
|
|
|
31
31
|
profile?: string;
|
|
32
32
|
/** Sandbox backend. Default: "none". */
|
|
33
33
|
sandbox?: "none" | "gondolin";
|
|
34
|
+
/**
|
|
35
|
+
* Image to boot when `sandbox: "gondolin"`. Values:
|
|
36
|
+
* - `"default"` (recommended) — bundled `agentic-pi-dev` image
|
|
37
|
+
* (auto-downloaded into `~/.cache/agentic-pi/images/`).
|
|
38
|
+
* - `"gondolin-builtin"` — stock `alpine-base:latest`, no toolchain.
|
|
39
|
+
* - absolute path — local `gondolin build` output directory.
|
|
40
|
+
* Default: `"default"`.
|
|
41
|
+
*/
|
|
42
|
+
sandboxImage?: string;
|
|
34
43
|
/** Working directory. Default: process.cwd(). */
|
|
35
44
|
cwd?: string;
|
|
36
45
|
/** Skip session persistence. Default: false. */
|
|
@@ -41,6 +50,27 @@ export interface RunOptions {
|
|
|
41
50
|
noBuiltinTools?: boolean;
|
|
42
51
|
/** Explicit tool allowlist. */
|
|
43
52
|
tools?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Environment variables to inject into the sandbox VM (ignored when
|
|
55
|
+
* `sandbox: "none"`). When `sandbox: "gondolin"` with a `profile`
|
|
56
|
+
* configured, a short-lived GitHub installation token is auto-injected
|
|
57
|
+
* as `GITHUB_TOKEN` + `GH_TOKEN` — values here override the auto ones.
|
|
58
|
+
*/
|
|
59
|
+
sandboxEnv?: Record<string, string>;
|
|
60
|
+
/**
|
|
61
|
+
* HTTP egress allowlist for the sandbox VM. Without this, gondolin's
|
|
62
|
+
* HTTP interceptor returns 502 to every outbound request.
|
|
63
|
+
*
|
|
64
|
+
* - `undefined` (default): allow the standard GitHub hosts + common
|
|
65
|
+
* public package registries (npm, pypi, crates, go, rubygems,
|
|
66
|
+
* alpine/debian apt). See `DEFAULT_GUEST_ALLOWED_HOSTS` in
|
|
67
|
+
* `sandbox/gondolin.ts` for the exact list.
|
|
68
|
+
* - explicit `string[]`: caller-supplied allowlist (replaces default).
|
|
69
|
+
* - `null`: disable HTTP hooks entirely; gondolin blocks egress.
|
|
70
|
+
*
|
|
71
|
+
* Ignored when `sandbox: "none"`.
|
|
72
|
+
*/
|
|
73
|
+
allowedHttpHosts?: string[] | null;
|
|
44
74
|
/**
|
|
45
75
|
* Called for every emitted JSONL record in order. Same shape that the
|
|
46
76
|
* CLI writes to stdout, with `sessionId` and `timestamp` already injected.
|
package/dist/run.js
CHANGED
|
@@ -43,6 +43,9 @@ export async function run(options) {
|
|
|
43
43
|
tools: options.tools,
|
|
44
44
|
dangerouslySkipPermissions: false,
|
|
45
45
|
sandbox: options.sandbox ?? "none",
|
|
46
|
+
sandboxEnv: options.sandboxEnv,
|
|
47
|
+
sandboxImage: options.sandboxImage,
|
|
48
|
+
allowedHttpHosts: options.allowedHttpHosts,
|
|
46
49
|
};
|
|
47
50
|
const collector = new CollectorSink(options.onEvent);
|
|
48
51
|
const sink = options.extraSink
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,OAAO,EACL,aAAa,EACb,OAAO,GAGR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAwB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.js
CHANGED
|
@@ -15,23 +15,17 @@ import { Emitter } from "./emitter.js";
|
|
|
15
15
|
import { loadGitHubExtension, isMisconfigurationSkip } from "./extensions/github/index.js";
|
|
16
16
|
import { resolveModel } from "./models.js";
|
|
17
17
|
import { buildSandbox } from "./sandbox/index.js";
|
|
18
|
+
import { ensureImage, ImageLoaderError } from "./sandbox/images/loader.js";
|
|
18
19
|
export async function runOnce(config, prompt, deps) {
|
|
19
20
|
const warn = deps.onWarn ?? (() => undefined);
|
|
20
21
|
const authStorage = AuthStorage.create();
|
|
21
22
|
const modelRegistry = ModelRegistry.create(authStorage);
|
|
22
23
|
const model = resolveModel(config.model, modelRegistry);
|
|
23
24
|
const sessionManager = buildSessionManager(config);
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
if (!sandboxOutcome.ok) {
|
|
29
|
-
warn(`--sandbox=${sandboxOutcome.backend} failed (${sandboxOutcome.reason}): ${sandboxOutcome.hint}`);
|
|
30
|
-
return 2;
|
|
31
|
-
}
|
|
32
|
-
const sandbox = sandboxOutcome.sandbox;
|
|
33
|
-
// Build the GitHub extension up-front so we can surface auth issues before
|
|
34
|
-
// creating the session (rather than at first tool call).
|
|
25
|
+
// GitHub extension built FIRST so the runner can mint an installation
|
|
26
|
+
// token before the sandbox boots — the token is one of the env values
|
|
27
|
+
// we hand to the VM. Building the extension is cheap (no LLM, no IO
|
|
28
|
+
// except reading the PEM); failures surface as a warning, not an exit.
|
|
35
29
|
const github = loadGitHubExtension(config.profile);
|
|
36
30
|
// Loud about misconfigurations (partial App creds, unreadable PEM) — the
|
|
37
31
|
// user almost certainly meant for GitHub to work. Silent about benign
|
|
@@ -44,6 +38,79 @@ export async function runOnce(config, prompt, deps) {
|
|
|
44
38
|
config.profile) {
|
|
45
39
|
warn(`--profile=${config.profile} set but no GITHUB_APP_* or GITHUB_TOKEN env vars found; GitHub tools disabled`);
|
|
46
40
|
}
|
|
41
|
+
// Compose the env for the sandbox VM. Order (later wins):
|
|
42
|
+
// 1. Auto-injected GITHUB_TOKEN/GH_TOKEN from a minted installation
|
|
43
|
+
// token (when sandbox=gondolin AND github extension is configured).
|
|
44
|
+
// 2. User-provided --sandbox-env entries.
|
|
45
|
+
// App PEM is never copied into the VM — only the short-lived token.
|
|
46
|
+
const sandboxEnv = {};
|
|
47
|
+
if (config.sandbox === "gondolin" && github.status === "configured" && github.auth) {
|
|
48
|
+
try {
|
|
49
|
+
const token = await github.auth.getToken();
|
|
50
|
+
sandboxEnv.GITHUB_TOKEN = token;
|
|
51
|
+
sandboxEnv.GH_TOKEN = token;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
warn(`Could not mint a GitHub installation token for sandbox env: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (config.sandboxEnv) {
|
|
58
|
+
Object.assign(sandboxEnv, config.sandboxEnv);
|
|
59
|
+
}
|
|
60
|
+
// Resolve --sandbox-image to an absolute path + descriptor. Default
|
|
61
|
+
// when --sandbox=gondolin is "default" (auto-downloaded
|
|
62
|
+
// agentic-pi-dev image). Explicit "gondolin-builtin" opts out.
|
|
63
|
+
let imagePath;
|
|
64
|
+
let imageDescriptor;
|
|
65
|
+
if (config.sandbox === "gondolin") {
|
|
66
|
+
const selector = config.sandboxImage ?? "default";
|
|
67
|
+
try {
|
|
68
|
+
const resolved = await ensureImage(selector);
|
|
69
|
+
if (resolved.kind === "builtin") {
|
|
70
|
+
imageDescriptor = { name: "gondolin-builtin", source: "builtin" };
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
imagePath = resolved.imagePath;
|
|
74
|
+
imageDescriptor = resolved.descriptor;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (err instanceof ImageLoaderError) {
|
|
79
|
+
// When the user didn't explicitly ask for the default image
|
|
80
|
+
// (i.e. they didn't pass --sandbox-image), fall back to the
|
|
81
|
+
// gondolin builtin with a warning so they still get a working
|
|
82
|
+
// sandbox. If they passed --sandbox-image=default explicitly,
|
|
83
|
+
// a failure there is fatal — they asked for this image.
|
|
84
|
+
if (config.sandboxImage === undefined) {
|
|
85
|
+
warn(`default image unavailable (${err.message}); falling back to gondolin-builtin. Hint: ${err.hint}`);
|
|
86
|
+
imageDescriptor = { name: "gondolin-builtin", source: "builtin" };
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
warn(`--sandbox-image=${selector} failed: ${err.message}. Hint: ${err.hint}`);
|
|
90
|
+
return 2;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Build the sandbox backend (boots Gondolin VM if --sandbox gondolin).
|
|
99
|
+
// Done eagerly so VM-boot / preflight failures surface before any tokens
|
|
100
|
+
// are spent on a prompt.
|
|
101
|
+
const sandboxOutcome = await buildSandbox({
|
|
102
|
+
backend: config.sandbox,
|
|
103
|
+
cwd: config.cwd,
|
|
104
|
+
env: Object.keys(sandboxEnv).length > 0 ? sandboxEnv : undefined,
|
|
105
|
+
imagePath,
|
|
106
|
+
image: imageDescriptor,
|
|
107
|
+
allowedHttpHosts: config.allowedHttpHosts,
|
|
108
|
+
});
|
|
109
|
+
if (!sandboxOutcome.ok) {
|
|
110
|
+
warn(`--sandbox=${sandboxOutcome.backend} failed (${sandboxOutcome.reason}): ${sandboxOutcome.hint}`);
|
|
111
|
+
return 2;
|
|
112
|
+
}
|
|
113
|
+
const sandbox = sandboxOutcome.sandbox;
|
|
47
114
|
// When a sandbox is active it supplies its own read/write/edit/bash that
|
|
48
115
|
// route through the VM; Pi's host built-ins of the same names must be
|
|
49
116
|
// suppressed so they don't shadow ours.
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,WAAW,EACX,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AAIzC,OAAO,EAAE,OAAO,EAAoB,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,60 @@
|
|
|
16
16
|
* upstream example. If full FS isolation is needed later, route those too.
|
|
17
17
|
*/
|
|
18
18
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
import type { ImageDescriptor } from "./index.js";
|
|
20
|
+
export interface GondolinSandboxOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Environment variables to set inside the guest VM. Available to every
|
|
23
|
+
* `bash` invocation the agent makes. Use this to hand credentials,
|
|
24
|
+
* workflow context, or feature flags to the sandboxed process.
|
|
25
|
+
*
|
|
26
|
+
* Note: gondolin docs explicitly warn against baking secrets into
|
|
27
|
+
* pre-built images; runtime env is the supported channel for
|
|
28
|
+
* credentials, which is what this option exists for.
|
|
29
|
+
*/
|
|
30
|
+
env?: Record<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* Absolute path to a gondolin build output directory. When set, the VM
|
|
33
|
+
* boots from these assets instead of gondolin's built-in
|
|
34
|
+
* `alpine-base:latest`. The caller (typically `runner.ts`) is
|
|
35
|
+
* responsible for resolving the user-facing `--sandbox-image` value
|
|
36
|
+
* (default / gondolin-builtin / path) to this absolute path.
|
|
37
|
+
*/
|
|
38
|
+
imagePath?: string;
|
|
39
|
+
/** Descriptor surfaced verbatim in `status.image`. */
|
|
40
|
+
image?: ImageDescriptor;
|
|
41
|
+
/**
|
|
42
|
+
* Hosts the sandboxed guest is allowed to make HTTP(S) egress to.
|
|
43
|
+
* Without an HTTP hook configured, gondolin's HTTP interceptor returns
|
|
44
|
+
* 502 to every outbound request — `git clone`, `git push`, `gh ...`,
|
|
45
|
+
* `npm install`, `pip install` all fail. When this option is set
|
|
46
|
+
* (or left at its default), `createHttpHooks({ allowedHosts })` wires
|
|
47
|
+
* up the egress proxy.
|
|
48
|
+
*
|
|
49
|
+
* Pass `null` to disable HTTP hooks entirely (gondolin will then block
|
|
50
|
+
* all HTTP egress at the QEMU layer). Pass an explicit array to scope
|
|
51
|
+
* down or extend the default. Omit to use the GitHub-only default,
|
|
52
|
+
* which matches agentic-pi's built-in github extension surface.
|
|
53
|
+
*/
|
|
54
|
+
allowedHttpHosts?: string[] | null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Hosts the sandboxed guest can reach by default. The set covers the
|
|
58
|
+
* everyday needs of a coding-agent `bash` session: git over HTTPS, gh,
|
|
59
|
+
* and the common public package registries so `npm install`, `pip
|
|
60
|
+
* install`, `cargo build`, `go mod download`, `bundle install`, and
|
|
61
|
+
* `apk add` work without extra configuration.
|
|
62
|
+
*
|
|
63
|
+
* Model-provider hosts (`api.anthropic.com`, `api.openai.com`, etc.)
|
|
64
|
+
* are deliberately omitted — agentic-pi calls the LLM from the host
|
|
65
|
+
* process, not from inside the sandbox, so the VM never needs to reach
|
|
66
|
+
* them.
|
|
67
|
+
*
|
|
68
|
+
* The list is intentionally limited to **public** registries. Anything
|
|
69
|
+
* private (internal artifact repos, npm enterprise, etc.) must be added
|
|
70
|
+
* explicitly via `--allow-host` / `allowedHttpHosts: [...]`.
|
|
71
|
+
*/
|
|
72
|
+
export declare const DEFAULT_GUEST_ALLOWED_HOSTS: readonly string[];
|
|
19
73
|
export interface GondolinSandbox {
|
|
20
74
|
/** Tools to pass into `createAgentSession({ customTools })`. */
|
|
21
75
|
customTools: ToolDefinition<any>[];
|
|
@@ -27,6 +81,16 @@ export interface GondolinSandbox {
|
|
|
27
81
|
cwd: string;
|
|
28
82
|
guestPath: string;
|
|
29
83
|
createMs: number;
|
|
84
|
+
/** Sorted list of env var KEY names injected (values omitted for safety). */
|
|
85
|
+
envKeys: string[];
|
|
86
|
+
/** Image descriptor (omitted when caller didn't supply one). */
|
|
87
|
+
image?: ImageDescriptor;
|
|
88
|
+
/**
|
|
89
|
+
* Resolved HTTP egress allowlist. `null` when HTTP hooks were disabled
|
|
90
|
+
* (caller passed `allowedHttpHosts: null`). Omitted when the backend
|
|
91
|
+
* doesn't have a meaningful HTTP policy.
|
|
92
|
+
*/
|
|
93
|
+
allowedHttpHosts?: string[] | null;
|
|
30
94
|
};
|
|
31
95
|
}
|
|
32
96
|
/**
|
|
@@ -36,4 +100,4 @@ export interface GondolinSandbox {
|
|
|
36
100
|
* Throws if VM.create rejects. The preflight check is the caller's
|
|
37
101
|
* responsibility — call `preflightGondolin()` first.
|
|
38
102
|
*/
|
|
39
|
-
export declare function buildGondolinSandbox(cwd: string): Promise<GondolinSandbox>;
|
|
103
|
+
export declare function buildGondolinSandbox(cwd: string, options?: GondolinSandboxOptions): Promise<GondolinSandbox>;
|
package/dist/sandbox/gondolin.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import { createBashTool, createEditTool, createReadTool, createWriteTool, } from "@earendil-works/pi-coding-agent";
|
|
20
|
-
import { RealFSProvider, VM } from "@earendil-works/gondolin";
|
|
20
|
+
import { RealFSProvider, VM, createHttpHooks } from "@earendil-works/gondolin";
|
|
21
21
|
const GUEST_WORKSPACE = "/workspace";
|
|
22
22
|
function shQuote(value) {
|
|
23
23
|
// POSIX shell quoting: wrap in single quotes; escape any internal quote.
|
|
@@ -148,6 +148,49 @@ function createGondolinBashOps(vm, localCwd) {
|
|
|
148
148
|
},
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Hosts the sandboxed guest can reach by default. The set covers the
|
|
153
|
+
* everyday needs of a coding-agent `bash` session: git over HTTPS, gh,
|
|
154
|
+
* and the common public package registries so `npm install`, `pip
|
|
155
|
+
* install`, `cargo build`, `go mod download`, `bundle install`, and
|
|
156
|
+
* `apk add` work without extra configuration.
|
|
157
|
+
*
|
|
158
|
+
* Model-provider hosts (`api.anthropic.com`, `api.openai.com`, etc.)
|
|
159
|
+
* are deliberately omitted — agentic-pi calls the LLM from the host
|
|
160
|
+
* process, not from inside the sandbox, so the VM never needs to reach
|
|
161
|
+
* them.
|
|
162
|
+
*
|
|
163
|
+
* The list is intentionally limited to **public** registries. Anything
|
|
164
|
+
* private (internal artifact repos, npm enterprise, etc.) must be added
|
|
165
|
+
* explicitly via `--allow-host` / `allowedHttpHosts: [...]`.
|
|
166
|
+
*/
|
|
167
|
+
export const DEFAULT_GUEST_ALLOWED_HOSTS = [
|
|
168
|
+
// GitHub — git over HTTPS + gh CLI
|
|
169
|
+
"github.com",
|
|
170
|
+
"api.github.com",
|
|
171
|
+
"codeload.github.com",
|
|
172
|
+
"objects.githubusercontent.com",
|
|
173
|
+
"raw.githubusercontent.com",
|
|
174
|
+
// npm / yarn / pnpm
|
|
175
|
+
"registry.npmjs.org",
|
|
176
|
+
"registry.yarnpkg.com",
|
|
177
|
+
// Python — pypi + wheels CDN
|
|
178
|
+
"pypi.org",
|
|
179
|
+
"files.pythonhosted.org",
|
|
180
|
+
// Rust
|
|
181
|
+
"crates.io",
|
|
182
|
+
"static.crates.io",
|
|
183
|
+
"index.crates.io",
|
|
184
|
+
// Go modules
|
|
185
|
+
"proxy.golang.org",
|
|
186
|
+
"sum.golang.org",
|
|
187
|
+
// Ruby
|
|
188
|
+
"rubygems.org",
|
|
189
|
+
// Alpine apk + Debian apt mirrors (the apk on `apk add` etc.)
|
|
190
|
+
"dl-cdn.alpinelinux.org",
|
|
191
|
+
"deb.debian.org",
|
|
192
|
+
"security.debian.org",
|
|
193
|
+
];
|
|
151
194
|
/**
|
|
152
195
|
* Boot a Gondolin VM mounting `cwd` at /workspace, and build the four
|
|
153
196
|
* Pi tool overrides (read, write, edit, bash) that route through it.
|
|
@@ -155,7 +198,18 @@ function createGondolinBashOps(vm, localCwd) {
|
|
|
155
198
|
* Throws if VM.create rejects. The preflight check is the caller's
|
|
156
199
|
* responsibility — call `preflightGondolin()` first.
|
|
157
200
|
*/
|
|
158
|
-
export async function buildGondolinSandbox(cwd) {
|
|
201
|
+
export async function buildGondolinSandbox(cwd, options = {}) {
|
|
202
|
+
const env = options.env;
|
|
203
|
+
const imagePath = options.imagePath;
|
|
204
|
+
// HTTP egress policy. `undefined` (default) → GitHub-only allowlist via
|
|
205
|
+
// createHttpHooks; explicit array → caller-provided allowlist; `null` →
|
|
206
|
+
// skip hooks entirely (gondolin then blocks all HTTP at the QEMU layer).
|
|
207
|
+
const allowedHosts = options.allowedHttpHosts === undefined
|
|
208
|
+
? [...DEFAULT_GUEST_ALLOWED_HOSTS]
|
|
209
|
+
: options.allowedHttpHosts;
|
|
210
|
+
const httpConfig = allowedHosts === null
|
|
211
|
+
? undefined
|
|
212
|
+
: createHttpHooks({ allowedHosts });
|
|
159
213
|
const t0 = Date.now();
|
|
160
214
|
const vm = await VM.create({
|
|
161
215
|
vfs: {
|
|
@@ -163,6 +217,9 @@ export async function buildGondolinSandbox(cwd) {
|
|
|
163
217
|
[GUEST_WORKSPACE]: new RealFSProvider(cwd),
|
|
164
218
|
},
|
|
165
219
|
},
|
|
220
|
+
...(imagePath ? { sandbox: { imagePath } } : {}),
|
|
221
|
+
...(env && Object.keys(env).length > 0 ? { env } : {}),
|
|
222
|
+
...(httpConfig ? { httpHooks: httpConfig.httpHooks } : {}),
|
|
166
223
|
});
|
|
167
224
|
const createMs = Date.now() - t0;
|
|
168
225
|
// Confirm the VM is actually executable before returning. Without this
|
|
@@ -198,6 +255,9 @@ export async function buildGondolinSandbox(cwd) {
|
|
|
198
255
|
cwd,
|
|
199
256
|
guestPath: GUEST_WORKSPACE,
|
|
200
257
|
createMs,
|
|
258
|
+
envKeys: env ? Object.keys(env).sort() : [],
|
|
259
|
+
...(options.image ? { image: options.image } : {}),
|
|
260
|
+
allowedHttpHosts: allowedHosts === null ? null : [...allowedHosts],
|
|
201
261
|
},
|
|
202
262
|
close: async () => {
|
|
203
263
|
if (closed)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin.js","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAKL,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"gondolin.js","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image loader for `--sandbox-image`.
|
|
3
|
+
*
|
|
4
|
+
* Three resolution modes:
|
|
5
|
+
* - `"gondolin-builtin"` → `{ kind: "builtin" }` (let gondolin pick).
|
|
6
|
+
* - absolute path → `{ kind: "local", imagePath }`.
|
|
7
|
+
* - `"default"` → resolves the bundled DEFAULT_IMAGE_MANIFEST,
|
|
8
|
+
* verifies the per-arch tarball against the
|
|
9
|
+
* baked sha256, extracts into
|
|
10
|
+
* `~/.cache/agentic-pi/images/<sha>/`,
|
|
11
|
+
* returns that path. Atomic: extract to
|
|
12
|
+
* `<sha>.tmp/` then rename.
|
|
13
|
+
*
|
|
14
|
+
* The cache layout is `<cacheRoot>/images/<sha256>/` — one directory
|
|
15
|
+
* per sha, so different image versions coexist and verification on
|
|
16
|
+
* disk is just `existsSync(dir)`. No eviction (Phase B4 open question).
|
|
17
|
+
*
|
|
18
|
+
* Network errors throw a typed `ImageLoaderError` with `hint` so the
|
|
19
|
+
* runner can surface `--sandbox-image gondolin-builtin` as the escape
|
|
20
|
+
* hatch without parsing the message.
|
|
21
|
+
*/
|
|
22
|
+
import { type ImageManifest } from "./manifest.js";
|
|
23
|
+
export type LoaderArch = "aarch64" | "x86_64";
|
|
24
|
+
export type ImageResolution = {
|
|
25
|
+
kind: "builtin";
|
|
26
|
+
} | {
|
|
27
|
+
kind: "local";
|
|
28
|
+
imagePath: string;
|
|
29
|
+
descriptor: {
|
|
30
|
+
name: string;
|
|
31
|
+
source: "local-path";
|
|
32
|
+
};
|
|
33
|
+
} | {
|
|
34
|
+
kind: "downloaded";
|
|
35
|
+
imagePath: string;
|
|
36
|
+
descriptor: {
|
|
37
|
+
name: string;
|
|
38
|
+
version: string;
|
|
39
|
+
source: "cached" | "downloaded";
|
|
40
|
+
downloadMs?: number;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare class ImageLoaderError extends Error {
|
|
44
|
+
readonly hint: string;
|
|
45
|
+
constructor(message: string, hint: string);
|
|
46
|
+
}
|
|
47
|
+
export interface EnsureImageOptions {
|
|
48
|
+
/** Override the manifest (used by tests). Defaults to DEFAULT_IMAGE_MANIFEST. */
|
|
49
|
+
manifest?: ImageManifest;
|
|
50
|
+
/** Override the cache root. Defaults to `<XDG_CACHE_HOME or ~/.cache>/agentic-pi`. */
|
|
51
|
+
cacheRoot?: string;
|
|
52
|
+
/** Override the detected host arch. */
|
|
53
|
+
arch?: LoaderArch;
|
|
54
|
+
/** Fetch function (DI for tests). Defaults to global `fetch`. */
|
|
55
|
+
fetch?: typeof fetch;
|
|
56
|
+
}
|
|
57
|
+
export declare function detectArch(): LoaderArch;
|
|
58
|
+
export declare function defaultCacheRoot(): string;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a `--sandbox-image` selector to a concrete path or builtin
|
|
61
|
+
* directive. Pure async; no side effects beyond the cache directory.
|
|
62
|
+
*/
|
|
63
|
+
export declare function ensureImage(name: string, options?: EnsureImageOptions): Promise<ImageResolution>;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image loader for `--sandbox-image`.
|
|
3
|
+
*
|
|
4
|
+
* Three resolution modes:
|
|
5
|
+
* - `"gondolin-builtin"` → `{ kind: "builtin" }` (let gondolin pick).
|
|
6
|
+
* - absolute path → `{ kind: "local", imagePath }`.
|
|
7
|
+
* - `"default"` → resolves the bundled DEFAULT_IMAGE_MANIFEST,
|
|
8
|
+
* verifies the per-arch tarball against the
|
|
9
|
+
* baked sha256, extracts into
|
|
10
|
+
* `~/.cache/agentic-pi/images/<sha>/`,
|
|
11
|
+
* returns that path. Atomic: extract to
|
|
12
|
+
* `<sha>.tmp/` then rename.
|
|
13
|
+
*
|
|
14
|
+
* The cache layout is `<cacheRoot>/images/<sha256>/` — one directory
|
|
15
|
+
* per sha, so different image versions coexist and verification on
|
|
16
|
+
* disk is just `existsSync(dir)`. No eviction (Phase B4 open question).
|
|
17
|
+
*
|
|
18
|
+
* Network errors throw a typed `ImageLoaderError` with `hint` so the
|
|
19
|
+
* runner can surface `--sandbox-image gondolin-builtin` as the escape
|
|
20
|
+
* hatch without parsing the message.
|
|
21
|
+
*/
|
|
22
|
+
import { spawn } from "node:child_process";
|
|
23
|
+
import { createHash } from "node:crypto";
|
|
24
|
+
import { createWriteStream } from "node:fs";
|
|
25
|
+
import { mkdir, rename, rm, stat } from "node:fs/promises";
|
|
26
|
+
import { tmpdir, homedir, arch as osArch } from "node:os";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { pipeline } from "node:stream/promises";
|
|
29
|
+
import { DEFAULT_IMAGE_MANIFEST, isManifestPublished, } from "./manifest.js";
|
|
30
|
+
export class ImageLoaderError extends Error {
|
|
31
|
+
hint;
|
|
32
|
+
constructor(message, hint) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "ImageLoaderError";
|
|
35
|
+
this.hint = hint;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function detectArch() {
|
|
39
|
+
const a = osArch();
|
|
40
|
+
if (a === "arm64")
|
|
41
|
+
return "aarch64";
|
|
42
|
+
if (a === "x64")
|
|
43
|
+
return "x86_64";
|
|
44
|
+
throw new ImageLoaderError(`unsupported host arch '${a}' for default image`, "use --sandbox-image gondolin-builtin or build a local image for your arch");
|
|
45
|
+
}
|
|
46
|
+
export function defaultCacheRoot() {
|
|
47
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
48
|
+
const base = xdg && xdg.length > 0 ? xdg : path.join(homedir(), ".cache");
|
|
49
|
+
return path.join(base, "agentic-pi");
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a `--sandbox-image` selector to a concrete path or builtin
|
|
53
|
+
* directive. Pure async; no side effects beyond the cache directory.
|
|
54
|
+
*/
|
|
55
|
+
export async function ensureImage(name, options = {}) {
|
|
56
|
+
if (name === "gondolin-builtin") {
|
|
57
|
+
return { kind: "builtin" };
|
|
58
|
+
}
|
|
59
|
+
if (path.isAbsolute(name)) {
|
|
60
|
+
return {
|
|
61
|
+
kind: "local",
|
|
62
|
+
imagePath: name,
|
|
63
|
+
descriptor: { name: path.basename(name), source: "local-path" },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (name !== "default") {
|
|
67
|
+
// Anything else (relative path, unknown name) is a usage error.
|
|
68
|
+
throw new ImageLoaderError(`unrecognized --sandbox-image value '${name}'`, "expected 'default', 'gondolin-builtin', or an absolute path");
|
|
69
|
+
}
|
|
70
|
+
const manifest = options.manifest ?? DEFAULT_IMAGE_MANIFEST;
|
|
71
|
+
if (!isManifestPublished(manifest)) {
|
|
72
|
+
throw new ImageLoaderError(`default image manifest not yet populated (this build of agentic-pi predates the first image release)`, "use --sandbox-image gondolin-builtin until the npm package is rebuilt against a published image");
|
|
73
|
+
}
|
|
74
|
+
const arch = options.arch ?? detectArch();
|
|
75
|
+
const archive = manifest.archives[arch];
|
|
76
|
+
if (!archive || !archive.url || archive.sha256.length !== 64) {
|
|
77
|
+
throw new ImageLoaderError(`default image has no entry for arch '${arch}'`, "use --sandbox-image gondolin-builtin or build a local image");
|
|
78
|
+
}
|
|
79
|
+
const cacheRoot = options.cacheRoot ?? defaultCacheRoot();
|
|
80
|
+
const imageDir = path.join(cacheRoot, "images", archive.sha256);
|
|
81
|
+
// Fast path: a complete extraction is on disk.
|
|
82
|
+
if (await isExtractedImage(imageDir)) {
|
|
83
|
+
return {
|
|
84
|
+
kind: "downloaded",
|
|
85
|
+
imagePath: imageDir,
|
|
86
|
+
descriptor: {
|
|
87
|
+
name: manifest.name,
|
|
88
|
+
version: manifest.version,
|
|
89
|
+
source: "cached",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
94
|
+
const t0 = Date.now();
|
|
95
|
+
await downloadAndExtract(archive, imageDir, fetchImpl);
|
|
96
|
+
const downloadMs = Date.now() - t0;
|
|
97
|
+
return {
|
|
98
|
+
kind: "downloaded",
|
|
99
|
+
imagePath: imageDir,
|
|
100
|
+
descriptor: {
|
|
101
|
+
name: manifest.name,
|
|
102
|
+
version: manifest.version,
|
|
103
|
+
source: "downloaded",
|
|
104
|
+
downloadMs,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A complete extraction has a `manifest.json` (gondolin's asset
|
|
110
|
+
* manifest, written by `gondolin build`). Checking for the directory
|
|
111
|
+
* alone is insufficient — a previous run could have crashed mid-extract.
|
|
112
|
+
*/
|
|
113
|
+
async function isExtractedImage(dir) {
|
|
114
|
+
try {
|
|
115
|
+
const s = await stat(path.join(dir, "manifest.json"));
|
|
116
|
+
return s.isFile();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function downloadAndExtract(archive, imageDir, fetchImpl) {
|
|
123
|
+
const tmpRoot = path.join(tmpdir(), `agentic-pi-image-${process.pid}-${Date.now()}`);
|
|
124
|
+
const tmpExtract = path.join(tmpRoot, "extract");
|
|
125
|
+
const tmpArchive = path.join(tmpRoot, "archive.tar.gz");
|
|
126
|
+
await mkdir(tmpExtract, { recursive: true });
|
|
127
|
+
let response;
|
|
128
|
+
try {
|
|
129
|
+
response = await fetchImpl(archive.url);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
133
|
+
throw new ImageLoaderError(`failed to download image from ${archive.url}: ${err.message}`, "set --sandbox-image gondolin-builtin to skip the auto-downloaded image");
|
|
134
|
+
}
|
|
135
|
+
if (!response.ok || !response.body) {
|
|
136
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
137
|
+
throw new ImageLoaderError(`image download failed: HTTP ${response.status} from ${archive.url}`, "set --sandbox-image gondolin-builtin to skip the auto-downloaded image");
|
|
138
|
+
}
|
|
139
|
+
// Stream to disk + hash in one pass — the tarball is ~hundreds of MB,
|
|
140
|
+
// so don't buffer in memory.
|
|
141
|
+
const hash = createHash("sha256");
|
|
142
|
+
const fileSink = createWriteStream(tmpArchive);
|
|
143
|
+
await pipeline(response.body, async function* (source) {
|
|
144
|
+
for await (const chunk of source) {
|
|
145
|
+
const buf = chunk;
|
|
146
|
+
hash.update(buf);
|
|
147
|
+
yield buf;
|
|
148
|
+
}
|
|
149
|
+
}, fileSink);
|
|
150
|
+
const actualSha = hash.digest("hex");
|
|
151
|
+
if (actualSha !== archive.sha256) {
|
|
152
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
153
|
+
throw new ImageLoaderError(`image sha256 mismatch: expected ${archive.sha256}, got ${actualSha}`, "the manifest baked into this build of agentic-pi is out of sync with the release artifact");
|
|
154
|
+
}
|
|
155
|
+
// Extract using system tar. Both macOS bsdtar and GNU tar accept
|
|
156
|
+
// `-x -z -f <file> -C <dir>`; that's a hard prerequisite for using
|
|
157
|
+
// gondolin in the first place (the build pipeline needs it too) so
|
|
158
|
+
// we don't preflight separately here.
|
|
159
|
+
await extractTarGz(tmpArchive, tmpExtract);
|
|
160
|
+
// Atomic publish: rename tmp dir to the final sha-keyed dir.
|
|
161
|
+
await mkdir(path.dirname(imageDir), { recursive: true });
|
|
162
|
+
try {
|
|
163
|
+
await rename(tmpExtract, imageDir);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
// Either a concurrent run won the race, or rename failed across FS.
|
|
167
|
+
// If the target now exists and is valid, treat as success.
|
|
168
|
+
if (await isExtractedImage(imageDir)) {
|
|
169
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
173
|
+
throw new ImageLoaderError(`failed to install image at ${imageDir}: ${err.message}`, "check disk space and ~/.cache/agentic-pi permissions");
|
|
174
|
+
}
|
|
175
|
+
await rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
176
|
+
}
|
|
177
|
+
function extractTarGz(archivePath, destDir) {
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
const proc = spawn("tar", ["-xzf", archivePath, "-C", destDir], {
|
|
180
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
181
|
+
});
|
|
182
|
+
let stderr = "";
|
|
183
|
+
proc.stderr.on("data", (c) => {
|
|
184
|
+
stderr += c.toString();
|
|
185
|
+
});
|
|
186
|
+
proc.on("error", reject);
|
|
187
|
+
proc.on("exit", (code) => {
|
|
188
|
+
if (code === 0)
|
|
189
|
+
resolve();
|
|
190
|
+
else
|
|
191
|
+
reject(new ImageLoaderError(`tar extraction failed (exit ${code}): ${stderr.trim() || "no stderr"}`, "ensure 'tar' is on PATH and the archive isn't corrupt"));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/sandbox/images/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baked-in manifest for the `default` agentic-pi-dev image.
|
|
3
|
+
*
|
|
4
|
+
* The image is built and released by `.github/workflows/image.yml`,
|
|
5
|
+
* which tags releases on `image-v*` (independent from the npm `v*`
|
|
6
|
+
* stream). After cutting an `image-v<x.y.z>` release, copy the
|
|
7
|
+
* per-arch URLs and sha256s from the release's `manifest.json` into
|
|
8
|
+
* the placeholders below and ship a new npm version.
|
|
9
|
+
*
|
|
10
|
+
* The sha256 is the authoritative signature — the loader verifies it
|
|
11
|
+
* before extracting. Reproducibility across rebuilds is best-effort
|
|
12
|
+
* (depends on Alpine mirror state), not guaranteed.
|
|
13
|
+
*/
|
|
14
|
+
export interface ImageArchive {
|
|
15
|
+
url: string;
|
|
16
|
+
sha256: string;
|
|
17
|
+
/** Size hint used for download progress / sanity check. Optional. */
|
|
18
|
+
uncompressedBytes: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ImageManifest {
|
|
21
|
+
name: string;
|
|
22
|
+
version: string;
|
|
23
|
+
archives: {
|
|
24
|
+
aarch64: ImageArchive;
|
|
25
|
+
x86_64: ImageArchive;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare const DEFAULT_IMAGE_MANIFEST: ImageManifest;
|
|
29
|
+
export declare function isManifestPublished(m?: ImageManifest): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baked-in manifest for the `default` agentic-pi-dev image.
|
|
3
|
+
*
|
|
4
|
+
* The image is built and released by `.github/workflows/image.yml`,
|
|
5
|
+
* which tags releases on `image-v*` (independent from the npm `v*`
|
|
6
|
+
* stream). After cutting an `image-v<x.y.z>` release, copy the
|
|
7
|
+
* per-arch URLs and sha256s from the release's `manifest.json` into
|
|
8
|
+
* the placeholders below and ship a new npm version.
|
|
9
|
+
*
|
|
10
|
+
* The sha256 is the authoritative signature — the loader verifies it
|
|
11
|
+
* before extracting. Reproducibility across rebuilds is best-effort
|
|
12
|
+
* (depends on Alpine mirror state), not guaranteed.
|
|
13
|
+
*/
|
|
14
|
+
// Pinned to image-v0.1.0 (first published release). Bump in lockstep
|
|
15
|
+
// with new image-v* releases — copy the published manifest.json
|
|
16
|
+
// verbatim. `uncompressedBytes` is informational only; the sha256 is
|
|
17
|
+
// the load-bearing check.
|
|
18
|
+
export const DEFAULT_IMAGE_MANIFEST = {
|
|
19
|
+
name: "agentic-pi-dev",
|
|
20
|
+
version: "0.1.0",
|
|
21
|
+
archives: {
|
|
22
|
+
aarch64: {
|
|
23
|
+
url: "https://github.com/cliftonc/agentic-pi/releases/download/image-v0.1.0/agentic-pi-dev-aarch64.tar.gz",
|
|
24
|
+
sha256: "2b5d303cbcdb8753d0b9eb1a15345b0b32140bc517ee07e3e946c6484093481c",
|
|
25
|
+
uncompressedBytes: 352753267,
|
|
26
|
+
},
|
|
27
|
+
x86_64: {
|
|
28
|
+
url: "https://github.com/cliftonc/agentic-pi/releases/download/image-v0.1.0/agentic-pi-dev-x86_64.tar.gz",
|
|
29
|
+
sha256: "08676e200b8ecd91e3727e67c295408756426447f9390a0f92c2d27d389e72f9",
|
|
30
|
+
uncompressedBytes: 378160995,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export function isManifestPublished(m = DEFAULT_IMAGE_MANIFEST) {
|
|
35
|
+
return m.archives.aarch64.sha256.length === 64 && m.archives.x86_64.sha256.length === 64;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/sandbox/images/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAkBH,qEAAqE;AACrE,gEAAgE;AAChE,qEAAqE;AACrE,0BAA0B;AAC1B,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE;QACR,OAAO,EAAE;YACP,GAAG,EAAE,qGAAqG;YAC1G,MAAM,EAAE,kEAAkE;YAC1E,iBAAiB,EAAE,SAAS;SAC7B;QACD,MAAM,EAAE;YACN,GAAG,EAAE,oGAAoG;YACzG,MAAM,EAAE,kEAAkE;YAC1E,iBAAiB,EAAE,SAAS;SAC7B;KACF;CACF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,IAAmB,sBAAsB;IAC3E,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC;AAC3F,CAAC"}
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -24,6 +24,38 @@ export type SandboxResult = SandboxNone | (Omit<GondolinSandbox, "status"> & {
|
|
|
24
24
|
export interface BuildSandboxOptions {
|
|
25
25
|
backend: SandboxBackend;
|
|
26
26
|
cwd: string;
|
|
27
|
+
/** Env vars to inject into the sandbox (ignored when backend === "none"). */
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Absolute path to a gondolin build output directory to mount as the
|
|
31
|
+
* guest. When `undefined`, gondolin uses its built-in `alpine-base`.
|
|
32
|
+
* Ignored when `backend === "none"`. The runner is responsible for
|
|
33
|
+
* resolving `--sandbox-image` (default / gondolin-builtin / path) to
|
|
34
|
+
* this path before calling buildSandbox.
|
|
35
|
+
*/
|
|
36
|
+
imagePath?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Optional descriptor of which image is being used, surfaced verbatim
|
|
39
|
+
* in `sandbox_status.image`. The sandbox layer does not interpret it —
|
|
40
|
+
* the runner builds it from the loader result.
|
|
41
|
+
*/
|
|
42
|
+
image?: ImageDescriptor;
|
|
43
|
+
/**
|
|
44
|
+
* Allowed HTTP egress hosts for the sandboxed guest. See
|
|
45
|
+
* `GondolinSandboxOptions.allowedHttpHosts` for the semantics:
|
|
46
|
+
* - `undefined` (default): GitHub-only allowlist
|
|
47
|
+
* - explicit `string[]`: caller-supplied allowlist
|
|
48
|
+
* - `null`: skip HTTP hooks entirely (gondolin blocks everything)
|
|
49
|
+
*
|
|
50
|
+
* Ignored when `backend === "none"`.
|
|
51
|
+
*/
|
|
52
|
+
allowedHttpHosts?: string[] | null;
|
|
53
|
+
}
|
|
54
|
+
export interface ImageDescriptor {
|
|
55
|
+
name: string;
|
|
56
|
+
version?: string;
|
|
57
|
+
source: "builtin" | "local-path" | "cached" | "downloaded";
|
|
58
|
+
downloadMs?: number;
|
|
27
59
|
}
|
|
28
60
|
export type BuildSandboxOutcome = {
|
|
29
61
|
ok: true;
|
package/dist/sandbox/index.js
CHANGED
|
@@ -31,7 +31,12 @@ export async function buildSandbox(opts) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
34
|
-
const sandbox = await buildGondolinSandbox(opts.cwd
|
|
34
|
+
const sandbox = await buildGondolinSandbox(opts.cwd, {
|
|
35
|
+
env: opts.env,
|
|
36
|
+
imagePath: opts.imagePath,
|
|
37
|
+
image: opts.image,
|
|
38
|
+
allowedHttpHosts: opts.allowedHttpHosts,
|
|
39
|
+
});
|
|
35
40
|
return {
|
|
36
41
|
ok: true,
|
|
37
42
|
sandbox: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAwB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAwB,MAAM,gBAAgB,CAAC;AAmEzE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAyB;IAC1D,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE;gBACP,OAAO,EAAE,MAAM;gBACf,WAAW,EAAE,EAAE;gBACf,gBAAgB,EAAE,KAAK;gBACvB,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;gBAC5B,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;aAC5B;SACF,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,MAAM,SAAS,GAAoB,iBAAiB,EAAE,CAAC;IACvD,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,UAAU;YACnB,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,IAAI,EAAE,SAAS,CAAC,IAAI;SACrB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,GAAG,EAAE;YACnD,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;SACxC,CAAC,CAAC;QACH,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE;gBACP,OAAO,EAAE,UAAU;gBACnB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,gBAAgB,EAAE,IAAI;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,UAAU;YACnB,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAG,GAAa,CAAC,OAAO;SAC7B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED