@omg-dev/sandbox 0.4.34 → 0.4.37
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/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
export { SandboxApiError, SandboxClient, applyTemplate, apt, assertTemplateId, bakeTemplate, check, compileStartScript, compileStep, defineTemplate, download, files, run, templateContentVersionRef, templateDefinitionHash, templateRuntimeContract, templateVersionRef, waitForSnapshotUpload };
|
|
1
|
+
import { _ as SandboxApiError, a as check, c as defineTemplate, d as run, f as templateContentVersionRef, g as waitForSnapshotUpload, h as templateVersionRef, i as bakeTemplate, l as download, m as templateRuntimeContract, n as applyTemplate, o as compileStartScript, p as templateDefinitionHash, r as apt, s as compileStep, t as APT_FORCE_IPV4_SNIPPET, u as files, v as SandboxClient, y as assertTemplateId } from "./templates-D-Ap1Yxk.mjs";
|
|
2
|
+
export { APT_FORCE_IPV4_SNIPPET, SandboxApiError, SandboxClient, applyTemplate, apt, assertTemplateId, bakeTemplate, check, compileStartScript, compileStep, defineTemplate, download, files, run, templateContentVersionRef, templateDefinitionHash, templateRuntimeContract, templateVersionRef, waitForSnapshotUpload };
|
|
@@ -203,9 +203,10 @@ function shellEnvAssignments(env) {
|
|
|
203
203
|
return `${key}=${quote(value)}`;
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
+
const APT_FORCE_IPV4_SNIPPET = `mkdir -p /etc/apt/apt.conf.d\nprintf 'Acquire::ForceIPv4 "true";\\n' > /etc/apt/apt.conf.d/99-omg-force-ipv4`;
|
|
206
207
|
function compileStep(step) {
|
|
207
208
|
if (step.kind === "files") throw new Error("files steps transfer local content and are applied via applyTemplate(), not compileStep()");
|
|
208
|
-
if (step.kind === "apt") return { script: `set -euo pipefail\napt-get update -qq\nDEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends ${step.packages.map(quote).join(" ")}\nrm -rf /var/lib/apt/lists/*` };
|
|
209
|
+
if (step.kind === "apt") return { script: `set -euo pipefail\n${APT_FORCE_IPV4_SNIPPET}\napt-get update -qq\nDEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends ${step.packages.map(quote).join(" ")}\nrm -rf /var/lib/apt/lists/*` };
|
|
209
210
|
if (step.kind === "archive") {
|
|
210
211
|
const strip = step.stripComponents ?? 1;
|
|
211
212
|
if (!Number.isInteger(strip) || strip < 0 || strip > 20) throw new Error("invalid stripComponents");
|
|
@@ -420,4 +421,4 @@ async function resolveTemplateVersion(client, versionRef) {
|
|
|
420
421
|
}
|
|
421
422
|
}
|
|
422
423
|
//#endregion
|
|
423
|
-
export {
|
|
424
|
+
export { SandboxApiError as _, check as a, defineTemplate as c, run as d, templateContentVersionRef as f, waitForSnapshotUpload as g, templateVersionRef as h, bakeTemplate as i, download as l, templateRuntimeContract as m, applyTemplate as n, compileStartScript as o, templateDefinitionHash as p, apt as r, compileStep as s, APT_FORCE_IPV4_SNIPPET as t, files as u, SandboxClient as v, assertTemplateId as y };
|
package/dist/templates.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { applyTemplate, apt, bakeTemplate, check, compileStartScript, compileStep, defineTemplate, download, files, run, templateContentVersionRef, templateDefinitionHash, templateRuntimeContract, templateVersionRef, waitForSnapshotUpload };
|
|
1
|
+
import { a as check, c as defineTemplate, d as run, f as templateContentVersionRef, g as waitForSnapshotUpload, h as templateVersionRef, i as bakeTemplate, l as download, m as templateRuntimeContract, n as applyTemplate, o as compileStartScript, p as templateDefinitionHash, r as apt, s as compileStep, t as APT_FORCE_IPV4_SNIPPET, u as files } from "./templates-D-Ap1Yxk.mjs";
|
|
2
|
+
export { APT_FORCE_IPV4_SNIPPET, applyTemplate, apt, bakeTemplate, check, compileStartScript, compileStep, defineTemplate, download, files, run, templateContentVersionRef, templateDefinitionHash, templateRuntimeContract, templateVersionRef, waitForSnapshotUpload };
|
package/package.json
CHANGED
package/src/templates.test.ts
CHANGED
|
@@ -33,6 +33,16 @@ describe("sandbox templates", () => {
|
|
|
33
33
|
expect(Object.isFrozen(template)).toBe(true);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
test("apt forces IPv4 before the first fetch — the guest has no IPv6 route", () => {
|
|
37
|
+
const { script } = compileStep(apt.packages(["tmux"]));
|
|
38
|
+
const dropIn = script.indexOf("Acquire::ForceIPv4");
|
|
39
|
+
expect(dropIn).toBeGreaterThan(-1);
|
|
40
|
+
expect(script).toContain("/etc/apt/apt.conf.d/99-omg-force-ipv4");
|
|
41
|
+
// Ordering is the whole fix: an AAAA-only attempt before the drop-in lands
|
|
42
|
+
// still burns the step deadline on an unroutable archive.ubuntu.com.
|
|
43
|
+
expect(dropIn).toBeLessThan(script.indexOf("apt-get update"));
|
|
44
|
+
});
|
|
45
|
+
|
|
36
46
|
test("archive extraction ignores inherited tar policy", () => {
|
|
37
47
|
const { script } = compileStep(download.archive({ url: "https://example.com/a.tgz", destination: "/home/user/app" }));
|
|
38
48
|
expect(script).toContain("TAR_OPTIONS= tar");
|
|
@@ -148,7 +158,7 @@ describe("sandbox templates", () => {
|
|
|
148
158
|
|
|
149
159
|
test("LFG owns every runtime dependency and zero-config transport default", () => {
|
|
150
160
|
const lfg = agentTemplates.lfg;
|
|
151
|
-
expect(templateVersionRef(lfg)).toBe("agent-lfg-
|
|
161
|
+
expect(templateVersionRef(lfg)).toBe("agent-lfg-v81");
|
|
152
162
|
expect(lfg.install).toContainEqual(apt.packages(["tmux"]));
|
|
153
163
|
expect(lfg.checks).toContainEqual(check.command("tmux"));
|
|
154
164
|
expect(lfg.checks).toContainEqual(check.file("/home/user/lfg/omg-start.sh"));
|
|
@@ -156,7 +166,7 @@ describe("sandbox templates", () => {
|
|
|
156
166
|
expect(lfg.start?.env?.LIVE_TRANSPORT).toBe("ws");
|
|
157
167
|
expect(lfg.start?.readiness?.port).toBe(8766);
|
|
158
168
|
expect(templateRuntimeContract(lfg, templateVersionRef(lfg))).toEqual({
|
|
159
|
-
immutableRef: "agent-lfg-
|
|
169
|
+
immutableRef: "agent-lfg-v81",
|
|
160
170
|
restoreKind: "full_rootfs",
|
|
161
171
|
startCommand: "exec /home/user/.omg/template/bootstrap.sh",
|
|
162
172
|
readinessPort: 8766,
|
|
@@ -196,7 +206,26 @@ describe("sandbox templates", () => {
|
|
|
196
206
|
.filter((step): step is Extract<typeof step, { kind: "run" }> => step.kind === "run")
|
|
197
207
|
.map((step) => step.command);
|
|
198
208
|
expect(commands.some((c) => c.includes("recipe.sh"))).toBe(true);
|
|
199
|
-
|
|
209
|
+
// The LFG release is pinned in three places that a bump has to move
|
|
210
|
+
// together: the bundle download URL, the install.json the guest writes
|
|
211
|
+
// (what self-update compares against), and the metadata the dashboard
|
|
212
|
+
// reads. A hardcoded version here just goes stale on every bump — and did,
|
|
213
|
+
// sitting at v0.1.209 while the catalog had moved on to v0.1.296. Assert
|
|
214
|
+
// the invariant that actually matters instead: all three agree.
|
|
215
|
+
const pinnedRelease = lfg.metadata?.lfgRelease as string | undefined;
|
|
216
|
+
expect(pinnedRelease).toMatch(/^v\d+\.\d+\.\d+$/);
|
|
217
|
+
expect(
|
|
218
|
+
lfg.install.some(
|
|
219
|
+
(step) =>
|
|
220
|
+
step.kind === "archive" &&
|
|
221
|
+
"url" in step &&
|
|
222
|
+
typeof step.url === "string" &&
|
|
223
|
+
step.url.includes(`/releases/download/${pinnedRelease}/lfg-bundle.tar.gz`),
|
|
224
|
+
),
|
|
225
|
+
).toBe(true);
|
|
226
|
+
expect(
|
|
227
|
+
commands.some((c) => c.includes(`"release":"${pinnedRelease}"`)),
|
|
228
|
+
).toBe(true);
|
|
200
229
|
const lfgInstall = commands.find((c) => c.includes("bun install --production"));
|
|
201
230
|
expect(lfgInstall).toBeDefined();
|
|
202
231
|
expect(lfgInstall!).not.toContain("delete manifest.workspaces");
|
package/src/templates.ts
CHANGED
|
@@ -179,12 +179,26 @@ function shellEnvAssignments(env: Record<string, string>): string[] {
|
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
// Guest VMs are IPv4-only: apps/infra/rootfs/init writes a v4-only
|
|
183
|
+
// /etc/resolv.conf and the netns contract hands the guest 169.254.0.2 with no
|
|
184
|
+
// IPv6 route. Ubuntu's mirrors publish AAAA records anyway, and apt walks the
|
|
185
|
+
// resolved addresses serially with no happy-eyeballs fallback — so a bake
|
|
186
|
+
// either dies on `Cannot initiate the connection to archive.ubuntu.com:80
|
|
187
|
+
// (2620:2d:4000:1::101). - connect (101: Network is unreachable)` or burns the
|
|
188
|
+
// whole 10-minute step deadline on an unroutable mirror. Both were silent:
|
|
189
|
+
// agent-lfg just stayed stale at the last good bake.
|
|
190
|
+
//
|
|
191
|
+
// Written as a drop-in rather than an inline `-o` so it lands before the first
|
|
192
|
+
// fetch AND persists into the baked snapshot — later apt calls in the guest
|
|
193
|
+
// (the agent's runtime tmux fallback, user apps) inherit it for free.
|
|
194
|
+
export const APT_FORCE_IPV4_SNIPPET = `mkdir -p /etc/apt/apt.conf.d\nprintf 'Acquire::ForceIPv4 "true";\\n' > /etc/apt/apt.conf.d/99-omg-force-ipv4`;
|
|
195
|
+
|
|
182
196
|
export function compileStep(step: TemplateStep): { script: string; timeoutMs?: number } {
|
|
183
197
|
if (step.kind === "files") {
|
|
184
198
|
throw new Error("files steps transfer local content and are applied via applyTemplate(), not compileStep()");
|
|
185
199
|
}
|
|
186
200
|
if (step.kind === "apt") {
|
|
187
|
-
return { script: `set -euo pipefail\napt-get update -qq\nDEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends ${step.packages.map(quote).join(" ")}\nrm -rf /var/lib/apt/lists/*` };
|
|
201
|
+
return { script: `set -euo pipefail\n${APT_FORCE_IPV4_SNIPPET}\napt-get update -qq\nDEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends ${step.packages.map(quote).join(" ")}\nrm -rf /var/lib/apt/lists/*` };
|
|
188
202
|
}
|
|
189
203
|
if (step.kind === "archive") {
|
|
190
204
|
const strip = step.stripComponents ?? 1;
|