@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.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +1 -0
  5. package/dist/{client/client.d.ts → api-client/api-client.d.ts} +30 -17
  6. package/dist/{client/client.js → api-client/api-client.js} +49 -44
  7. package/dist/{client → api-client}/base-client.d.ts +1 -1
  8. package/dist/{client → api-client}/base-client.js +3 -3
  9. package/dist/api-client/index.d.ts +1 -0
  10. package/dist/api-client/index.js +5 -0
  11. package/dist/{client → api-client}/validators.d.ts +20 -1
  12. package/dist/{client → api-client}/validators.js +9 -2
  13. package/dist/command.d.ts +127 -0
  14. package/dist/command.js +137 -0
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +6 -5
  17. package/dist/sandbox.d.ts +201 -0
  18. package/dist/sandbox.js +174 -0
  19. package/dist/utils/decode-base64-url.d.ts +7 -0
  20. package/dist/utils/decode-base64-url.js +12 -0
  21. package/dist/utils/get-credentials.d.ts +26 -0
  22. package/dist/utils/get-credentials.js +84 -0
  23. package/dist/utils/get-vercel-oidc-token.d.ts +6 -0
  24. package/dist/utils/get-vercel-oidc-token.js +21 -0
  25. package/dist/version.d.ts +1 -1
  26. package/dist/version.js +1 -1
  27. package/package.json +10 -3
  28. package/src/api-client/api-client.ts +225 -0
  29. package/src/{client → api-client}/base-client.ts +1 -1
  30. package/src/api-client/index.ts +1 -0
  31. package/src/{client → api-client}/validators.ts +9 -1
  32. package/src/command.test.ts +51 -0
  33. package/src/command.ts +176 -0
  34. package/src/index.ts +2 -1
  35. package/src/sandbox.ts +309 -0
  36. package/src/utils/decode-base64-url.ts +14 -0
  37. package/src/utils/get-credentials.ts +113 -0
  38. package/src/utils/get-vercel-oidc-token.ts +31 -0
  39. package/src/version.ts +1 -1
  40. package/tsconfig.json +2 -1
  41. package/typedoc.json +7 -1
  42. package/vitest.config.ts +8 -0
  43. package/vitest.setup.ts +4 -0
  44. package/dist/create-sandbox.d.ts +0 -219
  45. package/dist/create-sandbox.js +0 -231
  46. package/dist/utils/deferred-generator.d.ts +0 -5
  47. package/dist/utils/deferred-generator.js +0 -32
  48. package/dist/utils/deferred.d.ts +0 -6
  49. package/dist/utils/deferred.js +0 -12
  50. package/src/client/client.ts +0 -188
  51. package/src/create-sandbox.ts +0 -330
  52. package/src/utils/deferred-generator.ts +0 -38
  53. package/src/utils/deferred.ts +0 -12
  54. /package/dist/{client → api-client}/api-error.d.ts +0 -0
  55. /package/dist/{client → api-client}/api-error.js +0 -0
  56. /package/dist/{client → api-client}/with-retry.d.ts +0 -0
  57. /package/dist/{client → api-client}/with-retry.js +0 -0
  58. /package/src/{client → api-client}/api-error.ts +0 -0
  59. /package/src/{client → api-client}/with-retry.ts +0 -0
@@ -1,4 +1,4 @@
1
1
 
2
- > @vercel/sandbox@0.0.5 build /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.6 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.5 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.6 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
3
3
  > tsc --noEmit
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @vercel/sandbox
2
2
 
