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.
Files changed (94) hide show
  1. package/BLAXEL.md +7 -8
  2. package/DAYTONA.md +40 -35
  3. package/E2B.md +70 -57
  4. package/README.md +3 -3
  5. package/README.src.md +3 -3
  6. package/VERCEL.md +23 -20
  7. package/compatibility-lock.json +63 -84
  8. package/dist/api-defaults.d.ts +1 -1
  9. package/dist/api-defaults.js +1 -1
  10. package/dist/blaxel/lookup.d.ts +2 -1
  11. package/dist/blaxel/lookup.js +2 -1
  12. package/dist/blaxel/sandbox.d.ts +7 -5
  13. package/dist/blaxel/sandbox.js +15 -10
  14. package/dist/cli.js +112 -17
  15. package/dist/client.d.ts +17 -1
  16. package/dist/client.js +46 -3
  17. package/dist/compat/files.js +5 -1
  18. package/dist/compat/lease.d.ts +37 -0
  19. package/dist/compat/lease.js +93 -0
  20. package/dist/credentials.d.ts +9 -0
  21. package/dist/credentials.js +26 -4
  22. package/dist/daytona/computer-use.d.ts +162 -0
  23. package/dist/daytona/computer-use.js +324 -0
  24. package/dist/daytona/context.d.ts +10 -0
  25. package/dist/daytona/context.js +24 -0
  26. package/dist/daytona/daytona.d.ts +20 -0
  27. package/dist/daytona/daytona.js +150 -26
  28. package/dist/daytona/filesystem.js +8 -0
  29. package/dist/daytona/index.d.ts +2 -26
  30. package/dist/daytona/index.js +2 -7
  31. package/dist/daytona/process.d.ts +63 -6
  32. package/dist/daytona/process.js +224 -23
  33. package/dist/daytona/sandbox.d.ts +25 -6
  34. package/dist/daytona/sandbox.js +60 -27
  35. package/dist/e2b/client.d.ts +5 -0
  36. package/dist/e2b/client.js +5 -1
  37. package/dist/e2b/commands.d.ts +15 -6
  38. package/dist/e2b/commands.js +83 -18
  39. package/dist/e2b/errors.d.ts +7 -0
  40. package/dist/e2b/errors.js +20 -0
  41. package/dist/e2b/filesystem.d.ts +2 -1
  42. package/dist/e2b/filesystem.js +43 -6
  43. package/dist/e2b/index.d.ts +2 -2
  44. package/dist/e2b/index.js +1 -1
  45. package/dist/e2b/pty.js +6 -3
  46. package/dist/e2b/sandbox.d.ts +20 -6
  47. package/dist/e2b/sandbox.js +207 -31
  48. package/dist/e2b/users.d.ts +30 -0
  49. package/dist/e2b/users.js +185 -0
  50. package/dist/errors.d.ts +6 -0
  51. package/dist/errors.js +6 -0
  52. package/dist/identity.js +2 -1
  53. package/dist/index.d.ts +3 -1
  54. package/dist/index.js +2 -1
  55. package/dist/mcp.d.ts +2 -1
  56. package/dist/mcp.js +6 -1
  57. package/dist/modal/index.js +6 -2
  58. package/dist/network-cli.js +33 -5
  59. package/dist/products/browser.d.ts +61 -0
  60. package/dist/products/browser.js +27 -0
  61. package/dist/products/desktop.d.ts +4 -3
  62. package/dist/products/desktop.js +7 -32
  63. package/dist/products/images.d.ts +3 -1
  64. package/dist/products/images.js +14 -2
  65. package/dist/products/index.d.ts +2 -0
  66. package/dist/products/index.js +2 -0
  67. package/dist/products/jobs.d.ts +7 -5
  68. package/dist/products/jobs.js +7 -3
  69. package/dist/products/mcp.d.ts +5 -1
  70. package/dist/products/mcp.js +29 -8
  71. package/dist/products/network-products.d.ts +8 -1
  72. package/dist/products/network-products.js +4 -1
  73. package/dist/products/observability.d.ts +1 -1
  74. package/dist/products/private-network.d.ts +22 -0
  75. package/dist/products/private-network.js +10 -0
  76. package/dist/products/secrets.d.ts +2 -2
  77. package/dist/runloop/index.js +6 -2
  78. package/dist/sandbox.d.ts +44 -14
  79. package/dist/sandbox.js +162 -48
  80. package/dist/tar.d.ts +2 -0
  81. package/dist/tar.js +74 -16
  82. package/dist/transport.d.ts +33 -7
  83. package/dist/transport.js +171 -36
  84. package/dist/types.d.ts +59 -11
  85. package/dist/vercel/filesystem.js +3 -1
  86. package/dist/vercel/index.d.ts +1 -7
  87. package/dist/vercel/index.js +1 -3
  88. package/dist/vercel/sandbox-user.d.ts +70 -0
  89. package/dist/vercel/sandbox-user.js +206 -0
  90. package/dist/vercel/sandbox.d.ts +57 -15
  91. package/dist/vercel/sandbox.js +219 -47
  92. package/dist/wait.d.ts +12 -0
  93. package/dist/wait.js +47 -0
  94. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ import type { Command, CommandFinished } from "./command.js";
