@computesdk/archil 0.4.8 → 0.4.10
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 +6 -3
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +205 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +205 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ await provider.sandbox.destroy(sandbox.sandboxId);
|
|
|
58
58
|
| `runCommand` | ✅ | Executes shell commands through Archil's HTTP `exec` endpoint. |
|
|
59
59
|
| `getInfo` | ✅ | |
|
|
60
60
|
| `getUrl` | ❌ | Each exec runs in a fresh ephemeral container — no port to expose. |
|
|
61
|
-
| `filesystem` | ✅ |
|
|
61
|
+
| `filesystem` | ✅ | Maps paths into `/mnt/archil` and manages shared-mode checkout/checkin automatically. |
|
|
62
62
|
|
|
63
63
|
## Limitations
|
|
64
64
|
|
|
@@ -67,5 +67,8 @@ await provider.sandbox.destroy(sandbox.sandboxId);
|
|
|
67
67
|
- Responses are truncated to ~5 MB by the Archil control plane.
|
|
68
68
|
- `getUrl` is not supported — each exec runs in a fresh ephemeral container,
|
|
69
69
|
so there is no long-lived process to expose a port on.
|
|
70
|
-
- Filesystem operations
|
|
71
|
-
|
|
70
|
+
- Filesystem operations accept normal absolute paths and map them into the
|
|
71
|
+
`/mnt/archil` disk mount internally. Each mutating operation checks out and
|
|
72
|
+
checks in the disk automatically.
|
|
73
|
+
- Raw `runCommand` strings are not rewritten. Commands that access the disk
|
|
74
|
+
directly must use `/mnt/archil` themselves.
|
package/dist/index.d.mts
CHANGED
|
@@ -26,12 +26,13 @@ interface DiskResponse {
|
|
|
26
26
|
region: string;
|
|
27
27
|
createdAt: string;
|
|
28
28
|
}
|
|
29
|
+
type DiskHandle = Pick<DiskResponse, 'id'>;
|
|
29
30
|
interface ResolvedConfig {
|
|
30
31
|
apiKey: string;
|
|
31
32
|
baseUrl: string;
|
|
32
33
|
}
|
|
33
34
|
interface ArchilSandbox {
|
|
34
|
-
disk: DiskResponse;
|
|
35
|
+
disk: DiskHandle | DiskResponse;
|
|
35
36
|
resolved: ResolvedConfig;
|
|
36
37
|
createdAt: Date;
|
|
37
38
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,12 +26,13 @@ interface DiskResponse {
|
|
|
26
26
|
region: string;
|
|
27
27
|
createdAt: string;
|
|
28
28
|
}
|
|
29
|
+
type DiskHandle = Pick<DiskResponse, 'id'>;
|
|
29
30
|
interface ResolvedConfig {
|
|
30
31
|
apiKey: string;
|
|
31
32
|
baseUrl: string;
|
|
32
33
|
}
|
|
33
34
|
interface ArchilSandbox {
|
|
34
|
-
disk: DiskResponse;
|
|
35
|
+
disk: DiskHandle | DiskResponse;
|
|
35
36
|
resolved: ResolvedConfig;
|
|
36
37
|
createdAt: Date;
|
|
37
38
|
}
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,11 @@ __export(index_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
26
|
var import_provider = require("@computesdk/provider");
|
|
27
|
+
var import_node_crypto = require("crypto");
|
|
28
|
+
var import_node_path = require("path");
|
|
29
|
+
var ARCHIL_MOUNT_ROOT = "/mnt/archil";
|
|
30
|
+
var ARCHIL_MAX_EXEC_COMMAND_BYTES = 102400;
|
|
31
|
+
var ARCHIL_MAX_READ_CHUNK_BYTES = 2 * 1024 * 1024;
|
|
27
32
|
var REGION_COLORS = {
|
|
28
33
|
"gcp-us-central1": "blue"
|
|
29
34
|
};
|
|
@@ -94,6 +99,73 @@ function resolveCreateDiskId(options) {
|
|
|
94
99
|
function shellEscape(value) {
|
|
95
100
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
96
101
|
}
|
|
102
|
+
function mapFilesystemPath(path) {
|
|
103
|
+
const normalized = import_node_path.posix.normalize(path.startsWith("/") ? path : `/${path}`);
|
|
104
|
+
if (normalized === "/") {
|
|
105
|
+
return ARCHIL_MOUNT_ROOT;
|
|
106
|
+
}
|
|
107
|
+
if (normalized === ARCHIL_MOUNT_ROOT || normalized.startsWith(`${ARCHIL_MOUNT_ROOT}/`)) {
|
|
108
|
+
return normalized;
|
|
109
|
+
}
|
|
110
|
+
return `${ARCHIL_MOUNT_ROOT}${normalized}`;
|
|
111
|
+
}
|
|
112
|
+
function withDiskWriteLock(command) {
|
|
113
|
+
const mountRoot = shellEscape(ARCHIL_MOUNT_ROOT);
|
|
114
|
+
return [
|
|
115
|
+
`archil checkout --force --yes ${mountRoot}`,
|
|
116
|
+
`{ ${command}; status=$?; archil checkin ${mountRoot}; checkin_status=$?; if [ $status -ne 0 ]; then exit $status; fi; exit $checkin_status; }`
|
|
117
|
+
].join(" && ");
|
|
118
|
+
}
|
|
119
|
+
function execCommandBytes(command) {
|
|
120
|
+
return Buffer.byteLength(wrapCommand(command), "utf8");
|
|
121
|
+
}
|
|
122
|
+
function buildWriteChunkCommand(parent, tempPath, diskPath, encodedChunk, isFirst, isFinal) {
|
|
123
|
+
const writeCommand = `printf %s ${shellEscape(encodedChunk)} | base64 -d ${isFirst ? ">" : ">>"} ${shellEscape(tempPath)}`;
|
|
124
|
+
return [
|
|
125
|
+
...isFirst ? [`mkdir -p ${shellEscape(parent)}`] : [],
|
|
126
|
+
writeCommand,
|
|
127
|
+
...isFinal ? [finalizeStagedFileCommand(tempPath, diskPath)] : []
|
|
128
|
+
].join(" && ");
|
|
129
|
+
}
|
|
130
|
+
function finalizeStagedFileCommand(tempPath, diskPath) {
|
|
131
|
+
const quotedDestination = shellEscape(diskPath);
|
|
132
|
+
return [
|
|
133
|
+
`if [ -d ${quotedDestination} ]; then`,
|
|
134
|
+
`printf '%s\\n' ${shellEscape(`Refusing to overwrite directory ${diskPath}`)} >&2;`,
|
|
135
|
+
"exit 1;",
|
|
136
|
+
"fi;",
|
|
137
|
+
`mv ${shellEscape(tempPath)} ${quotedDestination}`
|
|
138
|
+
].join(" ");
|
|
139
|
+
}
|
|
140
|
+
function maxWriteChunkSize(parent, tempPath, diskPath) {
|
|
141
|
+
let low = 0;
|
|
142
|
+
let high = ARCHIL_MAX_EXEC_COMMAND_BYTES;
|
|
143
|
+
while (low < high) {
|
|
144
|
+
const candidate = Math.ceil((low + high) / 2);
|
|
145
|
+
const command = withDiskWriteLock(
|
|
146
|
+
buildWriteChunkCommand(
|
|
147
|
+
parent,
|
|
148
|
+
tempPath,
|
|
149
|
+
diskPath,
|
|
150
|
+
"A".repeat(candidate),
|
|
151
|
+
true,
|
|
152
|
+
true
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
if (execCommandBytes(command) <= ARCHIL_MAX_EXEC_COMMAND_BYTES) {
|
|
156
|
+
low = candidate;
|
|
157
|
+
} else {
|
|
158
|
+
high = candidate - 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const chunkSize = low - low % 4;
|
|
162
|
+
if (chunkSize < 4) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Archil filesystem path is too long to write within the ${ARCHIL_MAX_EXEC_COMMAND_BYTES}-byte exec command limit.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return chunkSize;
|
|
168
|
+
}
|
|
97
169
|
function wrapCommand(command, options) {
|
|
98
170
|
let wrapped = command;
|
|
99
171
|
const envEntries = Object.entries(options?.env ?? {});
|
|
@@ -125,14 +197,9 @@ var _provider = (0, import_provider.defineProvider)({
|
|
|
125
197
|
create: async (config, options) => {
|
|
126
198
|
const resolved = resolveConfig(config);
|
|
127
199
|
const diskId = resolveCreateDiskId(options);
|
|
128
|
-
const disk = await callApi(
|
|
129
|
-
resolved,
|
|
130
|
-
"GET",
|
|
131
|
-
`/api/disks/${encodeURIComponent(diskId)}`
|
|
132
|
-
);
|
|
133
200
|
return {
|
|
134
|
-
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
135
|
-
sandboxId:
|
|
201
|
+
sandbox: { disk: { id: diskId }, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
202
|
+
sandboxId: diskId
|
|
136
203
|
};
|
|
137
204
|
},
|
|
138
205
|
getById: async (config, sandboxId) => {
|
|
@@ -181,18 +248,19 @@ var _provider = (0, import_provider.defineProvider)({
|
|
|
181
248
|
}
|
|
182
249
|
},
|
|
183
250
|
getInfo: async (sandbox) => {
|
|
251
|
+
const diskInfo = "status" in sandbox.disk ? sandbox.disk : void 0;
|
|
184
252
|
return {
|
|
185
253
|
id: sandbox.disk.id,
|
|
186
254
|
provider: "archil",
|
|
187
|
-
status:
|
|
188
|
-
createdAt: new Date(sandbox.
|
|
255
|
+
status: !diskInfo || diskInfo.status === "ready" ? "running" : "stopped",
|
|
256
|
+
createdAt: diskInfo ? new Date(diskInfo.createdAt) : sandbox.createdAt,
|
|
189
257
|
timeout: 0,
|
|
190
|
-
metadata: {
|
|
191
|
-
name:
|
|
192
|
-
organization:
|
|
193
|
-
region:
|
|
194
|
-
provider:
|
|
195
|
-
}
|
|
258
|
+
metadata: diskInfo ? {
|
|
259
|
+
name: diskInfo.name,
|
|
260
|
+
organization: diskInfo.organization,
|
|
261
|
+
region: diskInfo.region,
|
|
262
|
+
provider: diskInfo.provider
|
|
263
|
+
} : {}
|
|
196
264
|
};
|
|
197
265
|
},
|
|
198
266
|
getUrl: async (_sandbox, options) => {
|
|
@@ -202,36 +270,132 @@ var _provider = (0, import_provider.defineProvider)({
|
|
|
202
270
|
},
|
|
203
271
|
filesystem: {
|
|
204
272
|
readFile: async (sandbox, path, runCommand) => {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
273
|
+
const diskPath = mapFilesystemPath(path);
|
|
274
|
+
const sizeResult = await runCommand(
|
|
275
|
+
sandbox,
|
|
276
|
+
`wc -c < ${shellEscape(diskPath)}`
|
|
277
|
+
);
|
|
278
|
+
if (sizeResult.exitCode !== 0) {
|
|
279
|
+
throw new Error(`Failed to read ${path}: ${sizeResult.stderr}`);
|
|
280
|
+
}
|
|
281
|
+
const sizeText = sizeResult.stdout.trim();
|
|
282
|
+
if (!/^\d+$/.test(sizeText)) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Failed to read ${path}: Archil returned an invalid file size.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
const size = Number(sizeText);
|
|
288
|
+
if (!Number.isSafeInteger(size)) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Failed to read ${path}: file size exceeds JavaScript's safe integer range.`
|
|
291
|
+
);
|
|
208
292
|
}
|
|
209
|
-
return
|
|
293
|
+
if (size === 0) return "";
|
|
294
|
+
const chunks = [];
|
|
295
|
+
for (let offset = 0; offset < size; offset += ARCHIL_MAX_READ_CHUNK_BYTES) {
|
|
296
|
+
const expectedBytes = Math.min(
|
|
297
|
+
ARCHIL_MAX_READ_CHUNK_BYTES,
|
|
298
|
+
size - offset
|
|
299
|
+
);
|
|
300
|
+
const result = await runCommand(
|
|
301
|
+
sandbox,
|
|
302
|
+
`dd if=${shellEscape(diskPath)} bs=1 skip=${offset} count=${expectedBytes} 2>/dev/null | base64`
|
|
303
|
+
);
|
|
304
|
+
if (result.exitCode !== 0) {
|
|
305
|
+
throw new Error(`Failed to read ${path}: ${result.stderr}`);
|
|
306
|
+
}
|
|
307
|
+
const encoded = result.stdout.replace(/\s/g, "");
|
|
308
|
+
if (encoded.length === 0 || encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
const chunk = Buffer.from(encoded, "base64");
|
|
314
|
+
if (chunk.length !== expectedBytes) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
chunks.push(chunk);
|
|
320
|
+
}
|
|
321
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
210
322
|
},
|
|
211
323
|
writeFile: async (sandbox, path, content, runCommand) => {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);
|
|
215
|
-
}
|
|
324
|
+
const diskPath = mapFilesystemPath(path);
|
|
325
|
+
const parent = import_node_path.posix.dirname(diskPath);
|
|
216
326
|
const encoded = Buffer.from(content, "utf8").toString("base64");
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
327
|
+
const tempPath = `${diskPath}.computesdk-write-${(0, import_node_crypto.randomUUID)()}`;
|
|
328
|
+
let started = false;
|
|
329
|
+
try {
|
|
330
|
+
if (encoded.length === 0) {
|
|
331
|
+
started = true;
|
|
332
|
+
const result = await runCommand(
|
|
333
|
+
sandbox,
|
|
334
|
+
withDiskWriteLock(
|
|
335
|
+
[
|
|
336
|
+
`mkdir -p ${shellEscape(parent)}`,
|
|
337
|
+
`: > ${shellEscape(tempPath)}`,
|
|
338
|
+
finalizeStagedFileCommand(tempPath, diskPath)
|
|
339
|
+
].join(" && ")
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
if (result.exitCode !== 0) {
|
|
343
|
+
throw new Error(result.stderr);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const chunkSize = maxWriteChunkSize(parent, tempPath, diskPath);
|
|
348
|
+
for (let offset = 0; offset < encoded.length; offset += chunkSize) {
|
|
349
|
+
const isFirst = offset === 0;
|
|
350
|
+
const isFinal = offset + chunkSize >= encoded.length;
|
|
351
|
+
started = true;
|
|
352
|
+
const result = await runCommand(
|
|
353
|
+
sandbox,
|
|
354
|
+
withDiskWriteLock(
|
|
355
|
+
buildWriteChunkCommand(
|
|
356
|
+
parent,
|
|
357
|
+
tempPath,
|
|
358
|
+
diskPath,
|
|
359
|
+
encoded.slice(offset, offset + chunkSize),
|
|
360
|
+
isFirst,
|
|
361
|
+
isFinal
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
if (result.exitCode !== 0) {
|
|
366
|
+
throw new Error(result.stderr);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (started) {
|
|
371
|
+
try {
|
|
372
|
+
await runCommand(
|
|
373
|
+
sandbox,
|
|
374
|
+
withDiskWriteLock(`rm -f ${shellEscape(tempPath)}`)
|
|
375
|
+
);
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
throw new Error(
|
|
380
|
+
`Failed to write ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
381
|
+
);
|
|
223
382
|
}
|
|
224
383
|
},
|
|
225
384
|
mkdir: async (sandbox, path, runCommand) => {
|
|
226
|
-
const
|
|
385
|
+
const diskPath = mapFilesystemPath(path);
|
|
386
|
+
const result = await runCommand(
|
|
387
|
+
sandbox,
|
|
388
|
+
withDiskWriteLock(`mkdir -p ${shellEscape(diskPath)}`)
|
|
389
|
+
);
|
|
227
390
|
if (result.exitCode !== 0) {
|
|
228
391
|
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
229
392
|
}
|
|
230
393
|
},
|
|
231
394
|
readdir: async (sandbox, path, runCommand) => {
|
|
395
|
+
const diskPath = mapFilesystemPath(path);
|
|
232
396
|
const result = await runCommand(
|
|
233
397
|
sandbox,
|
|
234
|
-
`find ${shellEscape(
|
|
398
|
+
`find ${shellEscape(diskPath)} -mindepth 1 -maxdepth 1 -printf '%y\\t%s\\t%T@\\t%f\\n'`
|
|
235
399
|
);
|
|
236
400
|
if (result.exitCode !== 0) {
|
|
237
401
|
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
@@ -251,11 +415,19 @@ var _provider = (0, import_provider.defineProvider)({
|
|
|
251
415
|
return entries;
|
|
252
416
|
},
|
|
253
417
|
exists: async (sandbox, path, runCommand) => {
|
|
254
|
-
const
|
|
418
|
+
const diskPath = mapFilesystemPath(path);
|
|
419
|
+
const result = await runCommand(sandbox, `test -e ${shellEscape(diskPath)}`);
|
|
255
420
|
return result.exitCode === 0;
|
|
256
421
|
},
|
|
257
422
|
remove: async (sandbox, path, runCommand) => {
|
|
258
|
-
const
|
|
423
|
+
const diskPath = mapFilesystemPath(path);
|
|
424
|
+
if (diskPath === ARCHIL_MOUNT_ROOT) {
|
|
425
|
+
throw new Error("Refusing to remove the Archil disk mount root.");
|
|
426
|
+
}
|
|
427
|
+
const result = await runCommand(
|
|
428
|
+
sandbox,
|
|
429
|
+
withDiskWriteLock(`rm -rf ${shellEscape(diskPath)}`)
|
|
430
|
+
);
|
|
259
431
|
if (result.exitCode !== 0) {
|
|
260
432
|
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
261
433
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" resolves a handle to an\n * existing disk id.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CommandResult,\n SandboxInfo,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\ninterface ArchilCreateOptions extends CreateSandboxOptions {\n diskId?: string;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\nfunction resolveCreateDiskId(options?: ArchilCreateOptions): string {\n const diskId = options?.diskId;\n\n if (!diskId) {\n throw new Error(\n 'Archil create() requires an existing disk id on the top-level options.\\n\\n' +\n 'Example:\\n' +\n ' provider.sandbox.create({ diskId: \"disk_abc123\" })',\n );\n }\n\n return diskId;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: ArchilCreateOptions) => {\n const resolved = resolveConfig(config);\n const diskId = resolveCreateDiskId(options);\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(diskId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (_config: ArchilConfig, _sandboxId: string) => {\n // No-op: Archil disks have an independent lifecycle.\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n status: sandbox.disk.status === 'ready' ? 'running' : 'stopped',\n createdAt: new Date(sandbox.disk.createdAt),\n timeout: 0,\n metadata: {\n name: sandbox.disk.name,\n organization: sandbox.disk.organization,\n region: sandbox.disk.region,\n provider: sandbox.disk.provider,\n },\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n return result.stdout;\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const parent = path.substring(0, path.lastIndexOf('/'));\n if (parent) {\n await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);\n }\n // base64-pipe to avoid heredoc/quoting hazards on arbitrary content.\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const result = await runCommand(\n sandbox,\n `printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to write ${path}: ${result.stderr}`);\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,sBAA+B;AAU/B,IAAM,gBAAwC;AAAA,EAC5C,mBAAmB;AACrB;AAEA,SAAS,gBAAgB,QAAwB;AAC/C,QAAM,OAAO,OAAO,QAAQ,GAAG;AAC/B,MAAI,QAAQ,KAAK,SAAS,OAAO,SAAS,GAAG;AAC3C,UAAM,IAAI;AAAA,MACR,0BAA0B,MAAM;AAAA,IAClC;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,MAAM,GAAG,IAAI;AAClC,QAAM,SAAS,OAAO,MAAM,OAAO,CAAC;AACpC,QAAM,QAAQ,cAAc,MAAM,KAAK;AACvC,SAAO,mBAAmB,KAAK,IAAI,MAAM,IAAI,KAAK;AACpD;AAiDA,SAAS,cAAc,QAAsC;AAC3D,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAE5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,MAAI,UAAU,OAAO;AACrB,MAAI,CAAC,SAAS;AACZ,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MAIF;AAAA,IACF;AACA,cAAU,gBAAgB,MAAM;AAAA,EAClC;AAEA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;AAEA,SAAS,WAAW,QAAwB;AAC1C,SAAO,OAAO,OAAO,QAAQ,SAAS,EAAE,CAAC;AAC3C;AAEA,eAAe,QACb,UACA,QACA,MACA,MACY;AACZ,QAAM,WAAW,MAAM,MAAM,GAAG,SAAS,OAAO,GAAG,IAAI,IAAI;AAAA,IACzD;AAAA,IACA,SAAS;AAAA,MACP,eAAe,WAAW,SAAS,MAAM;AAAA,MACzC,GAAI,OAAO,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,IACvD;AAAA,IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EACtC,CAAC;AAGD,MAAI,UAA2B;AAC/B,MAAI;AACF,cAAW,MAAM,SAAS,KAAK;AAAA,EACjC,QAAQ;AACN,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,SAAS,MAAM,CAAC,WAAW,QAAQ,YAAY,OAAO;AACzD,UAAM,UACH,WAAW,QAAQ,SACpB,cAAc,MAAM,IAAI,IAAI,uBAAuB,SAAS,MAAM;AACpE,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AAEA,SAAO,QAAQ;AACjB;AAEA,SAAS,oBAAoB,SAAuC;AAClE,QAAM,SAAS,SAAS;AAExB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,OAAuB;AAC1C,SAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,YAAY,SAAiB,SAAqC;AACzE,MAAI,UAAU;AAEd,QAAM,aAAa,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC;AACpD,MAAI,CAAC,WAAW,KAAK,CAAC,CAAC,GAAG,MAAM,QAAQ,MAAM,GAAG;AAC/C,eAAW,QAAQ,CAAC,QAAQ,QAAQ,IAAI,QAAQ,MAAM,CAAC;AAAA,EACzD;AACA,QAAM,YAAY,WACf,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,OAAO,CAAC,CAAC,CAAC,EAAE,EAChD,KAAK,GAAG;AACX,YAAU,GAAG,SAAS,IAAI,OAAO;AAEjC,MAAI,SAAS,KAAK;AAChB,cAAU,MAAM,YAAY,QAAQ,GAAG,CAAC,OAAO,OAAO;AAAA,EACxD;AAEA,MAAI,SAAS,YAAY;AACvB,cAAU,eAAe,YAAY,OAAO,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;AAEA,eAAe,WAAW,SAAwB,SAAwC;AACxF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,cAAc,mBAAmB,QAAQ,KAAK,EAAE,CAAC;AAAA,IACjD,EAAE,QAAQ;AAAA,EACZ;AACF;AAEA,IAAM,gBAAY,gCAA4C;AAAA,EAC5D,MAAM;AAAA,EACN,SAAS;AAAA,IACP,SAAS;AAAA,MACP,QAAQ,OAAO,QAAsB,YAAkC;AACrE,cAAM,WAAW,cAAc,MAAM;AACrC,cAAM,SAAS,oBAAoB,OAAO;AAC1C,cAAM,OAAO,MAAM;AAAA,UACjB;AAAA,UACA;AAAA,UACA,cAAc,mBAAmB,MAAM,CAAC;AAAA,QAC1C;AACA,eAAO;AAAA,UACL,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,UACjD,WAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,QAAsB,cAAsB;AAC1D,cAAM,WAAW,cAAc,MAAM;AACrC,YAAI;AACF,gBAAM,OAAO,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA,cAAc,mBAAmB,SAAS,CAAC;AAAA,UAC7C;AACA,iBAAO;AAAA,YACL,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,YACjD,WAAW,KAAK;AAAA,UAClB;AAAA,QACF,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,OAAO,WAAyB;AACpC,cAAM,WAAW,cAAc,MAAM;AACrC,cAAM,QAAQ,MAAM,QAAwB,UAAU,OAAO,YAAY;AACzE,eAAO,MAAM,IAAI,CAAC,UAAU;AAAA,UAC1B,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,UACjD,WAAW,KAAK;AAAA,QAClB,EAAE;AAAA,MACJ;AAAA,MAEA,SAAS,OAAO,SAAuB,eAAuB;AAAA,MAE9D;AAAA,MAEA,YAAY,OACV,SACA,SACA,YAC2B;AAC3B,cAAM,YAAY,KAAK,IAAI;AAC3B,YAAI;AACF,gBAAM,SAAS,MAAM,WAAW,SAAS,YAAY,SAAS,OAAO,CAAC;AACtE,iBAAO;AAAA,YACL,QAAQ,OAAO,UAAU;AAAA,YACzB,QAAQ,OAAO,UAAU;AAAA,YACzB,UAAU,OAAO;AAAA,YACjB,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC7D,UAAU;AAAA,YACV,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,YAAiD;AAC/D,eAAO;AAAA,UACL,IAAI,QAAQ,KAAK;AAAA,UACjB,UAAU;AAAA,UACV,QAAQ,QAAQ,KAAK,WAAW,UAAU,YAAY;AAAA,UACtD,WAAW,IAAI,KAAK,QAAQ,KAAK,SAAS;AAAA,UAC1C,SAAS;AAAA,UACT,UAAU;AAAA,YACR,MAAM,QAAQ,KAAK;AAAA,YACnB,cAAc,QAAQ,KAAK;AAAA,YAC3B,QAAQ,QAAQ,KAAK;AAAA,YACrB,UAAU,QAAQ,KAAK;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAAA,MAEA,QAAQ,OACN,UACA,YACoB;AACpB,cAAM,IAAI;AAAA,UACR,sJACsD,QAAQ,IAAI;AAAA,QAEpE;AAAA,MACF;AAAA,MAEA,YAAY;AAAA,QACV,UAAU,OAAO,SAAS,MAAM,eAAe;AAC7C,gBAAM,SAAS,MAAM,WAAW,SAAS,OAAO,YAAY,IAAI,CAAC,EAAE;AACnE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,kBAAkB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC5D;AACA,iBAAO,OAAO;AAAA,QAChB;AAAA,QAEA,WAAW,OAAO,SAAS,MAAM,SAAS,eAAe;AACvD,gBAAM,SAAS,KAAK,UAAU,GAAG,KAAK,YAAY,GAAG,CAAC;AACtD,cAAI,QAAQ;AACV,kBAAM,WAAW,SAAS,YAAY,YAAY,MAAM,CAAC,EAAE;AAAA,UAC7D;AAEA,gBAAM,UAAU,OAAO,KAAK,SAAS,MAAM,EAAE,SAAS,QAAQ;AAC9D,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA,aAAa,YAAY,OAAO,CAAC,kBAAkB,YAAY,IAAI,CAAC;AAAA,UACtE;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,mBAAmB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC7D;AAAA,QACF;AAAA,QAEA,OAAO,OAAO,SAAS,MAAM,eAAe;AAC1C,gBAAM,SAAS,MAAM,WAAW,SAAS,YAAY,YAAY,IAAI,CAAC,EAAE;AACxE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,8BAA8B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACxE;AAAA,QACF;AAAA,QAEA,SAAS,OAAO,SAAS,MAAM,eAAe;AAE5C,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA,QAAQ,YAAY,IAAI,CAAC;AAAA,UAC3B;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,4BAA4B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACtE;AACA,gBAAM,UAAuB,CAAC;AAC9B,qBAAW,QAAQ,OAAO,OAAO,MAAM,IAAI,GAAG;AAC5C,gBAAI,CAAC,KAAM;AACX,kBAAM,CAAC,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,KAAK,MAAM,GAAI;AACnE,kBAAM,OAAO,UAAU,KAAK,GAAI;AAChC,oBAAQ,KAAK;AAAA,cACX;AAAA,cACA,MAAM,aAAa,MAAM,cAAc;AAAA,cACvC,MAAM,SAAS,SAAS,EAAE,KAAK;AAAA,cAC/B,UAAU,IAAI,KAAK,WAAW,QAAQ,IAAI,GAAI;AAAA,YAChD,CAAC;AAAA,UACH;AACA,iBAAO;AAAA,QACT;AAAA,QAEA,QAAQ,OAAO,SAAS,MAAM,eAAe;AAC3C,gBAAM,SAAS,MAAM,WAAW,SAAS,WAAW,YAAY,IAAI,CAAC,EAAE;AACvE,iBAAO,OAAO,aAAa;AAAA,QAC7B;AAAA,QAEA,QAAQ,OAAO,SAAS,MAAM,eAAe;AAC3C,gBAAM,SAAS,MAAM,WAAW,SAAS,UAAU,YAAY,IAAI,CAAC,EAAE;AACtE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,oBAAoB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,MAEA,aAAa,CAAC,YAA0C;AAAA,IAC1D;AAAA,EACF;AACF,CAAC;AAEM,IAAM,SAAS,CAAC,SAAuB,CAAC,MAAM,UAAU,MAAM;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" resolves a handle to an\n * existing disk id.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n SandboxInfo,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\nconst ARCHIL_MOUNT_ROOT = '/mnt/archil';\nconst ARCHIL_MAX_EXEC_COMMAND_BYTES = 102_400;\n// Base64 expands the file data and adds line wrapping. Keep the raw chunk\n// comfortably below Archil's roughly 4 MiB response cap.\nconst ARCHIL_MAX_READ_CHUNK_BYTES = 2 * 1024 * 1024;\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ntype DiskHandle = Pick<DiskResponse, 'id'>;\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskHandle | DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\ninterface ArchilCreateOptions extends CreateSandboxOptions {\n diskId?: string;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\nfunction resolveCreateDiskId(options?: ArchilCreateOptions): string {\n const diskId = options?.diskId;\n\n if (!diskId) {\n throw new Error(\n 'Archil create() requires an existing disk id on the top-level options.\\n\\n' +\n 'Example:\\n' +\n ' provider.sandbox.create({ diskId: \"disk_abc123\" })',\n );\n }\n\n return diskId;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction mapFilesystemPath(path: string): string {\n const normalized = posix.normalize(path.startsWith('/') ? path : `/${path}`);\n\n if (normalized === '/') {\n return ARCHIL_MOUNT_ROOT;\n }\n\n if (\n normalized === ARCHIL_MOUNT_ROOT ||\n normalized.startsWith(`${ARCHIL_MOUNT_ROOT}/`)\n ) {\n return normalized;\n }\n\n return `${ARCHIL_MOUNT_ROOT}${normalized}`;\n}\n\nfunction withDiskWriteLock(command: string): string {\n const mountRoot = shellEscape(ARCHIL_MOUNT_ROOT);\n return [\n `archil checkout --force --yes ${mountRoot}`,\n `{ ${command}; status=$?; archil checkin ${mountRoot}; checkin_status=$?; ` +\n `if [ $status -ne 0 ]; then exit $status; fi; exit $checkin_status; }`,\n ].join(' && ');\n}\n\nfunction execCommandBytes(command: string): number {\n return Buffer.byteLength(wrapCommand(command), 'utf8');\n}\n\nfunction buildWriteChunkCommand(\n parent: string,\n tempPath: string,\n diskPath: string,\n encodedChunk: string,\n isFirst: boolean,\n isFinal: boolean,\n): string {\n const writeCommand =\n `printf %s ${shellEscape(encodedChunk)} | base64 -d ` +\n `${isFirst ? '>' : '>>'} ${shellEscape(tempPath)}`;\n return [\n ...(isFirst ? [`mkdir -p ${shellEscape(parent)}`] : []),\n writeCommand,\n ...(isFinal\n ? [finalizeStagedFileCommand(tempPath, diskPath)]\n : []),\n ].join(' && ');\n}\n\nfunction finalizeStagedFileCommand(tempPath: string, diskPath: string): string {\n const quotedDestination = shellEscape(diskPath);\n return [\n `if [ -d ${quotedDestination} ]; then`,\n `printf '%s\\\\n' ${shellEscape(`Refusing to overwrite directory ${diskPath}`)} >&2;`,\n 'exit 1;',\n 'fi;',\n `mv ${shellEscape(tempPath)} ${quotedDestination}`,\n ].join(' ');\n}\n\nfunction maxWriteChunkSize(\n parent: string,\n tempPath: string,\n diskPath: string,\n): number {\n let low = 0;\n let high = ARCHIL_MAX_EXEC_COMMAND_BYTES;\n\n while (low < high) {\n const candidate = Math.ceil((low + high) / 2);\n const command = withDiskWriteLock(\n buildWriteChunkCommand(\n parent,\n tempPath,\n diskPath,\n 'A'.repeat(candidate),\n true,\n true,\n ),\n );\n\n if (execCommandBytes(command) <= ARCHIL_MAX_EXEC_COMMAND_BYTES) {\n low = candidate;\n } else {\n high = candidate - 1;\n }\n }\n\n const chunkSize = low - (low % 4);\n if (chunkSize < 4) {\n throw new Error(\n `Archil filesystem path is too long to write within the ${ARCHIL_MAX_EXEC_COMMAND_BYTES}-byte exec command limit.`,\n );\n }\n return chunkSize;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: ArchilCreateOptions) => {\n const resolved = resolveConfig(config);\n const diskId = resolveCreateDiskId(options);\n return {\n sandbox: { disk: { id: diskId }, resolved, createdAt: new Date() },\n sandboxId: diskId,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (_config: ArchilConfig, _sandboxId: string) => {\n // No-op: Archil disks have an independent lifecycle.\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n const diskInfo = 'status' in sandbox.disk ? sandbox.disk : undefined;\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n status: !diskInfo || diskInfo.status === 'ready' ? 'running' : 'stopped',\n createdAt: diskInfo ? new Date(diskInfo.createdAt) : sandbox.createdAt,\n timeout: 0,\n metadata: diskInfo\n ? {\n name: diskInfo.name,\n organization: diskInfo.organization,\n region: diskInfo.region,\n provider: diskInfo.provider,\n }\n : {},\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const sizeResult = await runCommand(\n sandbox,\n `wc -c < ${shellEscape(diskPath)}`,\n );\n if (sizeResult.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${sizeResult.stderr}`);\n }\n\n const sizeText = sizeResult.stdout.trim();\n if (!/^\\d+$/.test(sizeText)) {\n throw new Error(\n `Failed to read ${path}: Archil returned an invalid file size.`,\n );\n }\n\n const size = Number(sizeText);\n if (!Number.isSafeInteger(size)) {\n throw new Error(\n `Failed to read ${path}: file size exceeds JavaScript's safe integer range.`,\n );\n }\n if (size === 0) return '';\n\n const chunks: Buffer[] = [];\n for (\n let offset = 0;\n offset < size;\n offset += ARCHIL_MAX_READ_CHUNK_BYTES\n ) {\n const expectedBytes = Math.min(\n ARCHIL_MAX_READ_CHUNK_BYTES,\n size - offset,\n );\n const result = await runCommand(\n sandbox,\n `dd if=${shellEscape(diskPath)} bs=1 skip=${offset} count=${expectedBytes} 2>/dev/null | base64`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n\n const encoded = result.stdout.replace(/\\s/g, '');\n if (\n encoded.length === 0 ||\n encoded.length % 4 !== 0 ||\n !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)\n ) {\n throw new Error(\n `Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`,\n );\n }\n\n const chunk = Buffer.from(encoded, 'base64');\n if (chunk.length !== expectedBytes) {\n throw new Error(\n `Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`,\n );\n }\n chunks.push(chunk);\n }\n\n return Buffer.concat(chunks).toString('utf8');\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const parent = posix.dirname(diskPath);\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const tempPath = `${diskPath}.computesdk-write-${randomUUID()}`;\n let started = false;\n\n try {\n if (encoded.length === 0) {\n started = true;\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(\n [\n `mkdir -p ${shellEscape(parent)}`,\n `: > ${shellEscape(tempPath)}`,\n finalizeStagedFileCommand(tempPath, diskPath),\n ].join(' && '),\n ),\n );\n if (result.exitCode !== 0) {\n throw new Error(result.stderr);\n }\n return;\n }\n\n const chunkSize = maxWriteChunkSize(parent, tempPath, diskPath);\n for (let offset = 0; offset < encoded.length; offset += chunkSize) {\n const isFirst = offset === 0;\n const isFinal = offset + chunkSize >= encoded.length;\n started = true;\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(\n buildWriteChunkCommand(\n parent,\n tempPath,\n diskPath,\n encoded.slice(offset, offset + chunkSize),\n isFirst,\n isFinal,\n ),\n ),\n );\n if (result.exitCode !== 0) {\n throw new Error(result.stderr);\n }\n }\n } catch (error) {\n if (started) {\n try {\n await runCommand(\n sandbox,\n withDiskWriteLock(`rm -f ${shellEscape(tempPath)}`),\n );\n } catch {\n // Preserve the original write error if cleanup fails.\n }\n }\n throw new Error(\n `Failed to write ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(`mkdir -p ${shellEscape(diskPath)}`),\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(diskPath)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const result = await runCommand(sandbox, `test -e ${shellEscape(diskPath)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n if (diskPath === ARCHIL_MOUNT_ROOT) {\n throw new Error('Refusing to remove the Archil disk mount root.');\n }\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(`rm -rf ${shellEscape(diskPath)}`),\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { defineProvider } from "@computesdk/provider";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
import { posix } from "path";
|
|
5
|
+
var ARCHIL_MOUNT_ROOT = "/mnt/archil";
|
|
6
|
+
var ARCHIL_MAX_EXEC_COMMAND_BYTES = 102400;
|
|
7
|
+
var ARCHIL_MAX_READ_CHUNK_BYTES = 2 * 1024 * 1024;
|
|
3
8
|
var REGION_COLORS = {
|
|
4
9
|
"gcp-us-central1": "blue"
|
|
5
10
|
};
|
|
@@ -70,6 +75,73 @@ function resolveCreateDiskId(options) {
|
|
|
70
75
|
function shellEscape(value) {
|
|
71
76
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
72
77
|
}
|
|
78
|
+
function mapFilesystemPath(path) {
|
|
79
|
+
const normalized = posix.normalize(path.startsWith("/") ? path : `/${path}`);
|
|
80
|
+
if (normalized === "/") {
|
|
81
|
+
return ARCHIL_MOUNT_ROOT;
|
|
82
|
+
}
|
|
83
|
+
if (normalized === ARCHIL_MOUNT_ROOT || normalized.startsWith(`${ARCHIL_MOUNT_ROOT}/`)) {
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
return `${ARCHIL_MOUNT_ROOT}${normalized}`;
|
|
87
|
+
}
|
|
88
|
+
function withDiskWriteLock(command) {
|
|
89
|
+
const mountRoot = shellEscape(ARCHIL_MOUNT_ROOT);
|
|
90
|
+
return [
|
|
91
|
+
`archil checkout --force --yes ${mountRoot}`,
|
|
92
|
+
`{ ${command}; status=$?; archil checkin ${mountRoot}; checkin_status=$?; if [ $status -ne 0 ]; then exit $status; fi; exit $checkin_status; }`
|
|
93
|
+
].join(" && ");
|
|
94
|
+
}
|
|
95
|
+
function execCommandBytes(command) {
|
|
96
|
+
return Buffer.byteLength(wrapCommand(command), "utf8");
|
|
97
|
+
}
|
|
98
|
+
function buildWriteChunkCommand(parent, tempPath, diskPath, encodedChunk, isFirst, isFinal) {
|
|
99
|
+
const writeCommand = `printf %s ${shellEscape(encodedChunk)} | base64 -d ${isFirst ? ">" : ">>"} ${shellEscape(tempPath)}`;
|
|
100
|
+
return [
|
|
101
|
+
...isFirst ? [`mkdir -p ${shellEscape(parent)}`] : [],
|
|
102
|
+
writeCommand,
|
|
103
|
+
...isFinal ? [finalizeStagedFileCommand(tempPath, diskPath)] : []
|
|
104
|
+
].join(" && ");
|
|
105
|
+
}
|
|
106
|
+
function finalizeStagedFileCommand(tempPath, diskPath) {
|
|
107
|
+
const quotedDestination = shellEscape(diskPath);
|
|
108
|
+
return [
|
|
109
|
+
`if [ -d ${quotedDestination} ]; then`,
|
|
110
|
+
`printf '%s\\n' ${shellEscape(`Refusing to overwrite directory ${diskPath}`)} >&2;`,
|
|
111
|
+
"exit 1;",
|
|
112
|
+
"fi;",
|
|
113
|
+
`mv ${shellEscape(tempPath)} ${quotedDestination}`
|
|
114
|
+
].join(" ");
|
|
115
|
+
}
|
|
116
|
+
function maxWriteChunkSize(parent, tempPath, diskPath) {
|
|
117
|
+
let low = 0;
|
|
118
|
+
let high = ARCHIL_MAX_EXEC_COMMAND_BYTES;
|
|
119
|
+
while (low < high) {
|
|
120
|
+
const candidate = Math.ceil((low + high) / 2);
|
|
121
|
+
const command = withDiskWriteLock(
|
|
122
|
+
buildWriteChunkCommand(
|
|
123
|
+
parent,
|
|
124
|
+
tempPath,
|
|
125
|
+
diskPath,
|
|
126
|
+
"A".repeat(candidate),
|
|
127
|
+
true,
|
|
128
|
+
true
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
if (execCommandBytes(command) <= ARCHIL_MAX_EXEC_COMMAND_BYTES) {
|
|
132
|
+
low = candidate;
|
|
133
|
+
} else {
|
|
134
|
+
high = candidate - 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const chunkSize = low - low % 4;
|
|
138
|
+
if (chunkSize < 4) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Archil filesystem path is too long to write within the ${ARCHIL_MAX_EXEC_COMMAND_BYTES}-byte exec command limit.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return chunkSize;
|
|
144
|
+
}
|
|
73
145
|
function wrapCommand(command, options) {
|
|
74
146
|
let wrapped = command;
|
|
75
147
|
const envEntries = Object.entries(options?.env ?? {});
|
|
@@ -101,14 +173,9 @@ var _provider = defineProvider({
|
|
|
101
173
|
create: async (config, options) => {
|
|
102
174
|
const resolved = resolveConfig(config);
|
|
103
175
|
const diskId = resolveCreateDiskId(options);
|
|
104
|
-
const disk = await callApi(
|
|
105
|
-
resolved,
|
|
106
|
-
"GET",
|
|
107
|
-
`/api/disks/${encodeURIComponent(diskId)}`
|
|
108
|
-
);
|
|
109
176
|
return {
|
|
110
|
-
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
111
|
-
sandboxId:
|
|
177
|
+
sandbox: { disk: { id: diskId }, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
178
|
+
sandboxId: diskId
|
|
112
179
|
};
|
|
113
180
|
},
|
|
114
181
|
getById: async (config, sandboxId) => {
|
|
@@ -157,18 +224,19 @@ var _provider = defineProvider({
|
|
|
157
224
|
}
|
|
158
225
|
},
|
|
159
226
|
getInfo: async (sandbox) => {
|
|
227
|
+
const diskInfo = "status" in sandbox.disk ? sandbox.disk : void 0;
|
|
160
228
|
return {
|
|
161
229
|
id: sandbox.disk.id,
|
|
162
230
|
provider: "archil",
|
|
163
|
-
status:
|
|
164
|
-
createdAt: new Date(sandbox.
|
|
231
|
+
status: !diskInfo || diskInfo.status === "ready" ? "running" : "stopped",
|
|
232
|
+
createdAt: diskInfo ? new Date(diskInfo.createdAt) : sandbox.createdAt,
|
|
165
233
|
timeout: 0,
|
|
166
|
-
metadata: {
|
|
167
|
-
name:
|
|
168
|
-
organization:
|
|
169
|
-
region:
|
|
170
|
-
provider:
|
|
171
|
-
}
|
|
234
|
+
metadata: diskInfo ? {
|
|
235
|
+
name: diskInfo.name,
|
|
236
|
+
organization: diskInfo.organization,
|
|
237
|
+
region: diskInfo.region,
|
|
238
|
+
provider: diskInfo.provider
|
|
239
|
+
} : {}
|
|
172
240
|
};
|
|
173
241
|
},
|
|
174
242
|
getUrl: async (_sandbox, options) => {
|
|
@@ -178,36 +246,132 @@ var _provider = defineProvider({
|
|
|
178
246
|
},
|
|
179
247
|
filesystem: {
|
|
180
248
|
readFile: async (sandbox, path, runCommand) => {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
249
|
+
const diskPath = mapFilesystemPath(path);
|
|
250
|
+
const sizeResult = await runCommand(
|
|
251
|
+
sandbox,
|
|
252
|
+
`wc -c < ${shellEscape(diskPath)}`
|
|
253
|
+
);
|
|
254
|
+
if (sizeResult.exitCode !== 0) {
|
|
255
|
+
throw new Error(`Failed to read ${path}: ${sizeResult.stderr}`);
|
|
256
|
+
}
|
|
257
|
+
const sizeText = sizeResult.stdout.trim();
|
|
258
|
+
if (!/^\d+$/.test(sizeText)) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Failed to read ${path}: Archil returned an invalid file size.`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const size = Number(sizeText);
|
|
264
|
+
if (!Number.isSafeInteger(size)) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`Failed to read ${path}: file size exceeds JavaScript's safe integer range.`
|
|
267
|
+
);
|
|
184
268
|
}
|
|
185
|
-
return
|
|
269
|
+
if (size === 0) return "";
|
|
270
|
+
const chunks = [];
|
|
271
|
+
for (let offset = 0; offset < size; offset += ARCHIL_MAX_READ_CHUNK_BYTES) {
|
|
272
|
+
const expectedBytes = Math.min(
|
|
273
|
+
ARCHIL_MAX_READ_CHUNK_BYTES,
|
|
274
|
+
size - offset
|
|
275
|
+
);
|
|
276
|
+
const result = await runCommand(
|
|
277
|
+
sandbox,
|
|
278
|
+
`dd if=${shellEscape(diskPath)} bs=1 skip=${offset} count=${expectedBytes} 2>/dev/null | base64`
|
|
279
|
+
);
|
|
280
|
+
if (result.exitCode !== 0) {
|
|
281
|
+
throw new Error(`Failed to read ${path}: ${result.stderr}`);
|
|
282
|
+
}
|
|
283
|
+
const encoded = result.stdout.replace(/\s/g, "");
|
|
284
|
+
if (encoded.length === 0 || encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
const chunk = Buffer.from(encoded, "base64");
|
|
290
|
+
if (chunk.length !== expectedBytes) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
chunks.push(chunk);
|
|
296
|
+
}
|
|
297
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
186
298
|
},
|
|
187
299
|
writeFile: async (sandbox, path, content, runCommand) => {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);
|
|
191
|
-
}
|
|
300
|
+
const diskPath = mapFilesystemPath(path);
|
|
301
|
+
const parent = posix.dirname(diskPath);
|
|
192
302
|
const encoded = Buffer.from(content, "utf8").toString("base64");
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
303
|
+
const tempPath = `${diskPath}.computesdk-write-${randomUUID()}`;
|
|
304
|
+
let started = false;
|
|
305
|
+
try {
|
|
306
|
+
if (encoded.length === 0) {
|
|
307
|
+
started = true;
|
|
308
|
+
const result = await runCommand(
|
|
309
|
+
sandbox,
|
|
310
|
+
withDiskWriteLock(
|
|
311
|
+
[
|
|
312
|
+
`mkdir -p ${shellEscape(parent)}`,
|
|
313
|
+
`: > ${shellEscape(tempPath)}`,
|
|
314
|
+
finalizeStagedFileCommand(tempPath, diskPath)
|
|
315
|
+
].join(" && ")
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
if (result.exitCode !== 0) {
|
|
319
|
+
throw new Error(result.stderr);
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const chunkSize = maxWriteChunkSize(parent, tempPath, diskPath);
|
|
324
|
+
for (let offset = 0; offset < encoded.length; offset += chunkSize) {
|
|
325
|
+
const isFirst = offset === 0;
|
|
326
|
+
const isFinal = offset + chunkSize >= encoded.length;
|
|
327
|
+
started = true;
|
|
328
|
+
const result = await runCommand(
|
|
329
|
+
sandbox,
|
|
330
|
+
withDiskWriteLock(
|
|
331
|
+
buildWriteChunkCommand(
|
|
332
|
+
parent,
|
|
333
|
+
tempPath,
|
|
334
|
+
diskPath,
|
|
335
|
+
encoded.slice(offset, offset + chunkSize),
|
|
336
|
+
isFirst,
|
|
337
|
+
isFinal
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
);
|
|
341
|
+
if (result.exitCode !== 0) {
|
|
342
|
+
throw new Error(result.stderr);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (started) {
|
|
347
|
+
try {
|
|
348
|
+
await runCommand(
|
|
349
|
+
sandbox,
|
|
350
|
+
withDiskWriteLock(`rm -f ${shellEscape(tempPath)}`)
|
|
351
|
+
);
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
throw new Error(
|
|
356
|
+
`Failed to write ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
357
|
+
);
|
|
199
358
|
}
|
|
200
359
|
},
|
|
201
360
|
mkdir: async (sandbox, path, runCommand) => {
|
|
202
|
-
const
|
|
361
|
+
const diskPath = mapFilesystemPath(path);
|
|
362
|
+
const result = await runCommand(
|
|
363
|
+
sandbox,
|
|
364
|
+
withDiskWriteLock(`mkdir -p ${shellEscape(diskPath)}`)
|
|
365
|
+
);
|
|
203
366
|
if (result.exitCode !== 0) {
|
|
204
367
|
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
205
368
|
}
|
|
206
369
|
},
|
|
207
370
|
readdir: async (sandbox, path, runCommand) => {
|
|
371
|
+
const diskPath = mapFilesystemPath(path);
|
|
208
372
|
const result = await runCommand(
|
|
209
373
|
sandbox,
|
|
210
|
-
`find ${shellEscape(
|
|
374
|
+
`find ${shellEscape(diskPath)} -mindepth 1 -maxdepth 1 -printf '%y\\t%s\\t%T@\\t%f\\n'`
|
|
211
375
|
);
|
|
212
376
|
if (result.exitCode !== 0) {
|
|
213
377
|
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
@@ -227,11 +391,19 @@ var _provider = defineProvider({
|
|
|
227
391
|
return entries;
|
|
228
392
|
},
|
|
229
393
|
exists: async (sandbox, path, runCommand) => {
|
|
230
|
-
const
|
|
394
|
+
const diskPath = mapFilesystemPath(path);
|
|
395
|
+
const result = await runCommand(sandbox, `test -e ${shellEscape(diskPath)}`);
|
|
231
396
|
return result.exitCode === 0;
|
|
232
397
|
},
|
|
233
398
|
remove: async (sandbox, path, runCommand) => {
|
|
234
|
-
const
|
|
399
|
+
const diskPath = mapFilesystemPath(path);
|
|
400
|
+
if (diskPath === ARCHIL_MOUNT_ROOT) {
|
|
401
|
+
throw new Error("Refusing to remove the Archil disk mount root.");
|
|
402
|
+
}
|
|
403
|
+
const result = await runCommand(
|
|
404
|
+
sandbox,
|
|
405
|
+
withDiskWriteLock(`rm -rf ${shellEscape(diskPath)}`)
|
|
406
|
+
);
|
|
235
407
|
if (result.exitCode !== 0) {
|
|
236
408
|
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
237
409
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" resolves a handle to an\n * existing disk id.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CommandResult,\n SandboxInfo,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\ninterface ArchilCreateOptions extends CreateSandboxOptions {\n diskId?: string;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\nfunction resolveCreateDiskId(options?: ArchilCreateOptions): string {\n const diskId = options?.diskId;\n\n if (!diskId) {\n throw new Error(\n 'Archil create() requires an existing disk id on the top-level options.\\n\\n' +\n 'Example:\\n' +\n ' provider.sandbox.create({ diskId: \"disk_abc123\" })',\n );\n }\n\n return diskId;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: ArchilCreateOptions) => {\n const resolved = resolveConfig(config);\n const diskId = resolveCreateDiskId(options);\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(diskId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (_config: ArchilConfig, _sandboxId: string) => {\n // No-op: Archil disks have an independent lifecycle.\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n status: sandbox.disk.status === 'ready' ? 'running' : 'stopped',\n createdAt: new Date(sandbox.disk.createdAt),\n timeout: 0,\n metadata: {\n name: sandbox.disk.name,\n organization: sandbox.disk.organization,\n region: sandbox.disk.region,\n provider: sandbox.disk.provider,\n },\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n return result.stdout;\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const parent = path.substring(0, path.lastIndexOf('/'));\n if (parent) {\n await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);\n }\n // base64-pipe to avoid heredoc/quoting hazards on arbitrary content.\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const result = await runCommand(\n sandbox,\n `printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to write ${path}: ${result.stderr}`);\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":";AAUA,SAAS,sBAAsB;AAU/B,IAAM,gBAAwC;AAAA,EAC5C,mBAAmB;AACrB;AAEA,SAAS,gBAAgB,QAAwB;AAC/C,QAAM,OAAO,OAAO,QAAQ,GAAG;AAC/B,MAAI,QAAQ,KAAK,SAAS,OAAO,SAAS,GAAG;AAC3C,UAAM,IAAI;AAAA,MACR,0BAA0B,MAAM;AAAA,IAClC;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,MAAM,GAAG,IAAI;AAClC,QAAM,SAAS,OAAO,MAAM,OAAO,CAAC;AACpC,QAAM,QAAQ,cAAc,MAAM,KAAK;AACvC,SAAO,mBAAmB,KAAK,IAAI,MAAM,IAAI,KAAK;AACpD;AAiDA,SAAS,cAAc,QAAsC;AAC3D,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAE5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,MAAI,UAAU,OAAO;AACrB,MAAI,CAAC,SAAS;AACZ,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MAIF;AAAA,IACF;AACA,cAAU,gBAAgB,MAAM;AAAA,EAClC;AAEA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;AAEA,SAAS,WAAW,QAAwB;AAC1C,SAAO,OAAO,OAAO,QAAQ,SAAS,EAAE,CAAC;AAC3C;AAEA,eAAe,QACb,UACA,QACA,MACA,MACY;AACZ,QAAM,WAAW,MAAM,MAAM,GAAG,SAAS,OAAO,GAAG,IAAI,IAAI;AAAA,IACzD;AAAA,IACA,SAAS;AAAA,MACP,eAAe,WAAW,SAAS,MAAM;AAAA,MACzC,GAAI,OAAO,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,IACvD;AAAA,IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EACtC,CAAC;AAGD,MAAI,UAA2B;AAC/B,MAAI;AACF,cAAW,MAAM,SAAS,KAAK;AAAA,EACjC,QAAQ;AACN,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,SAAS,MAAM,CAAC,WAAW,QAAQ,YAAY,OAAO;AACzD,UAAM,UACH,WAAW,QAAQ,SACpB,cAAc,MAAM,IAAI,IAAI,uBAAuB,SAAS,MAAM;AACpE,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AAEA,SAAO,QAAQ;AACjB;AAEA,SAAS,oBAAoB,SAAuC;AAClE,QAAM,SAAS,SAAS;AAExB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,OAAuB;AAC1C,SAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,YAAY,SAAiB,SAAqC;AACzE,MAAI,UAAU;AAEd,QAAM,aAAa,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC;AACpD,MAAI,CAAC,WAAW,KAAK,CAAC,CAAC,GAAG,MAAM,QAAQ,MAAM,GAAG;AAC/C,eAAW,QAAQ,CAAC,QAAQ,QAAQ,IAAI,QAAQ,MAAM,CAAC;AAAA,EACzD;AACA,QAAM,YAAY,WACf,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,OAAO,CAAC,CAAC,CAAC,EAAE,EAChD,KAAK,GAAG;AACX,YAAU,GAAG,SAAS,IAAI,OAAO;AAEjC,MAAI,SAAS,KAAK;AAChB,cAAU,MAAM,YAAY,QAAQ,GAAG,CAAC,OAAO,OAAO;AAAA,EACxD;AAEA,MAAI,SAAS,YAAY;AACvB,cAAU,eAAe,YAAY,OAAO,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;AAEA,eAAe,WAAW,SAAwB,SAAwC;AACxF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,cAAc,mBAAmB,QAAQ,KAAK,EAAE,CAAC;AAAA,IACjD,EAAE,QAAQ;AAAA,EACZ;AACF;AAEA,IAAM,YAAY,eAA4C;AAAA,EAC5D,MAAM;AAAA,EACN,SAAS;AAAA,IACP,SAAS;AAAA,MACP,QAAQ,OAAO,QAAsB,YAAkC;AACrE,cAAM,WAAW,cAAc,MAAM;AACrC,cAAM,SAAS,oBAAoB,OAAO;AAC1C,cAAM,OAAO,MAAM;AAAA,UACjB;AAAA,UACA;AAAA,UACA,cAAc,mBAAmB,MAAM,CAAC;AAAA,QAC1C;AACA,eAAO;AAAA,UACL,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,UACjD,WAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,QAAsB,cAAsB;AAC1D,cAAM,WAAW,cAAc,MAAM;AACrC,YAAI;AACF,gBAAM,OAAO,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA,cAAc,mBAAmB,SAAS,CAAC;AAAA,UAC7C;AACA,iBAAO;AAAA,YACL,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,YACjD,WAAW,KAAK;AAAA,UAClB;AAAA,QACF,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,OAAO,WAAyB;AACpC,cAAM,WAAW,cAAc,MAAM;AACrC,cAAM,QAAQ,MAAM,QAAwB,UAAU,OAAO,YAAY;AACzE,eAAO,MAAM,IAAI,CAAC,UAAU;AAAA,UAC1B,SAAS,EAAE,MAAM,UAAU,WAAW,oBAAI,KAAK,EAAE;AAAA,UACjD,WAAW,KAAK;AAAA,QAClB,EAAE;AAAA,MACJ;AAAA,MAEA,SAAS,OAAO,SAAuB,eAAuB;AAAA,MAE9D;AAAA,MAEA,YAAY,OACV,SACA,SACA,YAC2B;AAC3B,cAAM,YAAY,KAAK,IAAI;AAC3B,YAAI;AACF,gBAAM,SAAS,MAAM,WAAW,SAAS,YAAY,SAAS,OAAO,CAAC;AACtE,iBAAO;AAAA,YACL,QAAQ,OAAO,UAAU;AAAA,YACzB,QAAQ,OAAO,UAAU;AAAA,YACzB,UAAU,OAAO;AAAA,YACjB,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC7D,UAAU;AAAA,YACV,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,YAAiD;AAC/D,eAAO;AAAA,UACL,IAAI,QAAQ,KAAK;AAAA,UACjB,UAAU;AAAA,UACV,QAAQ,QAAQ,KAAK,WAAW,UAAU,YAAY;AAAA,UACtD,WAAW,IAAI,KAAK,QAAQ,KAAK,SAAS;AAAA,UAC1C,SAAS;AAAA,UACT,UAAU;AAAA,YACR,MAAM,QAAQ,KAAK;AAAA,YACnB,cAAc,QAAQ,KAAK;AAAA,YAC3B,QAAQ,QAAQ,KAAK;AAAA,YACrB,UAAU,QAAQ,KAAK;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAAA,MAEA,QAAQ,OACN,UACA,YACoB;AACpB,cAAM,IAAI;AAAA,UACR,sJACsD,QAAQ,IAAI;AAAA,QAEpE;AAAA,MACF;AAAA,MAEA,YAAY;AAAA,QACV,UAAU,OAAO,SAAS,MAAM,eAAe;AAC7C,gBAAM,SAAS,MAAM,WAAW,SAAS,OAAO,YAAY,IAAI,CAAC,EAAE;AACnE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,kBAAkB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC5D;AACA,iBAAO,OAAO;AAAA,QAChB;AAAA,QAEA,WAAW,OAAO,SAAS,MAAM,SAAS,eAAe;AACvD,gBAAM,SAAS,KAAK,UAAU,GAAG,KAAK,YAAY,GAAG,CAAC;AACtD,cAAI,QAAQ;AACV,kBAAM,WAAW,SAAS,YAAY,YAAY,MAAM,CAAC,EAAE;AAAA,UAC7D;AAEA,gBAAM,UAAU,OAAO,KAAK,SAAS,MAAM,EAAE,SAAS,QAAQ;AAC9D,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA,aAAa,YAAY,OAAO,CAAC,kBAAkB,YAAY,IAAI,CAAC;AAAA,UACtE;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,mBAAmB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC7D;AAAA,QACF;AAAA,QAEA,OAAO,OAAO,SAAS,MAAM,eAAe;AAC1C,gBAAM,SAAS,MAAM,WAAW,SAAS,YAAY,YAAY,IAAI,CAAC,EAAE;AACxE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,8BAA8B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACxE;AAAA,QACF;AAAA,QAEA,SAAS,OAAO,SAAS,MAAM,eAAe;AAE5C,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA,QAAQ,YAAY,IAAI,CAAC;AAAA,UAC3B;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,4BAA4B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACtE;AACA,gBAAM,UAAuB,CAAC;AAC9B,qBAAW,QAAQ,OAAO,OAAO,MAAM,IAAI,GAAG;AAC5C,gBAAI,CAAC,KAAM;AACX,kBAAM,CAAC,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,KAAK,MAAM,GAAI;AACnE,kBAAM,OAAO,UAAU,KAAK,GAAI;AAChC,oBAAQ,KAAK;AAAA,cACX;AAAA,cACA,MAAM,aAAa,MAAM,cAAc;AAAA,cACvC,MAAM,SAAS,SAAS,EAAE,KAAK;AAAA,cAC/B,UAAU,IAAI,KAAK,WAAW,QAAQ,IAAI,GAAI;AAAA,YAChD,CAAC;AAAA,UACH;AACA,iBAAO;AAAA,QACT;AAAA,QAEA,QAAQ,OAAO,SAAS,MAAM,eAAe;AAC3C,gBAAM,SAAS,MAAM,WAAW,SAAS,WAAW,YAAY,IAAI,CAAC,EAAE;AACvE,iBAAO,OAAO,aAAa;AAAA,QAC7B;AAAA,QAEA,QAAQ,OAAO,SAAS,MAAM,eAAe;AAC3C,gBAAM,SAAS,MAAM,WAAW,SAAS,UAAU,YAAY,IAAI,CAAC,EAAE;AACtE,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,oBAAoB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,MAEA,aAAa,CAAC,YAA0C;AAAA,IAC1D;AAAA,EACF;AACF,CAAC;AAEM,IAAM,SAAS,CAAC,SAAuB,CAAC,MAAM,UAAU,MAAM;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" resolves a handle to an\n * existing disk id.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n SandboxInfo,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\nconst ARCHIL_MOUNT_ROOT = '/mnt/archil';\nconst ARCHIL_MAX_EXEC_COMMAND_BYTES = 102_400;\n// Base64 expands the file data and adds line wrapping. Keep the raw chunk\n// comfortably below Archil's roughly 4 MiB response cap.\nconst ARCHIL_MAX_READ_CHUNK_BYTES = 2 * 1024 * 1024;\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ntype DiskHandle = Pick<DiskResponse, 'id'>;\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskHandle | DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\ninterface ArchilCreateOptions extends CreateSandboxOptions {\n diskId?: string;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\nfunction resolveCreateDiskId(options?: ArchilCreateOptions): string {\n const diskId = options?.diskId;\n\n if (!diskId) {\n throw new Error(\n 'Archil create() requires an existing disk id on the top-level options.\\n\\n' +\n 'Example:\\n' +\n ' provider.sandbox.create({ diskId: \"disk_abc123\" })',\n );\n }\n\n return diskId;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction mapFilesystemPath(path: string): string {\n const normalized = posix.normalize(path.startsWith('/') ? path : `/${path}`);\n\n if (normalized === '/') {\n return ARCHIL_MOUNT_ROOT;\n }\n\n if (\n normalized === ARCHIL_MOUNT_ROOT ||\n normalized.startsWith(`${ARCHIL_MOUNT_ROOT}/`)\n ) {\n return normalized;\n }\n\n return `${ARCHIL_MOUNT_ROOT}${normalized}`;\n}\n\nfunction withDiskWriteLock(command: string): string {\n const mountRoot = shellEscape(ARCHIL_MOUNT_ROOT);\n return [\n `archil checkout --force --yes ${mountRoot}`,\n `{ ${command}; status=$?; archil checkin ${mountRoot}; checkin_status=$?; ` +\n `if [ $status -ne 0 ]; then exit $status; fi; exit $checkin_status; }`,\n ].join(' && ');\n}\n\nfunction execCommandBytes(command: string): number {\n return Buffer.byteLength(wrapCommand(command), 'utf8');\n}\n\nfunction buildWriteChunkCommand(\n parent: string,\n tempPath: string,\n diskPath: string,\n encodedChunk: string,\n isFirst: boolean,\n isFinal: boolean,\n): string {\n const writeCommand =\n `printf %s ${shellEscape(encodedChunk)} | base64 -d ` +\n `${isFirst ? '>' : '>>'} ${shellEscape(tempPath)}`;\n return [\n ...(isFirst ? [`mkdir -p ${shellEscape(parent)}`] : []),\n writeCommand,\n ...(isFinal\n ? [finalizeStagedFileCommand(tempPath, diskPath)]\n : []),\n ].join(' && ');\n}\n\nfunction finalizeStagedFileCommand(tempPath: string, diskPath: string): string {\n const quotedDestination = shellEscape(diskPath);\n return [\n `if [ -d ${quotedDestination} ]; then`,\n `printf '%s\\\\n' ${shellEscape(`Refusing to overwrite directory ${diskPath}`)} >&2;`,\n 'exit 1;',\n 'fi;',\n `mv ${shellEscape(tempPath)} ${quotedDestination}`,\n ].join(' ');\n}\n\nfunction maxWriteChunkSize(\n parent: string,\n tempPath: string,\n diskPath: string,\n): number {\n let low = 0;\n let high = ARCHIL_MAX_EXEC_COMMAND_BYTES;\n\n while (low < high) {\n const candidate = Math.ceil((low + high) / 2);\n const command = withDiskWriteLock(\n buildWriteChunkCommand(\n parent,\n tempPath,\n diskPath,\n 'A'.repeat(candidate),\n true,\n true,\n ),\n );\n\n if (execCommandBytes(command) <= ARCHIL_MAX_EXEC_COMMAND_BYTES) {\n low = candidate;\n } else {\n high = candidate - 1;\n }\n }\n\n const chunkSize = low - (low % 4);\n if (chunkSize < 4) {\n throw new Error(\n `Archil filesystem path is too long to write within the ${ARCHIL_MAX_EXEC_COMMAND_BYTES}-byte exec command limit.`,\n );\n }\n return chunkSize;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: ArchilCreateOptions) => {\n const resolved = resolveConfig(config);\n const diskId = resolveCreateDiskId(options);\n return {\n sandbox: { disk: { id: diskId }, resolved, createdAt: new Date() },\n sandboxId: diskId,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (_config: ArchilConfig, _sandboxId: string) => {\n // No-op: Archil disks have an independent lifecycle.\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n const diskInfo = 'status' in sandbox.disk ? sandbox.disk : undefined;\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n status: !diskInfo || diskInfo.status === 'ready' ? 'running' : 'stopped',\n createdAt: diskInfo ? new Date(diskInfo.createdAt) : sandbox.createdAt,\n timeout: 0,\n metadata: diskInfo\n ? {\n name: diskInfo.name,\n organization: diskInfo.organization,\n region: diskInfo.region,\n provider: diskInfo.provider,\n }\n : {},\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const sizeResult = await runCommand(\n sandbox,\n `wc -c < ${shellEscape(diskPath)}`,\n );\n if (sizeResult.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${sizeResult.stderr}`);\n }\n\n const sizeText = sizeResult.stdout.trim();\n if (!/^\\d+$/.test(sizeText)) {\n throw new Error(\n `Failed to read ${path}: Archil returned an invalid file size.`,\n );\n }\n\n const size = Number(sizeText);\n if (!Number.isSafeInteger(size)) {\n throw new Error(\n `Failed to read ${path}: file size exceeds JavaScript's safe integer range.`,\n );\n }\n if (size === 0) return '';\n\n const chunks: Buffer[] = [];\n for (\n let offset = 0;\n offset < size;\n offset += ARCHIL_MAX_READ_CHUNK_BYTES\n ) {\n const expectedBytes = Math.min(\n ARCHIL_MAX_READ_CHUNK_BYTES,\n size - offset,\n );\n const result = await runCommand(\n sandbox,\n `dd if=${shellEscape(diskPath)} bs=1 skip=${offset} count=${expectedBytes} 2>/dev/null | base64`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n\n const encoded = result.stdout.replace(/\\s/g, '');\n if (\n encoded.length === 0 ||\n encoded.length % 4 !== 0 ||\n !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)\n ) {\n throw new Error(\n `Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`,\n );\n }\n\n const chunk = Buffer.from(encoded, 'base64');\n if (chunk.length !== expectedBytes) {\n throw new Error(\n `Failed to read ${path}: Archil returned an incomplete file chunk at byte ${offset}.`,\n );\n }\n chunks.push(chunk);\n }\n\n return Buffer.concat(chunks).toString('utf8');\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const parent = posix.dirname(diskPath);\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const tempPath = `${diskPath}.computesdk-write-${randomUUID()}`;\n let started = false;\n\n try {\n if (encoded.length === 0) {\n started = true;\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(\n [\n `mkdir -p ${shellEscape(parent)}`,\n `: > ${shellEscape(tempPath)}`,\n finalizeStagedFileCommand(tempPath, diskPath),\n ].join(' && '),\n ),\n );\n if (result.exitCode !== 0) {\n throw new Error(result.stderr);\n }\n return;\n }\n\n const chunkSize = maxWriteChunkSize(parent, tempPath, diskPath);\n for (let offset = 0; offset < encoded.length; offset += chunkSize) {\n const isFirst = offset === 0;\n const isFinal = offset + chunkSize >= encoded.length;\n started = true;\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(\n buildWriteChunkCommand(\n parent,\n tempPath,\n diskPath,\n encoded.slice(offset, offset + chunkSize),\n isFirst,\n isFinal,\n ),\n ),\n );\n if (result.exitCode !== 0) {\n throw new Error(result.stderr);\n }\n }\n } catch (error) {\n if (started) {\n try {\n await runCommand(\n sandbox,\n withDiskWriteLock(`rm -f ${shellEscape(tempPath)}`),\n );\n } catch {\n // Preserve the original write error if cleanup fails.\n }\n }\n throw new Error(\n `Failed to write ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(`mkdir -p ${shellEscape(diskPath)}`),\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(diskPath)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n const result = await runCommand(sandbox, `test -e ${shellEscape(diskPath)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const diskPath = mapFilesystemPath(path);\n if (diskPath === ARCHIL_MOUNT_ROOT) {\n throw new Error('Refusing to remove the Archil disk mount root.');\n }\n const result = await runCommand(\n sandbox,\n withDiskWriteLock(`rm -rf ${shellEscape(diskPath)}`),\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":"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","names":[]}
|