@crewhaus/sandbox-image-go 0.1.4 → 0.1.6

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.
@@ -0,0 +1,33 @@
1
+ import { type SandboxImageEntry } from "@crewhaus/sandbox-image-registry";
2
+ /**
3
+ * Catalog R8 `sandbox-image-go` — Section 36 polyglot Go sandbox image.
4
+ *
5
+ * Registers a curated Go 1.23 image into `@crewhaus/sandbox-image-registry`
6
+ * so polyglot agents can `lookupSandboxImage("go")` and route a snippet
7
+ * through `tool-code-execution`'s sandbox path.
8
+ *
9
+ * Snippet mode (default entrypoint): `go run -` reads stdin as a single
10
+ * `package main` source file. Advanced callers who mount a project can
11
+ * exec `./<binary>` after a build step — the entrypoint is overridable
12
+ * at `sandbox.exec({ argv })` time.
13
+ *
14
+ * Cold-start budget: ≤2s for the compiled-language warm pool; the
15
+ * `healthcheck.timeoutMs = 2_000` enforces the registry's view of
16
+ * "healthy". Live-image probe runs `go version` against the actual
17
+ * docker image when `CREWHAUS_SECTION36_LIVE_DOCKER=1` is set.
18
+ *
19
+ * Layer R8. Pairs with `sandbox-image-registry` (R8 — registration
20
+ * surface) and `sandbox` (R8 — exec backend).
21
+ */
22
+ export declare const GO_IMAGE_ID = "go";
23
+ export declare const GO_IMAGE_REF = "golang:1.23-alpine";
24
+ export declare const GO_DEFAULT_ENTRYPOINT: ReadonlyArray<string>;
25
+ export declare const GO_HEALTHCHECK_ARGV: ReadonlyArray<string>;
26
+ /** Cold-start budget for the warm pool (ms). T7 layer asserts this. */
27
+ export declare const GO_COLD_START_BUDGET_MS = 2000;
28
+ /**
29
+ * Register the Go 1.23 image. Idempotent — calling twice throws via
30
+ * the registry's duplicate-id refusal. Callers in app boot paths
31
+ * should call `registerGoSandboxImage()` once.
32
+ */
33
+ export declare function registerGoSandboxImage(): SandboxImageEntry;
@@ -1,5 +1,4 @@
1
- import { type SandboxImageEntry, registerSandboxImage } from "@crewhaus/sandbox-image-registry";
2
-
1
+ import { registerSandboxImage } from "@crewhaus/sandbox-image-registry";
3
2
  /**
4
3
  * Catalog R8 `sandbox-image-go` — Section 36 polyglot Go sandbox image.
5
4
  *
@@ -20,31 +19,27 @@ import { type SandboxImageEntry, registerSandboxImage } from "@crewhaus/sandbox-
20
19
  * Layer R8. Pairs with `sandbox-image-registry` (R8 — registration
21
20
  * surface) and `sandbox` (R8 — exec backend).
22
21
  */
23
-
24
22
  export const GO_IMAGE_ID = "go";
25
23
  export const GO_IMAGE_REF = "golang:1.23-alpine";
26
- export const GO_DEFAULT_ENTRYPOINT: ReadonlyArray<string> = ["go", "run", "-"];
27
- export const GO_HEALTHCHECK_ARGV: ReadonlyArray<string> = ["go", "version"];
28
-
24
+ export const GO_DEFAULT_ENTRYPOINT = ["go", "run", "-"];
25
+ export const GO_HEALTHCHECK_ARGV = ["go", "version"];
29
26
  /** Cold-start budget for the warm pool (ms). T7 layer asserts this. */
30
27
  export const GO_COLD_START_BUDGET_MS = 2_000;
31
-
32
28
  /**
33
29
  * Register the Go 1.23 image. Idempotent — calling twice throws via
34
30
  * the registry's duplicate-id refusal. Callers in app boot paths
35
31
  * should call `registerGoSandboxImage()` once.
36
32
  */
