@deepagents/context 1.0.0 → 2.2.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 +18 -14
- package/dist/browser.js +37 -2
- package/dist/browser.js.map +3 -3
- package/dist/index.js +1564 -454
- package/dist/index.js.map +4 -4
- package/dist/lib/guardrail.d.ts +2 -6
- package/dist/lib/guardrail.d.ts.map +1 -1
- package/dist/lib/sandbox/abort.d.ts +24 -0
- package/dist/lib/sandbox/abort.d.ts.map +1 -0
- package/dist/lib/sandbox/agent-os-sandbox.d.ts +8 -7
- package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/bash-tool.d.ts +29 -16
- package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
- package/dist/lib/sandbox/daytona-sandbox.d.ts +63 -0
- package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -0
- package/dist/lib/sandbox/docker-sandbox-errors.d.ts +28 -7
- package/dist/lib/sandbox/docker-sandbox-errors.d.ts.map +1 -1
- package/dist/lib/sandbox/docker-sandbox.d.ts +84 -19
- package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/file-events.d.ts +33 -38
- package/dist/lib/sandbox/file-events.d.ts.map +1 -1
- package/dist/lib/sandbox/index.d.ts +2 -1
- package/dist/lib/sandbox/index.d.ts.map +1 -1
- package/dist/lib/sandbox/installers/bin.d.ts +40 -0
- package/dist/lib/sandbox/installers/bin.d.ts.map +1 -0
- package/dist/lib/sandbox/installers/index.d.ts +1 -0
- package/dist/lib/sandbox/installers/index.d.ts.map +1 -1
- package/dist/lib/sandbox/types.d.ts +51 -6
- package/dist/lib/sandbox/types.d.ts.map +1 -1
- package/dist/lib/sandbox/virtual-sandbox.d.ts +2 -2
- package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
- package/dist/lib/save/save-pipeline.d.ts.map +1 -1
- package/dist/lib/skills/fragments.d.ts +2 -2
- package/dist/lib/store/sqlite.store.d.ts +1 -0
- package/dist/lib/store/sqlite.store.d.ts.map +1 -1
- package/dist/lib/store/store.d.ts +8 -0
- package/dist/lib/store/store.d.ts.map +1 -1
- package/dist/lib/stream/postgres.stream-store.d.ts.map +1 -1
- package/dist/lib/stream/sqlite.stream-store.d.ts.map +1 -1
- package/dist/lib/stream/stream-manager.d.ts +1 -1
- package/dist/lib/stream/stream-manager.d.ts.map +1 -1
- package/dist/lib/stream/stream-store.d.ts +8 -1
- package/dist/lib/stream/stream-store.d.ts.map +1 -1
- package/dist/lib/stream/types.d.ts +7 -0
- package/dist/lib/stream/types.d.ts.map +1 -0
- package/dist/lib/stream-buffer.d.ts.map +1 -1
- package/package.json +8 -12
- package/dist/lib/sandbox/container-tool.d.ts +0 -188
- package/dist/lib/sandbox/container-tool.d.ts.map +0 -1
- package/dist/lib/sandboxes/openai.d.ts +0 -6
- package/dist/lib/sandboxes/openai.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2623,6 +2623,8 @@ var SavePipeline = class {
|
|
|
2623
2623
|
async persist() {
|
|
2624
2624
|
let parentId = this.#engine.getActiveBranch().headMessageId;
|
|
2625
2625
|
const now = Date.now();
|
|
2626
|
+
const messages = [];
|
|
2627
|
+
const pendingMessagesById = /* @__PURE__ */ new Map();
|
|
2626
2628
|
for (const fragment2 of this.#pending) {
|
|
2627
2629
|
if (!fragment2.codec) {
|
|
2628
2630
|
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
@@ -2630,7 +2632,7 @@ var SavePipeline = class {
|
|
|
2630
2632
|
const msgId = fragment2.id ?? crypto.randomUUID();
|
|
2631
2633
|
let msgParentId = parentId;
|
|
2632
2634
|
if (!this.#shouldBranch && msgId === parentId) {
|
|
2633
|
-
const existing = await this.#engine.store.getMessage(msgId);
|
|
2635
|
+
const existing = pendingMessagesById.get(msgId) ?? await this.#engine.store.getMessage(msgId);
|
|
2634
2636
|
if (existing) msgParentId = existing.parentId;
|
|
2635
2637
|
}
|
|
2636
2638
|
const messageData = {
|
|
@@ -2642,7 +2644,8 @@ var SavePipeline = class {
|
|
|
2642
2644
|
data: fragment2.codec.encode(),
|
|
2643
2645
|
createdAt: now
|
|
2644
2646
|
};
|
|
2645
|
-
|
|
2647
|
+
messages.push(messageData);
|
|
2648
|
+
pendingMessagesById.set(messageData.id, messageData);
|
|
2646
2649
|
parentId = messageData.id;
|
|
2647
2650
|
}
|
|
2648
2651
|
if (parentId === null) {
|
|
@@ -2650,6 +2653,7 @@ var SavePipeline = class {
|
|
|
2650
2653
|
"Pipeline persisted no messages but pending was not empty"
|
|
2651
2654
|
);
|
|
2652
2655
|
}
|
|
2656
|
+
await this.#engine.store.addMessages(messages);
|
|
2653
2657
|
await this.#engine.commitHead(parentId);
|
|
2654
2658
|
return { headMessageId: parentId };
|
|
2655
2659
|
}
|
|
@@ -2765,6 +2769,18 @@ var ddl_sqlite_default = "-- Context Store DDL for SQLite\n-- This schema implem
|
|
|
2765
2769
|
|
|
2766
2770
|
// packages/context/src/lib/store/store.ts
|
|
2767
2771
|
var ContextStore = class {
|
|
2772
|
+
/**
|
|
2773
|
+
* Add multiple messages to the graph.
|
|
2774
|
+
*
|
|
2775
|
+
* Stores can override this for atomic/batched persistence. The default keeps
|
|
2776
|
+
* the single-message contract for implementations that do not need a custom
|
|
2777
|
+
* batch path.
|
|
2778
|
+
*/
|
|
2779
|
+
async addMessages(messages) {
|
|
2780
|
+
for (const message2 of messages) {
|
|
2781
|
+
await this.addMessage(message2);
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2768
2784
|
};
|
|
2769
2785
|
|
|
2770
2786
|
// packages/context/src/lib/store/sqlite.store.ts
|
|
@@ -2972,7 +2988,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2972
2988
|
// ==========================================================================
|
|
2973
2989
|
// Message Operations (Graph Nodes)
|
|
2974
2990
|
// ==========================================================================
|
|
2975
|
-
|
|
2991
|
+
#insertMessage(message2) {
|
|
2976
2992
|
if (message2.parentId === message2.id) {
|
|
2977
2993
|
throw new Error(`Message ${message2.id} cannot be its own parent`);
|
|
2978
2994
|
}
|
|
@@ -2999,6 +3015,17 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2999
3015
|
VALUES (?, ?, ?, ?)`
|
|
3000
3016
|
).run(message2.id, message2.chatId, message2.name, content);
|
|
3001
3017
|
}
|
|
3018
|
+
async addMessage(message2) {
|
|
3019
|
+
this.#insertMessage(message2);
|
|
3020
|
+
}
|
|
3021
|
+
async addMessages(messages) {
|
|
3022
|
+
if (messages.length === 0) return;
|
|
3023
|
+
this.#useTransaction(() => {
|
|
3024
|
+
for (const message2 of messages) {
|
|
3025
|
+
this.#insertMessage(message2);
|
|
3026
|
+
}
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3002
3029
|
async getMessage(messageId) {
|
|
3003
3030
|
const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
|
|
3004
3031
|
messageId
|
|
@@ -4697,7 +4724,7 @@ async function chat(agent2, options = {}) {
|
|
|
4697
4724
|
if (isAborted) {
|
|
4698
4725
|
normalizedMessage.parts = sanitizeAbortedParts(normalizedMessage.parts);
|
|
4699
4726
|
}
|
|
4700
|
-
const drained = sandbox.drainFileEvents
|
|
4727
|
+
const drained = sandbox.drainFileEvents();
|
|
4701
4728
|
const fileEvents = isAborted ? [] : drained;
|
|
4702
4729
|
const finalMetadata = await options.finalAssistantMetadata?.(normalizedMessage);
|
|
4703
4730
|
const mergedMetadata = {
|
|
@@ -5454,15 +5481,15 @@ function dateReminder(options) {
|
|
|
5454
5481
|
const now = /* @__PURE__ */ new Date();
|
|
5455
5482
|
const currentDate = formatDateKey(now, tz);
|
|
5456
5483
|
const currentDay = formatDayOfWeek(now, tz);
|
|
5457
|
-
let
|
|
5484
|
+
let diff2 = "";
|
|
5458
5485
|
if (ctx.lastMessageAt !== void 0) {
|
|
5459
5486
|
const prev = new Date(ctx.lastMessageAt);
|
|
5460
|
-
|
|
5487
|
+
diff2 = formatDiff([
|
|
5461
5488
|
diffLine("date", formatDateKey(prev, tz), currentDate),
|
|
5462
5489
|
diffLine("day of week", formatDayOfWeek(prev, tz), currentDay)
|
|
5463
5490
|
]);
|
|
5464
5491
|
}
|
|
5465
|
-
return `${
|
|
5492
|
+
return `${diff2}Date: ${currentDate}
|
|
5466
5493
|
Day of Week: ${currentDay}`;
|
|
5467
5494
|
},
|
|
5468
5495
|
{ when: dayChanged(options), asPart: false }
|
|
@@ -5474,14 +5501,14 @@ function timeReminder(options) {
|
|
|
5474
5501
|
const tz = resolveTz(options, ctx);
|
|
5475
5502
|
const now = /* @__PURE__ */ new Date();
|
|
5476
5503
|
const currentTime = formatTime(now, tz);
|
|
5477
|
-
let
|
|
5504
|
+
let diff2 = "";
|
|
5478
5505
|
if (ctx.lastMessageAt !== void 0) {
|
|
5479
5506
|
const prev = new Date(ctx.lastMessageAt);
|
|
5480
|
-
|
|
5507
|
+
diff2 = formatDiff([
|
|
5481
5508
|
diffLine("hour", formatHour(prev, tz), formatHour(now, tz))
|
|
5482
5509
|
]);
|
|
5483
5510
|
}
|
|
5484
|
-
return `${
|
|
5511
|
+
return `${diff2}Time: ${currentTime}`;
|
|
5485
5512
|
},
|
|
5486
5513
|
{ when: hourChanged(options), asPart: false }
|
|
5487
5514
|
);
|
|
@@ -5492,14 +5519,14 @@ function monthReminder(options) {
|
|
|
5492
5519
|
const tz = resolveTz(options, ctx);
|
|
5493
5520
|
const now = /* @__PURE__ */ new Date();
|
|
5494
5521
|
const currentMonth = formatMonthName(now, tz);
|
|
5495
|
-
let
|
|
5522
|
+
let diff2 = "";
|
|
5496
5523
|
if (ctx.lastMessageAt !== void 0) {
|
|
5497
5524
|
const prev = new Date(ctx.lastMessageAt);
|
|
5498
|
-
|
|
5525
|
+
diff2 = formatDiff([
|
|
5499
5526
|
diffLine("month", formatMonthName(prev, tz), currentMonth)
|
|
5500
5527
|
]);
|
|
5501
5528
|
}
|
|
5502
|
-
return `${
|
|
5529
|
+
return `${diff2}Month: ${currentMonth}`;
|
|
5503
5530
|
},
|
|
5504
5531
|
{ when: monthChanged(options), asPart: false }
|
|
5505
5532
|
);
|
|
@@ -5510,14 +5537,14 @@ function yearReminder(options) {
|
|
|
5510
5537
|
const tz = resolveTz(options, ctx);
|
|
5511
5538
|
const now = /* @__PURE__ */ new Date();
|
|
5512
5539
|
const currentYear = formatYear(now, tz);
|
|
5513
|
-
let
|
|
5540
|
+
let diff2 = "";
|
|
5514
5541
|
if (ctx.lastMessageAt !== void 0) {
|
|
5515
5542
|
const prev = new Date(ctx.lastMessageAt);
|
|
5516
|
-
|
|
5543
|
+
diff2 = formatDiff([
|
|
5517
5544
|
diffLine("year", formatYear(prev, tz), currentYear)
|
|
5518
5545
|
]);
|
|
5519
5546
|
}
|
|
5520
|
-
return `${
|
|
5547
|
+
return `${diff2}Year: ${currentYear}`;
|
|
5521
5548
|
},
|
|
5522
5549
|
{ when: yearChanged(options), asPart: false }
|
|
5523
5550
|
);
|
|
@@ -5528,14 +5555,14 @@ function seasonReminder(options) {
|
|
|
5528
5555
|
const tz = resolveTz(options, ctx);
|
|
5529
5556
|
const now = /* @__PURE__ */ new Date();
|
|
5530
5557
|
const currentSeason = getSeason(getMonthIndex(now, tz));
|
|
5531
|
-
let
|
|
5558
|
+
let diff2 = "";
|
|
5532
5559
|
if (ctx.lastMessageAt !== void 0) {
|
|
5533
5560
|
const prev = new Date(ctx.lastMessageAt);
|
|
5534
|
-
|
|
5561
|
+
diff2 = formatDiff([
|
|
5535
5562
|
diffLine("season", getSeason(getMonthIndex(prev, tz)), currentSeason)
|
|
5536
5563
|
]);
|
|
5537
5564
|
}
|
|
5538
|
-
return `${
|
|
5565
|
+
return `${diff2}Season: ${currentSeason}`;
|
|
5539
5566
|
},
|
|
5540
5567
|
{ when: seasonChanged(options), asPart: false }
|
|
5541
5568
|
);
|
|
@@ -5553,11 +5580,11 @@ function localeReminder() {
|
|
|
5553
5580
|
const current = getLocaleFromMessage(ctx.currentMessage);
|
|
5554
5581
|
if (!current) return "";
|
|
5555
5582
|
const last = getLocaleFromMessage(ctx.lastMessage);
|
|
5556
|
-
const
|
|
5583
|
+
const diff2 = last ? formatDiff([
|
|
5557
5584
|
diffLine("language", last.language, current.language),
|
|
5558
5585
|
diffLine("timezone", last.timeZone, current.timeZone)
|
|
5559
5586
|
]) : "";
|
|
5560
|
-
return `${
|
|
5587
|
+
return `${diff2}Language: ${current.language}
|
|
5561
5588
|
Timezone: ${current.timeZone}`;
|
|
5562
5589
|
},
|
|
5563
5590
|
{ when: whenFn, asPart: false }
|
|
@@ -5829,9 +5856,37 @@ function render(tag, ...fragments) {
|
|
|
5829
5856
|
return renderer.render([wrapped]);
|
|
5830
5857
|
}
|
|
5831
5858
|
|
|
5859
|
+
// packages/context/src/lib/sandbox/abort.ts
|
|
5860
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5861
|
+
var ambientAbortSignal = new AsyncLocalStorage();
|
|
5862
|
+
function runWithAbortSignal(signal, fn) {
|
|
5863
|
+
return ambientAbortSignal.run(signal, fn);
|
|
5864
|
+
}
|
|
5865
|
+
function withAbortSignal(sandbox) {
|
|
5866
|
+
const decorated = {
|
|
5867
|
+
...sandbox,
|
|
5868
|
+
async executeCommand(command, options) {
|
|
5869
|
+
const signal = options?.signal ?? ambientAbortSignal.getStore();
|
|
5870
|
+
return sandbox.executeCommand(
|
|
5871
|
+
command,
|
|
5872
|
+
signal ? { ...options, signal } : options
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
};
|
|
5876
|
+
if (sandbox.spawn) {
|
|
5877
|
+
const innerSpawn = sandbox.spawn.bind(sandbox);
|
|
5878
|
+
decorated.spawn = (command, options) => {
|
|
5879
|
+
const signal = options?.signal ?? ambientAbortSignal.getStore();
|
|
5880
|
+
return innerSpawn(command, signal ? { ...options, signal } : options);
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
return decorated;
|
|
5884
|
+
}
|
|
5885
|
+
|
|
5832
5886
|
// packages/context/src/lib/sandbox/agent-os-sandbox.ts
|
|
5833
5887
|
import "bash-tool";
|
|
5834
|
-
|
|
5888
|
+
import { PassThrough, Readable } from "node:stream";
|
|
5889
|
+
var decoder = new TextDecoder();
|
|
5835
5890
|
var AgentOsSandboxError = class extends Error {
|
|
5836
5891
|
constructor(message2) {
|
|
5837
5892
|
super(message2);
|
|
@@ -5863,6 +5918,49 @@ async function importAgentOs() {
|
|
|
5863
5918
|
);
|
|
5864
5919
|
}
|
|
5865
5920
|
}
|
|
5921
|
+
var SIGKILL_EXIT_CODE = 9;
|
|
5922
|
+
function startKernelProcess(os, command, options) {
|
|
5923
|
+
const { pid } = os.spawn("sh", ["-c", command], options);
|
|
5924
|
+
const stdout = new PassThrough();
|
|
5925
|
+
const stderr = new PassThrough();
|
|
5926
|
+
const unsubOut = os.onProcessStdout(pid, (chunk) => stdout.write(chunk));
|
|
5927
|
+
const unsubErr = os.onProcessStderr(pid, (chunk) => stderr.write(chunk));
|
|
5928
|
+
let killSignalled = false;
|
|
5929
|
+
const exit = os.waitProcess(pid).finally(() => {
|
|
5930
|
+
unsubOut();
|
|
5931
|
+
unsubErr();
|
|
5932
|
+
stdout.end();
|
|
5933
|
+
stderr.end();
|
|
5934
|
+
});
|
|
5935
|
+
return {
|
|
5936
|
+
pid,
|
|
5937
|
+
stdout,
|
|
5938
|
+
stderr,
|
|
5939
|
+
exit,
|
|
5940
|
+
kill: () => {
|
|
5941
|
+
if (killSignalled) return;
|
|
5942
|
+
killSignalled = true;
|
|
5943
|
+
os.killProcess(pid);
|
|
5944
|
+
},
|
|
5945
|
+
wasKilled: (exitCode) => killSignalled && exitCode === SIGKILL_EXIT_CODE
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
async function readAll(stream) {
|
|
5949
|
+
const chunks = [];
|
|
5950
|
+
for await (const chunk of stream) chunks.push(chunk);
|
|
5951
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
5952
|
+
}
|
|
5953
|
+
function bindAbort(signal, onAbort) {
|
|
5954
|
+
if (!signal) return () => {
|
|
5955
|
+
};
|
|
5956
|
+
if (signal.aborted) {
|
|
5957
|
+
onAbort();
|
|
5958
|
+
return () => {
|
|
5959
|
+
};
|
|
5960
|
+
}
|
|
5961
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5962
|
+
return () => signal.removeEventListener("abort", onAbort);
|
|
5963
|
+
}
|
|
5866
5964
|
async function createAgentOsSandbox(options = {}) {
|
|
5867
5965
|
const { AgentOs } = await importAgentOs();
|
|
5868
5966
|
let os;
|
|
@@ -5873,27 +5971,56 @@ async function createAgentOsSandbox(options = {}) {
|
|
|
5873
5971
|
throw new AgentOsCreationError(err.message, err);
|
|
5874
5972
|
}
|
|
5875
5973
|
return {
|
|
5876
|
-
async executeCommand(command) {
|
|
5974
|
+
async executeCommand(command, { signal } = {}) {
|
|
5975
|
+
if (signal?.aborted) {
|
|
5976
|
+
return { stdout: "", stderr: "", exitCode: SIGKILL_EXIT_CODE };
|
|
5977
|
+
}
|
|
5978
|
+
const proc = startKernelProcess(os, command, {});
|
|
5979
|
+
const unbind = bindAbort(signal, proc.kill);
|
|
5877
5980
|
try {
|
|
5878
|
-
const
|
|
5981
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
5982
|
+
readAll(proc.stdout),
|
|
5983
|
+
readAll(proc.stderr),
|
|
5984
|
+
proc.exit
|
|
5985
|
+
]);
|
|
5986
|
+
return { stdout, stderr, exitCode };
|
|
5987
|
+
} finally {
|
|
5988
|
+
unbind();
|
|
5989
|
+
}
|
|
5990
|
+
},
|
|
5991
|
+
spawn(command, { signal, env, cwd } = {}) {
|
|
5992
|
+
if (signal?.aborted) {
|
|
5993
|
+
const empty = () => new ReadableStream({ start: (c) => c.close() });
|
|
5879
5994
|
return {
|
|
5880
|
-
stdout:
|
|
5881
|
-
stderr:
|
|
5882
|
-
|
|
5995
|
+
stdout: empty(),
|
|
5996
|
+
stderr: empty(),
|
|
5997
|
+
exit: Promise.resolve({
|
|
5998
|
+
code: null,
|
|
5999
|
+
signal: "SIGKILL",
|
|
6000
|
+
success: false
|
|
6001
|
+
})
|
|
5883
6002
|
};
|
|
5884
|
-
}
|
|
5885
|
-
|
|
6003
|
+
}
|
|
6004
|
+
const proc = startKernelProcess(os, command, { env, cwd });
|
|
6005
|
+
const unbind = bindAbort(signal, proc.kill);
|
|
6006
|
+
const exit = proc.exit.then((code) => {
|
|
6007
|
+
const killed = proc.wasKilled(code);
|
|
5886
6008
|
return {
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
6009
|
+
code: killed ? null : code,
|
|
6010
|
+
signal: killed ? "SIGKILL" : null,
|
|
6011
|
+
success: !killed && code === 0
|
|
5890
6012
|
};
|
|
5891
|
-
}
|
|
6013
|
+
}).finally(unbind);
|
|
6014
|
+
return {
|
|
6015
|
+
stdout: Readable.toWeb(proc.stdout),
|
|
6016
|
+
stderr: Readable.toWeb(proc.stderr),
|
|
6017
|
+
exit
|
|
6018
|
+
};
|
|
5892
6019
|
},
|
|
5893
6020
|
async readFile(path5) {
|
|
5894
6021
|
try {
|
|
5895
6022
|
const bytes = await os.readFile(path5);
|
|
5896
|
-
return
|
|
6023
|
+
return decoder.decode(bytes);
|
|
5897
6024
|
} catch (error) {
|
|
5898
6025
|
throw new Error(
|
|
5899
6026
|
`Failed to read file "${path5}": ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -5935,8 +6062,8 @@ var BashException = class extends Error {
|
|
|
5935
6062
|
};
|
|
5936
6063
|
|
|
5937
6064
|
// packages/context/src/lib/sandbox/bash-meta.ts
|
|
5938
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5939
|
-
var store = new
|
|
6065
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
6066
|
+
var store = new AsyncLocalStorage2();
|
|
5940
6067
|
function runWithBashMeta(fn) {
|
|
5941
6068
|
return store.run({ hidden: {} }, fn);
|
|
5942
6069
|
}
|
|
@@ -5966,6 +6093,160 @@ import {
|
|
|
5966
6093
|
} from "bash-tool";
|
|
5967
6094
|
import z3 from "zod";
|
|
5968
6095
|
|
|
6096
|
+
// packages/context/src/lib/sandbox/file-events.ts
|
|
6097
|
+
var SnapshotFailedError = class extends Error {
|
|
6098
|
+
stderr;
|
|
6099
|
+
constructor(message2, stderr) {
|
|
6100
|
+
super(message2);
|
|
6101
|
+
this.name = "SnapshotFailedError";
|
|
6102
|
+
this.stderr = stderr;
|
|
6103
|
+
}
|
|
6104
|
+
};
|
|
6105
|
+
function shellQuote(value) {
|
|
6106
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6107
|
+
}
|
|
6108
|
+
async function snapshot(execute, destination) {
|
|
6109
|
+
const probe = await execute(`[ -d ${shellQuote(destination)} ]`);
|
|
6110
|
+
if (probe.exitCode !== 0) return /* @__PURE__ */ new Map();
|
|
6111
|
+
const list = await execute(
|
|
6112
|
+
`find ${shellQuote(destination)} -type f -print0 | while IFS= read -r -d '' p; do sha256sum "$p"; done`
|
|
6113
|
+
);
|
|
6114
|
+
if (list.exitCode !== 0) {
|
|
6115
|
+
throw new SnapshotFailedError(
|
|
6116
|
+
`snapshot failed for ${destination}`,
|
|
6117
|
+
list.stderr
|
|
6118
|
+
);
|
|
6119
|
+
}
|
|
6120
|
+
const snap = /* @__PURE__ */ new Map();
|
|
6121
|
+
if (!list.stdout) return snap;
|
|
6122
|
+
for (const line of list.stdout.split("\n")) {
|
|
6123
|
+
if (line.length < 66) continue;
|
|
6124
|
+
snap.set(line.slice(66), line.slice(0, 64));
|
|
6125
|
+
}
|
|
6126
|
+
return snap;
|
|
6127
|
+
}
|
|
6128
|
+
function lazyReadable(innerPromise) {
|
|
6129
|
+
let reader = null;
|
|
6130
|
+
return new ReadableStream({
|
|
6131
|
+
async pull(controller) {
|
|
6132
|
+
if (!reader) {
|
|
6133
|
+
const inner = await innerPromise;
|
|
6134
|
+
reader = inner.getReader();
|
|
6135
|
+
}
|
|
6136
|
+
const { done, value } = await reader.read();
|
|
6137
|
+
if (done) controller.close();
|
|
6138
|
+
else controller.enqueue(value);
|
|
6139
|
+
},
|
|
6140
|
+
async cancel(reason) {
|
|
6141
|
+
if (reader) {
|
|
6142
|
+
await reader.cancel(reason);
|
|
6143
|
+
} else {
|
|
6144
|
+
const inner = await innerPromise;
|
|
6145
|
+
await inner.cancel(reason);
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
});
|
|
6149
|
+
}
|
|
6150
|
+
function diff(before, after) {
|
|
6151
|
+
const events = [];
|
|
6152
|
+
const ts = Date.now();
|
|
6153
|
+
for (const [path5, hash] of after) {
|
|
6154
|
+
const prior = before.get(path5);
|
|
6155
|
+
if (prior === void 0) {
|
|
6156
|
+
events.push({ path: path5, op: "write", timestamp: ts });
|
|
6157
|
+
} else if (prior !== hash) {
|
|
6158
|
+
events.push({ path: path5, op: "modify", timestamp: ts });
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
for (const path5 of before.keys()) {
|
|
6162
|
+
if (!after.has(path5)) {
|
|
6163
|
+
events.push({ path: path5, op: "delete", timestamp: ts });
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
return events;
|
|
6167
|
+
}
|
|
6168
|
+
function observeSandboxFileEvents(sandbox, options) {
|
|
6169
|
+
const { destination } = options;
|
|
6170
|
+
if (!destination) {
|
|
6171
|
+
throw new Error("observeSandboxFileEvents: destination is required");
|
|
6172
|
+
}
|
|
6173
|
+
const innerExecute = sandbox.executeCommand.bind(sandbox);
|
|
6174
|
+
const innerReadFile = sandbox.readFile.bind(sandbox);
|
|
6175
|
+
const innerWriteFiles = sandbox.writeFiles.bind(sandbox);
|
|
6176
|
+
const buffer = [];
|
|
6177
|
+
const observe = async (fn) => {
|
|
6178
|
+
const before = await snapshot(innerExecute, destination);
|
|
6179
|
+
const takeAfter = async () => {
|
|
6180
|
+
const after = await snapshot(innerExecute, destination);
|
|
6181
|
+
buffer.push(...diff(before, after));
|
|
6182
|
+
};
|
|
6183
|
+
try {
|
|
6184
|
+
const result = await fn();
|
|
6185
|
+
await takeAfter();
|
|
6186
|
+
return result;
|
|
6187
|
+
} catch (err) {
|
|
6188
|
+
await takeAfter().catch(() => {
|
|
6189
|
+
});
|
|
6190
|
+
throw err;
|
|
6191
|
+
}
|
|
6192
|
+
};
|
|
6193
|
+
const decorated = {
|
|
6194
|
+
async executeCommand(command, options2) {
|
|
6195
|
+
return observe(() => innerExecute(command, options2));
|
|
6196
|
+
},
|
|
6197
|
+
async readFile(path5) {
|
|
6198
|
+
const content = await innerReadFile(path5);
|
|
6199
|
+
buffer.push({ path: path5, op: "read", timestamp: Date.now() });
|
|
6200
|
+
return content;
|
|
6201
|
+
},
|
|
6202
|
+
async writeFiles(files) {
|
|
6203
|
+
await observe(() => innerWriteFiles(files));
|
|
6204
|
+
},
|
|
6205
|
+
dispose: sandbox.dispose.bind(sandbox)
|
|
6206
|
+
};
|
|
6207
|
+
if (sandbox.spawn) {
|
|
6208
|
+
const innerSpawn = sandbox.spawn.bind(sandbox);
|
|
6209
|
+
decorated.spawn = (command, options2) => {
|
|
6210
|
+
const started = (async () => {
|
|
6211
|
+
const before = await snapshot(innerExecute, destination).catch(
|
|
6212
|
+
() => /* @__PURE__ */ new Map()
|
|
6213
|
+
);
|
|
6214
|
+
const child = innerSpawn(command, options2);
|
|
6215
|
+
return { before, child };
|
|
6216
|
+
})();
|
|
6217
|
+
const exit = (async () => {
|
|
6218
|
+
const { before, child } = await started;
|
|
6219
|
+
try {
|
|
6220
|
+
return await child.exit;
|
|
6221
|
+
} finally {
|
|
6222
|
+
try {
|
|
6223
|
+
const after = await snapshot(innerExecute, destination);
|
|
6224
|
+
buffer.push(...diff(before, after));
|
|
6225
|
+
} catch {
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
})();
|
|
6229
|
+
const stdoutPromise = started.then((s) => s.child.stdout);
|
|
6230
|
+
const stderrPromise = started.then((s) => s.child.stderr);
|
|
6231
|
+
stdoutPromise.catch(() => {
|
|
6232
|
+
});
|
|
6233
|
+
stderrPromise.catch(() => {
|
|
6234
|
+
});
|
|
6235
|
+
return {
|
|
6236
|
+
stdout: lazyReadable(stdoutPromise),
|
|
6237
|
+
stderr: lazyReadable(stderrPromise),
|
|
6238
|
+
exit
|
|
6239
|
+
};
|
|
6240
|
+
};
|
|
6241
|
+
}
|
|
6242
|
+
return {
|
|
6243
|
+
sandbox: decorated,
|
|
6244
|
+
drain() {
|
|
6245
|
+
return buffer.splice(0, buffer.length);
|
|
6246
|
+
}
|
|
6247
|
+
};
|
|
6248
|
+
}
|
|
6249
|
+
|
|
5969
6250
|
// packages/context/src/lib/sandbox/upload-skills.ts
|
|
5970
6251
|
import * as path3 from "node:path";
|
|
5971
6252
|
|
|
@@ -6111,22 +6392,38 @@ async function uploadSkills(sandbox, inputs) {
|
|
|
6111
6392
|
|
|
6112
6393
|
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
6113
6394
|
var REASONING_INSTRUCTION = 'Every bash tool call must include a brief non-empty "reasoning" input explaining why the command is needed.';
|
|
6114
|
-
|
|
6115
|
-
|
|
6395
|
+
function withBashExceptionCatch(sandbox) {
|
|
6396
|
+
return {
|
|
6397
|
+
...sandbox,
|
|
6398
|
+
async executeCommand(command, options) {
|
|
6399
|
+
try {
|
|
6400
|
+
return await sandbox.executeCommand(command, options);
|
|
6401
|
+
} catch (err) {
|
|
6402
|
+
if (err instanceof BashException) return err.format();
|
|
6403
|
+
throw err;
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
};
|
|
6407
|
+
}
|
|
6408
|
+
async function createBashTool(options) {
|
|
6409
|
+
const {
|
|
6410
|
+
skills: skillInputs = [],
|
|
6411
|
+
extraInstructions,
|
|
6412
|
+
sandbox: backend,
|
|
6413
|
+
destination,
|
|
6414
|
+
...rest
|
|
6415
|
+
} = options;
|
|
6416
|
+
const observer = observeSandboxFileEvents(backend, {
|
|
6417
|
+
destination: destination ?? "/workspace"
|
|
6418
|
+
});
|
|
6419
|
+
const sandbox = withAbortSignal(withBashExceptionCatch(observer.sandbox));
|
|
6116
6420
|
const combinedInstructions = [extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
|
|
6117
6421
|
const toolkit = await externalCreateBashTool({
|
|
6118
6422
|
...rest,
|
|
6423
|
+
sandbox,
|
|
6424
|
+
destination,
|
|
6119
6425
|
extraInstructions: combinedInstructions
|
|
6120
6426
|
});
|
|
6121
|
-
const innerExecute = toolkit.sandbox.executeCommand.bind(toolkit.sandbox);
|
|
6122
|
-
toolkit.sandbox.executeCommand = async (cmd) => {
|
|
6123
|
-
try {
|
|
6124
|
-
return await innerExecute(cmd);
|
|
6125
|
-
} catch (err) {
|
|
6126
|
-
if (err instanceof BashException) return err.format();
|
|
6127
|
-
throw err;
|
|
6128
|
-
}
|
|
6129
|
-
};
|
|
6130
6427
|
const upstreamBash = toolkit.bash;
|
|
6131
6428
|
const originalExecute = upstreamBash.execute;
|
|
6132
6429
|
const toolBuilder = tool2;
|
|
@@ -6141,197 +6438,43 @@ async function createBashTool(options = {}) {
|
|
|
6141
6438
|
if (!originalExecute) {
|
|
6142
6439
|
throw new Error("bash tool execution is not available");
|
|
6143
6440
|
}
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
...result,
|
|
6162
|
-
...hasHidden ? { meta: state.hidden } : {},
|
|
6163
|
-
...hasReminder ? { reminder: state.reminder } : {}
|
|
6164
|
-
};
|
|
6165
|
-
});
|
|
6441
|
+
const { abortSignal } = execOptions;
|
|
6442
|
+
return runWithAbortSignal(
|
|
6443
|
+
abortSignal,
|
|
6444
|
+
() => runWithBashMeta(async () => {
|
|
6445
|
+
const result = await originalExecute({ command }, execOptions);
|
|
6446
|
+
const state = readBashMeta();
|
|
6447
|
+
if (!state) return result;
|
|
6448
|
+
const hasHidden = Object.keys(state.hidden).length > 0;
|
|
6449
|
+
const hasReminder = state.reminder !== void 0;
|
|
6450
|
+
if (!hasHidden && !hasReminder) return result;
|
|
6451
|
+
return {
|
|
6452
|
+
...result,
|
|
6453
|
+
...hasHidden ? { meta: state.hidden } : {},
|
|
6454
|
+
...hasReminder ? { reminder: state.reminder } : {}
|
|
6455
|
+
};
|
|
6456
|
+
})
|
|
6457
|
+
);
|
|
6166
6458
|
},
|
|
6167
6459
|
toModelOutput: ({ output }) => {
|
|
6168
6460
|
if (typeof output !== "object" || output === null) {
|
|
6169
6461
|
return { type: "json", value: output };
|
|
6170
6462
|
}
|
|
6171
|
-
const
|
|
6172
|
-
if (!("meta" in record)) {
|
|
6173
|
-
return { type: "json", value: record };
|
|
6174
|
-
}
|
|
6175
|
-
const { meta: _meta, ...visible } = record;
|
|
6463
|
+
const { meta: _meta, ...visible } = output;
|
|
6176
6464
|
return { type: "json", value: visible };
|
|
6177
6465
|
}
|
|
6178
6466
|
});
|
|
6179
|
-
const skills2 = await uploadSkills(
|
|
6467
|
+
const skills2 = await uploadSkills(sandbox, skillInputs);
|
|
6180
6468
|
return {
|
|
6181
6469
|
...toolkit,
|
|
6470
|
+
sandbox,
|
|
6182
6471
|
bash,
|
|
6183
6472
|
tools: { ...toolkit.tools, bash },
|
|
6184
|
-
skills: skills2
|
|
6473
|
+
skills: skills2,
|
|
6474
|
+
drainFileEvents: () => observer.drain()
|
|
6185
6475
|
};
|
|
6186
6476
|
}
|
|
6187
6477
|
|
|
6188
|
-
// packages/context/src/lib/sandbox/file-events.ts
|
|
6189
|
-
var ObservedFs = class {
|
|
6190
|
-
#base;
|
|
6191
|
-
#events = [];
|
|
6192
|
-
readdirWithFileTypes;
|
|
6193
|
-
constructor(base) {
|
|
6194
|
-
this.#base = base;
|
|
6195
|
-
if (base.readdirWithFileTypes) {
|
|
6196
|
-
this.readdirWithFileTypes = (path5) => base.readdirWithFileTypes(path5);
|
|
6197
|
-
}
|
|
6198
|
-
}
|
|
6199
|
-
drain() {
|
|
6200
|
-
const events = this.#events;
|
|
6201
|
-
this.#events = [];
|
|
6202
|
-
return events;
|
|
6203
|
-
}
|
|
6204
|
-
#record(op, path5) {
|
|
6205
|
-
this.#events.push({ path: path5, op, timestamp: Date.now() });
|
|
6206
|
-
}
|
|
6207
|
-
async readFile(path5, options) {
|
|
6208
|
-
const content = await this.#base.readFile(path5, options);
|
|
6209
|
-
this.#record("read", path5);
|
|
6210
|
-
return content;
|
|
6211
|
-
}
|
|
6212
|
-
async readFileBuffer(path5) {
|
|
6213
|
-
const content = await this.#base.readFileBuffer(path5);
|
|
6214
|
-
this.#record("read", path5);
|
|
6215
|
-
return content;
|
|
6216
|
-
}
|
|
6217
|
-
async writeFile(path5, content, options) {
|
|
6218
|
-
const existed = await this.#base.exists(path5);
|
|
6219
|
-
await this.#base.writeFile(path5, content, options);
|
|
6220
|
-
this.#record(existed ? "modify" : "write", path5);
|
|
6221
|
-
}
|
|
6222
|
-
async appendFile(path5, content, options) {
|
|
6223
|
-
const existed = await this.#base.exists(path5);
|
|
6224
|
-
await this.#base.appendFile(path5, content, options);
|
|
6225
|
-
this.#record(existed ? "modify" : "write", path5);
|
|
6226
|
-
}
|
|
6227
|
-
async rm(path5, options) {
|
|
6228
|
-
const toRecord = options?.recursive ? await this.#walk(path5, { includeRoot: true }) : await this.#base.exists(path5) ? [path5] : [];
|
|
6229
|
-
await this.#base.rm(path5, options);
|
|
6230
|
-
for (const p of toRecord) {
|
|
6231
|
-
this.#record("delete", p);
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
async cp(src, dest, options) {
|
|
6235
|
-
if (options?.recursive) {
|
|
6236
|
-
const sources = await this.#walk(src, { includeRoot: false });
|
|
6237
|
-
const destChecks = await Promise.all(
|
|
6238
|
-
sources.map(async (srcFile) => {
|
|
6239
|
-
const relative3 = srcFile.slice(src.length);
|
|
6240
|
-
const destFile = this.#joinPath(dest, relative3);
|
|
6241
|
-
return {
|
|
6242
|
-
srcFile,
|
|
6243
|
-
destFile,
|
|
6244
|
-
existed: await this.#base.exists(destFile)
|
|
6245
|
-
};
|
|
6246
|
-
})
|
|
6247
|
-
);
|
|
6248
|
-
await this.#base.cp(src, dest, options);
|
|
6249
|
-
for (const { srcFile, destFile, existed } of destChecks) {
|
|
6250
|
-
this.#record("read", srcFile);
|
|
6251
|
-
this.#record(existed ? "modify" : "write", destFile);
|
|
6252
|
-
}
|
|
6253
|
-
return;
|
|
6254
|
-
}
|
|
6255
|
-
const destExisted = await this.#base.exists(dest);
|
|
6256
|
-
await this.#base.cp(src, dest, options);
|
|
6257
|
-
this.#record("read", src);
|
|
6258
|
-
this.#record(destExisted ? "modify" : "write", dest);
|
|
6259
|
-
}
|
|
6260
|
-
async #walk(root, { includeRoot }) {
|
|
6261
|
-
if (!await this.#base.exists(root)) return [];
|
|
6262
|
-
const stat = await this.#base.stat(root);
|
|
6263
|
-
if (!stat.isDirectory) return [root];
|
|
6264
|
-
const out = [];
|
|
6265
|
-
const visit = async (dir) => {
|
|
6266
|
-
const entries = await this.#base.readdir(dir);
|
|
6267
|
-
for (const name of entries) {
|
|
6268
|
-
const child = this.#joinPath(dir, name);
|
|
6269
|
-
const childStat = await this.#base.stat(child);
|
|
6270
|
-
if (childStat.isDirectory) {
|
|
6271
|
-
await visit(child);
|
|
6272
|
-
} else {
|
|
6273
|
-
out.push(child);
|
|
6274
|
-
}
|
|
6275
|
-
}
|
|
6276
|
-
};
|
|
6277
|
-
await visit(root);
|
|
6278
|
-
if (includeRoot) out.push(root);
|
|
6279
|
-
return out;
|
|
6280
|
-
}
|
|
6281
|
-
#joinPath(a, b) {
|
|
6282
|
-
if (!b) return a;
|
|
6283
|
-
if (b.startsWith("/")) return `${a.replace(/\/$/, "")}${b}`;
|
|
6284
|
-
return a.endsWith("/") ? `${a}${b}` : `${a}/${b}`;
|
|
6285
|
-
}
|
|
6286
|
-
async mv(src, dest) {
|
|
6287
|
-
const destExisted = await this.#base.exists(dest);
|
|
6288
|
-
await this.#base.mv(src, dest);
|
|
6289
|
-
this.#record("delete", src);
|
|
6290
|
-
this.#record(destExisted ? "modify" : "write", dest);
|
|
6291
|
-
}
|
|
6292
|
-
async symlink(target, linkPath) {
|
|
6293
|
-
await this.#base.symlink(target, linkPath);
|
|
6294
|
-
this.#record("write", linkPath);
|
|
6295
|
-
}
|
|
6296
|
-
async link(existingPath, newPath) {
|
|
6297
|
-
await this.#base.link(existingPath, newPath);
|
|
6298
|
-
this.#record("write", newPath);
|
|
6299
|
-
}
|
|
6300
|
-
mkdir(path5, options) {
|
|
6301
|
-
return this.#base.mkdir(path5, options);
|
|
6302
|
-
}
|
|
6303
|
-
exists(path5) {
|
|
6304
|
-
return this.#base.exists(path5);
|
|
6305
|
-
}
|
|
6306
|
-
stat(path5) {
|
|
6307
|
-
return this.#base.stat(path5);
|
|
6308
|
-
}
|
|
6309
|
-
lstat(path5) {
|
|
6310
|
-
return this.#base.lstat(path5);
|
|
6311
|
-
}
|
|
6312
|
-
readdir(path5) {
|
|
6313
|
-
return this.#base.readdir(path5);
|
|
6314
|
-
}
|
|
6315
|
-
readlink(path5) {
|
|
6316
|
-
return this.#base.readlink(path5);
|
|
6317
|
-
}
|
|
6318
|
-
realpath(path5) {
|
|
6319
|
-
return this.#base.realpath(path5);
|
|
6320
|
-
}
|
|
6321
|
-
chmod(path5, mode) {
|
|
6322
|
-
return this.#base.chmod(path5, mode);
|
|
6323
|
-
}
|
|
6324
|
-
utimes(path5, atime, mtime) {
|
|
6325
|
-
return this.#base.utimes(path5, atime, mtime);
|
|
6326
|
-
}
|
|
6327
|
-
resolvePath(base, path5) {
|
|
6328
|
-
return this.#base.resolvePath(base, path5);
|
|
6329
|
-
}
|
|
6330
|
-
getAllPaths() {
|
|
6331
|
-
return this.#base.getAllPaths();
|
|
6332
|
-
}
|
|
6333
|
-
};
|
|
6334
|
-
|
|
6335
6478
|
// packages/context/src/lib/sandbox/binary-bridges.ts
|
|
6336
6479
|
import { existsSync as existsSync2 } from "fs";
|
|
6337
6480
|
import { defineCommand } from "just-bash";
|
|
@@ -6428,113 +6571,647 @@ function resolveRealCwd(ctx) {
|
|
|
6428
6571
|
return realCwd;
|
|
6429
6572
|
}
|
|
6430
6573
|
|
|
6431
|
-
// packages/context/src/lib/sandbox/
|
|
6574
|
+
// packages/context/src/lib/sandbox/daytona-sandbox.ts
|
|
6432
6575
|
import "bash-tool";
|
|
6433
|
-
import
|
|
6434
|
-
import {
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
var
|
|
6439
|
-
|
|
6440
|
-
constructor(message2,
|
|
6576
|
+
import { randomUUID } from "node:crypto";
|
|
6577
|
+
import { createRequire } from "node:module";
|
|
6578
|
+
var DAYTONA_DEFAULT_DESTINATION = "/home/daytona";
|
|
6579
|
+
var DAYTONA_EXIT_POLL_INTERVAL_MS = 250;
|
|
6580
|
+
var DAYTONA_EXIT_POLL_TIMEOUT_MS = 3e4;
|
|
6581
|
+
var requireOptional = createRequire(import.meta.url);
|
|
6582
|
+
var DaytonaSandboxError = class extends Error {
|
|
6583
|
+
constructor(message2, cause) {
|
|
6441
6584
|
super(message2);
|
|
6442
|
-
this.name = "
|
|
6443
|
-
this.containerId = containerId;
|
|
6444
|
-
}
|
|
6445
|
-
};
|
|
6446
|
-
var DockerNotAvailableError = class extends DockerSandboxError {
|
|
6447
|
-
constructor() {
|
|
6448
|
-
super("Docker is not available. Ensure Docker daemon is running.");
|
|
6449
|
-
this.name = "DockerNotAvailableError";
|
|
6450
|
-
}
|
|
6451
|
-
};
|
|
6452
|
-
var ContainerCreationError = class extends DockerSandboxError {
|
|
6453
|
-
image;
|
|
6454
|
-
cause;
|
|
6455
|
-
constructor(message2, image, cause) {
|
|
6456
|
-
super(`Failed to create container from image "${image}": ${message2}`);
|
|
6457
|
-
this.name = "ContainerCreationError";
|
|
6458
|
-
this.image = image;
|
|
6585
|
+
this.name = "DaytonaSandboxError";
|
|
6459
6586
|
this.cause = cause;
|
|
6460
6587
|
}
|
|
6461
6588
|
};
|
|
6462
|
-
var
|
|
6463
|
-
|
|
6464
|
-
image;
|
|
6465
|
-
packageManager;
|
|
6466
|
-
stderr;
|
|
6467
|
-
constructor(packages, image, packageManager, stderr, containerId) {
|
|
6589
|
+
var DaytonaNotAvailableError = class extends DaytonaSandboxError {
|
|
6590
|
+
constructor(cause) {
|
|
6468
6591
|
super(
|
|
6469
|
-
|
|
6470
|
-
|
|
6592
|
+
"@daytona/sdk is not installed. Install it with: npm install @daytona/sdk",
|
|
6593
|
+
cause
|
|
6471
6594
|
);
|
|
6472
|
-
this.name = "
|
|
6473
|
-
this.packages = packages;
|
|
6474
|
-
this.image = image;
|
|
6475
|
-
this.packageManager = packageManager;
|
|
6476
|
-
this.stderr = stderr;
|
|
6595
|
+
this.name = "DaytonaNotAvailableError";
|
|
6477
6596
|
}
|
|
6478
6597
|
};
|
|
6479
|
-
var
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
url;
|
|
6484
|
-
constructor(opts) {
|
|
6485
|
-
const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
|
|
6486
|
-
super(
|
|
6487
|
-
`Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
|
|
6488
|
-
opts.containerId
|
|
6489
|
-
);
|
|
6490
|
-
this.name = "InstallError";
|
|
6491
|
-
this.target = opts.target;
|
|
6492
|
-
this.source = opts.source;
|
|
6493
|
-
this.reason = opts.reason;
|
|
6494
|
-
this.url = opts.url;
|
|
6598
|
+
var DaytonaCreationError = class extends DaytonaSandboxError {
|
|
6599
|
+
constructor(message2, cause) {
|
|
6600
|
+
super(`Failed to create Daytona sandbox: ${message2}`, cause);
|
|
6601
|
+
this.name = "DaytonaCreationError";
|
|
6495
6602
|
}
|
|
6496
6603
|
};
|
|
6497
|
-
var
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
|
|
6502
|
-
super(details ? `${base} ${details}` : base, containerId);
|
|
6503
|
-
this.name = "MissingRuntimeError";
|
|
6504
|
-
this.runtime = runtime;
|
|
6505
|
-
this.required = required;
|
|
6604
|
+
var DaytonaCommandError = class extends DaytonaSandboxError {
|
|
6605
|
+
constructor(message2, cause) {
|
|
6606
|
+
super(message2, cause);
|
|
6607
|
+
this.name = "DaytonaCommandError";
|
|
6506
6608
|
}
|
|
6507
6609
|
};
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
this.containerPath = containerPath;
|
|
6610
|
+
async function createDaytonaSandbox(options = {}) {
|
|
6611
|
+
validateDaytonaOptions(options);
|
|
6612
|
+
const { Daytona } = await importDaytonaSdk();
|
|
6613
|
+
let client;
|
|
6614
|
+
try {
|
|
6615
|
+
client = new Daytona(createDaytonaConfig(options));
|
|
6616
|
+
} catch (error) {
|
|
6617
|
+
const err = toError(error);
|
|
6618
|
+
throw new DaytonaCreationError(err.message, err);
|
|
6518
6619
|
}
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6620
|
+
const attached = options.sandboxId !== void 0;
|
|
6621
|
+
const deleteOnDispose = options.deleteOnDispose ?? !attached;
|
|
6622
|
+
let sandbox;
|
|
6623
|
+
try {
|
|
6624
|
+
sandbox = attached ? await client.get(options.sandboxId) : await client.create(createDaytonaParams(options), {
|
|
6625
|
+
timeout: options.createTimeout,
|
|
6626
|
+
onSnapshotCreateLogs: options.onSnapshotCreateLogs
|
|
6627
|
+
});
|
|
6628
|
+
if (attached && sandbox.state && sandbox.state !== "started") {
|
|
6629
|
+
await sandbox.start?.(options.startTimeout ?? options.createTimeout);
|
|
6630
|
+
}
|
|
6631
|
+
} catch (error) {
|
|
6632
|
+
const err = toError(error);
|
|
6633
|
+
throw new DaytonaCreationError(err.message, err);
|
|
6526
6634
|
}
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6635
|
+
return createDaytonaSandboxMethods({
|
|
6636
|
+
client,
|
|
6637
|
+
sandbox,
|
|
6638
|
+
commandTimeout: options.commandTimeout,
|
|
6639
|
+
deleteOnDispose,
|
|
6640
|
+
deleteTimeout: options.deleteTimeout
|
|
6641
|
+
});
|
|
6642
|
+
}
|
|
6643
|
+
async function importDaytonaSdk() {
|
|
6644
|
+
try {
|
|
6645
|
+
const mod = requireOptional("@daytona/sdk");
|
|
6646
|
+
if (typeof mod.Daytona !== "function") {
|
|
6647
|
+
throw new DaytonaSandboxError(
|
|
6648
|
+
"@daytona/sdk did not export a Daytona constructor"
|
|
6649
|
+
);
|
|
6650
|
+
}
|
|
6651
|
+
return { Daytona: mod.Daytona };
|
|
6652
|
+
} catch (error) {
|
|
6653
|
+
if (error instanceof DaytonaSandboxError) {
|
|
6654
|
+
throw error;
|
|
6655
|
+
}
|
|
6656
|
+
const err = toError(error);
|
|
6657
|
+
if (isMissingDaytonaSdk(err)) {
|
|
6658
|
+
throw new DaytonaNotAvailableError(err);
|
|
6659
|
+
}
|
|
6660
|
+
throw err;
|
|
6536
6661
|
}
|
|
6537
|
-
}
|
|
6662
|
+
}
|
|
6663
|
+
function createDaytonaConfig(options) {
|
|
6664
|
+
return compactObject({
|
|
6665
|
+
apiKey: options.apiKey,
|
|
6666
|
+
jwtToken: options.jwtToken,
|
|
6667
|
+
organizationId: options.organizationId,
|
|
6668
|
+
apiUrl: options.apiUrl,
|
|
6669
|
+
target: options.target,
|
|
6670
|
+
otelEnabled: options.otelEnabled,
|
|
6671
|
+
_experimental: options.experimental
|
|
6672
|
+
});
|
|
6673
|
+
}
|
|
6674
|
+
function createDaytonaParams(options) {
|
|
6675
|
+
const base = compactObject({
|
|
6676
|
+
name: options.name,
|
|
6677
|
+
user: options.user,
|
|
6678
|
+
language: options.language,
|
|
6679
|
+
envVars: options.envVars,
|
|
6680
|
+
labels: options.labels,
|
|
6681
|
+
public: options.public,
|
|
6682
|
+
autoStopInterval: options.autoStopInterval,
|
|
6683
|
+
autoArchiveInterval: options.autoArchiveInterval,
|
|
6684
|
+
autoDeleteInterval: options.autoDeleteInterval,
|
|
6685
|
+
volumes: options.volumes,
|
|
6686
|
+
networkBlockAll: options.networkBlockAll,
|
|
6687
|
+
networkAllowList: options.networkAllowList,
|
|
6688
|
+
ephemeral: options.ephemeral
|
|
6689
|
+
});
|
|
6690
|
+
if (options.image) {
|
|
6691
|
+
return compactObject({
|
|
6692
|
+
...base,
|
|
6693
|
+
image: options.image,
|
|
6694
|
+
resources: options.resources
|
|
6695
|
+
});
|
|
6696
|
+
}
|
|
6697
|
+
const params = compactObject({
|
|
6698
|
+
...base,
|
|
6699
|
+
snapshot: options.snapshot
|
|
6700
|
+
});
|
|
6701
|
+
return Object.keys(params).length > 0 ? params : void 0;
|
|
6702
|
+
}
|
|
6703
|
+
function validateDaytonaOptions(options) {
|
|
6704
|
+
if (options.image && options.snapshot) {
|
|
6705
|
+
throw new DaytonaSandboxError(
|
|
6706
|
+
'Daytona sandbox options cannot include both "image" and "snapshot". Choose one environment source.'
|
|
6707
|
+
);
|
|
6708
|
+
}
|
|
6709
|
+
if (options.resources && !options.image) {
|
|
6710
|
+
throw new DaytonaSandboxError(
|
|
6711
|
+
'Daytona sandbox options can only include "resources" when creating from "image". The Daytona SDK does not apply resources during default or snapshot creation.'
|
|
6712
|
+
);
|
|
6713
|
+
}
|
|
6714
|
+
if (!options.sandboxId) {
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
const creationOnlyFields = [
|
|
6718
|
+
"name",
|
|
6719
|
+
"user",
|
|
6720
|
+
"snapshot",
|
|
6721
|
+
"image",
|
|
6722
|
+
"language",
|
|
6723
|
+
"envVars",
|
|
6724
|
+
"labels",
|
|
6725
|
+
"public",
|
|
6726
|
+
"resources",
|
|
6727
|
+
"volumes",
|
|
6728
|
+
"networkAllowList",
|
|
6729
|
+
"networkBlockAll",
|
|
6730
|
+
"autoStopInterval",
|
|
6731
|
+
"autoArchiveInterval",
|
|
6732
|
+
"autoDeleteInterval",
|
|
6733
|
+
"ephemeral",
|
|
6734
|
+
"onSnapshotCreateLogs"
|
|
6735
|
+
];
|
|
6736
|
+
const present = creationOnlyFields.filter((field) => {
|
|
6737
|
+
return options[field] !== void 0;
|
|
6738
|
+
});
|
|
6739
|
+
if (present.length > 0) {
|
|
6740
|
+
throw new DaytonaSandboxError(
|
|
6741
|
+
`Daytona sandbox options cannot combine "sandboxId" with creation options: ${present.join(", ")}`
|
|
6742
|
+
);
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
function createDaytonaSandboxMethods(args) {
|
|
6746
|
+
const { client, sandbox, commandTimeout, deleteOnDispose, deleteTimeout } = args;
|
|
6747
|
+
let disposed = false;
|
|
6748
|
+
const executeCommand = async (command, options) => {
|
|
6749
|
+
if (options?.signal?.aborted) {
|
|
6750
|
+
return abortedCommandResult();
|
|
6751
|
+
}
|
|
6752
|
+
let aborted = false;
|
|
6753
|
+
const abort = () => {
|
|
6754
|
+
aborted = true;
|
|
6755
|
+
};
|
|
6756
|
+
options?.signal?.addEventListener("abort", abort, { once: true });
|
|
6757
|
+
try {
|
|
6758
|
+
if (aborted || options?.signal?.aborted) {
|
|
6759
|
+
return abortedCommandResult();
|
|
6760
|
+
}
|
|
6761
|
+
const response = await sandbox.process.executeCommand(
|
|
6762
|
+
command,
|
|
6763
|
+
void 0,
|
|
6764
|
+
void 0,
|
|
6765
|
+
commandTimeout
|
|
6766
|
+
);
|
|
6767
|
+
if (aborted) return abortedCommandResult();
|
|
6768
|
+
return {
|
|
6769
|
+
stdout: response.result ?? response.artifacts?.stdout ?? "",
|
|
6770
|
+
stderr: "",
|
|
6771
|
+
exitCode: response.exitCode ?? 0
|
|
6772
|
+
};
|
|
6773
|
+
} catch (error) {
|
|
6774
|
+
if (aborted) return abortedCommandResult();
|
|
6775
|
+
const err = error;
|
|
6776
|
+
return {
|
|
6777
|
+
stdout: err.stdout ?? "",
|
|
6778
|
+
stderr: err.stderr ?? err.message ?? String(error),
|
|
6779
|
+
exitCode: err.exitCode ?? 1
|
|
6780
|
+
};
|
|
6781
|
+
} finally {
|
|
6782
|
+
options?.signal?.removeEventListener("abort", abort);
|
|
6783
|
+
}
|
|
6784
|
+
};
|
|
6785
|
+
return {
|
|
6786
|
+
executeCommand,
|
|
6787
|
+
spawn(command, options) {
|
|
6788
|
+
return spawnDaytonaProcess(sandbox, command, {
|
|
6789
|
+
...options,
|
|
6790
|
+
commandTimeout
|
|
6791
|
+
});
|
|
6792
|
+
},
|
|
6793
|
+
async readFile(path5) {
|
|
6794
|
+
try {
|
|
6795
|
+
const bytes = await sandbox.fs.downloadFile(path5);
|
|
6796
|
+
return Buffer.from(bytes).toString("utf-8");
|
|
6797
|
+
} catch (error) {
|
|
6798
|
+
throw new DaytonaCommandError(
|
|
6799
|
+
`Failed to read file "${path5}": ${toError(error).message}`,
|
|
6800
|
+
toError(error)
|
|
6801
|
+
);
|
|
6802
|
+
}
|
|
6803
|
+
},
|
|
6804
|
+
async writeFiles(files) {
|
|
6805
|
+
try {
|
|
6806
|
+
for (const dir of uniqueParentDirectories(files.map((f) => f.path))) {
|
|
6807
|
+
const result = await executeCommand(`mkdir -p ${shellQuote2(dir)}`);
|
|
6808
|
+
if (result.exitCode !== 0) {
|
|
6809
|
+
throw new DaytonaCommandError(
|
|
6810
|
+
`Failed to create directory "${dir}": ${result.stderr}`
|
|
6811
|
+
);
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6814
|
+
await sandbox.fs.uploadFiles(
|
|
6815
|
+
files.map((file) => ({
|
|
6816
|
+
source: Buffer.from(file.content),
|
|
6817
|
+
destination: file.path
|
|
6818
|
+
}))
|
|
6819
|
+
);
|
|
6820
|
+
} catch (error) {
|
|
6821
|
+
if (error instanceof DaytonaSandboxError) throw error;
|
|
6822
|
+
const err = toError(error);
|
|
6823
|
+
throw new DaytonaCommandError(
|
|
6824
|
+
`Failed to write files: ${err.message}`,
|
|
6825
|
+
err
|
|
6826
|
+
);
|
|
6827
|
+
}
|
|
6828
|
+
},
|
|
6829
|
+
async dispose() {
|
|
6830
|
+
if (disposed) return;
|
|
6831
|
+
disposed = true;
|
|
6832
|
+
try {
|
|
6833
|
+
if (deleteOnDispose) {
|
|
6834
|
+
if (sandbox.delete) await sandbox.delete(deleteTimeout);
|
|
6835
|
+
else if (client.delete) await client.delete(sandbox, deleteTimeout);
|
|
6836
|
+
}
|
|
6837
|
+
} finally {
|
|
6838
|
+
await client[Symbol.asyncDispose]?.().catch(() => {
|
|
6839
|
+
});
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
};
|
|
6843
|
+
}
|
|
6844
|
+
function spawnDaytonaProcess(sandbox, command, options = {}) {
|
|
6845
|
+
const sessionId = createSessionId("spawn");
|
|
6846
|
+
const stdout = createTextReadable();
|
|
6847
|
+
const stderr = createTextReadable();
|
|
6848
|
+
const exit = runSpawnedSession({
|
|
6849
|
+
sandbox,
|
|
6850
|
+
sessionId,
|
|
6851
|
+
command: buildSessionCommand(command, options),
|
|
6852
|
+
signal: options.signal,
|
|
6853
|
+
commandTimeout: options.commandTimeout,
|
|
6854
|
+
stdout,
|
|
6855
|
+
stderr
|
|
6856
|
+
});
|
|
6857
|
+
return {
|
|
6858
|
+
stdout: stdout.stream,
|
|
6859
|
+
stderr: stderr.stream,
|
|
6860
|
+
exit
|
|
6861
|
+
};
|
|
6862
|
+
}
|
|
6863
|
+
async function runSpawnedSession(args) {
|
|
6864
|
+
const {
|
|
6865
|
+
sandbox,
|
|
6866
|
+
sessionId,
|
|
6867
|
+
command,
|
|
6868
|
+
signal,
|
|
6869
|
+
commandTimeout,
|
|
6870
|
+
stdout,
|
|
6871
|
+
stderr
|
|
6872
|
+
} = args;
|
|
6873
|
+
let sessionCreated = false;
|
|
6874
|
+
let aborted = signal?.aborted ?? false;
|
|
6875
|
+
let resolveAbort;
|
|
6876
|
+
const abortPromise = new Promise((resolve4) => {
|
|
6877
|
+
resolveAbort = () => resolve4("aborted");
|
|
6878
|
+
});
|
|
6879
|
+
const abort = () => {
|
|
6880
|
+
aborted = true;
|
|
6881
|
+
resolveAbort?.();
|
|
6882
|
+
if (sessionCreated) {
|
|
6883
|
+
sandbox.process.deleteSession(sessionId).catch(() => {
|
|
6884
|
+
});
|
|
6885
|
+
}
|
|
6886
|
+
};
|
|
6887
|
+
if (signal) {
|
|
6888
|
+
if (signal.aborted) abort();
|
|
6889
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
6890
|
+
}
|
|
6891
|
+
try {
|
|
6892
|
+
await sandbox.process.createSession(sessionId);
|
|
6893
|
+
sessionCreated = true;
|
|
6894
|
+
if (aborted) return abortedExitInfo();
|
|
6895
|
+
const response = await sandbox.process.executeSessionCommand(
|
|
6896
|
+
sessionId,
|
|
6897
|
+
{
|
|
6898
|
+
command,
|
|
6899
|
+
runAsync: true,
|
|
6900
|
+
suppressInputEcho: true
|
|
6901
|
+
},
|
|
6902
|
+
commandTimeout
|
|
6903
|
+
);
|
|
6904
|
+
const commandId = response.cmdId;
|
|
6905
|
+
if (!commandId) {
|
|
6906
|
+
throw new DaytonaCommandError(
|
|
6907
|
+
"Daytona did not return a command id for the spawned session command."
|
|
6908
|
+
);
|
|
6909
|
+
}
|
|
6910
|
+
if (aborted) return abortedExitInfo();
|
|
6911
|
+
const logsTask = sandbox.process.getSessionCommandLogs(
|
|
6912
|
+
sessionId,
|
|
6913
|
+
commandId,
|
|
6914
|
+
(chunk) => stdout.enqueue(chunk),
|
|
6915
|
+
(chunk) => stderr.enqueue(chunk)
|
|
6916
|
+
);
|
|
6917
|
+
logsTask.catch(() => {
|
|
6918
|
+
});
|
|
6919
|
+
const winner = await Promise.race([
|
|
6920
|
+
logsTask.then(() => "logs"),
|
|
6921
|
+
abortPromise
|
|
6922
|
+
]);
|
|
6923
|
+
if (winner === "aborted") {
|
|
6924
|
+
await sandbox.process.deleteSession(sessionId).catch(() => {
|
|
6925
|
+
});
|
|
6926
|
+
return abortedExitInfo();
|
|
6927
|
+
}
|
|
6928
|
+
await logsTask;
|
|
6929
|
+
const code = await waitForSessionCommandExitCode({
|
|
6930
|
+
sandbox,
|
|
6931
|
+
sessionId,
|
|
6932
|
+
commandId,
|
|
6933
|
+
timeoutMs: sessionExitPollTimeoutMs(commandTimeout),
|
|
6934
|
+
isAborted: () => aborted
|
|
6935
|
+
});
|
|
6936
|
+
return { code, signal: null, success: code === 0 };
|
|
6937
|
+
} catch (error) {
|
|
6938
|
+
if (aborted) return abortedExitInfo();
|
|
6939
|
+
const err = toError(error);
|
|
6940
|
+
stdout.error(err);
|
|
6941
|
+
stderr.error(err);
|
|
6942
|
+
throw err;
|
|
6943
|
+
} finally {
|
|
6944
|
+
if (signal) {
|
|
6945
|
+
signal.removeEventListener("abort", abort);
|
|
6946
|
+
}
|
|
6947
|
+
stdout.close();
|
|
6948
|
+
stderr.close();
|
|
6949
|
+
if (sessionCreated) {
|
|
6950
|
+
await sandbox.process.deleteSession(sessionId).catch(() => {
|
|
6951
|
+
});
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
async function waitForSessionCommandExitCode(args) {
|
|
6956
|
+
const { sandbox, sessionId, commandId, timeoutMs, isAborted } = args;
|
|
6957
|
+
const deadline = Date.now() + timeoutMs;
|
|
6958
|
+
while (true) {
|
|
6959
|
+
if (isAborted()) throw new DaytonaCommandError("Daytona command aborted.");
|
|
6960
|
+
const info = await sandbox.process.getSessionCommand(sessionId, commandId);
|
|
6961
|
+
if (typeof info.exitCode === "number") {
|
|
6962
|
+
return info.exitCode;
|
|
6963
|
+
}
|
|
6964
|
+
if (Date.now() >= deadline) {
|
|
6965
|
+
throw new DaytonaCommandError(
|
|
6966
|
+
`Daytona session command "${commandId}" logs closed before an exit code became available.`
|
|
6967
|
+
);
|
|
6968
|
+
}
|
|
6969
|
+
await delay(DAYTONA_EXIT_POLL_INTERVAL_MS);
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
function sessionExitPollTimeoutMs(commandTimeout) {
|
|
6973
|
+
if (commandTimeout === void 0 || commandTimeout === 0) {
|
|
6974
|
+
return DAYTONA_EXIT_POLL_TIMEOUT_MS;
|
|
6975
|
+
}
|
|
6976
|
+
return Math.max(commandTimeout * 1e3, DAYTONA_EXIT_POLL_TIMEOUT_MS);
|
|
6977
|
+
}
|
|
6978
|
+
function delay(ms) {
|
|
6979
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
6980
|
+
}
|
|
6981
|
+
function createTextReadable() {
|
|
6982
|
+
const encoder = new TextEncoder();
|
|
6983
|
+
let controller;
|
|
6984
|
+
let closed = false;
|
|
6985
|
+
return {
|
|
6986
|
+
stream: new ReadableStream({
|
|
6987
|
+
start(streamController) {
|
|
6988
|
+
controller = streamController;
|
|
6989
|
+
}
|
|
6990
|
+
}),
|
|
6991
|
+
enqueue(chunk) {
|
|
6992
|
+
if (closed || !chunk) return;
|
|
6993
|
+
controller?.enqueue(encoder.encode(chunk));
|
|
6994
|
+
},
|
|
6995
|
+
close() {
|
|
6996
|
+
if (closed) return;
|
|
6997
|
+
closed = true;
|
|
6998
|
+
controller?.close();
|
|
6999
|
+
},
|
|
7000
|
+
error(error) {
|
|
7001
|
+
if (closed) return;
|
|
7002
|
+
closed = true;
|
|
7003
|
+
controller?.error(error);
|
|
7004
|
+
}
|
|
7005
|
+
};
|
|
7006
|
+
}
|
|
7007
|
+
function buildSessionCommand(command, options) {
|
|
7008
|
+
const cwdPrefix = options.cwd ? `cd ${shellQuote2(options.cwd)} && ` : "";
|
|
7009
|
+
const env = options.env ?? {};
|
|
7010
|
+
const entries = Object.entries(env);
|
|
7011
|
+
if (entries.length === 0) {
|
|
7012
|
+
return `sh -lc ${shellQuote2(`${cwdPrefix}${command}`)}`;
|
|
7013
|
+
}
|
|
7014
|
+
for (const [key] of entries) {
|
|
7015
|
+
validateEnvKey(key);
|
|
7016
|
+
}
|
|
7017
|
+
const exports = entries.map(([key, value]) => `export ${key}=${shellQuote2(value)}`).join("; ");
|
|
7018
|
+
return `sh -lc ${shellQuote2(`${exports}; ${cwdPrefix}${command}`)}`;
|
|
7019
|
+
}
|
|
7020
|
+
function validateEnvKey(key) {
|
|
7021
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
7022
|
+
throw new DaytonaSandboxError(
|
|
7023
|
+
`Invalid environment variable key: "${key}". Use shell-compatible environment variable names.`
|
|
7024
|
+
);
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
function createSessionId(kind) {
|
|
7028
|
+
return `deepagents-${kind}-${randomUUID()}`;
|
|
7029
|
+
}
|
|
7030
|
+
function abortedCommandResult() {
|
|
7031
|
+
return {
|
|
7032
|
+
stdout: "",
|
|
7033
|
+
stderr: "Command aborted",
|
|
7034
|
+
exitCode: 1
|
|
7035
|
+
};
|
|
7036
|
+
}
|
|
7037
|
+
function abortedExitInfo() {
|
|
7038
|
+
return {
|
|
7039
|
+
code: null,
|
|
7040
|
+
signal: "SIGKILL",
|
|
7041
|
+
success: false
|
|
7042
|
+
};
|
|
7043
|
+
}
|
|
7044
|
+
function shellQuote2(value) {
|
|
7045
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7046
|
+
}
|
|
7047
|
+
function uniqueParentDirectories(paths) {
|
|
7048
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
7049
|
+
for (const path5 of paths) {
|
|
7050
|
+
const index = path5.lastIndexOf("/");
|
|
7051
|
+
if (index > 0) {
|
|
7052
|
+
dirs.add(path5.slice(0, index));
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
return [...dirs];
|
|
7056
|
+
}
|
|
7057
|
+
function compactObject(input) {
|
|
7058
|
+
const output = {};
|
|
7059
|
+
for (const [key, value] of Object.entries(input)) {
|
|
7060
|
+
if (value !== void 0) {
|
|
7061
|
+
output[key] = value;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
return output;
|
|
7065
|
+
}
|
|
7066
|
+
function isMissingDaytonaSdk(error) {
|
|
7067
|
+
const maybeCode = error.code;
|
|
7068
|
+
return maybeCode === "ERR_MODULE_NOT_FOUND" || maybeCode === "MODULE_NOT_FOUND" || error.message.includes("@daytona/sdk");
|
|
7069
|
+
}
|
|
7070
|
+
function toError(error) {
|
|
7071
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
7072
|
+
}
|
|
7073
|
+
|
|
7074
|
+
// packages/context/src/lib/sandbox/docker-sandbox.ts
|
|
7075
|
+
import "bash-tool";
|
|
7076
|
+
import spawn3 from "nano-spawn";
|
|
7077
|
+
import { spawn as childSpawn } from "node:child_process";
|
|
7078
|
+
import { createHash } from "node:crypto";
|
|
7079
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
7080
|
+
import { Readable as Readable2 } from "node:stream";
|
|
7081
|
+
|
|
7082
|
+
// packages/context/src/lib/sandbox/docker-sandbox-errors.ts
|
|
7083
|
+
var DockerSandboxError = class extends Error {
|
|
7084
|
+
containerId;
|
|
7085
|
+
constructor(message2, containerId) {
|
|
7086
|
+
super(message2);
|
|
7087
|
+
this.name = "DockerSandboxError";
|
|
7088
|
+
this.containerId = containerId;
|
|
7089
|
+
}
|
|
7090
|
+
};
|
|
7091
|
+
var DockerNotAvailableError = class extends DockerSandboxError {
|
|
7092
|
+
constructor() {
|
|
7093
|
+
super("Docker is not available. Ensure Docker daemon is running.");
|
|
7094
|
+
this.name = "DockerNotAvailableError";
|
|
7095
|
+
}
|
|
7096
|
+
};
|
|
7097
|
+
var ContainerCreationError = class extends DockerSandboxError {
|
|
7098
|
+
image;
|
|
7099
|
+
cause;
|
|
7100
|
+
constructor(message2, image, cause) {
|
|
7101
|
+
super(`Failed to create container from image "${image}": ${message2}`);
|
|
7102
|
+
this.name = "ContainerCreationError";
|
|
7103
|
+
this.image = image;
|
|
7104
|
+
this.cause = cause;
|
|
7105
|
+
}
|
|
7106
|
+
};
|
|
7107
|
+
var PackageInstallError = class extends DockerSandboxError {
|
|
7108
|
+
packages;
|
|
7109
|
+
image;
|
|
7110
|
+
packageManager;
|
|
7111
|
+
stderr;
|
|
7112
|
+
constructor(packages, image, packageManager, stderr, containerId) {
|
|
7113
|
+
super(
|
|
7114
|
+
`Package installation failed for [${packages.join(", ")}] using ${packageManager} on ${image}: ${stderr}`,
|
|
7115
|
+
containerId
|
|
7116
|
+
);
|
|
7117
|
+
this.name = "PackageInstallError";
|
|
7118
|
+
this.packages = packages;
|
|
7119
|
+
this.image = image;
|
|
7120
|
+
this.packageManager = packageManager;
|
|
7121
|
+
this.stderr = stderr;
|
|
7122
|
+
}
|
|
7123
|
+
};
|
|
7124
|
+
var InstallError = class extends DockerSandboxError {
|
|
7125
|
+
target;
|
|
7126
|
+
source;
|
|
7127
|
+
reason;
|
|
7128
|
+
url;
|
|
7129
|
+
constructor(opts) {
|
|
7130
|
+
const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
|
|
7131
|
+
super(
|
|
7132
|
+
`Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
|
|
7133
|
+
opts.containerId
|
|
7134
|
+
);
|
|
7135
|
+
this.name = "InstallError";
|
|
7136
|
+
this.target = opts.target;
|
|
7137
|
+
this.source = opts.source;
|
|
7138
|
+
this.reason = opts.reason;
|
|
7139
|
+
this.url = opts.url;
|
|
7140
|
+
}
|
|
7141
|
+
};
|
|
7142
|
+
var MissingRuntimeError = class extends DockerSandboxError {
|
|
7143
|
+
runtime;
|
|
7144
|
+
required;
|
|
7145
|
+
constructor(runtime, required, details, containerId) {
|
|
7146
|
+
const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
|
|
7147
|
+
super(details ? `${base} ${details}` : base, containerId);
|
|
7148
|
+
this.name = "MissingRuntimeError";
|
|
7149
|
+
this.runtime = runtime;
|
|
7150
|
+
this.required = required;
|
|
7151
|
+
}
|
|
7152
|
+
};
|
|
7153
|
+
var VolumePathError = class extends DockerSandboxError {
|
|
7154
|
+
source;
|
|
7155
|
+
containerPath;
|
|
7156
|
+
reason;
|
|
7157
|
+
constructor(source, containerPath, reason) {
|
|
7158
|
+
super(
|
|
7159
|
+
`Invalid Docker volume path "${source}" -> "${containerPath}": ${reason}`
|
|
7160
|
+
);
|
|
7161
|
+
this.name = "VolumePathError";
|
|
7162
|
+
this.source = source;
|
|
7163
|
+
this.containerPath = containerPath;
|
|
7164
|
+
this.reason = reason;
|
|
7165
|
+
}
|
|
7166
|
+
};
|
|
7167
|
+
var VolumeInspectError = class extends DockerSandboxError {
|
|
7168
|
+
volume;
|
|
7169
|
+
reason;
|
|
7170
|
+
constructor(volume, reason) {
|
|
7171
|
+
super(`Failed to inspect Docker volume "${volume}": ${reason}`);
|
|
7172
|
+
this.name = "VolumeInspectError";
|
|
7173
|
+
this.volume = volume;
|
|
7174
|
+
this.reason = reason;
|
|
7175
|
+
}
|
|
7176
|
+
};
|
|
7177
|
+
var VolumeCreateError = class extends DockerSandboxError {
|
|
7178
|
+
volume;
|
|
7179
|
+
reason;
|
|
7180
|
+
constructor(volume, reason) {
|
|
7181
|
+
super(`Failed to create Docker volume "${volume}": ${reason}`);
|
|
7182
|
+
this.name = "VolumeCreateError";
|
|
7183
|
+
this.volume = volume;
|
|
7184
|
+
this.reason = reason;
|
|
7185
|
+
}
|
|
7186
|
+
};
|
|
7187
|
+
var VolumeRemoveError = class extends DockerSandboxError {
|
|
7188
|
+
volume;
|
|
7189
|
+
reason;
|
|
7190
|
+
constructor(volume, reason) {
|
|
7191
|
+
super(`Failed to remove Docker volume "${volume}": ${reason}`);
|
|
7192
|
+
this.name = "VolumeRemoveError";
|
|
7193
|
+
this.volume = volume;
|
|
7194
|
+
this.reason = reason;
|
|
7195
|
+
}
|
|
7196
|
+
};
|
|
7197
|
+
var DockerfileBuildError = class extends DockerSandboxError {
|
|
7198
|
+
stderr;
|
|
7199
|
+
constructor(stderr) {
|
|
7200
|
+
super(`Dockerfile build failed: ${stderr}`);
|
|
7201
|
+
this.name = "DockerfileBuildError";
|
|
7202
|
+
this.stderr = stderr;
|
|
7203
|
+
}
|
|
7204
|
+
};
|
|
7205
|
+
var ComposeStartError = class extends DockerSandboxError {
|
|
7206
|
+
composeFile;
|
|
7207
|
+
stderr;
|
|
7208
|
+
constructor(composeFile, stderr) {
|
|
7209
|
+
super(`Docker Compose failed to start: ${stderr}`);
|
|
7210
|
+
this.name = "ComposeStartError";
|
|
7211
|
+
this.composeFile = composeFile;
|
|
7212
|
+
this.stderr = stderr;
|
|
7213
|
+
}
|
|
7214
|
+
};
|
|
6538
7215
|
|
|
6539
7216
|
// packages/context/src/lib/sandbox/installers/installer.ts
|
|
6540
7217
|
import "bash-tool";
|
|
@@ -6547,7 +7224,7 @@ function isDebianBased(image) {
|
|
|
6547
7224
|
const debianPatterns = ["debian", "ubuntu", "node", "python"];
|
|
6548
7225
|
return debianPatterns.some((pattern) => lower.includes(pattern));
|
|
6549
7226
|
}
|
|
6550
|
-
function
|
|
7227
|
+
function shellQuote3(s) {
|
|
6551
7228
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6552
7229
|
}
|
|
6553
7230
|
function createInstallerContext(containerId, image) {
|
|
@@ -6595,7 +7272,7 @@ function createInstallerContext(containerId, image) {
|
|
|
6595
7272
|
};
|
|
6596
7273
|
const installPackages = async (packages) => {
|
|
6597
7274
|
if (packages.length === 0) return;
|
|
6598
|
-
const quoted = packages.map(
|
|
7275
|
+
const quoted = packages.map(shellQuote3).join(" ");
|
|
6599
7276
|
let cmd;
|
|
6600
7277
|
if (packageManager === "apt-get") {
|
|
6601
7278
|
cmd = aptUpdated ? `apt-get install -y ${quoted}` : `apt-get update && apt-get install -y ${quoted}`;
|
|
@@ -6617,7 +7294,7 @@ function createInstallerContext(containerId, image) {
|
|
|
6617
7294
|
const ensureTool = async (checkName, installName) => {
|
|
6618
7295
|
const cacheKey = installName ?? checkName;
|
|
6619
7296
|
if (ensuredTools.has(cacheKey)) return;
|
|
6620
|
-
const check = await exec(`which ${
|
|
7297
|
+
const check = await exec(`which ${shellQuote3(checkName)}`);
|
|
6621
7298
|
if (check.exitCode === 0) {
|
|
6622
7299
|
ensuredTools.add(cacheKey);
|
|
6623
7300
|
return;
|
|
@@ -6643,44 +7320,216 @@ function isDockerfileOptions(opts) {
|
|
|
6643
7320
|
function isComposeOptions(opts) {
|
|
6644
7321
|
return "compose" in opts;
|
|
6645
7322
|
}
|
|
7323
|
+
var CONTAINER_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
6646
7324
|
var DockerSandboxStrategy = class {
|
|
6647
7325
|
context;
|
|
6648
|
-
|
|
7326
|
+
volumes;
|
|
6649
7327
|
resources;
|
|
6650
7328
|
env;
|
|
7329
|
+
name;
|
|
7330
|
+
createdVolumes = /* @__PURE__ */ new Set();
|
|
6651
7331
|
constructor(args = {}) {
|
|
6652
|
-
const {
|
|
7332
|
+
const { volumes = [], resources = {}, env = {}, name } = args;
|
|
6653
7333
|
for (const key of Object.keys(env)) {
|
|
6654
|
-
|
|
6655
|
-
throw new DockerSandboxError(
|
|
6656
|
-
`Invalid environment variable key: "${key}"`
|
|
6657
|
-
);
|
|
6658
|
-
}
|
|
7334
|
+
validateEnvKey2(key);
|
|
6659
7335
|
}
|
|
6660
|
-
|
|
7336
|
+
if (name !== void 0 && !CONTAINER_NAME_PATTERN.test(name)) {
|
|
7337
|
+
throw new DockerSandboxError(
|
|
7338
|
+
`Invalid container name: "${name}". Use only letters, numbers, underscore, period, or hyphen. The "sandbox-" prefix is added automatically.`
|
|
7339
|
+
);
|
|
7340
|
+
}
|
|
7341
|
+
this.volumes = volumes;
|
|
6661
7342
|
this.resources = resources;
|
|
6662
7343
|
this.env = env;
|
|
7344
|
+
this.name = name;
|
|
6663
7345
|
}
|
|
6664
7346
|
async create() {
|
|
6665
|
-
this.validateMounts();
|
|
6666
7347
|
const image = await this.getImage();
|
|
6667
|
-
|
|
6668
|
-
this.context = { containerId, image };
|
|
7348
|
+
let acquired;
|
|
6669
7349
|
try {
|
|
6670
|
-
await this.
|
|
7350
|
+
acquired = await this.acquireContainer(image);
|
|
7351
|
+
this.context = { containerId: acquired.containerId, image };
|
|
7352
|
+
if (!acquired.attached) {
|
|
7353
|
+
await this.configure();
|
|
7354
|
+
}
|
|
6671
7355
|
} catch (error) {
|
|
6672
|
-
|
|
7356
|
+
if (acquired && !acquired.attached) {
|
|
7357
|
+
await this.stopContainer(acquired.containerId);
|
|
7358
|
+
}
|
|
7359
|
+
await this.cleanupCreatedVolumesAfterFailure(error);
|
|
6673
7360
|
throw error;
|
|
6674
7361
|
}
|
|
6675
7362
|
return this.createSandboxMethods();
|
|
6676
7363
|
}
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
7364
|
+
namedContainerId() {
|
|
7365
|
+
return `sandbox-${this.name}`;
|
|
7366
|
+
}
|
|
7367
|
+
defaultContainerId() {
|
|
7368
|
+
return `sandbox-${crypto.randomUUID().slice(0, 8)}`;
|
|
7369
|
+
}
|
|
7370
|
+
async acquireContainer(image) {
|
|
7371
|
+
if (!this.name) {
|
|
7372
|
+
const containerId2 = this.defaultContainerId();
|
|
7373
|
+
await this.prepareVolumes();
|
|
7374
|
+
await this.startContainer(image, containerId2);
|
|
7375
|
+
return { containerId: containerId2, attached: false };
|
|
7376
|
+
}
|
|
7377
|
+
const containerId = this.namedContainerId();
|
|
7378
|
+
const probe = await this.inspectContainer(containerId);
|
|
7379
|
+
if (probe === "running") {
|
|
7380
|
+
return { containerId, attached: true };
|
|
7381
|
+
}
|
|
7382
|
+
if (probe === "stopped") {
|
|
7383
|
+
await this.startStoppedContainer(containerId, image);
|
|
7384
|
+
return { containerId, attached: true };
|
|
7385
|
+
}
|
|
7386
|
+
await this.prepareVolumes();
|
|
7387
|
+
try {
|
|
7388
|
+
await this.startContainer(image, containerId);
|
|
7389
|
+
return { containerId, attached: false };
|
|
7390
|
+
} catch (error) {
|
|
7391
|
+
if (error instanceof ContainerCreationError && this.isNameConflictError(error.message)) {
|
|
7392
|
+
await this.cleanupCreatedVolumes();
|
|
7393
|
+
const raced = await this.inspectContainer(containerId);
|
|
7394
|
+
if (raced === "running") {
|
|
7395
|
+
return { containerId, attached: true };
|
|
7396
|
+
}
|
|
7397
|
+
if (raced === "stopped") {
|
|
7398
|
+
await this.startStoppedContainer(containerId, image);
|
|
7399
|
+
return { containerId, attached: true };
|
|
7400
|
+
}
|
|
6681
7401
|
}
|
|
7402
|
+
throw error;
|
|
6682
7403
|
}
|
|
6683
7404
|
}
|
|
7405
|
+
async startStoppedContainer(containerId, image) {
|
|
7406
|
+
try {
|
|
7407
|
+
await spawn3("docker", ["start", containerId]);
|
|
7408
|
+
} catch (error) {
|
|
7409
|
+
const message2 = this.getDockerErrorMessage(error);
|
|
7410
|
+
if (this.isDockerUnavailableError(message2)) {
|
|
7411
|
+
throw new DockerNotAvailableError();
|
|
7412
|
+
}
|
|
7413
|
+
throw new ContainerCreationError(message2, image, error);
|
|
7414
|
+
}
|
|
7415
|
+
}
|
|
7416
|
+
async inspectContainer(containerId) {
|
|
7417
|
+
try {
|
|
7418
|
+
const result = await spawn3("docker", [
|
|
7419
|
+
"container",
|
|
7420
|
+
"inspect",
|
|
7421
|
+
"--format",
|
|
7422
|
+
"{{.State.Status}}",
|
|
7423
|
+
containerId
|
|
7424
|
+
]);
|
|
7425
|
+
const status = result.stdout.trim();
|
|
7426
|
+
return status === "running" ? "running" : "stopped";
|
|
7427
|
+
} catch (error) {
|
|
7428
|
+
const message2 = this.getDockerErrorMessage(error);
|
|
7429
|
+
if (this.isDockerUnavailableError(message2)) {
|
|
7430
|
+
throw new DockerNotAvailableError();
|
|
7431
|
+
}
|
|
7432
|
+
if (this.isMissingContainerError(message2)) {
|
|
7433
|
+
return "absent";
|
|
7434
|
+
}
|
|
7435
|
+
throw new DockerSandboxError(
|
|
7436
|
+
`Failed to inspect container "${containerId}": ${message2}`
|
|
7437
|
+
);
|
|
7438
|
+
}
|
|
7439
|
+
}
|
|
7440
|
+
isMissingContainerError(message2) {
|
|
7441
|
+
return message2.toLowerCase().includes("no such container");
|
|
7442
|
+
}
|
|
7443
|
+
isNameConflictError(message2) {
|
|
7444
|
+
return message2.toLowerCase().includes("is already in use by container");
|
|
7445
|
+
}
|
|
7446
|
+
async prepareVolumes() {
|
|
7447
|
+
this.validateVolumes();
|
|
7448
|
+
for (const volume of this.volumes) {
|
|
7449
|
+
if (volume.type !== "volume") {
|
|
7450
|
+
continue;
|
|
7451
|
+
}
|
|
7452
|
+
const lifecycle = volume.lifecycle ?? "external";
|
|
7453
|
+
if (lifecycle === "external") {
|
|
7454
|
+
await this.inspectVolume(volume.name);
|
|
7455
|
+
continue;
|
|
7456
|
+
}
|
|
7457
|
+
const exists = await this.volumeExists(volume.name);
|
|
7458
|
+
if (exists) {
|
|
7459
|
+
throw new VolumeCreateError(
|
|
7460
|
+
volume.name,
|
|
7461
|
+
"managed volume already exists"
|
|
7462
|
+
);
|
|
7463
|
+
}
|
|
7464
|
+
await this.createVolume(volume);
|
|
7465
|
+
if (volume.removeOnDispose !== false) {
|
|
7466
|
+
this.createdVolumes.add(volume.name);
|
|
7467
|
+
}
|
|
7468
|
+
}
|
|
7469
|
+
}
|
|
7470
|
+
validateVolumes() {
|
|
7471
|
+
const containerPaths = /* @__PURE__ */ new Set();
|
|
7472
|
+
for (const volume of this.volumes) {
|
|
7473
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
7474
|
+
if (!volume.containerPath.startsWith("/")) {
|
|
7475
|
+
throw new VolumePathError(
|
|
7476
|
+
source,
|
|
7477
|
+
volume.containerPath,
|
|
7478
|
+
"containerPath must be absolute"
|
|
7479
|
+
);
|
|
7480
|
+
}
|
|
7481
|
+
this.validateMountValue("containerPath", volume.containerPath, volume);
|
|
7482
|
+
if (containerPaths.has(volume.containerPath)) {
|
|
7483
|
+
throw new VolumePathError(
|
|
7484
|
+
source,
|
|
7485
|
+
volume.containerPath,
|
|
7486
|
+
"containerPath must be unique"
|
|
7487
|
+
);
|
|
7488
|
+
}
|
|
7489
|
+
containerPaths.add(volume.containerPath);
|
|
7490
|
+
if (volume.type === "bind") {
|
|
7491
|
+
this.validateMountValue("hostPath", volume.hostPath, volume);
|
|
7492
|
+
if (!existsSync3(volume.hostPath)) {
|
|
7493
|
+
throw new VolumePathError(
|
|
7494
|
+
volume.hostPath,
|
|
7495
|
+
volume.containerPath,
|
|
7496
|
+
"hostPath does not exist on host"
|
|
7497
|
+
);
|
|
7498
|
+
}
|
|
7499
|
+
continue;
|
|
7500
|
+
}
|
|
7501
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(volume.name)) {
|
|
7502
|
+
throw new VolumePathError(
|
|
7503
|
+
volume.name,
|
|
7504
|
+
volume.containerPath,
|
|
7505
|
+
"volume name must start with an alphanumeric character and contain only letters, numbers, underscore, period, or hyphen"
|
|
7506
|
+
);
|
|
7507
|
+
}
|
|
7508
|
+
if (volume.subPath) {
|
|
7509
|
+
this.validateMountValue("subPath", volume.subPath, volume);
|
|
7510
|
+
}
|
|
7511
|
+
if ((volume.lifecycle ?? "external") === "external") {
|
|
7512
|
+
if (volume.driver || volume.driverOptions) {
|
|
7513
|
+
throw new VolumePathError(
|
|
7514
|
+
volume.name,
|
|
7515
|
+
volume.containerPath,
|
|
7516
|
+
'driver and driverOptions require lifecycle "managed"'
|
|
7517
|
+
);
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
validateMountValue(field, value, volume) {
|
|
7523
|
+
if (!value.includes(",")) {
|
|
7524
|
+
return;
|
|
7525
|
+
}
|
|
7526
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
7527
|
+
throw new VolumePathError(
|
|
7528
|
+
source,
|
|
7529
|
+
volume.containerPath,
|
|
7530
|
+
`${field} must not contain commas`
|
|
7531
|
+
);
|
|
7532
|
+
}
|
|
6684
7533
|
buildDockerArgs(image, containerId) {
|
|
6685
7534
|
const { memory = "1g", cpus = 2 } = this.resources;
|
|
6686
7535
|
const args = [
|
|
@@ -6697,15 +7546,106 @@ var DockerSandboxStrategy = class {
|
|
|
6697
7546
|
for (const [key, value] of Object.entries(this.env)) {
|
|
6698
7547
|
args.push("-e", `${key}=${value}`);
|
|
6699
7548
|
}
|
|
6700
|
-
for (const
|
|
6701
|
-
|
|
6702
|
-
args.push("-v", `${mount.hostPath}:${mount.containerPath}:${mode}`);
|
|
7549
|
+
for (const volume of this.volumes) {
|
|
7550
|
+
args.push("--mount", this.buildVolumeMountArg(volume));
|
|
6703
7551
|
}
|
|
6704
7552
|
args.push(image, "tail", "-f", "/dev/null");
|
|
6705
7553
|
return args;
|
|
6706
7554
|
}
|
|
6707
|
-
|
|
6708
|
-
const
|
|
7555
|
+
buildVolumeMountArg(volume) {
|
|
7556
|
+
const readOnly = volume.readOnly !== false;
|
|
7557
|
+
const parts = volume.type === "bind" ? ["type=bind", `src=${volume.hostPath}`, `dst=${volume.containerPath}`] : [
|
|
7558
|
+
"type=volume",
|
|
7559
|
+
`src=${volume.name}`,
|
|
7560
|
+
`dst=${volume.containerPath}`,
|
|
7561
|
+
...volume.subPath ? [`volume-subpath=${volume.subPath}`] : [],
|
|
7562
|
+
...volume.noCopy ? ["volume-nocopy"] : []
|
|
7563
|
+
];
|
|
7564
|
+
if (readOnly) {
|
|
7565
|
+
parts.push("readonly");
|
|
7566
|
+
}
|
|
7567
|
+
return parts.join(",");
|
|
7568
|
+
}
|
|
7569
|
+
async inspectVolume(name) {
|
|
7570
|
+
try {
|
|
7571
|
+
await spawn3("docker", ["volume", "inspect", name]);
|
|
7572
|
+
} catch (error) {
|
|
7573
|
+
const reason = this.getDockerErrorMessage(error);
|
|
7574
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
7575
|
+
throw new DockerNotAvailableError();
|
|
7576
|
+
}
|
|
7577
|
+
throw new VolumeInspectError(name, reason);
|
|
7578
|
+
}
|
|
7579
|
+
}
|
|
7580
|
+
async volumeExists(name) {
|
|
7581
|
+
try {
|
|
7582
|
+
await this.inspectVolume(name);
|
|
7583
|
+
return true;
|
|
7584
|
+
} catch (error) {
|
|
7585
|
+
if (error instanceof VolumeInspectError) {
|
|
7586
|
+
if (this.isMissingVolumeInspectError(error.reason)) {
|
|
7587
|
+
return false;
|
|
7588
|
+
}
|
|
7589
|
+
throw error;
|
|
7590
|
+
}
|
|
7591
|
+
throw error;
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7594
|
+
async createVolume(volume) {
|
|
7595
|
+
const args = ["volume", "create"];
|
|
7596
|
+
if (volume.driver) {
|
|
7597
|
+
args.push("--driver", volume.driver);
|
|
7598
|
+
}
|
|
7599
|
+
for (const [key, value] of Object.entries(volume.driverOptions ?? {})) {
|
|
7600
|
+
args.push("--opt", `${key}=${value}`);
|
|
7601
|
+
}
|
|
7602
|
+
args.push(volume.name);
|
|
7603
|
+
try {
|
|
7604
|
+
await spawn3("docker", args);
|
|
7605
|
+
} catch (error) {
|
|
7606
|
+
const reason = this.getDockerErrorMessage(error);
|
|
7607
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
7608
|
+
throw new DockerNotAvailableError();
|
|
7609
|
+
}
|
|
7610
|
+
throw new VolumeCreateError(volume.name, reason);
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
async cleanupCreatedVolumes() {
|
|
7614
|
+
const volumes = [...this.createdVolumes].reverse();
|
|
7615
|
+
for (const volume of volumes) {
|
|
7616
|
+
try {
|
|
7617
|
+
await spawn3("docker", ["volume", "rm", volume]);
|
|
7618
|
+
this.createdVolumes.delete(volume);
|
|
7619
|
+
} catch (error) {
|
|
7620
|
+
const reason = this.getDockerErrorMessage(error);
|
|
7621
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
7622
|
+
throw new DockerNotAvailableError();
|
|
7623
|
+
}
|
|
7624
|
+
throw new VolumeRemoveError(volume, reason);
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7627
|
+
}
|
|
7628
|
+
async cleanupCreatedVolumesAfterFailure(originalError) {
|
|
7629
|
+
try {
|
|
7630
|
+
await this.cleanupCreatedVolumes();
|
|
7631
|
+
} catch (cleanupError) {
|
|
7632
|
+
if (originalError instanceof Error) {
|
|
7633
|
+
const original = originalError;
|
|
7634
|
+
original.suppressed = [...original.suppressed ?? [], cleanupError];
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
getDockerErrorMessage(error) {
|
|
7639
|
+
const err = error;
|
|
7640
|
+
return err.stderr || err.stdout || err.message || String(error);
|
|
7641
|
+
}
|
|
7642
|
+
isDockerUnavailableError(message2) {
|
|
7643
|
+
return message2.includes("Cannot connect") || message2.includes("docker daemon");
|
|
7644
|
+
}
|
|
7645
|
+
isMissingVolumeInspectError(message2) {
|
|
7646
|
+
return message2.toLowerCase().includes("no such volume");
|
|
7647
|
+
}
|
|
7648
|
+
async startContainer(image, containerId) {
|
|
6709
7649
|
const args = this.buildDockerArgs(image, containerId);
|
|
6710
7650
|
try {
|
|
6711
7651
|
await spawn3("docker", args);
|
|
@@ -6714,9 +7654,12 @@ var DockerSandboxStrategy = class {
|
|
|
6714
7654
|
if (err.message?.includes("Cannot connect") || err.message?.includes("docker daemon") || err.stderr?.includes("Cannot connect")) {
|
|
6715
7655
|
throw new DockerNotAvailableError();
|
|
6716
7656
|
}
|
|
6717
|
-
throw new ContainerCreationError(
|
|
7657
|
+
throw new ContainerCreationError(
|
|
7658
|
+
this.getDockerErrorMessage(err),
|
|
7659
|
+
image,
|
|
7660
|
+
err
|
|
7661
|
+
);
|
|
6718
7662
|
}
|
|
6719
|
-
return containerId;
|
|
6720
7663
|
}
|
|
6721
7664
|
async stopContainer(containerId) {
|
|
6722
7665
|
try {
|
|
@@ -6724,15 +7667,13 @@ var DockerSandboxStrategy = class {
|
|
|
6724
7667
|
} catch {
|
|
6725
7668
|
}
|
|
6726
7669
|
}
|
|
6727
|
-
async exec(command) {
|
|
7670
|
+
async exec(command, options) {
|
|
6728
7671
|
try {
|
|
6729
|
-
const result = await spawn3(
|
|
6730
|
-
"
|
|
6731
|
-
this.context.containerId,
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
command
|
|
6735
|
-
]);
|
|
7672
|
+
const result = await spawn3(
|
|
7673
|
+
"docker",
|
|
7674
|
+
["exec", this.context.containerId, "sh", "-c", command],
|
|
7675
|
+
{ signal: options?.signal }
|
|
7676
|
+
);
|
|
6736
7677
|
return {
|
|
6737
7678
|
stdout: result.stdout,
|
|
6738
7679
|
stderr: result.stderr,
|
|
@@ -6747,12 +7688,22 @@ var DockerSandboxStrategy = class {
|
|
|
6747
7688
|
};
|
|
6748
7689
|
}
|
|
6749
7690
|
}
|
|
7691
|
+
spawnProcess(command, options) {
|
|
7692
|
+
const child = childSpawn("docker", [
|
|
7693
|
+
"exec",
|
|
7694
|
+
...buildDockerExecFlags(options),
|
|
7695
|
+
this.context.containerId,
|
|
7696
|
+
"sh",
|
|
7697
|
+
"-c",
|
|
7698
|
+
command
|
|
7699
|
+
]);
|
|
7700
|
+
return toSandboxProcess(child, options?.signal);
|
|
7701
|
+
}
|
|
6750
7702
|
createSandboxMethods() {
|
|
6751
7703
|
const { containerId } = this.context;
|
|
6752
7704
|
const sandbox = {
|
|
6753
|
-
executeCommand: async (command) =>
|
|
6754
|
-
|
|
6755
|
-
},
|
|
7705
|
+
executeCommand: async (command, options) => this.exec(command, options),
|
|
7706
|
+
spawn: (command, options) => this.spawnProcess(command, options),
|
|
6756
7707
|
readFile: async (path5) => {
|
|
6757
7708
|
const result = await sandbox.executeCommand(`base64 "${path5}"`);
|
|
6758
7709
|
if (result.exitCode !== 0) {
|
|
@@ -6779,16 +7730,74 @@ var DockerSandboxStrategy = class {
|
|
|
6779
7730
|
},
|
|
6780
7731
|
dispose: async () => {
|
|
6781
7732
|
await this.stopContainer(containerId);
|
|
7733
|
+
await this.cleanupCreatedVolumes();
|
|
6782
7734
|
}
|
|
6783
7735
|
};
|
|
6784
7736
|
return sandbox;
|
|
6785
7737
|
}
|
|
6786
7738
|
};
|
|
7739
|
+
function validateEnvKey2(key) {
|
|
7740
|
+
if (key.length === 0 || key.includes("=")) {
|
|
7741
|
+
throw new DockerSandboxError(`Invalid environment variable key: "${key}"`);
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
function buildDockerExecFlags(options) {
|
|
7745
|
+
const flags = [];
|
|
7746
|
+
if (options?.cwd) {
|
|
7747
|
+
flags.push("-w", options.cwd);
|
|
7748
|
+
}
|
|
7749
|
+
if (options?.env) {
|
|
7750
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
7751
|
+
validateEnvKey2(key);
|
|
7752
|
+
flags.push("-e", `${key}=${value}`);
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
return flags;
|
|
7756
|
+
}
|
|
7757
|
+
function toSandboxProcess(child, abortSignal) {
|
|
7758
|
+
if (!child.stdout || !child.stderr) {
|
|
7759
|
+
child.kill("SIGKILL");
|
|
7760
|
+
throw new DockerSandboxError("docker exec child missing stdio streams");
|
|
7761
|
+
}
|
|
7762
|
+
const onAbort = abortSignal ? () => child.kill("SIGKILL") : void 0;
|
|
7763
|
+
if (abortSignal && onAbort) {
|
|
7764
|
+
if (abortSignal.aborted) onAbort();
|
|
7765
|
+
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
7766
|
+
}
|
|
7767
|
+
return {
|
|
7768
|
+
stdout: Readable2.toWeb(child.stdout),
|
|
7769
|
+
stderr: Readable2.toWeb(child.stderr),
|
|
7770
|
+
exit: new Promise((resolve4, reject) => {
|
|
7771
|
+
const settle = () => {
|
|
7772
|
+
child.removeListener("exit", onExitEvent);
|
|
7773
|
+
child.removeListener("error", onError);
|
|
7774
|
+
if (abortSignal && onAbort) {
|
|
7775
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
7776
|
+
}
|
|
7777
|
+
};
|
|
7778
|
+
const onError = (err) => {
|
|
7779
|
+
settle();
|
|
7780
|
+
reject(err);
|
|
7781
|
+
};
|
|
7782
|
+
const onExitEvent = (code, exitSignal) => {
|
|
7783
|
+
settle();
|
|
7784
|
+
resolve4({ code, signal: exitSignal, success: code === 0 });
|
|
7785
|
+
};
|
|
7786
|
+
child.on("exit", onExitEvent);
|
|
7787
|
+
child.on("error", onError);
|
|
7788
|
+
})
|
|
7789
|
+
};
|
|
7790
|
+
}
|
|
6787
7791
|
var RuntimeStrategy = class extends DockerSandboxStrategy {
|
|
6788
7792
|
image;
|
|
6789
7793
|
installers;
|
|
6790
7794
|
constructor(args = {}) {
|
|
6791
|
-
super({
|
|
7795
|
+
super({
|
|
7796
|
+
volumes: args.volumes,
|
|
7797
|
+
resources: args.resources,
|
|
7798
|
+
env: args.env,
|
|
7799
|
+
name: args.name
|
|
7800
|
+
});
|
|
6792
7801
|
this.image = args.image ?? "alpine:latest";
|
|
6793
7802
|
this.installers = args.installers ?? [];
|
|
6794
7803
|
}
|
|
@@ -6807,7 +7816,12 @@ var DockerfileStrategy = class extends DockerSandboxStrategy {
|
|
|
6807
7816
|
dockerfile;
|
|
6808
7817
|
dockerContext;
|
|
6809
7818
|
constructor(args) {
|
|
6810
|
-
super({
|
|
7819
|
+
super({
|
|
7820
|
+
volumes: args.volumes,
|
|
7821
|
+
resources: args.resources,
|
|
7822
|
+
env: args.env,
|
|
7823
|
+
name: args.name
|
|
7824
|
+
});
|
|
6811
7825
|
this.dockerfile = args.dockerfile;
|
|
6812
7826
|
this.dockerContext = args.context ?? ".";
|
|
6813
7827
|
this.imageTag = this.computeImageTag();
|
|
@@ -6876,7 +7890,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6876
7890
|
async getImage() {
|
|
6877
7891
|
return "";
|
|
6878
7892
|
}
|
|
6879
|
-
async startContainer(_image) {
|
|
7893
|
+
async startContainer(_image, _containerId) {
|
|
6880
7894
|
try {
|
|
6881
7895
|
await spawn3("docker", [
|
|
6882
7896
|
"compose",
|
|
@@ -6894,25 +7908,31 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6894
7908
|
}
|
|
6895
7909
|
throw new ComposeStartError(this.composeFile, err.stderr || err.message);
|
|
6896
7910
|
}
|
|
7911
|
+
}
|
|
7912
|
+
defaultContainerId() {
|
|
6897
7913
|
return this.projectName;
|
|
6898
7914
|
}
|
|
6899
7915
|
async configure() {
|
|
6900
7916
|
}
|
|
6901
|
-
async exec(command) {
|
|
7917
|
+
async exec(command, options) {
|
|
6902
7918
|
try {
|
|
6903
|
-
const result = await spawn3(
|
|
6904
|
-
"
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
7919
|
+
const result = await spawn3(
|
|
7920
|
+
"docker",
|
|
7921
|
+
[
|
|
7922
|
+
"compose",
|
|
7923
|
+
"-f",
|
|
7924
|
+
this.composeFile,
|
|
7925
|
+
"-p",
|
|
7926
|
+
this.projectName,
|
|
7927
|
+
"exec",
|
|
7928
|
+
"-T",
|
|
7929
|
+
this.service,
|
|
7930
|
+
"sh",
|
|
7931
|
+
"-c",
|
|
7932
|
+
command
|
|
7933
|
+
],
|
|
7934
|
+
{ signal: options?.signal }
|
|
7935
|
+
);
|
|
6916
7936
|
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
6917
7937
|
} catch (error) {
|
|
6918
7938
|
const err = error;
|
|
@@ -6923,6 +7943,23 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6923
7943
|
};
|
|
6924
7944
|
}
|
|
6925
7945
|
}
|
|
7946
|
+
spawnProcess(command, options) {
|
|
7947
|
+
const child = childSpawn("docker", [
|
|
7948
|
+
"compose",
|
|
7949
|
+
"-f",
|
|
7950
|
+
this.composeFile,
|
|
7951
|
+
"-p",
|
|
7952
|
+
this.projectName,
|
|
7953
|
+
"exec",
|
|
7954
|
+
"-T",
|
|
7955
|
+
...buildDockerExecFlags(options),
|
|
7956
|
+
this.service,
|
|
7957
|
+
"sh",
|
|
7958
|
+
"-c",
|
|
7959
|
+
command
|
|
7960
|
+
]);
|
|
7961
|
+
return toSandboxProcess(child, options?.signal);
|
|
7962
|
+
}
|
|
6926
7963
|
async stopContainer(_containerId) {
|
|
6927
7964
|
try {
|
|
6928
7965
|
await spawn3("docker", [
|
|
@@ -6949,17 +7986,19 @@ async function createDockerSandbox(options = {}) {
|
|
|
6949
7986
|
strategy = new DockerfileStrategy({
|
|
6950
7987
|
dockerfile: options.dockerfile,
|
|
6951
7988
|
context: options.context,
|
|
6952
|
-
|
|
7989
|
+
volumes: options.volumes,
|
|
6953
7990
|
resources: options.resources,
|
|
6954
|
-
env: options.env
|
|
7991
|
+
env: options.env,
|
|
7992
|
+
name: options.name
|
|
6955
7993
|
});
|
|
6956
7994
|
} else {
|
|
6957
7995
|
strategy = new RuntimeStrategy({
|
|
6958
7996
|
image: options.image,
|
|
6959
7997
|
installers: options.installers,
|
|
6960
|
-
|
|
7998
|
+
volumes: options.volumes,
|
|
6961
7999
|
resources: options.resources,
|
|
6962
|
-
env: options.env
|
|
8000
|
+
env: options.env,
|
|
8001
|
+
name: options.name
|
|
6963
8002
|
});
|
|
6964
8003
|
}
|
|
6965
8004
|
return strategy.create();
|
|
@@ -6973,57 +8012,6 @@ async function useSandbox(options, fn) {
|
|
|
6973
8012
|
}
|
|
6974
8013
|
}
|
|
6975
8014
|
|
|
6976
|
-
// packages/context/src/lib/sandbox/container-tool.ts
|
|
6977
|
-
async function createContainerTool(options = {}) {
|
|
6978
|
-
let sandboxOptions;
|
|
6979
|
-
let bashOptions;
|
|
6980
|
-
let skillInputs = [];
|
|
6981
|
-
if (isComposeOptions(options)) {
|
|
6982
|
-
const { compose, service, resources, skills: skills2 = [], ...rest } = options;
|
|
6983
|
-
sandboxOptions = { compose, service, resources };
|
|
6984
|
-
bashOptions = rest;
|
|
6985
|
-
skillInputs = skills2;
|
|
6986
|
-
} else if (isDockerfileOptions(options)) {
|
|
6987
|
-
const {
|
|
6988
|
-
dockerfile,
|
|
6989
|
-
context,
|
|
6990
|
-
mounts,
|
|
6991
|
-
resources,
|
|
6992
|
-
env,
|
|
6993
|
-
skills: skills2 = [],
|
|
6994
|
-
...rest
|
|
6995
|
-
} = options;
|
|
6996
|
-
sandboxOptions = { dockerfile, context, mounts, resources, env };
|
|
6997
|
-
bashOptions = rest;
|
|
6998
|
-
skillInputs = skills2;
|
|
6999
|
-
} else {
|
|
7000
|
-
const {
|
|
7001
|
-
image,
|
|
7002
|
-
installers,
|
|
7003
|
-
mounts,
|
|
7004
|
-
resources,
|
|
7005
|
-
env,
|
|
7006
|
-
skills: skills2 = [],
|
|
7007
|
-
...rest
|
|
7008
|
-
} = options;
|
|
7009
|
-
sandboxOptions = { image, installers, mounts, resources, env };
|
|
7010
|
-
bashOptions = rest;
|
|
7011
|
-
skillInputs = skills2;
|
|
7012
|
-
}
|
|
7013
|
-
const sandbox = await createDockerSandbox(sandboxOptions);
|
|
7014
|
-
const toolkit = await createBashTool({
|
|
7015
|
-
...bashOptions,
|
|
7016
|
-
sandbox,
|
|
7017
|
-
skills: skillInputs
|
|
7018
|
-
});
|
|
7019
|
-
return {
|
|
7020
|
-
bash: toolkit.bash,
|
|
7021
|
-
tools: toolkit.tools,
|
|
7022
|
-
sandbox,
|
|
7023
|
-
skills: toolkit.skills
|
|
7024
|
-
};
|
|
7025
|
-
}
|
|
7026
|
-
|
|
7027
8015
|
// packages/context/src/lib/sandbox/installers/package-manager.ts
|
|
7028
8016
|
var PackageInstaller = class extends Installer {
|
|
7029
8017
|
kind;
|
|
@@ -7096,9 +8084,9 @@ async function downloadAndInstall(ctx, name, url, binaryPath, source = "url") {
|
|
|
7096
8084
|
function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
|
|
7097
8085
|
return `
|
|
7098
8086
|
set -e
|
|
7099
|
-
NAME=${
|
|
7100
|
-
URL=${
|
|
7101
|
-
BIN_IN_ARCHIVE=${
|
|
8087
|
+
NAME=${shellQuote3(name)}
|
|
8088
|
+
URL=${shellQuote3(url)}
|
|
8089
|
+
BIN_IN_ARCHIVE=${shellQuote3(binaryPathInArchive)}
|
|
7102
8090
|
TMPDIR=$(mktemp -d)
|
|
7103
8091
|
cd "$TMPDIR"
|
|
7104
8092
|
curl -fsSL "$URL" -o archive.tar.gz
|
|
@@ -7117,8 +8105,8 @@ function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
|
|
|
7117
8105
|
}
|
|
7118
8106
|
function buildRawInstallCmd(name, url) {
|
|
7119
8107
|
return `
|
|
7120
|
-
NAME=${
|
|
7121
|
-
URL=${
|
|
8108
|
+
NAME=${shellQuote3(name)}
|
|
8109
|
+
URL=${shellQuote3(url)}
|
|
7122
8110
|
curl -fsSL "$URL" -o "/usr/local/bin/$NAME"
|
|
7123
8111
|
chmod +x "/usr/local/bin/$NAME"
|
|
7124
8112
|
`;
|
|
@@ -7171,6 +8159,59 @@ async function ensureNodeRuntime(ctx, ensure) {
|
|
|
7171
8159
|
}
|
|
7172
8160
|
}
|
|
7173
8161
|
|
|
8162
|
+
// packages/context/src/lib/sandbox/installers/bin.ts
|
|
8163
|
+
import { basename, posix } from "node:path";
|
|
8164
|
+
var NOT_FOUND_EXIT = 11;
|
|
8165
|
+
var CHMOD_FAILED_EXIT = 12;
|
|
8166
|
+
var BinInstaller = class extends Installer {
|
|
8167
|
+
kind;
|
|
8168
|
+
binary;
|
|
8169
|
+
options;
|
|
8170
|
+
#name;
|
|
8171
|
+
#target;
|
|
8172
|
+
constructor(binary, options = {}) {
|
|
8173
|
+
super();
|
|
8174
|
+
this.binary = binary;
|
|
8175
|
+
this.options = options;
|
|
8176
|
+
this.#name = resolveName(binary, options);
|
|
8177
|
+
this.#target = options.target ?? posix.join("/usr/local/bin", this.#name);
|
|
8178
|
+
this.kind = `bin:${this.#name}`;
|
|
8179
|
+
}
|
|
8180
|
+
async install(ctx) {
|
|
8181
|
+
const b = shellQuote3(this.binary);
|
|
8182
|
+
const t = shellQuote3(this.#target);
|
|
8183
|
+
const dir = shellQuote3(posix.dirname(this.#target));
|
|
8184
|
+
const result = await ctx.exec(
|
|
8185
|
+
`test -f ${b} || { echo "binary not found at ${this.binary}" >&2; exit ${NOT_FOUND_EXIT}; }; if ! test -x ${b}; then chmod +x ${b} || exit ${CHMOD_FAILED_EXIT}; fi; mkdir -p ${dir} && ln -sf ${b} ${t}`
|
|
8186
|
+
);
|
|
8187
|
+
if (result.exitCode === 0) return;
|
|
8188
|
+
throw new InstallError({
|
|
8189
|
+
target: this.#name,
|
|
8190
|
+
source: "bin",
|
|
8191
|
+
reason: explainFailure(result.exitCode, result.stderr, this.binary),
|
|
8192
|
+
containerId: ctx.containerId
|
|
8193
|
+
});
|
|
8194
|
+
}
|
|
8195
|
+
};
|
|
8196
|
+
function bin(binary, options) {
|
|
8197
|
+
return new BinInstaller(binary, options);
|
|
8198
|
+
}
|
|
8199
|
+
function explainFailure(exitCode, stderr, binary) {
|
|
8200
|
+
if (exitCode === NOT_FOUND_EXIT) {
|
|
8201
|
+
return `binary not found at ${binary}`;
|
|
8202
|
+
}
|
|
8203
|
+
if (exitCode === CHMOD_FAILED_EXIT && /read-only file system/i.test(stderr)) {
|
|
8204
|
+
return `${binary} is not executable and the bind mount is read-only \u2014 run \`chmod +x\` on the host, or mount with \`readOnly: false\`. (${stderr.trim()})`;
|
|
8205
|
+
}
|
|
8206
|
+
return stderr || `bin installer failed with exit code ${exitCode}`;
|
|
8207
|
+
}
|
|
8208
|
+
function resolveName(binary, options) {
|
|
8209
|
+
if (options.name) return options.name;
|
|
8210
|
+
const base = basename(binary);
|
|
8211
|
+
const dot = base.lastIndexOf(".");
|
|
8212
|
+
return dot > 0 ? base.slice(0, dot) : base;
|
|
8213
|
+
}
|
|
8214
|
+
|
|
7174
8215
|
// packages/context/src/lib/sandbox/installers/pip.ts
|
|
7175
8216
|
var PYTHON_BINARIES = ["python3", "pip3"];
|
|
7176
8217
|
var PipInstaller = class extends Installer {
|
|
@@ -7330,8 +8371,11 @@ async function createVirtualSandbox(options) {
|
|
|
7330
8371
|
customCommands: options.customCommands
|
|
7331
8372
|
});
|
|
7332
8373
|
return {
|
|
7333
|
-
async executeCommand(command) {
|
|
7334
|
-
const result = await bash.exec(
|
|
8374
|
+
async executeCommand(command, options2) {
|
|
8375
|
+
const result = await bash.exec(
|
|
8376
|
+
command,
|
|
8377
|
+
options2?.signal ? { signal: options2.signal } : void 0
|
|
8378
|
+
);
|
|
7335
8379
|
return {
|
|
7336
8380
|
stdout: result.stdout,
|
|
7337
8381
|
stderr: result.stderr,
|
|
@@ -7348,6 +8392,8 @@ async function createVirtualSandbox(options) {
|
|
|
7348
8392
|
typeof file.content === "string" ? file.content : Buffer.from(file.content).toString("utf-8")
|
|
7349
8393
|
);
|
|
7350
8394
|
}
|
|
8395
|
+
},
|
|
8396
|
+
async dispose() {
|
|
7351
8397
|
}
|
|
7352
8398
|
};
|
|
7353
8399
|
}
|
|
@@ -7429,7 +8475,7 @@ function soul() {
|
|
|
7429
8475
|
}
|
|
7430
8476
|
|
|
7431
8477
|
// packages/context/src/lib/store/postgres.store.ts
|
|
7432
|
-
import { createRequire } from "node:module";
|
|
8478
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
7433
8479
|
|
|
7434
8480
|
// packages/context/src/lib/store/ddl.postgres.ts
|
|
7435
8481
|
function storeDDL(schema) {
|
|
@@ -7541,7 +8587,7 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
|
|
|
7541
8587
|
}
|
|
7542
8588
|
static #requirePg() {
|
|
7543
8589
|
try {
|
|
7544
|
-
const require2 =
|
|
8590
|
+
const require2 = createRequire2(import.meta.url);
|
|
7545
8591
|
return require2("pg");
|
|
7546
8592
|
} catch {
|
|
7547
8593
|
throw new Error(
|
|
@@ -8137,7 +9183,7 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
|
|
|
8137
9183
|
};
|
|
8138
9184
|
|
|
8139
9185
|
// packages/context/src/lib/store/sqlserver.store.ts
|
|
8140
|
-
import { createRequire as
|
|
9186
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
8141
9187
|
|
|
8142
9188
|
// packages/context/src/lib/store/ddl.sqlserver.ts
|
|
8143
9189
|
function storeDDL2(schema) {
|
|
@@ -8279,7 +9325,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
8279
9325
|
}
|
|
8280
9326
|
static #requireMssql() {
|
|
8281
9327
|
try {
|
|
8282
|
-
const require2 =
|
|
9328
|
+
const require2 = createRequire3(import.meta.url);
|
|
8283
9329
|
return require2("mssql");
|
|
8284
9330
|
} catch {
|
|
8285
9331
|
throw new Error(
|
|
@@ -8989,11 +10035,12 @@ async function persistedWriter(options) {
|
|
|
8989
10035
|
} = options;
|
|
8990
10036
|
let seq = 0;
|
|
8991
10037
|
let buffer = [];
|
|
10038
|
+
let failedByErrorChunk = false;
|
|
8992
10039
|
async function flush() {
|
|
8993
10040
|
if (buffer.length === 0) return;
|
|
8994
10041
|
const batch = buffer;
|
|
8995
10042
|
buffer = [];
|
|
8996
|
-
await
|
|
10043
|
+
await appendBatch(batch);
|
|
8997
10044
|
}
|
|
8998
10045
|
function makeChunk(part) {
|
|
8999
10046
|
return {
|
|
@@ -9003,9 +10050,19 @@ async function persistedWriter(options) {
|
|
|
9003
10050
|
createdAt: Date.now()
|
|
9004
10051
|
};
|
|
9005
10052
|
}
|
|
10053
|
+
function isStreamErrorPart(part) {
|
|
10054
|
+
return part.type === "error";
|
|
10055
|
+
}
|
|
10056
|
+
async function appendBatch(batch) {
|
|
10057
|
+
const hasErrorPart = !failedByErrorChunk && batch.map((chunk) => chunk.data).some(isStreamErrorPart);
|
|
10058
|
+
await store2.appendChunks(batch);
|
|
10059
|
+
if (hasErrorPart) {
|
|
10060
|
+
failedByErrorChunk = true;
|
|
10061
|
+
}
|
|
10062
|
+
}
|
|
9006
10063
|
async function persistChunk(chunk) {
|
|
9007
10064
|
if (strategy === "immediate") {
|
|
9008
|
-
await
|
|
10065
|
+
await appendBatch([chunk]);
|
|
9009
10066
|
} else {
|
|
9010
10067
|
buffer.push(chunk);
|
|
9011
10068
|
if (buffer.length >= flushSize) {
|
|
@@ -9035,10 +10092,12 @@ async function persistedWriter(options) {
|
|
|
9035
10092
|
flush,
|
|
9036
10093
|
async complete() {
|
|
9037
10094
|
await flush();
|
|
10095
|
+
if (failedByErrorChunk) return;
|
|
9038
10096
|
await store2.updateStreamStatus(streamId, "completed");
|
|
9039
10097
|
},
|
|
9040
10098
|
async fail(error) {
|
|
9041
10099
|
await flush();
|
|
10100
|
+
if (failedByErrorChunk) return;
|
|
9042
10101
|
await store2.updateStreamStatus(streamId, "failed", { error });
|
|
9043
10102
|
},
|
|
9044
10103
|
async cleanup() {
|
|
@@ -9048,7 +10107,7 @@ async function persistedWriter(options) {
|
|
|
9048
10107
|
}
|
|
9049
10108
|
|
|
9050
10109
|
// packages/context/src/lib/stream/polling-change-source.ts
|
|
9051
|
-
import { setTimeout as
|
|
10110
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
9052
10111
|
|
|
9053
10112
|
// packages/context/src/lib/stream/polling-policy.ts
|
|
9054
10113
|
var DEFAULT_WATCH_POLLING = {
|
|
@@ -9093,7 +10152,7 @@ function nextAdaptivePollingDelay(state) {
|
|
|
9093
10152
|
state.config.minMs,
|
|
9094
10153
|
state.config.maxMs
|
|
9095
10154
|
);
|
|
9096
|
-
const
|
|
10155
|
+
const delay3 = applyJitter(
|
|
9097
10156
|
current,
|
|
9098
10157
|
state.config.jitterRatio,
|
|
9099
10158
|
state.config.minMs,
|
|
@@ -9104,7 +10163,7 @@ function nextAdaptivePollingDelay(state) {
|
|
|
9104
10163
|
state.config.minMs,
|
|
9105
10164
|
state.config.maxMs
|
|
9106
10165
|
);
|
|
9107
|
-
return
|
|
10166
|
+
return delay3;
|
|
9108
10167
|
}
|
|
9109
10168
|
function normalizeAdaptivePolling(polling, fallback) {
|
|
9110
10169
|
const merged = {
|
|
@@ -9204,7 +10263,7 @@ function isTerminal(status) {
|
|
|
9204
10263
|
return status !== "queued" && status !== "running";
|
|
9205
10264
|
}
|
|
9206
10265
|
function waitForDelay(ms, signal) {
|
|
9207
|
-
return
|
|
10266
|
+
return delay2(ms, true, { signal }).catch((err) => {
|
|
9208
10267
|
if (signal.aborted) return false;
|
|
9209
10268
|
throw err;
|
|
9210
10269
|
});
|
|
@@ -9256,7 +10315,7 @@ function asStaticWordPartText(parts, options = {}) {
|
|
|
9256
10315
|
}
|
|
9257
10316
|
|
|
9258
10317
|
// packages/context/src/lib/stream/postgres-notify-change-source.ts
|
|
9259
|
-
import { createRequire as
|
|
10318
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
9260
10319
|
|
|
9261
10320
|
// packages/context/src/lib/stream/ddl.stream.postgres-notify.ts
|
|
9262
10321
|
var DEFAULT_POSTGRES_STREAM_CHANGES_CHANNEL = "deepagents_stream_changes";
|
|
@@ -9358,7 +10417,7 @@ var PostgresNotifyChangeSource = class _PostgresNotifyChangeSource {
|
|
|
9358
10417
|
}
|
|
9359
10418
|
static #requirePg() {
|
|
9360
10419
|
try {
|
|
9361
|
-
const require2 =
|
|
10420
|
+
const require2 = createRequire4(import.meta.url);
|
|
9362
10421
|
return require2("pg");
|
|
9363
10422
|
} catch {
|
|
9364
10423
|
throw new Error(
|
|
@@ -9613,7 +10672,7 @@ function assertIdentifier(value, label) {
|
|
|
9613
10672
|
}
|
|
9614
10673
|
|
|
9615
10674
|
// packages/context/src/lib/stream/postgres.stream-store.ts
|
|
9616
|
-
import { createRequire as
|
|
10675
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
9617
10676
|
|
|
9618
10677
|
// packages/context/src/lib/stream/ddl.stream.postgres.ts
|
|
9619
10678
|
function postgresStreamDDL(schema) {
|
|
@@ -9649,6 +10708,15 @@ CREATE INDEX IF NOT EXISTS "idx_${schema}_streams_status_created_at_id"
|
|
|
9649
10708
|
}
|
|
9650
10709
|
|
|
9651
10710
|
// packages/context/src/lib/stream/stream-store.ts
|
|
10711
|
+
function collectStreamFailures(chunks) {
|
|
10712
|
+
const failures = /* @__PURE__ */ new Map();
|
|
10713
|
+
for (const chunk of chunks) {
|
|
10714
|
+
if (chunk.data.type === "error" && !failures.has(chunk.streamId)) {
|
|
10715
|
+
failures.set(chunk.streamId, chunk.data.errorText);
|
|
10716
|
+
}
|
|
10717
|
+
}
|
|
10718
|
+
return [...failures].map(([streamId, error]) => ({ streamId, error }));
|
|
10719
|
+
}
|
|
9652
10720
|
var StreamStore = class {
|
|
9653
10721
|
async listRunningStreamIds() {
|
|
9654
10722
|
return this.listStreamIds({ status: "running" });
|
|
@@ -9678,7 +10746,7 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
|
|
|
9678
10746
|
}
|
|
9679
10747
|
static #requirePg() {
|
|
9680
10748
|
try {
|
|
9681
|
-
const require2 =
|
|
10749
|
+
const require2 = createRequire5(import.meta.url);
|
|
9682
10750
|
return require2("pg");
|
|
9683
10751
|
} catch {
|
|
9684
10752
|
throw new Error(
|
|
@@ -9832,13 +10900,31 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
|
|
|
9832
10900
|
data: chunk.data,
|
|
9833
10901
|
created_at: chunk.createdAt
|
|
9834
10902
|
}));
|
|
9835
|
-
|
|
9836
|
-
|
|
10903
|
+
const params = [JSON.stringify(rows)];
|
|
10904
|
+
const insertSql = `INSERT INTO ${this.#t("stream_chunks")} (stream_id, seq, data, created_at)
|
|
9837
10905
|
SELECT stream_id, seq, data, created_at
|
|
9838
10906
|
FROM jsonb_to_recordset($1::jsonb)
|
|
9839
|
-
AS rows(stream_id TEXT, seq INTEGER, data JSONB, created_at BIGINT)
|
|
9840
|
-
|
|
9841
|
-
)
|
|
10907
|
+
AS rows(stream_id TEXT, seq INTEGER, data JSONB, created_at BIGINT)`;
|
|
10908
|
+
const failures = collectStreamFailures(chunks);
|
|
10909
|
+
if (failures.length === 0) {
|
|
10910
|
+
await this.#query(insertSql, params);
|
|
10911
|
+
return;
|
|
10912
|
+
}
|
|
10913
|
+
await this.#useTransaction(async (client) => {
|
|
10914
|
+
await client.query(insertSql, params);
|
|
10915
|
+
const failedAt = Date.now();
|
|
10916
|
+
for (const failure of failures) {
|
|
10917
|
+
const result = await client.query(
|
|
10918
|
+
`UPDATE ${this.#t("streams")}
|
|
10919
|
+
SET status = $1, finished_at = $2, error = $3
|
|
10920
|
+
WHERE id = $4`,
|
|
10921
|
+
["failed", failedAt, failure.error, failure.streamId]
|
|
10922
|
+
);
|
|
10923
|
+
if (result.rowCount !== 1) {
|
|
10924
|
+
throw new Error(`Stream "${failure.streamId}" not found`);
|
|
10925
|
+
}
|
|
10926
|
+
}
|
|
10927
|
+
});
|
|
9842
10928
|
}
|
|
9843
10929
|
async getChunks(streamId, fromSeq, limit) {
|
|
9844
10930
|
const params = [streamId];
|
|
@@ -10085,6 +11171,7 @@ var SqliteStreamStore = class extends StreamStore {
|
|
|
10085
11171
|
}
|
|
10086
11172
|
async appendChunks(chunks) {
|
|
10087
11173
|
if (chunks.length === 0) return;
|
|
11174
|
+
const failures = collectStreamFailures(chunks);
|
|
10088
11175
|
this.#db.exec("BEGIN TRANSACTION");
|
|
10089
11176
|
try {
|
|
10090
11177
|
for (const chunk of chunks) {
|
|
@@ -10098,6 +11185,15 @@ var SqliteStreamStore = class extends StreamStore {
|
|
|
10098
11185
|
chunk.createdAt
|
|
10099
11186
|
);
|
|
10100
11187
|
}
|
|
11188
|
+
const failedAt = Date.now();
|
|
11189
|
+
for (const failure of failures) {
|
|
11190
|
+
const result = this.#stmt(
|
|
11191
|
+
"UPDATE streams SET status = ?, finishedAt = ?, error = ? WHERE id = ?"
|
|
11192
|
+
).run("failed", failedAt, failure.error, failure.streamId);
|
|
11193
|
+
if (result.changes !== 1) {
|
|
11194
|
+
throw new Error(`Stream "${failure.streamId}" not found`);
|
|
11195
|
+
}
|
|
11196
|
+
}
|
|
10101
11197
|
this.#db.exec("COMMIT");
|
|
10102
11198
|
} catch (error) {
|
|
10103
11199
|
this.#db.exec("ROLLBACK");
|
|
@@ -10499,14 +11595,20 @@ export {
|
|
|
10499
11595
|
AsyncResolver,
|
|
10500
11596
|
BM25Classifier,
|
|
10501
11597
|
BashException,
|
|
11598
|
+
BinInstaller,
|
|
10502
11599
|
ComposeStartError,
|
|
10503
11600
|
ComposeStrategy,
|
|
10504
11601
|
ContainerCreationError,
|
|
10505
11602
|
ContextEngine,
|
|
10506
11603
|
ContextRenderer,
|
|
10507
11604
|
ContextStore,
|
|
11605
|
+
DAYTONA_DEFAULT_DESTINATION,
|
|
10508
11606
|
DEFAULT_CANCEL_POLLING,
|
|
10509
11607
|
DEFAULT_WATCH_POLLING,
|
|
11608
|
+
DaytonaCommandError,
|
|
11609
|
+
DaytonaCreationError,
|
|
11610
|
+
DaytonaNotAvailableError,
|
|
11611
|
+
DaytonaSandboxError,
|
|
10510
11612
|
DockerNotAvailableError,
|
|
10511
11613
|
DockerSandboxError,
|
|
10512
11614
|
DockerSandboxStrategy,
|
|
@@ -10524,9 +11626,7 @@ export {
|
|
|
10524
11626
|
MarkdownRenderer,
|
|
10525
11627
|
MissingRuntimeError,
|
|
10526
11628
|
ModelsRegistry,
|
|
10527
|
-
MountPathError,
|
|
10528
11629
|
NpmInstaller,
|
|
10529
|
-
ObservedFs,
|
|
10530
11630
|
PackageInstallError,
|
|
10531
11631
|
PackageInstaller,
|
|
10532
11632
|
PipInstaller,
|
|
@@ -10536,6 +11636,7 @@ export {
|
|
|
10536
11636
|
PostgresStreamStore,
|
|
10537
11637
|
PromiseResolver,
|
|
10538
11638
|
RuntimeStrategy,
|
|
11639
|
+
SnapshotFailedError,
|
|
10539
11640
|
SqlServerContextStore,
|
|
10540
11641
|
SqliteContextStore,
|
|
10541
11642
|
SqliteStreamStore,
|
|
@@ -10545,6 +11646,10 @@ export {
|
|
|
10545
11646
|
TomlRenderer,
|
|
10546
11647
|
ToonRenderer,
|
|
10547
11648
|
UrlBinaryInstaller,
|
|
11649
|
+
VolumeCreateError,
|
|
11650
|
+
VolumeInspectError,
|
|
11651
|
+
VolumePathError,
|
|
11652
|
+
VolumeRemoveError,
|
|
10548
11653
|
XmlRenderer,
|
|
10549
11654
|
addUsage,
|
|
10550
11655
|
advisorPreamble,
|
|
@@ -10565,11 +11670,13 @@ export {
|
|
|
10565
11670
|
assertCountSpec,
|
|
10566
11671
|
assistant,
|
|
10567
11672
|
assistantText,
|
|
11673
|
+
bin,
|
|
10568
11674
|
buildSubcommandRepair,
|
|
10569
11675
|
chat,
|
|
10570
11676
|
checkCount,
|
|
10571
11677
|
clarification,
|
|
10572
11678
|
classifies,
|
|
11679
|
+
collectStreamFailures,
|
|
10573
11680
|
contentIncludes,
|
|
10574
11681
|
contentMatches,
|
|
10575
11682
|
contentPattern,
|
|
@@ -10578,7 +11685,7 @@ export {
|
|
|
10578
11685
|
createAgentOsSandbox,
|
|
10579
11686
|
createBashTool,
|
|
10580
11687
|
createBinaryBridges,
|
|
10581
|
-
|
|
11688
|
+
createDaytonaSandbox,
|
|
10582
11689
|
createDockerSandbox,
|
|
10583
11690
|
createInstallerContext,
|
|
10584
11691
|
createRepairToolCall,
|
|
@@ -10639,6 +11746,7 @@ export {
|
|
|
10639
11746
|
not,
|
|
10640
11747
|
npm,
|
|
10641
11748
|
nullUsage,
|
|
11749
|
+
observeSandboxFileEvents,
|
|
10642
11750
|
once,
|
|
10643
11751
|
or,
|
|
10644
11752
|
parseFrontmatter,
|
|
@@ -10663,11 +11771,12 @@ export {
|
|
|
10663
11771
|
resolveTz,
|
|
10664
11772
|
role,
|
|
10665
11773
|
runGuardrailChain,
|
|
11774
|
+
runWithAbortSignal,
|
|
10666
11775
|
runWithBashMeta,
|
|
10667
11776
|
seasonChanged,
|
|
10668
11777
|
seasonReminder,
|
|
10669
11778
|
selfCritique,
|
|
10670
|
-
shellQuote,
|
|
11779
|
+
shellQuote3 as shellQuote,
|
|
10671
11780
|
skills,
|
|
10672
11781
|
skillsReminder,
|
|
10673
11782
|
socraticPrompting,
|
|
@@ -10696,6 +11805,7 @@ export {
|
|
|
10696
11805
|
user,
|
|
10697
11806
|
visualizeGraph,
|
|
10698
11807
|
weekChanged,
|
|
11808
|
+
withAbortSignal,
|
|
10699
11809
|
withinLastN,
|
|
10700
11810
|
workflow,
|
|
10701
11811
|
yearChanged,
|