@openai/agents-extensions 0.8.5 → 0.9.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/metadata.js +2 -2
- package/dist/metadata.mjs +2 -2
- package/dist/sandbox/blaxel/index.d.ts +2 -0
- package/dist/sandbox/blaxel/index.js +19 -0
- package/dist/sandbox/blaxel/index.js.map +1 -0
- package/dist/sandbox/blaxel/index.mjs +3 -0
- package/dist/sandbox/blaxel/index.mjs.map +1 -0
- package/dist/sandbox/blaxel/mounts.d.ts +101 -0
- package/dist/sandbox/blaxel/mounts.js +542 -0
- package/dist/sandbox/blaxel/mounts.js.map +1 -0
- package/dist/sandbox/blaxel/mounts.mjs +529 -0
- package/dist/sandbox/blaxel/mounts.mjs.map +1 -0
- package/dist/sandbox/blaxel/sandbox.d.ts +155 -0
- package/dist/sandbox/blaxel/sandbox.js +1110 -0
- package/dist/sandbox/blaxel/sandbox.js.map +1 -0
- package/dist/sandbox/blaxel/sandbox.mjs +1072 -0
- package/dist/sandbox/blaxel/sandbox.mjs.map +1 -0
- package/dist/sandbox/cloudflare/index.d.ts +2 -0
- package/dist/sandbox/cloudflare/index.js +19 -0
- package/dist/sandbox/cloudflare/index.js.map +1 -0
- package/dist/sandbox/cloudflare/index.mjs +3 -0
- package/dist/sandbox/cloudflare/index.mjs.map +1 -0
- package/dist/sandbox/cloudflare/mounts.d.ts +28 -0
- package/dist/sandbox/cloudflare/mounts.js +150 -0
- package/dist/sandbox/cloudflare/mounts.js.map +1 -0
- package/dist/sandbox/cloudflare/mounts.mjs +143 -0
- package/dist/sandbox/cloudflare/mounts.mjs.map +1 -0
- package/dist/sandbox/cloudflare/sandbox.d.ts +91 -0
- package/dist/sandbox/cloudflare/sandbox.js +1163 -0
- package/dist/sandbox/cloudflare/sandbox.js.map +1 -0
- package/dist/sandbox/cloudflare/sandbox.mjs +1125 -0
- package/dist/sandbox/cloudflare/sandbox.mjs.map +1 -0
- package/dist/sandbox/daytona/index.d.ts +2 -0
- package/dist/sandbox/daytona/index.js +19 -0
- package/dist/sandbox/daytona/index.js.map +1 -0
- package/dist/sandbox/daytona/index.mjs +3 -0
- package/dist/sandbox/daytona/index.mjs.map +1 -0
- package/dist/sandbox/daytona/mounts.d.ts +11 -0
- package/dist/sandbox/daytona/mounts.js +17 -0
- package/dist/sandbox/daytona/mounts.js.map +1 -0
- package/dist/sandbox/daytona/mounts.mjs +12 -0
- package/dist/sandbox/daytona/mounts.mjs.map +1 -0
- package/dist/sandbox/daytona/sandbox.d.ts +160 -0
- package/dist/sandbox/daytona/sandbox.js +938 -0
- package/dist/sandbox/daytona/sandbox.js.map +1 -0
- package/dist/sandbox/daytona/sandbox.mjs +900 -0
- package/dist/sandbox/daytona/sandbox.mjs.map +1 -0
- package/dist/sandbox/e2b/index.d.ts +2 -0
- package/dist/sandbox/e2b/index.js +19 -0
- package/dist/sandbox/e2b/index.js.map +1 -0
- package/dist/sandbox/e2b/index.mjs +3 -0
- package/dist/sandbox/e2b/index.mjs.map +1 -0
- package/dist/sandbox/e2b/mounts.d.ts +11 -0
- package/dist/sandbox/e2b/mounts.js +17 -0
- package/dist/sandbox/e2b/mounts.js.map +1 -0
- package/dist/sandbox/e2b/mounts.mjs +12 -0
- package/dist/sandbox/e2b/mounts.mjs.map +1 -0
- package/dist/sandbox/e2b/sandbox.d.ts +169 -0
- package/dist/sandbox/e2b/sandbox.js +829 -0
- package/dist/sandbox/e2b/sandbox.js.map +1 -0
- package/dist/sandbox/e2b/sandbox.mjs +791 -0
- package/dist/sandbox/e2b/sandbox.mjs.map +1 -0
- package/dist/sandbox/modal/index.d.ts +2 -0
- package/dist/sandbox/modal/index.js +19 -0
- package/dist/sandbox/modal/index.js.map +1 -0
- package/dist/sandbox/modal/index.mjs +3 -0
- package/dist/sandbox/modal/index.mjs.map +1 -0
- package/dist/sandbox/modal/mounts.d.ts +24 -0
- package/dist/sandbox/modal/mounts.js +194 -0
- package/dist/sandbox/modal/mounts.js.map +1 -0
- package/dist/sandbox/modal/mounts.mjs +188 -0
- package/dist/sandbox/modal/mounts.mjs.map +1 -0
- package/dist/sandbox/modal/sandbox.d.ts +220 -0
- package/dist/sandbox/modal/sandbox.js +1621 -0
- package/dist/sandbox/modal/sandbox.js.map +1 -0
- package/dist/sandbox/modal/sandbox.mjs +1581 -0
- package/dist/sandbox/modal/sandbox.mjs.map +1 -0
- package/dist/sandbox/runloop/index.d.ts +2 -0
- package/dist/sandbox/runloop/index.js +19 -0
- package/dist/sandbox/runloop/index.js.map +1 -0
- package/dist/sandbox/runloop/index.mjs +3 -0
- package/dist/sandbox/runloop/index.mjs.map +1 -0
- package/dist/sandbox/runloop/mounts.d.ts +11 -0
- package/dist/sandbox/runloop/mounts.js +17 -0
- package/dist/sandbox/runloop/mounts.js.map +1 -0
- package/dist/sandbox/runloop/mounts.mjs +12 -0
- package/dist/sandbox/runloop/mounts.mjs.map +1 -0
- package/dist/sandbox/runloop/sandbox.d.ts +266 -0
- package/dist/sandbox/runloop/sandbox.js +1390 -0
- package/dist/sandbox/runloop/sandbox.js.map +1 -0
- package/dist/sandbox/runloop/sandbox.mjs +1346 -0
- package/dist/sandbox/runloop/sandbox.mjs.map +1 -0
- package/dist/sandbox/shared/archive.d.ts +34 -0
- package/dist/sandbox/shared/archive.js +397 -0
- package/dist/sandbox/shared/archive.js.map +1 -0
- package/dist/sandbox/shared/archive.mjs +389 -0
- package/dist/sandbox/shared/archive.mjs.map +1 -0
- package/dist/sandbox/shared/editor.d.ts +17 -0
- package/dist/sandbox/shared/editor.js +65 -0
- package/dist/sandbox/shared/editor.js.map +1 -0
- package/dist/sandbox/shared/editor.mjs +61 -0
- package/dist/sandbox/shared/editor.mjs.map +1 -0
- package/dist/sandbox/shared/environment.d.ts +1 -0
- package/dist/sandbox/shared/environment.js +13 -0
- package/dist/sandbox/shared/environment.js.map +1 -0
- package/dist/sandbox/shared/environment.mjs +2 -0
- package/dist/sandbox/shared/environment.mjs.map +1 -0
- package/dist/sandbox/shared/git.d.ts +4 -0
- package/dist/sandbox/shared/git.js +20 -0
- package/dist/sandbox/shared/git.js.map +1 -0
- package/dist/sandbox/shared/git.mjs +17 -0
- package/dist/sandbox/shared/git.mjs.map +1 -0
- package/dist/sandbox/shared/inContainerMounts.d.ts +40 -0
- package/dist/sandbox/shared/inContainerMounts.js +670 -0
- package/dist/sandbox/shared/inContainerMounts.js.map +1 -0
- package/dist/sandbox/shared/inContainerMounts.mjs +662 -0
- package/dist/sandbox/shared/inContainerMounts.mjs.map +1 -0
- package/dist/sandbox/shared/index.d.ts +23 -0
- package/dist/sandbox/shared/index.js +112 -0
- package/dist/sandbox/shared/index.js.map +1 -0
- package/dist/sandbox/shared/index.mjs +18 -0
- package/dist/sandbox/shared/index.mjs.map +1 -0
- package/dist/sandbox/shared/localSources.d.ts +8 -0
- package/dist/sandbox/shared/localSources.js +378 -0
- package/dist/sandbox/shared/localSources.js.map +1 -0
- package/dist/sandbox/shared/localSources.mjs +372 -0
- package/dist/sandbox/shared/localSources.mjs.map +1 -0
- package/dist/sandbox/shared/manifest.d.ts +38 -0
- package/dist/sandbox/shared/manifest.js +254 -0
- package/dist/sandbox/shared/manifest.js.map +1 -0
- package/dist/sandbox/shared/manifest.mjs +233 -0
- package/dist/sandbox/shared/manifest.mjs.map +1 -0
- package/dist/sandbox/shared/media.d.ts +1 -0
- package/dist/sandbox/shared/media.js +10 -0
- package/dist/sandbox/shared/media.js.map +1 -0
- package/dist/sandbox/shared/media.mjs +2 -0
- package/dist/sandbox/shared/media.mjs.map +1 -0
- package/dist/sandbox/shared/metadata.d.ts +7 -0
- package/dist/sandbox/shared/metadata.js +87 -0
- package/dist/sandbox/shared/metadata.js.map +1 -0
- package/dist/sandbox/shared/metadata.mjs +81 -0
- package/dist/sandbox/shared/metadata.mjs.map +1 -0
- package/dist/sandbox/shared/output.d.ts +1 -0
- package/dist/sandbox/shared/output.js +8 -0
- package/dist/sandbox/shared/output.js.map +1 -0
- package/dist/sandbox/shared/output.mjs +2 -0
- package/dist/sandbox/shared/output.mjs.map +1 -0
- package/dist/sandbox/shared/paths.d.ts +27 -0
- package/dist/sandbox/shared/paths.js +245 -0
- package/dist/sandbox/shared/paths.js.map +1 -0
- package/dist/sandbox/shared/paths.mjs +236 -0
- package/dist/sandbox/shared/paths.mjs.map +1 -0
- package/dist/sandbox/shared/ports.d.ts +14 -0
- package/dist/sandbox/shared/ports.js +75 -0
- package/dist/sandbox/shared/ports.js.map +1 -0
- package/dist/sandbox/shared/ports.mjs +69 -0
- package/dist/sandbox/shared/ports.mjs.map +1 -0
- package/dist/sandbox/shared/process.d.ts +16 -0
- package/dist/sandbox/shared/process.js +90 -0
- package/dist/sandbox/shared/process.js.map +1 -0
- package/dist/sandbox/shared/process.mjs +86 -0
- package/dist/sandbox/shared/process.mjs.map +1 -0
- package/dist/sandbox/shared/pty.d.ts +81 -0
- package/dist/sandbox/shared/pty.js +382 -0
- package/dist/sandbox/shared/pty.js.map +1 -0
- package/dist/sandbox/shared/pty.mjs +335 -0
- package/dist/sandbox/shared/pty.mjs.map +1 -0
- package/dist/sandbox/shared/runAs.d.ts +46 -0
- package/dist/sandbox/shared/runAs.js +149 -0
- package/dist/sandbox/shared/runAs.js.map +1 -0
- package/dist/sandbox/shared/runAs.mjs +141 -0
- package/dist/sandbox/shared/runAs.mjs.map +1 -0
- package/dist/sandbox/shared/session.d.ts +16 -0
- package/dist/sandbox/shared/session.js +138 -0
- package/dist/sandbox/shared/session.js.map +1 -0
- package/dist/sandbox/shared/session.mjs +128 -0
- package/dist/sandbox/shared/session.mjs.map +1 -0
- package/dist/sandbox/shared/sessionBase.d.ts +90 -0
- package/dist/sandbox/shared/sessionBase.js +446 -0
- package/dist/sandbox/shared/sessionBase.js.map +1 -0
- package/dist/sandbox/shared/sessionBase.mjs +442 -0
- package/dist/sandbox/shared/sessionBase.mjs.map +1 -0
- package/dist/sandbox/shared/sessionState.d.ts +7 -0
- package/dist/sandbox/shared/sessionState.js +35 -0
- package/dist/sandbox/shared/sessionState.js.map +1 -0
- package/dist/sandbox/shared/sessionState.mjs +31 -0
- package/dist/sandbox/shared/sessionState.mjs.map +1 -0
- package/dist/sandbox/shared/snapshots.d.ts +9 -0
- package/dist/sandbox/shared/snapshots.js +62 -0
- package/dist/sandbox/shared/snapshots.js.map +1 -0
- package/dist/sandbox/shared/snapshots.mjs +57 -0
- package/dist/sandbox/shared/snapshots.mjs.map +1 -0
- package/dist/sandbox/shared/typeGuards.d.ts +10 -0
- package/dist/sandbox/shared/typeGuards.js +50 -0
- package/dist/sandbox/shared/typeGuards.js.map +1 -0
- package/dist/sandbox/shared/typeGuards.mjs +38 -0
- package/dist/sandbox/shared/typeGuards.mjs.map +1 -0
- package/dist/sandbox/shared/types.d.ts +24 -0
- package/dist/sandbox/shared/types.js +3 -0
- package/dist/sandbox/shared/types.js.map +1 -0
- package/dist/sandbox/shared/types.mjs +2 -0
- package/dist/sandbox/shared/types.mjs.map +1 -0
- package/dist/sandbox/vercel/index.d.ts +1 -0
- package/dist/sandbox/vercel/index.js +18 -0
- package/dist/sandbox/vercel/index.js.map +1 -0
- package/dist/sandbox/vercel/index.mjs +2 -0
- package/dist/sandbox/vercel/index.mjs.map +1 -0
- package/dist/sandbox/vercel/sandbox.d.ts +135 -0
- package/dist/sandbox/vercel/sandbox.js +1001 -0
- package/dist/sandbox/vercel/sandbox.js.map +1 -0
- package/dist/sandbox/vercel/sandbox.mjs +963 -0
- package/dist/sandbox/vercel/sandbox.mjs.map +1 -0
- package/package.json +108 -4
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
import { UserError } from '@openai/agents-core';
|
|
2
|
+
import { SandboxProviderError, SandboxUnsupportedFeatureError, normalizeSandboxClientCreateArgs, } from '@openai/agents-core/sandbox';
|
|
3
|
+
import { appendPtyOutput, assertCoreSnapshotUnsupported, assertTarWorkspacePersistence, createPtyProcessEntry, imageOutputFromBytes, RemoteSandboxEditor, assertSandboxEntryMetadataSupported, assertSandboxManifestMetadataSupported, MOUNT_MANIFEST_METADATA_SUPPORT, closeRemoteSessionOnManifestError, cloneManifestWithRoot, createRunAsRemoteEditor, deserializeRemoteSandboxSessionStateValues, elapsedSeconds, formatExecResponse, formatPtyExecUpdate, hydrateRemoteWorkspaceTar, assertResumeRecreateAllowed, materializeEnvironment, manifestMaterializationOptionsWithRunAs, persistRemoteWorkspaceTar, assertConfiguredExposedPort, getCachedExposedPortEndpoint, parseExposedPortEndpoint, recordResolvedExposedPortEndpoint, resolveSandboxAbsolutePath, resolveSandboxWorkdir, posixDirname, shellQuote, sandboxUserShellCommand, shellCommandForPty, serializeRemoteSandboxSessionState, truncateOutput, validateRemoteSandboxPathForManifest, watchPtyProcess, readOptionalNumber, readOptionalNumberArray, readOptionalRecord, readOptionalString, readString, withProviderError, withSandboxSpan, writePtyStdin, PtyProcessRegistry, readRunAsRemoteFile, runAsRemotePathExists, } from "../shared/index.mjs";
|
|
4
|
+
import { mountRcloneCloudBucket, rclonePatternFromMountStrategy, unmountRcloneMount, } from "../shared/inContainerMounts.mjs";
|
|
5
|
+
import { isDaytonaCloudBucketMountEntry } from "./mounts.mjs";
|
|
6
|
+
import { applyLocalSourceManifestEntryToState, applyLocalSourceManifestToState, materializeLocalSourceManifest, } from "../shared/localSources.mjs";
|
|
7
|
+
const DEFAULT_WORKSPACE_ROOT = '/home/daytona/workspace';
|
|
8
|
+
const DEFAULT_EXPOSED_PORT_URL_TTL_S = 60;
|
|
9
|
+
const DAYTONA_DELETE_RETRY_DELAYS_MS = [1_000, 2_000, 4_000];
|
|
10
|
+
export class DaytonaSandboxSession {
|
|
11
|
+
state;
|
|
12
|
+
sandbox;
|
|
13
|
+
ptyProcesses = new PtyProcessRegistry();
|
|
14
|
+
remotePathResolver = async (path, options) => await this.resolveRemotePath(path, options);
|
|
15
|
+
activeMountPaths = new Set();
|
|
16
|
+
concurrencyLimits;
|
|
17
|
+
stopPromise;
|
|
18
|
+
deletePromise;
|
|
19
|
+
constructor(args) {
|
|
20
|
+
this.state = args.state;
|
|
21
|
+
this.sandbox = args.sandbox;
|
|
22
|
+
this.concurrencyLimits = args.concurrencyLimits;
|
|
23
|
+
}
|
|
24
|
+
createEditor(runAs) {
|
|
25
|
+
if (runAs) {
|
|
26
|
+
return createRunAsRemoteEditor({
|
|
27
|
+
providerName: 'DaytonaSandboxClient',
|
|
28
|
+
providerId: 'daytona',
|
|
29
|
+
runAs,
|
|
30
|
+
resolvePath: this.remotePathResolver,
|
|
31
|
+
runCommand: this.runAsCommandRunner.bind(this),
|
|
32
|
+
writer: this.writer(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return new RemoteSandboxEditor({
|
|
36
|
+
resolvePath: this.remotePathResolver,
|
|
37
|
+
pathExists: async (path) => await this.pathExists(path),
|
|
38
|
+
mkdir: async (path) => {
|
|
39
|
+
await this.mkdirEditorPath(path);
|
|
40
|
+
},
|
|
41
|
+
readText: async (path) => {
|
|
42
|
+
return await this.readEditorText(path);
|
|
43
|
+
},
|
|
44
|
+
writeText: async (path, content) => {
|
|
45
|
+
await this.writeEditorText(path, content);
|
|
46
|
+
},
|
|
47
|
+
deletePath: async (path) => {
|
|
48
|
+
await this.deleteEditorPath(path);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
supportsPty() {
|
|
53
|
+
return typeof this.sandbox.process.createPty === 'function';
|
|
54
|
+
}
|
|
55
|
+
async execCommand(args) {
|
|
56
|
+
if (args.tty) {
|
|
57
|
+
return await this.execPtyCommand(args);
|
|
58
|
+
}
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
const result = await this.sandbox.process.executeCommand(commandForDaytonaUser(args.cmd, args.runAs), resolveSandboxWorkdir(this.state.manifest.root, args.workdir), this.state.environment);
|
|
61
|
+
const combinedOutput = result.artifacts?.stdout ?? result.result ?? '';
|
|
62
|
+
const output = truncateOutput(combinedOutput, args.maxOutputTokens);
|
|
63
|
+
return formatExecResponse({
|
|
64
|
+
output: output.text,
|
|
65
|
+
wallTimeSeconds: elapsedSeconds(start),
|
|
66
|
+
exitCode: result.exitCode,
|
|
67
|
+
originalTokenCount: output.originalTokenCount,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async writeStdin(args) {
|
|
71
|
+
return await writePtyStdin({
|
|
72
|
+
providerName: 'DaytonaSandboxClient',
|
|
73
|
+
registry: this.ptyProcesses,
|
|
74
|
+
sessionId: args.sessionId,
|
|
75
|
+
chars: args.chars,
|
|
76
|
+
yieldTimeMs: args.yieldTimeMs,
|
|
77
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async execPtyCommand(args) {
|
|
81
|
+
if (!this.sandbox.process.createPty) {
|
|
82
|
+
throw new SandboxUnsupportedFeatureError('DaytonaSandboxClient tty=true requires Daytona SDK PTY support.', {
|
|
83
|
+
provider: 'daytona',
|
|
84
|
+
feature: 'tty',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const start = Date.now();
|
|
88
|
+
const command = shellCommandForPty(args);
|
|
89
|
+
const entry = createPtyProcessEntry({ tty: true });
|
|
90
|
+
const providerSessionId = `sandbox-${Math.random().toString(16).slice(2, 14)}`;
|
|
91
|
+
const handle = await this.sandbox.process.createPty({
|
|
92
|
+
id: providerSessionId,
|
|
93
|
+
cwd: resolveSandboxWorkdir(this.state.manifest.root, args.workdir),
|
|
94
|
+
envs: this.state.environment,
|
|
95
|
+
cols: 80,
|
|
96
|
+
rows: 24,
|
|
97
|
+
onData: (data) => appendPtyOutput(entry, data),
|
|
98
|
+
});
|
|
99
|
+
if (handle.waitForConnection) {
|
|
100
|
+
await handle.waitForConnection();
|
|
101
|
+
}
|
|
102
|
+
if (!handle.sendInput) {
|
|
103
|
+
await this.terminatePtyHandle(handle, providerSessionId);
|
|
104
|
+
throw new SandboxUnsupportedFeatureError('DaytonaSandboxClient tty=true requires Daytona SDK PTY stdin support.', {
|
|
105
|
+
provider: 'daytona',
|
|
106
|
+
feature: 'tty.stdin',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!handle.wait) {
|
|
110
|
+
await this.terminatePtyHandle(handle, providerSessionId);
|
|
111
|
+
throw new SandboxUnsupportedFeatureError('DaytonaSandboxClient tty=true requires Daytona SDK PTY wait support.', {
|
|
112
|
+
provider: 'daytona',
|
|
113
|
+
feature: 'tty.wait',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const waitForExit = handle.wait.bind(handle);
|
|
117
|
+
entry.sendInput = async (chars) => {
|
|
118
|
+
await handle.sendInput(chars);
|
|
119
|
+
};
|
|
120
|
+
entry.terminate = async () => {
|
|
121
|
+
if (handle.kill) {
|
|
122
|
+
await handle.kill();
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
await this.sandbox.process.killPtySession?.(handle.sessionId ?? providerSessionId);
|
|
126
|
+
}
|
|
127
|
+
await handle.disconnect?.();
|
|
128
|
+
};
|
|
129
|
+
const { sessionId, pruned } = this.ptyProcesses.register(entry);
|
|
130
|
+
if (pruned) {
|
|
131
|
+
await pruned.terminate?.().catch(() => { });
|
|
132
|
+
}
|
|
133
|
+
watchPtyProcess(entry, async () => await waitForExit(), (result, error) => exitCodeFromDaytonaResult(result) ??
|
|
134
|
+
exitCodeFromDaytonaResult(error) ??
|
|
135
|
+
exitCodeFromDaytonaResult(handle));
|
|
136
|
+
await entry.sendInput(`${commandForDaytonaUser(command, args.runAs)}\n`);
|
|
137
|
+
return await formatPtyExecUpdate({
|
|
138
|
+
registry: this.ptyProcesses,
|
|
139
|
+
sessionId,
|
|
140
|
+
entry,
|
|
141
|
+
startTime: start,
|
|
142
|
+
yieldTimeMs: args.yieldTimeMs,
|
|
143
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async terminatePtyHandle(handle, providerSessionId) {
|
|
147
|
+
if (handle.kill) {
|
|
148
|
+
try {
|
|
149
|
+
await handle.kill();
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Ignore best-effort PTY cleanup failures.
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
try {
|
|
157
|
+
await this.sandbox.process.killPtySession?.(handle.sessionId ?? providerSessionId);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Ignore best-effort PTY cleanup failures.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
await handle.disconnect?.();
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Ignore best-effort PTY cleanup failures.
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async viewImage(args) {
|
|
171
|
+
const bytes = args.runAs
|
|
172
|
+
? await readRunAsRemoteFile({
|
|
173
|
+
providerName: 'DaytonaSandboxClient',
|
|
174
|
+
providerId: 'daytona',
|
|
175
|
+
path: await this.resolveRemotePath(args.path),
|
|
176
|
+
runAs: args.runAs,
|
|
177
|
+
runCommand: this.runAsCommandRunner.bind(this),
|
|
178
|
+
})
|
|
179
|
+
: await this.readFileBytes(args.path);
|
|
180
|
+
return imageOutputFromBytes(args.path, bytes);
|
|
181
|
+
}
|
|
182
|
+
async readFile(args) {
|
|
183
|
+
const bytes = args.runAs
|
|
184
|
+
? await readRunAsRemoteFile({
|
|
185
|
+
providerName: 'DaytonaSandboxClient',
|
|
186
|
+
providerId: 'daytona',
|
|
187
|
+
path: await this.resolveRemotePath(args.path),
|
|
188
|
+
runAs: args.runAs,
|
|
189
|
+
runCommand: this.runAsCommandRunner.bind(this),
|
|
190
|
+
})
|
|
191
|
+
: await this.readFileBytes(args.path);
|
|
192
|
+
if (typeof args.maxBytes === 'number' && bytes.byteLength > args.maxBytes) {
|
|
193
|
+
return bytes.subarray(0, args.maxBytes);
|
|
194
|
+
}
|
|
195
|
+
return bytes;
|
|
196
|
+
}
|
|
197
|
+
async pathExists(path, runAs) {
|
|
198
|
+
const absolutePath = await this.resolveRemotePath(path);
|
|
199
|
+
if (!runAs) {
|
|
200
|
+
const result = await this.sandbox.process.executeCommand(`test -e ${shellQuote(absolutePath)}`, this.state.manifest.root, this.state.environment, 5);
|
|
201
|
+
return result.exitCode === 0;
|
|
202
|
+
}
|
|
203
|
+
return await runAsRemotePathExists(absolutePath, runAs, this.runAsCommandRunner.bind(this));
|
|
204
|
+
}
|
|
205
|
+
async running() {
|
|
206
|
+
try {
|
|
207
|
+
const result = await this.sandbox.process.executeCommand('true', this.state.manifest.root, this.state.environment, 5);
|
|
208
|
+
return result.exitCode === 0;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async resolveExposedPort(port) {
|
|
215
|
+
const requestedPort = assertConfiguredExposedPort({
|
|
216
|
+
providerName: 'DaytonaSandboxClient',
|
|
217
|
+
port,
|
|
218
|
+
configuredPorts: this.state.configuredExposedPorts,
|
|
219
|
+
});
|
|
220
|
+
const cached = getUsableDaytonaCachedExposedPortEndpoint(this.state, requestedPort);
|
|
221
|
+
if (cached) {
|
|
222
|
+
return cached;
|
|
223
|
+
}
|
|
224
|
+
const preview = await this.createSignedPreviewUrl(requestedPort);
|
|
225
|
+
const url = typeof preview === 'string' ? preview : preview.url;
|
|
226
|
+
if (typeof url !== 'string') {
|
|
227
|
+
throw new SandboxProviderError('DaytonaSandboxClient exposed port preview did not include a URL.', {
|
|
228
|
+
provider: 'daytona',
|
|
229
|
+
port: requestedPort,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return recordResolvedExposedPortEndpoint(this.state, requestedPort, withDaytonaPreviewExpiration(parseExposedPortEndpoint(url, {
|
|
233
|
+
providerName: 'DaytonaSandboxClient',
|
|
234
|
+
source: 'preview URL',
|
|
235
|
+
}), this.state.exposedPortUrlTtlS));
|
|
236
|
+
}
|
|
237
|
+
async materializeEntry(args) {
|
|
238
|
+
assertSandboxEntryMetadataSupported('DaytonaSandboxClient', args.path, args.entry, MOUNT_MANIFEST_METADATA_SUPPORT);
|
|
239
|
+
await applyLocalSourceManifestEntryToState(this.state, args.path, args.entry, 'daytona', this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(args.runAs));
|
|
240
|
+
}
|
|
241
|
+
async applyManifest(manifest, runAs) {
|
|
242
|
+
const resolvedManifest = resolveManifestRoot(manifest);
|
|
243
|
+
if (resolvedManifest.root !== this.state.manifest.root) {
|
|
244
|
+
throw new UserError('DaytonaSandboxClient cannot apply a manifest with a different root than the active session. Create or resume a session with the desired root instead.');
|
|
245
|
+
}
|
|
246
|
+
assertSandboxManifestMetadataSupported('DaytonaSandboxClient', resolvedManifest, MOUNT_MANIFEST_METADATA_SUPPORT);
|
|
247
|
+
await applyLocalSourceManifestToState(this.state, resolvedManifest, 'daytona', this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(runAs));
|
|
248
|
+
}
|
|
249
|
+
async materializeInitialManifest(manifest) {
|
|
250
|
+
await materializeLocalSourceManifest(this.writer(), manifest, 'daytona', this.remotePathResolver, this.manifestMaterializationOptions());
|
|
251
|
+
}
|
|
252
|
+
async rematerializeMountEntries() {
|
|
253
|
+
const targets = [];
|
|
254
|
+
for (const target of this.state.manifest.mountTargetsForMaterialization()) {
|
|
255
|
+
targets.push({
|
|
256
|
+
...target,
|
|
257
|
+
resolvedMountPath: await this.resolveMountEntryPath(target.mountPath, target.entry),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
for (const { resolvedMountPath } of [...targets].reverse()) {
|
|
261
|
+
await this.unmountMountPath(resolvedMountPath);
|
|
262
|
+
}
|
|
263
|
+
this.activeMountPaths.clear();
|
|
264
|
+
for (const { mountPath, entry } of targets) {
|
|
265
|
+
await this.materializeMountEntry(mountPath, entry);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async prepareWorkspaceRoot() {
|
|
269
|
+
const root = this.state.manifest.root;
|
|
270
|
+
const result = await this.sandbox.process.executeCommand(`mkdir -p -- ${shellQuote(root)}`, '/', this.state.environment);
|
|
271
|
+
if (result.exitCode !== 0) {
|
|
272
|
+
throw new SandboxProviderError('DaytonaSandboxClient failed to prepare the workspace root.', {
|
|
273
|
+
provider: 'daytona',
|
|
274
|
+
operation: 'prepare workspace root',
|
|
275
|
+
sandboxId: this.state.sandboxId,
|
|
276
|
+
root,
|
|
277
|
+
stdout: result.artifacts?.stdout ?? result.result ?? '',
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async persistWorkspace() {
|
|
282
|
+
assertTarWorkspacePersistence('DaytonaSandboxClient', 'tar');
|
|
283
|
+
return await persistRemoteWorkspaceTar({
|
|
284
|
+
providerName: 'DaytonaSandboxClient',
|
|
285
|
+
manifest: this.state.manifest,
|
|
286
|
+
io: this.archiveIo(),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
async hydrateWorkspace(data) {
|
|
290
|
+
assertTarWorkspacePersistence('DaytonaSandboxClient', 'tar');
|
|
291
|
+
await hydrateRemoteWorkspaceTar({
|
|
292
|
+
providerName: 'DaytonaSandboxClient',
|
|
293
|
+
manifest: this.state.manifest,
|
|
294
|
+
io: this.archiveIo(),
|
|
295
|
+
data,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
async close() {
|
|
299
|
+
if (this.state.pauseOnExit) {
|
|
300
|
+
await this.stopOnce();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
await this.deleteOnce();
|
|
304
|
+
}
|
|
305
|
+
async shutdown() {
|
|
306
|
+
await this.close();
|
|
307
|
+
}
|
|
308
|
+
async delete() {
|
|
309
|
+
await this.deleteOnce();
|
|
310
|
+
}
|
|
311
|
+
async stopOnce() {
|
|
312
|
+
this.stopPromise ??= (async () => {
|
|
313
|
+
await this.ptyProcesses.terminateAll();
|
|
314
|
+
await withSandboxSpan('sandbox.stop', {
|
|
315
|
+
backend_id: 'daytona',
|
|
316
|
+
sandbox_id: this.state.sandboxId,
|
|
317
|
+
}, async () => {
|
|
318
|
+
await this.sandbox.stop();
|
|
319
|
+
});
|
|
320
|
+
})();
|
|
321
|
+
await this.stopPromise;
|
|
322
|
+
}
|
|
323
|
+
async deleteOnce() {
|
|
324
|
+
this.deletePromise ??= (async () => {
|
|
325
|
+
await this.ptyProcesses.terminateAll();
|
|
326
|
+
await withSandboxSpan('sandbox.shutdown', {
|
|
327
|
+
backend_id: 'daytona',
|
|
328
|
+
sandbox_id: this.state.sandboxId,
|
|
329
|
+
}, async () => {
|
|
330
|
+
await this.unmountActiveMounts();
|
|
331
|
+
await deleteDaytonaSandboxWithRetry(async () => {
|
|
332
|
+
await this.sandbox.delete();
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
})();
|
|
336
|
+
await this.deletePromise;
|
|
337
|
+
}
|
|
338
|
+
writer() {
|
|
339
|
+
return {
|
|
340
|
+
mkdir: async (path) => {
|
|
341
|
+
await this.sandbox.fs.createFolder(path, '755');
|
|
342
|
+
},
|
|
343
|
+
writeFile: async (path, content) => {
|
|
344
|
+
await this.ensureParentDir(path);
|
|
345
|
+
await this.sandbox.fs.uploadFile(Buffer.from(content), path);
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
manifestMaterializationOptions(runAs) {
|
|
350
|
+
return manifestMaterializationOptionsWithRunAs({
|
|
351
|
+
providerName: 'DaytonaSandboxClient',
|
|
352
|
+
providerId: 'daytona',
|
|
353
|
+
runAs,
|
|
354
|
+
runCommand: this.runAsCommandRunner.bind(this),
|
|
355
|
+
options: {
|
|
356
|
+
materializeMount: this.materializeMountEntry.bind(this),
|
|
357
|
+
concurrencyLimits: this.concurrencyLimits,
|
|
358
|
+
},
|
|
359
|
+
support: MOUNT_MANIFEST_METADATA_SUPPORT,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async materializeMountEntry(absolutePath, entry) {
|
|
363
|
+
if (!isDaytonaCloudBucketMountEntry(entry)) {
|
|
364
|
+
throw new SandboxUnsupportedFeatureError('DaytonaSandboxClient only supports DaytonaCloudBucketMountStrategy mount entries.', {
|
|
365
|
+
provider: 'daytona',
|
|
366
|
+
feature: 'entry.mountStrategy',
|
|
367
|
+
path: absolutePath,
|
|
368
|
+
mountType: entry.type,
|
|
369
|
+
strategyType: entry.mountStrategy?.type,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
const mountPath = await this.resolveMountEntryPath(absolutePath, entry);
|
|
373
|
+
await mountRcloneCloudBucket({
|
|
374
|
+
providerName: 'DaytonaSandboxClient',
|
|
375
|
+
providerId: 'daytona',
|
|
376
|
+
strategyType: 'daytona_cloud_bucket',
|
|
377
|
+
entry,
|
|
378
|
+
mountPath,
|
|
379
|
+
pattern: rclonePatternFromMountStrategy(entry.mountStrategy),
|
|
380
|
+
runCommand: this.mountCommandRunner(),
|
|
381
|
+
writeFile: async (path, content) => {
|
|
382
|
+
await this.ensureParentDir(path);
|
|
383
|
+
await this.sandbox.fs.uploadFile(Buffer.from(content), path);
|
|
384
|
+
},
|
|
385
|
+
packageManagers: ['apt', 'apk'],
|
|
386
|
+
});
|
|
387
|
+
this.activeMountPaths.add(mountPath);
|
|
388
|
+
}
|
|
389
|
+
async unmountActiveMounts() {
|
|
390
|
+
for (const mountPath of [...this.activeMountPaths].reverse()) {
|
|
391
|
+
await this.unmountMountPath(mountPath);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async unmountMountPath(mountPath) {
|
|
395
|
+
await unmountRcloneMount({
|
|
396
|
+
providerName: 'DaytonaSandboxClient',
|
|
397
|
+
providerId: 'daytona',
|
|
398
|
+
mountPath,
|
|
399
|
+
runCommand: this.mountCommandRunner(),
|
|
400
|
+
}).catch(() => { });
|
|
401
|
+
this.activeMountPaths.delete(mountPath);
|
|
402
|
+
}
|
|
403
|
+
async resolveMountEntryPath(absolutePath, entry) {
|
|
404
|
+
return await this.resolveRemotePath(entry.mountPath ?? absolutePath, {
|
|
405
|
+
forWrite: true,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
mountCommandRunner() {
|
|
409
|
+
return async (command, options = {}) => {
|
|
410
|
+
const commandToRun = commandForDaytonaUser(command, options.user);
|
|
411
|
+
const result = await this.sandbox.process.executeCommand(commandToRun, this.state.manifest.root, this.state.environment, options.timeoutMs ? Math.ceil(options.timeoutMs / 1000) : undefined);
|
|
412
|
+
return {
|
|
413
|
+
status: result.exitCode,
|
|
414
|
+
stdout: result.artifacts?.stdout ?? result.result ?? '',
|
|
415
|
+
stderr: '',
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
async runAsCommandRunner(command, options = {}) {
|
|
420
|
+
const result = await this.sandbox.process.executeCommand(commandForDaytonaUser(command, options.runAs), this.state.manifest.root, this.state.environment, this.state.timeoutSec);
|
|
421
|
+
return {
|
|
422
|
+
status: result.exitCode,
|
|
423
|
+
stdout: result.artifacts?.stdout ?? result.result ?? '',
|
|
424
|
+
stderr: '',
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
archiveIo() {
|
|
428
|
+
return {
|
|
429
|
+
runCommand: async (command) => {
|
|
430
|
+
const result = await this.sandbox.process.executeCommand(command, this.state.manifest.root, this.state.environment, this.state.timeoutSec);
|
|
431
|
+
return {
|
|
432
|
+
status: result.exitCode,
|
|
433
|
+
stdout: result.artifacts?.stdout ?? result.result ?? '',
|
|
434
|
+
stderr: '',
|
|
435
|
+
};
|
|
436
|
+
},
|
|
437
|
+
readFile: async (path) => Uint8Array.from(await this.sandbox.fs.downloadFile(path)),
|
|
438
|
+
writeFile: async (path, content) => {
|
|
439
|
+
await this.ensureParentDir(path);
|
|
440
|
+
await this.sandbox.fs.uploadFile(Buffer.from(content), path);
|
|
441
|
+
},
|
|
442
|
+
mkdir: async (path) => {
|
|
443
|
+
await this.sandbox.fs.createFolder(path, '755');
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async readFileBytes(path) {
|
|
448
|
+
const absolutePath = this.resolveEditorPath(path);
|
|
449
|
+
const output = await this.runEditorFileCommand([
|
|
450
|
+
'set -eu',
|
|
451
|
+
`root=${shellQuote(this.state.manifest.root)}`,
|
|
452
|
+
`path=${shellQuote(absolutePath)}`,
|
|
453
|
+
'resolved_root=$(realpath -m -- "$root")',
|
|
454
|
+
'resolved=$(readlink -f -- "$path")',
|
|
455
|
+
'case "$resolved" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved" >&2; exit 111 ;; esac',
|
|
456
|
+
'[ -f "$resolved" ] || { printf "not a regular file: %s\\n" "$resolved" >&2; exit 112; }',
|
|
457
|
+
'exec 3< "$resolved"',
|
|
458
|
+
'resolved=$(readlink -f "/proc/$$/fd/3")',
|
|
459
|
+
'case "$resolved" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved" >&2; exit 111 ;; esac',
|
|
460
|
+
'base64 <&3',
|
|
461
|
+
].join('\n'));
|
|
462
|
+
return Buffer.from(output.replace(/\s+/gu, ''), 'base64');
|
|
463
|
+
}
|
|
464
|
+
async readEditorText(path) {
|
|
465
|
+
return new TextDecoder().decode(await this.readFileBytes(path));
|
|
466
|
+
}
|
|
467
|
+
async mkdirEditorPath(path) {
|
|
468
|
+
const absolutePath = this.resolveEditorPath(path, { forWrite: true });
|
|
469
|
+
await this.runEditorFileCommand([
|
|
470
|
+
'set -eu',
|
|
471
|
+
`root=${shellQuote(this.state.manifest.root)}`,
|
|
472
|
+
`path=${shellQuote(absolutePath)}`,
|
|
473
|
+
'resolved_root=$(realpath -m -- "$root")',
|
|
474
|
+
'resolved_path=$(realpath -m -- "$path")',
|
|
475
|
+
'case "$resolved_path" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved_path" >&2; exit 111 ;; esac',
|
|
476
|
+
'mkdir -p -- "$resolved_path"',
|
|
477
|
+
'resolved_created=$(realpath -m -- "$resolved_path")',
|
|
478
|
+
'case "$resolved_created" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved_created" >&2; exit 111 ;; esac',
|
|
479
|
+
'[ -d "$resolved_created" ] || { printf "not a directory: %s\\n" "$resolved_created" >&2; exit 112; }',
|
|
480
|
+
].join('\n'));
|
|
481
|
+
}
|
|
482
|
+
async writeEditorText(path, content) {
|
|
483
|
+
const absolutePath = this.resolveEditorPath(path, { forWrite: true });
|
|
484
|
+
const encoded = Buffer.from(content, 'utf8').toString('base64');
|
|
485
|
+
await this.runEditorFileCommand([
|
|
486
|
+
'set -eu',
|
|
487
|
+
`root=${shellQuote(this.state.manifest.root)}`,
|
|
488
|
+
`path=${shellQuote(absolutePath)}`,
|
|
489
|
+
'resolved_root=$(realpath -m -- "$root")',
|
|
490
|
+
'parent=$(dirname -- "$path")',
|
|
491
|
+
'base=$(basename -- "$path")',
|
|
492
|
+
'resolved_parent=$(realpath -m -- "$parent")',
|
|
493
|
+
'case "$resolved_parent" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved_parent" >&2; exit 111 ;; esac',
|
|
494
|
+
'target="$resolved_parent/$base"',
|
|
495
|
+
'if [ -d "$target" ]; then printf "directory target: %s\\n" "$target" >&2; exit 112; fi',
|
|
496
|
+
'tmp=$(mktemp "$resolved_parent/.openai-agents-write.XXXXXX")',
|
|
497
|
+
'cleanup() { rm -f -- "$tmp"; }',
|
|
498
|
+
'trap cleanup EXIT HUP INT TERM',
|
|
499
|
+
'base64 -d > "$tmp" <<\'OPENAI_AGENTS_CONTENT\'',
|
|
500
|
+
encoded,
|
|
501
|
+
'OPENAI_AGENTS_CONTENT',
|
|
502
|
+
'chmod 644 "$tmp"',
|
|
503
|
+
'mv -f -- "$tmp" "$target"',
|
|
504
|
+
'trap - EXIT',
|
|
505
|
+
].join('\n'));
|
|
506
|
+
}
|
|
507
|
+
async deleteEditorPath(path) {
|
|
508
|
+
const absolutePath = this.resolveEditorPath(path, { forWrite: true });
|
|
509
|
+
await this.runEditorFileCommand([
|
|
510
|
+
'set -eu',
|
|
511
|
+
`root=${shellQuote(this.state.manifest.root)}`,
|
|
512
|
+
`path=${shellQuote(absolutePath)}`,
|
|
513
|
+
'resolved_root=$(realpath -m -- "$root")',
|
|
514
|
+
'parent=$(dirname -- "$path")',
|
|
515
|
+
'base=$(basename -- "$path")',
|
|
516
|
+
'resolved_parent=$(realpath -m -- "$parent")',
|
|
517
|
+
'case "$resolved_parent" in "$resolved_root"|"$resolved_root"/*) ;; *) printf "workspace escape: %s\\n" "$resolved_parent" >&2; exit 111 ;; esac',
|
|
518
|
+
'rm -f -- "$resolved_parent/$base"',
|
|
519
|
+
].join('\n'));
|
|
520
|
+
}
|
|
521
|
+
async runEditorFileCommand(command) {
|
|
522
|
+
const result = await this.sandbox.process.executeCommand(command, this.state.manifest.root, this.state.environment, this.state.timeoutSec);
|
|
523
|
+
if (result.exitCode !== 0) {
|
|
524
|
+
throw new UserError((result.artifacts?.stdout ||
|
|
525
|
+
result.result ||
|
|
526
|
+
'remote editor operation failed')
|
|
527
|
+
.trim()
|
|
528
|
+
.split(/\r?\n/u)
|
|
529
|
+
.join('; '));
|
|
530
|
+
}
|
|
531
|
+
return result.artifacts?.stdout ?? result.result ?? '';
|
|
532
|
+
}
|
|
533
|
+
resolveEditorPath(path, options = {}) {
|
|
534
|
+
return resolveSandboxAbsolutePath(this.state.manifest.root, path, options);
|
|
535
|
+
}
|
|
536
|
+
async ensureParentDir(path) {
|
|
537
|
+
const parent = posixDirname(path);
|
|
538
|
+
if (parent !== '.' && parent !== '/') {
|
|
539
|
+
await this.sandbox.fs.createFolder(parent, '755');
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
async resolveRemotePath(path, options = {}) {
|
|
543
|
+
return await validateRemoteSandboxPathForManifest({
|
|
544
|
+
manifest: this.state.manifest,
|
|
545
|
+
path,
|
|
546
|
+
options,
|
|
547
|
+
runCommand: async (command) => {
|
|
548
|
+
const result = await this.sandbox.process.executeCommand(command, this.state.manifest.root, this.state.environment);
|
|
549
|
+
return {
|
|
550
|
+
status: result.exitCode,
|
|
551
|
+
stdout: result.artifacts?.stdout ?? result.result ?? '',
|
|
552
|
+
stderr: '',
|
|
553
|
+
};
|
|
554
|
+
},
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async createSignedPreviewUrl(port) {
|
|
558
|
+
try {
|
|
559
|
+
if (this.sandbox.getSignedPreviewUrl) {
|
|
560
|
+
return await this.sandbox.getSignedPreviewUrl(port, this.state.exposedPortUrlTtlS);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
throw new SandboxProviderError(`DaytonaSandboxClient failed to resolve exposed port ${port}.`, {
|
|
565
|
+
provider: 'daytona',
|
|
566
|
+
port,
|
|
567
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
throw new SandboxProviderError('DaytonaSandboxClient exposed port resolution requires a Daytona signed preview URL API.', {
|
|
571
|
+
provider: 'daytona',
|
|
572
|
+
port,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* @see {@link https://www.daytona.io/docs/ | Daytona docs}.
|
|
578
|
+
* @see {@link https://www.daytona.io/docs/en/getting-started/ | Getting started}.
|
|
579
|
+
* @see {@link https://www.daytona.io/docs/en/typescript-sdk/sandbox/ | TypeScript SDK Sandbox reference}.
|
|
580
|
+
*/
|
|
581
|
+
export class DaytonaSandboxClient {
|
|
582
|
+
backendId = 'daytona';
|
|
583
|
+
options;
|
|
584
|
+
constructor(options = {}) {
|
|
585
|
+
this.options = options;
|
|
586
|
+
}
|
|
587
|
+
async create(args, manifestOptions) {
|
|
588
|
+
const createArgs = normalizeSandboxClientCreateArgs(args, manifestOptions);
|
|
589
|
+
assertCoreSnapshotUnsupported('DaytonaSandboxClient', createArgs.snapshot);
|
|
590
|
+
const manifest = createArgs.manifest;
|
|
591
|
+
const resolvedOptions = {
|
|
592
|
+
...this.options,
|
|
593
|
+
...createArgs.options,
|
|
594
|
+
};
|
|
595
|
+
const resolvedManifest = resolveManifestRoot(manifest);
|
|
596
|
+
assertSandboxManifestMetadataSupported('DaytonaSandboxClient', resolvedManifest, MOUNT_MANIFEST_METADATA_SUPPORT);
|
|
597
|
+
const client = await createDaytonaClient(resolvedOptions);
|
|
598
|
+
return await withSandboxSpan('sandbox.start', {
|
|
599
|
+
backend_id: this.backendId,
|
|
600
|
+
}, async () => {
|
|
601
|
+
const environment = await materializeEnvironment(resolvedManifest, resolvedOptions.env);
|
|
602
|
+
const sandbox = await withProviderError('DaytonaSandboxClient', 'daytona', 'create sandbox', async () => await client.create({
|
|
603
|
+
...(resolvedOptions.sandboxSnapshotName
|
|
604
|
+
? { snapshot: resolvedOptions.sandboxSnapshotName }
|
|
605
|
+
: { image: resolvedOptions.image ?? 'debian:12.9' }),
|
|
606
|
+
...(!resolvedOptions.sandboxSnapshotName &&
|
|
607
|
+
resolvedOptions.resources
|
|
608
|
+
? { resources: resolvedOptions.resources }
|
|
609
|
+
: {}),
|
|
610
|
+
...(resolvedOptions.name ? { name: resolvedOptions.name } : {}),
|
|
611
|
+
...(typeof resolvedOptions.autoStopInterval === 'number'
|
|
612
|
+
? { autoStopInterval: resolvedOptions.autoStopInterval }
|
|
613
|
+
: {}),
|
|
614
|
+
...(typeof resolvedOptions.startTimeoutSec === 'number'
|
|
615
|
+
? { startTimeoutSec: resolvedOptions.startTimeoutSec }
|
|
616
|
+
: {}),
|
|
617
|
+
...(typeof resolvedOptions.timeoutSec === 'number'
|
|
618
|
+
? { timeoutSec: resolvedOptions.timeoutSec }
|
|
619
|
+
: {}),
|
|
620
|
+
...(resolvedOptions.exposedPorts
|
|
621
|
+
? { exposedPorts: resolvedOptions.exposedPorts }
|
|
622
|
+
: {}),
|
|
623
|
+
...(typeof resolvedOptions.exposedPortUrlTtlS === 'number'
|
|
624
|
+
? { exposedPortUrlTtlS: resolvedOptions.exposedPortUrlTtlS }
|
|
625
|
+
: {}),
|
|
626
|
+
envVars: environment,
|
|
627
|
+
}, ...(typeof resolvedOptions.createTimeoutSec === 'number'
|
|
628
|
+
? [{ timeout: resolvedOptions.createTimeoutSec }]
|
|
629
|
+
: [])), resolvedOptions.sandboxSnapshotName
|
|
630
|
+
? { snapshot: resolvedOptions.sandboxSnapshotName }
|
|
631
|
+
: { image: resolvedOptions.image ?? 'debian:12.9' });
|
|
632
|
+
const session = new DaytonaSandboxSession({
|
|
633
|
+
sandbox,
|
|
634
|
+
concurrencyLimits: createArgs.concurrencyLimits,
|
|
635
|
+
state: {
|
|
636
|
+
manifest: resolvedManifest,
|
|
637
|
+
sandboxId: sandbox.id,
|
|
638
|
+
image: resolvedOptions.image,
|
|
639
|
+
resources: resolvedOptions.resources,
|
|
640
|
+
pauseOnExit: resolvedOptions.pauseOnExit ?? false,
|
|
641
|
+
createTimeoutSec: resolvedOptions.createTimeoutSec,
|
|
642
|
+
startTimeoutSec: resolvedOptions.startTimeoutSec,
|
|
643
|
+
timeoutSec: resolvedOptions.timeoutSec,
|
|
644
|
+
sandboxSnapshotName: resolvedOptions.sandboxSnapshotName,
|
|
645
|
+
configuredExposedPorts: resolvedOptions.exposedPorts,
|
|
646
|
+
exposedPortUrlTtlS: resolvedOptions.exposedPortUrlTtlS,
|
|
647
|
+
name: resolvedOptions.name,
|
|
648
|
+
autoStopInterval: resolvedOptions.autoStopInterval,
|
|
649
|
+
environment,
|
|
650
|
+
apiKey: resolvedOptions.apiKey,
|
|
651
|
+
apiUrl: resolvedOptions.apiUrl,
|
|
652
|
+
target: resolvedOptions.target,
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
try {
|
|
656
|
+
await session.prepareWorkspaceRoot();
|
|
657
|
+
await session.materializeInitialManifest(resolvedManifest);
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
session.state.pauseOnExit = false;
|
|
661
|
+
await closeRemoteSessionOnManifestError('Daytona', session, error);
|
|
662
|
+
}
|
|
663
|
+
return session;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
async serializeSessionState(state) {
|
|
667
|
+
return serializeRemoteSandboxSessionState(state);
|
|
668
|
+
}
|
|
669
|
+
canPersistOwnedSessionState(state) {
|
|
670
|
+
return state.pauseOnExit;
|
|
671
|
+
}
|
|
672
|
+
async deserializeSessionState(state) {
|
|
673
|
+
const baseState = deserializeRemoteSandboxSessionStateValues(state, this.options.env);
|
|
674
|
+
return {
|
|
675
|
+
...state,
|
|
676
|
+
...baseState,
|
|
677
|
+
sandboxId: readString(state, 'sandboxId'),
|
|
678
|
+
pauseOnExit: Boolean(state.pauseOnExit),
|
|
679
|
+
image: readOptionalString(state, 'image'),
|
|
680
|
+
resources: readOptionalRecord(state.resources),
|
|
681
|
+
createTimeoutSec: readOptionalNumber(state, 'createTimeoutSec'),
|
|
682
|
+
startTimeoutSec: readOptionalNumber(state, 'startTimeoutSec'),
|
|
683
|
+
timeoutSec: readOptionalNumber(state, 'timeoutSec'),
|
|
684
|
+
sandboxSnapshotName: readOptionalString(state, 'sandboxSnapshotName'),
|
|
685
|
+
configuredExposedPorts: readOptionalNumberArray(state.configuredExposedPorts),
|
|
686
|
+
exposedPortUrlTtlS: readOptionalNumber(state, 'exposedPortUrlTtlS'),
|
|
687
|
+
name: readOptionalString(state, 'name'),
|
|
688
|
+
autoStopInterval: readOptionalNumber(state, 'autoStopInterval'),
|
|
689
|
+
apiKey: readOptionalString(state, 'apiKey'),
|
|
690
|
+
apiUrl: readOptionalString(state, 'apiUrl'),
|
|
691
|
+
target: readOptionalString(state, 'target'),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
async resume(state) {
|
|
695
|
+
const client = await createDaytonaClient({
|
|
696
|
+
...this.options,
|
|
697
|
+
apiKey: state.apiKey ?? this.options.apiKey,
|
|
698
|
+
apiUrl: state.apiUrl ?? this.options.apiUrl,
|
|
699
|
+
target: state.target ?? this.options.target,
|
|
700
|
+
});
|
|
701
|
+
let sandbox;
|
|
702
|
+
try {
|
|
703
|
+
sandbox = await client.get(state.sandboxId);
|
|
704
|
+
await sandbox.start(state.startTimeoutSec);
|
|
705
|
+
}
|
|
706
|
+
catch (error) {
|
|
707
|
+
assertResumeRecreateAllowed(error, {
|
|
708
|
+
providerName: 'DaytonaSandboxClient',
|
|
709
|
+
provider: 'daytona',
|
|
710
|
+
details: { sandboxId: state.sandboxId },
|
|
711
|
+
});
|
|
712
|
+
return await this.recreateFromPersistedState(client, state);
|
|
713
|
+
}
|
|
714
|
+
const session = new DaytonaSandboxSession({ state, sandbox });
|
|
715
|
+
await session.prepareWorkspaceRoot();
|
|
716
|
+
await session.rematerializeMountEntries();
|
|
717
|
+
return session;
|
|
718
|
+
}
|
|
719
|
+
async recreateFromPersistedState(client, state) {
|
|
720
|
+
const sandbox = await withProviderError('DaytonaSandboxClient', 'daytona', 'create sandbox', async () => await client.create({
|
|
721
|
+
...(state.sandboxSnapshotName
|
|
722
|
+
? { snapshot: state.sandboxSnapshotName }
|
|
723
|
+
: { image: state.image ?? 'debian:12.9' }),
|
|
724
|
+
...(!state.sandboxSnapshotName && state.resources
|
|
725
|
+
? { resources: state.resources }
|
|
726
|
+
: {}),
|
|
727
|
+
...(state.name ? { name: state.name } : {}),
|
|
728
|
+
...(typeof state.autoStopInterval === 'number'
|
|
729
|
+
? { autoStopInterval: state.autoStopInterval }
|
|
730
|
+
: {}),
|
|
731
|
+
...(typeof state.startTimeoutSec === 'number'
|
|
732
|
+
? { startTimeoutSec: state.startTimeoutSec }
|
|
733
|
+
: {}),
|
|
734
|
+
...(typeof state.timeoutSec === 'number'
|
|
735
|
+
? { timeoutSec: state.timeoutSec }
|
|
736
|
+
: {}),
|
|
737
|
+
...(state.configuredExposedPorts
|
|
738
|
+
? { exposedPorts: state.configuredExposedPorts }
|
|
739
|
+
: {}),
|
|
740
|
+
...(typeof state.exposedPortUrlTtlS === 'number'
|
|
741
|
+
? { exposedPortUrlTtlS: state.exposedPortUrlTtlS }
|
|
742
|
+
: {}),
|
|
743
|
+
envVars: state.environment,
|
|
744
|
+
}, ...(typeof state.createTimeoutSec === 'number'
|
|
745
|
+
? [{ timeout: state.createTimeoutSec }]
|
|
746
|
+
: [])), state.sandboxSnapshotName
|
|
747
|
+
? { snapshot: state.sandboxSnapshotName }
|
|
748
|
+
: { image: state.image ?? 'debian:12.9' });
|
|
749
|
+
const nextState = {
|
|
750
|
+
...state,
|
|
751
|
+
sandboxId: sandbox.id,
|
|
752
|
+
environment: { ...state.environment },
|
|
753
|
+
};
|
|
754
|
+
delete nextState.exposedPorts;
|
|
755
|
+
const session = new DaytonaSandboxSession({
|
|
756
|
+
sandbox,
|
|
757
|
+
state: nextState,
|
|
758
|
+
});
|
|
759
|
+
try {
|
|
760
|
+
await session.prepareWorkspaceRoot();
|
|
761
|
+
await session.materializeInitialManifest(state.manifest);
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
session.state.pauseOnExit = false;
|
|
765
|
+
await closeRemoteSessionOnManifestError('Daytona', session, error);
|
|
766
|
+
}
|
|
767
|
+
return session;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function getUsableDaytonaCachedExposedPortEndpoint(state, requestedPort) {
|
|
771
|
+
const cached = getCachedExposedPortEndpoint(state, requestedPort);
|
|
772
|
+
if (!cached) {
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
775
|
+
const expiresAtMs = cached.daytonaExpiresAtMs;
|
|
776
|
+
return typeof expiresAtMs === 'number' &&
|
|
777
|
+
Number.isFinite(expiresAtMs) &&
|
|
778
|
+
expiresAtMs > Date.now()
|
|
779
|
+
? cached
|
|
780
|
+
: undefined;
|
|
781
|
+
}
|
|
782
|
+
function withDaytonaPreviewExpiration(endpoint, ttlS) {
|
|
783
|
+
const resolvedTtlS = typeof ttlS === 'number' ? ttlS : DEFAULT_EXPOSED_PORT_URL_TTL_S;
|
|
784
|
+
if (!Number.isFinite(resolvedTtlS) || resolvedTtlS <= 0) {
|
|
785
|
+
return endpoint;
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
...endpoint,
|
|
789
|
+
daytonaExpiresAtMs: Date.now() + resolvedTtlS * 1000,
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
async function createDaytonaClient(options) {
|
|
793
|
+
try {
|
|
794
|
+
const { Daytona } = await import('@daytonaio/sdk');
|
|
795
|
+
return adaptDaytonaClient(new Daytona({
|
|
796
|
+
...(options.apiKey ? { apiKey: options.apiKey } : {}),
|
|
797
|
+
...(options.apiUrl ? { apiUrl: options.apiUrl } : {}),
|
|
798
|
+
...(options.target ? { target: options.target } : {}),
|
|
799
|
+
}));
|
|
800
|
+
}
|
|
801
|
+
catch (error) {
|
|
802
|
+
throw new UserError(`Daytona sandbox support requires the optional \`@daytonaio/sdk\` package. Install it before using Daytona-backed sandbox examples. ${error.message}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function adaptDaytonaClient(client) {
|
|
806
|
+
return {
|
|
807
|
+
create: async (params, options) => adaptDaytonaSandbox(await client.create(params, options)),
|
|
808
|
+
get: async (idOrName) => adaptDaytonaSandbox(await client.get(idOrName)),
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function adaptDaytonaSandbox(sandbox) {
|
|
812
|
+
return {
|
|
813
|
+
id: sandbox.id,
|
|
814
|
+
start: async (timeout) => await sandbox.start(timeout),
|
|
815
|
+
stop: async (timeout, force) => await sandbox.stop(timeout, force),
|
|
816
|
+
delete: async (timeout) => await sandbox.delete(timeout),
|
|
817
|
+
fs: {
|
|
818
|
+
createFolder: async (path, mode) => await sandbox.fs.createFolder(path, mode),
|
|
819
|
+
uploadFile: async (source, remotePath, timeout) => {
|
|
820
|
+
if (typeof source === 'string') {
|
|
821
|
+
if (typeof timeout === 'number') {
|
|
822
|
+
await sandbox.fs.uploadFile(source, remotePath, timeout);
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
await sandbox.fs.uploadFile(source, remotePath);
|
|
826
|
+
}
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (typeof timeout === 'number') {
|
|
830
|
+
await sandbox.fs.uploadFile(source, remotePath, timeout);
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
await sandbox.fs.uploadFile(source, remotePath);
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
downloadFile: async (remotePath, timeout) => await sandbox.fs.downloadFile(remotePath, timeout),
|
|
837
|
+
deleteFile: async (path, recursive) => await sandbox.fs.deleteFile(path, recursive),
|
|
838
|
+
},
|
|
839
|
+
process: {
|
|
840
|
+
executeCommand: async (command, cwd, env, timeout) => typeof timeout === 'number'
|
|
841
|
+
? await sandbox.process.executeCommand(command, cwd, env, timeout)
|
|
842
|
+
: await sandbox.process.executeCommand(command, cwd, env),
|
|
843
|
+
...(typeof sandbox.process.createPty === 'function'
|
|
844
|
+
? {
|
|
845
|
+
createPty: async (options) => await sandbox.process.createPty(options),
|
|
846
|
+
}
|
|
847
|
+
: {}),
|
|
848
|
+
...(typeof sandbox.process.killPtySession === 'function'
|
|
849
|
+
? {
|
|
850
|
+
killPtySession: async (id) => await sandbox.process.killPtySession(id),
|
|
851
|
+
}
|
|
852
|
+
: {}),
|
|
853
|
+
},
|
|
854
|
+
getSignedPreviewUrl: async (port, expiresInSeconds) => await sandbox.getSignedPreviewUrl(port, expiresInSeconds),
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function resolveManifestRoot(manifest) {
|
|
858
|
+
if (manifest.root === '/workspace') {
|
|
859
|
+
return cloneManifestWithRoot(manifest, DEFAULT_WORKSPACE_ROOT);
|
|
860
|
+
}
|
|
861
|
+
return manifest;
|
|
862
|
+
}
|
|
863
|
+
function commandForDaytonaUser(command, user) {
|
|
864
|
+
return sandboxUserShellCommand(command, user);
|
|
865
|
+
}
|
|
866
|
+
async function deleteDaytonaSandboxWithRetry(deleteSandbox) {
|
|
867
|
+
let attempt = 0;
|
|
868
|
+
while (true) {
|
|
869
|
+
try {
|
|
870
|
+
await deleteSandbox();
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
const delayMs = DAYTONA_DELETE_RETRY_DELAYS_MS[attempt];
|
|
875
|
+
if (!isDaytonaStateChangeInProgressError(error) ||
|
|
876
|
+
delayMs === undefined) {
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
attempt += 1;
|
|
880
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
function isDaytonaStateChangeInProgressError(error) {
|
|
885
|
+
if (typeof error !== 'object' || error === null) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
const record = error;
|
|
889
|
+
return (record.statusCode === 409 &&
|
|
890
|
+
error instanceof Error &&
|
|
891
|
+
/state change in progress/i.test(error.message));
|
|
892
|
+
}
|
|
893
|
+
function exitCodeFromDaytonaResult(value) {
|
|
894
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
const record = value;
|
|
898
|
+
return typeof record.exitCode === 'number' ? record.exitCode : undefined;
|
|
899
|
+
}
|
|
900
|
+
//# sourceMappingURL=sandbox.mjs.map
|