37
- export function registerGoSandboxImage(): SandboxImageEntry {
38
- return registerSandboxImage({
39
- id: GO_IMAGE_ID,
40
- image: GO_IMAGE_REF,
41
- defaultEntrypoint: GO_DEFAULT_ENTRYPOINT,
42
- healthcheck: {
43
- command: GO_HEALTHCHECK_ARGV,
44
- expectedExitCode: 0,
45
- timeoutMs: GO_COLD_START_BUDGET_MS,
46
- },
47
- description:
48
- "Go 1.23 alpine — snippet mode via `go run -`; compiled-binary mode for mounted projects.",
49
- });
33
+ export function registerGoSandboxImage() {
34
+ return registerSandboxImage({
35
+ id: GO_IMAGE_ID,
36
+ image: GO_IMAGE_REF,
37
+ defaultEntrypoint: GO_DEFAULT_ENTRYPOINT,
38
+ healthcheck: {
39
+ command: GO_HEALTHCHECK_ARGV,
40
+ expectedExitCode: 0,
41
+ timeoutMs: GO_COLD_START_BUDGET_MS,
42
+ },
43
+ description: "Go 1.23 alpine — snippet mode via `go run -`; compiled-binary mode for mounted projects.",
44
+ });
50
45
  }
package/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "@crewhaus/sandbox-image-go",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "Go 1.23 polyglot sandbox image: registry registration + multi-stage Dockerfile + T2/T7/T8 contract tests (Section 36)",
6
- "main": "src/index.ts",
7
- "types": "src/index.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
8
  "exports": {
9
- ".": "./src/index.ts"
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
10
13
  },
11
14
  "scripts": {
12
15
  "test": "bun test src"
13
16
  },
14
17
  "dependencies": {
15
- "@crewhaus/errors": "0.1.4",
16
- "@crewhaus/sandbox": "0.1.4",
17
- "@crewhaus/sandbox-image-registry": "0.1.4"
18
+ "@crewhaus/errors": "0.1.6",
19
+ "@crewhaus/sandbox": "0.1.6",
20
+ "@crewhaus/sandbox-image-registry": "0.1.6"
18
21
  },
19
22
  "license": "Apache-2.0",
20
23
  "author": {
@@ -34,5 +37,5 @@
34
37
  "publishConfig": {
35
38
  "access": "public"
36
39
  },
37
- "files": ["src", "README.md", "LICENSE", "NOTICE"]
40
+ "files": ["dist", "README.md", "LICENSE", "NOTICE"]
38
41
  }
package/src/index.test.ts DELETED
@@ -1,185 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { createSandbox } from "@crewhaus/sandbox";
3
- import {
4
- ImageRegistrationError,
5
- _resetSandboxImageRegistry,
6
- hasSandboxImage,
7
- listAllowedImageRefs,
8
- lookupSandboxImage,
9
- registerSandboxImage,
10
- } from "@crewhaus/sandbox-image-registry";
11
- import {
12
- GO_COLD_START_BUDGET_MS,
13
- GO_DEFAULT_ENTRYPOINT,
14
- GO_HEALTHCHECK_ARGV,
15
- GO_IMAGE_ID,
16
- GO_IMAGE_REF,
17
- registerGoSandboxImage,
18
- } from "./index";
19
-
20
- describe("registerGoSandboxImage (T1 + T2)", () => {
21
- beforeEach(() => {
22
- _resetSandboxImageRegistry();
23
- });
24
- afterEach(() => {
25
- _resetSandboxImageRegistry();
26
- });
27
-
28
- test("constants match the kickoff prompt's spec", () => {
29
- expect(GO_IMAGE_ID).toBe("go");
30
- expect(GO_IMAGE_REF).toBe("golang:1.23-alpine");
31
- expect(GO_DEFAULT_ENTRYPOINT).toEqual(["go", "run", "-"]);
32
- expect(GO_HEALTHCHECK_ARGV).toEqual(["go", "version"]);
33
- });
34
-
35
- test("registerGoSandboxImage() registers an image with the right shape", () => {
36
- const entry = registerGoSandboxImage();
37
- expect(entry.id).toBe("go");
38
- expect(entry.image).toBe("golang:1.23-alpine");
39
- expect(entry.defaultEntrypoint).toEqual(["go", "run", "-"]);
40
- expect(entry.healthcheck.command).toEqual(["go", "version"]);
41
- expect(entry.healthcheck.expectedExitCode).toBe(0);
42
- expect(entry.healthcheck.timeoutMs).toBe(GO_COLD_START_BUDGET_MS);
43
- expect(entry.description).toMatch(/Go 1\.23/);
44
- });
45
-
46
- test("lookupSandboxImage('go') returns the registered entry", () => {
47
- registerGoSandboxImage();
48
- expect(hasSandboxImage("go")).toBe(true);
49
- const entry = lookupSandboxImage("go");
50
- expect(entry.image).toBe("golang:1.23-alpine");
51
- });
52
-
53
- test("listAllowedImageRefs includes golang:1.23-alpine after registration", () => {
54
- registerGoSandboxImage();
55
- const refs = listAllowedImageRefs();
56
- expect(refs).toContain("golang:1.23-alpine");
57
- // Bootstrap trio still present
58
- expect(refs).toContain("python:3.13-slim");
59
- expect(refs).toContain("node:22-alpine");
60
- });
61
- });
62
-
63
- describe("Go-shape T2 contract — round-trip via noop sandbox", () => {
64
- beforeEach(() => {
65
- _resetSandboxImageRegistry();
66
- });
67
- afterEach(() => {
68
- _resetSandboxImageRegistry();
69
- });
70
-
71
- test("noop sandbox accepts golang:1.23-alpine when registered", async () => {
72
- registerGoSandboxImage();
73
- const sandbox = createSandbox({
74
- backend: "noop",
75
- allowedImages: listAllowedImageRefs(),
76
- });
77
- // The noop backend spawns the argv directly — `printf` is universal,
78
- // so we use it instead of `go run -` to assert the wiring without
79
- // needing a Go toolchain on the test host.
80
- const result = await sandbox.exec({
81
- image: GO_IMAGE_REF,
82
- argv: ["printf", "hello-from-go"],
83
- });
84
- expect(result.exitCode).toBe(0);
85
- expect(result.stdout).toBe("hello-from-go");
86
- await sandbox.close();
87
- });
88
-
89
- test("noop sandbox refuses golang:1.23-alpine when NOT registered", async () => {
90
- // Registry not touched here — Go image is not in the allowlist.
91
- // The bootstrap trio is still present, so we explicitly use a
92
- // sandbox configured with only the trio.
93
- const trioRefs = ["python:3.13-slim", "node:22-alpine", "alpine:3.19"];
94
- const sandbox = createSandbox({ backend: "noop", allowedImages: trioRefs });
95
- await expect(sandbox.exec({ image: GO_IMAGE_REF, argv: ["printf", "x"] })).rejects.toThrow(
96
- /not on the allowlist/,
97
- );
98
- await sandbox.close();
99
- });
100
- });
101
-
102
- describe("Go-shape T7 — cold-start budget", () => {
103
- beforeEach(() => {
104
- _resetSandboxImageRegistry();
105
- });
106
- afterEach(() => {
107
- _resetSandboxImageRegistry();
108
- });
109
-
110
- test("healthcheck timeoutMs is within the compiled-language budget (≤2s)", () => {
111
- const entry = registerGoSandboxImage();
112
- expect(entry.healthcheck.timeoutMs).toBeLessThanOrEqual(2_000);
113
- expect(entry.healthcheck.timeoutMs).toBeGreaterThan(0);
114
- });
115
- });
116
-
117
- describe("Go-shape T8 — escape-attempt suite (reuses §18 corpus shape)", () => {
118
- beforeEach(() => {
119
- _resetSandboxImageRegistry();
120
- });
121
- afterEach(() => {
122
- _resetSandboxImageRegistry();
123
- });
124
-
125
- test("Go image registration is idempotent-then-rejected (no silent override)", () => {
126
- registerGoSandboxImage();
127
- expect(() => registerGoSandboxImage()).toThrow(/already registered/);
128
- });
129
-
130
- test("Go entry's image string passes registry validation (no leading dash)", () => {
131
- expect(GO_IMAGE_REF.startsWith("-")).toBe(false);
132
- });
133
-
134
- test("Go entry's image string contains no whitespace / newline", () => {
135
- expect(/\s/.test(GO_IMAGE_REF)).toBe(false);
136
- expect(GO_IMAGE_REF.includes("\n")).toBe(false);
137
- });
138
-
139
- test("Go entry's defaultEntrypoint contains no shell-meta sequences", () => {
140
- for (const arg of GO_DEFAULT_ENTRYPOINT) {
141
- expect(/[;&|<>$`(){}]/.test(arg)).toBe(false);
142
- expect(arg.includes("\n")).toBe(false);
143
- }
144
- });
145
-
146
- test("Go entry's healthcheck argv contains no shell-meta sequences", () => {
147
- for (const arg of GO_HEALTHCHECK_ARGV) {
148
- expect(/[;&|<>$`(){}]/.test(arg)).toBe(false);
149
- expect(arg.includes("\n")).toBe(false);
150
- }
151
- });
152
-
153
- test("attempted CLI-flag-injection registration via Go id is refused", () => {
154
- expect(() =>
155
- registerSandboxImage({
156
- id: "go",
157
- image: "--privileged",
158
- defaultEntrypoint: ["go", "run", "-"],
159
- healthcheck: { command: ["go", "version"], expectedExitCode: 0 },
160
- }),
161
- ).toThrow(ImageRegistrationError);
162
- });
163
-
164
- test("attempted whitespace-tampered Go image is refused", () => {
165
- expect(() =>
166
- registerSandboxImage({
167
- id: "go",
168
- image: "golang:1.23-alpine --privileged",
169
- defaultEntrypoint: ["go", "run", "-"],
170
- healthcheck: { command: ["go", "version"], expectedExitCode: 0 },
171
- }),
172
- ).toThrow(/whitespace/);
173
- });
174
-
175
- test("attempted shell-meta-tagged Go image is refused", () => {
176
- expect(() =>
177
- registerSandboxImage({
178
- id: "go",
179
- image: "golang:$(id)",
180
- defaultEntrypoint: ["go", "run", "-"],
181
- healthcheck: { command: ["go", "version"], expectedExitCode: 0 },
182
- }),
183
- ).toThrow(/valid registry/);
184
- });
185
- });