@vercel/sandbox 0.0.5 → 0.0.6
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 +8 -0
- package/README.md +1 -0
- package/dist/{client/client.d.ts → api-client/api-client.d.ts} +30 -17
- package/dist/{client/client.js → api-client/api-client.js} +49 -44
- package/dist/{client → api-client}/base-client.d.ts +1 -1
- package/dist/{client → api-client}/base-client.js +3 -3
- package/dist/api-client/index.d.ts +1 -0
- package/dist/api-client/index.js +5 -0
- package/dist/{client → api-client}/validators.d.ts +20 -1
- package/dist/{client → api-client}/validators.js +9 -2
- package/dist/command.d.ts +127 -0
- package/dist/command.js +137 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -5
- package/dist/sandbox.d.ts +201 -0
- package/dist/sandbox.js +174 -0
- package/dist/utils/decode-base64-url.d.ts +7 -0
- package/dist/utils/decode-base64-url.js +12 -0
- package/dist/utils/get-credentials.d.ts +26 -0
- package/dist/utils/get-credentials.js +84 -0
- package/dist/utils/get-vercel-oidc-token.d.ts +6 -0
- package/dist/utils/get-vercel-oidc-token.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -3
- package/src/api-client/api-client.ts +225 -0
- package/src/{client → api-client}/base-client.ts +1 -1
- package/src/api-client/index.ts +1 -0
- package/src/{client → api-client}/validators.ts +9 -1
- package/src/command.test.ts +51 -0
- package/src/command.ts +176 -0
- package/src/index.ts +2 -1
- package/src/sandbox.ts +309 -0
- package/src/utils/decode-base64-url.ts +14 -0
- package/src/utils/get-credentials.ts +113 -0
- package/src/utils/get-vercel-oidc-token.ts +31 -0
- package/src/version.ts +1 -1
- package/tsconfig.json +2 -1
- package/typedoc.json +7 -1
- package/vitest.config.ts +8 -0
- package/vitest.setup.ts +4 -0
- package/dist/create-sandbox.d.ts +0 -219
- package/dist/create-sandbox.js +0 -231
- package/dist/utils/deferred-generator.d.ts +0 -5
- package/dist/utils/deferred-generator.js +0 -32
- package/dist/utils/deferred.d.ts +0 -6
- package/dist/utils/deferred.js +0 -12
- package/src/client/client.ts +0 -188
- package/src/create-sandbox.ts +0 -330
- package/src/utils/deferred-generator.ts +0 -38
- package/src/utils/deferred.ts +0 -12
- /package/dist/{client → api-client}/api-error.d.ts +0 -0
- /package/dist/{client → api-client}/api-error.js +0 -0
- /package/dist/{client → api-client}/with-retry.d.ts +0 -0
- /package/dist/{client → api-client}/with-retry.js +0 -0
- /package/src/{client → api-client}/api-error.ts +0 -0
- /package/src/{client → api-client}/with-retry.ts +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import FormData from "form-data";
|
|
2
|
+
import {
|
|
3
|
+
BaseClient,
|
|
4
|
+
parseOrThrow,
|
|
5
|
+
type Parsed,
|
|
6
|
+
type RequestParams,
|
|
7
|
+
} from "./base-client";
|
|
8
|
+
import {
|
|
9
|
+
Command,
|
|
10
|
+
CommandFinished,
|
|
11
|
+
CreatedCommand,
|
|
12
|
+
CreatedSandbox,
|
|
13
|
+
LogLine,
|
|
14
|
+
StoppedSandbox,
|
|
15
|
+
WrittenFile,
|
|
16
|
+
} from "./validators";
|
|
17
|
+
import { Readable } from "stream";
|
|
18
|
+
import { APIError } from "./api-error";
|
|
19
|
+
import { LRUCache } from "lru-cache";
|
|
20
|
+
import { VERSION } from "../version";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import jsonlines from "jsonlines";
|
|
23
|
+
import os from "os";
|
|
24
|
+
import ms from "ms";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Allows to track the logs hits for a command un a to maximum of items and
|
|
28
|
+
* TTL so that we don't incur in memory leaks in a log running process.
|
|
29
|
+
*/
|
|
30
|
+
const logHits = new LRUCache<string, boolean>({
|
|
31
|
+
ttl: ms("45m"),
|
|
32
|
+
max: 1000,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export class APIClient extends BaseClient {
|
|
36
|
+
private teamId: string;
|
|
37
|
+
|
|
38
|
+
constructor(params: { host?: string; teamId: string; token: string }) {
|
|
39
|
+
super({
|
|
40
|
+
host: params.host ?? "https://api.vercel.com",
|
|
41
|
+
token: params.token,
|
|
42
|
+
debug: false,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.teamId = params.teamId;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected async request(path: string, params?: RequestParams) {
|
|
49
|
+
return super.request(path, {
|
|
50
|
+
...params,
|
|
51
|
+
query: { teamId: this.teamId, ...params?.query },
|
|
52
|
+
headers: {
|
|
53
|
+
"content-type": "application/json",
|
|
54
|
+
"user-agent": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,
|
|
55
|
+
...params?.headers,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async createSandbox(params: {
|
|
61
|
+
ports?: number[];
|
|
62
|
+
projectId: string;
|
|
63
|
+
source?: { type: "git"; url: string } | { type: "tarball"; url: string };
|
|
64
|
+
timeout?: number;
|
|
65
|
+
resources?: { cores: number; memory: number };
|
|
66
|
+
}) {
|
|
67
|
+
return parseOrThrow(
|
|
68
|
+
CreatedSandbox,
|
|
69
|
+
await this.request("/v1/sandboxes", {
|
|
70
|
+
method: "POST",
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
projectId: params.projectId,
|
|
73
|
+
ports: params.ports,
|
|
74
|
+
source: params.source,
|
|
75
|
+
timeout: params.timeout,
|
|
76
|
+
resources: params.resources,
|
|
77
|
+
}),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async runCommand(params: {
|
|
83
|
+
sandboxId: string;
|
|
84
|
+
cwd?: string;
|
|
85
|
+
command: string;
|
|
86
|
+
args: string[];
|
|
87
|
+
env: Record<string, string>;
|
|
88
|
+
}) {
|
|
89
|
+
return parseOrThrow(
|
|
90
|
+
CreatedCommand,
|
|
91
|
+
await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
body: JSON.stringify({
|
|
94
|
+
command: params.command,
|
|
95
|
+
args: params.args,
|
|
96
|
+
cwd: params.cwd,
|
|
97
|
+
env: params.env,
|
|
98
|
+
}),
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getCommand(params: {
|
|
104
|
+
sandboxId: string;
|
|
105
|
+
cmdId: string;
|
|
106
|
+
wait: true;
|
|
107
|
+
}): Promise<Parsed<z.infer<typeof CommandFinished>>>;
|
|
108
|
+
async getCommand(params: {
|
|
109
|
+
sandboxId: string;
|
|
110
|
+
cmdId: string;
|
|
111
|
+
wait?: boolean;
|
|
112
|
+
}): Promise<Parsed<z.infer<typeof Command>>>;
|
|
113
|
+
async getCommand(params: {
|
|
114
|
+
sandboxId: string;
|
|
115
|
+
cmdId: string;
|
|
116
|
+
wait?: boolean;
|
|
117
|
+
}) {
|
|
118
|
+
return params.wait
|
|
119
|
+
? parseOrThrow(
|
|
120
|
+
CommandFinished,
|
|
121
|
+
await this.request(
|
|
122
|
+
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
123
|
+
{ query: { wait: "true" } },
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
: parseOrThrow(
|
|
127
|
+
Command,
|
|
128
|
+
await this.request(
|
|
129
|
+
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async mkDir(params: { sandboxId: string; path: string; cwd?: string }) {
|
|
135
|
+
return parseOrThrow(
|
|
136
|
+
WrittenFile,
|
|
137
|
+
await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
body: JSON.stringify({ path: params.path, cwd: params.cwd }),
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async writeFiles(params: {
|
|
145
|
+
sandboxId: string;
|
|
146
|
+
files: { path: string; stream: Readable | Buffer }[];
|
|
147
|
+
}) {
|
|
148
|
+
const formData = new FormData();
|
|
149
|
+
|
|
150
|
+
for (const file of params.files) {
|
|
151
|
+
formData.append(file.path, file.stream, file.path);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await parseOrThrow(
|
|
155
|
+
WrittenFile,
|
|
156
|
+
await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { ...formData.getHeaders() },
|
|
159
|
+
body: formData,
|
|
160
|
+
}),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async readFile(params: {
|
|
165
|
+
sandboxId: string;
|
|
166
|
+
path: string;
|
|
167
|
+
cwd?: string;
|
|
168
|
+
}): Promise<NodeJS.ReadableStream | null> {
|
|
169
|
+
const response = await this.request(
|
|
170
|
+
`/v1/sandboxes/${params.sandboxId}/fs/read`,
|
|
171
|
+
{
|
|
172
|
+
method: "POST",
|
|
173
|
+
body: JSON.stringify({ path: params.path, cwd: params.cwd }),
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (response.status === 404) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return response.body;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async *getLogs(params: {
|
|
185
|
+
sandboxId: string;
|
|
186
|
+
cmdId: string;
|
|
187
|
+
}): AsyncIterable<z.infer<typeof LogLine>> {
|
|
188
|
+
const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;
|
|
189
|
+
const response = await this.request(url, { method: "GET" });
|
|
190
|
+
if (response.headers.get("content-type") !== "application/x-ndjson") {
|
|
191
|
+
throw new APIError(response, {
|
|
192
|
+
message: "Expected a stream of logs",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Currently, once we consume logs in the backend we cannot read them
|
|
198
|
+
* again. This logic writes a warning when the endpoint for a command
|
|
199
|
+
* logs is consumed more than once to alert the user about this.
|
|
200
|
+
*
|
|
201
|
+
* This is a temporary solution, we should be able to handle this in
|
|
202
|
+
* the backend in the future.
|
|
203
|
+
*/
|
|
204
|
+
if (logHits.get(url)) {
|
|
205
|
+
console.warn(
|
|
206
|
+
`Multiple consumers for logs of command \`${params.cmdId}\`. This may lead to unexpected behavior.`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
logHits.set(url, true);
|
|
211
|
+
for await (const chunk of response.body.pipe(jsonlines.parse())) {
|
|
212
|
+
yield LogLine.parse(chunk);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async stopSandbox(params: {
|
|
217
|
+
sandboxId: string;
|
|
218
|
+
}): Promise<Parsed<z.infer<typeof StoppedSandbox>>> {
|
|
219
|
+
const url = `/v1/sandboxes/${params.sandboxId}/stop`;
|
|
220
|
+
return parseOrThrow(
|
|
221
|
+
StoppedSandbox,
|
|
222
|
+
await this.request(url, { method: "POST" }),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -18,7 +18,7 @@ export interface RequestParams extends RequestInit {
|
|
|
18
18
|
* we can pass query parameters as an object, support retries, debugging
|
|
19
19
|
* and automatic authorization.
|
|
20
20
|
*/
|
|
21
|
-
export class
|
|
21
|
+
export class BaseClient {
|
|
22
22
|
protected token?: string;
|
|
23
23
|
private fetch: ReturnType<typeof withRetry<RequestInit>>;
|
|
24
24
|
private debug: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { APIClient } from "./api-client";
|
|
@@ -17,12 +17,20 @@ export const Command = z.object({
|
|
|
17
17
|
name: z.string(),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const CommandFinished = z.object({
|
|
21
|
+
args: z.array(z.string()),
|
|
21
22
|
cmdId: z.string(),
|
|
23
|
+
cwd: z.string(),
|
|
24
|
+
exitCode: z.number(),
|
|
25
|
+
name: z.string(),
|
|
22
26
|
});
|
|
23
27
|
|
|
24
28
|
export const WrittenFile = z.object({});
|
|
25
29
|
|
|
30
|
+
export const StoppedSandbox = z.object({
|
|
31
|
+
sandboxId: z.string(),
|
|
32
|
+
});
|
|
33
|
+
|
|
26
34
|
export const LogLine = z.object({
|
|
27
35
|
stream: z.enum(["stdout", "stderr"]),
|
|
28
36
|
data: z.string(),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { it, expect, vi } from "vitest";
|
|
2
|
+
import { Sandbox } from "./sandbox";
|
|
3
|
+
|
|
4
|
+
it("warns when there is more than one logs consumer", async () => {
|
|
5
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
6
|
+
const stdoutSpy = vi
|
|
7
|
+
.spyOn(process.stdout, "write")
|
|
8
|
+
.mockImplementation(() => true);
|
|
9
|
+
|
|
10
|
+
const sandbox = await Sandbox.create({
|
|
11
|
+
projectId: process.env.VERCEL_PROJECT_ID!,
|
|
12
|
+
teamId: process.env.VERCEL_TEAM_ID!,
|
|
13
|
+
token: process.env.VERCEL_TOKEN!,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const cmd = await sandbox.runCommand({
|
|
17
|
+
cmd: "echo",
|
|
18
|
+
args: ["Hello World!"],
|
|
19
|
+
stdout: process.stdout,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(await cmd.stdout()).toEqual("");
|
|
23
|
+
expect(stdoutSpy).toHaveBeenCalledWith("Hello World!\n");
|
|
24
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
25
|
+
expect.stringMatching(
|
|
26
|
+
/Multiple consumers for logs of command `[^`]+`\.\sThis may lead to unexpected behavior\./,
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await sandbox.stop();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("does not warn when there is only one logs consumer", async () => {
|
|
34
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
35
|
+
|
|
36
|
+
const sandbox = await Sandbox.create({
|
|
37
|
+
projectId: process.env.VERCEL_PROJECT_ID!,
|
|
38
|
+
teamId: process.env.VERCEL_TEAM_ID!,
|
|
39
|
+
token: process.env.VERCEL_TOKEN!,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const cmd = await sandbox.runCommand({
|
|
43
|
+
cmd: "echo",
|
|
44
|
+
args: ["Hello World!"],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(await cmd.stdout()).toEqual("Hello World!\n");
|
|
48
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
49
|
+
|
|
50
|
+
await sandbox.stop();
|
|
51
|
+
});
|
package/src/command.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { APIClient } from "./api-client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A command executed in a Sandbox.
|
|
5
|
+
*
|
|
6
|
+
* You can {@link wait} on commands to access their {@link CommandFinished.exitCode}, and
|
|
7
|
+
* iterate over their output with {@link logs}.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link Sandbox.runCommand} to start a command.
|
|
10
|
+
*
|
|
11
|
+
* @hideconstructor
|
|
12
|
+
*/
|
|
13
|
+
export class Command {
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
protected client: APIClient;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ID of the sandbox this command is running in.
|
|
22
|
+
*/
|
|
23
|
+
private sandboxId: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ID of the command execution.
|
|
27
|
+
*/
|
|
28
|
+
public cmdId: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param params - Object containing the client, sandbox ID, and command ID.
|
|
32
|
+
* @param params.client - API client used to interact with the backend.
|
|
33
|
+
* @param params.sandboxId - The ID of the sandbox where the command is running.
|
|
34
|
+
* @param params.cmdId - The ID of the command execution.
|
|
35
|
+
*/
|
|
36
|
+
constructor({
|
|
37
|
+
client,
|
|
38
|
+
sandboxId,
|
|
39
|
+
cmdId,
|
|
40
|
+
}: {
|
|
41
|
+
client: APIClient;
|
|
42
|
+
sandboxId: string;
|
|
43
|
+
cmdId: string;
|
|
44
|
+
}) {
|
|
45
|
+
this.client = client;
|
|
46
|
+
this.sandboxId = sandboxId;
|
|
47
|
+
this.cmdId = cmdId;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Iterate over the output of this command.
|
|
52
|
+
*
|
|
53
|
+
* ```
|
|
54
|
+
* for await (const log of cmd.logs()) {
|
|
55
|
+
* if (log.stream === "stdout") {
|
|
56
|
+
* process.stdout.write(log.data);
|
|
57
|
+
* } else {
|
|
58
|
+
* process.stderr.write(log.data);
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @returns An async iterable of log entries from the command output.
|
|
64
|
+
*
|
|
65
|
+
* @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}
|
|
66
|
+
* to access output as a string.
|
|
67
|
+
*/
|
|
68
|
+
logs() {
|
|
69
|
+
return this.client.getLogs({
|
|
70
|
+
sandboxId: this.sandboxId,
|
|
71
|
+
cmdId: this.cmdId,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Wait for a command to exit and populate its exit code.
|
|
77
|
+
*
|
|
78
|
+
* ```
|
|
79
|
+
* await cmd.wait()
|
|
80
|
+
* if (cmd.exitCode != 0) {
|
|
81
|
+
* console.error("Something went wrong...")
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @returns A {@link CommandFinished} instance with populated exit code.
|
|
86
|
+
*/
|
|
87
|
+
async wait() {
|
|
88
|
+
const command = await this.client.getCommand({
|
|
89
|
+
sandboxId: this.sandboxId,
|
|
90
|
+
cmdId: this.cmdId,
|
|
91
|
+
wait: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return new CommandFinished({
|
|
95
|
+
client: this.client,
|
|
96
|
+
sandboxId: this.sandboxId,
|
|
97
|
+
cmdId: command.json.cmdId,
|
|
98
|
+
exitCode: command.json.exitCode,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get the output of `stdout`, `stderr`, or both as a string.
|
|
104
|
+
*
|
|
105
|
+
* NOTE: This may throw string conversion errors if the command does
|
|
106
|
+
* not output valid Unicode.
|
|
107
|
+
*
|
|
108
|
+
* @param stream - The output stream to read: "stdout", "stderr", or "both".
|
|
109
|
+
* @returns The output of the specified stream(s) as a string.
|
|
110
|
+
*/
|
|
111
|
+
async output(stream: "stdout" | "stderr" | "both" = "both") {
|
|
112
|
+
let data = "";
|
|
113
|
+
for await (const log of this.logs()) {
|
|
114
|
+
if (stream === "both" || log.stream === stream) {
|
|
115
|
+
data += log.data;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return data;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get the output of `stdout` as a string.
|
|
123
|
+
*
|
|
124
|
+
* NOTE: This may throw string conversion errors if the command does
|
|
125
|
+
* not output valid Unicode.
|
|
126
|
+
*
|
|
127
|
+
* @returns The standard output of the command.
|
|
128
|
+
*/
|
|
129
|
+
async stdout() {
|
|
130
|
+
return this.output("stdout");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the output of `stderr` as a string.
|
|
135
|
+
*
|
|
136
|
+
* NOTE: This may throw string conversion errors if the command does
|
|
137
|
+
* not output valid Unicode.
|
|
138
|
+
*
|
|
139
|
+
* @returns The standard error output of the command.
|
|
140
|
+
*/
|
|
141
|
+
async stderr() {
|
|
142
|
+
return this.output("stderr");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A command that has finished executing.
|
|
148
|
+
*
|
|
149
|
+
* Contains the exit code of the command.
|
|
150
|
+
*
|
|
151
|
+
* @hideconstructor
|
|
152
|
+
*/
|
|
153
|
+
export class CommandFinished extends Command {
|
|
154
|
+
/**
|
|
155
|
+
* The exit code of the command, if available. This is set after
|
|
156
|
+
* {@link wait} has returned.
|
|
157
|
+
*/
|
|
158
|
+
public exitCode: number;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param params - Object containing client, sandbox ID, command ID, and exit code.
|
|
162
|
+
* @param params.client - API client used to interact with the backend.
|
|
163
|
+
* @param params.sandboxId - The ID of the sandbox where the command ran.
|
|
164
|
+
* @param params.cmdId - The ID of the command execution.
|
|
165
|
+
* @param params.exitCode - The exit code of the completed command.
|
|
166
|
+
*/
|
|
167
|
+
constructor(params: {
|
|
168
|
+
client: APIClient;
|
|
169
|
+
sandboxId: string;
|
|
170
|
+
cmdId: string;
|
|
171
|
+
exitCode: number;
|
|
172
|
+
}) {
|
|
173
|
+
super({ ...params });
|
|
174
|
+
this.exitCode = params.exitCode;
|
|
175
|
+
}
|
|
176
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Sandbox } from "./sandbox";
|
|
2
|
+
export { Command, CommandFinished } from "./command";
|