2
+ import type { RunCommandParams, Sandbox } from "./sandbox.js";
3
+ /** A Linux user or group name Vercel accepts, which is also safe to pass to
4
+ * useradd, groupadd and sudo. */
5
+ export declare function validateName(name: string, kind: string): void;
6
+ /** Thrown when `sandbox.createUser` is called for an existing username. */
7
+ export declare class SandboxUserAlreadyExistsError extends Error {
8
+ readonly username: string;
9
+ constructor(username: string);
10
+ }
11
+ type Signal = {
12
+ signal?: AbortSignal;
13
+ };
14
+ /** A user in a sandbox, made with `sandbox.createUser` or found with
15
+ * `sandbox.asUser`. Commands and file calls run as this user. */
16
+ export declare class SandboxUser {
17
+ #private;
18
+ readonly username: string;
19
+ /** `/root` for root, else `/home/<username>`. */
20
+ readonly homeDir: string;
21
+ /** Use sandbox.createUser or sandbox.asUser. */
22
+ constructor({ sandbox, username }: {
23
+ sandbox: Sandbox;
24
+ username: string;
25
+ });
26
+ /** Runs a command as this user. `sudo: true` runs it as root instead. */
27
+ runCommand(command: string, args?: string[], opts?: {
28
+ signal?: AbortSignal;
29
+ timeoutMs?: number;
30
+ }): Promise<CommandFinished>;
31
+ runCommand(params: RunCommandParams & {
32
+ detached: true;
33
+ }): Promise<Command>;
34
+ runCommand(params: RunCommandParams): Promise<CommandFinished>;
35
+ /** Writes files into this user's home (or absolute paths), then gives them
36
+ * and the directories made for them to this user. */
37
+ writeFiles(files: {
38
+ path: string;
39
+ content: string | Uint8Array;
40
+ mode?: number;
41
+ }[], opts?: Signal): Promise<void>;
42
+ /** The file as this user may read it, as a stream; null when missing. */
43
+ readFile(file: {
44
+ path: string;
45
+ cwd?: string;
46
+ }, opts?: Signal): Promise<NodeJS.ReadableStream | null>;
47
+ /** The file as this user may read it; null when missing. */
48
+ readFileToBuffer(file: {
49
+ path: string;
50
+ cwd?: string;
51
+ }, opts?: Signal): Promise<Buffer | null>;
52
+ /** Copies a file this user may read to the local disk; null when missing. */
53
+ downloadFile(src: {
54
+ path: string;
55
+ cwd?: string;
56
+ }, dst: {
57
+ path: string;
58
+ cwd?: string;
59
+ }, opts?: {
60
+ mkdirRecursive?: boolean;
61
+ signal?: AbortSignal;
62
+ }): Promise<string | null>;
63
+ /** Makes a directory owned by this user. */
64
+ mkDir(path: string, opts?: Signal): Promise<void>;
65
+ addToGroup(groupname: string, opts?: Signal): Promise<void>;
66
+ removeFromGroup(groupname: string, opts?: Signal): Promise<void>;
67
+ }
68
+ /** Runs `cmd args` under sudo; a non-zero exit throws `what(): <stderr>`. */
69
+ export declare function rootOrThrow(sandbox: Sandbox, cmd: string, args: string[], signal: AbortSignal | undefined, what: () => string): Promise<CommandFinished>;
70
+ export {};
@@ -0,0 +1,206 @@
1
+ import { dirname, resolve } from "node:path";
2
+ /* Vercel's multi-user helpers over Runtime's sandbox owner, who has
3
+ passwordless sudo: a user is a real Linux user made with `sudo useradd`,
4
+ and every call as that user is `sudo -u <user>`, exactly as Vercel's own
5
+ SDK does it (checked against @vercel/sandbox 3.5.1). */
6
+ const VALID_NAME = /^[a-z_][a-z0-9_-]*$/;
7
+ const MAX_NAME_LENGTH = 32;
8
+ /** A Linux user or group name Vercel accepts, which is also safe to pass to
9
+ * useradd, groupadd and sudo. */
10
+ export function validateName(name, kind) {
11
+ if (!name)
12
+ throw new Error(`Invalid ${kind}: must not be empty`);
13
+ if (name.length > MAX_NAME_LENGTH)
14
+ throw new Error(`Invalid ${kind} "${name}": must be at most ${MAX_NAME_LENGTH} characters`);
15
+ if (!VALID_NAME.test(name))
16
+ throw new Error(`Invalid ${kind} "${name}": must match ${VALID_NAME} (lowercase letters, digits, hyphens, underscores)`);
17
+ }
18
+ /** Thrown when `sandbox.createUser` is called for an existing username. */
19
+ export class SandboxUserAlreadyExistsError extends Error {
20
+ username;
21
+ constructor(username) {
22
+ super(`Failed to create user "${username}": user already exists`);
23
+ this.name = "SandboxUserAlreadyExistsError";
24
+ this.username = username;
25
+ }
26
+ }
27
+ /** A user in a sandbox, made with `sandbox.createUser` or found with
28
+ * `sandbox.asUser`. Commands and file calls run as this user. */
29
+ export class SandboxUser {
30
+ username;
31
+ /** `/root` for root, else `/home/<username>`. */
32
+ homeDir;
33
+ #sandbox;
34
+ #primaryGroup;
35
+ /** Use sandbox.createUser or sandbox.asUser. */
36
+ constructor({ sandbox, username }) {
37
+ this.#sandbox = sandbox;
38
+ this.username = username;
39
+ this.homeDir = username === "root" ? "/root" : `/home/${username}`;
40
+ }
41
+ /** `sudo -u <user>`, in `cwd` (the user's home by default), with `env`
42
+ * given through `env` so it survives sudo. */
43
+ #wrap(params) {
44
+ const env = Object.entries(params.env ?? {}).map(([key, value]) => `${key}=${value}`);
45
+ return {
46
+ cmd: "sudo",
47
+ args: [
48
+ "-u",
49
+ this.username,
50
+ "--",
51
+ "bash",
52
+ "-c",
53
+ 'cd "$1" || exit 1; shift; exec "$@"',
54
+ "bash",
55
+ params.cwd ?? this.homeDir,
56
+ ...(env.length ? ["env", ...env] : []),
57
+ params.cmd,
58
+ ...(params.args ?? []),
59
+ ],
60
+ };
61
+ }
62
+ #resolve(path) {
63
+ return path.startsWith("/") ? path : `${this.homeDir}/${path}`;
64
+ }
65
+ runCommand(commandOrParams, args, opts = {}) {
66
+ if (typeof commandOrParams === "string")
67
+ return this.#sandbox.runCommand({
68
+ ...this.#wrap({ cmd: commandOrParams, ...(args ? { args } : {}) }),
69
+ ...(opts.signal ? { signal: opts.signal } : {}),
70
+ ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
71
+ });
72
+ const params = commandOrParams;
73
+ if (params.sudo)
74
+ return this.#sandbox.runCommand({ ...params });
75
+ const wrapped = this.#wrap(params);
76
+ return this.#sandbox.runCommand({
77
+ cmd: wrapped.cmd,
78
+ args: wrapped.args,
79
+ ...(params.detached ? { detached: true } : {}),
80
+ ...(params.stdout ? { stdout: params.stdout } : {}),
81
+ ...(params.stderr ? { stderr: params.stderr } : {}),
82
+ ...(params.signal ? { signal: params.signal } : {}),
83
+ ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs } : {}),
84
+ });
85
+ }
86
+ /** Writes files into this user's home (or absolute paths), then gives them
87
+ * and the directories made for them to this user. */
88
+ async writeFiles(files, opts = {}) {
89
+ const absolute = files.map((file) => ({ ...file, path: this.#resolve(file.path) }));
90
+ await this.#sandbox.writeFiles(absolute, opts);
91
+ const paths = absolute.map((file) => file.path);
92
+ if (!paths.length)
93
+ return;
94
+ await this.#chown(paths, `${this.username}:${await this.#group(opts.signal)}`, opts.signal);
95
+ const dirs = this.#ancestorsUnderHome(paths);
96
+ if (dirs.length) {
97
+ const { group } = await this.#sandbox.getDefaultUser(opts);
98
+ await this.#chown(dirs, `${this.username}:${group}`, opts.signal);
99
+ await this.#chmod(dirs, "770", opts.signal);
100
+ }
101
+ }
102
+ /** The file as this user may read it, as a stream; null when missing. */
103
+ async readFile(file, opts = {}) {
104
+ const buffer = await this.#cat(file, opts);
105
+ if (buffer === null)
106
+ return null;
107
+ const { Readable } = await import("node:stream");
108
+ return Readable.from([buffer]);
109
+ }
110
+ /** The file as this user may read it; null when missing. */
111
+ readFileToBuffer(file, opts = {}) {
112
+ return this.#cat(file, opts);
113
+ }
114
+ /** Copies a file this user may read to the local disk; null when missing. */
115
+ async downloadFile(src, dst, opts = {}) {
116
+ const buffer = await this.#cat(src, opts);
117
+ if (buffer === null)
118
+ return null;
119
+ const { mkdir, writeFile } = await import("node:fs/promises");
120
+ const target = resolve(dst.cwd ?? "", dst.path);
121
+ if (opts.mkdirRecursive)
122
+ await mkdir(dirname(target), { recursive: true });
123
+ await writeFile(target, buffer, opts.signal ? { signal: opts.signal } : {});
124
+ return target;
125
+ }
126
+ /** Reads as this user with `base64`, so the user's own permissions apply
127
+ * and binary files arrive whole. */
128
+ async #cat(file, opts) {
129
+ const path = file.path.startsWith("/") ? file.path : `${file.cwd ?? this.homeDir}/${file.path}`;
130
+ const result = await this.runCommand({
131
+ cmd: "base64",
132
+ args: [path],
133
+ ...(opts.signal ? { signal: opts.signal } : {}),
134
+ });
135
+ if (result.exitCode !== 0) {
136
+ const stderr = await result.stderr();
137
+ if (/No such file or directory/i.test(stderr))
138
+ return null;
139
+ throw new Error(`Failed to read ${path}: ${stderr}`);
140
+ }
141
+ return Buffer.from(await result.stdout(), "base64");
142
+ }
143
+ /** Makes a directory owned by this user. */
144
+ async mkDir(path, opts = {}) {
145
+ const absolute = this.#resolve(path);
146
+ await this.#sandbox.mkDir(absolute, opts);
147
+ const dirs = [absolute, ...this.#ancestorsUnderHome([absolute])];
148
+ const { group } = await this.#sandbox.getDefaultUser(opts);
149
+ await this.#chown(dirs, `${this.username}:${group}`, opts.signal);
150
+ await this.#chmod(dirs, "770", opts.signal);
151
+ }
152
+ async addToGroup(groupname, opts = {}) {
153
+ validateName(groupname, "group name");
154
+ await this.#sandbox.addUserToGroup(this.username, groupname, opts);
155
+ }
156
+ async removeFromGroup(groupname, opts = {}) {
157
+ validateName(groupname, "group name");
158
+ await this.#sandbox.removeUserFromGroup(this.username, groupname, opts);
159
+ }
160
+ /** The user's primary group, read from the sandbox once. */
161
+ #group(signal) {
162
+ this.#primaryGroup ??= (async () => {
163
+ const result = await this.#sandbox.runCommand({
164
+ cmd: "id",
165
+ args: ["-gn", this.username],
166
+ ...(signal ? { signal } : {}),
167
+ });
168
+ if (result.exitCode !== 0)
169
+ throw new Error(`Failed to resolve the primary group of "${this.username}": ${await result.stderr()}`);
170
+ const group = (await result.stdout()).trim();
171
+ if (!group)
172
+ throw new Error(`Failed to resolve the primary group of "${this.username}"`);
173
+ return group;
174
+ })().catch((error) => {
175
+ this.#primaryGroup = undefined;
176
+ throw error;
177
+ });
178
+ return this.#primaryGroup;
179
+ }
180
+ async #chown(paths, owner, signal) {
181
+ await rootOrThrow(this.#sandbox, "chown", [owner, ...paths], signal, () => `Failed to set ownership on ${paths.join(", ")}`);
182
+ }
183
+ async #chmod(paths, mode, signal) {
184
+ await rootOrThrow(this.#sandbox, "chmod", [mode, ...paths], signal, () => `Failed to set permissions on ${paths.join(", ")}`);
185
+ }
186
+ /** The directories strictly between this user's home and each path. */
187
+ #ancestorsUnderHome(paths) {
188
+ const dirs = new Set();
189
+ const prefix = `${this.homeDir}/`;
190
+ for (const path of paths) {
191
+ let dir = path.slice(0, path.lastIndexOf("/"));
192
+ while (dir.length > this.homeDir.length && dir.startsWith(prefix)) {
193
+ dirs.add(dir);
194
+ dir = dir.slice(0, dir.lastIndexOf("/"));
195
+ }
196
+ }
197
+ return [...dirs];
198
+ }
199
+ }
200
+ /** Runs `cmd args` under sudo; a non-zero exit throws `what(): <stderr>`. */
201
+ export async function rootOrThrow(sandbox, cmd, args, signal, what) {
202
+ const result = await sandbox.runCommand({ cmd, args, sudo: true, ...(signal ? { signal } : {}) });
203
+ if (result.exitCode !== 0)
204
+ throw new Error(`${what()}: ${await result.stderr()}`);
205
+ return result;
206
+ }
@@ -3,12 +3,15 @@ import type { Runtime } from "../client.js";
3
3
  import { type Credentials, type RuntimeCreate, type RuntimeSandbox, type WithRuntime } from "./client.js";
