@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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @vercel/sandbox@0.0.7 build /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.9 build /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
3
3
  > tsc
4
4
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @vercel/sandbox@0.0.7 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.9 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
3
3
  > tsc --noEmit
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @vercel/sandbox
2
2
 
3
+ ## 0.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Add `cmd.kill()` to stop/signal commands ([#48](https://github.com/vercel/sandbox-sdk/pull/48))
8
+ - Update SDK to use the new API ([#51](https://github.com/vercel/sandbox-sdk/pull/51))
9
+
10
+ ## 0.0.8
11
+
12
+ ### Patch Changes
13
+
14
+ - Write files using a single compressed stream ([#44](https://github.com/vercel/sandbox-sdk/pull/44))
15
+ - Expose `runtime` parameter ([#46](https://github.com/vercel/sandbox-sdk/pull/46))
16
+ - Add git depth and revision options to sandbox source ([#47](https://github.com/vercel/sandbox-sdk/pull/47))
17
+
3
18
  ## 0.0.7
4
19
 
5
20
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { BaseClient, type Parsed, type RequestParams } from "./base-client";
2
- import { Command, CommandFinished, LogLine, StoppedSandbox } from "./validators";
3
- import { Readable } from "stream";
2
+ import { SandboxResponse, CommandResponse, CommandFinishedResponse, LogLine } from "./validators";
3
+ import { FileWriter } from "./file-writer";
4
4
  import { z } from "zod";
5
5
  export declare class APIClient extends BaseClient {
6
6
  private teamId;
@@ -10,12 +10,39 @@ export declare class APIClient extends BaseClient {
10
10
  token: string;
11
11
  });
12
12
  protected request(path: string, params?: RequestParams): Promise<import("node-fetch").Response>;
13
+ getSandbox(params: {
14
+ sandboxId: string;
15
+ }): Promise<Parsed<{
16
+ sandbox: {
17
+ region: string;
18
+ timeout: number;
19
+ status: "pending" | "running" | "stopping" | "stopped" | "failed";
20
+ id: string;
21
+ memory: number;
22
+ vcpus: number;
23
+ runtime: string;
24
+ requestedAt: number;
25
+ createdAt: number;
26
+ updatedAt: number;
27
+ duration?: number | undefined;
28
+ startedAt?: number | undefined;
29
+ requestedStopAt?: number | undefined;
30
+ stoppedAt?: number | undefined;
31
+ };
32
+ routes: {
33
+ port: number;
34
+ url: string;
35
+ subdomain: string;
36
+ }[];
37
+ }>>;
13
38
  createSandbox(params: {
14
39
  ports?: number[];
15
40
  projectId: string;
16
41
  source?: {
17
42
  type: "git";
18
43
  url: string;
44
+ depth?: number;
45
+ revision?: string;
19
46
  } | {
20
47
  type: "tarball";
21
48
  url: string;
@@ -24,10 +51,27 @@ export declare class APIClient extends BaseClient {
24
51
  resources?: {
25
52
  vcpus: number;
26
53
  };
54
+ runtime?: "node22" | "python3.13";
27
55
  }): Promise<Parsed<{
28
- sandboxId: string;
56
+ sandbox: {
57
+ region: string;
58
+ timeout: number;
59
+ status: "pending" | "running" | "stopping" | "stopped" | "failed";
60
+ id: string;
61
+ memory: number;
62
+ vcpus: number;
63
+ runtime: string;
64
+ requestedAt: number;
65
+ createdAt: number;
66
+ updatedAt: number;
67
+ duration?: number | undefined;
68
+ startedAt?: number | undefined;
69
+ requestedStopAt?: number | undefined;
70
+ stoppedAt?: number | undefined;
71
+ };
29
72
  routes: {
30
73
  port: number;
74
+ url: string;
31
75
  subdomain: string;
32
76
  }[];
33
77
  }>>;
@@ -38,28 +82,42 @@ export declare class APIClient extends BaseClient {
38
82
  args: string[];
39
83
  env: Record<string, string>;
40
84
  }): Promise<Parsed<{
41
- cmdId: string;
85
+ command: {
86
+ name: string;
87
+ cwd: string;
88
+ args: string[];
89
+ id: string;
90
+ startedAt: number;
91
+ sandboxId: string;
92
+ exitCode: number | null;
93
+ };
42
94
  }>>;
43
95
  getCommand(params: {
44
96
  sandboxId: string;
45
97
  cmdId: string;
46
98
  wait: true;
47
- }): Promise<Parsed<z.infer<typeof CommandFinished>>>;
99
+ }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;
48
100
  getCommand(params: {
49
101
  sandboxId: string;
50
102
  cmdId: string;
51
103
  wait?: boolean;
52
- }): Promise<Parsed<z.infer<typeof Command>>>;
104
+ }): Promise<Parsed<z.infer<typeof CommandResponse>>>;
53
105
  mkDir(params: {
54
106
  sandboxId: string;
55
107
  path: string;
56
108
  cwd?: string;
57
109
  }): Promise<Parsed<{}>>;
110
+ getFileWriter(params: {
111
+ sandboxId: string;
112
+ }): {
113
+ response: Promise<import("node-fetch").Response>;
114
+ writer: FileWriter;
115
+ };
58
116
  writeFiles(params: {
59
117
  sandboxId: string;
60
118
  files: {
61
119
  path: string;
62
- stream: Readable | Buffer;
120
+ stream: Buffer;
63
121
  }[];
64
122
  }): Promise<void>;
65
123
  readFile(params: {
@@ -67,11 +125,26 @@ export declare class APIClient extends BaseClient {
67
125
  path: string;
68
126
  cwd?: string;
69
127
  }): Promise<NodeJS.ReadableStream | null>;
128
+ killCommand(params: {
129
+ sandboxId: string;
130
+ commandId: string;
131
+ signal: number;
132
+ }): Promise<Parsed<{
133
+ command: {
134
+ name: string;
135
+ cwd: string;
136
+ args: string[];
137
+ id: string;
138
+ startedAt: number;
139
+ sandboxId: string;
140
+ exitCode: number | null;
141
+ };
142
+ }>>;
70
143
  getLogs(params: {
71
144
  sandboxId: string;
72
145
  cmdId: string;
73
146
  }): AsyncIterable<z.infer<typeof LogLine>>;
74
147
  stopSandbox(params: {
75
148
  sandboxId: string;
76
- }): Promise<Parsed<z.infer<typeof StoppedSandbox>>>;
149
+ }): Promise<Parsed<z.infer<typeof SandboxResponse>>>;
77
150
  }
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.APIClient = void 0;
7
- const form_data_1 = __importDefault(require("form-data"));
8
7
  const base_client_1 = require("./base-client");
9
8
  const validators_1 = require("./validators");
10
9
  const api_error_1 = require("./api-error");
10
+ const file_writer_1 = require("./file-writer");
11
11
  const lru_cache_1 = require("lru-cache");
12
12
  const version_1 = require("../version");
13
13
  const jsonlines_1 = __importDefault(require("jsonlines"));
@@ -41,8 +41,11 @@ class APIClient extends base_client_1.BaseClient {
41
41
  },
42
42
  });
43
43
  }
44
+ async getSandbox(params) {
45
+ return (0, base_client_1.parseOrThrow)(validators_1.SandboxAndRoutesResponse, await this.request(`/v1/sandboxes/${params.sandboxId}`));
46
+ }
44
47
  async createSandbox(params) {
45
- return (0, base_client_1.parseOrThrow)(validators_1.CreatedSandbox, await this.request("/v1/sandboxes", {
48
+ return (0, base_client_1.parseOrThrow)(validators_1.SandboxAndRoutesResponse, await this.request("/v1/sandboxes", {
46
49
  method: "POST",
47
50
  body: JSON.stringify({
48
51
  projectId: params.projectId,
@@ -50,11 +53,12 @@ class APIClient extends base_client_1.BaseClient {
50
53
  source: params.source,
51
54
  timeout: params.timeout,
52
55
  resources: params.resources,
56
+ runtime: params.runtime,
53
57
  }),
54
58
  }));
55
59
  }
56
60
  async runCommand(params) {
57
- return (0, base_client_1.parseOrThrow)(validators_1.CreatedCommand, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
61
+ return (0, base_client_1.parseOrThrow)(validators_1.CommandResponse, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
58
62
  method: "POST",
59
63
  body: JSON.stringify({
60
64
  command: params.command,
@@ -66,25 +70,35 @@ class APIClient extends base_client_1.BaseClient {
66
70
  }
67
71
  async getCommand(params) {
68
72
  return params.wait
69
- ? (0, base_client_1.parseOrThrow)(validators_1.CommandFinished, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`, { query: { wait: "true" } }))
70
- : (0, base_client_1.parseOrThrow)(validators_1.Command, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`));
73
+ ? (0, base_client_1.parseOrThrow)(validators_1.CommandFinishedResponse, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`, { query: { wait: "true" } }))
74
+ : (0, base_client_1.parseOrThrow)(validators_1.CommandResponse, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`));
71
75
  }
72
76
  async mkDir(params) {
73
- return (0, base_client_1.parseOrThrow)(validators_1.WrittenFile, await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
77
+ return (0, base_client_1.parseOrThrow)(validators_1.EmptyResponse, await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
74
78
  method: "POST",
75
79
  body: JSON.stringify({ path: params.path, cwd: params.cwd }),
76
80
  }));
77
81
  }
82
+ getFileWriter(params) {
83
+ const writer = new file_writer_1.FileWriter();
84
+ return {
85
+ response: this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
86
+ method: "POST",
87
+ headers: { "content-type": "application/gzip" },
88
+ body: writer.readable,
89
+ }),
90
+ writer,
91
+ };
92
+ }
78
93
  async writeFiles(params) {
79
- const formData = new form_data_1.default();
94
+ const { writer, response } = this.getFileWriter({
95
+ sandboxId: params.sandboxId,
96
+ });
80
97
  for (const file of params.files) {
81
- formData.append(file.path, file.stream, file.path);
98
+ await writer.addFile({ name: file.path, content: file.stream });
82
99
  }
83
- await (0, base_client_1.parseOrThrow)(validators_1.WrittenFile, await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
84
- method: "POST",
85
- headers: { ...formData.getHeaders() },
86
- body: formData,
87
- }));
100
+ await writer.end();
101
+ await (0, base_client_1.parseOrThrow)(validators_1.EmptyResponse, await response);
88
102
  }
89
103
  async readFile(params) {
90
104
  const response = await this.request(`/v1/sandboxes/${params.sandboxId}/fs/read`, {
@@ -96,6 +110,12 @@ class APIClient extends base_client_1.BaseClient {
96
110
  }
97
111
  return response.body;
98
112
  }
113
+ async killCommand(params) {
114
+ return (0, base_client_1.parseOrThrow)(validators_1.CommandResponse, await this.request(`/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`, {
115
+ method: "POST",
116
+ body: JSON.stringify({ signal: params.signal }),
117
+ }));
118
+ }
99
119
  async *getLogs(params) {
100
120
  const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;
101
121
  const response = await this.request(url, { method: "GET" });
@@ -122,7 +142,7 @@ class APIClient extends base_client_1.BaseClient {
122
142
  }
123
143
  async stopSandbox(params) {
124
144
  const url = `/v1/sandboxes/${params.sandboxId}/stop`;
125
- return (0, base_client_1.parseOrThrow)(validators_1.StoppedSandbox, await this.request(url, { method: "POST" }));
145
+ return (0, base_client_1.parseOrThrow)(validators_1.SandboxResponse, await this.request(url, { method: "POST" }));
126
146
  }
127
147
  }
128
148
  exports.APIClient = APIClient;
@@ -0,0 +1,52 @@
1
+ import { Readable } from "stream";
2
+ interface FileBuffer {
3
+ /**
4
+ * The name (path) of the file to write.
5
+ */
6
+ name: string;
7
+ /**
8
+ * The content of the file as a Buffer.
9
+ */
10
+ content: Buffer;
11
+ }
12
+ interface FileStream {
13
+ /**
14
+ * The name (path) of the file to write.
15
+ */
16
+ name: string;
17
+ /**
18
+ * A Readable stream to consume the content of the file.
19
+ */
20
+ content: Readable;
21
+ /**
22
+ * The expected size of the file. This is required to write
23
+ * the header of the compressed file.
24
+ */
25
+ size: number;
26
+ }
27
+ /**
28
+ * Allows to create a Readable stream with methods to write files
29
+ * to it and to finish it. Files written are compressed together
30
+ * and gzipped in the stream.
31
+ */
32
+ export declare class FileWriter {
33
+ readable: Readable;
34
+ private pack;
35
+ constructor();
36
+ /**
37
+ * Allows to add a file to the stream. Size is required to write
38
+ * the tarball header so when content is a stream it must be
39
+ * provided.
40
+ *
41
+ * Returns a Promise resolved once the file is written in the
42
+ * stream.
43
+ */
44
+ addFile(file: FileBuffer | FileStream): Promise<void>;
45
+ /**
46
+ * Allows to finish the stream returning a Promise that will
47
+ * resolve once the readable is effectively closed or
48
+ * errored.
49
+ */
50
+ end(): Promise<void>;
51
+ }
52
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileWriter = void 0;
7
+ const zlib_1 = __importDefault(require("zlib"));
8
+ const tar_stream_1 = __importDefault(require("tar-stream"));
9
+ const stream_1 = require("stream");
10
+ /**
11
+ * Allows to create a Readable stream with methods to write files
12
+ * to it and to finish it. Files written are compressed together
13
+ * and gzipped in the stream.
14
+ */
15
+ class FileWriter {
16
+ constructor() {
17
+ const gzip = zlib_1.default.createGzip();
18
+ this.pack = tar_stream_1.default.pack();
19
+ this.readable = this.pack.pipe(gzip);
20
+ }
21
+ /**
22
+ * Allows to add a file to the stream. Size is required to write
23
+ * the tarball header so when content is a stream it must be
24
+ * provided.
25
+ *
26
+ * Returns a Promise resolved once the file is written in the
27
+ * stream.
28
+ */
29
+ async addFile(file) {
30
+ return new Promise((resolve, reject) => {
31
+ const entry = this.pack.entry("size" in file
32
+ ? { name: file.name, size: file.size }
33
+ : { name: file.name, size: file.content.length }, (error) => {
34
+ if (error) {
35
+ return reject(error);
36
+ }
37
+ else {
38
+ resolve();
39
+ }
40
+ });
41
+ if (file.content instanceof stream_1.Readable) {
42
+ file.content.pipe(entry);
43
+ }
44
+ else {
45
+ entry.end(file.content);
46
+ }
47
+ });
48
+ }
49
+ /**
50
+ * Allows to finish the stream returning a Promise that will
51
+ * resolve once the readable is effectively closed or
52
+ * errored.
53
+ */
54
+ async end() {
55
+ return new Promise((resolve, reject) => {
56
+ this.readable.on("error", reject);
57
+ this.readable.on("end", resolve);
58
+ this.pack.finalize();
59
+ });
60
+ }
61
+ }
62
+ exports.FileWriter = FileWriter;
@@ -1 +1,2 @@
1
1
  export { APIClient } from "./api-client";
2
+ export * from "./validators";
@@ -1,5 +1,20 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.APIClient = void 0;
4
18
  var api_client_1 = require("./api-client");
5
19
  Object.defineProperty(exports, "APIClient", { enumerable: true, get: function () { return api_client_1.APIClient; } });
20
+ __exportStar(require("./validators"), exports);