@deepagents/context 4.0.0 → 4.1.0
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 +19 -14
- package/dist/browser.js +18 -8
- package/dist/browser.js.map +2 -2
- package/dist/index.js +2115 -1565
- package/dist/index.js.map +4 -4
- package/dist/lib/agent.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +3 -3
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/fragments/message/user.d.ts +16 -14
- package/dist/lib/fragments/message/user.d.ts.map +1 -1
- package/dist/lib/sandbox/apple-container-sandbox-errors.d.ts +44 -0
- package/dist/lib/sandbox/apple-container-sandbox-errors.d.ts.map +1 -0
- package/dist/lib/sandbox/apple-container-sandbox.d.ts +122 -0
- package/dist/lib/sandbox/apple-container-sandbox.d.ts.map +1 -0
- package/dist/lib/sandbox/cli-process.d.ts +24 -0
- package/dist/lib/sandbox/cli-process.d.ts.map +1 -0
- package/dist/lib/sandbox/container-engine.d.ts +149 -0
- package/dist/lib/sandbox/container-engine.d.ts.map +1 -0
- package/dist/lib/sandbox/container-sandbox-errors.d.ts +10 -0
- package/dist/lib/sandbox/container-sandbox-errors.d.ts.map +1 -0
- package/dist/lib/sandbox/container-sandbox.d.ts +90 -0
- package/dist/lib/sandbox/container-sandbox.d.ts.map +1 -0
- package/dist/lib/sandbox/docker-sandbox-errors.d.ts +2 -2
- package/dist/lib/sandbox/docker-sandbox-errors.d.ts.map +1 -1
- package/dist/lib/sandbox/docker-sandbox.d.ts +122 -218
- package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/index.d.ts +4 -0
- package/dist/lib/sandbox/index.d.ts.map +1 -1
- package/dist/lib/sandbox/shell-quote.d.ts +2 -2
- package/dist/lib/sandbox/strace/index.d.ts +6 -21
- package/dist/lib/sandbox/strace/index.d.ts.map +1 -1
- package/dist/lib/sandbox/strace/index.js +18 -7
- package/dist/lib/sandbox/strace/index.js.map +2 -2
- package/dist/lib/sandbox/types.d.ts +3 -3
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1186,7 +1186,7 @@ function isOutputAvailableToolPart(part) {
|
|
|
1186
1186
|
return isStaticToolUIPart(part) && part.state === "output-available";
|
|
1187
1187
|
}
|
|
1188
1188
|
function isToolOutputReminderEnvelope(value) {
|
|
1189
|
-
return isRecord(value) && "result" in value && typeof value.systemReminder === "string"
|
|
1189
|
+
return isRecord(value) && isRecord(value.meta) && value.meta.reminder === true && "result" in value && typeof value.systemReminder === "string";
|
|
1190
1190
|
}
|
|
1191
1191
|
function stripTextByRanges(text, ranges) {
|
|
1192
1192
|
if (ranges.length === 0) {
|
|
@@ -1309,7 +1309,8 @@ function applyInlineReminder(message2, value) {
|
|
|
1309
1309
|
};
|
|
1310
1310
|
}
|
|
1311
1311
|
function applyPartReminder(message2, value) {
|
|
1312
|
-
const
|
|
1312
|
+
const reminderText = formatTaggedReminder(value);
|
|
1313
|
+
const part = { type: "text", text: reminderText };
|
|
1313
1314
|
message2.parts.push(part);
|
|
1314
1315
|
const partIndex = message2.parts.length - 1;
|
|
1315
1316
|
return {
|
|
@@ -1318,13 +1319,10 @@ function applyPartReminder(message2, value) {
|
|
|
1318
1319
|
target: "user",
|
|
1319
1320
|
partIndex,
|
|
1320
1321
|
start: 0,
|
|
1321
|
-
end:
|
|
1322
|
+
end: reminderText.length,
|
|
1322
1323
|
mode: "part"
|
|
1323
1324
|
};
|
|
1324
1325
|
}
|
|
1325
|
-
function resolveReminderText(item, ctx) {
|
|
1326
|
-
return resolveReminder(item, ctx)?.text ?? "";
|
|
1327
|
-
}
|
|
1328
1326
|
function normalizeReminderResolution(value) {
|
|
1329
1327
|
if (typeof value === "string") {
|
|
1330
1328
|
return value.trim().length === 0 ? null : { text: value };
|
|
@@ -1374,7 +1372,19 @@ function applyRemindersToToolOutput(output, texts) {
|
|
|
1374
1372
|
if (texts.length === 0) return output;
|
|
1375
1373
|
return {
|
|
1376
1374
|
result: output === void 0 ? null : output,
|
|
1377
|
-
systemReminder: formatTaggedReminder(texts.join("\n"))
|
|
1375
|
+
systemReminder: formatTaggedReminder(texts.join("\n")),
|
|
1376
|
+
meta: { reminder: true }
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
function toToolReminderModelOutput(output, projectResult) {
|
|
1380
|
+
if (!isToolOutputReminderEnvelope(output)) return null;
|
|
1381
|
+
const projected = projectResult(output.result);
|
|
1382
|
+
return {
|
|
1383
|
+
type: "json",
|
|
1384
|
+
value: {
|
|
1385
|
+
result: isRecord(projected) ? projected.value : projected,
|
|
1386
|
+
systemReminder: output.systemReminder
|
|
1387
|
+
}
|
|
1378
1388
|
};
|
|
1379
1389
|
}
|
|
1380
1390
|
function mergeReminderMetadata(message2, addedReminders) {
|
|
@@ -1916,12 +1926,20 @@ function wrapToolsWithOutputReminders(tools, context) {
|
|
|
1916
1926
|
wrapped[name] = toolDef;
|
|
1917
1927
|
continue;
|
|
1918
1928
|
}
|
|
1929
|
+
const originalToModelOutput = toolDef.toModelOutput;
|
|
1919
1930
|
wrapped[name] = {
|
|
1920
1931
|
...toolDef,
|
|
1921
1932
|
execute: async (input, options) => {
|
|
1922
1933
|
const result = await execute.call(toolDef, input, options);
|
|
1923
1934
|
if (isAsyncIterable(result)) return result;
|
|
1924
1935
|
return context.applyToolOutputReminders(result);
|
|
1936
|
+
},
|
|
1937
|
+
toModelOutput: (args) => {
|
|
1938
|
+
const project = (output) => originalToModelOutput ? originalToModelOutput({
|
|
1939
|
+
...args,
|
|
1940
|
+
output
|
|
1941
|
+
}) : defaultToolModelOutput(output);
|
|
1942
|
+
return toToolReminderModelOutput(args.output, project) ?? project(args.output);
|
|
1925
1943
|
}
|
|
1926
1944
|
};
|
|
1927
1945
|
}
|
|
@@ -1930,6 +1948,9 @@ function wrapToolsWithOutputReminders(tools, context) {
|
|
|
1930
1948
|
function isAsyncIterable(value) {
|
|
1931
1949
|
return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
1932
1950
|
}
|
|
1951
|
+
function defaultToolModelOutput(output) {
|
|
1952
|
+
return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output ?? null };
|
|
1953
|
+
}
|
|
1933
1954
|
function agent(options) {
|
|
1934
1955
|
return new Agent(options);
|
|
1935
1956
|
}
|
|
@@ -3835,7 +3856,6 @@ var ContextEngine = class _ContextEngine {
|
|
|
3835
3856
|
const reminders = matched.map((m) => ({
|
|
3836
3857
|
text: m.resolved.text,
|
|
3837
3858
|
asPart: m.config.asPart,
|
|
3838
|
-
target: "user",
|
|
3839
3859
|
metadata: m.resolved.metadata
|
|
3840
3860
|
}));
|
|
3841
3861
|
const carrier = {
|
|
@@ -3895,9 +3915,9 @@ var ContextEngine = class _ContextEngine {
|
|
|
3895
3915
|
* Evaluate `target: 'tool-output'` reminders against a tool's raw result and
|
|
3896
3916
|
* return the (possibly wrapped) output.
|
|
3897
3917
|
*
|
|
3898
|
-
* Called by the agent's tool wrapper right after each `execute()` resolves
|
|
3899
|
-
*
|
|
3900
|
-
*
|
|
3918
|
+
* Called by the agent's tool wrapper right after each `execute()` resolves.
|
|
3919
|
+
* The store keeps the wrapped value with a host-only marker; the model-facing
|
|
3920
|
+
* projection strips that marker while preserving `result` + `systemReminder`.
|
|
3901
3921
|
*
|
|
3902
3922
|
* Returns the output unchanged when no tool-output reminder fires. Without a
|
|
3903
3923
|
* persisted user message there is no turn context to evaluate against
|
|
@@ -6209,1406 +6229,2175 @@ async function useAgentOsSandbox(options, fn) {
|
|
|
6209
6229
|
}
|
|
6210
6230
|
}
|
|
6211
6231
|
|
|
6212
|
-
// packages/context/src/lib/sandbox/
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
import {
|
|
6218
|
-
|
|
6219
|
-
function runWithBashMeta(fn) {
|
|
6220
|
-
return store.run({ hidden: {} }, fn);
|
|
6221
|
-
}
|
|
6222
|
-
function useBashMeta() {
|
|
6223
|
-
const state = store.getStore();
|
|
6224
|
-
if (!state) return null;
|
|
6225
|
-
return {
|
|
6226
|
-
setHidden(patch) {
|
|
6227
|
-
state.hidden = { ...state.hidden, ...patch };
|
|
6228
|
-
},
|
|
6229
|
-
setReminder(text) {
|
|
6230
|
-
state.reminder = text;
|
|
6231
|
-
},
|
|
6232
|
-
clearReminder() {
|
|
6233
|
-
state.reminder = void 0;
|
|
6234
|
-
}
|
|
6235
|
-
};
|
|
6236
|
-
}
|
|
6237
|
-
function readBashMeta() {
|
|
6238
|
-
return store.getStore();
|
|
6239
|
-
}
|
|
6240
|
-
|
|
6241
|
-
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
6242
|
-
import { tool as tool2 } from "ai";
|
|
6243
|
-
import {
|
|
6244
|
-
createBashTool as externalCreateBashTool
|
|
6245
|
-
} from "bash-tool";
|
|
6246
|
-
import z3 from "zod";
|
|
6232
|
+
// packages/context/src/lib/sandbox/apple-container-sandbox.ts
|
|
6233
|
+
import "bash-tool";
|
|
6234
|
+
import spawn3 from "nano-spawn";
|
|
6235
|
+
import { spawn as childSpawn2 } from "node:child_process";
|
|
6236
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
6237
|
+
import { tmpdir } from "node:os";
|
|
6238
|
+
import { join } from "node:path";
|
|
6247
6239
|
|
|
6248
|
-
// packages/context/src/lib/sandbox/
|
|
6249
|
-
|
|
6240
|
+
// packages/context/src/lib/sandbox/container-sandbox-errors.ts
|
|
6241
|
+
var ContainerSandboxError = class extends Error {
|
|
6242
|
+
containerId;
|
|
6243
|
+
constructor(message2, containerId) {
|
|
6244
|
+
super(message2);
|
|
6245
|
+
this.name = "ContainerSandboxError";
|
|
6246
|
+
this.containerId = containerId;
|
|
6247
|
+
}
|
|
6248
|
+
};
|
|
6250
6249
|
|
|
6251
|
-
// packages/context/src/lib/
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
6257
|
-
const match = content.match(frontmatterRegex);
|
|
6258
|
-
if (!match) {
|
|
6259
|
-
throw new Error("Invalid SKILL.md: missing or malformed frontmatter");
|
|
6250
|
+
// packages/context/src/lib/sandbox/apple-container-sandbox-errors.ts
|
|
6251
|
+
var AppleContainerSandboxError = class extends ContainerSandboxError {
|
|
6252
|
+
constructor(message2, containerId) {
|
|
6253
|
+
super(message2, containerId);
|
|
6254
|
+
this.name = "AppleContainerSandboxError";
|
|
6260
6255
|
}
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6256
|
+
};
|
|
6257
|
+
var ContainerServiceNotRunningError = class extends AppleContainerSandboxError {
|
|
6258
|
+
constructor() {
|
|
6259
|
+
super(
|
|
6260
|
+
"Apple container service is not running. Start it with `container system start` (first run also needs `container system kernel set --recommended`)."
|
|
6261
|
+
);
|
|
6262
|
+
this.name = "ContainerServiceNotRunningError";
|
|
6265
6263
|
}
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6264
|
+
};
|
|
6265
|
+
var AppleContainerCreationError = class extends AppleContainerSandboxError {
|
|
6266
|
+
image;
|
|
6267
|
+
cause;
|
|
6268
|
+
constructor(message2, image, cause) {
|
|
6269
|
+
super(`Failed to create container from image "${image}": ${message2}`);
|
|
6270
|
+
this.name = "AppleContainerCreationError";
|
|
6271
|
+
this.image = image;
|
|
6272
|
+
this.cause = cause;
|
|
6273
|
+
}
|
|
6274
|
+
};
|
|
6275
|
+
var AppleContainerImageBuildError = class extends AppleContainerSandboxError {
|
|
6276
|
+
stderr;
|
|
6277
|
+
constructor(stderr) {
|
|
6278
|
+
super(`Container image build failed: ${stderr}`);
|
|
6279
|
+
this.name = "AppleContainerImageBuildError";
|
|
6280
|
+
this.stderr = stderr;
|
|
6281
|
+
}
|
|
6282
|
+
};
|
|
6283
|
+
var AppleContainerVolumePathError = class extends AppleContainerSandboxError {
|
|
6284
|
+
source;
|
|
6285
|
+
containerPath;
|
|
6286
|
+
reason;
|
|
6287
|
+
constructor(source, containerPath, reason) {
|
|
6288
|
+
super(
|
|
6289
|
+
`Invalid container volume path "${source}" -> "${containerPath}": ${reason}`
|
|
6269
6290
|
);
|
|
6291
|
+
this.name = "AppleContainerVolumePathError";
|
|
6292
|
+
this.source = source;
|
|
6293
|
+
this.containerPath = containerPath;
|
|
6294
|
+
this.reason = reason;
|
|
6270
6295
|
}
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6296
|
+
};
|
|
6297
|
+
var AppleContainerVolumeInspectError = class extends AppleContainerSandboxError {
|
|
6298
|
+
volume;
|
|
6299
|
+
reason;
|
|
6300
|
+
constructor(volume, reason) {
|
|
6301
|
+
super(`Failed to inspect container volume "${volume}": ${reason}`);
|
|
6302
|
+
this.name = "AppleContainerVolumeInspectError";
|
|
6303
|
+
this.volume = volume;
|
|
6304
|
+
this.reason = reason;
|
|
6305
|
+
}
|
|
6306
|
+
};
|
|
6307
|
+
var AppleContainerVolumeCreateError = class extends AppleContainerSandboxError {
|
|
6308
|
+
volume;
|
|
6309
|
+
reason;
|
|
6310
|
+
constructor(volume, reason) {
|
|
6311
|
+
super(`Failed to create container volume "${volume}": ${reason}`);
|
|
6312
|
+
this.name = "AppleContainerVolumeCreateError";
|
|
6313
|
+
this.volume = volume;
|
|
6314
|
+
this.reason = reason;
|
|
6315
|
+
}
|
|
6316
|
+
};
|
|
6317
|
+
var AppleContainerVolumeRemoveError = class extends AppleContainerSandboxError {
|
|
6318
|
+
volume;
|
|
6319
|
+
reason;
|
|
6320
|
+
constructor(volume, reason) {
|
|
6321
|
+
super(`Failed to remove container volume "${volume}": ${reason}`);
|
|
6322
|
+
this.name = "AppleContainerVolumeRemoveError";
|
|
6323
|
+
this.volume = volume;
|
|
6324
|
+
this.reason = reason;
|
|
6325
|
+
}
|
|
6326
|
+
};
|
|
6327
|
+
|
|
6328
|
+
// packages/context/src/lib/sandbox/container-sandbox.ts
|
|
6329
|
+
import "bash-tool";
|
|
6330
|
+
import spawn2 from "nano-spawn";
|
|
6331
|
+
import { spawn as childSpawn } from "node:child_process";
|
|
6332
|
+
import { createHash } from "node:crypto";
|
|
6333
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6334
|
+
|
|
6335
|
+
// packages/context/src/lib/sandbox/cli-process.ts
|
|
6336
|
+
import { Readable as Readable2 } from "node:stream";
|
|
6337
|
+
|
|
6338
|
+
// packages/context/src/lib/sandbox/shell-quote.ts
|
|
6339
|
+
function shellQuote(s) {
|
|
6340
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6275
6341
|
}
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6342
|
+
|
|
6343
|
+
// packages/context/src/lib/sandbox/cli-process.ts
|
|
6344
|
+
function toSandboxProcess(child, abortSignal) {
|
|
6345
|
+
if (!child.stdout || !child.stderr) {
|
|
6346
|
+
child.kill("SIGKILL");
|
|
6347
|
+
throw new Error("exec child process is missing stdout/stderr streams");
|
|
6348
|
+
}
|
|
6349
|
+
const onAbort = () => child.kill("SIGKILL");
|
|
6350
|
+
if (abortSignal) {
|
|
6351
|
+
if (abortSignal.aborted) onAbort();
|
|
6352
|
+
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
6353
|
+
}
|
|
6280
6354
|
return {
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6355
|
+
stdout: Readable2.toWeb(child.stdout),
|
|
6356
|
+
stderr: Readable2.toWeb(child.stderr),
|
|
6357
|
+
exit: new Promise((resolve4, reject) => {
|
|
6358
|
+
const settle = () => {
|
|
6359
|
+
child.removeListener("exit", onExitEvent);
|
|
6360
|
+
child.removeListener("error", onError);
|
|
6361
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
6362
|
+
};
|
|
6363
|
+
const onError = (err) => {
|
|
6364
|
+
settle();
|
|
6365
|
+
reject(err);
|
|
6366
|
+
};
|
|
6367
|
+
const onExitEvent = (code, exitSignal) => {
|
|
6368
|
+
settle();
|
|
6369
|
+
resolve4({ code, signal: exitSignal, success: code === 0 });
|
|
6370
|
+
};
|
|
6371
|
+
child.on("exit", onExitEvent);
|
|
6372
|
+
child.on("error", onError);
|
|
6373
|
+
})
|
|
6285
6374
|
};
|
|
6286
6375
|
}
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
const
|
|
6290
|
-
|
|
6291
|
-
|
|
6376
|
+
var BASE64_WRITE_CHUNK = 32768;
|
|
6377
|
+
function base64WriteCommands(path5, content) {
|
|
6378
|
+
const base64 = Buffer.from(content).toString("base64");
|
|
6379
|
+
const quotedPath = shellQuote(path5);
|
|
6380
|
+
const commands = [];
|
|
6381
|
+
for (let offset = 0; offset < base64.length; offset += BASE64_WRITE_CHUNK) {
|
|
6382
|
+
const chunk = base64.slice(offset, offset + BASE64_WRITE_CHUNK);
|
|
6383
|
+
const redirect = offset === 0 ? ">" : ">>";
|
|
6384
|
+
commands.push(
|
|
6385
|
+
`printf '%s' ${shellQuote(chunk)} | base64 -d ${redirect} ${quotedPath}`
|
|
6386
|
+
);
|
|
6292
6387
|
}
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
if (!entry.isDirectory()) continue;
|
|
6296
|
-
const skillMdPath = path.join(expandedDir, entry.name, "SKILL.md");
|
|
6297
|
-
if (!fs.existsSync(skillMdPath)) continue;
|
|
6298
|
-
try {
|
|
6299
|
-
const metadata = loadSkillMetadata(skillMdPath);
|
|
6300
|
-
skills2.push(metadata);
|
|
6301
|
-
} catch (error) {
|
|
6302
|
-
console.warn(`Warning: Failed to load skill at ${skillMdPath}:`, error);
|
|
6303
|
-
}
|
|
6388
|
+
if (commands.length === 0) {
|
|
6389
|
+
commands.push(`printf '' > ${quotedPath}`);
|
|
6304
6390
|
}
|
|
6305
|
-
return
|
|
6306
|
-
}
|
|
6307
|
-
|
|
6308
|
-
// packages/context/src/lib/sandbox/walk.ts
|
|
6309
|
-
import { readFile, readdir } from "node:fs/promises";
|
|
6310
|
-
import * as path2 from "node:path";
|
|
6311
|
-
function expandHome(input) {
|
|
6312
|
-
if (!input.startsWith("~")) return input;
|
|
6313
|
-
const home = process.env.HOME ?? "";
|
|
6314
|
-
return path2.join(home, input.slice(1));
|
|
6391
|
+
return commands;
|
|
6315
6392
|
}
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
const files = [];
|
|
6319
|
-
async function walk(current) {
|
|
6320
|
-
let entries;
|
|
6321
|
-
try {
|
|
6322
|
-
entries = await readdir(current, { withFileTypes: true });
|
|
6323
|
-
} catch {
|
|
6324
|
-
return;
|
|
6325
|
-
}
|
|
6326
|
-
for (const entry of entries) {
|
|
6327
|
-
if (entry.name.startsWith(".")) continue;
|
|
6328
|
-
if (entry.isSymbolicLink()) continue;
|
|
6329
|
-
const entryPath = path2.join(current, entry.name);
|
|
6330
|
-
if (entry.isDirectory()) {
|
|
6331
|
-
await walk(entryPath);
|
|
6332
|
-
} else if (entry.isFile()) {
|
|
6333
|
-
const content = await readFile(entryPath);
|
|
6334
|
-
const relative3 = path2.relative(absoluteRoot, entryPath).split(path2.sep).join("/");
|
|
6335
|
-
files.push({ path: entryPath, relativePath: relative3, content });
|
|
6336
|
-
}
|
|
6337
|
-
}
|
|
6338
|
-
}
|
|
6339
|
-
await walk(absoluteRoot);
|
|
6340
|
-
return files;
|
|
6393
|
+
function base64ReadCommand(path5) {
|
|
6394
|
+
return `base64 ${shellQuote(path5)}`;
|
|
6341
6395
|
}
|
|
6342
6396
|
|
|
6343
|
-
// packages/context/src/lib/sandbox/
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
return `${normalizedBase}/${relative3}`;
|
|
6353
|
-
}
|
|
6354
|
-
function discoverSkillMappings(inputs) {
|
|
6355
|
-
if (inputs.length === 0) return [];
|
|
6356
|
-
const discoveredByName = /* @__PURE__ */ new Map();
|
|
6357
|
-
for (const { host, sandbox: sandboxBase } of inputs) {
|
|
6358
|
-
const absoluteHost = path3.resolve(expandHome2(host));
|
|
6359
|
-
for (const skill of discoverSkillsInDirectory(host)) {
|
|
6360
|
-
const absoluteSkillMd = path3.resolve(skill.skillMdPath);
|
|
6361
|
-
const relative3 = path3.relative(absoluteHost, absoluteSkillMd).split(path3.sep).join("/");
|
|
6362
|
-
discoveredByName.set(skill.name, {
|
|
6363
|
-
name: skill.name,
|
|
6364
|
-
description: skill.description,
|
|
6365
|
-
host: skill.skillMdPath,
|
|
6366
|
-
sandbox: joinSandbox(sandboxBase, relative3)
|
|
6367
|
-
});
|
|
6368
|
-
}
|
|
6397
|
+
// packages/context/src/lib/sandbox/installers/installer.ts
|
|
6398
|
+
import "bash-tool";
|
|
6399
|
+
import spawn from "nano-spawn";
|
|
6400
|
+
|
|
6401
|
+
// packages/context/src/lib/sandbox/docker-sandbox-errors.ts
|
|
6402
|
+
var DockerSandboxError = class extends ContainerSandboxError {
|
|
6403
|
+
constructor(message2, containerId) {
|
|
6404
|
+
super(message2, containerId);
|
|
6405
|
+
this.name = "DockerSandboxError";
|
|
6369
6406
|
}
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
const filesToUpload = [];
|
|
6376
|
-
for (const { host, sandbox: sandboxBase } of inputs) {
|
|
6377
|
-
const walked = await walkDirectory(host);
|
|
6378
|
-
for (const file of walked) {
|
|
6379
|
-
filesToUpload.push({
|
|
6380
|
-
path: joinSandbox(sandboxBase, file.relativePath),
|
|
6381
|
-
content: file.content
|
|
6382
|
-
});
|
|
6383
|
-
}
|
|
6407
|
+
};
|
|
6408
|
+
var DockerNotAvailableError = class extends DockerSandboxError {
|
|
6409
|
+
constructor() {
|
|
6410
|
+
super("Docker is not available. Ensure Docker daemon is running.");
|
|
6411
|
+
this.name = "DockerNotAvailableError";
|
|
6384
6412
|
}
|
|
6385
|
-
|
|
6386
|
-
|
|
6413
|
+
};
|
|
6414
|
+
var ContainerCreationError = class extends DockerSandboxError {
|
|
6415
|
+
image;
|
|
6416
|
+
cause;
|
|
6417
|
+
constructor(message2, image, cause) {
|
|
6418
|
+
super(`Failed to create container from image "${image}": ${message2}`);
|
|
6419
|
+
this.name = "ContainerCreationError";
|
|
6420
|
+
this.image = image;
|
|
6421
|
+
this.cause = cause;
|
|
6387
6422
|
}
|
|
6388
|
-
|
|
6389
|
-
|
|
6423
|
+
};
|
|
6424
|
+
var PackageInstallError = class extends DockerSandboxError {
|
|
6425
|
+
packages;
|
|
6426
|
+
image;
|
|
6427
|
+
packageManager;
|
|
6428
|
+
stderr;
|
|
6429
|
+
constructor(packages, image, packageManager, stderr, containerId) {
|
|
6430
|
+
super(
|
|
6431
|
+
`Package installation failed for [${packages.join(", ")}] using ${packageManager} on ${image}: ${stderr}`,
|
|
6432
|
+
containerId
|
|
6433
|
+
);
|
|
6434
|
+
this.name = "PackageInstallError";
|
|
6435
|
+
this.packages = packages;
|
|
6436
|
+
this.image = image;
|
|
6437
|
+
this.packageManager = packageManager;
|
|
6438
|
+
this.stderr = stderr;
|
|
6439
|
+
}
|
|
6440
|
+
};
|
|
6441
|
+
var InstallError = class extends DockerSandboxError {
|
|
6442
|
+
target;
|
|
6443
|
+
source;
|
|
6444
|
+
reason;
|
|
6445
|
+
url;
|
|
6446
|
+
constructor(opts) {
|
|
6447
|
+
const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
|
|
6448
|
+
super(
|
|
6449
|
+
`Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
|
|
6450
|
+
opts.containerId
|
|
6451
|
+
);
|
|
6452
|
+
this.name = "InstallError";
|
|
6453
|
+
this.target = opts.target;
|
|
6454
|
+
this.source = opts.source;
|
|
6455
|
+
this.reason = opts.reason;
|
|
6456
|
+
this.url = opts.url;
|
|
6457
|
+
}
|
|
6458
|
+
};
|
|
6459
|
+
var MissingRuntimeError = class extends DockerSandboxError {
|
|
6460
|
+
runtime;
|
|
6461
|
+
required;
|
|
6462
|
+
constructor(runtime, required, details, containerId) {
|
|
6463
|
+
const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
|
|
6464
|
+
super(details ? `${base} ${details}` : base, containerId);
|
|
6465
|
+
this.name = "MissingRuntimeError";
|
|
6466
|
+
this.runtime = runtime;
|
|
6467
|
+
this.required = required;
|
|
6468
|
+
}
|
|
6469
|
+
};
|
|
6470
|
+
var VolumePathError = class extends DockerSandboxError {
|
|
6471
|
+
source;
|
|
6472
|
+
containerPath;
|
|
6473
|
+
reason;
|
|
6474
|
+
constructor(source, containerPath, reason) {
|
|
6475
|
+
super(
|
|
6476
|
+
`Invalid Docker volume path "${source}" -> "${containerPath}": ${reason}`
|
|
6477
|
+
);
|
|
6478
|
+
this.name = "VolumePathError";
|
|
6479
|
+
this.source = source;
|
|
6480
|
+
this.containerPath = containerPath;
|
|
6481
|
+
this.reason = reason;
|
|
6482
|
+
}
|
|
6483
|
+
};
|
|
6484
|
+
var VolumeInspectError = class extends DockerSandboxError {
|
|
6485
|
+
volume;
|
|
6486
|
+
reason;
|
|
6487
|
+
constructor(volume, reason) {
|
|
6488
|
+
super(`Failed to inspect Docker volume "${volume}": ${reason}`);
|
|
6489
|
+
this.name = "VolumeInspectError";
|
|
6490
|
+
this.volume = volume;
|
|
6491
|
+
this.reason = reason;
|
|
6492
|
+
}
|
|
6493
|
+
};
|
|
6494
|
+
var VolumeCreateError = class extends DockerSandboxError {
|
|
6495
|
+
volume;
|
|
6496
|
+
reason;
|
|
6497
|
+
constructor(volume, reason) {
|
|
6498
|
+
super(`Failed to create Docker volume "${volume}": ${reason}`);
|
|
6499
|
+
this.name = "VolumeCreateError";
|
|
6500
|
+
this.volume = volume;
|
|
6501
|
+
this.reason = reason;
|
|
6502
|
+
}
|
|
6503
|
+
};
|
|
6504
|
+
var VolumeRemoveError = class extends DockerSandboxError {
|
|
6505
|
+
volume;
|
|
6506
|
+
reason;
|
|
6507
|
+
constructor(volume, reason) {
|
|
6508
|
+
super(`Failed to remove Docker volume "${volume}": ${reason}`);
|
|
6509
|
+
this.name = "VolumeRemoveError";
|
|
6510
|
+
this.volume = volume;
|
|
6511
|
+
this.reason = reason;
|
|
6512
|
+
}
|
|
6513
|
+
};
|
|
6514
|
+
var DockerfileBuildError = class extends DockerSandboxError {
|
|
6515
|
+
stderr;
|
|
6516
|
+
constructor(stderr) {
|
|
6517
|
+
super(`Dockerfile build failed: ${stderr}`);
|
|
6518
|
+
this.name = "DockerfileBuildError";
|
|
6519
|
+
this.stderr = stderr;
|
|
6520
|
+
}
|
|
6521
|
+
};
|
|
6522
|
+
var ComposeStartError = class extends DockerSandboxError {
|
|
6523
|
+
composeFile;
|
|
6524
|
+
stderr;
|
|
6525
|
+
constructor(composeFile, stderr) {
|
|
6526
|
+
super(`Docker Compose failed to start: ${stderr}`);
|
|
6527
|
+
this.name = "ComposeStartError";
|
|
6528
|
+
this.composeFile = composeFile;
|
|
6529
|
+
this.stderr = stderr;
|
|
6530
|
+
}
|
|
6531
|
+
};
|
|
6390
6532
|
|
|
6391
|
-
// packages/context/src/lib/sandbox/
|
|
6392
|
-
var
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6533
|
+
// packages/context/src/lib/sandbox/installers/installer.ts
|
|
6534
|
+
var Installer = class {
|
|
6535
|
+
};
|
|
6536
|
+
function isDebianBased(image) {
|
|
6537
|
+
const lower = image.toLowerCase();
|
|
6538
|
+
if (lower.includes("alpine")) return false;
|
|
6539
|
+
const debianPatterns = ["debian", "ubuntu", "node", "python"];
|
|
6540
|
+
return debianPatterns.some((pattern) => lower.includes(pattern));
|
|
6541
|
+
}
|
|
6542
|
+
function createInstallerContext(containerId, image) {
|
|
6543
|
+
const packageManager = isDebianBased(image) ? "apt-get" : "apk";
|
|
6544
|
+
let archPromise = null;
|
|
6545
|
+
const ensuredTools = /* @__PURE__ */ new Set();
|
|
6546
|
+
let aptUpdated = false;
|
|
6547
|
+
const exec = async (command) => {
|
|
6548
|
+
try {
|
|
6549
|
+
const result = await spawn("docker", [
|
|
6550
|
+
"exec",
|
|
6551
|
+
containerId,
|
|
6552
|
+
"sh",
|
|
6553
|
+
"-c",
|
|
6554
|
+
command
|
|
6555
|
+
]);
|
|
6556
|
+
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
6557
|
+
} catch (error) {
|
|
6558
|
+
const err = error;
|
|
6559
|
+
return {
|
|
6560
|
+
stdout: err.stdout ?? "",
|
|
6561
|
+
stderr: err.stderr ?? err.message ?? "",
|
|
6562
|
+
exitCode: err.exitCode ?? 1
|
|
6563
|
+
};
|
|
6564
|
+
}
|
|
6565
|
+
};
|
|
6566
|
+
const arch = async () => {
|
|
6567
|
+
if (!archPromise) {
|
|
6568
|
+
const attempt = (async () => {
|
|
6569
|
+
const result = await exec("uname -m");
|
|
6570
|
+
if (result.exitCode !== 0) {
|
|
6571
|
+
throw new DockerSandboxError(
|
|
6572
|
+
`Failed to detect container architecture: ${result.stderr}`,
|
|
6573
|
+
containerId
|
|
6574
|
+
);
|
|
6575
|
+
}
|
|
6576
|
+
return result.stdout.trim();
|
|
6577
|
+
})();
|
|
6578
|
+
archPromise = attempt.catch((err) => {
|
|
6579
|
+
archPromise = null;
|
|
6401
6580
|
throw err;
|
|
6402
|
-
}
|
|
6581
|
+
});
|
|
6582
|
+
}
|
|
6583
|
+
return archPromise;
|
|
6584
|
+
};
|
|
6585
|
+
const installPackages = async (packages) => {
|
|
6586
|
+
if (packages.length === 0) return;
|
|
6587
|
+
const quoted = packages.map(shellQuote).join(" ");
|
|
6588
|
+
let cmd;
|
|
6589
|
+
if (packageManager === "apt-get") {
|
|
6590
|
+
cmd = aptUpdated ? `apt-get install -y ${quoted}` : `apt-get update && apt-get install -y ${quoted}`;
|
|
6591
|
+
} else {
|
|
6592
|
+
cmd = `apk add --no-cache ${quoted}`;
|
|
6593
|
+
}
|
|
6594
|
+
const result = await exec(cmd);
|
|
6595
|
+
if (result.exitCode !== 0) {
|
|
6596
|
+
throw new PackageInstallError(
|
|
6597
|
+
packages,
|
|
6598
|
+
image,
|
|
6599
|
+
packageManager,
|
|
6600
|
+
result.stderr,
|
|
6601
|
+
containerId
|
|
6602
|
+
);
|
|
6603
|
+
}
|
|
6604
|
+
if (packageManager === "apt-get") aptUpdated = true;
|
|
6605
|
+
};
|
|
6606
|
+
const ensureTool = async (checkName, installName) => {
|
|
6607
|
+
const cacheKey = installName ?? checkName;
|
|
6608
|
+
if (ensuredTools.has(cacheKey)) return;
|
|
6609
|
+
const check = await exec(`which ${shellQuote(checkName)}`);
|
|
6610
|
+
if (check.exitCode === 0) {
|
|
6611
|
+
ensuredTools.add(cacheKey);
|
|
6612
|
+
return;
|
|
6403
6613
|
}
|
|
6614
|
+
await installPackages([installName ?? checkName]);
|
|
6615
|
+
ensuredTools.add(cacheKey);
|
|
6616
|
+
};
|
|
6617
|
+
return {
|
|
6618
|
+
containerId,
|
|
6619
|
+
image,
|
|
6620
|
+
packageManager,
|
|
6621
|
+
arch,
|
|
6622
|
+
exec,
|
|
6623
|
+
installPackages,
|
|
6624
|
+
ensureTool
|
|
6404
6625
|
};
|
|
6405
6626
|
}
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6627
|
+
|
|
6628
|
+
// packages/context/src/lib/sandbox/container-sandbox.ts
|
|
6629
|
+
var CONTAINER_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
6630
|
+
var ContainerSandboxStrategy = class {
|
|
6631
|
+
context;
|
|
6632
|
+
engine;
|
|
6633
|
+
opts;
|
|
6634
|
+
volumes;
|
|
6635
|
+
name;
|
|
6636
|
+
workdir;
|
|
6637
|
+
createdVolumes = /* @__PURE__ */ new Set();
|
|
6638
|
+
constructor(opts, engine) {
|
|
6639
|
+
this.opts = opts;
|
|
6640
|
+
this.engine = engine;
|
|
6641
|
+
const { volumes = [], env = {}, name, workdir = "/workspace" } = opts;
|
|
6642
|
+
for (const key of Object.keys(env)) {
|
|
6643
|
+
if (key.length === 0 || key.includes("=")) {
|
|
6644
|
+
throw this.engine.errors.generic(
|
|
6645
|
+
`Invalid environment variable key: "${key}"`
|
|
6646
|
+
);
|
|
6647
|
+
}
|
|
6648
|
+
}
|
|
6649
|
+
if (name !== void 0 && !CONTAINER_NAME_PATTERN.test(name)) {
|
|
6650
|
+
throw this.engine.errors.generic(
|
|
6651
|
+
`Invalid container name: "${name}". Use only letters, numbers, underscore, period, or hyphen. The "sandbox-" prefix is added automatically.`
|
|
6652
|
+
);
|
|
6653
|
+
}
|
|
6654
|
+
this.volumes = volumes;
|
|
6655
|
+
this.name = name;
|
|
6656
|
+
this.workdir = workdir;
|
|
6657
|
+
}
|
|
6658
|
+
async create() {
|
|
6659
|
+
const image = await this.getImage();
|
|
6660
|
+
let acquired;
|
|
6661
|
+
try {
|
|
6662
|
+
acquired = await this.acquireContainer(image);
|
|
6663
|
+
this.context = { containerId: acquired.containerId, image };
|
|
6664
|
+
if (!acquired.attached) {
|
|
6665
|
+
await this.engine.ensureWorkdir(this.context.containerId, this.workdir);
|
|
6666
|
+
await this.configure();
|
|
6667
|
+
}
|
|
6668
|
+
} catch (error) {
|
|
6669
|
+
if (acquired && !acquired.attached) {
|
|
6670
|
+
await this.stopContainer(acquired.containerId);
|
|
6671
|
+
}
|
|
6672
|
+
await this.cleanupCreatedVolumesAfterFailure(error);
|
|
6673
|
+
throw error;
|
|
6674
|
+
}
|
|
6675
|
+
return this.createSandboxMethods();
|
|
6676
|
+
}
|
|
6677
|
+
namedContainerId() {
|
|
6678
|
+
return `sandbox-${this.name}`;
|
|
6679
|
+
}
|
|
6680
|
+
defaultContainerId() {
|
|
6681
|
+
return `sandbox-${crypto.randomUUID().slice(0, 8)}`;
|
|
6682
|
+
}
|
|
6683
|
+
async acquireContainer(image) {
|
|
6684
|
+
if (!this.name) {
|
|
6685
|
+
const containerId2 = this.defaultContainerId();
|
|
6686
|
+
await this.prepareVolumes();
|
|
6687
|
+
await this.startContainer(image, containerId2);
|
|
6688
|
+
return { containerId: containerId2, attached: false };
|
|
6689
|
+
}
|
|
6690
|
+
const containerId = this.namedContainerId();
|
|
6691
|
+
const probe = await this.inspectContainer(containerId);
|
|
6692
|
+
if (probe === "running") {
|
|
6693
|
+
return { containerId, attached: true };
|
|
6694
|
+
}
|
|
6695
|
+
if (probe === "stopped") {
|
|
6696
|
+
await this.startStoppedContainer(containerId, image);
|
|
6697
|
+
return { containerId, attached: true };
|
|
6698
|
+
}
|
|
6699
|
+
await this.prepareVolumes();
|
|
6700
|
+
try {
|
|
6701
|
+
await this.startContainer(image, containerId);
|
|
6702
|
+
return { containerId, attached: false };
|
|
6703
|
+
} catch (error) {
|
|
6704
|
+
if (this.engine.isNameConflict(this.engine.errorMessage(error))) {
|
|
6705
|
+
await this.cleanupCreatedVolumes();
|
|
6706
|
+
const raced = await this.inspectContainer(containerId);
|
|
6707
|
+
if (raced === "running") {
|
|
6708
|
+
return { containerId, attached: true };
|
|
6709
|
+
}
|
|
6710
|
+
if (raced === "stopped") {
|
|
6711
|
+
await this.startStoppedContainer(containerId, image);
|
|
6712
|
+
return { containerId, attached: true };
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
throw error;
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
async startStoppedContainer(containerId, image) {
|
|
6719
|
+
try {
|
|
6720
|
+
await spawn2(this.engine.cli, ["start", containerId]);
|
|
6721
|
+
} catch (error) {
|
|
6722
|
+
const message2 = this.engineErrorMessage(error);
|
|
6723
|
+
if (this.isServiceDown(message2)) {
|
|
6724
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6435
6725
|
}
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
const hasReminder = state.reminder !== void 0;
|
|
6445
|
-
if (!hasHidden && !hasReminder) return result;
|
|
6446
|
-
return {
|
|
6447
|
-
...result,
|
|
6448
|
-
...hasHidden ? { meta: state.hidden } : {},
|
|
6449
|
-
...hasReminder ? { reminder: state.reminder } : {}
|
|
6450
|
-
};
|
|
6451
|
-
})
|
|
6726
|
+
throw this.engine.errors.creation(message2, image, error);
|
|
6727
|
+
}
|
|
6728
|
+
}
|
|
6729
|
+
async inspectContainer(containerId) {
|
|
6730
|
+
try {
|
|
6731
|
+
const result = await spawn2(
|
|
6732
|
+
this.engine.cli,
|
|
6733
|
+
this.engine.inspectArgs(containerId)
|
|
6452
6734
|
);
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6735
|
+
return this.engine.parseStatus(result.stdout.trim());
|
|
6736
|
+
} catch (error) {
|
|
6737
|
+
const message2 = this.engineErrorMessage(error);
|
|
6738
|
+
if (this.isServiceDown(message2)) {
|
|
6739
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6457
6740
|
}
|
|
6458
|
-
|
|
6459
|
-
|
|
6741
|
+
if (this.engine.isMissingContainer(message2)) {
|
|
6742
|
+
return "absent";
|
|
6743
|
+
}
|
|
6744
|
+
throw this.engine.errors.generic(
|
|
6745
|
+
`Failed to inspect container "${containerId}": ${message2}`
|
|
6746
|
+
);
|
|
6460
6747
|
}
|
|
6461
|
-
}
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
execute: async (input, options2) => {
|
|
6468
|
-
if (!originalWriteExecute) {
|
|
6469
|
-
throw new Error("writeFile tool execution is not available");
|
|
6748
|
+
}
|
|
6749
|
+
async prepareVolumes() {
|
|
6750
|
+
this.validateVolumes();
|
|
6751
|
+
for (const volume of this.volumes) {
|
|
6752
|
+
if (volume.type !== "volume") {
|
|
6753
|
+
continue;
|
|
6470
6754
|
}
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6755
|
+
const lifecycle = volume.lifecycle ?? "external";
|
|
6756
|
+
if (lifecycle === "external") {
|
|
6757
|
+
await this.inspectVolume(volume.name);
|
|
6758
|
+
continue;
|
|
6759
|
+
}
|
|
6760
|
+
const exists = await this.volumeExists(volume.name);
|
|
6761
|
+
if (exists) {
|
|
6762
|
+
throw this.engine.errors.volumeCreate(
|
|
6763
|
+
volume.name,
|
|
6764
|
+
"managed volume already exists"
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6767
|
+
await this.createVolume(volume);
|
|
6768
|
+
if (volume.removeOnDispose !== false) {
|
|
6769
|
+
this.createdVolumes.add(volume.name);
|
|
6476
6770
|
}
|
|
6477
6771
|
}
|
|
6478
|
-
}
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
// packages/context/src/lib/sandbox/binary-bridges.ts
|
|
6490
|
-
import { existsSync as existsSync2 } from "fs";
|
|
6491
|
-
import { defineCommand } from "just-bash";
|
|
6492
|
-
import spawn from "nano-spawn";
|
|
6493
|
-
import * as path4 from "path";
|
|
6494
|
-
function createBinaryBridges(...binaries) {
|
|
6495
|
-
return binaries.map((input) => {
|
|
6496
|
-
const config = typeof input === "string" ? { name: input } : input;
|
|
6497
|
-
const { name, binaryPath = name, allowedArgs } = config;
|
|
6498
|
-
return defineCommand(name, async (args, ctx) => {
|
|
6499
|
-
if (allowedArgs) {
|
|
6500
|
-
const invalidArg = args.find((arg) => !allowedArgs.test(arg));
|
|
6501
|
-
if (invalidArg) {
|
|
6502
|
-
return {
|
|
6503
|
-
stdout: "",
|
|
6504
|
-
stderr: `${name}: argument '${invalidArg}' not allowed by security policy`,
|
|
6505
|
-
exitCode: 1
|
|
6506
|
-
};
|
|
6507
|
-
}
|
|
6772
|
+
}
|
|
6773
|
+
validateVolumes() {
|
|
6774
|
+
const containerPaths = /* @__PURE__ */ new Set();
|
|
6775
|
+
for (const volume of this.volumes) {
|
|
6776
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
6777
|
+
if (!volume.containerPath.startsWith("/")) {
|
|
6778
|
+
throw this.engine.errors.volumePath(
|
|
6779
|
+
source,
|
|
6780
|
+
volume.containerPath,
|
|
6781
|
+
"containerPath must be absolute"
|
|
6782
|
+
);
|
|
6508
6783
|
}
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
// ctx.env is a Map, convert to object
|
|
6527
|
-
PATH: process.env.PATH
|
|
6528
|
-
// Always use host PATH for binary bridges
|
|
6529
|
-
};
|
|
6530
|
-
const result = await spawn(binaryPath, resolvedArgs, {
|
|
6531
|
-
cwd: realCwd,
|
|
6532
|
-
env: mergedEnv
|
|
6533
|
-
});
|
|
6534
|
-
return {
|
|
6535
|
-
stdout: result.stdout,
|
|
6536
|
-
stderr: result.stderr,
|
|
6537
|
-
exitCode: 0
|
|
6538
|
-
};
|
|
6539
|
-
} catch (error) {
|
|
6540
|
-
if (error && typeof error === "object") {
|
|
6541
|
-
const err = error;
|
|
6542
|
-
const cause = err.cause;
|
|
6543
|
-
if (cause?.code === "ENOENT") {
|
|
6544
|
-
return {
|
|
6545
|
-
stdout: "",
|
|
6546
|
-
stderr: `${name}: ${binaryPath} not found`,
|
|
6547
|
-
exitCode: 127
|
|
6548
|
-
};
|
|
6549
|
-
}
|
|
6784
|
+
this.validateMountValue("containerPath", volume.containerPath, volume);
|
|
6785
|
+
if (containerPaths.has(volume.containerPath)) {
|
|
6786
|
+
throw this.engine.errors.volumePath(
|
|
6787
|
+
source,
|
|
6788
|
+
volume.containerPath,
|
|
6789
|
+
"containerPath must be unique"
|
|
6790
|
+
);
|
|
6791
|
+
}
|
|
6792
|
+
containerPaths.add(volume.containerPath);
|
|
6793
|
+
if (volume.type === "bind") {
|
|
6794
|
+
this.validateMountValue("hostPath", volume.hostPath, volume);
|
|
6795
|
+
if (!existsSync(volume.hostPath)) {
|
|
6796
|
+
throw this.engine.errors.volumePath(
|
|
6797
|
+
volume.hostPath,
|
|
6798
|
+
volume.containerPath,
|
|
6799
|
+
"hostPath does not exist on host"
|
|
6800
|
+
);
|
|
6550
6801
|
}
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6802
|
+
continue;
|
|
6803
|
+
}
|
|
6804
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(volume.name)) {
|
|
6805
|
+
throw this.engine.errors.volumePath(
|
|
6806
|
+
volume.name,
|
|
6807
|
+
volume.containerPath,
|
|
6808
|
+
"volume name must start with an alphanumeric character and contain only letters, numbers, underscore, period, or hyphen"
|
|
6809
|
+
);
|
|
6810
|
+
}
|
|
6811
|
+
if (volume.subPath) {
|
|
6812
|
+
this.validateMountValue("subPath", volume.subPath, volume);
|
|
6813
|
+
}
|
|
6814
|
+
if ((volume.lifecycle ?? "external") === "external") {
|
|
6815
|
+
if (volume.driver || volume.driverOptions) {
|
|
6816
|
+
throw this.engine.errors.volumePath(
|
|
6817
|
+
volume.name,
|
|
6818
|
+
volume.containerPath,
|
|
6819
|
+
'driver and driverOptions require lifecycle "managed"'
|
|
6820
|
+
);
|
|
6558
6821
|
}
|
|
6559
|
-
return {
|
|
6560
|
-
stdout: "",
|
|
6561
|
-
stderr: `${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
6562
|
-
exitCode: 127
|
|
6563
|
-
};
|
|
6564
6822
|
}
|
|
6565
|
-
}
|
|
6566
|
-
});
|
|
6567
|
-
}
|
|
6568
|
-
function resolveRealCwd(ctx) {
|
|
6569
|
-
const fs2 = ctx.fs;
|
|
6570
|
-
let realCwd;
|
|
6571
|
-
if (fs2.root) {
|
|
6572
|
-
realCwd = path4.join(fs2.root, ctx.cwd);
|
|
6573
|
-
} else if (typeof fs2.getMountPoint === "function" && typeof fs2.toRealPath === "function") {
|
|
6574
|
-
const real = fs2.toRealPath(ctx.cwd);
|
|
6575
|
-
realCwd = real ?? process.cwd();
|
|
6576
|
-
} else {
|
|
6577
|
-
realCwd = process.cwd();
|
|
6823
|
+
}
|
|
6578
6824
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
6825
|
+
validateMountValue(field, value, volume) {
|
|
6826
|
+
if (!value.includes(",")) {
|
|
6827
|
+
return;
|
|
6828
|
+
}
|
|
6829
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
6830
|
+
throw this.engine.errors.volumePath(
|
|
6831
|
+
source,
|
|
6832
|
+
volume.containerPath,
|
|
6833
|
+
`${field} must not contain commas`
|
|
6834
|
+
);
|
|
6581
6835
|
}
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
}
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6836
|
+
buildRunArgs(image, containerId) {
|
|
6837
|
+
return this.engine.runArgs(image, containerId, this.opts, this.workdir);
|
|
6838
|
+
}
|
|
6839
|
+
async inspectVolume(name) {
|
|
6840
|
+
try {
|
|
6841
|
+
await spawn2(this.engine.cli, ["volume", "inspect", name]);
|
|
6842
|
+
} catch (error) {
|
|
6843
|
+
const reason = this.engineErrorMessage(error);
|
|
6844
|
+
if (this.isServiceDown(reason)) {
|
|
6845
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6846
|
+
}
|
|
6847
|
+
throw this.engine.errors.volumeInspect(name, reason);
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
async volumeExists(name) {
|
|
6851
|
+
try {
|
|
6852
|
+
await this.inspectVolume(name);
|
|
6853
|
+
return true;
|
|
6854
|
+
} catch (error) {
|
|
6855
|
+
if (error instanceof ContainerSandboxError && this.engine.isMissingVolume(error.message)) {
|
|
6856
|
+
return false;
|
|
6857
|
+
}
|
|
6858
|
+
throw error;
|
|
6859
|
+
}
|
|
6860
|
+
}
|
|
6861
|
+
async createVolume(volume) {
|
|
6862
|
+
const args = this.engine.volumeCreateArgs(volume);
|
|
6863
|
+
try {
|
|
6864
|
+
await spawn2(this.engine.cli, args);
|
|
6865
|
+
} catch (error) {
|
|
6866
|
+
const reason = this.engineErrorMessage(error);
|
|
6867
|
+
if (this.isServiceDown(reason)) {
|
|
6868
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6869
|
+
}
|
|
6870
|
+
throw this.engine.errors.volumeCreate(volume.name, reason);
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
async cleanupCreatedVolumes() {
|
|
6874
|
+
const volumes = [...this.createdVolumes].reverse();
|
|
6875
|
+
for (const volume of volumes) {
|
|
6876
|
+
try {
|
|
6877
|
+
await spawn2(this.engine.cli, ["volume", "rm", volume]);
|
|
6878
|
+
this.createdVolumes.delete(volume);
|
|
6879
|
+
} catch (error) {
|
|
6880
|
+
const reason = this.engineErrorMessage(error);
|
|
6881
|
+
if (this.isServiceDown(reason)) {
|
|
6882
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6883
|
+
}
|
|
6884
|
+
throw this.engine.errors.volumeRemove(volume, reason);
|
|
6885
|
+
}
|
|
6886
|
+
}
|
|
6603
6887
|
}
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6888
|
+
async cleanupCreatedVolumesAfterFailure(originalError) {
|
|
6889
|
+
try {
|
|
6890
|
+
await this.cleanupCreatedVolumes();
|
|
6891
|
+
} catch (cleanupError) {
|
|
6892
|
+
if (originalError instanceof Error) {
|
|
6893
|
+
const original = originalError;
|
|
6894
|
+
original.suppressed = [...original.suppressed ?? [], cleanupError];
|
|
6895
|
+
}
|
|
6896
|
+
}
|
|
6609
6897
|
}
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
constructor(message2, cause) {
|
|
6613
|
-
super(message2, cause);
|
|
6614
|
-
this.name = "DaytonaCommandError";
|
|
6898
|
+
engineErrorMessage(error) {
|
|
6899
|
+
return this.engine.errorMessage(error);
|
|
6615
6900
|
}
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6901
|
+
isServiceDown(message2) {
|
|
6902
|
+
return this.engine.isServiceDown(message2);
|
|
6903
|
+
}
|
|
6904
|
+
async startContainer(image, containerId) {
|
|
6905
|
+
const args = this.buildRunArgs(image, containerId);
|
|
6906
|
+
try {
|
|
6907
|
+
await spawn2(this.engine.cli, args);
|
|
6908
|
+
} catch (error) {
|
|
6909
|
+
const message2 = this.engineErrorMessage(error);
|
|
6910
|
+
if (this.isServiceDown(message2)) {
|
|
6911
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
6912
|
+
}
|
|
6913
|
+
throw this.engine.errors.creation(message2, image, error);
|
|
6627
6914
|
}
|
|
6628
|
-
} catch (error) {
|
|
6629
|
-
throw normalizeDaytonaError(error, sdk);
|
|
6630
6915
|
}
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
}
|
|
6636
|
-
var UNRECOVERABLE_SANDBOX_STATES = /* @__PURE__ */ new Set([
|
|
6637
|
-
"error",
|
|
6638
|
-
"build_failed",
|
|
6639
|
-
"destroyed"
|
|
6640
|
-
]);
|
|
6641
|
-
async function acquireReusedSandbox(client, options, sdk) {
|
|
6642
|
-
let existing;
|
|
6643
|
-
try {
|
|
6644
|
-
existing = await client.get(options.name);
|
|
6645
|
-
} catch (error) {
|
|
6646
|
-
if (error instanceof sdk.DaytonaNotFoundError) {
|
|
6647
|
-
return createSandbox(client, options);
|
|
6916
|
+
async stopContainer(containerId) {
|
|
6917
|
+
try {
|
|
6918
|
+
await spawn2(this.engine.cli, ["stop", containerId]);
|
|
6919
|
+
} catch {
|
|
6648
6920
|
}
|
|
6649
|
-
throw error;
|
|
6650
6921
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6922
|
+
async exec(command, options) {
|
|
6923
|
+
try {
|
|
6924
|
+
const result = await spawn2(
|
|
6925
|
+
this.engine.cli,
|
|
6926
|
+
this.engine.execArgs(this.context.containerId, command),
|
|
6927
|
+
{ signal: options?.signal }
|
|
6928
|
+
);
|
|
6929
|
+
return {
|
|
6930
|
+
stdout: result.stdout,
|
|
6931
|
+
stderr: result.stderr,
|
|
6932
|
+
exitCode: 0
|
|
6933
|
+
};
|
|
6934
|
+
} catch (error) {
|
|
6935
|
+
const err = error;
|
|
6936
|
+
return {
|
|
6937
|
+
stdout: err.stdout || "",
|
|
6938
|
+
stderr: err.stderr || err.message || "",
|
|
6939
|
+
exitCode: err.exitCode ?? 1
|
|
6940
|
+
};
|
|
6941
|
+
}
|
|
6654
6942
|
}
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
} catch {
|
|
6943
|
+
spawnProcess(command, options) {
|
|
6944
|
+
const child = childSpawn(
|
|
6945
|
+
this.engine.cli,
|
|
6946
|
+
this.engine.execArgs(this.context.containerId, command, options)
|
|
6947
|
+
);
|
|
6948
|
+
return toSandboxProcess(child, options?.signal);
|
|
6662
6949
|
}
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6950
|
+
createSandboxMethods() {
|
|
6951
|
+
const { containerId } = this.context;
|
|
6952
|
+
const sandbox = {
|
|
6953
|
+
executeCommand: async (command, options) => this.exec(command, options),
|
|
6954
|
+
spawn: (command, options) => this.spawnProcess(command, options),
|
|
6955
|
+
readFile: async (path5) => {
|
|
6956
|
+
const result = await sandbox.executeCommand(base64ReadCommand(path5));
|
|
6957
|
+
if (result.exitCode !== 0) {
|
|
6958
|
+
throw new Error(`Failed to read file "${path5}": ${result.stderr}`);
|
|
6959
|
+
}
|
|
6960
|
+
return Buffer.from(result.stdout, "base64").toString("utf-8");
|
|
6961
|
+
},
|
|
6962
|
+
writeFiles: async (files) => {
|
|
6963
|
+
for (const file of files) {
|
|
6964
|
+
const dir = file.path.substring(0, file.path.lastIndexOf("/"));
|
|
6965
|
+
if (dir) {
|
|
6966
|
+
await sandbox.executeCommand(`mkdir -p ${shellQuote(dir)}`);
|
|
6967
|
+
}
|
|
6968
|
+
for (const command of base64WriteCommands(file.path, file.content)) {
|
|
6969
|
+
const result = await sandbox.executeCommand(command);
|
|
6970
|
+
if (result.exitCode !== 0) {
|
|
6971
|
+
throw new Error(
|
|
6972
|
+
`Failed to write file "${file.path}": ${result.stderr}`
|
|
6973
|
+
);
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
},
|
|
6978
|
+
dispose: async () => {
|
|
6979
|
+
await this.stopContainer(containerId);
|
|
6980
|
+
await this.cleanupCreatedVolumes();
|
|
6981
|
+
},
|
|
6982
|
+
[Symbol.asyncDispose]() {
|
|
6983
|
+
return this.dispose();
|
|
6984
|
+
}
|
|
6985
|
+
};
|
|
6986
|
+
return sandbox;
|
|
6667
6987
|
}
|
|
6668
|
-
}
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6988
|
+
};
|
|
6989
|
+
var RuntimeStrategy = class extends ContainerSandboxStrategy {
|
|
6990
|
+
image;
|
|
6991
|
+
installers;
|
|
6992
|
+
constructor(opts, engine, mode) {
|
|
6993
|
+
super(opts, engine);
|
|
6994
|
+
this.image = mode.image;
|
|
6995
|
+
this.installers = mode.installers;
|
|
6673
6996
|
}
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
function createSandbox(client, options) {
|
|
6677
|
-
const base = compactObject({
|
|
6678
|
-
name: options.name,
|
|
6679
|
-
user: options.user,
|
|
6680
|
-
language: options.language,
|
|
6681
|
-
envVars: options.envVars,
|
|
6682
|
-
labels: options.labels,
|
|
6683
|
-
public: options.public,
|
|
6684
|
-
autoStopInterval: options.autoStopInterval,
|
|
6685
|
-
autoArchiveInterval: options.autoArchiveInterval,
|
|
6686
|
-
autoDeleteInterval: options.autoDeleteInterval,
|
|
6687
|
-
volumes: options.volumes,
|
|
6688
|
-
networkBlockAll: options.networkBlockAll,
|
|
6689
|
-
networkAllowList: options.networkAllowList,
|
|
6690
|
-
ephemeral: options.ephemeral
|
|
6691
|
-
});
|
|
6692
|
-
if (options.image !== void 0) {
|
|
6693
|
-
const params2 = compactObject({
|
|
6694
|
-
...base,
|
|
6695
|
-
image: options.image,
|
|
6696
|
-
resources: options.resources
|
|
6697
|
-
});
|
|
6698
|
-
return client.create(params2, {
|
|
6699
|
-
timeout: options.createTimeout,
|
|
6700
|
-
onSnapshotCreateLogs: options.onSnapshotCreateLogs
|
|
6701
|
-
});
|
|
6997
|
+
async getImage() {
|
|
6998
|
+
return this.image;
|
|
6702
6999
|
}
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
return client.create(params, { timeout: options.createTimeout });
|
|
6708
|
-
}
|
|
6709
|
-
function validateDaytonaOptions(options) {
|
|
6710
|
-
if (options.image && options.snapshot) {
|
|
6711
|
-
throw new DaytonaSandboxError(
|
|
6712
|
-
'Daytona sandbox options cannot include both "image" and "snapshot". Choose one environment source.'
|
|
7000
|
+
async configure() {
|
|
7001
|
+
const ctx = this.engine.createInstallerContext(
|
|
7002
|
+
this.context.containerId,
|
|
7003
|
+
this.image
|
|
6713
7004
|
);
|
|
7005
|
+
for (const installer of this.installers) {
|
|
7006
|
+
await installer.install(ctx);
|
|
7007
|
+
}
|
|
6714
7008
|
}
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
7009
|
+
};
|
|
7010
|
+
var ContainerfileStrategy = class extends ContainerSandboxStrategy {
|
|
7011
|
+
dockerfile;
|
|
7012
|
+
dockerContext;
|
|
7013
|
+
showBuildLogs;
|
|
7014
|
+
identity;
|
|
7015
|
+
constructor(opts, engine, mode) {
|
|
7016
|
+
super(opts, engine);
|
|
7017
|
+
this.dockerfile = mode.dockerfile;
|
|
7018
|
+
this.dockerContext = mode.context;
|
|
7019
|
+
this.showBuildLogs = mode.showBuildLogs;
|
|
7020
|
+
this.identity = mode.identity;
|
|
6719
7021
|
}
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
)
|
|
7022
|
+
async getImage() {
|
|
7023
|
+
const content = this.dockerfile.includes("\n") ? this.dockerfile : readFileSync(this.dockerfile, "utf-8");
|
|
7024
|
+
const tag = `sandbox-${createHash("sha256").update(content).update(this.identity ?? "").digest("hex").slice(0, 12)}`;
|
|
7025
|
+
if (!await this.engine.imageExists(tag)) {
|
|
7026
|
+
await this.engine.buildImage({
|
|
7027
|
+
tag,
|
|
7028
|
+
dockerfile: this.dockerfile,
|
|
7029
|
+
context: this.dockerContext,
|
|
7030
|
+
showBuildLogs: this.showBuildLogs,
|
|
7031
|
+
identity: this.identity
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
return tag;
|
|
6724
7035
|
}
|
|
6725
|
-
|
|
6726
|
-
return;
|
|
7036
|
+
async configure() {
|
|
6727
7037
|
}
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
"
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
"
|
|
6745
|
-
|
|
7038
|
+
};
|
|
7039
|
+
|
|
7040
|
+
// packages/context/src/lib/sandbox/apple-container-sandbox.ts
|
|
7041
|
+
var CLI = "container";
|
|
7042
|
+
var WORKDIR = "/workspace";
|
|
7043
|
+
function isAppleContainerfileOptions(opts) {
|
|
7044
|
+
return "dockerfile" in opts;
|
|
7045
|
+
}
|
|
7046
|
+
function buildMountArg(volume) {
|
|
7047
|
+
const readOnly = volume.readOnly !== false;
|
|
7048
|
+
const parts = volume.type === "bind" ? [
|
|
7049
|
+
"type=bind",
|
|
7050
|
+
`source=${volume.hostPath}`,
|
|
7051
|
+
`target=${volume.containerPath}`
|
|
7052
|
+
] : [
|
|
7053
|
+
// Named volumes mount as virtiofs; `container` has no `type=volume`.
|
|
7054
|
+
"type=virtiofs",
|
|
7055
|
+
`source=${volume.name}`,
|
|
7056
|
+
`target=${volume.containerPath}`
|
|
6746
7057
|
];
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
});
|
|
6750
|
-
if (present.length > 0) {
|
|
6751
|
-
throw new DaytonaSandboxError(
|
|
6752
|
-
`Daytona sandbox options cannot combine "sandboxId" with creation options: ${present.join(", ")}`
|
|
6753
|
-
);
|
|
7058
|
+
if (readOnly) {
|
|
7059
|
+
parts.push("readonly");
|
|
6754
7060
|
}
|
|
7061
|
+
return parts.join(",");
|
|
6755
7062
|
}
|
|
6756
|
-
function
|
|
6757
|
-
const
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
7063
|
+
function getCliErrorMessage(error) {
|
|
7064
|
+
const err = error;
|
|
7065
|
+
return err.stderr?.trim() || err.stdout?.trim() || err.message || String(error);
|
|
7066
|
+
}
|
|
7067
|
+
function safeParseArray(stdout) {
|
|
7068
|
+
try {
|
|
7069
|
+
const parsed = JSON.parse(stdout || "[]");
|
|
7070
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
7071
|
+
} catch {
|
|
7072
|
+
return [];
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
function readContainerStatus(entry) {
|
|
7076
|
+
const status = String(
|
|
7077
|
+
entry?.status ?? ""
|
|
7078
|
+
).toLowerCase();
|
|
7079
|
+
if (status === "running" || status === "booted") return "running";
|
|
7080
|
+
if (status === "stopped") return "stopped";
|
|
7081
|
+
throw new AppleContainerSandboxError(
|
|
7082
|
+
`Container is in an unexpected state "${status}"`
|
|
7083
|
+
);
|
|
7084
|
+
}
|
|
7085
|
+
var appleEngine = {
|
|
7086
|
+
cli: CLI,
|
|
7087
|
+
runArgs(image, containerId, opts) {
|
|
7088
|
+
const { memory = "1024M", cpus = 2 } = opts.resources ?? {};
|
|
7089
|
+
const args = [
|
|
7090
|
+
"run",
|
|
7091
|
+
"--detach",
|
|
7092
|
+
"--rm",
|
|
7093
|
+
"--name",
|
|
7094
|
+
containerId,
|
|
7095
|
+
"--memory",
|
|
7096
|
+
memory,
|
|
7097
|
+
"--cpus",
|
|
7098
|
+
String(cpus)
|
|
7099
|
+
];
|
|
7100
|
+
if (opts.arch) {
|
|
7101
|
+
args.push("--arch", opts.arch);
|
|
7102
|
+
}
|
|
7103
|
+
for (const [key, value] of Object.entries(opts.env ?? {})) {
|
|
7104
|
+
args.push("--env", `${key}=${value}`);
|
|
7105
|
+
}
|
|
7106
|
+
for (const volume of opts.volumes ?? []) {
|
|
7107
|
+
args.push("--mount", buildMountArg(volume));
|
|
7108
|
+
}
|
|
7109
|
+
args.push(image);
|
|
7110
|
+
if (opts.command === void 0) {
|
|
7111
|
+
args.push("tail", "-f", "/dev/null");
|
|
7112
|
+
} else if (opts.command !== null) {
|
|
7113
|
+
args.push(...opts.command);
|
|
7114
|
+
}
|
|
7115
|
+
return args;
|
|
7116
|
+
},
|
|
7117
|
+
execArgs(containerId, command, options) {
|
|
7118
|
+
const flags = ["--cwd", options?.cwd || WORKDIR];
|
|
7119
|
+
if (options?.env) {
|
|
7120
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
7121
|
+
if (key.length === 0 || key.includes("=")) {
|
|
7122
|
+
throw new AppleContainerSandboxError(
|
|
7123
|
+
`Invalid environment variable key: "${key}"`
|
|
7124
|
+
);
|
|
7125
|
+
}
|
|
7126
|
+
flags.push("--env", `${key}=${value}`);
|
|
7127
|
+
}
|
|
6761
7128
|
}
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
7129
|
+
return ["exec", ...flags, containerId, "sh", "-c", command];
|
|
7130
|
+
},
|
|
7131
|
+
inspectArgs(containerId) {
|
|
7132
|
+
return ["inspect", containerId];
|
|
7133
|
+
},
|
|
7134
|
+
mountArg: buildMountArg,
|
|
7135
|
+
parseStatus(stdout) {
|
|
7136
|
+
const entries = safeParseArray(stdout);
|
|
7137
|
+
if (entries.length === 0) return "absent";
|
|
7138
|
+
return readContainerStatus(entries[0]);
|
|
7139
|
+
},
|
|
7140
|
+
volumeCreateArgs(volume) {
|
|
7141
|
+
return ["volume", "create", volume.name];
|
|
7142
|
+
},
|
|
7143
|
+
errorMessage: getCliErrorMessage,
|
|
7144
|
+
isServiceDown(message2) {
|
|
7145
|
+
return /apiserver|not running|connection refused|could not connect|xpc/i.test(
|
|
7146
|
+
message2
|
|
7147
|
+
);
|
|
7148
|
+
},
|
|
7149
|
+
isMissingContainer(message2) {
|
|
7150
|
+
return /not found|no such/i.test(message2);
|
|
7151
|
+
},
|
|
7152
|
+
isMissingVolume(message2) {
|
|
7153
|
+
return /not found|no such/i.test(message2);
|
|
7154
|
+
},
|
|
7155
|
+
isNameConflict(message2) {
|
|
7156
|
+
return /already exists/i.test(message2);
|
|
7157
|
+
},
|
|
7158
|
+
async ensureWorkdir(containerId, workdir) {
|
|
7159
|
+
await spawn3(CLI, ["exec", containerId, "sh", "-c", `mkdir -p ${workdir}`]);
|
|
7160
|
+
},
|
|
7161
|
+
defaultImage: "docker.io/library/alpine:latest",
|
|
7162
|
+
createInstallerContext: createAppleInstallerContext,
|
|
7163
|
+
async imageExists(tag) {
|
|
6767
7164
|
try {
|
|
6768
|
-
|
|
6769
|
-
|
|
7165
|
+
await spawn3(CLI, ["image", "inspect", tag]);
|
|
7166
|
+
return true;
|
|
7167
|
+
} catch {
|
|
7168
|
+
return false;
|
|
7169
|
+
}
|
|
7170
|
+
},
|
|
7171
|
+
async buildImage(spec) {
|
|
7172
|
+
const archFlag = spec.identity ? ["--arch", spec.identity] : [];
|
|
7173
|
+
if (spec.dockerfile.includes("\n")) {
|
|
7174
|
+
const tempDir = await mkdtemp(join(tmpdir(), "sandbox-containerfile-"));
|
|
7175
|
+
const dockerfilePath = join(tempDir, "Dockerfile");
|
|
7176
|
+
await writeFile(dockerfilePath, spec.dockerfile);
|
|
7177
|
+
try {
|
|
7178
|
+
await runAppleBuild(
|
|
7179
|
+
["build", ...archFlag, "-t", spec.tag, "-f", dockerfilePath, tempDir],
|
|
7180
|
+
spec.showBuildLogs
|
|
7181
|
+
);
|
|
7182
|
+
} finally {
|
|
7183
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
6770
7184
|
}
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
7185
|
+
return;
|
|
7186
|
+
}
|
|
7187
|
+
await runAppleBuild(
|
|
7188
|
+
[
|
|
7189
|
+
"build",
|
|
7190
|
+
...archFlag,
|
|
7191
|
+
"-t",
|
|
7192
|
+
spec.tag,
|
|
7193
|
+
"-f",
|
|
7194
|
+
spec.dockerfile,
|
|
7195
|
+
spec.context
|
|
7196
|
+
],
|
|
7197
|
+
spec.showBuildLogs
|
|
7198
|
+
);
|
|
7199
|
+
},
|
|
7200
|
+
errors: {
|
|
7201
|
+
serviceNotAvailable: () => new ContainerServiceNotRunningError(),
|
|
7202
|
+
creation: (message2, image, cause) => new AppleContainerCreationError(message2, image, cause),
|
|
7203
|
+
generic: (message2, containerId) => new AppleContainerSandboxError(message2, containerId),
|
|
7204
|
+
volumePath: (source, containerPath, reason) => new AppleContainerVolumePathError(source, containerPath, reason),
|
|
7205
|
+
volumeInspect: (name, reason) => new AppleContainerVolumeInspectError(name, reason),
|
|
7206
|
+
volumeCreate: (name, reason) => new AppleContainerVolumeCreateError(name, reason),
|
|
7207
|
+
volumeRemove: (name, reason) => new AppleContainerVolumeRemoveError(name, reason)
|
|
7208
|
+
}
|
|
7209
|
+
};
|
|
7210
|
+
function createAppleInstallerContext(containerId, image) {
|
|
7211
|
+
const packageManager = isDebianBased(image) ? "apt-get" : "apk";
|
|
7212
|
+
let archPromise = null;
|
|
7213
|
+
const ensuredTools = /* @__PURE__ */ new Set();
|
|
7214
|
+
let aptUpdated = false;
|
|
7215
|
+
const exec = async (command) => {
|
|
7216
|
+
try {
|
|
7217
|
+
const result = await spawn3(CLI, [
|
|
7218
|
+
"exec",
|
|
7219
|
+
containerId,
|
|
7220
|
+
"sh",
|
|
7221
|
+
"-c",
|
|
7222
|
+
command
|
|
7223
|
+
]);
|
|
7224
|
+
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
6783
7225
|
} catch (error) {
|
|
6784
|
-
if (aborted) return abortedCommandResult();
|
|
6785
7226
|
const err = error;
|
|
6786
7227
|
return {
|
|
6787
7228
|
stdout: err.stdout ?? "",
|
|
6788
|
-
stderr: err.stderr ?? err.message ??
|
|
7229
|
+
stderr: err.stderr ?? err.message ?? "",
|
|
6789
7230
|
exitCode: err.exitCode ?? 1
|
|
6790
7231
|
};
|
|
6791
|
-
} finally {
|
|
6792
|
-
options?.signal?.removeEventListener("abort", abort);
|
|
6793
7232
|
}
|
|
6794
7233
|
};
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
try {
|
|
6805
|
-
const bytes = await sandbox.fs.downloadFile(path5);
|
|
6806
|
-
return Buffer.from(bytes).toString("utf-8");
|
|
6807
|
-
} catch (error) {
|
|
6808
|
-
throw new DaytonaCommandError(
|
|
6809
|
-
`Failed to read file "${path5}": ${toError(error).message}`,
|
|
6810
|
-
toError(error)
|
|
6811
|
-
);
|
|
6812
|
-
}
|
|
6813
|
-
},
|
|
6814
|
-
async writeFiles(files) {
|
|
6815
|
-
try {
|
|
6816
|
-
for (const dir of uniqueParentDirectories(files.map((f) => f.path))) {
|
|
6817
|
-
const result = await executeCommand(`mkdir -p ${shellQuote(dir)}`);
|
|
6818
|
-
if (result.exitCode !== 0) {
|
|
6819
|
-
throw new DaytonaCommandError(
|
|
6820
|
-
`Failed to create directory "${dir}": ${result.stderr}`
|
|
6821
|
-
);
|
|
6822
|
-
}
|
|
7234
|
+
const arch = async () => {
|
|
7235
|
+
if (!archPromise) {
|
|
7236
|
+
const attempt = (async () => {
|
|
7237
|
+
const result = await exec("uname -m");
|
|
7238
|
+
if (result.exitCode !== 0) {
|
|
7239
|
+
throw new AppleContainerSandboxError(
|
|
7240
|
+
`Failed to detect container architecture: ${result.stderr}`,
|
|
7241
|
+
containerId
|
|
7242
|
+
);
|
|
6823
7243
|
}
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
7244
|
+
return result.stdout.trim();
|
|
7245
|
+
})();
|
|
7246
|
+
archPromise = attempt.catch((err) => {
|
|
7247
|
+
archPromise = null;
|
|
7248
|
+
throw err;
|
|
7249
|
+
});
|
|
7250
|
+
}
|
|
7251
|
+
return archPromise;
|
|
7252
|
+
};
|
|
7253
|
+
const installPackages = async (packages) => {
|
|
7254
|
+
if (packages.length === 0) return;
|
|
7255
|
+
const quoted = packages.map(shellQuote).join(" ");
|
|
7256
|
+
let cmd;
|
|
7257
|
+
if (packageManager === "apt-get") {
|
|
7258
|
+
cmd = aptUpdated ? `apt-get install -y ${quoted}` : `apt-get update && apt-get install -y ${quoted}`;
|
|
7259
|
+
} else {
|
|
7260
|
+
cmd = `apk add --no-cache ${quoted}`;
|
|
7261
|
+
}
|
|
7262
|
+
const result = await exec(cmd);
|
|
7263
|
+
if (result.exitCode !== 0) {
|
|
7264
|
+
throw new PackageInstallError(
|
|
7265
|
+
packages,
|
|
7266
|
+
image,
|
|
7267
|
+
packageManager,
|
|
7268
|
+
result.stderr,
|
|
7269
|
+
containerId
|
|
7270
|
+
);
|
|
7271
|
+
}
|
|
7272
|
+
if (packageManager === "apt-get") aptUpdated = true;
|
|
7273
|
+
};
|
|
7274
|
+
const ensureTool = async (checkName, installName) => {
|
|
7275
|
+
const cacheKey = installName ?? checkName;
|
|
7276
|
+
if (ensuredTools.has(cacheKey)) return;
|
|
7277
|
+
const check = await exec(`which ${shellQuote(checkName)}`);
|
|
7278
|
+
if (check.exitCode === 0) {
|
|
7279
|
+
ensuredTools.add(cacheKey);
|
|
7280
|
+
return;
|
|
7281
|
+
}
|
|
7282
|
+
await installPackages([installName ?? checkName]);
|
|
7283
|
+
ensuredTools.add(cacheKey);
|
|
7284
|
+
};
|
|
7285
|
+
return {
|
|
7286
|
+
containerId,
|
|
7287
|
+
image,
|
|
7288
|
+
packageManager,
|
|
7289
|
+
arch,
|
|
7290
|
+
exec,
|
|
7291
|
+
installPackages,
|
|
7292
|
+
ensureTool
|
|
7293
|
+
};
|
|
7294
|
+
}
|
|
7295
|
+
async function runAppleBuild(args, showBuildLogs) {
|
|
7296
|
+
try {
|
|
7297
|
+
if (showBuildLogs) {
|
|
7298
|
+
await runStreamed(CLI, args);
|
|
7299
|
+
} else {
|
|
7300
|
+
await spawn3(CLI, args);
|
|
7301
|
+
}
|
|
7302
|
+
} catch (error) {
|
|
7303
|
+
throw new AppleContainerImageBuildError(getCliErrorMessage(error));
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
function runStreamed(command, args) {
|
|
7307
|
+
return new Promise((resolve4, reject) => {
|
|
7308
|
+
const child = childSpawn2(command, args, { stdio: "inherit" });
|
|
7309
|
+
child.once("error", reject);
|
|
7310
|
+
child.once(
|
|
7311
|
+
"exit",
|
|
7312
|
+
(code) => code === 0 ? resolve4() : reject(
|
|
7313
|
+
new Error(
|
|
7314
|
+
`${command} build exited with code ${code} (see build output above)`
|
|
7315
|
+
)
|
|
7316
|
+
)
|
|
7317
|
+
);
|
|
7318
|
+
});
|
|
7319
|
+
}
|
|
7320
|
+
async function createAppleContainerSandbox(options = {}) {
|
|
7321
|
+
if (isAppleContainerfileOptions(options)) {
|
|
7322
|
+
return new ContainerfileStrategy(options, appleEngine, {
|
|
7323
|
+
dockerfile: options.dockerfile,
|
|
7324
|
+
context: options.context ?? ".",
|
|
7325
|
+
showBuildLogs: options.showBuildLogs ?? false,
|
|
7326
|
+
identity: options.arch
|
|
7327
|
+
}).create();
|
|
7328
|
+
}
|
|
7329
|
+
return new RuntimeStrategy(options, appleEngine, {
|
|
7330
|
+
image: options.image ?? appleEngine.defaultImage,
|
|
7331
|
+
installers: options.installers ?? []
|
|
7332
|
+
}).create();
|
|
7333
|
+
}
|
|
7334
|
+
async function useAppleContainerSandbox(options, fn) {
|
|
7335
|
+
const sandbox = await createAppleContainerSandbox(options);
|
|
7336
|
+
try {
|
|
7337
|
+
return await fn(sandbox);
|
|
7338
|
+
} finally {
|
|
7339
|
+
await sandbox.dispose();
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
|
|
7343
|
+
// packages/context/src/lib/sandbox/bash-exception.ts
|
|
7344
|
+
var BashException = class extends Error {
|
|
7345
|
+
};
|
|
7346
|
+
|
|
7347
|
+
// packages/context/src/lib/sandbox/bash-meta.ts
|
|
7348
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
7349
|
+
var store = new AsyncLocalStorage2();
|
|
7350
|
+
function runWithBashMeta(fn) {
|
|
7351
|
+
return store.run({ hidden: {} }, fn);
|
|
7352
|
+
}
|
|
7353
|
+
function useBashMeta() {
|
|
7354
|
+
const state = store.getStore();
|
|
7355
|
+
if (!state) return null;
|
|
7356
|
+
return {
|
|
7357
|
+
setHidden(patch) {
|
|
7358
|
+
state.hidden = { ...state.hidden, ...patch };
|
|
6838
7359
|
},
|
|
6839
|
-
|
|
7360
|
+
setReminder(text) {
|
|
7361
|
+
state.reminder = text;
|
|
6840
7362
|
},
|
|
6841
|
-
|
|
6842
|
-
|
|
7363
|
+
clearReminder() {
|
|
7364
|
+
state.reminder = void 0;
|
|
6843
7365
|
}
|
|
6844
7366
|
};
|
|
6845
7367
|
}
|
|
6846
|
-
function
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
7368
|
+
function readBashMeta() {
|
|
7369
|
+
return store.getStore();
|
|
7370
|
+
}
|
|
7371
|
+
|
|
7372
|
+
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
7373
|
+
import { tool as tool2 } from "ai";
|
|
7374
|
+
import {
|
|
7375
|
+
createBashTool as externalCreateBashTool
|
|
7376
|
+
} from "bash-tool";
|
|
7377
|
+
import z3 from "zod";
|
|
7378
|
+
|
|
7379
|
+
// packages/context/src/lib/sandbox/upload-skills.ts
|
|
7380
|
+
import * as path3 from "node:path";
|
|
7381
|
+
|
|
7382
|
+
// packages/context/src/lib/skills/loader.ts
|
|
7383
|
+
import * as fs from "node:fs";
|
|
7384
|
+
import * as path from "node:path";
|
|
7385
|
+
import YAML from "yaml";
|
|
7386
|
+
function parseFrontmatter(content) {
|
|
7387
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
7388
|
+
const match = content.match(frontmatterRegex);
|
|
7389
|
+
if (!match) {
|
|
7390
|
+
throw new Error("Invalid SKILL.md: missing or malformed frontmatter");
|
|
7391
|
+
}
|
|
7392
|
+
const [, yamlContent, body] = match;
|
|
7393
|
+
const frontmatter = YAML.parse(yamlContent);
|
|
7394
|
+
if (!frontmatter.name || typeof frontmatter.name !== "string") {
|
|
7395
|
+
throw new Error('Invalid SKILL.md: frontmatter must have a "name" field');
|
|
7396
|
+
}
|
|
7397
|
+
if (!frontmatter.description || typeof frontmatter.description !== "string") {
|
|
7398
|
+
throw new Error(
|
|
7399
|
+
'Invalid SKILL.md: frontmatter must have a "description" field'
|
|
7400
|
+
);
|
|
7401
|
+
}
|
|
6859
7402
|
return {
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
exit
|
|
7403
|
+
frontmatter,
|
|
7404
|
+
body: body.trim()
|
|
6863
7405
|
};
|
|
6864
7406
|
}
|
|
6865
|
-
|
|
6866
|
-
const
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
} = args;
|
|
6875
|
-
let sessionCreated = false;
|
|
6876
|
-
let aborted = signal?.aborted ?? false;
|
|
6877
|
-
let resolveAbort;
|
|
6878
|
-
const abortPromise = new Promise((resolve4) => {
|
|
6879
|
-
resolveAbort = () => resolve4("aborted");
|
|
6880
|
-
});
|
|
6881
|
-
const abort = () => {
|
|
6882
|
-
aborted = true;
|
|
6883
|
-
resolveAbort?.();
|
|
6884
|
-
if (sessionCreated) {
|
|
6885
|
-
sandbox.process.deleteSession(sessionId).catch(() => {
|
|
6886
|
-
});
|
|
6887
|
-
}
|
|
7407
|
+
function loadSkillMetadata(skillMdPath) {
|
|
7408
|
+
const content = fs.readFileSync(skillMdPath, "utf-8");
|
|
7409
|
+
const parsed = parseFrontmatter(content);
|
|
7410
|
+
const skillDir = path.dirname(skillMdPath);
|
|
7411
|
+
return {
|
|
7412
|
+
name: parsed.frontmatter.name,
|
|
7413
|
+
description: parsed.frontmatter.description,
|
|
7414
|
+
path: skillDir,
|
|
7415
|
+
skillMdPath
|
|
6888
7416
|
};
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
7417
|
+
}
|
|
7418
|
+
function discoverSkillsInDirectory(directory) {
|
|
7419
|
+
const skills2 = [];
|
|
7420
|
+
const expandedDir = directory.startsWith("~") ? path.join(process.env.HOME || "", directory.slice(1)) : directory;
|
|
7421
|
+
if (!fs.existsSync(expandedDir)) {
|
|
7422
|
+
return skills2;
|
|
6892
7423
|
}
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
},
|
|
6904
|
-
commandTimeout
|
|
6905
|
-
);
|
|
6906
|
-
const commandId = response.cmdId;
|
|
6907
|
-
if (!commandId) {
|
|
6908
|
-
throw new DaytonaCommandError(
|
|
6909
|
-
"Daytona did not return a command id for the spawned session command."
|
|
6910
|
-
);
|
|
7424
|
+
const entries = fs.readdirSync(expandedDir, { withFileTypes: true });
|
|
7425
|
+
for (const entry of entries) {
|
|
7426
|
+
if (!entry.isDirectory()) continue;
|
|
7427
|
+
const skillMdPath = path.join(expandedDir, entry.name, "SKILL.md");
|
|
7428
|
+
if (!fs.existsSync(skillMdPath)) continue;
|
|
7429
|
+
try {
|
|
7430
|
+
const metadata = loadSkillMetadata(skillMdPath);
|
|
7431
|
+
skills2.push(metadata);
|
|
7432
|
+
} catch (error) {
|
|
7433
|
+
console.warn(`Warning: Failed to load skill at ${skillMdPath}:`, error);
|
|
6911
7434
|
}
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
7435
|
+
}
|
|
7436
|
+
return skills2;
|
|
7437
|
+
}
|
|
7438
|
+
|
|
7439
|
+
// packages/context/src/lib/sandbox/walk.ts
|
|
7440
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
7441
|
+
import * as path2 from "node:path";
|
|
7442
|
+
function expandHome(input) {
|
|
7443
|
+
if (!input.startsWith("~")) return input;
|
|
7444
|
+
const home = process.env.HOME ?? "";
|
|
7445
|
+
return path2.join(home, input.slice(1));
|
|
7446
|
+
}
|
|
7447
|
+
async function walkDirectory(root) {
|
|
7448
|
+
const absoluteRoot = path2.resolve(expandHome(root));
|
|
7449
|
+
const files = [];
|
|
7450
|
+
async function walk(current) {
|
|
7451
|
+
let entries;
|
|
7452
|
+
try {
|
|
7453
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
7454
|
+
} catch {
|
|
7455
|
+
return;
|
|
6929
7456
|
}
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
const err = toError(error);
|
|
6942
|
-
stdout.error(err);
|
|
6943
|
-
stderr.error(err);
|
|
6944
|
-
throw err;
|
|
6945
|
-
} finally {
|
|
6946
|
-
if (signal) {
|
|
6947
|
-
signal.removeEventListener("abort", abort);
|
|
7457
|
+
for (const entry of entries) {
|
|
7458
|
+
if (entry.name.startsWith(".")) continue;
|
|
7459
|
+
if (entry.isSymbolicLink()) continue;
|
|
7460
|
+
const entryPath = path2.join(current, entry.name);
|
|
7461
|
+
if (entry.isDirectory()) {
|
|
7462
|
+
await walk(entryPath);
|
|
7463
|
+
} else if (entry.isFile()) {
|
|
7464
|
+
const content = await readFile(entryPath);
|
|
7465
|
+
const relative3 = path2.relative(absoluteRoot, entryPath).split(path2.sep).join("/");
|
|
7466
|
+
files.push({ path: entryPath, relativePath: relative3, content });
|
|
7467
|
+
}
|
|
6948
7468
|
}
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
7469
|
+
}
|
|
7470
|
+
await walk(absoluteRoot);
|
|
7471
|
+
return files;
|
|
7472
|
+
}
|
|
7473
|
+
|
|
7474
|
+
// packages/context/src/lib/sandbox/upload-skills.ts
|
|
7475
|
+
function expandHome2(input) {
|
|
7476
|
+
if (!input.startsWith("~")) return input;
|
|
7477
|
+
const home = process.env.HOME ?? "";
|
|
7478
|
+
return path3.join(home, input.slice(1));
|
|
7479
|
+
}
|
|
7480
|
+
function joinSandbox(base, relative3) {
|
|
7481
|
+
if (!relative3) return base;
|
|
7482
|
+
const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
7483
|
+
return `${normalizedBase}/${relative3}`;
|
|
7484
|
+
}
|
|
7485
|
+
function discoverSkillMappings(inputs) {
|
|
7486
|
+
if (inputs.length === 0) return [];
|
|
7487
|
+
const discoveredByName = /* @__PURE__ */ new Map();
|
|
7488
|
+
for (const { host, sandbox: sandboxBase } of inputs) {
|
|
7489
|
+
const absoluteHost = path3.resolve(expandHome2(host));
|
|
7490
|
+
for (const skill of discoverSkillsInDirectory(host)) {
|
|
7491
|
+
const absoluteSkillMd = path3.resolve(skill.skillMdPath);
|
|
7492
|
+
const relative3 = path3.relative(absoluteHost, absoluteSkillMd).split(path3.sep).join("/");
|
|
7493
|
+
discoveredByName.set(skill.name, {
|
|
7494
|
+
name: skill.name,
|
|
7495
|
+
description: skill.description,
|
|
7496
|
+
host: skill.skillMdPath,
|
|
7497
|
+
sandbox: joinSandbox(sandboxBase, relative3)
|
|
6953
7498
|
});
|
|
6954
7499
|
}
|
|
6955
7500
|
}
|
|
7501
|
+
return Array.from(discoveredByName.values());
|
|
6956
7502
|
}
|
|
6957
|
-
async function
|
|
6958
|
-
|
|
6959
|
-
const
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
const
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
`Daytona session command "${commandId}" logs closed before an exit code became available.`
|
|
6969
|
-
);
|
|
7503
|
+
async function uploadSkills(sandbox, inputs) {
|
|
7504
|
+
if (inputs.length === 0) return [];
|
|
7505
|
+
const skills2 = discoverSkillMappings(inputs);
|
|
7506
|
+
const filesToUpload = [];
|
|
7507
|
+
for (const { host, sandbox: sandboxBase } of inputs) {
|
|
7508
|
+
const walked = await walkDirectory(host);
|
|
7509
|
+
for (const file of walked) {
|
|
7510
|
+
filesToUpload.push({
|
|
7511
|
+
path: joinSandbox(sandboxBase, file.relativePath),
|
|
7512
|
+
content: file.content
|
|
7513
|
+
});
|
|
6970
7514
|
}
|
|
6971
|
-
await delay(DAYTONA_EXIT_POLL_INTERVAL_MS);
|
|
6972
7515
|
}
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
if (commandTimeout === void 0 || commandTimeout === 0) {
|
|
6976
|
-
return DAYTONA_EXIT_POLL_TIMEOUT_MS;
|
|
7516
|
+
if (filesToUpload.length > 0) {
|
|
7517
|
+
await sandbox.writeFiles(filesToUpload);
|
|
6977
7518
|
}
|
|
6978
|
-
return
|
|
6979
|
-
}
|
|
6980
|
-
function delay(ms) {
|
|
6981
|
-
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
7519
|
+
return skills2;
|
|
6982
7520
|
}
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
7521
|
+
|
|
7522
|
+
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
7523
|
+
var REASONING_INSTRUCTION = 'Every bash tool call must include a brief non-empty "reasoning" input explaining why the command is needed.';
|
|
7524
|
+
function withBashExceptionCatch(sandbox) {
|
|
6987
7525
|
return {
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
7526
|
+
...sandbox,
|
|
7527
|
+
async executeCommand(command, options) {
|
|
7528
|
+
try {
|
|
7529
|
+
return await sandbox.executeCommand(command, options);
|
|
7530
|
+
} catch (err) {
|
|
7531
|
+
if (err instanceof BashException) return err.format();
|
|
7532
|
+
throw err;
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
};
|
|
7536
|
+
}
|
|
7537
|
+
async function createBashTool(options) {
|
|
7538
|
+
const {
|
|
7539
|
+
skills: skillInputs = [],
|
|
7540
|
+
extraInstructions,
|
|
7541
|
+
sandbox: backend,
|
|
7542
|
+
destination,
|
|
7543
|
+
...rest
|
|
7544
|
+
} = options;
|
|
7545
|
+
const sandbox = withAbortSignal(withBashExceptionCatch(backend));
|
|
7546
|
+
const combinedInstructions = [extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
|
|
7547
|
+
const toolkit = await externalCreateBashTool({
|
|
7548
|
+
...rest,
|
|
7549
|
+
sandbox,
|
|
7550
|
+
destination,
|
|
7551
|
+
extraInstructions: combinedInstructions
|
|
7552
|
+
});
|
|
7553
|
+
const upstreamBash = toolkit.bash;
|
|
7554
|
+
const originalExecute = upstreamBash.execute;
|
|
7555
|
+
const toolBuilder = tool2;
|
|
7556
|
+
const bash = toolBuilder({
|
|
7557
|
+
...upstreamBash,
|
|
7558
|
+
description: upstreamBash.description ?? "",
|
|
7559
|
+
inputSchema: z3.object({
|
|
7560
|
+
command: z3.string().describe("The bash command to execute"),
|
|
7561
|
+
reasoning: z3.string().trim().describe("Brief reason for executing this command")
|
|
7562
|
+
}),
|
|
7563
|
+
execute: async ({ command }, execOptions) => {
|
|
7564
|
+
if (!originalExecute) {
|
|
7565
|
+
throw new Error("bash tool execution is not available");
|
|
6991
7566
|
}
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7567
|
+
const { abortSignal } = execOptions;
|
|
7568
|
+
return runWithAbortSignal(
|
|
7569
|
+
abortSignal,
|
|
7570
|
+
() => runWithBashMeta(async () => {
|
|
7571
|
+
const result = await originalExecute({ command }, execOptions);
|
|
7572
|
+
const state = readBashMeta();
|
|
7573
|
+
if (!state) return result;
|
|
7574
|
+
const hasHidden = Object.keys(state.hidden).length > 0;
|
|
7575
|
+
const hasReminder = state.reminder !== void 0;
|
|
7576
|
+
if (!hasHidden && !hasReminder) return result;
|
|
7577
|
+
return {
|
|
7578
|
+
...result,
|
|
7579
|
+
...hasHidden ? { meta: state.hidden } : {},
|
|
7580
|
+
...hasReminder ? { reminder: state.reminder } : {}
|
|
7581
|
+
};
|
|
7582
|
+
})
|
|
7583
|
+
);
|
|
7001
7584
|
},
|
|
7002
|
-
|
|
7003
|
-
if (
|
|
7004
|
-
|
|
7005
|
-
|
|
7585
|
+
toModelOutput: ({ output }) => {
|
|
7586
|
+
if (typeof output !== "object" || output === null) {
|
|
7587
|
+
return { type: "json", value: output };
|
|
7588
|
+
}
|
|
7589
|
+
const { meta: _meta, ...visible } = output;
|
|
7590
|
+
return { type: "json", value: visible };
|
|
7591
|
+
}
|
|
7592
|
+
});
|
|
7593
|
+
const upstreamWriteFile = toolkit.tools.writeFile;
|
|
7594
|
+
const originalWriteExecute = upstreamWriteFile.execute;
|
|
7595
|
+
const writeFileBuilder = tool2;
|
|
7596
|
+
const writeFile2 = writeFileBuilder({
|
|
7597
|
+
...upstreamWriteFile,
|
|
7598
|
+
execute: async (input, options2) => {
|
|
7599
|
+
if (!originalWriteExecute) {
|
|
7600
|
+
throw new Error("writeFile tool execution is not available");
|
|
7601
|
+
}
|
|
7602
|
+
try {
|
|
7603
|
+
return await originalWriteExecute(input, options2);
|
|
7604
|
+
} catch (err) {
|
|
7605
|
+
if (err instanceof BashException) return err.format();
|
|
7606
|
+
throw err;
|
|
7607
|
+
}
|
|
7006
7608
|
}
|
|
7609
|
+
});
|
|
7610
|
+
const skills2 = await uploadSkills(backend, skillInputs);
|
|
7611
|
+
return {
|
|
7612
|
+
...toolkit,
|
|
7613
|
+
sandbox,
|
|
7614
|
+
bash,
|
|
7615
|
+
tools: { ...toolkit.tools, bash, writeFile: writeFile2 },
|
|
7616
|
+
skills: skills2
|
|
7007
7617
|
};
|
|
7008
7618
|
}
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7619
|
+
|
|
7620
|
+
// packages/context/src/lib/sandbox/binary-bridges.ts
|
|
7621
|
+
import { existsSync as existsSync3 } from "fs";
|
|
7622
|
+
import { defineCommand } from "just-bash";
|
|
7623
|
+
import spawn4 from "nano-spawn";
|
|
7624
|
+
import * as path4 from "path";
|
|
7625
|
+
function createBinaryBridges(...binaries) {
|
|
7626
|
+
return binaries.map((input) => {
|
|
7627
|
+
const config = typeof input === "string" ? { name: input } : input;
|
|
7628
|
+
const { name, binaryPath = name, allowedArgs } = config;
|
|
7629
|
+
return defineCommand(name, async (args, ctx) => {
|
|
7630
|
+
if (allowedArgs) {
|
|
7631
|
+
const invalidArg = args.find((arg) => !allowedArgs.test(arg));
|
|
7632
|
+
if (invalidArg) {
|
|
7633
|
+
return {
|
|
7634
|
+
stdout: "",
|
|
7635
|
+
stderr: `${name}: argument '${invalidArg}' not allowed by security policy`,
|
|
7636
|
+
exitCode: 1
|
|
7637
|
+
};
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
try {
|
|
7641
|
+
const realCwd = resolveRealCwd(ctx);
|
|
7642
|
+
const resolvedArgs = args.map((arg) => {
|
|
7643
|
+
if (arg.startsWith("-")) {
|
|
7644
|
+
return arg;
|
|
7645
|
+
}
|
|
7646
|
+
const hasExtension = path4.extname(arg) !== "";
|
|
7647
|
+
const hasPathSep = arg.includes(path4.sep) || arg.includes("/");
|
|
7648
|
+
const isRelative = arg.startsWith(".");
|
|
7649
|
+
if (hasExtension || hasPathSep || isRelative) {
|
|
7650
|
+
return path4.resolve(realCwd, arg);
|
|
7651
|
+
}
|
|
7652
|
+
return arg;
|
|
7653
|
+
});
|
|
7654
|
+
const mergedEnv = {
|
|
7655
|
+
...process.env,
|
|
7656
|
+
...Object.fromEntries(ctx.env),
|
|
7657
|
+
// ctx.env is a Map, convert to object
|
|
7658
|
+
PATH: process.env.PATH
|
|
7659
|
+
// Always use host PATH for binary bridges
|
|
7660
|
+
};
|
|
7661
|
+
const result = await spawn4(binaryPath, resolvedArgs, {
|
|
7662
|
+
cwd: realCwd,
|
|
7663
|
+
env: mergedEnv
|
|
7664
|
+
});
|
|
7665
|
+
return {
|
|
7666
|
+
stdout: result.stdout,
|
|
7667
|
+
stderr: result.stderr,
|
|
7668
|
+
exitCode: 0
|
|
7669
|
+
};
|
|
7670
|
+
} catch (error) {
|
|
7671
|
+
if (error && typeof error === "object") {
|
|
7672
|
+
const err = error;
|
|
7673
|
+
const cause = err.cause;
|
|
7674
|
+
if (cause?.code === "ENOENT") {
|
|
7675
|
+
return {
|
|
7676
|
+
stdout: "",
|
|
7677
|
+
stderr: `${name}: ${binaryPath} not found`,
|
|
7678
|
+
exitCode: 127
|
|
7679
|
+
};
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
if (error && typeof error === "object" && "exitCode" in error) {
|
|
7683
|
+
const subprocessError = error;
|
|
7684
|
+
return {
|
|
7685
|
+
stdout: subprocessError.stdout ?? "",
|
|
7686
|
+
stderr: subprocessError.stderr ?? "",
|
|
7687
|
+
exitCode: subprocessError.exitCode ?? 1
|
|
7688
|
+
};
|
|
7689
|
+
}
|
|
7690
|
+
return {
|
|
7691
|
+
stdout: "",
|
|
7692
|
+
stderr: `${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
7693
|
+
exitCode: 127
|
|
7694
|
+
};
|
|
7695
|
+
}
|
|
7696
|
+
});
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7699
|
+
function resolveRealCwd(ctx) {
|
|
7700
|
+
const fs2 = ctx.fs;
|
|
7701
|
+
let realCwd;
|
|
7702
|
+
if (fs2.root) {
|
|
7703
|
+
realCwd = path4.join(fs2.root, ctx.cwd);
|
|
7704
|
+
} else if (typeof fs2.getMountPoint === "function" && typeof fs2.toRealPath === "function") {
|
|
7705
|
+
const real = fs2.toRealPath(ctx.cwd);
|
|
7706
|
+
realCwd = real ?? process.cwd();
|
|
7707
|
+
} else {
|
|
7708
|
+
realCwd = process.cwd();
|
|
7015
7709
|
}
|
|
7016
|
-
|
|
7017
|
-
|
|
7710
|
+
if (!existsSync3(realCwd)) {
|
|
7711
|
+
realCwd = process.cwd();
|
|
7018
7712
|
}
|
|
7019
|
-
|
|
7020
|
-
return `sh -lc ${shellQuote(`${exports}; ${cwdPrefix}${command}`)}`;
|
|
7713
|
+
return realCwd;
|
|
7021
7714
|
}
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7715
|
+
|
|
7716
|
+
// packages/context/src/lib/sandbox/daytona-sandbox.ts
|
|
7717
|
+
import "bash-tool";
|
|
7718
|
+
import { randomUUID } from "node:crypto";
|
|
7719
|
+
var DAYTONA_DEFAULT_DESTINATION = "/home/daytona";
|
|
7720
|
+
var DAYTONA_EXIT_POLL_INTERVAL_MS = 250;
|
|
7721
|
+
var DAYTONA_EXIT_POLL_TIMEOUT_MS = 3e4;
|
|
7722
|
+
var DaytonaSandboxError = class extends Error {
|
|
7723
|
+
constructor(message2, cause) {
|
|
7724
|
+
super(message2);
|
|
7725
|
+
this.name = "DaytonaSandboxError";
|
|
7726
|
+
this.cause = cause;
|
|
7027
7727
|
}
|
|
7028
|
-
}
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
}
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
dirs.add(path5.slice(0, index));
|
|
7728
|
+
};
|
|
7729
|
+
var DaytonaCreationError = class extends DaytonaSandboxError {
|
|
7730
|
+
constructor(message2, cause) {
|
|
7731
|
+
super(`Failed to create Daytona sandbox: ${message2}`, cause);
|
|
7732
|
+
this.name = "DaytonaCreationError";
|
|
7733
|
+
}
|
|
7734
|
+
};
|
|
7735
|
+
var DaytonaCommandError = class extends DaytonaSandboxError {
|
|
7736
|
+
constructor(message2, cause) {
|
|
7737
|
+
super(message2, cause);
|
|
7738
|
+
this.name = "DaytonaCommandError";
|
|
7739
|
+
}
|
|
7740
|
+
};
|
|
7741
|
+
async function createDaytonaSandbox(client, options = {}) {
|
|
7742
|
+
validateDaytonaOptions(options);
|
|
7743
|
+
const sdk = await import("@daytona/sdk");
|
|
7744
|
+
let sandbox;
|
|
7745
|
+
try {
|
|
7746
|
+
if (options.sandboxId !== void 0) {
|
|
7747
|
+
sandbox = await client.get(options.sandboxId);
|
|
7748
|
+
await startIfStopped(sandbox, options);
|
|
7749
|
+
} else {
|
|
7750
|
+
sandbox = await acquireReusedSandbox(client, options, sdk);
|
|
7052
7751
|
}
|
|
7752
|
+
} catch (error) {
|
|
7753
|
+
throw normalizeDaytonaError(error, sdk);
|
|
7053
7754
|
}
|
|
7054
|
-
return
|
|
7755
|
+
return createDaytonaSandboxMethods({
|
|
7756
|
+
sandbox,
|
|
7757
|
+
commandTimeout: options.commandTimeout
|
|
7758
|
+
});
|
|
7055
7759
|
}
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7760
|
+
var UNRECOVERABLE_SANDBOX_STATES = /* @__PURE__ */ new Set([
|
|
7761
|
+
"error",
|
|
7762
|
+
"build_failed",
|
|
7763
|
+
"destroyed"
|
|
7764
|
+
]);
|
|
7765
|
+
async function acquireReusedSandbox(client, options, sdk) {
|
|
7766
|
+
let existing;
|
|
7767
|
+
try {
|
|
7768
|
+
existing = await client.get(options.name);
|
|
7769
|
+
} catch (error) {
|
|
7770
|
+
if (error instanceof sdk.DaytonaNotFoundError) {
|
|
7771
|
+
return createSandbox(client, options);
|
|
7061
7772
|
}
|
|
7773
|
+
throw error;
|
|
7062
7774
|
}
|
|
7063
|
-
|
|
7775
|
+
if (existing.state && UNRECOVERABLE_SANDBOX_STATES.has(existing.state)) {
|
|
7776
|
+
await deleteSandboxQuietly(existing, options);
|
|
7777
|
+
return createSandbox(client, options);
|
|
7778
|
+
}
|
|
7779
|
+
await startIfStopped(existing, options);
|
|
7780
|
+
return existing;
|
|
7064
7781
|
}
|
|
7065
|
-
function
|
|
7066
|
-
|
|
7782
|
+
async function deleteSandboxQuietly(sandbox, options) {
|
|
7783
|
+
try {
|
|
7784
|
+
await sandbox.delete(options.deleteTimeout);
|
|
7785
|
+
} catch {
|
|
7786
|
+
}
|
|
7067
7787
|
}
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
import spawn3 from "nano-spawn";
|
|
7072
|
-
import { spawn as childSpawn } from "node:child_process";
|
|
7073
|
-
import { createHash } from "node:crypto";
|
|
7074
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
7075
|
-
import { Readable as Readable2 } from "node:stream";
|
|
7076
|
-
|
|
7077
|
-
// packages/context/src/lib/sandbox/docker-sandbox-errors.ts
|
|
7078
|
-
var DockerSandboxError = class extends Error {
|
|
7079
|
-
containerId;
|
|
7080
|
-
constructor(message2, containerId) {
|
|
7081
|
-
super(message2);
|
|
7082
|
-
this.name = "DockerSandboxError";
|
|
7083
|
-
this.containerId = containerId;
|
|
7788
|
+
async function startIfStopped(sandbox, options) {
|
|
7789
|
+
if (sandbox.state && sandbox.state !== "started") {
|
|
7790
|
+
await sandbox.start(options.startTimeout ?? options.createTimeout);
|
|
7084
7791
|
}
|
|
7085
|
-
}
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7792
|
+
}
|
|
7793
|
+
function normalizeDaytonaError(error, sdk) {
|
|
7794
|
+
const err = toError(error);
|
|
7795
|
+
if (err instanceof sdk.DaytonaError) {
|
|
7796
|
+
return err;
|
|
7090
7797
|
}
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7798
|
+
return new DaytonaCreationError(err.message, err);
|
|
7799
|
+
}
|
|
7800
|
+
function createSandbox(client, options) {
|
|
7801
|
+
const base = compactObject({
|
|
7802
|
+
name: options.name,
|
|
7803
|
+
user: options.user,
|
|
7804
|
+
language: options.language,
|
|
7805
|
+
envVars: options.envVars,
|
|
7806
|
+
labels: options.labels,
|
|
7807
|
+
public: options.public,
|
|
7808
|
+
autoStopInterval: options.autoStopInterval,
|
|
7809
|
+
autoArchiveInterval: options.autoArchiveInterval,
|
|
7810
|
+
autoDeleteInterval: options.autoDeleteInterval,
|
|
7811
|
+
volumes: options.volumes,
|
|
7812
|
+
networkBlockAll: options.networkBlockAll,
|
|
7813
|
+
networkAllowList: options.networkAllowList,
|
|
7814
|
+
ephemeral: options.ephemeral
|
|
7815
|
+
});
|
|
7816
|
+
if (options.image !== void 0) {
|
|
7817
|
+
const params2 = compactObject({
|
|
7818
|
+
...base,
|
|
7819
|
+
image: options.image,
|
|
7820
|
+
resources: options.resources
|
|
7821
|
+
});
|
|
7822
|
+
return client.create(params2, {
|
|
7823
|
+
timeout: options.createTimeout,
|
|
7824
|
+
onSnapshotCreateLogs: options.onSnapshotCreateLogs
|
|
7825
|
+
});
|
|
7100
7826
|
}
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7827
|
+
const params = compactObject({
|
|
7828
|
+
...base,
|
|
7829
|
+
snapshot: options.snapshot
|
|
7830
|
+
});
|
|
7831
|
+
return client.create(params, { timeout: options.createTimeout });
|
|
7832
|
+
}
|
|
7833
|
+
function validateDaytonaOptions(options) {
|
|
7834
|
+
if (options.image && options.snapshot) {
|
|
7835
|
+
throw new DaytonaSandboxError(
|
|
7836
|
+
'Daytona sandbox options cannot include both "image" and "snapshot". Choose one environment source.'
|
|
7111
7837
|
);
|
|
7112
|
-
this.name = "PackageInstallError";
|
|
7113
|
-
this.packages = packages;
|
|
7114
|
-
this.image = image;
|
|
7115
|
-
this.packageManager = packageManager;
|
|
7116
|
-
this.stderr = stderr;
|
|
7117
7838
|
}
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
source;
|
|
7122
|
-
reason;
|
|
7123
|
-
url;
|
|
7124
|
-
constructor(opts) {
|
|
7125
|
-
const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
|
|
7126
|
-
super(
|
|
7127
|
-
`Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
|
|
7128
|
-
opts.containerId
|
|
7839
|
+
if (options.resources && !options.image) {
|
|
7840
|
+
throw new DaytonaSandboxError(
|
|
7841
|
+
'Daytona sandbox options can only include "resources" when creating from "image". The Daytona SDK does not apply resources during default or snapshot creation.'
|
|
7129
7842
|
);
|
|
7130
|
-
this.name = "InstallError";
|
|
7131
|
-
this.target = opts.target;
|
|
7132
|
-
this.source = opts.source;
|
|
7133
|
-
this.reason = opts.reason;
|
|
7134
|
-
this.url = opts.url;
|
|
7135
|
-
}
|
|
7136
|
-
};
|
|
7137
|
-
var MissingRuntimeError = class extends DockerSandboxError {
|
|
7138
|
-
runtime;
|
|
7139
|
-
required;
|
|
7140
|
-
constructor(runtime, required, details, containerId) {
|
|
7141
|
-
const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
|
|
7142
|
-
super(details ? `${base} ${details}` : base, containerId);
|
|
7143
|
-
this.name = "MissingRuntimeError";
|
|
7144
|
-
this.runtime = runtime;
|
|
7145
|
-
this.required = required;
|
|
7146
7843
|
}
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
containerPath;
|
|
7151
|
-
reason;
|
|
7152
|
-
constructor(source, containerPath, reason) {
|
|
7153
|
-
super(
|
|
7154
|
-
`Invalid Docker volume path "${source}" -> "${containerPath}": ${reason}`
|
|
7844
|
+
if (options.sandboxId === void 0 && options.name === void 0) {
|
|
7845
|
+
throw new DaytonaSandboxError(
|
|
7846
|
+
'Daytona sandbox options require "name" (get-or-create) or "sandboxId" (attach). An unnamed sandbox cannot be reclaimed, since dispose() does not delete it.'
|
|
7155
7847
|
);
|
|
7156
|
-
this.name = "VolumePathError";
|
|
7157
|
-
this.source = source;
|
|
7158
|
-
this.containerPath = containerPath;
|
|
7159
|
-
this.reason = reason;
|
|
7160
|
-
}
|
|
7161
|
-
};
|
|
7162
|
-
var VolumeInspectError = class extends DockerSandboxError {
|
|
7163
|
-
volume;
|
|
7164
|
-
reason;
|
|
7165
|
-
constructor(volume, reason) {
|
|
7166
|
-
super(`Failed to inspect Docker volume "${volume}": ${reason}`);
|
|
7167
|
-
this.name = "VolumeInspectError";
|
|
7168
|
-
this.volume = volume;
|
|
7169
|
-
this.reason = reason;
|
|
7170
|
-
}
|
|
7171
|
-
};
|
|
7172
|
-
var VolumeCreateError = class extends DockerSandboxError {
|
|
7173
|
-
volume;
|
|
7174
|
-
reason;
|
|
7175
|
-
constructor(volume, reason) {
|
|
7176
|
-
super(`Failed to create Docker volume "${volume}": ${reason}`);
|
|
7177
|
-
this.name = "VolumeCreateError";
|
|
7178
|
-
this.volume = volume;
|
|
7179
|
-
this.reason = reason;
|
|
7180
|
-
}
|
|
7181
|
-
};
|
|
7182
|
-
var VolumeRemoveError = class extends DockerSandboxError {
|
|
7183
|
-
volume;
|
|
7184
|
-
reason;
|
|
7185
|
-
constructor(volume, reason) {
|
|
7186
|
-
super(`Failed to remove Docker volume "${volume}": ${reason}`);
|
|
7187
|
-
this.name = "VolumeRemoveError";
|
|
7188
|
-
this.volume = volume;
|
|
7189
|
-
this.reason = reason;
|
|
7190
7848
|
}
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
stderr;
|
|
7194
|
-
constructor(stderr) {
|
|
7195
|
-
super(`Dockerfile build failed: ${stderr}`);
|
|
7196
|
-
this.name = "DockerfileBuildError";
|
|
7197
|
-
this.stderr = stderr;
|
|
7849
|
+
if (!options.sandboxId) {
|
|
7850
|
+
return;
|
|
7198
7851
|
}
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7852
|
+
const creationOnlyFields = [
|
|
7853
|
+
"name",
|
|
7854
|
+
"user",
|
|
7855
|
+
"snapshot",
|
|
7856
|
+
"image",
|
|
7857
|
+
"language",
|
|
7858
|
+
"envVars",
|
|
7859
|
+
"labels",
|
|
7860
|
+
"public",
|
|
7861
|
+
"resources",
|
|
7862
|
+
"volumes",
|
|
7863
|
+
"networkAllowList",
|
|
7864
|
+
"networkBlockAll",
|
|
7865
|
+
"autoStopInterval",
|
|
7866
|
+
"autoArchiveInterval",
|
|
7867
|
+
"autoDeleteInterval",
|
|
7868
|
+
"ephemeral",
|
|
7869
|
+
"onSnapshotCreateLogs"
|
|
7870
|
+
];
|
|
7871
|
+
const present = creationOnlyFields.filter((field) => {
|
|
7872
|
+
return options[field] !== void 0;
|
|
7873
|
+
});
|
|
7874
|
+
if (present.length > 0) {
|
|
7875
|
+
throw new DaytonaSandboxError(
|
|
7876
|
+
`Daytona sandbox options cannot combine "sandboxId" with creation options: ${present.join(", ")}`
|
|
7877
|
+
);
|
|
7208
7878
|
}
|
|
7209
|
-
};
|
|
7210
|
-
|
|
7211
|
-
// packages/context/src/lib/sandbox/installers/installer.ts
|
|
7212
|
-
import "bash-tool";
|
|
7213
|
-
import spawn2 from "nano-spawn";
|
|
7214
|
-
var Installer = class {
|
|
7215
|
-
};
|
|
7216
|
-
function isDebianBased(image) {
|
|
7217
|
-
const lower = image.toLowerCase();
|
|
7218
|
-
if (lower.includes("alpine")) return false;
|
|
7219
|
-
const debianPatterns = ["debian", "ubuntu", "node", "python"];
|
|
7220
|
-
return debianPatterns.some((pattern) => lower.includes(pattern));
|
|
7221
7879
|
}
|
|
7222
|
-
function
|
|
7223
|
-
const
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7880
|
+
function createDaytonaSandboxMethods(args) {
|
|
7881
|
+
const { sandbox, commandTimeout } = args;
|
|
7882
|
+
const executeCommand = async (command, options) => {
|
|
7883
|
+
if (options?.signal?.aborted) {
|
|
7884
|
+
return abortedCommandResult();
|
|
7885
|
+
}
|
|
7886
|
+
let aborted = false;
|
|
7887
|
+
const abort = () => {
|
|
7888
|
+
aborted = true;
|
|
7889
|
+
};
|
|
7890
|
+
options?.signal?.addEventListener("abort", abort, { once: true });
|
|
7228
7891
|
try {
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7892
|
+
if (aborted || options?.signal?.aborted) {
|
|
7893
|
+
return abortedCommandResult();
|
|
7894
|
+
}
|
|
7895
|
+
const response = await sandbox.process.executeCommand(
|
|
7896
|
+
command,
|
|
7897
|
+
void 0,
|
|
7898
|
+
void 0,
|
|
7899
|
+
commandTimeout
|
|
7900
|
+
);
|
|
7901
|
+
if (aborted) return abortedCommandResult();
|
|
7902
|
+
return {
|
|
7903
|
+
stdout: response.result ?? response.artifacts?.stdout ?? "",
|
|
7904
|
+
stderr: "",
|
|
7905
|
+
exitCode: response.exitCode ?? 0
|
|
7906
|
+
};
|
|
7237
7907
|
} catch (error) {
|
|
7908
|
+
if (aborted) return abortedCommandResult();
|
|
7238
7909
|
const err = error;
|
|
7239
7910
|
return {
|
|
7240
7911
|
stdout: err.stdout ?? "",
|
|
7241
|
-
stderr: err.stderr ?? err.message ??
|
|
7912
|
+
stderr: err.stderr ?? err.message ?? String(error),
|
|
7242
7913
|
exitCode: err.exitCode ?? 1
|
|
7243
7914
|
};
|
|
7915
|
+
} finally {
|
|
7916
|
+
options?.signal?.removeEventListener("abort", abort);
|
|
7244
7917
|
}
|
|
7245
7918
|
};
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7919
|
+
return {
|
|
7920
|
+
executeCommand,
|
|
7921
|
+
spawn(command, options) {
|
|
7922
|
+
return spawnDaytonaProcess(sandbox, command, {
|
|
7923
|
+
...options,
|
|
7924
|
+
commandTimeout
|
|
7925
|
+
});
|
|
7926
|
+
},
|
|
7927
|
+
async readFile(path5) {
|
|
7928
|
+
try {
|
|
7929
|
+
const bytes = await sandbox.fs.downloadFile(path5);
|
|
7930
|
+
return Buffer.from(bytes).toString("utf-8");
|
|
7931
|
+
} catch (error) {
|
|
7932
|
+
throw new DaytonaCommandError(
|
|
7933
|
+
`Failed to read file "${path5}": ${toError(error).message}`,
|
|
7934
|
+
toError(error)
|
|
7935
|
+
);
|
|
7936
|
+
}
|
|
7937
|
+
},
|
|
7938
|
+
async writeFiles(files) {
|
|
7939
|
+
try {
|
|
7940
|
+
for (const dir of uniqueParentDirectories(files.map((f) => f.path))) {
|
|
7941
|
+
const result = await executeCommand(`mkdir -p ${shellQuote(dir)}`);
|
|
7942
|
+
if (result.exitCode !== 0) {
|
|
7943
|
+
throw new DaytonaCommandError(
|
|
7944
|
+
`Failed to create directory "${dir}": ${result.stderr}`
|
|
7945
|
+
);
|
|
7946
|
+
}
|
|
7255
7947
|
}
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7948
|
+
await sandbox.fs.uploadFiles(
|
|
7949
|
+
files.map((file) => ({
|
|
7950
|
+
source: Buffer.from(file.content),
|
|
7951
|
+
destination: file.path
|
|
7952
|
+
}))
|
|
7953
|
+
);
|
|
7954
|
+
} catch (error) {
|
|
7955
|
+
if (error instanceof DaytonaSandboxError) throw error;
|
|
7956
|
+
const err = toError(error);
|
|
7957
|
+
throw new DaytonaCommandError(
|
|
7958
|
+
`Failed to write files: ${err.message}`,
|
|
7959
|
+
err
|
|
7960
|
+
);
|
|
7961
|
+
}
|
|
7962
|
+
},
|
|
7963
|
+
async dispose() {
|
|
7964
|
+
},
|
|
7965
|
+
[Symbol.asyncDispose]() {
|
|
7966
|
+
return this.dispose();
|
|
7967
|
+
}
|
|
7968
|
+
};
|
|
7969
|
+
}
|
|
7970
|
+
function spawnDaytonaProcess(sandbox, command, options = {}) {
|
|
7971
|
+
const sessionId = createSessionId("spawn");
|
|
7972
|
+
const stdout = createTextReadable();
|
|
7973
|
+
const stderr = createTextReadable();
|
|
7974
|
+
const exit = runSpawnedSession({
|
|
7975
|
+
sandbox,
|
|
7976
|
+
sessionId,
|
|
7977
|
+
command: buildSessionCommand(command, options),
|
|
7978
|
+
signal: options.signal,
|
|
7979
|
+
commandTimeout: options.commandTimeout,
|
|
7980
|
+
stdout,
|
|
7981
|
+
stderr
|
|
7982
|
+
});
|
|
7983
|
+
return {
|
|
7984
|
+
stdout: stdout.stream,
|
|
7985
|
+
stderr: stderr.stream,
|
|
7986
|
+
exit
|
|
7987
|
+
};
|
|
7988
|
+
}
|
|
7989
|
+
async function runSpawnedSession(args) {
|
|
7990
|
+
const {
|
|
7991
|
+
sandbox,
|
|
7992
|
+
sessionId,
|
|
7993
|
+
command,
|
|
7994
|
+
signal,
|
|
7995
|
+
commandTimeout,
|
|
7996
|
+
stdout,
|
|
7997
|
+
stderr
|
|
7998
|
+
} = args;
|
|
7999
|
+
let sessionCreated = false;
|
|
8000
|
+
let aborted = signal?.aborted ?? false;
|
|
8001
|
+
let resolveAbort;
|
|
8002
|
+
const abortPromise = new Promise((resolve4) => {
|
|
8003
|
+
resolveAbort = () => resolve4("aborted");
|
|
8004
|
+
});
|
|
8005
|
+
const abort = () => {
|
|
8006
|
+
aborted = true;
|
|
8007
|
+
resolveAbort?.();
|
|
8008
|
+
if (sessionCreated) {
|
|
8009
|
+
sandbox.process.deleteSession(sessionId).catch(() => {
|
|
7261
8010
|
});
|
|
7262
8011
|
}
|
|
7263
|
-
return archPromise;
|
|
7264
8012
|
};
|
|
7265
|
-
|
|
7266
|
-
if (
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
8013
|
+
if (signal) {
|
|
8014
|
+
if (signal.aborted) abort();
|
|
8015
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
8016
|
+
}
|
|
8017
|
+
try {
|
|
8018
|
+
await sandbox.process.createSession(sessionId);
|
|
8019
|
+
sessionCreated = true;
|
|
8020
|
+
if (aborted) return abortedExitInfo();
|
|
8021
|
+
const response = await sandbox.process.executeSessionCommand(
|
|
8022
|
+
sessionId,
|
|
8023
|
+
{
|
|
8024
|
+
command,
|
|
8025
|
+
runAsync: true,
|
|
8026
|
+
suppressInputEcho: true
|
|
8027
|
+
},
|
|
8028
|
+
commandTimeout
|
|
8029
|
+
);
|
|
8030
|
+
const commandId = response.cmdId;
|
|
8031
|
+
if (!commandId) {
|
|
8032
|
+
throw new DaytonaCommandError(
|
|
8033
|
+
"Daytona did not return a command id for the spawned session command."
|
|
8034
|
+
);
|
|
7273
8035
|
}
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
8036
|
+
if (aborted) return abortedExitInfo();
|
|
8037
|
+
const logsTask = sandbox.process.getSessionCommandLogs(
|
|
8038
|
+
sessionId,
|
|
8039
|
+
commandId,
|
|
8040
|
+
(chunk) => stdout.enqueue(chunk),
|
|
8041
|
+
(chunk) => stderr.enqueue(chunk)
|
|
8042
|
+
);
|
|
8043
|
+
logsTask.catch(() => {
|
|
8044
|
+
});
|
|
8045
|
+
const winner = await Promise.race([
|
|
8046
|
+
logsTask.then(() => "logs"),
|
|
8047
|
+
abortPromise
|
|
8048
|
+
]);
|
|
8049
|
+
if (winner === "aborted") {
|
|
8050
|
+
await sandbox.process.deleteSession(sessionId).catch(() => {
|
|
8051
|
+
});
|
|
8052
|
+
return abortedExitInfo();
|
|
8053
|
+
}
|
|
8054
|
+
await logsTask;
|
|
8055
|
+
const code = await waitForSessionCommandExitCode({
|
|
8056
|
+
sandbox,
|
|
8057
|
+
sessionId,
|
|
8058
|
+
commandId,
|
|
8059
|
+
timeoutMs: sessionExitPollTimeoutMs(commandTimeout),
|
|
8060
|
+
isAborted: () => aborted
|
|
8061
|
+
});
|
|
8062
|
+
return { code, signal: null, success: code === 0 };
|
|
8063
|
+
} catch (error) {
|
|
8064
|
+
if (aborted) return abortedExitInfo();
|
|
8065
|
+
const err = toError(error);
|
|
8066
|
+
stdout.error(err);
|
|
8067
|
+
stderr.error(err);
|
|
8068
|
+
throw err;
|
|
8069
|
+
} finally {
|
|
8070
|
+
if (signal) {
|
|
8071
|
+
signal.removeEventListener("abort", abort);
|
|
8072
|
+
}
|
|
8073
|
+
stdout.close();
|
|
8074
|
+
stderr.close();
|
|
8075
|
+
if (sessionCreated) {
|
|
8076
|
+
await sandbox.process.deleteSession(sessionId).catch(() => {
|
|
8077
|
+
});
|
|
8078
|
+
}
|
|
8079
|
+
}
|
|
8080
|
+
}
|
|
8081
|
+
async function waitForSessionCommandExitCode(args) {
|
|
8082
|
+
const { sandbox, sessionId, commandId, timeoutMs, isAborted } = args;
|
|
8083
|
+
const deadline = Date.now() + timeoutMs;
|
|
8084
|
+
while (true) {
|
|
8085
|
+
if (isAborted()) throw new DaytonaCommandError("Daytona command aborted.");
|
|
8086
|
+
const info = await sandbox.process.getSessionCommand(sessionId, commandId);
|
|
8087
|
+
if (typeof info.exitCode === "number") {
|
|
8088
|
+
return info.exitCode;
|
|
8089
|
+
}
|
|
8090
|
+
if (Date.now() >= deadline) {
|
|
8091
|
+
throw new DaytonaCommandError(
|
|
8092
|
+
`Daytona session command "${commandId}" logs closed before an exit code became available.`
|
|
7282
8093
|
);
|
|
7283
8094
|
}
|
|
7284
|
-
|
|
7285
|
-
}
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
8095
|
+
await delay(DAYTONA_EXIT_POLL_INTERVAL_MS);
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
function sessionExitPollTimeoutMs(commandTimeout) {
|
|
8099
|
+
if (commandTimeout === void 0 || commandTimeout === 0) {
|
|
8100
|
+
return DAYTONA_EXIT_POLL_TIMEOUT_MS;
|
|
8101
|
+
}
|
|
8102
|
+
return Math.max(commandTimeout * 1e3, DAYTONA_EXIT_POLL_TIMEOUT_MS);
|
|
8103
|
+
}
|
|
8104
|
+
function delay(ms) {
|
|
8105
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
8106
|
+
}
|
|
8107
|
+
function createTextReadable() {
|
|
8108
|
+
const encoder = new TextEncoder();
|
|
8109
|
+
let controller;
|
|
8110
|
+
let closed = false;
|
|
8111
|
+
return {
|
|
8112
|
+
stream: new ReadableStream({
|
|
8113
|
+
start(streamController) {
|
|
8114
|
+
controller = streamController;
|
|
8115
|
+
}
|
|
8116
|
+
}),
|
|
8117
|
+
enqueue(chunk) {
|
|
8118
|
+
if (closed || !chunk) return;
|
|
8119
|
+
controller?.enqueue(encoder.encode(chunk));
|
|
8120
|
+
},
|
|
8121
|
+
close() {
|
|
8122
|
+
if (closed) return;
|
|
8123
|
+
closed = true;
|
|
8124
|
+
controller?.close();
|
|
8125
|
+
},
|
|
8126
|
+
error(error) {
|
|
8127
|
+
if (closed) return;
|
|
8128
|
+
closed = true;
|
|
8129
|
+
controller?.error(error);
|
|
7293
8130
|
}
|
|
7294
|
-
await installPackages([installName ?? checkName]);
|
|
7295
|
-
ensuredTools.add(cacheKey);
|
|
7296
8131
|
};
|
|
8132
|
+
}
|
|
8133
|
+
function buildSessionCommand(command, options) {
|
|
8134
|
+
const cwdPrefix = options.cwd ? `cd ${shellQuote(options.cwd)} && ` : "";
|
|
8135
|
+
const env = options.env ?? {};
|
|
8136
|
+
const entries = Object.entries(env);
|
|
8137
|
+
if (entries.length === 0) {
|
|
8138
|
+
return `sh -lc ${shellQuote(`${cwdPrefix}${command}`)}`;
|
|
8139
|
+
}
|
|
8140
|
+
for (const [key] of entries) {
|
|
8141
|
+
validateEnvKey(key);
|
|
8142
|
+
}
|
|
8143
|
+
const exports = entries.map(([key, value]) => `export ${key}=${shellQuote(value)}`).join("; ");
|
|
8144
|
+
return `sh -lc ${shellQuote(`${exports}; ${cwdPrefix}${command}`)}`;
|
|
8145
|
+
}
|
|
8146
|
+
function validateEnvKey(key) {
|
|
8147
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
8148
|
+
throw new DaytonaSandboxError(
|
|
8149
|
+
`Invalid environment variable key: "${key}". Use shell-compatible environment variable names.`
|
|
8150
|
+
);
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
function createSessionId(kind) {
|
|
8154
|
+
return `deepagents-${kind}-${randomUUID()}`;
|
|
8155
|
+
}
|
|
8156
|
+
function abortedCommandResult() {
|
|
7297
8157
|
return {
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
arch,
|
|
7302
|
-
exec,
|
|
7303
|
-
installPackages,
|
|
7304
|
-
ensureTool
|
|
8158
|
+
stdout: "",
|
|
8159
|
+
stderr: "Command aborted",
|
|
8160
|
+
exitCode: 1
|
|
7305
8161
|
};
|
|
7306
8162
|
}
|
|
8163
|
+
function abortedExitInfo() {
|
|
8164
|
+
return {
|
|
8165
|
+
code: null,
|
|
8166
|
+
signal: "SIGKILL",
|
|
8167
|
+
success: false
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
function uniqueParentDirectories(paths) {
|
|
8171
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
8172
|
+
for (const path5 of paths) {
|
|
8173
|
+
const index = path5.lastIndexOf("/");
|
|
8174
|
+
if (index > 0) {
|
|
8175
|
+
dirs.add(path5.slice(0, index));
|
|
8176
|
+
}
|
|
8177
|
+
}
|
|
8178
|
+
return [...dirs];
|
|
8179
|
+
}
|
|
8180
|
+
function compactObject(input) {
|
|
8181
|
+
const output = {};
|
|
8182
|
+
for (const [key, value] of Object.entries(input)) {
|
|
8183
|
+
if (value !== void 0) {
|
|
8184
|
+
output[key] = value;
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
return output;
|
|
8188
|
+
}
|
|
8189
|
+
function toError(error) {
|
|
8190
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
8191
|
+
}
|
|
7307
8192
|
|
|
7308
8193
|
// packages/context/src/lib/sandbox/docker-sandbox.ts
|
|
8194
|
+
import "bash-tool";
|
|
8195
|
+
import spawn5 from "nano-spawn";
|
|
8196
|
+
import { spawn as childSpawn3 } from "node:child_process";
|
|
8197
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
8198
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
7309
8199
|
function isDockerfileOptions(opts) {
|
|
7310
8200
|
return "dockerfile" in opts;
|
|
7311
8201
|
}
|
|
7312
8202
|
function isComposeOptions(opts) {
|
|
7313
8203
|
return "compose" in opts;
|
|
7314
8204
|
}
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
8205
|
+
function dockerMountArg(volume) {
|
|
8206
|
+
const readOnly = volume.readOnly !== false;
|
|
8207
|
+
const parts = volume.type === "bind" ? ["type=bind", `src=${volume.hostPath}`, `dst=${volume.containerPath}`] : [
|
|
8208
|
+
"type=volume",
|
|
8209
|
+
`src=${volume.name}`,
|
|
8210
|
+
`dst=${volume.containerPath}`,
|
|
8211
|
+
...volume.subPath ? [`volume-subpath=${volume.subPath}`] : [],
|
|
8212
|
+
...volume.noCopy ? ["volume-nocopy"] : []
|
|
8213
|
+
];
|
|
8214
|
+
if (readOnly) {
|
|
8215
|
+
parts.push("readonly");
|
|
8216
|
+
}
|
|
8217
|
+
return parts.join(",");
|
|
8218
|
+
}
|
|
8219
|
+
function runDockerBuild(args, stdin, showBuildLogs) {
|
|
8220
|
+
const stdio = [
|
|
8221
|
+
stdin === void 0 ? "inherit" : "pipe",
|
|
8222
|
+
showBuildLogs ? "inherit" : "ignore",
|
|
8223
|
+
showBuildLogs ? "inherit" : "pipe"
|
|
8224
|
+
];
|
|
8225
|
+
return new Promise((resolve4, reject) => {
|
|
8226
|
+
const child = childSpawn3("docker", args, { stdio });
|
|
8227
|
+
let stderr = "";
|
|
8228
|
+
child.stderr?.on("data", (chunk) => {
|
|
8229
|
+
stderr += chunk;
|
|
8230
|
+
});
|
|
8231
|
+
child.once(
|
|
8232
|
+
"error",
|
|
8233
|
+
(error) => reject(new DockerfileBuildError(error.message))
|
|
8234
|
+
);
|
|
8235
|
+
child.once("exit", (code) => {
|
|
8236
|
+
if (code === 0) {
|
|
8237
|
+
resolve4();
|
|
8238
|
+
return;
|
|
8239
|
+
}
|
|
8240
|
+
reject(
|
|
8241
|
+
new DockerfileBuildError(
|
|
8242
|
+
showBuildLogs ? `docker build exited with code ${code} (see build output above)` : stderr || `docker build exited with code ${code}`
|
|
8243
|
+
)
|
|
8244
|
+
);
|
|
8245
|
+
});
|
|
8246
|
+
if (stdin !== void 0 && child.stdin) {
|
|
8247
|
+
child.stdin.on("error", () => {
|
|
8248
|
+
});
|
|
8249
|
+
child.stdin.end(stdin);
|
|
8250
|
+
}
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
var dockerEngine = {
|
|
8254
|
+
cli: "docker",
|
|
8255
|
+
runArgs(image, containerId, opts, workdir) {
|
|
7327
8256
|
const {
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
8257
|
+
memory = "1g",
|
|
8258
|
+
cpus = 2,
|
|
8259
|
+
memorySwap,
|
|
8260
|
+
shmSize,
|
|
8261
|
+
pidsLimit,
|
|
8262
|
+
ulimits = [],
|
|
8263
|
+
cpusetCpus,
|
|
8264
|
+
cpuShares
|
|
8265
|
+
} = opts.resources ?? {};
|
|
8266
|
+
const security = opts.security ?? {};
|
|
8267
|
+
const network = opts.network ?? {};
|
|
8268
|
+
const args = [
|
|
8269
|
+
"run",
|
|
8270
|
+
"-d",
|
|
8271
|
+
"--rm",
|
|
8272
|
+
"--name",
|
|
8273
|
+
containerId,
|
|
8274
|
+
"--memory",
|
|
8275
|
+
memory,
|
|
8276
|
+
"--cpus",
|
|
8277
|
+
String(cpus),
|
|
8278
|
+
"-w",
|
|
8279
|
+
workdir
|
|
8280
|
+
];
|
|
8281
|
+
if (memorySwap) {
|
|
8282
|
+
args.push("--memory-swap", memorySwap);
|
|
7338
8283
|
}
|
|
7339
|
-
if (
|
|
7340
|
-
|
|
7341
|
-
`Invalid container name: "${name}". Use only letters, numbers, underscore, period, or hyphen. The "sandbox-" prefix is added automatically.`
|
|
7342
|
-
);
|
|
8284
|
+
if (shmSize) {
|
|
8285
|
+
args.push("--shm-size", shmSize);
|
|
7343
8286
|
}
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
this.env = env;
|
|
7347
|
-
this.name = name;
|
|
7348
|
-
this.command = command;
|
|
7349
|
-
this.securityOpt = securityOpt;
|
|
7350
|
-
this.platform = platform;
|
|
7351
|
-
}
|
|
7352
|
-
async create() {
|
|
7353
|
-
const image = await this.getImage();
|
|
7354
|
-
let acquired;
|
|
7355
|
-
try {
|
|
7356
|
-
acquired = await this.acquireContainer(image);
|
|
7357
|
-
this.context = { containerId: acquired.containerId, image };
|
|
7358
|
-
if (!acquired.attached) {
|
|
7359
|
-
await this.configure();
|
|
7360
|
-
}
|
|
7361
|
-
} catch (error) {
|
|
7362
|
-
if (acquired && !acquired.attached) {
|
|
7363
|
-
await this.stopContainer(acquired.containerId);
|
|
7364
|
-
}
|
|
7365
|
-
await this.cleanupCreatedVolumesAfterFailure(error);
|
|
7366
|
-
throw error;
|
|
8287
|
+
if (pidsLimit !== void 0) {
|
|
8288
|
+
args.push("--pids-limit", String(pidsLimit));
|
|
7367
8289
|
}
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
namedContainerId() {
|
|
7371
|
-
return `sandbox-${this.name}`;
|
|
7372
|
-
}
|
|
7373
|
-
defaultContainerId() {
|
|
7374
|
-
return `sandbox-${crypto.randomUUID().slice(0, 8)}`;
|
|
7375
|
-
}
|
|
7376
|
-
async acquireContainer(image) {
|
|
7377
|
-
if (!this.name) {
|
|
7378
|
-
const containerId2 = this.defaultContainerId();
|
|
7379
|
-
await this.prepareVolumes();
|
|
7380
|
-
await this.startContainer(image, containerId2);
|
|
7381
|
-
return { containerId: containerId2, attached: false };
|
|
8290
|
+
for (const ulimit of ulimits) {
|
|
8291
|
+
args.push("--ulimit", ulimit);
|
|
7382
8292
|
}
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
if (probe === "running") {
|
|
7386
|
-
return { containerId, attached: true };
|
|
8293
|
+
if (cpusetCpus) {
|
|
8294
|
+
args.push("--cpuset-cpus", cpusetCpus);
|
|
7387
8295
|
}
|
|
7388
|
-
if (
|
|
7389
|
-
|
|
7390
|
-
return { containerId, attached: true };
|
|
8296
|
+
if (cpuShares !== void 0) {
|
|
8297
|
+
args.push("--cpu-shares", String(cpuShares));
|
|
7391
8298
|
}
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
await this.startContainer(image, containerId);
|
|
7395
|
-
return { containerId, attached: false };
|
|
7396
|
-
} catch (error) {
|
|
7397
|
-
if (error instanceof ContainerCreationError && this.isNameConflictError(error.message)) {
|
|
7398
|
-
await this.cleanupCreatedVolumes();
|
|
7399
|
-
const raced = await this.inspectContainer(containerId);
|
|
7400
|
-
if (raced === "running") {
|
|
7401
|
-
return { containerId, attached: true };
|
|
7402
|
-
}
|
|
7403
|
-
if (raced === "stopped") {
|
|
7404
|
-
await this.startStoppedContainer(containerId, image);
|
|
7405
|
-
return { containerId, attached: true };
|
|
7406
|
-
}
|
|
7407
|
-
}
|
|
7408
|
-
throw error;
|
|
8299
|
+
if (opts.platform) {
|
|
8300
|
+
args.push("--platform", opts.platform);
|
|
7409
8301
|
}
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
try {
|
|
7413
|
-
await spawn3("docker", ["start", containerId]);
|
|
7414
|
-
} catch (error) {
|
|
7415
|
-
const message2 = this.getDockerErrorMessage(error);
|
|
7416
|
-
if (this.isDockerUnavailableError(message2)) {
|
|
7417
|
-
throw new DockerNotAvailableError();
|
|
7418
|
-
}
|
|
7419
|
-
throw new ContainerCreationError(message2, image, error);
|
|
8302
|
+
if (opts.runtime) {
|
|
8303
|
+
args.push("--runtime", opts.runtime);
|
|
7420
8304
|
}
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
try {
|
|
7424
|
-
const result = await spawn3("docker", [
|
|
7425
|
-
"container",
|
|
7426
|
-
"inspect",
|
|
7427
|
-
"--format",
|
|
7428
|
-
"{{.State.Status}}",
|
|
7429
|
-
containerId
|
|
7430
|
-
]);
|
|
7431
|
-
const status = result.stdout.trim();
|
|
7432
|
-
return status === "running" ? "running" : "stopped";
|
|
7433
|
-
} catch (error) {
|
|
7434
|
-
const message2 = this.getDockerErrorMessage(error);
|
|
7435
|
-
if (this.isDockerUnavailableError(message2)) {
|
|
7436
|
-
throw new DockerNotAvailableError();
|
|
7437
|
-
}
|
|
7438
|
-
if (this.isMissingContainerError(message2)) {
|
|
7439
|
-
return "absent";
|
|
7440
|
-
}
|
|
7441
|
-
throw new DockerSandboxError(
|
|
7442
|
-
`Failed to inspect container "${containerId}": ${message2}`
|
|
7443
|
-
);
|
|
8305
|
+
for (const cap of security.capDrop ?? []) {
|
|
8306
|
+
args.push("--cap-drop", cap);
|
|
7444
8307
|
}
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
return message2.toLowerCase().includes("no such container");
|
|
7448
|
-
}
|
|
7449
|
-
isNameConflictError(message2) {
|
|
7450
|
-
return message2.toLowerCase().includes("is already in use by container");
|
|
7451
|
-
}
|
|
7452
|
-
async prepareVolumes() {
|
|
7453
|
-
this.validateVolumes();
|
|
7454
|
-
for (const volume of this.volumes) {
|
|
7455
|
-
if (volume.type !== "volume") {
|
|
7456
|
-
continue;
|
|
7457
|
-
}
|
|
7458
|
-
const lifecycle = volume.lifecycle ?? "external";
|
|
7459
|
-
if (lifecycle === "external") {
|
|
7460
|
-
await this.inspectVolume(volume.name);
|
|
7461
|
-
continue;
|
|
7462
|
-
}
|
|
7463
|
-
const exists = await this.volumeExists(volume.name);
|
|
7464
|
-
if (exists) {
|
|
7465
|
-
throw new VolumeCreateError(
|
|
7466
|
-
volume.name,
|
|
7467
|
-
"managed volume already exists"
|
|
7468
|
-
);
|
|
7469
|
-
}
|
|
7470
|
-
await this.createVolume(volume);
|
|
7471
|
-
if (volume.removeOnDispose !== false) {
|
|
7472
|
-
this.createdVolumes.add(volume.name);
|
|
7473
|
-
}
|
|
8308
|
+
for (const cap of security.capAdd ?? []) {
|
|
8309
|
+
args.push("--cap-add", cap);
|
|
7474
8310
|
}
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
}
|
|
7505
|
-
continue;
|
|
7506
|
-
}
|
|
7507
|
-
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(volume.name)) {
|
|
7508
|
-
throw new VolumePathError(
|
|
7509
|
-
volume.name,
|
|
7510
|
-
volume.containerPath,
|
|
7511
|
-
"volume name must start with an alphanumeric character and contain only letters, numbers, underscore, period, or hyphen"
|
|
7512
|
-
);
|
|
7513
|
-
}
|
|
7514
|
-
if (volume.subPath) {
|
|
7515
|
-
this.validateMountValue("subPath", volume.subPath, volume);
|
|
7516
|
-
}
|
|
7517
|
-
if ((volume.lifecycle ?? "external") === "external") {
|
|
7518
|
-
if (volume.driver || volume.driverOptions) {
|
|
7519
|
-
throw new VolumePathError(
|
|
7520
|
-
volume.name,
|
|
7521
|
-
volume.containerPath,
|
|
7522
|
-
'driver and driverOptions require lifecycle "managed"'
|
|
7523
|
-
);
|
|
7524
|
-
}
|
|
7525
|
-
}
|
|
8311
|
+
if (security.readOnly) {
|
|
8312
|
+
args.push("--read-only");
|
|
8313
|
+
}
|
|
8314
|
+
if (security.user) {
|
|
8315
|
+
args.push("--user", security.user);
|
|
8316
|
+
}
|
|
8317
|
+
for (const mount of security.tmpfs ?? []) {
|
|
8318
|
+
args.push("--tmpfs", mount);
|
|
8319
|
+
}
|
|
8320
|
+
for (const opt of security.securityOpt ?? []) {
|
|
8321
|
+
args.push("--security-opt", opt);
|
|
8322
|
+
}
|
|
8323
|
+
if (network.mode) {
|
|
8324
|
+
args.push("--network", network.mode);
|
|
8325
|
+
}
|
|
8326
|
+
for (const port of network.publish ?? []) {
|
|
8327
|
+
args.push("--publish", port);
|
|
8328
|
+
}
|
|
8329
|
+
for (const server of network.dns ?? []) {
|
|
8330
|
+
args.push("--dns", server);
|
|
8331
|
+
}
|
|
8332
|
+
for (const host of network.addHost ?? []) {
|
|
8333
|
+
args.push("--add-host", host);
|
|
8334
|
+
}
|
|
8335
|
+
if (network.hostname) {
|
|
8336
|
+
args.push("--hostname", network.hostname);
|
|
8337
|
+
}
|
|
8338
|
+
if (opts.gpus) {
|
|
8339
|
+
args.push("--gpus", opts.gpus);
|
|
7526
8340
|
}
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
if (!value.includes(",")) {
|
|
7530
|
-
return;
|
|
8341
|
+
for (const device of opts.devices ?? []) {
|
|
8342
|
+
args.push("--device", device);
|
|
7531
8343
|
}
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
source,
|
|
7535
|
-
volume.containerPath,
|
|
7536
|
-
`${field} must not contain commas`
|
|
7537
|
-
);
|
|
7538
|
-
}
|
|
7539
|
-
buildDockerArgs(image, containerId) {
|
|
7540
|
-
const { memory = "1g", cpus = 2 } = this.resources;
|
|
7541
|
-
const args = [
|
|
7542
|
-
"run",
|
|
7543
|
-
"-d",
|
|
7544
|
-
"--rm",
|
|
7545
|
-
"--name",
|
|
7546
|
-
containerId,
|
|
7547
|
-
`--memory=${memory}`,
|
|
7548
|
-
`--cpus=${cpus}`,
|
|
7549
|
-
"-w",
|
|
7550
|
-
"/workspace"
|
|
7551
|
-
];
|
|
7552
|
-
if (this.platform) {
|
|
7553
|
-
args.push("--platform", this.platform);
|
|
8344
|
+
if (opts.init) {
|
|
8345
|
+
args.push("--init");
|
|
7554
8346
|
}
|
|
7555
|
-
for (const
|
|
7556
|
-
args.push("--
|
|
8347
|
+
for (const [key, value] of Object.entries(opts.labels ?? {})) {
|
|
8348
|
+
args.push("--label", `${key}=${value}`);
|
|
8349
|
+
}
|
|
8350
|
+
for (const [key, value] of Object.entries(opts.sysctls ?? {})) {
|
|
8351
|
+
args.push("--sysctl", `${key}=${value}`);
|
|
7557
8352
|
}
|
|
7558
|
-
|
|
8353
|
+
if (opts.entrypoint) {
|
|
8354
|
+
args.push("--entrypoint", opts.entrypoint);
|
|
8355
|
+
}
|
|
8356
|
+
for (const [key, value] of Object.entries(opts.env ?? {})) {
|
|
7559
8357
|
args.push("-e", `${key}=${value}`);
|
|
7560
8358
|
}
|
|
7561
|
-
for (const volume of
|
|
7562
|
-
args.push("--mount",
|
|
8359
|
+
for (const volume of opts.volumes ?? []) {
|
|
8360
|
+
args.push("--mount", dockerMountArg(volume));
|
|
7563
8361
|
}
|
|
7564
8362
|
args.push(image);
|
|
7565
|
-
if (
|
|
8363
|
+
if (opts.command === void 0) {
|
|
7566
8364
|
args.push("tail", "-f", "/dev/null");
|
|
7567
|
-
} else if (
|
|
7568
|
-
args.push(...
|
|
8365
|
+
} else if (opts.command !== null) {
|
|
8366
|
+
args.push(...opts.command);
|
|
7569
8367
|
}
|
|
7570
8368
|
return args;
|
|
7571
|
-
}
|
|
7572
|
-
|
|
7573
|
-
const
|
|
7574
|
-
|
|
7575
|
-
"
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
}
|
|
7584
|
-
return parts.join(",");
|
|
7585
|
-
}
|
|
7586
|
-
async inspectVolume(name) {
|
|
7587
|
-
try {
|
|
7588
|
-
await spawn3("docker", ["volume", "inspect", name]);
|
|
7589
|
-
} catch (error) {
|
|
7590
|
-
const reason = this.getDockerErrorMessage(error);
|
|
7591
|
-
if (this.isDockerUnavailableError(reason)) {
|
|
7592
|
-
throw new DockerNotAvailableError();
|
|
7593
|
-
}
|
|
7594
|
-
throw new VolumeInspectError(name, reason);
|
|
7595
|
-
}
|
|
7596
|
-
}
|
|
7597
|
-
async volumeExists(name) {
|
|
7598
|
-
try {
|
|
7599
|
-
await this.inspectVolume(name);
|
|
7600
|
-
return true;
|
|
7601
|
-
} catch (error) {
|
|
7602
|
-
if (error instanceof VolumeInspectError) {
|
|
7603
|
-
if (this.isMissingVolumeInspectError(error.reason)) {
|
|
7604
|
-
return false;
|
|
8369
|
+
},
|
|
8370
|
+
execArgs(containerId, command, options) {
|
|
8371
|
+
const flags = [];
|
|
8372
|
+
if (options?.cwd) {
|
|
8373
|
+
flags.push("-w", options.cwd);
|
|
8374
|
+
}
|
|
8375
|
+
if (options?.env) {
|
|
8376
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
8377
|
+
if (key.length === 0 || key.includes("=")) {
|
|
8378
|
+
throw new DockerSandboxError(
|
|
8379
|
+
`Invalid environment variable key: "${key}"`
|
|
8380
|
+
);
|
|
7605
8381
|
}
|
|
7606
|
-
|
|
8382
|
+
flags.push("-e", `${key}=${value}`);
|
|
7607
8383
|
}
|
|
7608
|
-
throw error;
|
|
7609
8384
|
}
|
|
7610
|
-
|
|
7611
|
-
|
|
8385
|
+
return ["exec", ...flags, containerId, "sh", "-c", command];
|
|
8386
|
+
},
|
|
8387
|
+
inspectArgs(containerId) {
|
|
8388
|
+
return [
|
|
8389
|
+
"container",
|
|
8390
|
+
"inspect",
|
|
8391
|
+
"--format",
|
|
8392
|
+
"{{.State.Status}}",
|
|
8393
|
+
containerId
|
|
8394
|
+
];
|
|
8395
|
+
},
|
|
8396
|
+
mountArg: dockerMountArg,
|
|
8397
|
+
parseStatus(status) {
|
|
8398
|
+
return status === "running" ? "running" : "stopped";
|
|
8399
|
+
},
|
|
8400
|
+
volumeCreateArgs(volume) {
|
|
7612
8401
|
const args = ["volume", "create"];
|
|
7613
8402
|
if (volume.driver) {
|
|
7614
8403
|
args.push("--driver", volume.driver);
|
|
@@ -7617,143 +8406,61 @@ var DockerSandboxStrategy = class {
|
|
|
7617
8406
|
args.push("--opt", `${key}=${value}`);
|
|
7618
8407
|
}
|
|
7619
8408
|
args.push(volume.name);
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
const reason = this.getDockerErrorMessage(error);
|
|
7624
|
-
if (this.isDockerUnavailableError(reason)) {
|
|
7625
|
-
throw new DockerNotAvailableError();
|
|
7626
|
-
}
|
|
7627
|
-
throw new VolumeCreateError(volume.name, reason);
|
|
7628
|
-
}
|
|
7629
|
-
}
|
|
7630
|
-
async cleanupCreatedVolumes() {
|
|
7631
|
-
const volumes = [...this.createdVolumes].reverse();
|
|
7632
|
-
for (const volume of volumes) {
|
|
7633
|
-
try {
|
|
7634
|
-
await spawn3("docker", ["volume", "rm", volume]);
|
|
7635
|
-
this.createdVolumes.delete(volume);
|
|
7636
|
-
} catch (error) {
|
|
7637
|
-
const reason = this.getDockerErrorMessage(error);
|
|
7638
|
-
if (this.isDockerUnavailableError(reason)) {
|
|
7639
|
-
throw new DockerNotAvailableError();
|
|
7640
|
-
}
|
|
7641
|
-
throw new VolumeRemoveError(volume, reason);
|
|
7642
|
-
}
|
|
7643
|
-
}
|
|
7644
|
-
}
|
|
7645
|
-
async cleanupCreatedVolumesAfterFailure(originalError) {
|
|
7646
|
-
try {
|
|
7647
|
-
await this.cleanupCreatedVolumes();
|
|
7648
|
-
} catch (cleanupError) {
|
|
7649
|
-
if (originalError instanceof Error) {
|
|
7650
|
-
const original = originalError;
|
|
7651
|
-
original.suppressed = [...original.suppressed ?? [], cleanupError];
|
|
7652
|
-
}
|
|
7653
|
-
}
|
|
7654
|
-
}
|
|
7655
|
-
getDockerErrorMessage(error) {
|
|
8409
|
+
return args;
|
|
8410
|
+
},
|
|
8411
|
+
errorMessage(error) {
|
|
7656
8412
|
const err = error;
|
|
7657
8413
|
return err.stderr || err.stdout || err.message || String(error);
|
|
7658
|
-
}
|
|
7659
|
-
|
|
8414
|
+
},
|
|
8415
|
+
isServiceDown(message2) {
|
|
7660
8416
|
return message2.includes("Cannot connect") || message2.includes("docker daemon");
|
|
7661
|
-
}
|
|
7662
|
-
|
|
8417
|
+
},
|
|
8418
|
+
isMissingContainer(message2) {
|
|
8419
|
+
return message2.toLowerCase().includes("no such container");
|
|
8420
|
+
},
|
|
8421
|
+
isMissingVolume(message2) {
|
|
7663
8422
|
return message2.toLowerCase().includes("no such volume");
|
|
7664
|
-
}
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
}
|
|
7674
|
-
throw new ContainerCreationError(
|
|
7675
|
-
this.getDockerErrorMessage(err),
|
|
7676
|
-
image,
|
|
7677
|
-
err
|
|
7678
|
-
);
|
|
7679
|
-
}
|
|
7680
|
-
}
|
|
7681
|
-
async stopContainer(containerId) {
|
|
8423
|
+
},
|
|
8424
|
+
isNameConflict(message2) {
|
|
8425
|
+
return message2.toLowerCase().includes("is already in use by container");
|
|
8426
|
+
},
|
|
8427
|
+
async ensureWorkdir() {
|
|
8428
|
+
},
|
|
8429
|
+
defaultImage: "alpine:latest",
|
|
8430
|
+
createInstallerContext,
|
|
8431
|
+
async imageExists(tag) {
|
|
7682
8432
|
try {
|
|
7683
|
-
await
|
|
8433
|
+
await spawn5("docker", ["image", "inspect", tag]);
|
|
8434
|
+
return true;
|
|
7684
8435
|
} catch {
|
|
8436
|
+
return false;
|
|
7685
8437
|
}
|
|
7686
|
-
}
|
|
7687
|
-
async
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
this.context.containerId,
|
|
7713
|
-
"sh",
|
|
7714
|
-
"-c",
|
|
7715
|
-
command
|
|
7716
|
-
]);
|
|
7717
|
-
return toSandboxProcess(child, options?.signal);
|
|
7718
|
-
}
|
|
7719
|
-
createSandboxMethods() {
|
|
7720
|
-
const { containerId } = this.context;
|
|
7721
|
-
const sandbox = {
|
|
7722
|
-
executeCommand: async (command, options) => this.exec(command, options),
|
|
7723
|
-
spawn: (command, options) => this.spawnProcess(command, options),
|
|
7724
|
-
readFile: async (path5) => {
|
|
7725
|
-
const result = await sandbox.executeCommand(`base64 "${path5}"`);
|
|
7726
|
-
if (result.exitCode !== 0) {
|
|
7727
|
-
throw new Error(`Failed to read file "${path5}": ${result.stderr}`);
|
|
7728
|
-
}
|
|
7729
|
-
return Buffer.from(result.stdout, "base64").toString("utf-8");
|
|
7730
|
-
},
|
|
7731
|
-
writeFiles: async (files) => {
|
|
7732
|
-
for (const file of files) {
|
|
7733
|
-
const dir = file.path.substring(0, file.path.lastIndexOf("/"));
|
|
7734
|
-
if (dir) {
|
|
7735
|
-
await sandbox.executeCommand(`mkdir -p "${dir}"`);
|
|
7736
|
-
}
|
|
7737
|
-
const base64Content = Buffer.from(file.content).toString("base64");
|
|
7738
|
-
const result = await sandbox.executeCommand(
|
|
7739
|
-
`echo "${base64Content}" | base64 -d > "${file.path}"`
|
|
7740
|
-
);
|
|
7741
|
-
if (result.exitCode !== 0) {
|
|
7742
|
-
throw new Error(
|
|
7743
|
-
`Failed to write file "${file.path}": ${result.stderr}`
|
|
7744
|
-
);
|
|
7745
|
-
}
|
|
7746
|
-
}
|
|
7747
|
-
},
|
|
7748
|
-
dispose: async () => {
|
|
7749
|
-
await this.stopContainer(containerId);
|
|
7750
|
-
await this.cleanupCreatedVolumes();
|
|
7751
|
-
},
|
|
7752
|
-
[Symbol.asyncDispose]() {
|
|
7753
|
-
return this.dispose();
|
|
7754
|
-
}
|
|
7755
|
-
};
|
|
7756
|
-
return sandbox;
|
|
8438
|
+
},
|
|
8439
|
+
async buildImage(spec) {
|
|
8440
|
+
const inline = spec.dockerfile.includes("\n");
|
|
8441
|
+
const args = [
|
|
8442
|
+
"build",
|
|
8443
|
+
...spec.identity ? ["--platform", spec.identity] : [],
|
|
8444
|
+
"-t",
|
|
8445
|
+
spec.tag,
|
|
8446
|
+
"-f",
|
|
8447
|
+
inline ? "-" : spec.dockerfile,
|
|
8448
|
+
spec.context
|
|
8449
|
+
];
|
|
8450
|
+
await runDockerBuild(
|
|
8451
|
+
args,
|
|
8452
|
+
inline ? spec.dockerfile : void 0,
|
|
8453
|
+
spec.showBuildLogs
|
|
8454
|
+
);
|
|
8455
|
+
},
|
|
8456
|
+
errors: {
|
|
8457
|
+
serviceNotAvailable: () => new DockerNotAvailableError(),
|
|
8458
|
+
creation: (message2, image, cause) => new ContainerCreationError(message2, image, cause),
|
|
8459
|
+
generic: (message2, containerId) => new DockerSandboxError(message2, containerId),
|
|
8460
|
+
volumePath: (source, containerPath, reason) => new VolumePathError(source, containerPath, reason),
|
|
8461
|
+
volumeInspect: (name, reason) => new VolumeInspectError(name, reason),
|
|
8462
|
+
volumeCreate: (name, reason) => new VolumeCreateError(name, reason),
|
|
8463
|
+
volumeRemove: (name, reason) => new VolumeRemoveError(name, reason)
|
|
7757
8464
|
}
|
|
7758
8465
|
};
|
|
7759
8466
|
function validateEnvKey2(key) {
|
|
@@ -7774,175 +8481,19 @@ function buildDockerExecFlags(options) {
|
|
|
7774
8481
|
}
|
|
7775
8482
|
return flags;
|
|
7776
8483
|
}
|
|
7777
|
-
|
|
7778
|
-
if (!child.stdout || !child.stderr) {
|
|
7779
|
-
child.kill("SIGKILL");
|
|
7780
|
-
throw new DockerSandboxError("docker exec child missing stdio streams");
|
|
7781
|
-
}
|
|
7782
|
-
const onAbort = abortSignal ? () => child.kill("SIGKILL") : void 0;
|
|
7783
|
-
if (abortSignal && onAbort) {
|
|
7784
|
-
if (abortSignal.aborted) onAbort();
|
|
7785
|
-
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
7786
|
-
}
|
|
7787
|
-
return {
|
|
7788
|
-
stdout: Readable2.toWeb(child.stdout),
|
|
7789
|
-
stderr: Readable2.toWeb(child.stderr),
|
|
7790
|
-
exit: new Promise((resolve4, reject) => {
|
|
7791
|
-
const settle = () => {
|
|
7792
|
-
child.removeListener("exit", onExitEvent);
|
|
7793
|
-
child.removeListener("error", onError);
|
|
7794
|
-
if (abortSignal && onAbort) {
|
|
7795
|
-
abortSignal.removeEventListener("abort", onAbort);
|
|
7796
|
-
}
|
|
7797
|
-
};
|
|
7798
|
-
const onError = (err) => {
|
|
7799
|
-
settle();
|
|
7800
|
-
reject(err);
|
|
7801
|
-
};
|
|
7802
|
-
const onExitEvent = (code, exitSignal) => {
|
|
7803
|
-
settle();
|
|
7804
|
-
resolve4({ code, signal: exitSignal, success: code === 0 });
|
|
7805
|
-
};
|
|
7806
|
-
child.on("exit", onExitEvent);
|
|
7807
|
-
child.on("error", onError);
|
|
7808
|
-
})
|
|
7809
|
-
};
|
|
7810
|
-
}
|
|
7811
|
-
var RuntimeStrategy = class extends DockerSandboxStrategy {
|
|
7812
|
-
image;
|
|
7813
|
-
installers;
|
|
7814
|
-
constructor(args = {}) {
|
|
7815
|
-
super({
|
|
7816
|
-
volumes: args.volumes,
|
|
7817
|
-
resources: args.resources,
|
|
7818
|
-
env: args.env,
|
|
7819
|
-
name: args.name,
|
|
7820
|
-
command: args.command,
|
|
7821
|
-
securityOpt: args.securityOpt,
|
|
7822
|
-
platform: args.platform
|
|
7823
|
-
});
|
|
7824
|
-
this.image = args.image ?? "alpine:latest";
|
|
7825
|
-
this.installers = args.installers ?? [];
|
|
7826
|
-
}
|
|
7827
|
-
async getImage() {
|
|
7828
|
-
return this.image;
|
|
7829
|
-
}
|
|
7830
|
-
async configure() {
|
|
7831
|
-
const ctx = createInstallerContext(this.context.containerId, this.image);
|
|
7832
|
-
for (const installer of this.installers) {
|
|
7833
|
-
await installer.install(ctx);
|
|
7834
|
-
}
|
|
7835
|
-
}
|
|
7836
|
-
};
|
|
7837
|
-
var DockerfileStrategy = class extends DockerSandboxStrategy {
|
|
7838
|
-
imageTag;
|
|
7839
|
-
dockerfile;
|
|
7840
|
-
dockerContext;
|
|
7841
|
-
showBuildLogs;
|
|
7842
|
-
constructor(args) {
|
|
7843
|
-
super({
|
|
7844
|
-
volumes: args.volumes,
|
|
7845
|
-
resources: args.resources,
|
|
7846
|
-
env: args.env,
|
|
7847
|
-
name: args.name,
|
|
7848
|
-
command: args.command,
|
|
7849
|
-
securityOpt: args.securityOpt,
|
|
7850
|
-
platform: args.platform
|
|
7851
|
-
});
|
|
7852
|
-
this.dockerfile = args.dockerfile;
|
|
7853
|
-
this.dockerContext = args.context ?? ".";
|
|
7854
|
-
this.showBuildLogs = args.showBuildLogs ?? false;
|
|
7855
|
-
this.imageTag = this.computeImageTag();
|
|
7856
|
-
}
|
|
7857
|
-
computeImageTag() {
|
|
7858
|
-
const content = this.isInlineDockerfile() ? this.dockerfile : readFileSync2(this.dockerfile, "utf-8");
|
|
7859
|
-
const hash = createHash("sha256").update(content).update(this.platform ?? "").digest("hex").slice(0, 12);
|
|
7860
|
-
return `sandbox-${hash}`;
|
|
7861
|
-
}
|
|
7862
|
-
isInlineDockerfile() {
|
|
7863
|
-
return this.dockerfile.includes("\n");
|
|
7864
|
-
}
|
|
7865
|
-
async getImage() {
|
|
7866
|
-
const exists = await this.imageExists();
|
|
7867
|
-
if (!exists) {
|
|
7868
|
-
await this.buildImage();
|
|
7869
|
-
}
|
|
7870
|
-
return this.imageTag;
|
|
7871
|
-
}
|
|
7872
|
-
async configure() {
|
|
7873
|
-
}
|
|
7874
|
-
async imageExists() {
|
|
7875
|
-
try {
|
|
7876
|
-
await spawn3("docker", ["image", "inspect", this.imageTag]);
|
|
7877
|
-
return true;
|
|
7878
|
-
} catch {
|
|
7879
|
-
return false;
|
|
7880
|
-
}
|
|
7881
|
-
}
|
|
7882
|
-
async buildImage() {
|
|
7883
|
-
try {
|
|
7884
|
-
const platformFlag = this.platform ? `--platform ${this.platform} ` : "";
|
|
7885
|
-
if (this.isInlineDockerfile()) {
|
|
7886
|
-
const buildCmd = `echo '${this.dockerfile.replace(/'/g, "'\\''")}' | docker build ${platformFlag}-t ${this.imageTag} -f - ${this.dockerContext}`;
|
|
7887
|
-
if (this.showBuildLogs) {
|
|
7888
|
-
await this.runStreamed("sh", ["-c", buildCmd]);
|
|
7889
|
-
} else {
|
|
7890
|
-
await spawn3("sh", ["-c", buildCmd]);
|
|
7891
|
-
}
|
|
7892
|
-
} else {
|
|
7893
|
-
const args = [
|
|
7894
|
-
"build",
|
|
7895
|
-
...this.platform ? ["--platform", this.platform] : [],
|
|
7896
|
-
"-t",
|
|
7897
|
-
this.imageTag,
|
|
7898
|
-
"-f",
|
|
7899
|
-
this.dockerfile,
|
|
7900
|
-
this.dockerContext
|
|
7901
|
-
];
|
|
7902
|
-
if (this.showBuildLogs) {
|
|
7903
|
-
await this.runStreamed("docker", args);
|
|
7904
|
-
} else {
|
|
7905
|
-
await spawn3("docker", args);
|
|
7906
|
-
}
|
|
7907
|
-
}
|
|
7908
|
-
} catch (error) {
|
|
7909
|
-
const err = error;
|
|
7910
|
-
throw new DockerfileBuildError(err.stderr || err.message);
|
|
7911
|
-
}
|
|
7912
|
-
}
|
|
7913
|
-
/**
|
|
7914
|
-
* Run a build command with stdio inherited so its output streams live to the
|
|
7915
|
-
* parent terminal. On failure the build error is already on screen; the
|
|
7916
|
-
* rejection just carries the exit code.
|
|
7917
|
-
*/
|
|
7918
|
-
runStreamed(command, args) {
|
|
7919
|
-
return new Promise((resolve4, reject) => {
|
|
7920
|
-
const child = childSpawn(command, args, { stdio: "inherit" });
|
|
7921
|
-
child.once("error", reject);
|
|
7922
|
-
child.once(
|
|
7923
|
-
"exit",
|
|
7924
|
-
(code) => code === 0 ? resolve4() : reject(
|
|
7925
|
-
new Error(
|
|
7926
|
-
`docker build exited with code ${code} (see build output above)`
|
|
7927
|
-
)
|
|
7928
|
-
)
|
|
7929
|
-
);
|
|
7930
|
-
});
|
|
7931
|
-
}
|
|
7932
|
-
};
|
|
7933
|
-
var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
8484
|
+
var ComposeStrategy = class extends ContainerSandboxStrategy {
|
|
7934
8485
|
projectName;
|
|
7935
8486
|
composeFile;
|
|
7936
8487
|
service;
|
|
7937
8488
|
constructor(args) {
|
|
7938
|
-
super({ resources: args.resources });
|
|
8489
|
+
super({ resources: args.resources }, dockerEngine);
|
|
7939
8490
|
this.composeFile = args.compose;
|
|
7940
8491
|
this.service = args.service;
|
|
7941
8492
|
this.projectName = this.computeProjectName();
|
|
7942
8493
|
}
|
|
7943
8494
|
computeProjectName() {
|
|
7944
|
-
const content =
|
|
7945
|
-
const hash =
|
|
8495
|
+
const content = readFileSync3(this.composeFile, "utf-8");
|
|
8496
|
+
const hash = createHash2("sha256").update(content).digest("hex").slice(0, 8);
|
|
7946
8497
|
return `sandbox-${hash}`;
|
|
7947
8498
|
}
|
|
7948
8499
|
async getImage() {
|
|
@@ -7950,7 +8501,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
7950
8501
|
}
|
|
7951
8502
|
async startContainer(_image, _containerId) {
|
|
7952
8503
|
try {
|
|
7953
|
-
await
|
|
8504
|
+
await spawn5("docker", [
|
|
7954
8505
|
"compose",
|
|
7955
8506
|
"-f",
|
|
7956
8507
|
this.composeFile,
|
|
@@ -7962,7 +8513,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
7962
8513
|
} catch (error) {
|
|
7963
8514
|
const err = error;
|
|
7964
8515
|
if (err.stderr?.includes("Cannot connect")) {
|
|
7965
|
-
throw
|
|
8516
|
+
throw this.engine.errors.serviceNotAvailable();
|
|
7966
8517
|
}
|
|
7967
8518
|
throw new ComposeStartError(this.composeFile, err.stderr || err.message);
|
|
7968
8519
|
}
|
|
@@ -7974,7 +8525,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
7974
8525
|
}
|
|
7975
8526
|
async exec(command, options) {
|
|
7976
8527
|
try {
|
|
7977
|
-
const result = await
|
|
8528
|
+
const result = await spawn5(
|
|
7978
8529
|
"docker",
|
|
7979
8530
|
[
|
|
7980
8531
|
"compose",
|
|
@@ -8002,7 +8553,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
8002
8553
|
}
|
|
8003
8554
|
}
|
|
8004
8555
|
spawnProcess(command, options) {
|
|
8005
|
-
const child =
|
|
8556
|
+
const child = childSpawn3("docker", [
|
|
8006
8557
|
"compose",
|
|
8007
8558
|
"-f",
|
|
8008
8559
|
this.composeFile,
|
|
@@ -8020,7 +8571,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
8020
8571
|
}
|
|
8021
8572
|
async stopContainer(_containerId) {
|
|
8022
8573
|
try {
|
|
8023
|
-
await
|
|
8574
|
+
await spawn5("docker", [
|
|
8024
8575
|
"compose",
|
|
8025
8576
|
"-f",
|
|
8026
8577
|
this.composeFile,
|
|
@@ -8033,40 +8584,25 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
8033
8584
|
}
|
|
8034
8585
|
};
|
|
8035
8586
|
async function createDockerSandbox(options = {}) {
|
|
8036
|
-
let strategy;
|
|
8037
8587
|
if (isComposeOptions(options)) {
|
|
8038
|
-
|
|
8588
|
+
return new ComposeStrategy({
|
|
8039
8589
|
compose: options.compose,
|
|
8040
8590
|
service: options.service,
|
|
8041
8591
|
resources: options.resources
|
|
8042
|
-
});
|
|
8043
|
-
}
|
|
8044
|
-
|
|
8592
|
+
}).create();
|
|
8593
|
+
}
|
|
8594
|
+
if (isDockerfileOptions(options)) {
|
|
8595
|
+
return new ContainerfileStrategy(options, dockerEngine, {
|
|
8045
8596
|
dockerfile: options.dockerfile,
|
|
8046
|
-
context: options.context,
|
|
8047
|
-
showBuildLogs: options.showBuildLogs,
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
env: options.env,
|
|
8051
|
-
name: options.name,
|
|
8052
|
-
command: options.command,
|
|
8053
|
-
securityOpt: options.securityOpt,
|
|
8054
|
-
platform: options.platform
|
|
8055
|
-
});
|
|
8056
|
-
} else {
|
|
8057
|
-
strategy = new RuntimeStrategy({
|
|
8058
|
-
image: options.image,
|
|
8059
|
-
installers: options.installers,
|
|
8060
|
-
volumes: options.volumes,
|
|
8061
|
-
resources: options.resources,
|
|
8062
|
-
env: options.env,
|
|
8063
|
-
name: options.name,
|
|
8064
|
-
command: options.command,
|
|
8065
|
-
securityOpt: options.securityOpt,
|
|
8066
|
-
platform: options.platform
|
|
8067
|
-
});
|
|
8597
|
+
context: options.context ?? ".",
|
|
8598
|
+
showBuildLogs: options.showBuildLogs ?? false,
|
|
8599
|
+
identity: options.platform
|
|
8600
|
+
}).create();
|
|
8068
8601
|
}
|
|
8069
|
-
return
|
|
8602
|
+
return new RuntimeStrategy(options, dockerEngine, {
|
|
8603
|
+
image: options.image ?? dockerEngine.defaultImage,
|
|
8604
|
+
installers: options.installers ?? []
|
|
8605
|
+
}).create();
|
|
8070
8606
|
}
|
|
8071
8607
|
async function useSandbox(options, fn) {
|
|
8072
8608
|
const sandbox = await createDockerSandbox(options);
|
|
@@ -8082,6 +8618,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
8082
8618
|
import { posix as posix2 } from "node:path";
|
|
8083
8619
|
|
|
8084
8620
|
// packages/context/src/lib/sandbox/strace/index.ts
|
|
8621
|
+
import spawn6 from "nano-spawn";
|
|
8085
8622
|
import { posix } from "node:path";
|
|
8086
8623
|
var STRACE_FLAGS = "-f -y -qq -e trace=%file,write,pwrite64,writev";
|
|
8087
8624
|
function buildStraceCommand(command, traceFile, traceDir) {
|
|
@@ -12005,6 +12542,13 @@ export {
|
|
|
12005
12542
|
AgentOsCreationError,
|
|
12006
12543
|
AgentOsNotAvailableError,
|
|
12007
12544
|
AgentOsSandboxError,
|
|
12545
|
+
AppleContainerCreationError,
|
|
12546
|
+
AppleContainerImageBuildError,
|
|
12547
|
+
AppleContainerSandboxError,
|
|
12548
|
+
AppleContainerVolumeCreateError,
|
|
12549
|
+
AppleContainerVolumeInspectError,
|
|
12550
|
+
AppleContainerVolumePathError,
|
|
12551
|
+
AppleContainerVolumeRemoveError,
|
|
12008
12552
|
AsyncResolver,
|
|
12009
12553
|
BM25Classifier,
|
|
12010
12554
|
BashException,
|
|
@@ -12012,6 +12556,10 @@ export {
|
|
|
12012
12556
|
ComposeStartError,
|
|
12013
12557
|
ComposeStrategy,
|
|
12014
12558
|
ContainerCreationError,
|
|
12559
|
+
ContainerSandboxError,
|
|
12560
|
+
ContainerSandboxStrategy,
|
|
12561
|
+
ContainerServiceNotRunningError,
|
|
12562
|
+
ContainerfileStrategy,
|
|
12015
12563
|
ContextEngine,
|
|
12016
12564
|
ContextRenderer,
|
|
12017
12565
|
ContextStore,
|
|
@@ -12023,9 +12571,7 @@ export {
|
|
|
12023
12571
|
DaytonaSandboxError,
|
|
12024
12572
|
DockerNotAvailableError,
|
|
12025
12573
|
DockerSandboxError,
|
|
12026
|
-
DockerSandboxStrategy,
|
|
12027
12574
|
DockerfileBuildError,
|
|
12028
|
-
DockerfileStrategy,
|
|
12029
12575
|
FragmentLoaderResolver,
|
|
12030
12576
|
FunctionResolver,
|
|
12031
12577
|
GeneratorResolver,
|
|
@@ -12095,6 +12641,7 @@ export {
|
|
|
12095
12641
|
correction,
|
|
12096
12642
|
createAdaptivePollingState,
|
|
12097
12643
|
createAgentOsSandbox,
|
|
12644
|
+
createAppleContainerSandbox,
|
|
12098
12645
|
createBashTool,
|
|
12099
12646
|
createBinaryBridges,
|
|
12100
12647
|
createDaytonaSandbox,
|
|
@@ -12110,6 +12657,7 @@ export {
|
|
|
12110
12657
|
defineSubcommandGroup,
|
|
12111
12658
|
discoverSkillMappings,
|
|
12112
12659
|
discoverSkillsInDirectory,
|
|
12660
|
+
dockerEngine,
|
|
12113
12661
|
downloadAndInstall,
|
|
12114
12662
|
elapsedExceeds,
|
|
12115
12663
|
encodeSerializedValue,
|
|
@@ -12138,6 +12686,7 @@ export {
|
|
|
12138
12686
|
hint,
|
|
12139
12687
|
hourChanged,
|
|
12140
12688
|
identity,
|
|
12689
|
+
isAppleContainerfileOptions,
|
|
12141
12690
|
isComposeOptions,
|
|
12142
12691
|
isConditionalReminder,
|
|
12143
12692
|
isDebianBased,
|
|
@@ -12182,7 +12731,6 @@ export {
|
|
|
12182
12731
|
resetAdaptivePolling,
|
|
12183
12732
|
resolveReminder,
|
|
12184
12733
|
resolveReminderAsync,
|
|
12185
|
-
resolveReminderText,
|
|
12186
12734
|
resolveTz,
|
|
12187
12735
|
role,
|
|
12188
12736
|
runGuardrailChain,
|
|
@@ -12208,6 +12756,7 @@ export {
|
|
|
12208
12756
|
timeReminder,
|
|
12209
12757
|
toFragment,
|
|
12210
12758
|
toMessageFragment,
|
|
12759
|
+
toToolReminderModelOutput,
|
|
12211
12760
|
toolCall,
|
|
12212
12761
|
toolCallCount,
|
|
12213
12762
|
toolCalled,
|
|
@@ -12216,6 +12765,7 @@ export {
|
|
|
12216
12765
|
urlBinary,
|
|
12217
12766
|
usageExceeds,
|
|
12218
12767
|
useAgentOsSandbox,
|
|
12768
|
+
useAppleContainerSandbox,
|
|
12219
12769
|
useBashMeta,
|
|
12220
12770
|
useSandbox,
|
|
12221
12771
|
user,
|