4
4
  import { Command, CommandFinished } from "./command.js";
5
5
  import { FileSystem } from "./filesystem.js";
6
+ import { SandboxUser } from "./sandbox-user.js";
6
7
  import { Snapshot } from "./snapshot.js";
7
8
  /** Vercel's default machine: 2 vCPUs with 2048 MB each (checked 23 September
8
9
  * 2026). Runtime gives 2 vCPU and 4096 MiB. */
9
10
  export declare const DEFAULT_VCPUS = 2;
10
11
  export declare const MEMORY_MIB_PER_VCPU = 2048;
11
- /** Vercel's default timeout, 5 minutes. */
12
+ /** Vercel's default timeout, 5 minutes. Not sent: a sandbox created with no
13
+ * timeout has no time limit on Runtime, running while it works and pausing
14
+ * when idle (0300). Kept for code that imports it. */
12
15
  export declare const DEFAULT_TIMEOUT_MS = 300000;
13
16
  /** Vercel's working directory; `/workspace` on Runtime. */
14
17
  export declare const HOME = "/vercel/sandbox";
@@ -39,7 +42,10 @@ export interface CreateSandboxParams extends Credentials {
39
42
  /** Shared at public HTTPS addresses (Runtime previews); `domain(port)`
40
43
  * answers from them. */
41
44
  ports?: number[];
42
- /** Milliseconds. Default 300 000; 60 s to 1 hour on Runtime. */
45
+ /** Milliseconds, at least 60 s: when it stops. Leave it out for no time
46
+ * limit: it runs while it works and pauses when idle. Over an hour, the
47
+ * hour-long limit is extended while this object lives, up to the timeout
48
+ * (`sandbox.timeout`). */
43
49
  timeout?: number;
44
50
  resources?: {
45
51
  vcpus: number;
@@ -131,13 +137,16 @@ export declare class Sandbox {
131
137
  get updatedAt(): Date;
132
138
  get cwd(): string;
133
139
  get status(): "pending" | "running" | "stopping" | "stopped";
134
- /** Milliseconds. */
140
+ /** Milliseconds from the start of the session: the timeout given at
141
+ * create, with every extension, or the time limit of a sandbox found by
142
+ * name. 0 for one with no time limit, which runs while it works. */
135
143
  get timeout(): number;
136
144
  get expiresAt(): Date;
137
145
  get tags(): Record<string, string>;
138
146
  get sourceSnapshotId(): string | undefined;
139
147
  /** Creates a sandbox and waits until it runs. Vercel's defaults: 2 vCPUs,
140
- * 2048 MiB per vCPU, a 5-minute timeout, persistent. Funding is left to
148
+ * 2048 MiB per vCPU, persistent. With no `timeout` it has no time limit: it
149
+ * runs while it works and pauses when idle. A `timeout` is kept as given. Funding is left to
141
150
  * Runtime: the free trial while the account has trial time, then prepaid
142
151
  * credit, exactly as withruntime's own create. */
143
152
  static create(params?: CreateSandboxParams): Promise<Sandbox & AsyncDisposable>;
@@ -162,14 +171,23 @@ export declare class Sandbox {
162
171
  sourceSandbox: string;
163
172
  }): Promise<Sandbox & AsyncDisposable>;
164
173
  /** Running and stopped (paused) sandboxes, oldest first, with Vercel's
165
- * paginator: `for await (const s of await Sandbox.list())`. */
174
+ * paginator: `for await (const s of await Sandbox.list())`. A name prefix,
175
+ * creation-time bounds (`since`, `until`, epoch ms), `sortBy` and
176
+ * `sortOrder`, and a `cursor` from an earlier page are applied here, over
177
+ * every sandbox the key sees. */
166
178
  static list(params?: {
167
179
  tags?: Record<string, string>;
168
180
  limit?: number;
181
+ namePrefix?: string;
182
+ sortBy?: "createdAt" | "name" | "statusUpdatedAt";
183
+ sortOrder?: "asc" | "desc";
184
+ cursor?: string;
185
+ since?: number | Date;
186
+ until?: number | Date;
169
187
  signal?: AbortSignal;
170
188
  } & Credentials & {
171
189
  withruntime?: WithRuntime;
172
- } & Record<string, unknown>): Promise<{
190
+ }): Promise<{
173
191
  sandboxes: ListItem[];
174
192
  pagination: {
175
193
  count: number;
@@ -256,8 +274,10 @@ export declare class Sandbox {
256
274
  signal?: AbortSignal;
257
275
  }): Promise<void>;
258
276
  [Symbol.asyncDispose](): Promise<void>;
259
- /** Moves the end `duration` milliseconds later (at most an hour ahead of
260
- * now on Runtime). */
277
+ /** Moves the end `duration` milliseconds later. The time limit moves at
278
+ * once as far as it may (an hour ahead of now); past that it is extended
279
+ * while this object lives. A sandbox with no time limit has no end to
280
+ * move, and nothing changes. */
261
281
  extendTimeout(duration: number, opts?: {
262
282
  signal?: AbortSignal;
263
283
  }): Promise<void>;
@@ -267,7 +287,8 @@ export declare class Sandbox {
267
287
  }): Promise<NetworkPolicy>;
268
288
  /** Changes what Runtime can change on a sandbox: `timeout`, `ports`,
269
289
  * `networkPolicy` and `snapshotExpiration`. Anything else is refused before
270
- * anything changes. */
290
+ * anything changes. A `timeout` on a sandbox with no time limit changes
291
+ * nothing: it runs while it works. */
271
292
  update(params: {
272
293
  timeout?: number;
273
294
  ports?: number[];
@@ -283,16 +304,37 @@ export declare class Sandbox {
283
304
  expiration?: number;
284
305
  signal?: AbortSignal;
285
306
  }): Promise<Snapshot>;
307
+ /** Refused: Vercel hands back a token scoped to this one shell, and
308
+ * Runtime's terminal opens with the account key, which must not leave the
309
+ * server. */
286
310
  openInteractive(): Promise<never>;
287
- getDefaultUser(): Promise<{
311
+ /** The user commands run as, `runtime`, and its group. */
312
+ getDefaultUser(_opts?: {
313
+ signal?: AbortSignal;
314
+ }): Promise<{
288
315
  username: string;
289
316
  group: string;
290
317
  }>;
291
- createUser(): Promise<never>;
292
- asUser(): never;
293
- createGroup(): Promise<never>;
294
- addUserToGroup(): Promise<never>;
295
- removeUserFromGroup(): Promise<never>;
318
+ /** Makes a Linux user with its own home, as Vercel does: `useradd -m`,
319
+ * then the home given to the user and the sandbox's group, mode 770. */
320
+ createUser(username: string, opts?: {
321
+ signal?: AbortSignal;
322
+ }): Promise<SandboxUser>;
323
+ /** A handle for an existing user, such as `root`. */
324
+ asUser(username: "root" | (string & {})): SandboxUser;
325
+ /** Makes a group and its shared directory, `/shared/<group>`, setgid 2770. */
326
+ createGroup(groupname: string, opts?: {
327
+ signal?: AbortSignal;
328
+ }): Promise<{
329
+ groupname: string;
330
+ sharedDir: string;
331
+ }>;
332
+ addUserToGroup(username: string, groupname: string, opts?: {
333
+ signal?: AbortSignal;
334
+ }): Promise<void>;
335
+ removeUserFromGroup(username: string, groupname: string, opts?: {
336
+ signal?: AbortSignal;
337
+ }): Promise<void>;
296
338
  currentSession(): never;
297
339
  listSessions(): Promise<never>;
298
340
  listSnapshots(): Promise<never>;