@blaxel/core 0.2.67-preview.88 → 0.2.67-preview.89
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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/image/image.browser.js +84 -0
- package/dist/cjs/image/image.js +567 -0
- package/dist/cjs/image/image.test.js +545 -0
- package/dist/cjs/image/index.js +7 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/types/image/image.browser.d.ts +59 -0
- package/dist/cjs/types/image/image.d.ts +202 -0
- package/dist/cjs/types/image/image.test.d.ts +4 -0
- package/dist/cjs/types/image/index.d.ts +1 -0
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/image/image.js +84 -0
- package/dist/cjs-browser/image/image.test.js +545 -0
- package/dist/cjs-browser/image/index.js +7 -0
- package/dist/cjs-browser/index.js +1 -0
- package/dist/cjs-browser/types/image/image.browser.d.ts +59 -0
- package/dist/cjs-browser/types/image/image.d.ts +202 -0
- package/dist/cjs-browser/types/image/image.test.d.ts +4 -0
- package/dist/cjs-browser/types/image/index.d.ts +1 -0
- package/dist/cjs-browser/types/index.d.ts +1 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm/image/image.browser.js +80 -0
- package/dist/esm/image/image.js +560 -0
- package/dist/esm/image/image.test.js +543 -0
- package/dist/esm/image/index.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/settings.js +2 -2
- package/dist/esm-browser/image/image.js +80 -0
- package/dist/esm-browser/image/image.test.js +543 -0
- package/dist/esm-browser/image/index.js +1 -0
- package/dist/esm-browser/index.js +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @vitest-environment node
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const os_1 = require("os");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const vitest_1 = require("vitest");
|
|
10
|
+
const image_js_1 = require("./image.js");
|
|
11
|
+
// Test fixtures
|
|
12
|
+
let tempDir;
|
|
13
|
+
let tempFile;
|
|
14
|
+
let tempSourceDir;
|
|
15
|
+
(0, vitest_1.beforeEach)(() => {
|
|
16
|
+
// Create temp directory
|
|
17
|
+
tempDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "blaxel-test-"));
|
|
18
|
+
// Create temp file
|
|
19
|
+
tempFile = (0, path_1.join)(tempDir, "test.txt");
|
|
20
|
+
(0, fs_1.writeFileSync)(tempFile, "test content");
|
|
21
|
+
// Create temp source directory with test files
|
|
22
|
+
tempSourceDir = (0, path_1.join)(tempDir, "source");
|
|
23
|
+
(0, fs_1.mkdirSync)(tempSourceDir);
|
|
24
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(tempSourceDir, "file1.txt"), "content1");
|
|
25
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(tempSourceDir, "file2.txt"), "content2");
|
|
26
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(tempSourceDir, "subdir"));
|
|
27
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(tempSourceDir, "subdir", "nested.txt"), "nested content");
|
|
28
|
+
});
|
|
29
|
+
(0, vitest_1.afterEach)(() => {
|
|
30
|
+
// Cleanup temp directory
|
|
31
|
+
try {
|
|
32
|
+
(0, fs_1.rmSync)(tempDir, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Ignore cleanup errors
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.describe)("ImageInstanceInstance.fromRegistry", () => {
|
|
39
|
+
(0, vitest_1.it)("creates image with correct base image", () => {
|
|
40
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim");
|
|
41
|
+
(0, vitest_1.expect)(image.baseImage).toBe("python:3.11-slim");
|
|
42
|
+
});
|
|
43
|
+
(0, vitest_1.it)("works with full DockerHub image tag", () => {
|
|
44
|
+
const image = image_js_1.ImageInstance.fromRegistry("namanjain12/numpy_final:05aa44d53f4f9528847a0c014fe4bda5caa5fd3d");
|
|
45
|
+
(0, vitest_1.expect)(image.baseImage).toBe("namanjain12/numpy_final:05aa44d53f4f9528847a0c014fe4bda5caa5fd3d");
|
|
46
|
+
});
|
|
47
|
+
(0, vitest_1.it)("generates correct FROM instruction in dockerfile", () => {
|
|
48
|
+
const image = image_js_1.ImageInstance.fromRegistry("ubuntu:22.04");
|
|
49
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("FROM ubuntu:22.04");
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.it)("works with private registry URL", () => {
|
|
52
|
+
const image = image_js_1.ImageInstance.fromRegistry("gcr.io/my-project/my-image:v1.0.0");
|
|
53
|
+
(0, vitest_1.expect)(image.baseImage).toBe("gcr.io/my-project/my-image:v1.0.0");
|
|
54
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("FROM gcr.io/my-project/my-image:v1.0.0");
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.it)("works with image digest", () => {
|
|
57
|
+
const image = image_js_1.ImageInstance.fromRegistry("python@sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
|
|
58
|
+
(0, vitest_1.expect)(image.baseImage).toContain("sha256:abcdef");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
(0, vitest_1.describe)("ImageInstanceInstance.workdir", () => {
|
|
62
|
+
(0, vitest_1.it)("adds WORKDIR instruction", () => {
|
|
63
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
64
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("WORKDIR /app");
|
|
65
|
+
});
|
|
66
|
+
(0, vitest_1.it)("returns new image instance (immutability)", () => {
|
|
67
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
68
|
+
const image2 = image1.workdir("/app");
|
|
69
|
+
(0, vitest_1.expect)(image1).not.toBe(image2);
|
|
70
|
+
(0, vitest_1.expect)(image1.dockerfile).not.toContain("WORKDIR /app");
|
|
71
|
+
(0, vitest_1.expect)(image2.dockerfile).toContain("WORKDIR /app");
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)("supports multiple workdir changes in sequence", () => {
|
|
74
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
75
|
+
.workdir("/first")
|
|
76
|
+
.runCommands("echo first")
|
|
77
|
+
.workdir("/second")
|
|
78
|
+
.runCommands("echo second");
|
|
79
|
+
const dockerfile = image.dockerfile;
|
|
80
|
+
(0, vitest_1.expect)(dockerfile).toContain("WORKDIR /first");
|
|
81
|
+
(0, vitest_1.expect)(dockerfile).toContain("WORKDIR /second");
|
|
82
|
+
// Check order
|
|
83
|
+
const firstIdx = dockerfile.indexOf("WORKDIR /first");
|
|
84
|
+
const secondIdx = dockerfile.indexOf("WORKDIR /second");
|
|
85
|
+
(0, vitest_1.expect)(firstIdx).toBeLessThan(secondIdx);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.describe)("ImageInstanceInstance.runCommands", () => {
|
|
89
|
+
(0, vitest_1.it)("adds single RUN instruction", () => {
|
|
90
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").runCommands("echo hello");
|
|
91
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("RUN echo hello");
|
|
92
|
+
});
|
|
93
|
+
(0, vitest_1.it)("adds multiple RUN instructions", () => {
|
|
94
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").runCommands("echo hello", "echo world");
|
|
95
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("RUN echo hello");
|
|
96
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("RUN echo world");
|
|
97
|
+
});
|
|
98
|
+
(0, vitest_1.it)("supports chaining multiple runCommands calls", () => {
|
|
99
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
100
|
+
.runCommands("echo first")
|
|
101
|
+
.runCommands("echo second");
|
|
102
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("RUN echo first");
|
|
103
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("RUN echo second");
|
|
104
|
+
});
|
|
105
|
+
(0, vitest_1.it)("works with complex shell commands", () => {
|
|
106
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").runCommands("cd /app && git clone https://github.com/example/repo.git", "find . -name '*.pyc' -delete", "chmod +x ./run_tests.sh && ./run_tests.sh");
|
|
107
|
+
const dockerfile = image.dockerfile;
|
|
108
|
+
(0, vitest_1.expect)(dockerfile).toContain("git clone");
|
|
109
|
+
(0, vitest_1.expect)(dockerfile).toContain("find . -name");
|
|
110
|
+
(0, vitest_1.expect)(dockerfile).toContain("chmod +x");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
(0, vitest_1.describe)("ImageInstanceInstance.env", () => {
|
|
114
|
+
(0, vitest_1.it)("sets single variable", () => {
|
|
115
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").env({ PYTHONUNBUFFERED: "1" });
|
|
116
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENV PYTHONUNBUFFERED="1"');
|
|
117
|
+
});
|
|
118
|
+
(0, vitest_1.it)("sets multiple variables", () => {
|
|
119
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").env({
|
|
120
|
+
PYTHONUNBUFFERED: "1",
|
|
121
|
+
DEBUG: "true",
|
|
122
|
+
});
|
|
123
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENV PYTHONUNBUFFERED="1"');
|
|
124
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENV DEBUG="true"');
|
|
125
|
+
});
|
|
126
|
+
(0, vitest_1.it)("returns same image with empty object", () => {
|
|
127
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
128
|
+
const image2 = image1.env({});
|
|
129
|
+
(0, vitest_1.expect)(image1).toBe(image2);
|
|
130
|
+
});
|
|
131
|
+
(0, vitest_1.it)("handles special characters in values", () => {
|
|
132
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").env({
|
|
133
|
+
PATH: "/usr/local/bin:$PATH",
|
|
134
|
+
CONNECTION_STRING: "host=localhost;port=5432",
|
|
135
|
+
});
|
|
136
|
+
const dockerfile = image.dockerfile;
|
|
137
|
+
(0, vitest_1.expect)(dockerfile).toContain('ENV PATH="/usr/local/bin:$PATH"');
|
|
138
|
+
(0, vitest_1.expect)(dockerfile).toContain('ENV CONNECTION_STRING="host=localhost;port=5432"');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
(0, vitest_1.describe)("ImageInstanceInstance.copy", () => {
|
|
142
|
+
(0, vitest_1.it)("adds COPY instruction", () => {
|
|
143
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").copy(".", "/app");
|
|
144
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("COPY . /app");
|
|
145
|
+
});
|
|
146
|
+
(0, vitest_1.it)("works with specific paths", () => {
|
|
147
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").copy("requirements.txt", "/app/requirements.txt");
|
|
148
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("COPY requirements.txt /app/requirements.txt");
|
|
149
|
+
});
|
|
150
|
+
(0, vitest_1.it)("supports multiple copy instructions", () => {
|
|
151
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
152
|
+
.copy("requirements.txt", "/app/requirements.txt")
|
|
153
|
+
.copy("src/", "/app/src/");
|
|
154
|
+
const dockerfile = image.dockerfile;
|
|
155
|
+
(0, vitest_1.expect)(dockerfile).toContain("COPY requirements.txt /app/requirements.txt");
|
|
156
|
+
(0, vitest_1.expect)(dockerfile).toContain("COPY src/ /app/src/");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
(0, vitest_1.describe)("ImageInstanceInstance.expose", () => {
|
|
160
|
+
(0, vitest_1.it)("exposes single port", () => {
|
|
161
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").expose(8080);
|
|
162
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("EXPOSE 8080");
|
|
163
|
+
});
|
|
164
|
+
(0, vitest_1.it)("exposes multiple ports", () => {
|
|
165
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").expose(80, 443, 8080);
|
|
166
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("EXPOSE 80");
|
|
167
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("EXPOSE 443");
|
|
168
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("EXPOSE 8080");
|
|
169
|
+
});
|
|
170
|
+
(0, vitest_1.it)("returns same image with no ports", () => {
|
|
171
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
172
|
+
const image2 = image1.expose();
|
|
173
|
+
(0, vitest_1.expect)(image1).toBe(image2);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
(0, vitest_1.describe)("ImageInstanceInstance.entrypoint", () => {
|
|
177
|
+
(0, vitest_1.it)("sets single arg entrypoint", () => {
|
|
178
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("python");
|
|
179
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENTRYPOINT ["python"]');
|
|
180
|
+
});
|
|
181
|
+
(0, vitest_1.it)("sets multiple args entrypoint", () => {
|
|
182
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("python", "-m", "app");
|
|
183
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENTRYPOINT ["python", "-m", "app"]');
|
|
184
|
+
});
|
|
185
|
+
(0, vitest_1.it)("returns same image with no args", () => {
|
|
186
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
187
|
+
const image2 = image1.entrypoint();
|
|
188
|
+
(0, vitest_1.expect)(image1).toBe(image2);
|
|
189
|
+
});
|
|
190
|
+
(0, vitest_1.it)("escapes double quotes in arguments", () => {
|
|
191
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("echo", 'hello"world');
|
|
192
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENTRYPOINT ["echo", "hello\\"world"]');
|
|
193
|
+
});
|
|
194
|
+
(0, vitest_1.it)("escapes backslashes in arguments", () => {
|
|
195
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("echo", "path\\to\\file");
|
|
196
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENTRYPOINT ["echo", "path\\\\to\\\\file"]');
|
|
197
|
+
});
|
|
198
|
+
(0, vitest_1.it)("escapes newlines in arguments", () => {
|
|
199
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("echo", "line1\nline2");
|
|
200
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('ENTRYPOINT ["echo", "line1\\nline2"]');
|
|
201
|
+
});
|
|
202
|
+
(0, vitest_1.it)("handles complex strings with multiple special characters", () => {
|
|
203
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").entrypoint("/bin/sh", "-c", 'echo "hello\nworld" && cat /path\\to\\file');
|
|
204
|
+
const dockerfile = image.dockerfile;
|
|
205
|
+
(0, vitest_1.expect)(dockerfile).toContain('ENTRYPOINT ["/bin/sh", "-c", "echo \\"hello\\nworld\\" && cat /path\\\\to\\\\file"]');
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
(0, vitest_1.describe)("ImageInstanceInstance.user", () => {
|
|
209
|
+
(0, vitest_1.it)("sets user by name", () => {
|
|
210
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").user("appuser");
|
|
211
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("USER appuser");
|
|
212
|
+
});
|
|
213
|
+
(0, vitest_1.it)("sets user by UID", () => {
|
|
214
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").user("1000");
|
|
215
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("USER 1000");
|
|
216
|
+
});
|
|
217
|
+
(0, vitest_1.it)("sets user with UID:GID format", () => {
|
|
218
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").user("1000:1000");
|
|
219
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("USER 1000:1000");
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.describe)("ImageInstanceInstance.label", () => {
|
|
223
|
+
(0, vitest_1.it)("adds single label", () => {
|
|
224
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").label({ version: "1.0" });
|
|
225
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('LABEL version="1.0"');
|
|
226
|
+
});
|
|
227
|
+
(0, vitest_1.it)("adds multiple labels", () => {
|
|
228
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").label({
|
|
229
|
+
version: "1.0",
|
|
230
|
+
maintainer: "test@example.com",
|
|
231
|
+
});
|
|
232
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('LABEL version="1.0"');
|
|
233
|
+
(0, vitest_1.expect)(image.dockerfile).toContain('LABEL maintainer="test@example.com"');
|
|
234
|
+
});
|
|
235
|
+
(0, vitest_1.it)("returns same image with empty object", () => {
|
|
236
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
237
|
+
const image2 = image1.label({});
|
|
238
|
+
(0, vitest_1.expect)(image1).toBe(image2);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
(0, vitest_1.describe)("ImageInstanceInstance.arg", () => {
|
|
242
|
+
(0, vitest_1.it)("defines arg without default value", () => {
|
|
243
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").arg("VERSION");
|
|
244
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("ARG VERSION");
|
|
245
|
+
});
|
|
246
|
+
(0, vitest_1.it)("defines arg with default value", () => {
|
|
247
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").arg("VERSION", "1.0");
|
|
248
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("ARG VERSION=1.0");
|
|
249
|
+
});
|
|
250
|
+
(0, vitest_1.it)("supports multiple ARG instructions", () => {
|
|
251
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
252
|
+
.arg("VERSION", "1.0")
|
|
253
|
+
.arg("BUILD_DATE")
|
|
254
|
+
.arg("GIT_COMMIT", "unknown");
|
|
255
|
+
const dockerfile = image.dockerfile;
|
|
256
|
+
(0, vitest_1.expect)(dockerfile).toContain("ARG VERSION=1.0");
|
|
257
|
+
(0, vitest_1.expect)(dockerfile).toContain("ARG BUILD_DATE");
|
|
258
|
+
(0, vitest_1.expect)(dockerfile).toContain("ARG GIT_COMMIT=unknown");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
(0, vitest_1.describe)("ImageInstance chaining", () => {
|
|
262
|
+
(0, vitest_1.it)("supports full chain of operations", () => {
|
|
263
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim")
|
|
264
|
+
.runCommands("apt-get update && apt-get install -y git curl")
|
|
265
|
+
.workdir("/app")
|
|
266
|
+
.copy(".", "/app")
|
|
267
|
+
.runCommands("pip install -r requirements.txt")
|
|
268
|
+
.env({ PYTHONUNBUFFERED: "1" })
|
|
269
|
+
.expose(8080);
|
|
270
|
+
const dockerfile = image.dockerfile;
|
|
271
|
+
(0, vitest_1.expect)(dockerfile).toContain("FROM python:3.11-slim");
|
|
272
|
+
(0, vitest_1.expect)(dockerfile).toContain("apt-get install");
|
|
273
|
+
(0, vitest_1.expect)(dockerfile).toContain("WORKDIR /app");
|
|
274
|
+
(0, vitest_1.expect)(dockerfile).toContain("COPY . /app");
|
|
275
|
+
(0, vitest_1.expect)(dockerfile).toContain("RUN pip install -r requirements.txt");
|
|
276
|
+
(0, vitest_1.expect)(dockerfile).toContain('ENV PYTHONUNBUFFERED="1"');
|
|
277
|
+
(0, vitest_1.expect)(dockerfile).toContain("EXPOSE 8080");
|
|
278
|
+
});
|
|
279
|
+
(0, vitest_1.it)("maintains immutability through chaining", () => {
|
|
280
|
+
const base = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
281
|
+
const withWorkdir = base.workdir("/app");
|
|
282
|
+
const withEnv = withWorkdir.env({ DEBUG: "true" });
|
|
283
|
+
// Each should be a different instance
|
|
284
|
+
(0, vitest_1.expect)(base).not.toBe(withWorkdir);
|
|
285
|
+
(0, vitest_1.expect)(withWorkdir).not.toBe(withEnv);
|
|
286
|
+
(0, vitest_1.expect)(base).not.toBe(withEnv);
|
|
287
|
+
// Original should not be modified
|
|
288
|
+
(0, vitest_1.expect)(base.dockerfile).not.toContain("WORKDIR");
|
|
289
|
+
(0, vitest_1.expect)(base.dockerfile).not.toContain("ENV");
|
|
290
|
+
});
|
|
291
|
+
(0, vitest_1.it)("supports complex web app image pattern", () => {
|
|
292
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim")
|
|
293
|
+
.arg("APP_VERSION", "1.0.0")
|
|
294
|
+
.label({
|
|
295
|
+
maintainer: "dev@example.com",
|
|
296
|
+
version: "1.0.0",
|
|
297
|
+
})
|
|
298
|
+
.runCommands("apt-get update && apt-get install -y curl ca-certificates")
|
|
299
|
+
.workdir("/app")
|
|
300
|
+
.env({
|
|
301
|
+
PYTHONUNBUFFERED: "1",
|
|
302
|
+
PYTHONDONTWRITEBYTECODE: "1",
|
|
303
|
+
PIP_NO_CACHE_DIR: "1",
|
|
304
|
+
})
|
|
305
|
+
.copy("requirements.txt", "/app/requirements.txt")
|
|
306
|
+
.runCommands("pip install -r requirements.txt")
|
|
307
|
+
.copy(".", "/app")
|
|
308
|
+
.user("1000:1000")
|
|
309
|
+
.expose(8000)
|
|
310
|
+
.entrypoint("python", "-m", "gunicorn");
|
|
311
|
+
const dockerfile = image.dockerfile;
|
|
312
|
+
(0, vitest_1.expect)(dockerfile).toContain("FROM python:3.11-slim");
|
|
313
|
+
(0, vitest_1.expect)(dockerfile).toContain("ARG APP_VERSION=1.0.0");
|
|
314
|
+
(0, vitest_1.expect)(dockerfile).toContain('LABEL maintainer="dev@example.com"');
|
|
315
|
+
(0, vitest_1.expect)(dockerfile).toContain("apt-get install");
|
|
316
|
+
(0, vitest_1.expect)(dockerfile).toContain("WORKDIR /app");
|
|
317
|
+
(0, vitest_1.expect)(dockerfile).toContain("PYTHONUNBUFFERED");
|
|
318
|
+
(0, vitest_1.expect)(dockerfile).toContain("USER 1000:1000");
|
|
319
|
+
(0, vitest_1.expect)(dockerfile).toContain("EXPOSE 8000");
|
|
320
|
+
(0, vitest_1.expect)(dockerfile).toContain("ENTRYPOINT");
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
(0, vitest_1.describe)("ImageInstanceInstance.hash", () => {
|
|
324
|
+
(0, vitest_1.it)("produces consistent hash for same image", () => {
|
|
325
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
326
|
+
const image2 = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
327
|
+
(0, vitest_1.expect)(image1.hash).toBe(image2.hash);
|
|
328
|
+
});
|
|
329
|
+
(0, vitest_1.it)("produces different hash for different images", () => {
|
|
330
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
331
|
+
const image2 = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/other");
|
|
332
|
+
(0, vitest_1.expect)(image1.hash).not.toBe(image2.hash);
|
|
333
|
+
});
|
|
334
|
+
(0, vitest_1.it)("produces different hash for different base images", () => {
|
|
335
|
+
const image1 = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
336
|
+
const image2 = image_js_1.ImageInstance.fromRegistry("python:3.12").workdir("/app");
|
|
337
|
+
(0, vitest_1.expect)(image1.hash).not.toBe(image2.hash);
|
|
338
|
+
});
|
|
339
|
+
(0, vitest_1.it)("has consistent length of 12 characters", () => {
|
|
340
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
341
|
+
(0, vitest_1.expect)(image.hash.length).toBe(12);
|
|
342
|
+
});
|
|
343
|
+
(0, vitest_1.it)("throws error when local file is missing", () => {
|
|
344
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalFile(tempFile, "/app/file.txt");
|
|
345
|
+
// Delete the file after adding it to the image
|
|
346
|
+
(0, fs_1.rmSync)(tempFile);
|
|
347
|
+
(0, vitest_1.expect)(() => image.hash).toThrow(/Local file not found/);
|
|
348
|
+
});
|
|
349
|
+
(0, vitest_1.it)("throws error when local directory is missing", () => {
|
|
350
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalDir(tempSourceDir, "/app");
|
|
351
|
+
// Delete the directory after adding it to the image
|
|
352
|
+
(0, fs_1.rmSync)(tempSourceDir, { recursive: true });
|
|
353
|
+
(0, vitest_1.expect)(() => image.hash).toThrow(/Local file not found/);
|
|
354
|
+
});
|
|
355
|
+
(0, vitest_1.it)("produces different hash when file content changes", () => {
|
|
356
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalFile(tempFile, "/app/file.txt");
|
|
357
|
+
const hash1 = image.hash;
|
|
358
|
+
// Modify the file (change mtime)
|
|
359
|
+
(0, fs_1.writeFileSync)(tempFile, "modified content");
|
|
360
|
+
const hash2 = image.hash;
|
|
361
|
+
(0, vitest_1.expect)(hash1).not.toBe(hash2);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
(0, vitest_1.describe)("ImageInstanceInstance.write", () => {
|
|
365
|
+
(0, vitest_1.it)("creates Dockerfile", () => {
|
|
366
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
367
|
+
const buildDir = image.write(tempDir, "test-image");
|
|
368
|
+
const dockerfilePath = (0, path_1.join)(buildDir, "Dockerfile");
|
|
369
|
+
const content = (0, fs_1.readFileSync)(dockerfilePath, "utf-8");
|
|
370
|
+
(0, vitest_1.expect)(content).toContain("FROM python:3.11");
|
|
371
|
+
(0, vitest_1.expect)(content).toContain("WORKDIR /app");
|
|
372
|
+
});
|
|
373
|
+
(0, vitest_1.it)("creates manifest.json", () => {
|
|
374
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
375
|
+
const buildDir = image.write(tempDir, "test-image");
|
|
376
|
+
const manifestPath = (0, path_1.join)(buildDir, "manifest.json");
|
|
377
|
+
const manifest = JSON.parse((0, fs_1.readFileSync)(manifestPath, "utf-8"));
|
|
378
|
+
(0, vitest_1.expect)(manifest.base_image).toBe("python:3.11");
|
|
379
|
+
(0, vitest_1.expect)(manifest.hash).toBeDefined();
|
|
380
|
+
(0, vitest_1.expect)(manifest.instructions_count).toBe(1);
|
|
381
|
+
});
|
|
382
|
+
(0, vitest_1.it)("auto-generates folder name from hash", () => {
|
|
383
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
384
|
+
const buildDir = image.write(tempDir);
|
|
385
|
+
const dirName = buildDir.split("/").pop() || "";
|
|
386
|
+
(0, vitest_1.expect)(dirName.startsWith("image-")).toBe(true);
|
|
387
|
+
(0, vitest_1.expect)(dirName.length).toBe(6 + 12); // "image-" + 12 char hash
|
|
388
|
+
});
|
|
389
|
+
(0, vitest_1.it)("creates nested directories if needed", () => {
|
|
390
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11");
|
|
391
|
+
const nestedPath = (0, path_1.join)(tempDir, "deep", "nested", "path");
|
|
392
|
+
const buildDir = image.write(nestedPath, "test-image");
|
|
393
|
+
const dockerfilePath = (0, path_1.join)(buildDir, "Dockerfile");
|
|
394
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)(dockerfilePath, "utf-8")).toContain("FROM python:3.11");
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
(0, vitest_1.describe)("ImageInstanceInstance.writeTemp", () => {
|
|
398
|
+
(0, vitest_1.it)("creates in temporary directory", () => {
|
|
399
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").workdir("/app");
|
|
400
|
+
let buildDir = null;
|
|
401
|
+
try {
|
|
402
|
+
buildDir = image.writeTemp();
|
|
403
|
+
const dockerfilePath = (0, path_1.join)(buildDir, "Dockerfile");
|
|
404
|
+
const manifestPath = (0, path_1.join)(buildDir, "manifest.json");
|
|
405
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)(dockerfilePath, "utf-8")).toContain("FROM python:3.11");
|
|
406
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)(manifestPath, "utf-8")).toContain("python:3.11");
|
|
407
|
+
}
|
|
408
|
+
finally {
|
|
409
|
+
if (buildDir) {
|
|
410
|
+
(0, fs_1.rmSync)((0, path_1.join)(buildDir, ".."), { recursive: true, force: true });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
(0, vitest_1.describe)("ImageInstanceInstance.addLocalFile", () => {
|
|
416
|
+
(0, vitest_1.it)("adds COPY instruction", () => {
|
|
417
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalFile(tempFile, "/app/file.txt");
|
|
418
|
+
(0, vitest_1.expect)(image.dockerfile).toContain("COPY");
|
|
419
|
+
});
|
|
420
|
+
(0, vitest_1.it)("copies file to build context", () => {
|
|
421
|
+
const outputDir = (0, path_1.join)(tempDir, "output");
|
|
422
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalFile(tempFile, "/app/file.txt", "myfile.txt");
|
|
423
|
+
const buildDir = image.write(outputDir, "test-image");
|
|
424
|
+
const copiedFile = (0, path_1.join)(buildDir, "myfile.txt");
|
|
425
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)(copiedFile, "utf-8")).toBe("test content");
|
|
426
|
+
});
|
|
427
|
+
(0, vitest_1.it)("throws error for missing file", () => {
|
|
428
|
+
const outputDir = (0, path_1.join)(tempDir, "output");
|
|
429
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalFile("/nonexistent/file.txt", "/app/file.txt");
|
|
430
|
+
(0, vitest_1.expect)(() => image.write(outputDir, "test-image")).toThrow(/not found/i);
|
|
431
|
+
});
|
|
432
|
+
(0, vitest_1.it)("adds multiple local files", () => {
|
|
433
|
+
const file1 = (0, path_1.join)(tempDir, "file1.txt");
|
|
434
|
+
const file2 = (0, path_1.join)(tempDir, "file2.txt");
|
|
435
|
+
(0, fs_1.writeFileSync)(file1, "content1");
|
|
436
|
+
(0, fs_1.writeFileSync)(file2, "content2");
|
|
437
|
+
const outputDir = (0, path_1.join)(tempDir, "output");
|
|
438
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
439
|
+
.addLocalFile(file1, "/app/file1.txt")
|
|
440
|
+
.addLocalFile(file2, "/app/file2.txt");
|
|
441
|
+
const buildDir = image.write(outputDir, "test-image");
|
|
442
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)((0, path_1.join)(buildDir, "file1.txt"), "utf-8")).toBe("content1");
|
|
443
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)((0, path_1.join)(buildDir, "file2.txt"), "utf-8")).toBe("content2");
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
(0, vitest_1.describe)("ImageInstanceInstance.addLocalDir", () => {
|
|
447
|
+
(0, vitest_1.it)("copies directory to build context", () => {
|
|
448
|
+
const outputDir = (0, path_1.join)(tempDir, "output");
|
|
449
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalDir(tempSourceDir, "/app", "mydir");
|
|
450
|
+
const buildDir = image.write(outputDir, "test-image");
|
|
451
|
+
const copiedDir = (0, path_1.join)(buildDir, "mydir");
|
|
452
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)((0, path_1.join)(copiedDir, "file1.txt"), "utf-8")).toBe("content1");
|
|
453
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)((0, path_1.join)(copiedDir, "file2.txt"), "utf-8")).toBe("content2");
|
|
454
|
+
});
|
|
455
|
+
(0, vitest_1.it)("preserves directory structure", () => {
|
|
456
|
+
const outputDir = (0, path_1.join)(tempDir, "output");
|
|
457
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11").addLocalDir(tempSourceDir, "/app", "mydir");
|
|
458
|
+
const buildDir = image.write(outputDir, "test-image");
|
|
459
|
+
const copiedDir = (0, path_1.join)(buildDir, "mydir");
|
|
460
|
+
(0, vitest_1.expect)((0, fs_1.readFileSync)((0, path_1.join)(copiedDir, "subdir", "nested.txt"), "utf-8")).toBe("nested content");
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
(0, vitest_1.describe)("ImageInstance sandbox-api preparation", () => {
|
|
464
|
+
(0, vitest_1.it)("adds sandbox-api COPY instruction", () => {
|
|
465
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim");
|
|
466
|
+
// @ts-expect-error - accessing private method for testing
|
|
467
|
+
const prepared = image._prepareForSandbox();
|
|
468
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api");
|
|
469
|
+
});
|
|
470
|
+
(0, vitest_1.it)("adds default entrypoint if not set", () => {
|
|
471
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim");
|
|
472
|
+
// @ts-expect-error - accessing private method for testing
|
|
473
|
+
const prepared = image._prepareForSandbox();
|
|
474
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain('ENTRYPOINT ["/usr/local/bin/sandbox-api"]');
|
|
475
|
+
});
|
|
476
|
+
(0, vitest_1.it)("preserves user-defined entrypoint", () => {
|
|
477
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim").entrypoint("/custom/entrypoint");
|
|
478
|
+
// @ts-expect-error - accessing private method for testing
|
|
479
|
+
const prepared = image._prepareForSandbox();
|
|
480
|
+
// User entrypoint should be present
|
|
481
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain('ENTRYPOINT ["/custom/entrypoint"]');
|
|
482
|
+
// Should only have one ENTRYPOINT
|
|
483
|
+
const entrypointCount = (prepared.dockerfile.match(/ENTRYPOINT/g) || []).length;
|
|
484
|
+
(0, vitest_1.expect)(entrypointCount).toBe(1);
|
|
485
|
+
});
|
|
486
|
+
(0, vitest_1.it)("uses custom sandbox version", () => {
|
|
487
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim");
|
|
488
|
+
// @ts-expect-error - accessing private method for testing
|
|
489
|
+
const prepared = image._prepareForSandbox("v1.2.3");
|
|
490
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("COPY --from=ghcr.io/blaxel-ai/sandbox:v1.2.3 /sandbox-api /usr/local/bin/sandbox-api");
|
|
491
|
+
});
|
|
492
|
+
(0, vitest_1.it)("does not duplicate sandbox-api if base image is sandbox", () => {
|
|
493
|
+
const image = image_js_1.ImageInstance.fromRegistry("ghcr.io/blaxel-ai/sandbox:latest");
|
|
494
|
+
// @ts-expect-error - accessing private method for testing
|
|
495
|
+
(0, vitest_1.expect)(image._hasSandboxApi()).toBe(true);
|
|
496
|
+
// @ts-expect-error - accessing private method for testing
|
|
497
|
+
const prepared = image._prepareForSandbox();
|
|
498
|
+
const copyCount = (prepared.dockerfile.match(/COPY --from=ghcr.io\/blaxel-ai\/sandbox/g) || [])
|
|
499
|
+
.length;
|
|
500
|
+
(0, vitest_1.expect)(copyCount).toBe(0);
|
|
501
|
+
});
|
|
502
|
+
(0, vitest_1.it)("returns new image instance (immutability)", () => {
|
|
503
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim");
|
|
504
|
+
// @ts-expect-error - accessing private method for testing
|
|
505
|
+
const prepared = image._prepareForSandbox();
|
|
506
|
+
(0, vitest_1.expect)(image).not.toBe(prepared);
|
|
507
|
+
(0, vitest_1.expect)(image.dockerfile).not.toContain("sandbox-api");
|
|
508
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("sandbox-api");
|
|
509
|
+
});
|
|
510
|
+
(0, vitest_1.it)("preserves all original instructions", () => {
|
|
511
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11-slim")
|
|
512
|
+
.runCommands("apt-get update && apt-get install -y curl git")
|
|
513
|
+
.workdir("/app")
|
|
514
|
+
.runCommands("pip install requests")
|
|
515
|
+
.env({ DEBUG: "true" });
|
|
516
|
+
// @ts-expect-error - accessing private method for testing
|
|
517
|
+
const prepared = image._prepareForSandbox();
|
|
518
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("apt-get install");
|
|
519
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("WORKDIR /app");
|
|
520
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("pip install");
|
|
521
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain('DEBUG="true"');
|
|
522
|
+
(0, vitest_1.expect)(prepared.dockerfile).toContain("sandbox-api");
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
(0, vitest_1.describe)("Dockerfile instruction order", () => {
|
|
526
|
+
(0, vitest_1.it)("preserves instruction order", () => {
|
|
527
|
+
const image = image_js_1.ImageInstance.fromRegistry("python:3.11")
|
|
528
|
+
.env({ FIRST: "1" })
|
|
529
|
+
.workdir("/app")
|
|
530
|
+
.env({ SECOND: "2" })
|
|
531
|
+
.runCommands("echo middle")
|
|
532
|
+
.env({ THIRD: "3" });
|
|
533
|
+
const dockerfile = image.dockerfile;
|
|
534
|
+
const lines = dockerfile.split("\n");
|
|
535
|
+
const firstIdx = lines.findIndex((line) => line.includes('FIRST="1"'));
|
|
536
|
+
const workdirIdx = lines.findIndex((line) => line.includes("WORKDIR /app"));
|
|
537
|
+
const secondIdx = lines.findIndex((line) => line.includes('SECOND="2"'));
|
|
538
|
+
const runIdx = lines.findIndex((line) => line.includes("echo middle"));
|
|
539
|
+
const thirdIdx = lines.findIndex((line) => line.includes('THIRD="3"'));
|
|
540
|
+
(0, vitest_1.expect)(firstIdx).toBeLessThan(workdirIdx);
|
|
541
|
+
(0, vitest_1.expect)(workdirIdx).toBeLessThan(secondIdx);
|
|
542
|
+
(0, vitest_1.expect)(secondIdx).toBeLessThan(runIdx);
|
|
543
|
+
(0, vitest_1.expect)(runIdx).toBeLessThan(thirdIdx);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SANDBOX_API_PATH = exports.SANDBOX_API_IMAGE = exports.ImageInstance = void 0;
|
|
4
|
+
var image_js_1 = require("./image.js");
|
|
5
|
+
Object.defineProperty(exports, "ImageInstance", { enumerable: true, get: function () { return image_js_1.ImageInstance; } });
|
|
6
|
+
Object.defineProperty(exports, "SANDBOX_API_IMAGE", { enumerable: true, get: function () { return image_js_1.SANDBOX_API_IMAGE; } });
|
|
7
|
+
Object.defineProperty(exports, "SANDBOX_API_PATH", { enumerable: true, get: function () { return image_js_1.SANDBOX_API_PATH; } });
|
package/dist/cjs/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __exportStar(require("./common/internal.js"), exports);
|
|
|
25
25
|
__exportStar(require("./common/logger.js"), exports);
|
|
26
26
|
__exportStar(require("./common/settings.js"), exports);
|
|
27
27
|
__exportStar(require("./common/webhook.js"), exports);
|
|
28
|
+
__exportStar(require("./image/index.js"), exports);
|
|
28
29
|
__exportStar(require("./jobs/index.js"), exports);
|
|
29
30
|
__exportStar(require("./mcp/index.js"), exports);
|
|
30
31
|
__exportStar(require("./models/index.js"), exports);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Sandbox } from "../client/types.gen.js";
|
|
2
|
+
export declare const SANDBOX_API_IMAGE = "ghcr.io/blaxel-ai/sandbox";
|
|
3
|
+
export declare const SANDBOX_API_PATH = "/usr/local/bin/sandbox-api";
|
|
4
|
+
/**
|
|
5
|
+
* Represents a local file to be copied into the build context.
|
|
6
|
+
*/
|
|
7
|
+
export interface LocalFile {
|
|
8
|
+
sourcePath: string;
|
|
9
|
+
destinationPath: string;
|
|
10
|
+
contextName: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Contains all information needed to generate a deployable folder.
|
|
14
|
+
*/
|
|
15
|
+
export interface ImageBuildContext {
|
|
16
|
+
baseImage: string;
|
|
17
|
+
instructions: string[];
|
|
18
|
+
localFiles: LocalFile[];
|
|
19
|
+
hasEntrypoint: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Options for building and deploying an image
|
|
23
|
+
*/
|
|
24
|
+
export interface ImageBuildOptions {
|
|
25
|
+
name: string;
|
|
26
|
+
memory?: number;
|
|
27
|
+
timeout?: number;
|
|
28
|
+
onStatusChange?: (status: string) => void;
|
|
29
|
+
sandboxVersion?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A fluent builder for creating sandbox images programmatically.
|
|
33
|
+
*
|
|
34
|
+
* NOTE: This class is only available in Node.js environments.
|
|
35
|
+
* In browser environments, all methods will throw an error.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ImageInstance {
|
|
38
|
+
private _context;
|
|
39
|
+
constructor(_context: ImageBuildContext);
|
|
40
|
+
static fromRegistry(_tag: string): ImageInstance;
|
|
41
|
+
workdir(_path: string): ImageInstance;
|
|
42
|
+
runCommands(..._commands: string[]): ImageInstance;
|
|
43
|
+
env(_variables: Record<string, string>): ImageInstance;
|
|
44
|
+
copy(_source: string, _destination: string): ImageInstance;
|
|
45
|
+
addLocalFile(_sourcePath: string, _destination: string, _contextName?: string): ImageInstance;
|
|
46
|
+
addLocalDir(_sourcePath: string, _destination: string, _contextName?: string): ImageInstance;
|
|
47
|
+
expose(..._ports: number[]): ImageInstance;
|
|
48
|
+
entrypoint(..._args: string[]): ImageInstance;
|
|
49
|
+
user(_user: string): ImageInstance;
|
|
50
|
+
label(_labels: Record<string, string>): ImageInstance;
|
|
51
|
+
arg(_name: string, _defaultValue?: string): ImageInstance;
|
|
52
|
+
get dockerfile(): string;
|
|
53
|
+
get hash(): string;
|
|
54
|
+
get baseImage(): string;
|
|
55
|
+
write(_outputPath: string, _name?: string): string;
|
|
56
|
+
writeTemp(): string;
|
|
57
|
+
build(_options: ImageBuildOptions): Promise<Sandbox>;
|
|
58
|
+
buildSync(_options: ImageBuildOptions): Promise<Sandbox>;
|
|
59
|
+
}
|