@deepagents/context 1.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +678 -290
- package/dist/index.js.map +4 -4
- 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 +3 -6
- 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/container-tool.d.ts +16 -14
- package/dist/lib/sandbox/container-tool.d.ts.map +1 -1
- package/dist/lib/sandbox/docker-sandbox-errors.d.ts +19 -3
- 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 +24 -38
- package/dist/lib/sandbox/file-events.d.ts.map +1 -1
- package/dist/lib/sandbox/index.d.ts +1 -0
- package/dist/lib/sandbox/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/package.json +4 -12
- package/dist/lib/sandboxes/openai.d.ts +0 -6
- package/dist/lib/sandboxes/openai.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -4697,7 +4697,7 @@ async function chat(agent2, options = {}) {
|
|
|
4697
4697
|
if (isAborted) {
|
|
4698
4698
|
normalizedMessage.parts = sanitizeAbortedParts(normalizedMessage.parts);
|
|
4699
4699
|
}
|
|
4700
|
-
const drained = sandbox.drainFileEvents
|
|
4700
|
+
const drained = sandbox.drainFileEvents();
|
|
4701
4701
|
const fileEvents = isAborted ? [] : drained;
|
|
4702
4702
|
const finalMetadata = await options.finalAssistantMetadata?.(normalizedMessage);
|
|
4703
4703
|
const mergedMetadata = {
|
|
@@ -5454,15 +5454,15 @@ function dateReminder(options) {
|
|
|
5454
5454
|
const now = /* @__PURE__ */ new Date();
|
|
5455
5455
|
const currentDate = formatDateKey(now, tz);
|
|
5456
5456
|
const currentDay = formatDayOfWeek(now, tz);
|
|
5457
|
-
let
|
|
5457
|
+
let diff2 = "";
|
|
5458
5458
|
if (ctx.lastMessageAt !== void 0) {
|
|
5459
5459
|
const prev = new Date(ctx.lastMessageAt);
|
|
5460
|
-
|
|
5460
|
+
diff2 = formatDiff([
|
|
5461
5461
|
diffLine("date", formatDateKey(prev, tz), currentDate),
|
|
5462
5462
|
diffLine("day of week", formatDayOfWeek(prev, tz), currentDay)
|
|
5463
5463
|
]);
|
|
5464
5464
|
}
|
|
5465
|
-
return `${
|
|
5465
|
+
return `${diff2}Date: ${currentDate}
|
|
5466
5466
|
Day of Week: ${currentDay}`;
|
|
5467
5467
|
},
|
|
5468
5468
|
{ when: dayChanged(options), asPart: false }
|
|
@@ -5474,14 +5474,14 @@ function timeReminder(options) {
|
|
|
5474
5474
|
const tz = resolveTz(options, ctx);
|
|
5475
5475
|
const now = /* @__PURE__ */ new Date();
|
|
5476
5476
|
const currentTime = formatTime(now, tz);
|
|
5477
|
-
let
|
|
5477
|
+
let diff2 = "";
|
|
5478
5478
|
if (ctx.lastMessageAt !== void 0) {
|
|
5479
5479
|
const prev = new Date(ctx.lastMessageAt);
|
|
5480
|
-
|
|
5480
|
+
diff2 = formatDiff([
|
|
5481
5481
|
diffLine("hour", formatHour(prev, tz), formatHour(now, tz))
|
|
5482
5482
|
]);
|
|
5483
5483
|
}
|
|
5484
|
-
return `${
|
|
5484
|
+
return `${diff2}Time: ${currentTime}`;
|
|
5485
5485
|
},
|
|
5486
5486
|
{ when: hourChanged(options), asPart: false }
|
|
5487
5487
|
);
|
|
@@ -5492,14 +5492,14 @@ function monthReminder(options) {
|
|
|
5492
5492
|
const tz = resolveTz(options, ctx);
|
|
5493
5493
|
const now = /* @__PURE__ */ new Date();
|
|
5494
5494
|
const currentMonth = formatMonthName(now, tz);
|
|
5495
|
-
let
|
|
5495
|
+
let diff2 = "";
|
|
5496
5496
|
if (ctx.lastMessageAt !== void 0) {
|
|
5497
5497
|
const prev = new Date(ctx.lastMessageAt);
|
|
5498
|
-
|
|
5498
|
+
diff2 = formatDiff([
|
|
5499
5499
|
diffLine("month", formatMonthName(prev, tz), currentMonth)
|
|
5500
5500
|
]);
|
|
5501
5501
|
}
|
|
5502
|
-
return `${
|
|
5502
|
+
return `${diff2}Month: ${currentMonth}`;
|
|
5503
5503
|
},
|
|
5504
5504
|
{ when: monthChanged(options), asPart: false }
|
|
5505
5505
|
);
|
|
@@ -5510,14 +5510,14 @@ function yearReminder(options) {
|
|
|
5510
5510
|
const tz = resolveTz(options, ctx);
|
|
5511
5511
|
const now = /* @__PURE__ */ new Date();
|
|
5512
5512
|
const currentYear = formatYear(now, tz);
|
|
5513
|
-
let
|
|
5513
|
+
let diff2 = "";
|
|
5514
5514
|
if (ctx.lastMessageAt !== void 0) {
|
|
5515
5515
|
const prev = new Date(ctx.lastMessageAt);
|
|
5516
|
-
|
|
5516
|
+
diff2 = formatDiff([
|
|
5517
5517
|
diffLine("year", formatYear(prev, tz), currentYear)
|
|
5518
5518
|
]);
|
|
5519
5519
|
}
|
|
5520
|
-
return `${
|
|
5520
|
+
return `${diff2}Year: ${currentYear}`;
|
|
5521
5521
|
},
|
|
5522
5522
|
{ when: yearChanged(options), asPart: false }
|
|
5523
5523
|
);
|
|
@@ -5528,14 +5528,14 @@ function seasonReminder(options) {
|
|
|
5528
5528
|
const tz = resolveTz(options, ctx);
|
|
5529
5529
|
const now = /* @__PURE__ */ new Date();
|
|
5530
5530
|
const currentSeason = getSeason(getMonthIndex(now, tz));
|
|
5531
|
-
let
|
|
5531
|
+
let diff2 = "";
|
|
5532
5532
|
if (ctx.lastMessageAt !== void 0) {
|
|
5533
5533
|
const prev = new Date(ctx.lastMessageAt);
|
|
5534
|
-
|
|
5534
|
+
diff2 = formatDiff([
|
|
5535
5535
|
diffLine("season", getSeason(getMonthIndex(prev, tz)), currentSeason)
|
|
5536
5536
|
]);
|
|
5537
5537
|
}
|
|
5538
|
-
return `${
|
|
5538
|
+
return `${diff2}Season: ${currentSeason}`;
|
|
5539
5539
|
},
|
|
5540
5540
|
{ when: seasonChanged(options), asPart: false }
|
|
5541
5541
|
);
|
|
@@ -5553,11 +5553,11 @@ function localeReminder() {
|
|
|
5553
5553
|
const current = getLocaleFromMessage(ctx.currentMessage);
|
|
5554
5554
|
if (!current) return "";
|
|
5555
5555
|
const last = getLocaleFromMessage(ctx.lastMessage);
|
|
5556
|
-
const
|
|
5556
|
+
const diff2 = last ? formatDiff([
|
|
5557
5557
|
diffLine("language", last.language, current.language),
|
|
5558
5558
|
diffLine("timezone", last.timeZone, current.timeZone)
|
|
5559
5559
|
]) : "";
|
|
5560
|
-
return `${
|
|
5560
|
+
return `${diff2}Language: ${current.language}
|
|
5561
5561
|
Timezone: ${current.timeZone}`;
|
|
5562
5562
|
},
|
|
5563
5563
|
{ when: whenFn, asPart: false }
|
|
@@ -5829,6 +5829,25 @@ function render(tag, ...fragments) {
|
|
|
5829
5829
|
return renderer.render([wrapped]);
|
|
5830
5830
|
}
|
|
5831
5831
|
|
|
5832
|
+
// packages/context/src/lib/sandbox/abort.ts
|
|
5833
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5834
|
+
var ambientAbortSignal = new AsyncLocalStorage();
|
|
5835
|
+
function runWithAbortSignal(signal, fn) {
|
|
5836
|
+
return ambientAbortSignal.run(signal, fn);
|
|
5837
|
+
}
|
|
5838
|
+
function withAbortSignal(sandbox) {
|
|
5839
|
+
return {
|
|
5840
|
+
...sandbox,
|
|
5841
|
+
async executeCommand(command, options) {
|
|
5842
|
+
const signal = options?.signal ?? ambientAbortSignal.getStore();
|
|
5843
|
+
return sandbox.executeCommand(
|
|
5844
|
+
command,
|
|
5845
|
+
signal ? { ...options, signal } : options
|
|
5846
|
+
);
|
|
5847
|
+
}
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5832
5851
|
// packages/context/src/lib/sandbox/agent-os-sandbox.ts
|
|
5833
5852
|
import "bash-tool";
|
|
5834
5853
|
var textDecoder = new TextDecoder();
|
|
@@ -5935,8 +5954,8 @@ var BashException = class extends Error {
|
|
|
5935
5954
|
};
|
|
5936
5955
|
|
|
5937
5956
|
// packages/context/src/lib/sandbox/bash-meta.ts
|
|
5938
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5939
|
-
var store = new
|
|
5957
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
5958
|
+
var store = new AsyncLocalStorage2();
|
|
5940
5959
|
function runWithBashMeta(fn) {
|
|
5941
5960
|
return store.run({ hidden: {} }, fn);
|
|
5942
5961
|
}
|
|
@@ -5966,6 +5985,103 @@ import {
|
|
|
5966
5985
|
} from "bash-tool";
|
|
5967
5986
|
import z3 from "zod";
|
|
5968
5987
|
|
|
5988
|
+
// packages/context/src/lib/sandbox/file-events.ts
|
|
5989
|
+
var SnapshotFailedError = class extends Error {
|
|
5990
|
+
stderr;
|
|
5991
|
+
constructor(message2, stderr) {
|
|
5992
|
+
super(message2);
|
|
5993
|
+
this.name = "SnapshotFailedError";
|
|
5994
|
+
this.stderr = stderr;
|
|
5995
|
+
}
|
|
5996
|
+
};
|
|
5997
|
+
function shellQuote(value) {
|
|
5998
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
5999
|
+
}
|
|
6000
|
+
async function snapshot(execute, destination) {
|
|
6001
|
+
const probe = await execute(`[ -d ${shellQuote(destination)} ]`);
|
|
6002
|
+
if (probe.exitCode !== 0) return /* @__PURE__ */ new Map();
|
|
6003
|
+
const list = await execute(
|
|
6004
|
+
`find ${shellQuote(destination)} -type f -print0 | while IFS= read -r -d '' p; do sha256sum "$p"; done`
|
|
6005
|
+
);
|
|
6006
|
+
if (list.exitCode !== 0) {
|
|
6007
|
+
throw new SnapshotFailedError(
|
|
6008
|
+
`snapshot failed for ${destination}`,
|
|
6009
|
+
list.stderr
|
|
6010
|
+
);
|
|
6011
|
+
}
|
|
6012
|
+
const snap = /* @__PURE__ */ new Map();
|
|
6013
|
+
if (!list.stdout) return snap;
|
|
6014
|
+
for (const line of list.stdout.split("\n")) {
|
|
6015
|
+
if (line.length < 66) continue;
|
|
6016
|
+
snap.set(line.slice(66), line.slice(0, 64));
|
|
6017
|
+
}
|
|
6018
|
+
return snap;
|
|
6019
|
+
}
|
|
6020
|
+
function diff(before, after) {
|
|
6021
|
+
const events = [];
|
|
6022
|
+
const ts = Date.now();
|
|
6023
|
+
for (const [path5, hash] of after) {
|
|
6024
|
+
const prior = before.get(path5);
|
|
6025
|
+
if (prior === void 0) {
|
|
6026
|
+
events.push({ path: path5, op: "write", timestamp: ts });
|
|
6027
|
+
} else if (prior !== hash) {
|
|
6028
|
+
events.push({ path: path5, op: "modify", timestamp: ts });
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
for (const path5 of before.keys()) {
|
|
6032
|
+
if (!after.has(path5)) {
|
|
6033
|
+
events.push({ path: path5, op: "delete", timestamp: ts });
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
return events;
|
|
6037
|
+
}
|
|
6038
|
+
function observeSandboxFileEvents(sandbox, options) {
|
|
6039
|
+
const { destination } = options;
|
|
6040
|
+
if (!destination) {
|
|
6041
|
+
throw new Error("observeSandboxFileEvents: destination is required");
|
|
6042
|
+
}
|
|
6043
|
+
const innerExecute = sandbox.executeCommand.bind(sandbox);
|
|
6044
|
+
const innerReadFile = sandbox.readFile.bind(sandbox);
|
|
6045
|
+
const innerWriteFiles = sandbox.writeFiles.bind(sandbox);
|
|
6046
|
+
const buffer = [];
|
|
6047
|
+
const observe = async (fn) => {
|
|
6048
|
+
const before = await snapshot(innerExecute, destination);
|
|
6049
|
+
const takeAfter = async () => {
|
|
6050
|
+
const after = await snapshot(innerExecute, destination);
|
|
6051
|
+
buffer.push(...diff(before, after));
|
|
6052
|
+
};
|
|
6053
|
+
try {
|
|
6054
|
+
const result = await fn();
|
|
6055
|
+
await takeAfter();
|
|
6056
|
+
return result;
|
|
6057
|
+
} catch (err) {
|
|
6058
|
+
await takeAfter().catch(() => {
|
|
6059
|
+
});
|
|
6060
|
+
throw err;
|
|
6061
|
+
}
|
|
6062
|
+
};
|
|
6063
|
+
const decorated = {
|
|
6064
|
+
async executeCommand(command, options2) {
|
|
6065
|
+
return observe(() => innerExecute(command, options2));
|
|
6066
|
+
},
|
|
6067
|
+
async readFile(path5) {
|
|
6068
|
+
const content = await innerReadFile(path5);
|
|
6069
|
+
buffer.push({ path: path5, op: "read", timestamp: Date.now() });
|
|
6070
|
+
return content;
|
|
6071
|
+
},
|
|
6072
|
+
async writeFiles(files) {
|
|
6073
|
+
await observe(() => innerWriteFiles(files));
|
|
6074
|
+
},
|
|
6075
|
+
dispose: sandbox.dispose.bind(sandbox)
|
|
6076
|
+
};
|
|
6077
|
+
return {
|
|
6078
|
+
sandbox: decorated,
|
|
6079
|
+
drain() {
|
|
6080
|
+
return buffer.splice(0, buffer.length);
|
|
6081
|
+
}
|
|
6082
|
+
};
|
|
6083
|
+
}
|
|
6084
|
+
|
|
5969
6085
|
// packages/context/src/lib/sandbox/upload-skills.ts
|
|
5970
6086
|
import * as path3 from "node:path";
|
|
5971
6087
|
|
|
@@ -6111,22 +6227,38 @@ async function uploadSkills(sandbox, inputs) {
|
|
|
6111
6227
|
|
|
6112
6228
|
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
6113
6229
|
var REASONING_INSTRUCTION = 'Every bash tool call must include a brief non-empty "reasoning" input explaining why the command is needed.';
|
|
6114
|
-
|
|
6115
|
-
|
|
6230
|
+
function withBashExceptionCatch(sandbox) {
|
|
6231
|
+
return {
|
|
6232
|
+
...sandbox,
|
|
6233
|
+
async executeCommand(command, options) {
|
|
6234
|
+
try {
|
|
6235
|
+
return await sandbox.executeCommand(command, options);
|
|
6236
|
+
} catch (err) {
|
|
6237
|
+
if (err instanceof BashException) return err.format();
|
|
6238
|
+
throw err;
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6243
|
+
async function createBashTool(options) {
|
|
6244
|
+
const {
|
|
6245
|
+
skills: skillInputs = [],
|
|
6246
|
+
extraInstructions,
|
|
6247
|
+
sandbox: backend,
|
|
6248
|
+
destination,
|
|
6249
|
+
...rest
|
|
6250
|
+
} = options;
|
|
6251
|
+
const observer = observeSandboxFileEvents(backend, {
|
|
6252
|
+
destination: destination ?? "/workspace"
|
|
6253
|
+
});
|
|
6254
|
+
const sandbox = withAbortSignal(withBashExceptionCatch(observer.sandbox));
|
|
6116
6255
|
const combinedInstructions = [extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
|
|
6117
6256
|
const toolkit = await externalCreateBashTool({
|
|
6118
6257
|
...rest,
|
|
6258
|
+
sandbox,
|
|
6259
|
+
destination,
|
|
6119
6260
|
extraInstructions: combinedInstructions
|
|
6120
6261
|
});
|
|
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
6262
|
const upstreamBash = toolkit.bash;
|
|
6131
6263
|
const originalExecute = upstreamBash.execute;
|
|
6132
6264
|
const toolBuilder = tool2;
|
|
@@ -6141,197 +6273,43 @@ async function createBashTool(options = {}) {
|
|
|
6141
6273
|
if (!originalExecute) {
|
|
6142
6274
|
throw new Error("bash tool execution is not available");
|
|
6143
6275
|
}
|
|
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
|
-
});
|
|
6276
|
+
const { abortSignal } = execOptions;
|
|
6277
|
+
return runWithAbortSignal(
|
|
6278
|
+
abortSignal,
|
|
6279
|
+
() => runWithBashMeta(async () => {
|
|
6280
|
+
const result = await originalExecute({ command }, execOptions);
|
|
6281
|
+
const state = readBashMeta();
|
|
6282
|
+
if (!state) return result;
|
|
6283
|
+
const hasHidden = Object.keys(state.hidden).length > 0;
|
|
6284
|
+
const hasReminder = state.reminder !== void 0;
|
|
6285
|
+
if (!hasHidden && !hasReminder) return result;
|
|
6286
|
+
return {
|
|
6287
|
+
...result,
|
|
6288
|
+
...hasHidden ? { meta: state.hidden } : {},
|
|
6289
|
+
...hasReminder ? { reminder: state.reminder } : {}
|
|
6290
|
+
};
|
|
6291
|
+
})
|
|
6292
|
+
);
|
|
6166
6293
|
},
|
|
6167
6294
|
toModelOutput: ({ output }) => {
|
|
6168
6295
|
if (typeof output !== "object" || output === null) {
|
|
6169
6296
|
return { type: "json", value: output };
|
|
6170
6297
|
}
|
|
6171
|
-
const
|
|
6172
|
-
if (!("meta" in record)) {
|
|
6173
|
-
return { type: "json", value: record };
|
|
6174
|
-
}
|
|
6175
|
-
const { meta: _meta, ...visible } = record;
|
|
6298
|
+
const { meta: _meta, ...visible } = output;
|
|
6176
6299
|
return { type: "json", value: visible };
|
|
6177
6300
|
}
|
|
6178
6301
|
});
|
|
6179
|
-
const skills2 = await uploadSkills(
|
|
6302
|
+
const skills2 = await uploadSkills(sandbox, skillInputs);
|
|
6180
6303
|
return {
|
|
6181
6304
|
...toolkit,
|
|
6305
|
+
sandbox,
|
|
6182
6306
|
bash,
|
|
6183
6307
|
tools: { ...toolkit.tools, bash },
|
|
6184
|
-
skills: skills2
|
|
6308
|
+
skills: skills2,
|
|
6309
|
+
drainFileEvents: () => observer.drain()
|
|
6185
6310
|
};
|
|
6186
6311
|
}
|
|
6187
6312
|
|
|
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
6313
|
// packages/context/src/lib/sandbox/binary-bridges.ts
|
|
6336
6314
|
import { existsSync as existsSync2 } from "fs";
|
|
6337
6315
|
import { defineCommand } from "just-bash";
|
|
@@ -6431,8 +6409,10 @@ function resolveRealCwd(ctx) {
|
|
|
6431
6409
|
// packages/context/src/lib/sandbox/docker-sandbox.ts
|
|
6432
6410
|
import "bash-tool";
|
|
6433
6411
|
import spawn3 from "nano-spawn";
|
|
6412
|
+
import { spawn as childSpawn } from "node:child_process";
|
|
6434
6413
|
import { createHash } from "node:crypto";
|
|
6435
6414
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
6415
|
+
import { Readable } from "node:stream";
|
|
6436
6416
|
|
|
6437
6417
|
// packages/context/src/lib/sandbox/docker-sandbox-errors.ts
|
|
6438
6418
|
var DockerSandboxError = class extends Error {
|
|
@@ -6505,16 +6485,48 @@ var MissingRuntimeError = class extends DockerSandboxError {
|
|
|
6505
6485
|
this.required = required;
|
|
6506
6486
|
}
|
|
6507
6487
|
};
|
|
6508
|
-
var
|
|
6509
|
-
|
|
6488
|
+
var VolumePathError = class extends DockerSandboxError {
|
|
6489
|
+
source;
|
|
6510
6490
|
containerPath;
|
|
6511
|
-
|
|
6491
|
+
reason;
|
|
6492
|
+
constructor(source, containerPath, reason) {
|
|
6512
6493
|
super(
|
|
6513
|
-
`
|
|
6494
|
+
`Invalid Docker volume path "${source}" -> "${containerPath}": ${reason}`
|
|
6514
6495
|
);
|
|
6515
|
-
this.name = "
|
|
6516
|
-
this.
|
|
6496
|
+
this.name = "VolumePathError";
|
|
6497
|
+
this.source = source;
|
|
6517
6498
|
this.containerPath = containerPath;
|
|
6499
|
+
this.reason = reason;
|
|
6500
|
+
}
|
|
6501
|
+
};
|
|
6502
|
+
var VolumeInspectError = class extends DockerSandboxError {
|
|
6503
|
+
volume;
|
|
6504
|
+
reason;
|
|
6505
|
+
constructor(volume, reason) {
|
|
6506
|
+
super(`Failed to inspect Docker volume "${volume}": ${reason}`);
|
|
6507
|
+
this.name = "VolumeInspectError";
|
|
6508
|
+
this.volume = volume;
|
|
6509
|
+
this.reason = reason;
|
|
6510
|
+
}
|
|
6511
|
+
};
|
|
6512
|
+
var VolumeCreateError = class extends DockerSandboxError {
|
|
6513
|
+
volume;
|
|
6514
|
+
reason;
|
|
6515
|
+
constructor(volume, reason) {
|
|
6516
|
+
super(`Failed to create Docker volume "${volume}": ${reason}`);
|
|
6517
|
+
this.name = "VolumeCreateError";
|
|
6518
|
+
this.volume = volume;
|
|
6519
|
+
this.reason = reason;
|
|
6520
|
+
}
|
|
6521
|
+
};
|
|
6522
|
+
var VolumeRemoveError = class extends DockerSandboxError {
|
|
6523
|
+
volume;
|
|
6524
|
+
reason;
|
|
6525
|
+
constructor(volume, reason) {
|
|
6526
|
+
super(`Failed to remove Docker volume "${volume}": ${reason}`);
|
|
6527
|
+
this.name = "VolumeRemoveError";
|
|
6528
|
+
this.volume = volume;
|
|
6529
|
+
this.reason = reason;
|
|
6518
6530
|
}
|
|
6519
6531
|
};
|
|
6520
6532
|
var DockerfileBuildError = class extends DockerSandboxError {
|
|
@@ -6547,7 +6559,7 @@ function isDebianBased(image) {
|
|
|
6547
6559
|
const debianPatterns = ["debian", "ubuntu", "node", "python"];
|
|
6548
6560
|
return debianPatterns.some((pattern) => lower.includes(pattern));
|
|
6549
6561
|
}
|
|
6550
|
-
function
|
|
6562
|
+
function shellQuote2(s) {
|
|
6551
6563
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6552
6564
|
}
|
|
6553
6565
|
function createInstallerContext(containerId, image) {
|
|
@@ -6595,7 +6607,7 @@ function createInstallerContext(containerId, image) {
|
|
|
6595
6607
|
};
|
|
6596
6608
|
const installPackages = async (packages) => {
|
|
6597
6609
|
if (packages.length === 0) return;
|
|
6598
|
-
const quoted = packages.map(
|
|
6610
|
+
const quoted = packages.map(shellQuote2).join(" ");
|
|
6599
6611
|
let cmd;
|
|
6600
6612
|
if (packageManager === "apt-get") {
|
|
6601
6613
|
cmd = aptUpdated ? `apt-get install -y ${quoted}` : `apt-get update && apt-get install -y ${quoted}`;
|
|
@@ -6617,7 +6629,7 @@ function createInstallerContext(containerId, image) {
|
|
|
6617
6629
|
const ensureTool = async (checkName, installName) => {
|
|
6618
6630
|
const cacheKey = installName ?? checkName;
|
|
6619
6631
|
if (ensuredTools.has(cacheKey)) return;
|
|
6620
|
-
const check = await exec(`which ${
|
|
6632
|
+
const check = await exec(`which ${shellQuote2(checkName)}`);
|
|
6621
6633
|
if (check.exitCode === 0) {
|
|
6622
6634
|
ensuredTools.add(cacheKey);
|
|
6623
6635
|
return;
|
|
@@ -6643,44 +6655,216 @@ function isDockerfileOptions(opts) {
|
|
|
6643
6655
|
function isComposeOptions(opts) {
|
|
6644
6656
|
return "compose" in opts;
|
|
6645
6657
|
}
|
|
6658
|
+
var CONTAINER_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
6646
6659
|
var DockerSandboxStrategy = class {
|
|
6647
6660
|
context;
|
|
6648
|
-
|
|
6661
|
+
volumes;
|
|
6649
6662
|
resources;
|
|
6650
6663
|
env;
|
|
6664
|
+
name;
|
|
6665
|
+
createdVolumes = /* @__PURE__ */ new Set();
|
|
6651
6666
|
constructor(args = {}) {
|
|
6652
|
-
const {
|
|
6667
|
+
const { volumes = [], resources = {}, env = {}, name } = args;
|
|
6653
6668
|
for (const key of Object.keys(env)) {
|
|
6654
|
-
|
|
6655
|
-
throw new DockerSandboxError(
|
|
6656
|
-
`Invalid environment variable key: "${key}"`
|
|
6657
|
-
);
|
|
6658
|
-
}
|
|
6669
|
+
validateEnvKey(key);
|
|
6659
6670
|
}
|
|
6660
|
-
|
|
6671
|
+
if (name !== void 0 && !CONTAINER_NAME_PATTERN.test(name)) {
|
|
6672
|
+
throw new DockerSandboxError(
|
|
6673
|
+
`Invalid container name: "${name}". Use only letters, numbers, underscore, period, or hyphen. The "sandbox-" prefix is added automatically.`
|
|
6674
|
+
);
|
|
6675
|
+
}
|
|
6676
|
+
this.volumes = volumes;
|
|
6661
6677
|
this.resources = resources;
|
|
6662
6678
|
this.env = env;
|
|
6679
|
+
this.name = name;
|
|
6663
6680
|
}
|
|
6664
6681
|
async create() {
|
|
6665
|
-
this.validateMounts();
|
|
6666
6682
|
const image = await this.getImage();
|
|
6667
|
-
|
|
6668
|
-
this.context = { containerId, image };
|
|
6683
|
+
let acquired;
|
|
6669
6684
|
try {
|
|
6670
|
-
await this.
|
|
6685
|
+
acquired = await this.acquireContainer(image);
|
|
6686
|
+
this.context = { containerId: acquired.containerId, image };
|
|
6687
|
+
if (!acquired.attached) {
|
|
6688
|
+
await this.configure();
|
|
6689
|
+
}
|
|
6671
6690
|
} catch (error) {
|
|
6672
|
-
|
|
6691
|
+
if (acquired && !acquired.attached) {
|
|
6692
|
+
await this.stopContainer(acquired.containerId);
|
|
6693
|
+
}
|
|
6694
|
+
await this.cleanupCreatedVolumesAfterFailure(error);
|
|
6673
6695
|
throw error;
|
|
6674
6696
|
}
|
|
6675
6697
|
return this.createSandboxMethods();
|
|
6676
6698
|
}
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6699
|
+
namedContainerId() {
|
|
6700
|
+
return `sandbox-${this.name}`;
|
|
6701
|
+
}
|
|
6702
|
+
defaultContainerId() {
|
|
6703
|
+
return `sandbox-${crypto.randomUUID().slice(0, 8)}`;
|
|
6704
|
+
}
|
|
6705
|
+
async acquireContainer(image) {
|
|
6706
|
+
if (!this.name) {
|
|
6707
|
+
const containerId2 = this.defaultContainerId();
|
|
6708
|
+
await this.prepareVolumes();
|
|
6709
|
+
await this.startContainer(image, containerId2);
|
|
6710
|
+
return { containerId: containerId2, attached: false };
|
|
6711
|
+
}
|
|
6712
|
+
const containerId = this.namedContainerId();
|
|
6713
|
+
const probe = await this.inspectContainer(containerId);
|
|
6714
|
+
if (probe === "running") {
|
|
6715
|
+
return { containerId, attached: true };
|
|
6716
|
+
}
|
|
6717
|
+
if (probe === "stopped") {
|
|
6718
|
+
await this.startStoppedContainer(containerId, image);
|
|
6719
|
+
return { containerId, attached: true };
|
|
6720
|
+
}
|
|
6721
|
+
await this.prepareVolumes();
|
|
6722
|
+
try {
|
|
6723
|
+
await this.startContainer(image, containerId);
|
|
6724
|
+
return { containerId, attached: false };
|
|
6725
|
+
} catch (error) {
|
|
6726
|
+
if (error instanceof ContainerCreationError && this.isNameConflictError(error.message)) {
|
|
6727
|
+
await this.cleanupCreatedVolumes();
|
|
6728
|
+
const raced = await this.inspectContainer(containerId);
|
|
6729
|
+
if (raced === "running") {
|
|
6730
|
+
return { containerId, attached: true };
|
|
6731
|
+
}
|
|
6732
|
+
if (raced === "stopped") {
|
|
6733
|
+
await this.startStoppedContainer(containerId, image);
|
|
6734
|
+
return { containerId, attached: true };
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
throw error;
|
|
6738
|
+
}
|
|
6739
|
+
}
|
|
6740
|
+
async startStoppedContainer(containerId, image) {
|
|
6741
|
+
try {
|
|
6742
|
+
await spawn3("docker", ["start", containerId]);
|
|
6743
|
+
} catch (error) {
|
|
6744
|
+
const message2 = this.getDockerErrorMessage(error);
|
|
6745
|
+
if (this.isDockerUnavailableError(message2)) {
|
|
6746
|
+
throw new DockerNotAvailableError();
|
|
6747
|
+
}
|
|
6748
|
+
throw new ContainerCreationError(message2, image, error);
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
async inspectContainer(containerId) {
|
|
6752
|
+
try {
|
|
6753
|
+
const result = await spawn3("docker", [
|
|
6754
|
+
"container",
|
|
6755
|
+
"inspect",
|
|
6756
|
+
"--format",
|
|
6757
|
+
"{{.State.Status}}",
|
|
6758
|
+
containerId
|
|
6759
|
+
]);
|
|
6760
|
+
const status = result.stdout.trim();
|
|
6761
|
+
return status === "running" ? "running" : "stopped";
|
|
6762
|
+
} catch (error) {
|
|
6763
|
+
const message2 = this.getDockerErrorMessage(error);
|
|
6764
|
+
if (this.isDockerUnavailableError(message2)) {
|
|
6765
|
+
throw new DockerNotAvailableError();
|
|
6766
|
+
}
|
|
6767
|
+
if (this.isMissingContainerError(message2)) {
|
|
6768
|
+
return "absent";
|
|
6769
|
+
}
|
|
6770
|
+
throw new DockerSandboxError(
|
|
6771
|
+
`Failed to inspect container "${containerId}": ${message2}`
|
|
6772
|
+
);
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
isMissingContainerError(message2) {
|
|
6776
|
+
return message2.toLowerCase().includes("no such container");
|
|
6777
|
+
}
|
|
6778
|
+
isNameConflictError(message2) {
|
|
6779
|
+
return message2.toLowerCase().includes("is already in use by container");
|
|
6780
|
+
}
|
|
6781
|
+
async prepareVolumes() {
|
|
6782
|
+
this.validateVolumes();
|
|
6783
|
+
for (const volume of this.volumes) {
|
|
6784
|
+
if (volume.type !== "volume") {
|
|
6785
|
+
continue;
|
|
6786
|
+
}
|
|
6787
|
+
const lifecycle = volume.lifecycle ?? "external";
|
|
6788
|
+
if (lifecycle === "external") {
|
|
6789
|
+
await this.inspectVolume(volume.name);
|
|
6790
|
+
continue;
|
|
6791
|
+
}
|
|
6792
|
+
const exists = await this.volumeExists(volume.name);
|
|
6793
|
+
if (exists) {
|
|
6794
|
+
throw new VolumeCreateError(
|
|
6795
|
+
volume.name,
|
|
6796
|
+
"managed volume already exists"
|
|
6797
|
+
);
|
|
6798
|
+
}
|
|
6799
|
+
await this.createVolume(volume);
|
|
6800
|
+
if (volume.removeOnDispose !== false) {
|
|
6801
|
+
this.createdVolumes.add(volume.name);
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
validateVolumes() {
|
|
6806
|
+
const containerPaths = /* @__PURE__ */ new Set();
|
|
6807
|
+
for (const volume of this.volumes) {
|
|
6808
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
6809
|
+
if (!volume.containerPath.startsWith("/")) {
|
|
6810
|
+
throw new VolumePathError(
|
|
6811
|
+
source,
|
|
6812
|
+
volume.containerPath,
|
|
6813
|
+
"containerPath must be absolute"
|
|
6814
|
+
);
|
|
6815
|
+
}
|
|
6816
|
+
this.validateMountValue("containerPath", volume.containerPath, volume);
|
|
6817
|
+
if (containerPaths.has(volume.containerPath)) {
|
|
6818
|
+
throw new VolumePathError(
|
|
6819
|
+
source,
|
|
6820
|
+
volume.containerPath,
|
|
6821
|
+
"containerPath must be unique"
|
|
6822
|
+
);
|
|
6823
|
+
}
|
|
6824
|
+
containerPaths.add(volume.containerPath);
|
|
6825
|
+
if (volume.type === "bind") {
|
|
6826
|
+
this.validateMountValue("hostPath", volume.hostPath, volume);
|
|
6827
|
+
if (!existsSync3(volume.hostPath)) {
|
|
6828
|
+
throw new VolumePathError(
|
|
6829
|
+
volume.hostPath,
|
|
6830
|
+
volume.containerPath,
|
|
6831
|
+
"hostPath does not exist on host"
|
|
6832
|
+
);
|
|
6833
|
+
}
|
|
6834
|
+
continue;
|
|
6835
|
+
}
|
|
6836
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(volume.name)) {
|
|
6837
|
+
throw new VolumePathError(
|
|
6838
|
+
volume.name,
|
|
6839
|
+
volume.containerPath,
|
|
6840
|
+
"volume name must start with an alphanumeric character and contain only letters, numbers, underscore, period, or hyphen"
|
|
6841
|
+
);
|
|
6842
|
+
}
|
|
6843
|
+
if (volume.subPath) {
|
|
6844
|
+
this.validateMountValue("subPath", volume.subPath, volume);
|
|
6845
|
+
}
|
|
6846
|
+
if ((volume.lifecycle ?? "external") === "external") {
|
|
6847
|
+
if (volume.driver || volume.driverOptions) {
|
|
6848
|
+
throw new VolumePathError(
|
|
6849
|
+
volume.name,
|
|
6850
|
+
volume.containerPath,
|
|
6851
|
+
'driver and driverOptions require lifecycle "managed"'
|
|
6852
|
+
);
|
|
6853
|
+
}
|
|
6681
6854
|
}
|
|
6682
6855
|
}
|
|
6683
6856
|
}
|
|
6857
|
+
validateMountValue(field, value, volume) {
|
|
6858
|
+
if (!value.includes(",")) {
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
const source = volume.type === "bind" ? volume.hostPath : volume.name;
|
|
6862
|
+
throw new VolumePathError(
|
|
6863
|
+
source,
|
|
6864
|
+
volume.containerPath,
|
|
6865
|
+
`${field} must not contain commas`
|
|
6866
|
+
);
|
|
6867
|
+
}
|
|
6684
6868
|
buildDockerArgs(image, containerId) {
|
|
6685
6869
|
const { memory = "1g", cpus = 2 } = this.resources;
|
|
6686
6870
|
const args = [
|
|
@@ -6697,15 +6881,106 @@ var DockerSandboxStrategy = class {
|
|
|
6697
6881
|
for (const [key, value] of Object.entries(this.env)) {
|
|
6698
6882
|
args.push("-e", `${key}=${value}`);
|
|
6699
6883
|
}
|
|
6700
|
-
for (const
|
|
6701
|
-
|
|
6702
|
-
args.push("-v", `${mount.hostPath}:${mount.containerPath}:${mode}`);
|
|
6884
|
+
for (const volume of this.volumes) {
|
|
6885
|
+
args.push("--mount", this.buildVolumeMountArg(volume));
|
|
6703
6886
|
}
|
|
6704
6887
|
args.push(image, "tail", "-f", "/dev/null");
|
|
6705
6888
|
return args;
|
|
6706
6889
|
}
|
|
6707
|
-
|
|
6708
|
-
const
|
|
6890
|
+
buildVolumeMountArg(volume) {
|
|
6891
|
+
const readOnly = volume.readOnly !== false;
|
|
6892
|
+
const parts = volume.type === "bind" ? ["type=bind", `src=${volume.hostPath}`, `dst=${volume.containerPath}`] : [
|
|
6893
|
+
"type=volume",
|
|
6894
|
+
`src=${volume.name}`,
|
|
6895
|
+
`dst=${volume.containerPath}`,
|
|
6896
|
+
...volume.subPath ? [`volume-subpath=${volume.subPath}`] : [],
|
|
6897
|
+
...volume.noCopy ? ["volume-nocopy"] : []
|
|
6898
|
+
];
|
|
6899
|
+
if (readOnly) {
|
|
6900
|
+
parts.push("readonly");
|
|
6901
|
+
}
|
|
6902
|
+
return parts.join(",");
|
|
6903
|
+
}
|
|
6904
|
+
async inspectVolume(name) {
|
|
6905
|
+
try {
|
|
6906
|
+
await spawn3("docker", ["volume", "inspect", name]);
|
|
6907
|
+
} catch (error) {
|
|
6908
|
+
const reason = this.getDockerErrorMessage(error);
|
|
6909
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
6910
|
+
throw new DockerNotAvailableError();
|
|
6911
|
+
}
|
|
6912
|
+
throw new VolumeInspectError(name, reason);
|
|
6913
|
+
}
|
|
6914
|
+
}
|
|
6915
|
+
async volumeExists(name) {
|
|
6916
|
+
try {
|
|
6917
|
+
await this.inspectVolume(name);
|
|
6918
|
+
return true;
|
|
6919
|
+
} catch (error) {
|
|
6920
|
+
if (error instanceof VolumeInspectError) {
|
|
6921
|
+
if (this.isMissingVolumeInspectError(error.reason)) {
|
|
6922
|
+
return false;
|
|
6923
|
+
}
|
|
6924
|
+
throw error;
|
|
6925
|
+
}
|
|
6926
|
+
throw error;
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
async createVolume(volume) {
|
|
6930
|
+
const args = ["volume", "create"];
|
|
6931
|
+
if (volume.driver) {
|
|
6932
|
+
args.push("--driver", volume.driver);
|
|
6933
|
+
}
|
|
6934
|
+
for (const [key, value] of Object.entries(volume.driverOptions ?? {})) {
|
|
6935
|
+
args.push("--opt", `${key}=${value}`);
|
|
6936
|
+
}
|
|
6937
|
+
args.push(volume.name);
|
|
6938
|
+
try {
|
|
6939
|
+
await spawn3("docker", args);
|
|
6940
|
+
} catch (error) {
|
|
6941
|
+
const reason = this.getDockerErrorMessage(error);
|
|
6942
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
6943
|
+
throw new DockerNotAvailableError();
|
|
6944
|
+
}
|
|
6945
|
+
throw new VolumeCreateError(volume.name, reason);
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
async cleanupCreatedVolumes() {
|
|
6949
|
+
const volumes = [...this.createdVolumes].reverse();
|
|
6950
|
+
for (const volume of volumes) {
|
|
6951
|
+
try {
|
|
6952
|
+
await spawn3("docker", ["volume", "rm", volume]);
|
|
6953
|
+
this.createdVolumes.delete(volume);
|
|
6954
|
+
} catch (error) {
|
|
6955
|
+
const reason = this.getDockerErrorMessage(error);
|
|
6956
|
+
if (this.isDockerUnavailableError(reason)) {
|
|
6957
|
+
throw new DockerNotAvailableError();
|
|
6958
|
+
}
|
|
6959
|
+
throw new VolumeRemoveError(volume, reason);
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6963
|
+
async cleanupCreatedVolumesAfterFailure(originalError) {
|
|
6964
|
+
try {
|
|
6965
|
+
await this.cleanupCreatedVolumes();
|
|
6966
|
+
} catch (cleanupError) {
|
|
6967
|
+
if (originalError instanceof Error) {
|
|
6968
|
+
const original = originalError;
|
|
6969
|
+
original.suppressed = [...original.suppressed ?? [], cleanupError];
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
getDockerErrorMessage(error) {
|
|
6974
|
+
const err = error;
|
|
6975
|
+
return err.stderr || err.stdout || err.message || String(error);
|
|
6976
|
+
}
|
|
6977
|
+
isDockerUnavailableError(message2) {
|
|
6978
|
+
return message2.includes("Cannot connect") || message2.includes("docker daemon");
|
|
6979
|
+
}
|
|
6980
|
+
isMissingVolumeInspectError(message2) {
|
|
6981
|
+
return message2.toLowerCase().includes("no such volume");
|
|
6982
|
+
}
|
|
6983
|
+
async startContainer(image, containerId) {
|
|
6709
6984
|
const args = this.buildDockerArgs(image, containerId);
|
|
6710
6985
|
try {
|
|
6711
6986
|
await spawn3("docker", args);
|
|
@@ -6714,9 +6989,12 @@ var DockerSandboxStrategy = class {
|
|
|
6714
6989
|
if (err.message?.includes("Cannot connect") || err.message?.includes("docker daemon") || err.stderr?.includes("Cannot connect")) {
|
|
6715
6990
|
throw new DockerNotAvailableError();
|
|
6716
6991
|
}
|
|
6717
|
-
throw new ContainerCreationError(
|
|
6992
|
+
throw new ContainerCreationError(
|
|
6993
|
+
this.getDockerErrorMessage(err),
|
|
6994
|
+
image,
|
|
6995
|
+
err
|
|
6996
|
+
);
|
|
6718
6997
|
}
|
|
6719
|
-
return containerId;
|
|
6720
6998
|
}
|
|
6721
6999
|
async stopContainer(containerId) {
|
|
6722
7000
|
try {
|
|
@@ -6724,15 +7002,13 @@ var DockerSandboxStrategy = class {
|
|
|
6724
7002
|
} catch {
|
|
6725
7003
|
}
|
|
6726
7004
|
}
|
|
6727
|
-
async exec(command) {
|
|
7005
|
+
async exec(command, options) {
|
|
6728
7006
|
try {
|
|
6729
|
-
const result = await spawn3(
|
|
6730
|
-
"
|
|
6731
|
-
this.context.containerId,
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
command
|
|
6735
|
-
]);
|
|
7007
|
+
const result = await spawn3(
|
|
7008
|
+
"docker",
|
|
7009
|
+
["exec", this.context.containerId, "sh", "-c", command],
|
|
7010
|
+
{ signal: options?.signal }
|
|
7011
|
+
);
|
|
6736
7012
|
return {
|
|
6737
7013
|
stdout: result.stdout,
|
|
6738
7014
|
stderr: result.stderr,
|
|
@@ -6747,12 +7023,22 @@ var DockerSandboxStrategy = class {
|
|
|
6747
7023
|
};
|
|
6748
7024
|
}
|
|
6749
7025
|
}
|
|
7026
|
+
spawnProcess(command, options) {
|
|
7027
|
+
const child = childSpawn("docker", [
|
|
7028
|
+
"exec",
|
|
7029
|
+
...buildDockerExecFlags(options),
|
|
7030
|
+
this.context.containerId,
|
|
7031
|
+
"sh",
|
|
7032
|
+
"-c",
|
|
7033
|
+
command
|
|
7034
|
+
]);
|
|
7035
|
+
return toSandboxProcess(child, options?.signal);
|
|
7036
|
+
}
|
|
6750
7037
|
createSandboxMethods() {
|
|
6751
7038
|
const { containerId } = this.context;
|
|
6752
7039
|
const sandbox = {
|
|
6753
|
-
executeCommand: async (command) =>
|
|
6754
|
-
|
|
6755
|
-
},
|
|
7040
|
+
executeCommand: async (command, options) => this.exec(command, options),
|
|
7041
|
+
spawn: (command, options) => this.spawnProcess(command, options),
|
|
6756
7042
|
readFile: async (path5) => {
|
|
6757
7043
|
const result = await sandbox.executeCommand(`base64 "${path5}"`);
|
|
6758
7044
|
if (result.exitCode !== 0) {
|
|
@@ -6779,16 +7065,74 @@ var DockerSandboxStrategy = class {
|
|
|
6779
7065
|
},
|
|
6780
7066
|
dispose: async () => {
|
|
6781
7067
|
await this.stopContainer(containerId);
|
|
7068
|
+
await this.cleanupCreatedVolumes();
|
|
6782
7069
|
}
|
|
6783
7070
|
};
|
|
6784
7071
|
return sandbox;
|
|
6785
7072
|
}
|
|
6786
7073
|
};
|
|
7074
|
+
function validateEnvKey(key) {
|
|
7075
|
+
if (key.length === 0 || key.includes("=")) {
|
|
7076
|
+
throw new DockerSandboxError(`Invalid environment variable key: "${key}"`);
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
function buildDockerExecFlags(options) {
|
|
7080
|
+
const flags = [];
|
|
7081
|
+
if (options?.cwd) {
|
|
7082
|
+
flags.push("-w", options.cwd);
|
|
7083
|
+
}
|
|
7084
|
+
if (options?.env) {
|
|
7085
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
7086
|
+
validateEnvKey(key);
|
|
7087
|
+
flags.push("-e", `${key}=${value}`);
|
|
7088
|
+
}
|
|
7089
|
+
}
|
|
7090
|
+
return flags;
|
|
7091
|
+
}
|
|
7092
|
+
function toSandboxProcess(child, abortSignal) {
|
|
7093
|
+
if (!child.stdout || !child.stderr) {
|
|
7094
|
+
child.kill("SIGKILL");
|
|
7095
|
+
throw new DockerSandboxError("docker exec child missing stdio streams");
|
|
7096
|
+
}
|
|
7097
|
+
const onAbort = abortSignal ? () => child.kill("SIGKILL") : void 0;
|
|
7098
|
+
if (abortSignal && onAbort) {
|
|
7099
|
+
if (abortSignal.aborted) onAbort();
|
|
7100
|
+
else abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
7101
|
+
}
|
|
7102
|
+
return {
|
|
7103
|
+
stdout: Readable.toWeb(child.stdout),
|
|
7104
|
+
stderr: Readable.toWeb(child.stderr),
|
|
7105
|
+
exit: new Promise((resolve4, reject) => {
|
|
7106
|
+
const settle = () => {
|
|
7107
|
+
child.removeListener("exit", onExitEvent);
|
|
7108
|
+
child.removeListener("error", onError);
|
|
7109
|
+
if (abortSignal && onAbort) {
|
|
7110
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
7111
|
+
}
|
|
7112
|
+
};
|
|
7113
|
+
const onError = (err) => {
|
|
7114
|
+
settle();
|
|
7115
|
+
reject(err);
|
|
7116
|
+
};
|
|
7117
|
+
const onExitEvent = (code, exitSignal) => {
|
|
7118
|
+
settle();
|
|
7119
|
+
resolve4({ code, signal: exitSignal, success: code === 0 });
|
|
7120
|
+
};
|
|
7121
|
+
child.on("exit", onExitEvent);
|
|
7122
|
+
child.on("error", onError);
|
|
7123
|
+
})
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
6787
7126
|
var RuntimeStrategy = class extends DockerSandboxStrategy {
|
|
6788
7127
|
image;
|
|
6789
7128
|
installers;
|
|
6790
7129
|
constructor(args = {}) {
|
|
6791
|
-
super({
|
|
7130
|
+
super({
|
|
7131
|
+
volumes: args.volumes,
|
|
7132
|
+
resources: args.resources,
|
|
7133
|
+
env: args.env,
|
|
7134
|
+
name: args.name
|
|
7135
|
+
});
|
|
6792
7136
|
this.image = args.image ?? "alpine:latest";
|
|
6793
7137
|
this.installers = args.installers ?? [];
|
|
6794
7138
|
}
|
|
@@ -6807,7 +7151,12 @@ var DockerfileStrategy = class extends DockerSandboxStrategy {
|
|
|
6807
7151
|
dockerfile;
|
|
6808
7152
|
dockerContext;
|
|
6809
7153
|
constructor(args) {
|
|
6810
|
-
super({
|
|
7154
|
+
super({
|
|
7155
|
+
volumes: args.volumes,
|
|
7156
|
+
resources: args.resources,
|
|
7157
|
+
env: args.env,
|
|
7158
|
+
name: args.name
|
|
7159
|
+
});
|
|
6811
7160
|
this.dockerfile = args.dockerfile;
|
|
6812
7161
|
this.dockerContext = args.context ?? ".";
|
|
6813
7162
|
this.imageTag = this.computeImageTag();
|
|
@@ -6876,7 +7225,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6876
7225
|
async getImage() {
|
|
6877
7226
|
return "";
|
|
6878
7227
|
}
|
|
6879
|
-
async startContainer(_image) {
|
|
7228
|
+
async startContainer(_image, _containerId) {
|
|
6880
7229
|
try {
|
|
6881
7230
|
await spawn3("docker", [
|
|
6882
7231
|
"compose",
|
|
@@ -6894,25 +7243,31 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6894
7243
|
}
|
|
6895
7244
|
throw new ComposeStartError(this.composeFile, err.stderr || err.message);
|
|
6896
7245
|
}
|
|
7246
|
+
}
|
|
7247
|
+
defaultContainerId() {
|
|
6897
7248
|
return this.projectName;
|
|
6898
7249
|
}
|
|
6899
7250
|
async configure() {
|
|
6900
7251
|
}
|
|
6901
|
-
async exec(command) {
|
|
7252
|
+
async exec(command, options) {
|
|
6902
7253
|
try {
|
|
6903
|
-
const result = await spawn3(
|
|
6904
|
-
"
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
7254
|
+
const result = await spawn3(
|
|
7255
|
+
"docker",
|
|
7256
|
+
[
|
|
7257
|
+
"compose",
|
|
7258
|
+
"-f",
|
|
7259
|
+
this.composeFile,
|
|
7260
|
+
"-p",
|
|
7261
|
+
this.projectName,
|
|
7262
|
+
"exec",
|
|
7263
|
+
"-T",
|
|
7264
|
+
this.service,
|
|
7265
|
+
"sh",
|
|
7266
|
+
"-c",
|
|
7267
|
+
command
|
|
7268
|
+
],
|
|
7269
|
+
{ signal: options?.signal }
|
|
7270
|
+
);
|
|
6916
7271
|
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
6917
7272
|
} catch (error) {
|
|
6918
7273
|
const err = error;
|
|
@@ -6923,6 +7278,23 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
|
|
|
6923
7278
|
};
|
|
6924
7279
|
}
|
|
6925
7280
|
}
|
|
7281
|
+
spawnProcess(command, options) {
|
|
7282
|
+
const child = childSpawn("docker", [
|
|
7283
|
+
"compose",
|
|
7284
|
+
"-f",
|
|
7285
|
+
this.composeFile,
|
|
7286
|
+
"-p",
|
|
7287
|
+
this.projectName,
|
|
7288
|
+
"exec",
|
|
7289
|
+
"-T",
|
|
7290
|
+
...buildDockerExecFlags(options),
|
|
7291
|
+
this.service,
|
|
7292
|
+
"sh",
|
|
7293
|
+
"-c",
|
|
7294
|
+
command
|
|
7295
|
+
]);
|
|
7296
|
+
return toSandboxProcess(child, options?.signal);
|
|
7297
|
+
}
|
|
6926
7298
|
async stopContainer(_containerId) {
|
|
6927
7299
|
try {
|
|
6928
7300
|
await spawn3("docker", [
|
|
@@ -6949,17 +7321,19 @@ async function createDockerSandbox(options = {}) {
|
|
|
6949
7321
|
strategy = new DockerfileStrategy({
|
|
6950
7322
|
dockerfile: options.dockerfile,
|
|
6951
7323
|
context: options.context,
|
|
6952
|
-
|
|
7324
|
+
volumes: options.volumes,
|
|
6953
7325
|
resources: options.resources,
|
|
6954
|
-
env: options.env
|
|
7326
|
+
env: options.env,
|
|
7327
|
+
name: options.name
|
|
6955
7328
|
});
|
|
6956
7329
|
} else {
|
|
6957
7330
|
strategy = new RuntimeStrategy({
|
|
6958
7331
|
image: options.image,
|
|
6959
7332
|
installers: options.installers,
|
|
6960
|
-
|
|
7333
|
+
volumes: options.volumes,
|
|
6961
7334
|
resources: options.resources,
|
|
6962
|
-
env: options.env
|
|
7335
|
+
env: options.env,
|
|
7336
|
+
name: options.name
|
|
6963
7337
|
});
|
|
6964
7338
|
}
|
|
6965
7339
|
return strategy.create();
|
|
@@ -6987,40 +7361,43 @@ async function createContainerTool(options = {}) {
|
|
|
6987
7361
|
const {
|
|
6988
7362
|
dockerfile,
|
|
6989
7363
|
context,
|
|
6990
|
-
|
|
7364
|
+
volumes,
|
|
6991
7365
|
resources,
|
|
6992
7366
|
env,
|
|
7367
|
+
name,
|
|
6993
7368
|
skills: skills2 = [],
|
|
6994
7369
|
...rest
|
|
6995
7370
|
} = options;
|
|
6996
|
-
sandboxOptions = { dockerfile, context,
|
|
7371
|
+
sandboxOptions = { dockerfile, context, volumes, resources, env, name };
|
|
6997
7372
|
bashOptions = rest;
|
|
6998
7373
|
skillInputs = skills2;
|
|
6999
7374
|
} else {
|
|
7000
7375
|
const {
|
|
7001
7376
|
image,
|
|
7002
7377
|
installers,
|
|
7003
|
-
|
|
7378
|
+
volumes,
|
|
7004
7379
|
resources,
|
|
7005
7380
|
env,
|
|
7381
|
+
name,
|
|
7006
7382
|
skills: skills2 = [],
|
|
7007
7383
|
...rest
|
|
7008
7384
|
} = options;
|
|
7009
|
-
sandboxOptions = { image, installers,
|
|
7385
|
+
sandboxOptions = { image, installers, volumes, resources, env, name };
|
|
7010
7386
|
bashOptions = rest;
|
|
7011
7387
|
skillInputs = skills2;
|
|
7012
7388
|
}
|
|
7013
|
-
const
|
|
7389
|
+
const backend = await createDockerSandbox(sandboxOptions);
|
|
7014
7390
|
const toolkit = await createBashTool({
|
|
7015
7391
|
...bashOptions,
|
|
7016
|
-
sandbox,
|
|
7392
|
+
sandbox: backend,
|
|
7017
7393
|
skills: skillInputs
|
|
7018
7394
|
});
|
|
7019
7395
|
return {
|
|
7020
7396
|
bash: toolkit.bash,
|
|
7021
7397
|
tools: toolkit.tools,
|
|
7022
|
-
sandbox,
|
|
7023
|
-
skills: toolkit.skills
|
|
7398
|
+
sandbox: toolkit.sandbox,
|
|
7399
|
+
skills: toolkit.skills,
|
|
7400
|
+
drainFileEvents: toolkit.drainFileEvents
|
|
7024
7401
|
};
|
|
7025
7402
|
}
|
|
7026
7403
|
|
|
@@ -7096,9 +7473,9 @@ async function downloadAndInstall(ctx, name, url, binaryPath, source = "url") {
|
|
|
7096
7473
|
function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
|
|
7097
7474
|
return `
|
|
7098
7475
|
set -e
|
|
7099
|
-
NAME=${
|
|
7100
|
-
URL=${
|
|
7101
|
-
BIN_IN_ARCHIVE=${
|
|
7476
|
+
NAME=${shellQuote2(name)}
|
|
7477
|
+
URL=${shellQuote2(url)}
|
|
7478
|
+
BIN_IN_ARCHIVE=${shellQuote2(binaryPathInArchive)}
|
|
7102
7479
|
TMPDIR=$(mktemp -d)
|
|
7103
7480
|
cd "$TMPDIR"
|
|
7104
7481
|
curl -fsSL "$URL" -o archive.tar.gz
|
|
@@ -7117,8 +7494,8 @@ function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
|
|
|
7117
7494
|
}
|
|
7118
7495
|
function buildRawInstallCmd(name, url) {
|
|
7119
7496
|
return `
|
|
7120
|
-
NAME=${
|
|
7121
|
-
URL=${
|
|
7497
|
+
NAME=${shellQuote2(name)}
|
|
7498
|
+
URL=${shellQuote2(url)}
|
|
7122
7499
|
curl -fsSL "$URL" -o "/usr/local/bin/$NAME"
|
|
7123
7500
|
chmod +x "/usr/local/bin/$NAME"
|
|
7124
7501
|
`;
|
|
@@ -7330,8 +7707,11 @@ async function createVirtualSandbox(options) {
|
|
|
7330
7707
|
customCommands: options.customCommands
|
|
7331
7708
|
});
|
|
7332
7709
|
return {
|
|
7333
|
-
async executeCommand(command) {
|
|
7334
|
-
const result = await bash.exec(
|
|
7710
|
+
async executeCommand(command, options2) {
|
|
7711
|
+
const result = await bash.exec(
|
|
7712
|
+
command,
|
|
7713
|
+
options2?.signal ? { signal: options2.signal } : void 0
|
|
7714
|
+
);
|
|
7335
7715
|
return {
|
|
7336
7716
|
stdout: result.stdout,
|
|
7337
7717
|
stderr: result.stderr,
|
|
@@ -7348,6 +7728,8 @@ async function createVirtualSandbox(options) {
|
|
|
7348
7728
|
typeof file.content === "string" ? file.content : Buffer.from(file.content).toString("utf-8")
|
|
7349
7729
|
);
|
|
7350
7730
|
}
|
|
7731
|
+
},
|
|
7732
|
+
async dispose() {
|
|
7351
7733
|
}
|
|
7352
7734
|
};
|
|
7353
7735
|
}
|
|
@@ -10524,9 +10906,7 @@ export {
|
|
|
10524
10906
|
MarkdownRenderer,
|
|
10525
10907
|
MissingRuntimeError,
|
|
10526
10908
|
ModelsRegistry,
|
|
10527
|
-
MountPathError,
|
|
10528
10909
|
NpmInstaller,
|
|
10529
|
-
ObservedFs,
|
|
10530
10910
|
PackageInstallError,
|
|
10531
10911
|
PackageInstaller,
|
|
10532
10912
|
PipInstaller,
|
|
@@ -10536,6 +10916,7 @@ export {
|
|
|
10536
10916
|
PostgresStreamStore,
|
|
10537
10917
|
PromiseResolver,
|
|
10538
10918
|
RuntimeStrategy,
|
|
10919
|
+
SnapshotFailedError,
|
|
10539
10920
|
SqlServerContextStore,
|
|
10540
10921
|
SqliteContextStore,
|
|
10541
10922
|
SqliteStreamStore,
|
|
@@ -10545,6 +10926,10 @@ export {
|
|
|
10545
10926
|
TomlRenderer,
|
|
10546
10927
|
ToonRenderer,
|
|
10547
10928
|
UrlBinaryInstaller,
|
|
10929
|
+
VolumeCreateError,
|
|
10930
|
+
VolumeInspectError,
|
|
10931
|
+
VolumePathError,
|
|
10932
|
+
VolumeRemoveError,
|
|
10548
10933
|
XmlRenderer,
|
|
10549
10934
|
addUsage,
|
|
10550
10935
|
advisorPreamble,
|
|
@@ -10639,6 +11024,7 @@ export {
|
|
|
10639
11024
|
not,
|
|
10640
11025
|
npm,
|
|
10641
11026
|
nullUsage,
|
|
11027
|
+
observeSandboxFileEvents,
|
|
10642
11028
|
once,
|
|
10643
11029
|
or,
|
|
10644
11030
|
parseFrontmatter,
|
|
@@ -10663,11 +11049,12 @@ export {
|
|
|
10663
11049
|
resolveTz,
|
|
10664
11050
|
role,
|
|
10665
11051
|
runGuardrailChain,
|
|
11052
|
+
runWithAbortSignal,
|
|
10666
11053
|
runWithBashMeta,
|
|
10667
11054
|
seasonChanged,
|
|
10668
11055
|
seasonReminder,
|
|
10669
11056
|
selfCritique,
|
|
10670
|
-
shellQuote,
|
|
11057
|
+
shellQuote2 as shellQuote,
|
|
10671
11058
|
skills,
|
|
10672
11059
|
skillsReminder,
|
|
10673
11060
|
socraticPrompting,
|
|
@@ -10696,6 +11083,7 @@ export {
|
|
|
10696
11083
|
user,
|
|
10697
11084
|
visualizeGraph,
|
|
10698
11085
|
weekChanged,
|
|
11086
|
+
withAbortSignal,
|
|
10699
11087
|
withinLastN,
|
|
10700
11088
|
workflow,
|
|
10701
11089
|
yearChanged,
|