withruntime 0.10.0 → 0.11.1
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/BLAXEL.md +7 -8
- package/DAYTONA.md +40 -35
- package/E2B.md +70 -57
- package/README.md +3 -3
- package/README.src.md +3 -3
- package/VERCEL.md +23 -20
- package/compatibility-lock.json +63 -84
- package/dist/api-defaults.d.ts +1 -1
- package/dist/api-defaults.js +1 -1
- package/dist/blaxel/lookup.d.ts +2 -1
- package/dist/blaxel/lookup.js +2 -1
- package/dist/blaxel/sandbox.d.ts +7 -5
- package/dist/blaxel/sandbox.js +15 -10
- package/dist/cli.js +112 -17
- package/dist/client.d.ts +17 -1
- package/dist/client.js +46 -3
- package/dist/compat/files.js +5 -1
- package/dist/compat/lease.d.ts +37 -0
- package/dist/compat/lease.js +93 -0
- package/dist/credentials.d.ts +9 -0
- package/dist/credentials.js +26 -4
- package/dist/daytona/computer-use.d.ts +162 -0
- package/dist/daytona/computer-use.js +324 -0
- package/dist/daytona/context.d.ts +10 -0
- package/dist/daytona/context.js +24 -0
- package/dist/daytona/daytona.d.ts +20 -0
- package/dist/daytona/daytona.js +150 -26
- package/dist/daytona/filesystem.js +8 -0
- package/dist/daytona/index.d.ts +2 -26
- package/dist/daytona/index.js +2 -7
- package/dist/daytona/process.d.ts +63 -6
- package/dist/daytona/process.js +224 -23
- package/dist/daytona/sandbox.d.ts +25 -6
- package/dist/daytona/sandbox.js +60 -27
- package/dist/e2b/client.d.ts +5 -0
- package/dist/e2b/client.js +5 -1
- package/dist/e2b/commands.d.ts +15 -6
- package/dist/e2b/commands.js +83 -18
- package/dist/e2b/errors.d.ts +7 -0
- package/dist/e2b/errors.js +20 -0
- package/dist/e2b/filesystem.d.ts +2 -1
- package/dist/e2b/filesystem.js +43 -6
- package/dist/e2b/index.d.ts +2 -2
- package/dist/e2b/index.js +1 -1
- package/dist/e2b/pty.js +6 -3
- package/dist/e2b/sandbox.d.ts +20 -6
- package/dist/e2b/sandbox.js +207 -31
- package/dist/e2b/users.d.ts +30 -0
- package/dist/e2b/users.js +185 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +6 -0
- package/dist/identity.js +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +6 -1
- package/dist/modal/index.js +6 -2
- package/dist/network-cli.js +33 -5
- package/dist/products/browser.d.ts +61 -0
- package/dist/products/browser.js +27 -0
- package/dist/products/desktop.d.ts +4 -3
- package/dist/products/desktop.js +7 -32
- package/dist/products/images.d.ts +3 -1
- package/dist/products/images.js +14 -2
- package/dist/products/index.d.ts +2 -0
- package/dist/products/index.js +2 -0
- package/dist/products/jobs.d.ts +7 -5
- package/dist/products/jobs.js +7 -3
- package/dist/products/mcp.d.ts +5 -1
- package/dist/products/mcp.js +29 -8
- package/dist/products/network-products.d.ts +8 -1
- package/dist/products/network-products.js +4 -1
- package/dist/products/observability.d.ts +1 -1
- package/dist/products/private-network.d.ts +22 -0
- package/dist/products/private-network.js +10 -0
- package/dist/products/secrets.d.ts +2 -2
- package/dist/runloop/index.js +6 -2
- package/dist/sandbox.d.ts +44 -14
- package/dist/sandbox.js +162 -48
- package/dist/tar.d.ts +2 -0
- package/dist/tar.js +74 -16
- package/dist/transport.d.ts +33 -7
- package/dist/transport.js +171 -36
- package/dist/types.d.ts +59 -11
- package/dist/vercel/filesystem.js +3 -1
- package/dist/vercel/index.d.ts +1 -7
- package/dist/vercel/index.js +1 -3
- package/dist/vercel/sandbox-user.d.ts +70 -0
- package/dist/vercel/sandbox-user.js +206 -0
- package/dist/vercel/sandbox.d.ts +57 -15
- package/dist/vercel/sandbox.js +219 -47
- package/dist/wait.d.ts +12 -0
- package/dist/wait.js +47 -0
- package/package.json +1 -1
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname } from "node:path";
|
|
3
|
+
import { RuntimeError } from "../errors.js";
|
|
4
|
+
import { guard, NotSupportedError } from "./errors.js";
|
|
5
|
+
const ALTERNATIVE_STREAM = "Open the live desktop from `(await sandbox.withruntime.desktop.start()).streamUrl`.";
|
|
6
|
+
async function desktop(live) {
|
|
7
|
+
return (await live()).desktop;
|
|
8
|
+
}
|
|
9
|
+
/** `sandbox.computerUse.mouse`. */
|
|
10
|
+
export class Mouse {
|
|
11
|
+
#live;
|
|
12
|
+
/** Use sandbox.computerUse.mouse. */
|
|
13
|
+
constructor(live) {
|
|
14
|
+
this.#live = live;
|
|
15
|
+
}
|
|
16
|
+
async getPosition() {
|
|
17
|
+
const { x, y } = await guard("other", async () => (await desktop(this.#live)).cursor());
|
|
18
|
+
return { x, y };
|
|
19
|
+
}
|
|
20
|
+
async move(x, y) {
|
|
21
|
+
await guard("other", async () => (await desktop(this.#live)).move(x, y));
|
|
22
|
+
return { x, y };
|
|
23
|
+
}
|
|
24
|
+
async click(x, y, button = "left", double = false) {
|
|
25
|
+
await guard("other", async () => (await desktop(this.#live)).click(x, y, { button: buttonOf(button), double }));
|
|
26
|
+
return { x, y };
|
|
27
|
+
}
|
|
28
|
+
async drag(startX, startY, endX, endY, button = "left") {
|
|
29
|
+
const which = buttonOf(button);
|
|
30
|
+
await guard("other", async () => {
|
|
31
|
+
const screen = await desktop(this.#live);
|
|
32
|
+
if (which === "left")
|
|
33
|
+
return screen.drag([startX, startY], [endX, endY]);
|
|
34
|
+
await screen.move(startX, startY);
|
|
35
|
+
await screen.mouseDown(which);
|
|
36
|
+
await screen.move(endX, endY);
|
|
37
|
+
return screen.mouseUp(which);
|
|
38
|
+
});
|
|
39
|
+
return { x: endX, y: endY };
|
|
40
|
+
}
|
|
41
|
+
/** `amount` wheel clicks up or down at (x, y). */
|
|
42
|
+
async scroll(x, y, direction, amount = 1) {
|
|
43
|
+
if (direction !== "up" && direction !== "down")
|
|
44
|
+
throw new RangeError(`direction is "up" or "down", not ${JSON.stringify(direction)}.`);
|
|
45
|
+
await guard("other", async () => (await desktop(this.#live)).scroll(direction === "down" ? amount : -amount, { x, y }));
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** `sandbox.computerUse.keyboard`. Key names are Daytona's (enter, esc,
|
|
50
|
+
* ctrl, cmd, pageup, f5) or xdotool's (Return, Escape, Prior). */
|
|
51
|
+
export class Keyboard {
|
|
52
|
+
#live;
|
|
53
|
+
/** Use sandbox.computerUse.keyboard. */
|
|
54
|
+
constructor(live) {
|
|
55
|
+
this.#live = live;
|
|
56
|
+
}
|
|
57
|
+
/** Types `text`, `delay` milliseconds between keys. */
|
|
58
|
+
async type(text, delay) {
|
|
59
|
+
await guard("other", async () => (await desktop(this.#live)).type(text, delay === undefined ? {} : { delayMs: delay }));
|
|
60
|
+
}
|
|
61
|
+
async press(key, modifiers = []) {
|
|
62
|
+
await guard("other", async () => (await desktop(this.#live)).press([...modifiers, key].map(keyOf).join("+")));
|
|
63
|
+
}
|
|
64
|
+
/** A combination such as "ctrl+c" or "ctrl+shift+t". */
|
|
65
|
+
async hotkey(keys) {
|
|
66
|
+
await guard("other", async () => (await desktop(this.#live)).press(keys.split("+").map(keyOf).join("+")));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** `sandbox.computerUse.screenshot`: the whole screen, PNG or JPEG. */
|
|
70
|
+
export class Screenshot {
|
|
71
|
+
#live;
|
|
72
|
+
/** Use sandbox.computerUse.screenshot. */
|
|
73
|
+
constructor(live) {
|
|
74
|
+
this.#live = live;
|
|
75
|
+
}
|
|
76
|
+
takeFullScreen(showCursor = false) {
|
|
77
|
+
return this.takeCompressed({ showCursor, format: "png" });
|
|
78
|
+
}
|
|
79
|
+
takeRegion(_region, _showCursor) {
|
|
80
|
+
return Promise.reject(region());
|
|
81
|
+
}
|
|
82
|
+
async takeCompressed(options = {}) {
|
|
83
|
+
if (options.showCursor)
|
|
84
|
+
throw new NotSupportedError("Drawing the cursor into a screenshot (showCursor)", "Take it without; sandbox.computerUse.mouse.getPosition() says where the cursor is.");
|
|
85
|
+
if (options.scale !== undefined && options.scale !== 1)
|
|
86
|
+
throw new NotSupportedError("Scaling a screenshot", "Take it at full size and scale it yourself, or start the desktop smaller: sandbox.withruntime.desktop.start({ width, height }).");
|
|
87
|
+
const format = (options.format ?? "png").toLowerCase();
|
|
88
|
+
if (format !== "png" && format !== "jpeg" && format !== "jpg")
|
|
89
|
+
throw new NotSupportedError(`Screenshots as ${format}`, 'Use format "png" or "jpeg".');
|
|
90
|
+
const bytes = await guard("other", async () => (await desktop(this.#live)).screenshot(format === "png"
|
|
91
|
+
? {}
|
|
92
|
+
: {
|
|
93
|
+
format: "jpeg",
|
|
94
|
+
...(options.quality === undefined ? {} : { quality: options.quality }),
|
|
95
|
+
}));
|
|
96
|
+
return { screenshot: Buffer.from(bytes).toString("base64"), sizeBytes: bytes.byteLength };
|
|
97
|
+
}
|
|
98
|
+
takeCompressedRegion(_region, _options) {
|
|
99
|
+
return Promise.reject(region());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** `sandbox.computerUse.display`. */
|
|
103
|
+
export class Display {
|
|
104
|
+
#live;
|
|
105
|
+
/** Use sandbox.computerUse.display. */
|
|
106
|
+
constructor(live) {
|
|
107
|
+
this.#live = live;
|
|
108
|
+
}
|
|
109
|
+
/** The one screen, its size read from a screenshot. */
|
|
110
|
+
async getInfo() {
|
|
111
|
+
const png = await guard("other", async () => (await desktop(this.#live)).screenshot());
|
|
112
|
+
const { width, height } = pngSize(png);
|
|
113
|
+
return { displays: [{ id: 0, x: 0, y: 0, width, height, isActive: true }] };
|
|
114
|
+
}
|
|
115
|
+
async getWindows() {
|
|
116
|
+
const windows = await guard("other", async () => (await desktop(this.#live)).windows());
|
|
117
|
+
return {
|
|
118
|
+
windows: windows.map((one) => ({
|
|
119
|
+
id: Number(one.id),
|
|
120
|
+
title: one.title,
|
|
121
|
+
x: one.x,
|
|
122
|
+
y: one.y,
|
|
123
|
+
width: one.width,
|
|
124
|
+
height: one.height,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** `sandbox.computerUse.recording`: screen recordings to MP4. */
|
|
130
|
+
export class RecordingService {
|
|
131
|
+
#live;
|
|
132
|
+
/** Use sandbox.computerUse.recording. */
|
|
133
|
+
constructor(live) {
|
|
134
|
+
this.#live = live;
|
|
135
|
+
}
|
|
136
|
+
/** Starts recording. Runtime names the file itself; `label` is not used. */
|
|
137
|
+
async start(_label) {
|
|
138
|
+
return recordingOf(await guard("other", async () => (await desktop(this.#live)).recordings.start()));
|
|
139
|
+
}
|
|
140
|
+
async stop(id) {
|
|
141
|
+
return recordingOf(await guard("other", async () => (await desktop(this.#live)).recordings.stop(id)));
|
|
142
|
+
}
|
|
143
|
+
async list() {
|
|
144
|
+
const all = await guard("other", async () => (await desktop(this.#live)).recordings.list());
|
|
145
|
+
return { recordings: all.map(recordingOf) };
|
|
146
|
+
}
|
|
147
|
+
async get(id) {
|
|
148
|
+
return recordingOf(await guard("other", async () => (await desktop(this.#live)).recordings.get(id)));
|
|
149
|
+
}
|
|
150
|
+
async delete(id) {
|
|
151
|
+
await guard("other", async () => (await desktop(this.#live)).recordings.delete(id));
|
|
152
|
+
}
|
|
153
|
+
/** Saves the MP4 to `localPath`, making its directory. */
|
|
154
|
+
async download(id, localPath) {
|
|
155
|
+
const bytes = await guard("other", async () => (await desktop(this.#live)).recordings.download(id));
|
|
156
|
+
await mkdir(dirname(localPath), { recursive: true });
|
|
157
|
+
await writeFile(localPath, bytes);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** `sandbox.computerUse.accessibility`: Runtime's desktop has no
|
|
161
|
+
* accessibility tree; every method throws NotSupportedError. */
|
|
162
|
+
export class Accessibility {
|
|
163
|
+
#refuse() {
|
|
164
|
+
return Promise.reject(new NotSupportedError("The desktop's accessibility tree", "Find things on screen with a screenshot (sandbox.computerUse.screenshot.takeFullScreen()) and act with the mouse and keyboard."));
|
|
165
|
+
}
|
|
166
|
+
getTree(_options) {
|
|
167
|
+
return this.#refuse();
|
|
168
|
+
}
|
|
169
|
+
findNodes(_options) {
|
|
170
|
+
return this.#refuse();
|
|
171
|
+
}
|
|
172
|
+
focusNode(_id) {
|
|
173
|
+
return this.#refuse();
|
|
174
|
+
}
|
|
175
|
+
invokeNode(_id, _action) {
|
|
176
|
+
return this.#refuse();
|
|
177
|
+
}
|
|
178
|
+
setNodeValue(_id, _value) {
|
|
179
|
+
return this.#refuse();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** `sandbox.computerUse`: a Linux desktop driven like a person would. */
|
|
183
|
+
export class ComputerUse {
|
|
184
|
+
mouse;
|
|
185
|
+
keyboard;
|
|
186
|
+
screenshot;
|
|
187
|
+
display;
|
|
188
|
+
recording;
|
|
189
|
+
accessibility;
|
|
190
|
+
#live;
|
|
191
|
+
/** Use sandbox.computerUse. */
|
|
192
|
+
constructor(live) {
|
|
193
|
+
this.#live = live;
|
|
194
|
+
this.mouse = new Mouse(live);
|
|
195
|
+
this.keyboard = new Keyboard(live);
|
|
196
|
+
this.screenshot = new Screenshot(live);
|
|
197
|
+
this.display = new Display(live);
|
|
198
|
+
this.recording = new RecordingService(live);
|
|
199
|
+
this.accessibility = new Accessibility();
|
|
200
|
+
}
|
|
201
|
+
/** Starts the desktop. A sandbox's first start installs it, which takes a
|
|
202
|
+
* minute or two; this waits for that. */
|
|
203
|
+
async start() {
|
|
204
|
+
await guard("other", async () => (await desktop(this.#live)).start());
|
|
205
|
+
return {
|
|
206
|
+
message: "Computer use processes started successfully",
|
|
207
|
+
status: { desktop: { running: true } },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async stop() {
|
|
211
|
+
await guard("other", async () => (await desktop(this.#live)).stop());
|
|
212
|
+
return {
|
|
213
|
+
message: "Computer use processes stopped successfully",
|
|
214
|
+
status: { desktop: { running: false } },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** "active" while the desktop runs, else "inactive". */
|
|
218
|
+
async getStatus() {
|
|
219
|
+
try {
|
|
220
|
+
await (await desktop(this.#live)).cursor();
|
|
221
|
+
return { status: "active" };
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (error instanceof RuntimeError &&
|
|
225
|
+
(error.code === "desktop_not_running" || error.code === "desktop_not_installed"))
|
|
226
|
+
return { status: "inactive" };
|
|
227
|
+
return guard("other", () => {
|
|
228
|
+
throw error;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
getProcessStatus(_processName) {
|
|
233
|
+
return processes();
|
|
234
|
+
}
|
|
235
|
+
restartProcess(_processName) {
|
|
236
|
+
return processes();
|
|
237
|
+
}
|
|
238
|
+
getProcessLogs(_processName) {
|
|
239
|
+
return processes();
|
|
240
|
+
}
|
|
241
|
+
getProcessErrors(_processName) {
|
|
242
|
+
return processes();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function processes() {
|
|
246
|
+
return Promise.reject(new NotSupportedError("Managing the desktop's processes one by one", `Use computerUse.getStatus(), stop() and start(). ${ALTERNATIVE_STREAM}`));
|
|
247
|
+
}
|
|
248
|
+
function region() {
|
|
249
|
+
return new NotSupportedError("Screenshots of part of the screen", "Take the whole screen with takeFullScreen() or takeCompressed() and crop it yourself.");
|
|
250
|
+
}
|
|
251
|
+
function buttonOf(button) {
|
|
252
|
+
if (button === "left" || button === "middle" || button === "right")
|
|
253
|
+
return button;
|
|
254
|
+
throw new RangeError(`button is "left", "middle" or "right", not ${JSON.stringify(button)}.`);
|
|
255
|
+
}
|
|
256
|
+
/** Daytona's key names as xdotool's; anything else passes as given. */
|
|
257
|
+
const KEYS = {
|
|
258
|
+
enter: "Return",
|
|
259
|
+
return: "Return",
|
|
260
|
+
esc: "Escape",
|
|
261
|
+
escape: "Escape",
|
|
262
|
+
backspace: "BackSpace",
|
|
263
|
+
delete: "Delete",
|
|
264
|
+
del: "Delete",
|
|
265
|
+
tab: "Tab",
|
|
266
|
+
space: "space",
|
|
267
|
+
up: "Up",
|
|
268
|
+
down: "Down",
|
|
269
|
+
left: "Left",
|
|
270
|
+
right: "Right",
|
|
271
|
+
home: "Home",
|
|
272
|
+
end: "End",
|
|
273
|
+
pageup: "Prior",
|
|
274
|
+
pagedown: "Next",
|
|
275
|
+
insert: "Insert",
|
|
276
|
+
capslock: "Caps_Lock",
|
|
277
|
+
control: "ctrl",
|
|
278
|
+
ctrl: "ctrl",
|
|
279
|
+
shift: "shift",
|
|
280
|
+
alt: "alt",
|
|
281
|
+
cmd: "super",
|
|
282
|
+
command: "super",
|
|
283
|
+
meta: "super",
|
|
284
|
+
win: "super",
|
|
285
|
+
super: "super",
|
|
286
|
+
};
|
|
287
|
+
function keyOf(key) {
|
|
288
|
+
const name = key.trim();
|
|
289
|
+
const lower = name.toLowerCase();
|
|
290
|
+
if (KEYS[lower])
|
|
291
|
+
return KEYS[lower];
|
|
292
|
+
if (/^f([1-9]|1[0-2])$/.test(lower))
|
|
293
|
+
return lower.toUpperCase();
|
|
294
|
+
return name;
|
|
295
|
+
}
|
|
296
|
+
const STATUS = {
|
|
297
|
+
starting: "recording",
|
|
298
|
+
installing: "recording",
|
|
299
|
+
recording: "recording",
|
|
300
|
+
finished: "completed",
|
|
301
|
+
failed: "failed",
|
|
302
|
+
};
|
|
303
|
+
function recordingOf(recording) {
|
|
304
|
+
const start = recording.startedAt ?? null;
|
|
305
|
+
return {
|
|
306
|
+
id: recording.id,
|
|
307
|
+
fileName: basename(recording.path),
|
|
308
|
+
filePath: recording.path,
|
|
309
|
+
startTime: start === null ? "" : new Date(start).toISOString(),
|
|
310
|
+
...(start !== null && recording.seconds !== undefined && STATUS[recording.state] !== "recording"
|
|
311
|
+
? { endTime: new Date(start + recording.seconds * 1000).toISOString() }
|
|
312
|
+
: {}),
|
|
313
|
+
status: STATUS[recording.state] ?? recording.state,
|
|
314
|
+
sizeBytes: recording.bytes,
|
|
315
|
+
...(recording.seconds === undefined ? {} : { durationSeconds: recording.seconds }),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/** Width and height from a PNG's header. */
|
|
319
|
+
function pngSize(png) {
|
|
320
|
+
const view = new DataView(png.buffer, png.byteOffset, png.byteLength);
|
|
321
|
+
if (png.byteLength < 24 || view.getUint32(12) !== 0x49484452)
|
|
322
|
+
throw new RangeError("The desktop's screenshot was not a PNG.");
|
|
323
|
+
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
324
|
+
}
|
|
@@ -11,6 +11,8 @@ export interface SandboxContext {
|
|
|
11
11
|
/** Makes /home/daytona (Daytona's home) lead to /workspace (Runtime's),
|
|
12
12
|
* once, the first time something names it. */
|
|
13
13
|
ensureHome(text: string | undefined): Promise<void>;
|
|
14
|
+
/** The Linux user commands run as (create's `user`), when not the owner. */
|
|
15
|
+
readonly user?: string;
|
|
14
16
|
}
|
|
15
17
|
/** Daytona's home, and the working directory relative paths resolve from. */
|
|
16
18
|
export declare const DAYTONA_HOME = "/home/daytona";
|
|
@@ -29,3 +31,11 @@ export declare const WHOLE_OUTPUT: {
|
|
|
29
31
|
export declare function resolvePath(path: string): string;
|
|
30
32
|
/** Quotes a word for bash. */
|
|
31
33
|
export declare function quote(word: string): string;
|
|
34
|
+
/** A command and its environment as Runtime runs them: as given for the
|
|
35
|
+
* sandbox owner; for another user through sudo, its environment carried on
|
|
36
|
+
* the command line (sudo resets it) and umask 002, so files it makes in the
|
|
37
|
+
* shared working directory stay writable by the owner's group. */
|
|
38
|
+
export declare function runAs(user: string | undefined, argv: readonly string[], env?: Record<string, string>): {
|
|
39
|
+
argv: string[];
|
|
40
|
+
env?: Record<string, string>;
|
|
41
|
+
};
|
package/dist/daytona/context.js
CHANGED
|
@@ -24,3 +24,27 @@ export function resolvePath(path) {
|
|
|
24
24
|
export function quote(word) {
|
|
25
25
|
return `'${word.replace(/'/g, `'\\''`)}'`;
|
|
26
26
|
}
|
|
27
|
+
/** A command and its environment as Runtime runs them: as given for the
|
|
28
|
+
* sandbox owner; for another user through sudo, its environment carried on
|
|
29
|
+
* the command line (sudo resets it) and umask 002, so files it makes in the
|
|
30
|
+
* shared working directory stay writable by the owner's group. */
|
|
31
|
+
export function runAs(user, argv, env = {}) {
|
|
32
|
+
if (!user)
|
|
33
|
+
return { argv: [...argv], ...(Object.keys(env).length ? { env } : {}) };
|
|
34
|
+
return {
|
|
35
|
+
argv: [
|
|
36
|
+
"sudo",
|
|
37
|
+
"-u",
|
|
38
|
+
user,
|
|
39
|
+
"-H",
|
|
40
|
+
"--",
|
|
41
|
+
"env",
|
|
42
|
+
...Object.entries(env).map(([name, value]) => `${name}=${value}`),
|
|
43
|
+
"sh",
|
|
44
|
+
"-c",
|
|
45
|
+
'umask 002; exec "$@"',
|
|
46
|
+
"sh",
|
|
47
|
+
...argv,
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -13,6 +13,8 @@ export declare const DEFAULT_RESOURCES: {
|
|
|
13
13
|
export declare const DEFAULT_AUTO_STOP_MINUTES = 15;
|
|
14
14
|
/** Daytona's label for a sandbox's code language. */
|
|
15
15
|
export declare const LANGUAGE_LABEL = "code-toolbox-language";
|
|
16
|
+
/** The Linux user given at create, so get() and list() run as it too. */
|
|
17
|
+
export declare const USER_LABEL = "compat.daytona-user";
|
|
16
18
|
export declare enum CodeLanguage {
|
|
17
19
|
PYTHON = "python",
|
|
18
20
|
TYPESCRIPT = "typescript",
|
|
@@ -69,10 +71,28 @@ export type ForkSandboxParams = {
|
|
|
69
71
|
name?: string;
|
|
70
72
|
};
|
|
71
73
|
export interface ListSandboxesQuery {
|
|
74
|
+
/** Daytona's page size; every sandbox is still listed. */
|
|
72
75
|
limit?: number;
|
|
76
|
+
sort?: "name" | "cpu" | "memoryGib" | "diskGib" | "lastActivityAt" | "createdAt";
|
|
77
|
+
order?: "asc" | "desc";
|
|
78
|
+
/** An id prefix, any case. */
|
|
79
|
+
id?: string;
|
|
80
|
+
/** A name prefix, any case. */
|
|
73
81
|
name?: string;
|
|
74
82
|
labels?: Record<string, string>;
|
|
75
83
|
states?: SandboxState[];
|
|
84
|
+
targets?: string[];
|
|
85
|
+
minCpu?: number;
|
|
86
|
+
maxCpu?: number;
|
|
87
|
+
minMemoryGib?: number;
|
|
88
|
+
maxMemoryGib?: number;
|
|
89
|
+
minDiskGib?: number;
|
|
90
|
+
maxDiskGib?: number;
|
|
91
|
+
isRecoverable?: boolean;
|
|
92
|
+
createdAtAfter?: Date;
|
|
93
|
+
createdAtBefore?: Date;
|
|
94
|
+
lastActivityAfter?: Date;
|
|
95
|
+
lastActivityBefore?: Date;
|
|
76
96
|
[other: string]: unknown;
|
|
77
97
|
}
|
|
78
98
|
/** A Daytona snapshot: on Runtime, an image (or a snapshot made with
|