@vercel/sandbox 0.0.7 → 0.0.9
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/api-client/api-client.d.ts +81 -8
- package/dist/api-client/api-client.js +34 -14
- package/dist/api-client/file-writer.d.ts +52 -0
- package/dist/api-client/file-writer.js +62 -0
- package/dist/api-client/index.d.ts +1 -0
- package/dist/api-client/index.js +15 -0
- package/dist/api-client/validators.d.ts +343 -50
- package/dist/api-client/validators.js +37 -16
- package/dist/command.d.ts +18 -5
- package/dist/command.js +27 -6
- package/dist/sandbox.d.ts +25 -15
- package/dist/sandbox.js +28 -14
- package/dist/utils/resolveSignal.d.ts +13 -0
- package/dist/utils/resolveSignal.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/src/api-client/api-client.ts +63 -29
- package/src/api-client/file-writer.ts +90 -0
- package/src/api-client/index.ts +1 -0
- package/src/api-client/validators.ts +45 -15
- package/src/command.test.ts +39 -16
- package/src/command.ts +33 -10
- package/src/sandbox.ts +53 -23
- package/src/utils/resolveSignal.ts +24 -0
- package/src/version.ts +1 -1
package/dist/sandbox.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SandboxData, SandboxRouteData } from "./api-client";
|
|
2
|
+
import type { Writable } from "stream";
|
|
2
3
|
import { APIClient } from "./api-client";
|
|
3
|
-
import { Command, CommandFinished } from "./command";
|
|
4
|
+
import { Command, type CommandFinished } from "./command";
|
|
4
5
|
import { type Credentials } from "./utils/get-credentials";
|
|
5
6
|
/** @inline */
|
|
6
7
|
export interface CreateSandboxParams {
|
|
@@ -8,10 +9,16 @@ export interface CreateSandboxParams {
|
|
|
8
9
|
* The source of the sandbox.
|
|
9
10
|
*
|
|
10
11
|
* Omit this parameter start a sandbox without a source.
|
|
12
|
+
*
|
|
13
|
+
* For git sources:
|
|
14
|
+
* - `depth`: Creates shallow clones with limited commit history (minimum: 1)
|
|
15
|
+
* - `revision`: Clones and checks out a specific commit, branch, or tag
|
|
11
16
|
*/
|
|
12
17
|
source?: {
|
|
13
18
|
type: "git";
|
|
14
19
|
url: string;
|
|
20
|
+
depth?: number;
|
|
21
|
+
revision?: string;
|
|
15
22
|
} | {
|
|
16
23
|
type: "tarball";
|
|
17
24
|
url: string;
|
|
@@ -33,6 +40,11 @@ export interface CreateSandboxParams {
|
|
|
33
40
|
resources?: {
|
|
34
41
|
vcpus: number;
|
|
35
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* The runtime of the sandbox, currently only `node22` and `python3.13` are supported.
|
|
45
|
+
* If not specified, the default runtime `node22` will be used.
|
|
46
|
+
*/
|
|
47
|
+
runtime?: "node22" | "python3.13";
|
|
36
48
|
}
|
|
37
49
|
/** @inline */
|
|
38
50
|
interface GetSandboxParams {
|
|
@@ -91,14 +103,15 @@ export declare class Sandbox {
|
|
|
91
103
|
* Routes from ports to subdomains.
|
|
92
104
|
/* @hidden
|
|
93
105
|
*/
|
|
94
|
-
readonly routes:
|
|
95
|
-
subdomain: string;
|
|
96
|
-
port: number;
|
|
97
|
-
}[];
|
|
106
|
+
readonly routes: SandboxRouteData[];
|
|
98
107
|
/**
|
|
99
108
|
* Unique ID of this sandbox.
|
|
100
109
|
*/
|
|
101
|
-
|
|
110
|
+
get sandboxId(): string;
|
|
111
|
+
/**
|
|
112
|
+
* Data about this sandbox.
|
|
113
|
+
*/
|
|
114
|
+
private readonly sandbox;
|
|
102
115
|
/**
|
|
103
116
|
* Create a new sandbox.
|
|
104
117
|
*
|
|
@@ -120,13 +133,10 @@ export declare class Sandbox {
|
|
|
120
133
|
* @param routes - Port-to-subdomain mappings for exposed ports
|
|
121
134
|
* @param sandboxId - Unique identifier for the sandbox
|
|
122
135
|
*/
|
|
123
|
-
constructor({ client, routes,
|
|
136
|
+
constructor({ client, routes, sandbox, }: {
|
|
124
137
|
client: APIClient;
|
|
125
|
-
routes:
|
|
126
|
-
|
|
127
|
-
port: number;
|
|
128
|
-
}[];
|
|
129
|
-
sandboxId: string;
|
|
138
|
+
routes: SandboxRouteData[];
|
|
139
|
+
sandbox: SandboxData;
|
|
130
140
|
});
|
|
131
141
|
/**
|
|
132
142
|
* Get a previously run command by its ID.
|
|
@@ -134,7 +144,7 @@ export declare class Sandbox {
|
|
|
134
144
|
* @param cmdId - ID of the command to retrieve
|
|
135
145
|
* @returns A {@link Command} instance representing the command
|
|
136
146
|
*/
|
|
137
|
-
getCommand(cmdId: string): Command
|
|
147
|
+
getCommand(cmdId: string): Promise<Command>;
|
|
138
148
|
/**
|
|
139
149
|
* Start executing a command in this sandbox.
|
|
140
150
|
*
|
|
@@ -181,7 +191,7 @@ export declare class Sandbox {
|
|
|
181
191
|
*/
|
|
182
192
|
writeFiles(files: {
|
|
183
193
|
path: string;
|
|
184
|
-
stream:
|
|
194
|
+
stream: Buffer;
|
|
185
195
|
}[]): Promise<void>;
|
|
186
196
|
/**
|
|
187
197
|
* Get the public domain of a port of this sandbox.
|
package/dist/sandbox.js
CHANGED
|
@@ -11,6 +11,12 @@ const get_credentials_1 = require("./utils/get-credentials");
|
|
|
11
11
|
* @hideconstructor
|
|
12
12
|
*/
|
|
13
13
|
class Sandbox {
|
|
14
|
+
/**
|
|
15
|
+
* Unique ID of this sandbox.
|
|
16
|
+
*/
|
|
17
|
+
get sandboxId() {
|
|
18
|
+
return this.sandbox.id;
|
|
19
|
+
}
|
|
14
20
|
/**
|
|
15
21
|
* Create a new sandbox.
|
|
16
22
|
*
|
|
@@ -29,10 +35,11 @@ class Sandbox {
|
|
|
29
35
|
ports: params?.ports ?? [],
|
|
30
36
|
timeout: params?.timeout,
|
|
31
37
|
resources: params?.resources,
|
|
38
|
+
runtime: params?.runtime,
|
|
32
39
|
});
|
|
33
40
|
return new Sandbox({
|
|
34
41
|
client,
|
|
35
|
-
|
|
42
|
+
sandbox: sandbox.json.sandbox,
|
|
36
43
|
routes: sandbox.json.routes,
|
|
37
44
|
});
|
|
38
45
|
}
|
|
@@ -48,10 +55,13 @@ class Sandbox {
|
|
|
48
55
|
teamId: credentials.teamId,
|
|
49
56
|
token: credentials.token,
|
|
50
57
|
});
|
|
58
|
+
const sandbox = await client.getSandbox({
|
|
59
|
+
sandboxId: params.sandboxId,
|
|
60
|
+
});
|
|
51
61
|
return new Sandbox({
|
|
52
62
|
client,
|
|
53
|
-
|
|
54
|
-
routes:
|
|
63
|
+
sandbox: sandbox.json.sandbox,
|
|
64
|
+
routes: sandbox.json.routes,
|
|
55
65
|
});
|
|
56
66
|
}
|
|
57
67
|
/**
|
|
@@ -61,10 +71,10 @@ class Sandbox {
|
|
|
61
71
|
* @param routes - Port-to-subdomain mappings for exposed ports
|
|
62
72
|
* @param sandboxId - Unique identifier for the sandbox
|
|
63
73
|
*/
|
|
64
|
-
constructor({ client, routes,
|
|
74
|
+
constructor({ client, routes, sandbox, }) {
|
|
65
75
|
this.client = client;
|
|
66
76
|
this.routes = routes;
|
|
67
|
-
this.
|
|
77
|
+
this.sandbox = sandbox;
|
|
68
78
|
}
|
|
69
79
|
/**
|
|
70
80
|
* Get a previously run command by its ID.
|
|
@@ -72,11 +82,15 @@ class Sandbox {
|
|
|
72
82
|
* @param cmdId - ID of the command to retrieve
|
|
73
83
|
* @returns A {@link Command} instance representing the command
|
|
74
84
|
*/
|
|
75
|
-
getCommand(cmdId) {
|
|
85
|
+
async getCommand(cmdId) {
|
|
86
|
+
const command = await this.client.getCommand({
|
|
87
|
+
sandboxId: this.sandbox.id,
|
|
88
|
+
cmdId,
|
|
89
|
+
});
|
|
76
90
|
return new command_1.Command({
|
|
77
91
|
client: this.client,
|
|
78
|
-
sandboxId: this.
|
|
79
|
-
|
|
92
|
+
sandboxId: this.sandbox.id,
|
|
93
|
+
cmd: command.json.command,
|
|
80
94
|
});
|
|
81
95
|
}
|
|
82
96
|
async runCommand(commandOrParams, args) {
|
|
@@ -93,7 +107,7 @@ class Sandbox {
|
|
|
93
107
|
*/
|
|
94
108
|
async _runCommand(params) {
|
|
95
109
|
const commandResponse = await this.client.runCommand({
|
|
96
|
-
sandboxId: this.
|
|
110
|
+
sandboxId: this.sandbox.id,
|
|
97
111
|
command: params.cmd,
|
|
98
112
|
args: params.args ?? [],
|
|
99
113
|
cwd: params.cwd,
|
|
@@ -101,8 +115,8 @@ class Sandbox {
|
|
|
101
115
|
});
|
|
102
116
|
const command = new command_1.Command({
|
|
103
117
|
client: this.client,
|
|
104
|
-
sandboxId: this.
|
|
105
|
-
|
|
118
|
+
sandboxId: this.sandbox.id,
|
|
119
|
+
cmd: commandResponse.json.command,
|
|
106
120
|
});
|
|
107
121
|
if (params.stdout || params.stderr) {
|
|
108
122
|
(async () => {
|
|
@@ -125,7 +139,7 @@ class Sandbox {
|
|
|
125
139
|
*/
|
|
126
140
|
async mkDir(path) {
|
|
127
141
|
await this.client.mkDir({
|
|
128
|
-
sandboxId: this.
|
|
142
|
+
sandboxId: this.sandbox.id,
|
|
129
143
|
path: path,
|
|
130
144
|
});
|
|
131
145
|
}
|
|
@@ -137,7 +151,7 @@ class Sandbox {
|
|
|
137
151
|
*/
|
|
138
152
|
async writeFiles(files) {
|
|
139
153
|
return this.client.writeFiles({
|
|
140
|
-
sandboxId: this.
|
|
154
|
+
sandboxId: this.sandbox.id,
|
|
141
155
|
files: files,
|
|
142
156
|
});
|
|
143
157
|
}
|
|
@@ -164,7 +178,7 @@ class Sandbox {
|
|
|
164
178
|
*/
|
|
165
179
|
async stop() {
|
|
166
180
|
await this.client.stopSandbox({
|
|
167
|
-
sandboxId: this.
|
|
181
|
+
sandboxId: this.sandbox.id,
|
|
168
182
|
});
|
|
169
183
|
}
|
|
170
184
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const linuxSignalMapping: {
|
|
2
|
+
readonly SIGHUP: 1;
|
|
3
|
+
readonly SIGINT: 2;
|
|
4
|
+
readonly SIGQUIT: 3;
|
|
5
|
+
readonly SIGKILL: 9;
|
|
6
|
+
readonly SIGTERM: 15;
|
|
7
|
+
readonly SIGCONT: 18;
|
|
8
|
+
readonly SIGSTOP: 19;
|
|
9
|
+
};
|
|
10
|
+
type CommonLinuxSignals = keyof typeof linuxSignalMapping;
|
|
11
|
+
export type Signal = CommonLinuxSignals | number;
|
|
12
|
+
export declare function resolveSignal(signal: Signal): number;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveSignal = resolveSignal;
|
|
4
|
+
const linuxSignalMapping = {
|
|
5
|
+
SIGHUP: 1,
|
|
6
|
+
SIGINT: 2,
|
|
7
|
+
SIGQUIT: 3,
|
|
8
|
+
SIGKILL: 9,
|
|
9
|
+
SIGTERM: 15,
|
|
10
|
+
SIGCONT: 18,
|
|
11
|
+
SIGSTOP: 19,
|
|
12
|
+
};
|
|
13
|
+
function resolveSignal(signal) {
|
|
14
|
+
if (typeof signal === "number") {
|
|
15
|
+
return signal;
|
|
16
|
+
}
|
|
17
|
+
if (signal in linuxSignalMapping) {
|
|
18
|
+
return linuxSignalMapping[signal];
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`Unknown signal name: ${String(signal)}`);
|
|
21
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.0.
|
|
1
|
+
export declare const VERSION = "0.0.9";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/sandbox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"lru-cache": "11.1.0",
|
|
16
16
|
"ms": "2.1.3",
|
|
17
17
|
"node-fetch": "2.6.11",
|
|
18
|
+
"tar-stream": "3.1.7",
|
|
19
|
+
"zlib": "1.0.5",
|
|
18
20
|
"zod": "3.24.4"
|
|
19
21
|
},
|
|
20
22
|
"devDependencies": {
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
"@types/ms": "2.1.0",
|
|
24
26
|
"@types/node": "22.15.12",
|
|
25
27
|
"@types/node-fetch": "2.6.12",
|
|
28
|
+
"@types/tar-stream": "3.1.4",
|
|
26
29
|
"dotenv": "16.5.0",
|
|
27
30
|
"typedoc": "0.28.5",
|
|
28
31
|
"typescript": "5.8.3",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import FormData from "form-data";
|
|
2
1
|
import {
|
|
3
2
|
BaseClient,
|
|
4
3
|
parseOrThrow,
|
|
@@ -6,16 +5,15 @@ import {
|
|
|
6
5
|
type RequestParams,
|
|
7
6
|
} from "./base-client";
|
|
8
7
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
SandboxAndRoutesResponse,
|
|
9
|
+
SandboxResponse,
|
|
10
|
+
CommandResponse,
|
|
11
|
+
CommandFinishedResponse,
|
|
12
|
+
EmptyResponse,
|
|
13
13
|
LogLine,
|
|
14
|
-
StoppedSandbox,
|
|
15
|
-
WrittenFile,
|
|
16
14
|
} from "./validators";
|
|
17
|
-
import { Readable } from "stream";
|
|
18
15
|
import { APIError } from "./api-error";
|
|
16
|
+
import { FileWriter } from "./file-writer";
|
|
19
17
|
import { LRUCache } from "lru-cache";
|
|
20
18
|
import { VERSION } from "../version";
|
|
21
19
|
import { z } from "zod";
|
|
@@ -57,15 +55,25 @@ export class APIClient extends BaseClient {
|
|
|
57
55
|
});
|
|
58
56
|
}
|
|
59
57
|
|
|
58
|
+
async getSandbox(params: { sandboxId: string }) {
|
|
59
|
+
return parseOrThrow(
|
|
60
|
+
SandboxAndRoutesResponse,
|
|
61
|
+
await this.request(`/v1/sandboxes/${params.sandboxId}`),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
async createSandbox(params: {
|
|
61
66
|
ports?: number[];
|
|
62
67
|
projectId: string;
|
|
63
|
-
source?:
|
|
68
|
+
source?:
|
|
69
|
+
| { type: "git"; url: string; depth?: number; revision?: string }
|
|
70
|
+
| { type: "tarball"; url: string };
|
|
64
71
|
timeout?: number;
|
|
65
72
|
resources?: { vcpus: number };
|
|
73
|
+
runtime?: "node22" | "python3.13";
|
|
66
74
|
}) {
|
|
67
75
|
return parseOrThrow(
|
|
68
|
-
|
|
76
|
+
SandboxAndRoutesResponse,
|
|
69
77
|
await this.request("/v1/sandboxes", {
|
|
70
78
|
method: "POST",
|
|
71
79
|
body: JSON.stringify({
|
|
@@ -74,6 +82,7 @@ export class APIClient extends BaseClient {
|
|
|
74
82
|
source: params.source,
|
|
75
83
|
timeout: params.timeout,
|
|
76
84
|
resources: params.resources,
|
|
85
|
+
runtime: params.runtime,
|
|
77
86
|
}),
|
|
78
87
|
}),
|
|
79
88
|
);
|
|
@@ -87,7 +96,7 @@ export class APIClient extends BaseClient {
|
|
|
87
96
|
env: Record<string, string>;
|
|
88
97
|
}) {
|
|
89
98
|
return parseOrThrow(
|
|
90
|
-
|
|
99
|
+
CommandResponse,
|
|
91
100
|
await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
|
|
92
101
|
method: "POST",
|
|
93
102
|
body: JSON.stringify({
|
|
@@ -104,12 +113,12 @@ export class APIClient extends BaseClient {
|
|
|
104
113
|
sandboxId: string;
|
|
105
114
|
cmdId: string;
|
|
106
115
|
wait: true;
|
|
107
|
-
}): Promise<Parsed<z.infer<typeof
|
|
116
|
+
}): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;
|
|
108
117
|
async getCommand(params: {
|
|
109
118
|
sandboxId: string;
|
|
110
119
|
cmdId: string;
|
|
111
120
|
wait?: boolean;
|
|
112
|
-
}): Promise<Parsed<z.infer<typeof
|
|
121
|
+
}): Promise<Parsed<z.infer<typeof CommandResponse>>>;
|
|
113
122
|
async getCommand(params: {
|
|
114
123
|
sandboxId: string;
|
|
115
124
|
cmdId: string;
|
|
@@ -117,14 +126,14 @@ export class APIClient extends BaseClient {
|
|
|
117
126
|
}) {
|
|
118
127
|
return params.wait
|
|
119
128
|
? parseOrThrow(
|
|
120
|
-
|
|
129
|
+
CommandFinishedResponse,
|
|
121
130
|
await this.request(
|
|
122
131
|
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
123
132
|
{ query: { wait: "true" } },
|
|
124
133
|
),
|
|
125
134
|
)
|
|
126
135
|
: parseOrThrow(
|
|
127
|
-
|
|
136
|
+
CommandResponse,
|
|
128
137
|
await this.request(
|
|
129
138
|
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
130
139
|
),
|
|
@@ -133,7 +142,7 @@ export class APIClient extends BaseClient {
|
|
|
133
142
|
|
|
134
143
|
async mkDir(params: { sandboxId: string; path: string; cwd?: string }) {
|
|
135
144
|
return parseOrThrow(
|
|
136
|
-
|
|
145
|
+
EmptyResponse,
|
|
137
146
|
await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
|
|
138
147
|
method: "POST",
|
|
139
148
|
body: JSON.stringify({ path: params.path, cwd: params.cwd }),
|
|
@@ -141,24 +150,32 @@ export class APIClient extends BaseClient {
|
|
|
141
150
|
);
|
|
142
151
|
}
|
|
143
152
|
|
|
153
|
+
getFileWriter(params: { sandboxId: string }) {
|
|
154
|
+
const writer = new FileWriter();
|
|
155
|
+
return {
|
|
156
|
+
response: this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "content-type": "application/gzip" },
|
|
159
|
+
body: writer.readable,
|
|
160
|
+
}),
|
|
161
|
+
writer,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
144
165
|
async writeFiles(params: {
|
|
145
166
|
sandboxId: string;
|
|
146
|
-
files: { path: string; stream:
|
|
167
|
+
files: { path: string; stream: Buffer }[];
|
|
147
168
|
}) {
|
|
148
|
-
const
|
|
169
|
+
const { writer, response } = this.getFileWriter({
|
|
170
|
+
sandboxId: params.sandboxId,
|
|
171
|
+
});
|
|
149
172
|
|
|
150
173
|
for (const file of params.files) {
|
|
151
|
-
|
|
174
|
+
await writer.addFile({ name: file.path, content: file.stream });
|
|
152
175
|
}
|
|
153
176
|
|
|
154
|
-
await
|
|
155
|
-
|
|
156
|
-
await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers: { ...formData.getHeaders() },
|
|
159
|
-
body: formData,
|
|
160
|
-
}),
|
|
161
|
-
);
|
|
177
|
+
await writer.end();
|
|
178
|
+
await parseOrThrow(EmptyResponse, await response);
|
|
162
179
|
}
|
|
163
180
|
|
|
164
181
|
async readFile(params: {
|
|
@@ -181,6 +198,23 @@ export class APIClient extends BaseClient {
|
|
|
181
198
|
return response.body;
|
|
182
199
|
}
|
|
183
200
|
|
|
201
|
+
async killCommand(params: {
|
|
202
|
+
sandboxId: string;
|
|
203
|
+
commandId: string;
|
|
204
|
+
signal: number;
|
|
205
|
+
}) {
|
|
206
|
+
return parseOrThrow(
|
|
207
|
+
CommandResponse,
|
|
208
|
+
await this.request(
|
|
209
|
+
`/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,
|
|
210
|
+
{
|
|
211
|
+
method: "POST",
|
|
212
|
+
body: JSON.stringify({ signal: params.signal }),
|
|
213
|
+
},
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
184
218
|
async *getLogs(params: {
|
|
185
219
|
sandboxId: string;
|
|
186
220
|
cmdId: string;
|
|
@@ -215,10 +249,10 @@ export class APIClient extends BaseClient {
|
|
|
215
249
|
|
|
216
250
|
async stopSandbox(params: {
|
|
217
251
|
sandboxId: string;
|
|
218
|
-
}): Promise<Parsed<z.infer<typeof
|
|
252
|
+
}): Promise<Parsed<z.infer<typeof SandboxResponse>>> {
|
|
219
253
|
const url = `/v1/sandboxes/${params.sandboxId}/stop`;
|
|
220
254
|
return parseOrThrow(
|
|
221
|
-
|
|
255
|
+
SandboxResponse,
|
|
222
256
|
await this.request(url, { method: "POST" }),
|
|
223
257
|
);
|
|
224
258
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import zlib from "zlib";
|
|
2
|
+
import tar, { type Pack } from "tar-stream";
|
|
3
|
+
import { Readable } from "stream";
|
|
4
|
+
|
|
5
|
+
interface FileBuffer {
|
|
6
|
+
/**
|
|
7
|
+
* The name (path) of the file to write.
|
|
8
|
+
*/
|
|
9
|
+
name: string;
|
|
10
|
+
/**
|
|
11
|
+
* The content of the file as a Buffer.
|
|
12
|
+
*/
|
|
13
|
+
content: Buffer;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface FileStream {
|
|
17
|
+
/**
|
|
18
|
+
* The name (path) of the file to write.
|
|
19
|
+
*/
|
|
20
|
+
name: string;
|
|
21
|
+
/**
|
|
22
|
+
* A Readable stream to consume the content of the file.
|
|
23
|
+
*/
|
|
24
|
+
content: Readable;
|
|
25
|
+
/**
|
|
26
|
+
* The expected size of the file. This is required to write
|
|
27
|
+
* the header of the compressed file.
|
|
28
|
+
*/
|
|
29
|
+
size: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Allows to create a Readable stream with methods to write files
|
|
34
|
+
* to it and to finish it. Files written are compressed together
|
|
35
|
+
* and gzipped in the stream.
|
|
36
|
+
*/
|
|
37
|
+
export class FileWriter {
|
|
38
|
+
public readable: Readable;
|
|
39
|
+
private pack: Pack;
|
|
40
|
+
|
|
41
|
+
constructor() {
|
|
42
|
+
const gzip = zlib.createGzip();
|
|
43
|
+
this.pack = tar.pack();
|
|
44
|
+
this.readable = this.pack.pipe(gzip);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Allows to add a file to the stream. Size is required to write
|
|
49
|
+
* the tarball header so when content is a stream it must be
|
|
50
|
+
* provided.
|
|
51
|
+
*
|
|
52
|
+
* Returns a Promise resolved once the file is written in the
|
|
53
|
+
* stream.
|
|
54
|
+
*/
|
|
55
|
+
async addFile(file: FileBuffer | FileStream) {
|
|
56
|
+
return new Promise<void>((resolve, reject) => {
|
|
57
|
+
const entry = this.pack.entry(
|
|
58
|
+
"size" in file
|
|
59
|
+
? { name: file.name, size: file.size }
|
|
60
|
+
: { name: file.name, size: file.content.length },
|
|
61
|
+
(error) => {
|
|
62
|
+
if (error) {
|
|
63
|
+
return reject(error);
|
|
64
|
+
} else {
|
|
65
|
+
resolve();
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (file.content instanceof Readable) {
|
|
71
|
+
file.content.pipe(entry);
|
|
72
|
+
} else {
|
|
73
|
+
entry.end(file.content);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Allows to finish the stream returning a Promise that will
|
|
80
|
+
* resolve once the readable is effectively closed or
|
|
81
|
+
* errored.
|
|
82
|
+
*/
|
|
83
|
+
async end() {
|
|
84
|
+
return new Promise<void>((resolve, reject) => {
|
|
85
|
+
this.readable.on("error", reject);
|
|
86
|
+
this.readable.on("end", resolve);
|
|
87
|
+
this.pack.finalize();
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/api-client/index.ts
CHANGED
|
@@ -1,36 +1,66 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export type SandboxData = z.infer<typeof Sandbox>;
|
|
4
|
+
|
|
5
|
+
export const Sandbox = z.object({
|
|
6
|
+
id: z.string(),
|
|
7
|
+
memory: z.number(),
|
|
8
|
+
vcpus: z.number(),
|
|
9
|
+
region: z.string(),
|
|
10
|
+
runtime: z.string(),
|
|
11
|
+
timeout: z.number(),
|
|
12
|
+
status: z.enum(["pending", "running", "stopping", "stopped", "failed"]),
|
|
13
|
+
requestedAt: z.number(),
|
|
14
|
+
startedAt: z.number().optional(),
|
|
15
|
+
requestedStopAt: z.number().optional(),
|
|
16
|
+
stoppedAt: z.number().optional(),
|
|
17
|
+
duration: z.number().optional(),
|
|
18
|
+
createdAt: z.number(),
|
|
19
|
+
updatedAt: z.number(),
|
|
6
20
|
});
|
|
7
21
|
|
|
8
|
-
export
|
|
9
|
-
|
|
22
|
+
export type SandboxRouteData = z.infer<typeof SandboxRoute>;
|
|
23
|
+
|
|
24
|
+
export const SandboxRoute = z.object({
|
|
25
|
+
url: z.string(),
|
|
26
|
+
subdomain: z.string(),
|
|
27
|
+
port: z.number(),
|
|
10
28
|
});
|
|
11
29
|
|
|
30
|
+
export type CommandData = z.infer<typeof Command>;
|
|
31
|
+
|
|
12
32
|
export const Command = z.object({
|
|
33
|
+
id: z.string(),
|
|
34
|
+
name: z.string(),
|
|
13
35
|
args: z.array(z.string()),
|
|
14
|
-
cmdId: z.string(),
|
|
15
36
|
cwd: z.string(),
|
|
37
|
+
sandboxId: z.string(),
|
|
16
38
|
exitCode: z.number().nullable(),
|
|
17
|
-
|
|
39
|
+
startedAt: z.number(),
|
|
18
40
|
});
|
|
19
41
|
|
|
20
|
-
|
|
21
|
-
args: z.array(z.string()),
|
|
22
|
-
cmdId: z.string(),
|
|
23
|
-
cwd: z.string(),
|
|
42
|
+
const CommandFinished = Command.extend({
|
|
24
43
|
exitCode: z.number(),
|
|
25
|
-
name: z.string(),
|
|
26
44
|
});
|
|
27
45
|
|
|
28
|
-
export const
|
|
46
|
+
export const SandboxResponse = z.object({
|
|
47
|
+
sandbox: Sandbox,
|
|
48
|
+
});
|
|
29
49
|
|
|
30
|
-
export const
|
|
31
|
-
|
|
50
|
+
export const SandboxAndRoutesResponse = SandboxResponse.extend({
|
|
51
|
+
routes: z.array(SandboxRoute),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const CommandResponse = z.object({
|
|
55
|
+
command: Command,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const CommandFinishedResponse = z.object({
|
|
59
|
+
command: CommandFinished,
|
|
32
60
|
});
|
|
33
61
|
|
|
62
|
+
export const EmptyResponse = z.object({});
|
|
63
|
+
|
|
34
64
|
export const LogLine = z.object({
|
|
35
65
|
stream: z.enum(["stdout", "stderr"]),
|
|
36
66
|
data: z.string(),
|