3
+ ## 0.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Better types for `Command` allowing a shorcut for waiting ([#29](https://github.com/vercel/sandbox-sdk/pull/29))
8
+
9
+ - Remove `SDK` and simplify API surface ([#38](https://github.com/vercel/sandbox-sdk/pull/38))
10
+
3
11
  ## 0.0.5
4
12
 
5
13
  ### Patch Changes
package/README.md CHANGED
@@ -23,6 +23,7 @@ mkdir sandbox-test
23
23
  cd sandbox-test
24
24
  pnpm init
25
25
  pnpm add @vercel/sandbox ms
26
+ pnpm add -D @types/ms @types/node
26
27
  ```
27
28
 
28
29
  Now create `next-dev.ts`:
@@ -1,6 +1,8 @@
1
- import { APIClient, type RequestParams } from "./base-client";
1
+ import { BaseClient, type Parsed, type RequestParams } from "./base-client";
2
+ import { Command, CommandFinished, LogLine, StoppedSandbox } from "./validators";
2
3
  import { Readable } from "stream";
3
- export declare class SandboxClient extends APIClient {
4
+ import { z } from "zod";
5
+ export declare class APIClient extends BaseClient {
4
6
  private teamId;
5
7
  constructor(params: {
6
8
  host?: string;
@@ -9,14 +11,21 @@ export declare class SandboxClient extends APIClient {
9
11
  });
10
12
  protected request(path: string, params?: RequestParams): Promise<import("node-fetch").Response>;
11
13
  createSandbox(params: {
12
- ports: number[];
14
+ ports?: number[];
13
15
  projectId: string;
14
- source: {
16
+ source?: {
15
17
  type: "git";
16
18
  url: string;
19
+ } | {
20
+ type: "tarball";
21
+ url: string;
17
22
  };
18
23
  timeout?: number;
19
- }): Promise<import("./base-client").Parsed<{
24
+ resources?: {
25
+ cores: number;
26
+ memory: number;
27
+ };
28
+ }): Promise<Parsed<{
20
29
  sandboxId: string;
21
30
  routes: {
22
31
  port: number;
@@ -29,20 +38,24 @@ export declare class SandboxClient extends APIClient {
29
38
  command: string;
30
39
  args: string[];
31
40
  env: Record<string, string>;
32
- }): Promise<import("./base-client").Parsed<{
41
+ }): Promise<Parsed<{
33
42
  cmdId: string;
34
43
  }>>;
35
44
  getCommand(params: {
36
45
  sandboxId: string;
37
46
  cmdId: string;
38
- wait?: boolean;
39
- }): Promise<import("./base-client").Parsed<{
40
- name: string;
41
- cwd: string;
42
- args: string[];
47
+ wait: true;
48
+ }): Promise<Parsed<z.infer<typeof CommandFinished>>>;
49
+ getCommand(params: {
50
+ sandboxId: string;
43
51
  cmdId: string;
44
- exitCode: number | null;
45
- }>>;
52
+ wait?: boolean;
53
+ }): Promise<Parsed<z.infer<typeof Command>>>;
54
+ mkDir(params: {
55
+ sandboxId: string;
56
+ path: string;
57
+ cwd?: string;
58
+ }): Promise<Parsed<{}>>;
46
59
  writeFiles(params: {
47
60
  sandboxId: string;
48
61
  files: {
@@ -58,8 +71,8 @@ export declare class SandboxClient extends APIClient {
58
71
  getLogs(params: {
59
72
  sandboxId: string;
60
73
  cmdId: string;
61
- }): AsyncGenerator<{
62
- data: string;
63
- stream: "stdout" | "stderr";
64
- }, void, void>;
74
+ }): AsyncIterable<z.infer<typeof LogLine>>;
75
+ stopSandbox(params: {
76
+ sandboxId: string;
77
+ }): Promise<Parsed<z.infer<typeof StoppedSandbox>>>;
65
78
  }
@@ -3,16 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SandboxClient = void 0;
6
+ exports.APIClient = void 0;
7
7
  const form_data_1 = __importDefault(require("form-data"));
8
8
  const base_client_1 = require("./base-client");
9
9
  const validators_1 = require("./validators");
10
10
  const api_error_1 = require("./api-error");
11
- const deferred_generator_1 = require("../utils/deferred-generator");
11
+ const lru_cache_1 = require("lru-cache");
12
12
  const version_1 = require("../version");
13
13
  const jsonlines_1 = __importDefault(require("jsonlines"));
14
14
  const os_1 = __importDefault(require("os"));
15
- class SandboxClient extends base_client_1.APIClient {
15
+ const ms_1 = __importDefault(require("ms"));
16
+ /**
17
+ * Allows to track the logs hits for a command un a to maximum of items and
18
+ * TTL so that we don't incur in memory leaks in a log running process.
19
+ */
20
+ const logHits = new lru_cache_1.LRUCache({
21
+ ttl: (0, ms_1.default)("45m"),
22
+ max: 1000,
23
+ });
24
+ class APIClient extends base_client_1.BaseClient {
16
25
  constructor(params) {
17
26
  super({
18
27
  host: params.host ?? "https://api.vercel.com",
@@ -40,6 +49,7 @@ class SandboxClient extends base_client_1.APIClient {
40
49
  ports: params.ports,
41
50
  source: params.source,
42
51
  timeout: params.timeout,
52
+ resources: params.resources,
43
53
  }),
44
54
  }));
45
55
  }
@@ -55,7 +65,15 @@ class SandboxClient extends base_client_1.APIClient {
55
65
  }));
56
66
  }
57
67
  async getCommand(params) {
58
- return (0, base_client_1.parseOrThrow)(validators_1.Command, await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`, { query: { wait: params.wait ? "true" : undefined } }));
68
+ 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}`));
71
+ }
72
+ async mkDir(params) {
73
+ return (0, base_client_1.parseOrThrow)(validators_1.WrittenFile, await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
74
+ method: "POST",
75
+ body: JSON.stringify({ path: params.path, cwd: params.cwd }),
76
+ }));
59
77
  }
60
78
  async writeFiles(params) {
61
79
  const formData = new form_data_1.default();
@@ -78,46 +96,33 @@ class SandboxClient extends base_client_1.APIClient {
78
96
  }
79
97
  return response.body;
80
98
  }
81
- getLogs(params) {
82
- const deferred = (0, deferred_generator_1.createDeferredGenerator)();
83
- (async () => {
84
- const response = await this.request(`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`, { method: "GET" });
85
- if (response.headers.get("content-type") !== "application/x-ndjson") {
86
- throw new api_error_1.APIError(response, {
87
- message: "Expected a stream of logs",
88
- });
89
- }
90
- const parser = jsonlines_1.default.parse();
91
- response.body.pipe(parser);
92
- parser.on("data", (data) => {
93
- const parsed = validators_1.LogLine.safeParse(data);
94
- if (parsed.success) {
95
- deferred.next({
96
- value: parsed.data,
97
- done: false,
98
- });
99
- }
100
- else {
101
- deferred.next({
102
- value: Promise.reject(parsed.error),
103
- done: false,
104
- });
105
- }
106
- });
107
- parser.on("error", (err) => {
108
- deferred.next({
109
- value: Promise.reject(err),
110
- done: false,
111
- });
99
+ async *getLogs(params) {
100
+ const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;
101
+ const response = await this.request(url, { method: "GET" });
102
+ if (response.headers.get("content-type") !== "application/x-ndjson") {
103
+ throw new api_error_1.APIError(response, {
104
+ message: "Expected a stream of logs",
112
105
  });
113
- parser.on("end", () => {
114
- deferred.next({
115
- value: undefined,
116
- done: true,
117
- });
118
- });
119
- })();
120
- return deferred.generator();
106
+ }
107
+ /**
108
+ * Currently, once we consume logs in the backend we cannot read them
109
+ * again. This logic writes a warning when the endpoint for a command
110
+ * logs is consumed more than once to alert the user about this.
111
+ *
112
+ * This is a temporary solution, we should be able to handle this in
113
+ * the backend in the future.
114
+ */
115
+ if (logHits.get(url)) {
116
+ console.warn(`Multiple consumers for logs of command \`${params.cmdId}\`. This may lead to unexpected behavior.`);
117
+ }
118
+ logHits.set(url, true);
119
+ for await (const chunk of response.body.pipe(jsonlines_1.default.parse())) {
120
+ yield validators_1.LogLine.parse(chunk);
121
+ }
122
+ }
123
+ async stopSandbox(params) {
124
+ const url = `/v1/sandboxes/${params.sandboxId}/stop`;
125
+ return (0, base_client_1.parseOrThrow)(validators_1.StoppedSandbox, await this.request(url, { method: "POST" }));
121
126
  }
122
127
  }
123
- exports.SandboxClient = SandboxClient;
128
+ exports.APIClient = APIClient;
@@ -15,7 +15,7 @@ export interface RequestParams extends RequestInit {
15
15
  * we can pass query parameters as an object, support retries, debugging
16
16
  * and automatic authorization.
17
17
  */
18
- export declare class APIClient {
18
+ export declare class BaseClient {
19
19
  protected token?: string;
20
20
  private fetch;
21
21
  private debug;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.APIClient = void 0;
6
+ exports.BaseClient = void 0;
7
7
  exports.parse = parse;
8
8
  exports.parseOrThrow = parseOrThrow;
9
9
  const api_error_1 = require("./api-error");
@@ -15,7 +15,7 @@ const node_fetch_1 = __importDefault(require("node-fetch"));
15
15
  * we can pass query parameters as an object, support retries, debugging
16
16
  * and automatic authorization.
17
17
  */
18
- class APIClient {
18
+ class BaseClient {
19
19
  constructor(params) {
20
20
  this.fetch = (0, with_retry_1.withRetry)(node_fetch_1.default);
21
21
  this.host = params.host;
@@ -54,7 +54,7 @@ class APIClient {
54
54
  return response;
55
55
  }
56
56
  }
57
- exports.APIClient = APIClient;
57
+ exports.BaseClient = BaseClient;
58
58
  /**
59
59
  * Allows to read the response text and parse it as JSON casting to the given
60
60
  * type. If the response is not ok or cannot be parsed it will return error.
@@ -0,0 +1 @@
1
+ export { APIClient } from "./api-client";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.APIClient = void 0;
4
+ var api_client_1 = require("./api-client");
5
+ Object.defineProperty(exports, "APIClient", { enumerable: true, get: function () { return api_client_1.APIClient; } });
@@ -50,14 +50,33 @@ export declare const Command: z.ZodObject<{
50
50
  cmdId: string;
51
51
  exitCode: number | null;
52
52
  }>;
53
- export declare const FinishedCommand: z.ZodObject<{
53
+ export declare const CommandFinished: z.ZodObject<{
54
+ args: z.ZodArray<z.ZodString, "many">;
54
55
  cmdId: z.ZodString;
56
+ cwd: z.ZodString;
57
+ exitCode: z.ZodNumber;
58
+ name: z.ZodString;
55
59
  }, "strip", z.ZodTypeAny, {
60
+ name: string;
61
+ cwd: string;
62
+ args: string[];
56
63
  cmdId: string;
64
+ exitCode: number;
57
65
  }, {
66
+ name: string;
67
+ cwd: string;
68
+ args: string[];
58
69
  cmdId: string;
70
+ exitCode: number;
59
71
  }>;
60
72
  export declare const WrittenFile: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
73
+ export declare const StoppedSandbox: z.ZodObject<{
74
+ sandboxId: z.ZodString;
75
+ }, "strip", z.ZodTypeAny, {
76
+ sandboxId: string;
77
+ }, {
78
+ sandboxId: string;
79
+ }>;
61
80
  export declare const LogLine: z.ZodObject<{
62
81
  stream: z.ZodEnum<["stdout", "stderr"]>;
63
82
  data: z.ZodString;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LogLine = exports.WrittenFile = exports.FinishedCommand = exports.Command = exports.CreatedCommand = exports.CreatedSandbox = void 0;
3
+ exports.LogLine = exports.StoppedSandbox = exports.WrittenFile = exports.CommandFinished = exports.Command = exports.CreatedCommand = exports.CreatedSandbox = void 0;
4
4
  const zod_1 = require("zod");
5
5
  exports.CreatedSandbox = zod_1.z.object({
6
6
  sandboxId: zod_1.z.string(),
@@ -16,10 +16,17 @@ exports.Command = zod_1.z.object({
16
16
  exitCode: zod_1.z.number().nullable(),
17
17
  name: zod_1.z.string(),
18
18
  });
19
- exports.FinishedCommand = zod_1.z.object({
19
+ exports.CommandFinished = zod_1.z.object({
20
+ args: zod_1.z.array(zod_1.z.string()),
20
21
  cmdId: zod_1.z.string(),
22
+ cwd: zod_1.z.string(),
23
+ exitCode: zod_1.z.number(),
24
+ name: zod_1.z.string(),
21
25
  });
22
26
  exports.WrittenFile = zod_1.z.object({});
27
+ exports.StoppedSandbox = zod_1.z.object({
28
+ sandboxId: zod_1.z.string(),
29
+ });
23
30
  exports.LogLine = zod_1.z.object({
24
31
  stream: zod_1.z.enum(["stdout", "stderr"]),
25
32
  data: zod_1.z.string(),
@@ -0,0 +1,127 @@
1
+ import { APIClient } from "./api-client";
2
+ /**
3
+ * A command executed in a Sandbox.
4
+ *
5
+ * You can {@link wait} on commands to access their {@link CommandFinished.exitCode}, and
6
+ * iterate over their output with {@link logs}.
7
+ *
8
+ * @see {@link Sandbox.runCommand} to start a command.
9
+ *
10
+ * @hideconstructor
11
+ */
12
+ export declare class Command {
13
+ /**
14
+ * @internal
15
+ * @private
16
+ */
17
+ protected client: APIClient;
18
+ /**
19
+ * ID of the sandbox this command is running in.
20
+ */
21
+ private sandboxId;
22
+ /**
23
+ * ID of the command execution.
24
+ */
25
+ cmdId: string;
26
+ /**
27
+ * @param params - Object containing the client, sandbox ID, and command ID.
28
+ * @param params.client - API client used to interact with the backend.
29
+ * @param params.sandboxId - The ID of the sandbox where the command is running.
30
+ * @param params.cmdId - The ID of the command execution.
31
+ */
32
+ constructor({ client, sandboxId, cmdId, }: {
33
+ client: APIClient;
34
+ sandboxId: string;
35
+ cmdId: string;
36
+ });
37
+ /**
38
+ * Iterate over the output of this command.
39
+ *
40
+ * ```
41
+ * for await (const log of cmd.logs()) {
42
+ * if (log.stream === "stdout") {
43
+ * process.stdout.write(log.data);
44
+ * } else {
45
+ * process.stderr.write(log.data);
46
+ * }
47
+ * }
48
+ * ```
49
+ *
50
+ * @returns An async iterable of log entries from the command output.
51
+ *
52
+ * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}
53
+ * to access output as a string.
54
+ */
55
+ logs(): AsyncIterable<{
56
+ data: string;
57
+ stream: "stdout" | "stderr";
58
+ }>;
59
+ /**
60
+ * Wait for a command to exit and populate its exit code.
61
+ *
62
+ * ```
63
+ * await cmd.wait()
64
+ * if (cmd.exitCode != 0) {
65
+ * console.error("Something went wrong...")
66
+ * }
67
+ * ```
68
+ *
69
+ * @returns A {@link CommandFinished} instance with populated exit code.
70
+ */
71
+ wait(): Promise<CommandFinished>;
72
+ /**
73
+ * Get the output of `stdout`, `stderr`, or both as a string.
74
+ *
75
+ * NOTE: This may throw string conversion errors if the command does
76
+ * not output valid Unicode.
77
+ *
78
+ * @param stream - The output stream to read: "stdout", "stderr", or "both".
79
+ * @returns The output of the specified stream(s) as a string.
80
+ */
81
+ output(stream?: "stdout" | "stderr" | "both"): Promise<string>;
82
+ /**
83
+ * Get the output of `stdout` as a string.
84
+ *
85
+ * NOTE: This may throw string conversion errors if the command does
86
+ * not output valid Unicode.
87
+ *
88
+ * @returns The standard output of the command.
89
+ */
90
+ stdout(): Promise<string>;
91
+ /**
92
+ * Get the output of `stderr` as a string.
93
+ *
94
+ * NOTE: This may throw string conversion errors if the command does
95
+ * not output valid Unicode.
96
+ *
97
+ * @returns The standard error output of the command.
98
+ */
99
+ stderr(): Promise<string>;
100
+ }
101
+ /**
102
+ * A command that has finished executing.
103
+ *
104
+ * Contains the exit code of the command.
105
+ *
106
+ * @hideconstructor
107
+ */
108
+ export declare class CommandFinished extends Command {
109
+ /**
110
+ * The exit code of the command, if available. This is set after
111
+ * {@link wait} has returned.
112
+ */
113
+ exitCode: number;
114
+ /**
115
+ * @param params - Object containing client, sandbox ID, command ID, and exit code.
116
+ * @param params.client - API client used to interact with the backend.
117
+ * @param params.sandboxId - The ID of the sandbox where the command ran.
118
+ * @param params.cmdId - The ID of the command execution.
119
+ * @param params.exitCode - The exit code of the completed command.
120
+ */
121
+ constructor(params: {
122
+ client: APIClient;
123
+ sandboxId: string;
124
+ cmdId: string;
125
+ exitCode: number;
126
+ });
127
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommandFinished = exports.Command = void 0;
4
+ /**
5
+ * A command executed in a Sandbox.
6
+ *
7
+ * You can {@link wait} on commands to access their {@link CommandFinished.exitCode}, and
8
+ * iterate over their output with {@link logs}.
9
+ *
10
+ * @see {@link Sandbox.runCommand} to start a command.
11
+ *
12
+ * @hideconstructor
13
+ */
14
+ class Command {
15
+ /**
16
+ * @param params - Object containing the client, sandbox ID, and command ID.
17
+ * @param params.client - API client used to interact with the backend.
18
+ * @param params.sandboxId - The ID of the sandbox where the command is running.
19
+ * @param params.cmdId - The ID of the command execution.
20
+ */
21
+ constructor({ client, sandboxId, cmdId, }) {
22
+ this.client = client;
23
+ this.sandboxId = sandboxId;
24
+ this.cmdId = cmdId;
25
+ }
26
+ /**
27
+ * Iterate over the output of this command.
28
+ *
29
+ * ```
30
+ * for await (const log of cmd.logs()) {
31
+ * if (log.stream === "stdout") {
32
+ * process.stdout.write(log.data);
33
+ * } else {
34
+ * process.stderr.write(log.data);
35
+ * }
36
+ * }
37
+ * ```
38
+ *
39
+ * @returns An async iterable of log entries from the command output.
40
+ *
41
+ * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}
42
+ * to access output as a string.
43
+ */
44
+ logs() {
45
+ return this.client.getLogs({
46
+ sandboxId: this.sandboxId,
47
+ cmdId: this.cmdId,
48
+ });
49
+ }
50
+ /**
51
+ * Wait for a command to exit and populate its exit code.
52
+ *
53
+ * ```
54
+ * await cmd.wait()
55
+ * if (cmd.exitCode != 0) {
56
+ * console.error("Something went wrong...")
57
+ * }
58
+ * ```
59
+ *
60
+ * @returns A {@link CommandFinished} instance with populated exit code.
61
+ */
62
+ async wait() {
63
+ const command = await this.client.getCommand({
64
+ sandboxId: this.sandboxId,
65
+ cmdId: this.cmdId,
66
+ wait: true,
67
+ });
68
+ return new CommandFinished({
69
+ client: this.client,
70
+ sandboxId: this.sandboxId,
71
+ cmdId: command.json.cmdId,
72
+ exitCode: command.json.exitCode,
73
+ });
74
+ }
75
+ /**
76
+ * Get the output of `stdout`, `stderr`, or both as a string.
77
+ *
78
+ * NOTE: This may throw string conversion errors if the command does
79
+ * not output valid Unicode.
80
+ *
81
+ * @param stream - The output stream to read: "stdout", "stderr", or "both".
82
+ * @returns The output of the specified stream(s) as a string.
83
+ */
84
+ async output(stream = "both") {
85
+ let data = "";
86
+ for await (const log of this.logs()) {
87
+ if (stream === "both" || log.stream === stream) {
88
+ data += log.data;
89
+ }
90
+ }
91
+ return data;
92
+ }
93
+ /**
94
+ * Get the output of `stdout` as a string.
95
+ *
96
+ * NOTE: This may throw string conversion errors if the command does
97
+ * not output valid Unicode.
98
+ *
99
+ * @returns The standard output of the command.
100
+ */
101
+ async stdout() {
102
+ return this.output("stdout");
103
+ }
104
+ /**
105
+ * Get the output of `stderr` as a string.
106
+ *
107
+ * NOTE: This may throw string conversion errors if the command does
108
+ * not output valid Unicode.
109
+ *
110
+ * @returns The standard error output of the command.
111
+ */
112
+ async stderr() {
113
+ return this.output("stderr");
114
+ }
115
+ }
116
+ exports.Command = Command;
117
+ /**
118
+ * A command that has finished executing.
119
+ *
120
+ * Contains the exit code of the command.
121
+ *
122
+ * @hideconstructor
123
+ */
124
+ class CommandFinished extends Command {
125
+ /**
126
+ * @param params - Object containing client, sandbox ID, command ID, and exit code.
127
+ * @param params.client - API client used to interact with the backend.
128
+ * @param params.sandboxId - The ID of the sandbox where the command ran.
129
+ * @param params.cmdId - The ID of the command execution.
130
+ * @param params.exitCode - The exit code of the completed command.
131
+ */
132
+ constructor(params) {
133
+ super({ ...params });
134
+ this.exitCode = params.exitCode;
135
+ }
136
+ }
137
+ exports.CommandFinished = CommandFinished;
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { SDK, Sandbox, Command } from "./create-sandbox";
1
+ export { Sandbox } from "./sandbox";
2
+ export { Command, CommandFinished } from "./command";
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Command = exports.Sandbox = exports.SDK = void 0;
4
- var create_sandbox_1 = require("./create-sandbox");
5
- Object.defineProperty(exports, "SDK", { enumerable: true, get: function () { return create_sandbox_1.SDK; } });
6
- Object.defineProperty(exports, "Sandbox", { enumerable: true, get: function () { return create_sandbox_1.Sandbox; } });
7
- Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return create_sandbox_1.Command; } });
3
+ exports.CommandFinished = exports.Command = exports.Sandbox = void 0;
4
+ var sandbox_1 = require("./sandbox");
5
+ Object.defineProperty(exports, "Sandbox", { enumerable: true, get: function () { return sandbox_1.Sandbox; } });
6
+ var command_1 = require("./command");
7
+ Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_1.Command; } });
8
+ Object.defineProperty(exports, "CommandFinished", { enumerable: true, get: function () { return command_1.CommandFinished; } });