@vercel/sandbox 1.9.1 → 1.9.3
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/dist/api-client/api-client.cjs +23 -5
- package/dist/api-client/api-client.cjs.map +1 -1
- package/dist/api-client/api-client.js +23 -5
- package/dist/api-client/api-client.js.map +1 -1
- package/dist/auth/file.d.cts +3 -3
- package/dist/auth/file.d.ts +3 -3
- package/dist/command.cjs +2 -0
- package/dist/command.cjs.map +1 -1
- package/dist/command.js +2 -0
- package/dist/command.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -126,16 +126,18 @@ var APIClient = class extends require_base_client.BaseClient {
|
|
|
126
126
|
sandboxId: params.sandboxId
|
|
127
127
|
});
|
|
128
128
|
const jsonlinesStream = jsonlines.default.parse();
|
|
129
|
-
pipe(response.body, jsonlinesStream).catch((err) => {
|
|
129
|
+
pipe(response.body, jsonlinesStream, { signal: params.signal }).catch((err) => {
|
|
130
130
|
console.error("Error piping command stream:", err);
|
|
131
131
|
});
|
|
132
132
|
const iterator = jsonlinesStream[Symbol.asyncIterator]();
|
|
133
133
|
const commandChunk = await iterator.next();
|
|
134
|
+
if (commandChunk.done) throw new require_api_error.StreamError("stream_ended_early", "Stream ended before command data was received", params.sandboxId);
|
|
134
135
|
const { command } = require_validators.CommandResponse.parse(commandChunk.value);
|
|
135
136
|
return {
|
|
136
137
|
command,
|
|
137
138
|
finished: (async () => {
|
|
138
139
|
const finishedChunk = await iterator.next();
|
|
140
|
+
if (finishedChunk.done) throw new require_api_error.StreamError("stream_ended_early", "Stream ended before command finished", params.sandboxId);
|
|
139
141
|
const { command: command$1 } = require_validators.CommandFinishedResponse.parse(finishedChunk.value);
|
|
140
142
|
return command$1;
|
|
141
143
|
})()
|
|
@@ -268,7 +270,7 @@ var APIClient = class extends require_base_client.BaseClient {
|
|
|
268
270
|
sandboxId: params.sandboxId
|
|
269
271
|
});
|
|
270
272
|
const jsonlinesStream = jsonlines.default.parse();
|
|
271
|
-
pipe(response.body, jsonlinesStream).catch((err) => {
|
|
273
|
+
pipe(response.body, jsonlinesStream, { signal }).catch((err) => {
|
|
272
274
|
console.error("Error piping logs:", err);
|
|
273
275
|
});
|
|
274
276
|
for await (const chunk of jsonlinesStream) {
|
|
@@ -340,8 +342,23 @@ var APIClient = class extends require_base_client.BaseClient {
|
|
|
340
342
|
return require_base_client.parseOrThrow(require_validators.SnapshotResponse, await this.request(url, { signal: params.signal }));
|
|
341
343
|
}
|
|
342
344
|
};
|
|
343
|
-
async function pipe(readable, output) {
|
|
345
|
+
async function pipe(readable, output, options) {
|
|
344
346
|
const reader = readable.getReader();
|
|
347
|
+
let aborted = false;
|
|
348
|
+
const signal = options?.signal;
|
|
349
|
+
const onAbort = () => {
|
|
350
|
+
aborted = true;
|
|
351
|
+
const reason = signal?.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
352
|
+
reader.cancel(reason).catch(() => {});
|
|
353
|
+
if ("destroy" in output && typeof output.destroy === "function") {
|
|
354
|
+
output.destroy(reason);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
output.emit("error", reason);
|
|
358
|
+
output.end();
|
|
359
|
+
};
|
|
360
|
+
if (signal) if (signal.aborted) onAbort();
|
|
361
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
345
362
|
try {
|
|
346
363
|
while (true) {
|
|
347
364
|
const read = await reader.read();
|
|
@@ -349,9 +366,10 @@ async function pipe(readable, output) {
|
|
|
349
366
|
if (read.done) break;
|
|
350
367
|
}
|
|
351
368
|
} catch (err) {
|
|
352
|
-
output.emit("error", err);
|
|
369
|
+
if (!aborted) output.emit("error", err);
|
|
353
370
|
} finally {
|
|
354
|
-
|
|
371
|
+
signal?.removeEventListener("abort", onAbort);
|
|
372
|
+
if (!aborted) output.end();
|
|
355
373
|
}
|
|
356
374
|
}
|
|
357
375
|
function mergeSignals(...signals) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.cjs","names":["BaseClient","VERSION","getPrivateParams","parseOrThrow","SandboxAndRoutesResponse","toAPINetworkPolicy","z","APIError","CommandResponse","CommandFinishedResponse","command","EmptyResponse","FileWriter","consumeReadable","SandboxesResponse","SnapshotsResponse","normalizePath","Readable","LogLine","StreamError","SandboxResponse","UpdateNetworkPolicyResponse","ExtendTimeoutResponse","CreateSnapshotResponse","SnapshotResponse"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n CommandFinishedData,\n SandboxAndRoutesResponse,\n SandboxResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n LogLineStdout,\n LogLineStderr,\n SandboxesResponse,\n SnapshotsResponse,\n ExtendTimeoutResponse,\n UpdateNetworkPolicyResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSandbox(\n params: WithPrivate<{ sandboxId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(\"/v1/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream).catch((err) => {\n console.error(\"Error piping command stream:\", err);\n });\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sandboxId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSandboxes(params: {\n /**\n * The ID or name of the project to which the sandboxes belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of sandboxes to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get sandboxes created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get sandboxes created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesResponse,\n await this.request(`/v1/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get snapshots created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get snapshots created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v1/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sandboxId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sandboxId: params.sandboxId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sandboxId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sandboxId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sandboxId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSandbox(params: {\n sandboxId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SandboxResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/stop`;\n const response = await parseOrThrow(\n SandboxResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let sandbox = response.json.sandbox;\n while (\n sandbox.status !== \"stopped\" &&\n sandbox.status !== \"failed\" &&\n sandbox.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSandbox({\n sandboxId: params.sandboxId,\n signal: params.signal,\n });\n sandbox = poll.json.sandbox;\n response.json.sandbox = sandbox;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sandboxId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof UpdateNetworkPolicyResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/network-policy`;\n return parseOrThrow(\n UpdateNetworkPolicyResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sandboxId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;\n return parseOrThrow(\n ExtendTimeoutResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sandboxId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n) {\n const reader = readable.getReader();\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n output.emit(\"error\", err);\n } finally {\n output.end();\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgDA,SAAS,sBAAsB,OAA8B;AAC3D,KAAI,MAAM,MAAM,IAAI,CAAC,WAAW,EAC9B,QAAO;AAET,KAAI;EACF,MAAM,UAAU,KAAK,MACnB,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC,IAAI,YAAY,CAAC,SAAS,OAAO,CAC/D;AACD,MAAI,QAAQ,SACV,QAAO;GAAE,UAAU,QAAQ;GAAU,YAAY,QAAQ;GAAY;AAEvE,SAAO;SACD;AACN,SAAO;;;AAQX,IAAa,YAAb,cAA+BA,+BAAW;CAKxC,YAAY,QAKT;AACD,QAAM;GACJ,SAAS,OAAO,WAAW;GAC3B,OAAO,OAAO;GACd,OAAO;GACP,OAAO,OAAO;GACf,CAAC;AAEF,OAAK,SAAS,OAAO;AACrB,OAAK,aAAa;EAElB,MAAM,SAAS,sBAAsB,OAAO,MAAM;AAClD,MAAI,QAAQ;AACV,QAAK,aAAa;AAClB,QAAK,YAAY,OAAO;AACxB,QAAK,SAAS,OAAO;;;CAIzB,MAAc,mBAAkC;AAC9C,MAAI,CAAC,KAAK,WACR;AAGF,MAAI;GAEF,MAAM,aAAa,4CAAyB;IAC1C,oBAAoB,MAAS;IAC7B,MAAM,KAAK;IACX,SAAS,KAAK;IACf,CAAC;AAGF,OAAI,eAAe,KAAK,OAAO;AAC7B,SAAK,QAAQ;IAEb,MAAM,SAAS,sBAAsB,WAAW;AAChD,QAAI,OACF,MAAK,SAAS,OAAO;;UAGnB;;CAKV,MAAgB,QAAQ,MAAc,QAAwB;AAC5D,QAAM,KAAK,kBAAkB;AAE7B,SAAO,MAAM,QAAQ,MAAM;GACzB,GAAG;GACH,OAAO;IAAE,QAAQ,KAAK;IAAQ,GAAG,QAAQ;IAAO;GAChD,SAAS;IACP,gBAAgB;IAChB,cAAc,kBAAkBC,wBAAQ,YAAY,QAAQ,QAAQ,IAAI,WAAG,UAAU,CAAC,GAAG,WAAG,MAAM,CAAC;IACnG,GAAG,QAAQ;IACZ;GACF,CAAC;;CAGJ,MAAM,WACJ,QACA;EACA,MAAM,gBAAgBC,+BAAiB,OAAO;EAC9C,IAAI,cAAc,IAAI,gBAAgB,cAAc,CAAC,UAAU;AAC/D,gBAAc,cAAc,IAAI,gBAAgB;AAChD,SAAOC,iCACLC,6CACA,MAAM,KAAK,QAAQ,iBAAiB,OAAO,YAAY,eAAe,EACpE,QAAQ,OAAO,QAChB,CAAC,CACH;;CAGH,MAAM,cACJ,QAqBA;EACA,MAAM,gBAAgBF,+BAAiB,OAAO;AAC9C,SAAOC,iCACLC,6CACA,MAAM,KAAK,QAAQ,iBAAiB;GAClC,QAAQ;GACR,MAAM,KAAK,UAAU;IACnB,WAAW,OAAO;IAClB,OAAO,OAAO;IACd,QAAQ,OAAO;IACf,SAAS,OAAO;IAChB,WAAW,OAAO;IAClB,SAAS,OAAO;IAChB,eAAe,OAAO,gBAClBC,0CAAmB,OAAO,cAAc,GACxC;IACJ,KAAK,OAAO;IACZ,GAAG;IACJ,CAAC;GACF,QAAQ,OAAO;GAChB,CAAC,CACH;;CAuBH,MAAM,WAAW,QASd;AACD,MAAI,OAAO,MAAM;GACf,MAAM,WAAW,MAAM,KAAK,QAC1B,iBAAiB,OAAO,UAAU,OAClC;IACE,QAAQ;IACR,MAAM,KAAK,UAAU;KACnB,SAAS,OAAO;KAChB,MAAM,OAAO;KACb,KAAK,OAAO;KACZ,KAAK,OAAO;KACZ,MAAM,OAAO;KACb,MAAM;KACP,CAAC;IACF,QAAQ,OAAO;IAChB,CACF;AAED,OAAI,CAAC,SAAS,GACZ,OAAMF,iCAAaG,MAAE,KAAK,EAAE,SAAS;AAGvC,OAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,uBAC3C,OAAM,IAAIC,2BAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;AAGJ,OAAI,SAAS,SAAS,KACpB,OAAM,IAAIA,2BAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;GAGJ,MAAM,kBAAkB,kBAAU,OAAO;AACzC,QAAK,SAAS,MAAM,gBAAgB,CAAC,OAAO,QAAQ;AAClD,YAAQ,MAAM,gCAAgC,IAAI;KAClD;GAEF,MAAM,WAAW,gBAAgB,OAAO,gBAAgB;GACxD,MAAM,eAAe,MAAM,SAAS,MAAM;GAC1C,MAAM,EAAE,YAAYC,mCAAgB,MAAM,aAAa,MAAM;AAQ7D,UAAO;IAAE;IAAS,WANA,YAAY;KAC5B,MAAM,gBAAgB,MAAM,SAAS,MAAM;KAC3C,MAAM,EAAE,uBAAYC,2CAAwB,MAAM,cAAc,MAAM;AACtE,YAAOC;QACL;IAEwB;;AAG9B,SAAOP,iCACLK,oCACA,MAAM,KAAK,QAAQ,iBAAiB,OAAO,UAAU,OAAO;GAC1D,QAAQ;GACR,MAAM,KAAK,UAAU;IACnB,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,KAAK,OAAO;IACZ,KAAK,OAAO;IACZ,MAAM,OAAO;IACd,CAAC;GACF,QAAQ,OAAO;GAChB,CAAC,CACH;;CAeH,MAAM,WAAW,QAKd;AACD,SAAO,OAAO,OACVL,iCACEM,4CACA,MAAM,KAAK,QACT,iBAAiB,OAAO,UAAU,OAAO,OAAO,SAChD;GAAE,QAAQ,OAAO;GAAQ,OAAO,EAAE,MAAM,QAAQ;GAAE,CACnD,CACF,GACDN,iCACEK,oCACA,MAAM,KAAK,QACT,iBAAiB,OAAO,UAAU,OAAO,OAAO,SAChD,EAAE,QAAQ,OAAO,QAAQ,CAC1B,CACF;;CAGP,MAAM,MAAM,QAKT;AACD,SAAOL,iCACLQ,kCACA,MAAM,KAAK,QAAQ,iBAAiB,OAAO,UAAU,YAAY;GAC/D,QAAQ;GACR,MAAM,KAAK,UAAU;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;IAAK,CAAC;GAC5D,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,cAAc,QAIX;EACD,MAAM,SAAS,IAAIC,gCAAY;AAC/B,SAAO;GACL,WAAW,YAAY;AACrB,WAAO,KAAK,QAAQ,iBAAiB,OAAO,UAAU,YAAY;KAChE,QAAQ;KACR,SAAS;MACP,gBAAgB;MAChB,SAAS,OAAO;MACjB;KACD,MAAM,MAAMC,yCAAgB,OAAO,SAAS;KAC5C,QAAQ,OAAO;KAChB,CAAC;OACA;GACJ;GACD;;CAGH,MAAM,cAAc,QAsBjB;AACD,SAAOV,iCACLW,sCACA,MAAM,KAAK,QAAQ,iBAAiB;GAClC,OAAO;IACL,SAAS,OAAO;IAChB,OAAO,OAAO;IACd,OACE,OAAO,OAAO,UAAU,WACpB,OAAO,QACP,OAAO,OAAO,SAAS;IAC7B,OACE,OAAO,OAAO,UAAU,WACpB,OAAO,QACP,OAAO,OAAO,SAAS;IAC9B;GACD,QAAQ;GACR,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAsBjB;AACD,SAAOX,iCACLY,sCACA,MAAM,KAAK,QAAQ,2BAA2B;GAC5C,OAAO;IACL,SAAS,OAAO;IAChB,OAAO,OAAO;IACd,OACE,OAAO,OAAO,UAAU,WACpB,OAAO,QACP,OAAO,OAAO,SAAS;IAC7B,OACE,OAAO,OAAO,UAAU,WACpB,OAAO,QACP,OAAO,OAAO,SAAS;IAC9B;GACD,QAAQ;GACR,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,WAAW,QAUd;EACD,MAAM,EAAE,QAAQ,aAAa,KAAK,cAAc;GAC9C,WAAW,OAAO;GAClB,YAAY,OAAO;GACnB,QAAQ,OAAO;GAChB,CAAC;AAEF,OAAK,MAAM,QAAQ,OAAO,MACxB,OAAM,OAAO,QAAQ;GACnB,MAAMC,oCAAc;IAClB,UAAU,KAAK;IACf,YAAY,OAAO;IACnB,KAAK,OAAO;IACb,CAAC;GACF,SAAS,KAAK;GACd,MAAM,KAAK;GACZ,CAAC;AAGJ,SAAO,KAAK;AACZ,QAAMb,iCAAaQ,kCAAe,MAAM,SAAS;;CAGnD,MAAM,SAAS,QAKc;EAC3B,MAAM,WAAW,MAAM,KAAK,QAC1B,iBAAiB,OAAO,UAAU,WAClC;GACE,QAAQ;GACR,MAAM,KAAK,UAAU;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;IAAK,CAAC;GAC5D,QAAQ,OAAO;GAChB,CACF;AAED,MAAI,SAAS,WAAW,IACtB,QAAO;AAGT,MAAI,SAAS,SAAS,KACpB,QAAO;AAGT,SAAOM,gBAAS,QAAQ,SAAS,KAAK;;CAGxC,MAAM,YAAY,QAKf;AACD,SAAOd,iCACLK,oCACA,MAAM,KAAK,QACT,iBAAiB,OAAO,UAAU,GAAG,OAAO,UAAU,QACtD;GACE,QAAQ;GACR,MAAM,KAAK,UAAU,EAAE,QAAQ,OAAO,QAAQ,CAAC;GAC/C,QAAQ,OAAO;GAChB,CACF,CACF;;CAGH,QAAQ,QASyB;EAC/B,MAAM,OAAO;EACb,MAAM,WAAW,IAAI,iBAAiB;EACtC,MAAM,SAAS,CAAC,OAAO,SACnB,SAAS,SACT,aAAa,OAAO,QAAQ,SAAS,OAAO;EAEhD,MAAM,aAAa,mBAAmB;GACpC,MAAM,MAAM,iBAAiB,OAAO,UAAU,OAAO,OAAO,MAAM;GAClE,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK;IACvC,QAAQ;IACR;IACD,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAML,iCAAaG,MAAE,KAAK,EAAE,SAAS;AAGvC,OAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,uBAC3C,OAAM,IAAIC,2BAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;AAGJ,OAAI,SAAS,SAAS,KACpB,OAAM,IAAIA,2BAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;GAGJ,MAAM,kBAAkB,kBAAU,OAAO;AACzC,QAAK,SAAS,MAAM,gBAAgB,CAAC,OAAO,QAAQ;AAClD,YAAQ,MAAM,sBAAsB,IAAI;KACxC;AAEF,cAAW,MAAM,SAAS,iBAAiB;IACzC,MAAM,SAASW,2BAAQ,MAAM,MAAM;AACnC,QAAI,OAAO,WAAW,QACpB,OAAM,IAAIC,8BACR,OAAO,KAAK,MACZ,OAAO,KAAK,SACZ,OAAO,UACR;AAEH,UAAM;;MAEN;AAEJ,SAAO,OAAO,OAAO,WAAW;GAC9B,CAAC,OAAO,WAAW;AACjB,aAAS,MAAM,WAAW;;GAE5B,aAAa,SAAS,MAAM,WAAW;GACxC,CAAC;;CAGJ,MAAM,YAAY,QAImC;EACnD,MAAM,MAAM,iBAAiB,OAAO,UAAU;EAC9C,MAAM,WAAW,MAAMhB,iCACrBiB,oCACA,MAAM,KAAK,QAAQ,KAAK;GAAE,QAAQ;GAAQ,QAAQ,OAAO;GAAQ,CAAC,CACnE;AAED,MAAI,OAAO,UAAU;GACnB,IAAI,UAAU,SAAS,KAAK;AAC5B,UACE,QAAQ,WAAW,aACnB,QAAQ,WAAW,YACnB,QAAQ,WAAW,WACnB;AACA,+CAAiB,KAAK,QAAW,EAAE,QAAQ,OAAO,QAAQ,CAAC;AAK3D,eAJa,MAAM,KAAK,WAAW;KACjC,WAAW,OAAO;KAClB,QAAQ,OAAO;KAChB,CAAC,EACa,KAAK;AACpB,aAAS,KAAK,UAAU;;;AAI5B,SAAO;;CAGT,MAAM,oBAAoB,QAIuC;EAC/D,MAAM,MAAM,iBAAiB,OAAO,UAAU;AAC9C,SAAOjB,iCACLkB,gDACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR,MAAM,KAAK,UAAUhB,0CAAmB,OAAO,cAAc,CAAC;GAC9D,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAIuC;EACzD,MAAM,MAAM,iBAAiB,OAAO,UAAU;AAC9C,SAAOF,iCACLmB,0CACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR,MAAM,KAAK,UAAU,EAAE,UAAU,OAAO,UAAU,CAAC;GACnD,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,eAAe,QAIuC;EAC1D,MAAM,MAAM,iBAAiB,OAAO,UAAU;EAC9C,MAAM,OACJ,OAAO,eAAe,SAClB,SACA,KAAK,UAAU,EAAE,YAAY,OAAO,YAAY,CAAC;AACvD,SAAOnB,iCACLoB,2CACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR;GACA,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,eAAe,QAGiC;EACpD,MAAM,MAAM,2BAA2B,OAAO;AAC9C,SAAOpB,iCACLqB,qCACA,MAAM,KAAK,QAAQ,KAAK;GAAE,QAAQ;GAAU,QAAQ,OAAO;GAAQ,CAAC,CACrE;;CAGH,MAAM,YAAY,QAGoC;EACpD,MAAM,MAAM,2BAA2B,OAAO;AAC9C,SAAOrB,iCACLqB,qCACA,MAAM,KAAK,QAAQ,KAAK,EAAE,QAAQ,OAAO,QAAQ,CAAC,CACnD;;;AAIL,eAAe,KACb,UACA,QACA;CACA,MAAM,SAAS,SAAS,WAAW;AACnC,KAAI;AACF,SAAO,MAAM;GACX,MAAM,OAAO,MAAM,OAAO,MAAM;AAChC,OAAI,KAAK,MACP,QAAO,MAAM,OAAO,KAAK,KAAK,MAAM,CAAC;AAEvC,OAAI,KAAK,KACP;;UAGG,KAAK;AACZ,SAAO,KAAK,SAAS,IAAI;WACjB;AACR,SAAO,KAAK;;;AAIhB,SAAS,aAAa,GAAG,SAA0C;CACjE,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,gBAAgB;AACpB,aAAW,OAAO;AAClB,OAAK,MAAM,UAAU,QACnB,QAAO,oBAAoB,SAAS,QAAQ;;AAGhD,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,OAAO,SAAS;AAClB,cAAW,OAAO;AAClB;;AAEF,SAAO,iBAAiB,SAAS,QAAQ;;AAE3C,QAAO,WAAW"}
|
|
1
|
+
{"version":3,"file":"api-client.cjs","names":["BaseClient","VERSION","getPrivateParams","parseOrThrow","SandboxAndRoutesResponse","toAPINetworkPolicy","z","APIError","StreamError","CommandResponse","CommandFinishedResponse","command","EmptyResponse","FileWriter","consumeReadable","SandboxesResponse","SnapshotsResponse","normalizePath","Readable","LogLine","SandboxResponse","UpdateNetworkPolicyResponse","ExtendTimeoutResponse","CreateSnapshotResponse","SnapshotResponse"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n CommandFinishedData,\n SandboxAndRoutesResponse,\n SandboxResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n LogLineStdout,\n LogLineStderr,\n SandboxesResponse,\n SnapshotsResponse,\n ExtendTimeoutResponse,\n UpdateNetworkPolicyResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSandbox(\n params: WithPrivate<{ sandboxId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(\"/v1/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sandboxId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sandboxId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sandboxId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSandboxes(params: {\n /**\n * The ID or name of the project to which the sandboxes belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of sandboxes to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get sandboxes created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get sandboxes created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesResponse,\n await this.request(`/v1/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get snapshots created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get snapshots created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v1/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sandboxId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sandboxId: params.sandboxId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sandboxId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sandboxId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sandboxId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSandbox(params: {\n sandboxId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SandboxResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/stop`;\n const response = await parseOrThrow(\n SandboxResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let sandbox = response.json.sandbox;\n while (\n sandbox.status !== \"stopped\" &&\n sandbox.status !== \"failed\" &&\n sandbox.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSandbox({\n sandboxId: params.sandboxId,\n signal: params.signal,\n });\n sandbox = poll.json.sandbox;\n response.json.sandbox = sandbox;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sandboxId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof UpdateNetworkPolicyResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/network-policy`;\n return parseOrThrow(\n UpdateNetworkPolicyResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sandboxId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;\n return parseOrThrow(\n ExtendTimeoutResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sandboxId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n"],"mappings":"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"}
|
|
@@ -123,16 +123,18 @@ var APIClient = class extends BaseClient {
|
|
|
123
123
|
sandboxId: params.sandboxId
|
|
124
124
|
});
|
|
125
125
|
const jsonlinesStream = jsonlines.parse();
|
|
126
|
-
pipe(response.body, jsonlinesStream).catch((err) => {
|
|
126
|
+
pipe(response.body, jsonlinesStream, { signal: params.signal }).catch((err) => {
|
|
127
127
|
console.error("Error piping command stream:", err);
|
|
128
128
|
});
|
|
129
129
|
const iterator = jsonlinesStream[Symbol.asyncIterator]();
|
|
130
130
|
const commandChunk = await iterator.next();
|
|
131
|
+
if (commandChunk.done) throw new StreamError("stream_ended_early", "Stream ended before command data was received", params.sandboxId);
|
|
131
132
|
const { command } = CommandResponse.parse(commandChunk.value);
|
|
132
133
|
return {
|
|
133
134
|
command,
|
|
134
135
|
finished: (async () => {
|
|
135
136
|
const finishedChunk = await iterator.next();
|
|
137
|
+
if (finishedChunk.done) throw new StreamError("stream_ended_early", "Stream ended before command finished", params.sandboxId);
|
|
136
138
|
const { command: command$1 } = CommandFinishedResponse.parse(finishedChunk.value);
|
|
137
139
|
return command$1;
|
|
138
140
|
})()
|
|
@@ -265,7 +267,7 @@ var APIClient = class extends BaseClient {
|
|
|
265
267
|
sandboxId: params.sandboxId
|
|
266
268
|
});
|
|
267
269
|
const jsonlinesStream = jsonlines.parse();
|
|
268
|
-
pipe(response.body, jsonlinesStream).catch((err) => {
|
|
270
|
+
pipe(response.body, jsonlinesStream, { signal }).catch((err) => {
|
|
269
271
|
console.error("Error piping logs:", err);
|
|
270
272
|
});
|
|
271
273
|
for await (const chunk of jsonlinesStream) {
|
|
@@ -337,8 +339,23 @@ var APIClient = class extends BaseClient {
|
|
|
337
339
|
return parseOrThrow(SnapshotResponse, await this.request(url, { signal: params.signal }));
|
|
338
340
|
}
|
|
339
341
|
};
|
|
340
|
-
async function pipe(readable, output) {
|
|
342
|
+
async function pipe(readable, output, options) {
|
|
341
343
|
const reader = readable.getReader();
|
|
344
|
+
let aborted = false;
|
|
345
|
+
const signal = options?.signal;
|
|
346
|
+
const onAbort = () => {
|
|
347
|
+
aborted = true;
|
|
348
|
+
const reason = signal?.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
349
|
+
reader.cancel(reason).catch(() => {});
|
|
350
|
+
if ("destroy" in output && typeof output.destroy === "function") {
|
|
351
|
+
output.destroy(reason);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
output.emit("error", reason);
|
|
355
|
+
output.end();
|
|
356
|
+
};
|
|
357
|
+
if (signal) if (signal.aborted) onAbort();
|
|
358
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
342
359
|
try {
|
|
343
360
|
while (true) {
|
|
344
361
|
const read = await reader.read();
|
|
@@ -346,9 +363,10 @@ async function pipe(readable, output) {
|
|
|
346
363
|
if (read.done) break;
|
|
347
364
|
}
|
|
348
365
|
} catch (err) {
|
|
349
|
-
output.emit("error", err);
|
|
366
|
+
if (!aborted) output.emit("error", err);
|
|
350
367
|
} finally {
|
|
351
|
-
|
|
368
|
+
signal?.removeEventListener("abort", onAbort);
|
|
369
|
+
if (!aborted) output.end();
|
|
352
370
|
}
|
|
353
371
|
}
|
|
354
372
|
function mergeSignals(...signals) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","names":["command"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n CommandFinishedData,\n SandboxAndRoutesResponse,\n SandboxResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n LogLineStdout,\n LogLineStderr,\n SandboxesResponse,\n SnapshotsResponse,\n ExtendTimeoutResponse,\n UpdateNetworkPolicyResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSandbox(\n params: WithPrivate<{ sandboxId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(\"/v1/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream).catch((err) => {\n console.error(\"Error piping command stream:\", err);\n });\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sandboxId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSandboxes(params: {\n /**\n * The ID or name of the project to which the sandboxes belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of sandboxes to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get sandboxes created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get sandboxes created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesResponse,\n await this.request(`/v1/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get snapshots created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get snapshots created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v1/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sandboxId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sandboxId: params.sandboxId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sandboxId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sandboxId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sandboxId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSandbox(params: {\n sandboxId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SandboxResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/stop`;\n const response = await parseOrThrow(\n SandboxResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let sandbox = response.json.sandbox;\n while (\n sandbox.status !== \"stopped\" &&\n sandbox.status !== \"failed\" &&\n sandbox.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSandbox({\n sandboxId: params.sandboxId,\n signal: params.signal,\n });\n sandbox = poll.json.sandbox;\n response.json.sandbox = sandbox;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sandboxId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof UpdateNetworkPolicyResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/network-policy`;\n return parseOrThrow(\n UpdateNetworkPolicyResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sandboxId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;\n return parseOrThrow(\n ExtendTimeoutResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sandboxId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n) {\n const reader = readable.getReader();\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n output.emit(\"error\", err);\n } finally {\n output.end();\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-client.js","names":["command"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n CommandFinishedData,\n SandboxAndRoutesResponse,\n SandboxResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n LogLineStdout,\n LogLineStderr,\n SandboxesResponse,\n SnapshotsResponse,\n ExtendTimeoutResponse,\n UpdateNetworkPolicyResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSandbox(\n params: WithPrivate<{ sandboxId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndRoutesResponse,\n await this.request(\"/v1/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sandboxId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sandboxId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sandboxId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sandboxId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sandboxId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSandboxes(params: {\n /**\n * The ID or name of the project to which the sandboxes belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of sandboxes to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get sandboxes created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get sandboxes created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesResponse,\n await this.request(`/v1/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Get snapshots created after this JavaScript timestamp.\n * @example 1540095775941\n */\n since?: number | Date;\n /**\n * Get snapshots created before this JavaScript timestamp.\n * @example 1540095775951\n */\n until?: number | Date;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v1/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n since:\n typeof params.since === \"number\"\n ? params.since\n : params.since?.getTime(),\n until:\n typeof params.until === \"number\"\n ? params.until\n : params.until?.getTime(),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sandboxId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sandboxId: params.sandboxId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sandboxId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v1/sandboxes/${params.sandboxId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sandboxId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sandboxId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sandboxId: params.sandboxId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sandboxId: params.sandboxId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sandboxId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSandbox(params: {\n sandboxId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SandboxResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/stop`;\n const response = await parseOrThrow(\n SandboxResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let sandbox = response.json.sandbox;\n while (\n sandbox.status !== \"stopped\" &&\n sandbox.status !== \"failed\" &&\n sandbox.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSandbox({\n sandboxId: params.sandboxId,\n signal: params.signal,\n });\n sandbox = poll.json.sandbox;\n response.json.sandbox = sandbox;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sandboxId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof UpdateNetworkPolicyResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/network-policy`;\n return parseOrThrow(\n UpdateNetworkPolicyResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sandboxId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;\n return parseOrThrow(\n ExtendTimeoutResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sandboxId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v1/sandboxes/${params.sandboxId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n"],"mappings":"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"}
|
package/dist/auth/file.d.cts
CHANGED
|
@@ -7,18 +7,18 @@ declare const AuthFile: z.ZodObject<{
|
|
|
7
7
|
expiresAt: z.ZodOptional<z.ZodEffects<z.ZodNumber, Date, number>>;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
9
|
token?: string | undefined;
|
|
10
|
-
refreshToken?: string | undefined;
|
|
11
10
|
expiresAt?: Date | undefined;
|
|
11
|
+
refreshToken?: string | undefined;
|
|
12
12
|
}, {
|
|
13
13
|
token?: string | undefined;
|
|
14
|
-
refreshToken?: string | undefined;
|
|
15
14
|
expiresAt?: number | undefined;
|
|
15
|
+
refreshToken?: string | undefined;
|
|
16
16
|
}>;
|
|
17
17
|
type AuthFile = z.infer<typeof AuthFile>;
|
|
18
18
|
declare const getAuth: () => {
|
|
19
19
|
token?: string | undefined;
|
|
20
|
-
refreshToken?: string | undefined;
|
|
21
20
|
expiresAt?: Date | undefined;
|
|
21
|
+
refreshToken?: string | undefined;
|
|
22
22
|
} | null;
|
|
23
23
|
declare function updateAuthConfig(config: AuthFile): void;
|
|
24
24
|
//#endregion
|
package/dist/auth/file.d.ts
CHANGED
|
@@ -7,18 +7,18 @@ declare const AuthFile: z.ZodObject<{
|
|
|
7
7
|
expiresAt: z.ZodOptional<z.ZodEffects<z.ZodNumber, Date, number>>;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
9
|
token?: string | undefined;
|
|
10
|
-
refreshToken?: string | undefined;
|
|
11
10
|
expiresAt?: Date | undefined;
|
|
11
|
+
refreshToken?: string | undefined;
|
|
12
12
|
}, {
|
|
13
13
|
token?: string | undefined;
|
|
14
|
-
refreshToken?: string | undefined;
|
|
15
14
|
expiresAt?: number | undefined;
|
|
15
|
+
refreshToken?: string | undefined;
|
|
16
16
|
}>;
|
|
17
17
|
type AuthFile = z.infer<typeof AuthFile>;
|
|
18
18
|
declare const getAuth: () => {
|
|
19
19
|
token?: string | undefined;
|
|
20
|
-
refreshToken?: string | undefined;
|
|
21
20
|
expiresAt?: Date | undefined;
|
|
21
|
+
refreshToken?: string | undefined;
|
|
22
22
|
} | null;
|
|
23
23
|
declare function updateAuthConfig(config: AuthFile): void;
|
|
24
24
|
//#endregion
|
package/dist/command.cjs
CHANGED
|
@@ -171,6 +171,8 @@ var Command = class Command {
|
|
|
171
171
|
async getCachedOutput(opts) {
|
|
172
172
|
if (!this.outputCache) this.outputCache = (async () => {
|
|
173
173
|
try {
|
|
174
|
+
opts?.signal?.throwIfAborted();
|
|
175
|
+
await this.ensureClient();
|
|
174
176
|
let stdout = "";
|
|
175
177
|
let stderr = "";
|
|
176
178
|
let both = "";
|
package/dist/command.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","serialized: SerializedCommand","WORKFLOW_DESERIALIZE","resolveSignal"],"sources":["../src/command.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { APIClient, type CommandData } from \"./api-client/index.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\nimport { resolveSignal, type Signal } from \"./utils/resolveSignal.js\";\n\n/**\n * Cached output from a command execution.\n */\nexport interface CommandOutput {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Serialized representation of a Command for @workflow/serde.\n */\nexport interface SerializedCommand {\n sandboxId: string;\n cmd: CommandData;\n /** Cached output, included if output was fetched before serialization */\n output?: CommandOutput;\n}\n\n/**\n * Serialized representation of a CommandFinished for @workflow/serde.\n */\nexport interface SerializedCommandFinished extends SerializedCommand {\n exitCode: number;\n}\n\n/**\n * A command executed in a Sandbox.\n *\n * For detached commands, you can {@link wait} to get a {@link CommandFinished} instance\n * with the populated exit code. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * You can iterate over command output with {@link logs}.\n *\n * @see {@link Sandbox.runCommand} to start a command.\n *\n * @hideconstructor\n */\nexport class Command {\n /**\n * Cached API client instance.\n * @internal\n */\n protected _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n protected async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * ID of the sandbox this command is running in.\n */\n protected sandboxId: string;\n\n /**\n * Data for the command execution.\n */\n protected cmd: CommandData;\n\n public exitCode: number | null;\n\n protected outputCache: Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> | null = null;\n\n /**\n * Synchronously accessible resolved output, populated after output is fetched.\n * Used for serialization.\n * @internal\n */\n protected _resolvedOutput: CommandOutput | null = null;\n\n /**\n * ID of the command execution.\n */\n get cmdId() {\n return this.cmd.id;\n }\n\n get cwd() {\n return this.cmd.cwd;\n }\n\n get startedAt() {\n return this.cmd.startedAt;\n }\n\n /**\n * @param params - Object containing the client, sandbox ID, and command data.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command is running.\n * @param params.cmd - The command data.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor({\n client,\n sandboxId,\n cmd,\n output,\n }: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n output?: CommandOutput;\n }) {\n this._client = client ?? null;\n this.sandboxId = sandboxId;\n this.cmd = cmd;\n this.exitCode = cmd.exitCode ?? null;\n if (output) {\n this._resolvedOutput = output;\n // Note: `both` is reconstructed as stdout + stderr concatenation,\n // which loses the original interleaved order of the streams.\n this.outputCache = Promise.resolve({\n stdout: output.stdout,\n stderr: output.stderr,\n both: output.stdout + output.stderr,\n });\n }\n }\n\n /**\n * Serialize a Command instance to plain data for @workflow/serde.\n *\n * @param instance - The Command instance to serialize\n * @returns A plain object containing the sandbox ID, command data, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](instance: Command): SerializedCommand {\n const serialized: SerializedCommand = {\n sandboxId: instance.sandboxId,\n cmd: instance.cmd,\n };\n if (instance._resolvedOutput) {\n serialized.output = instance._resolvedOutput;\n }\n return serialized;\n }\n\n /**\n * Deserialize plain data back into a Command instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command data\n * @returns The reconstructed Command instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedCommand): Command {\n return new Command({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n output: data.output,\n });\n }\n\n /**\n * Iterate over the output of this command.\n *\n * ```\n * for await (const log of cmd.logs()) {\n * if (log.stream === \"stdout\") {\n * process.stdout.write(log.data);\n * } else {\n * process.stderr.write(log.data);\n * }\n * }\n * ```\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel log streaming.\n * @returns An async iterable of log entries from the command output.\n *\n * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}\n * to access output as a string.\n */\n logs(opts?: { signal?: AbortSignal }) {\n if (!this._client) {\n throw new Error(\n \"logs() requires an API client. Call an async method first to initialize the client.\",\n );\n }\n return this._client.getLogs({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n signal: opts?.signal,\n });\n }\n\n /**\n * Wait for a command to exit and populate its exit code.\n *\n * This method is useful for detached commands where you need to wait\n * for completion. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * ```\n * const detachedCmd = await sandbox.runCommand({ cmd: 'sleep', args: ['5'], detached: true });\n * const result = await detachedCmd.wait();\n * if (result.exitCode !== 0) {\n * console.error(\"Something went wrong...\")\n * }\n * ```\n *\n * @param params - Optional parameters.\n * @param params.signal - An AbortSignal to cancel waiting.\n * @returns A {@link CommandFinished} instance with populated exit code.\n */\n async wait(params?: { signal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n params?.signal?.throwIfAborted();\n\n const command = await client.getCommand({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n wait: true,\n signal: params?.signal,\n });\n\n return new CommandFinished({\n client,\n sandboxId: this.sandboxId,\n cmd: command.json.command,\n exitCode: command.json.command.exitCode,\n });\n }\n\n /**\n * Get cached output, fetching logs only once and reusing for concurrent calls.\n * This prevents race conditions when stdout() and stderr() are called in parallel.\n */\n protected async getCachedOutput(opts?: { signal?: AbortSignal }): Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> {\n if (!this.outputCache) {\n this.outputCache = (async () => {\n try {\n let stdout = \"\";\n let stderr = \"\";\n let both = \"\";\n for await (const log of this.logs({ signal: opts?.signal })) {\n both += log.data;\n if (log.stream === \"stdout\") {\n stdout += log.data;\n } else {\n stderr += log.data;\n }\n }\n // Store resolved output for serialization\n this._resolvedOutput = { stdout, stderr };\n return { stdout, stderr, both };\n } catch (err) {\n // Clear the promise so future calls can retry\n this.outputCache = null;\n throw err;\n }\n })();\n }\n\n return this.outputCache;\n }\n\n /**\n * Get the output of `stdout`, `stderr`, or both as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param stream - The output stream to read: \"stdout\", \"stderr\", or \"both\".\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The output of the specified stream(s) as a string.\n */\n async output(\n stream: \"stdout\" | \"stderr\" | \"both\" = \"both\",\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const cached = await this.getCachedOutput(opts);\n return cached[stream];\n }\n\n /**\n * Get the output of `stdout` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard output of the command.\n */\n async stdout(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stdout\", opts);\n }\n\n /**\n * Get the output of `stderr` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard error output of the command.\n */\n async stderr(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stderr\", opts);\n }\n\n /**\n * Kill a running command in a sandbox.\n *\n * @param signal - The signal to send the running process. Defaults to SIGTERM.\n * @param opts - Optional parameters.\n * @param opts.abortSignal - An AbortSignal to cancel the kill operation.\n * @returns Promise<void>.\n */\n async kill(signal?: Signal, opts?: { abortSignal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n await client.killCommand({\n sandboxId: this.sandboxId,\n commandId: this.cmd.id,\n signal: resolveSignal(signal ?? \"SIGTERM\"),\n abortSignal: opts?.abortSignal,\n });\n }\n}\n\n/**\n * A command that has finished executing.\n *\n * The exit code is immediately available and populated upon creation.\n * Unlike {@link Command}, you don't need to call wait() - the command\n * has already completed execution.\n *\n * @hideconstructor\n */\nexport class CommandFinished extends Command {\n /**\n * The exit code of the command. This is always populated for\n * CommandFinished instances.\n */\n public exitCode: number;\n\n /**\n * @param params - Object containing client, sandbox ID, command data, and exit code.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command ran.\n * @param params.cmd - The command data.\n * @param params.exitCode - The exit code of the completed command.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor(params: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n exitCode: number;\n output?: CommandOutput;\n }) {\n super({ ...params });\n this.exitCode = params.exitCode;\n }\n\n /**\n * Serialize a CommandFinished instance to plain data for @workflow/serde.\n *\n * @param instance - The CommandFinished instance to serialize\n * @returns A plain object containing the sandbox ID, command data, exit code, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](\n instance: CommandFinished,\n ): SerializedCommandFinished {\n return {\n ...Command[WORKFLOW_SERIALIZE](instance),\n exitCode: instance.exitCode,\n };\n }\n\n /**\n * Deserialize plain data back into a CommandFinished instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command finished data\n * @returns The reconstructed CommandFinished instance\n */\n static [WORKFLOW_DESERIALIZE](\n data: SerializedCommandFinished,\n ): CommandFinished {\n return new CommandFinished({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n exitCode: data.exitCode,\n output: data.output,\n });\n }\n\n /**\n * The wait method is not needed for CommandFinished instances since\n * the command has already completed and exitCode is populated.\n *\n * @deprecated This method is redundant for CommandFinished instances.\n * The exitCode is already available.\n * @returns This CommandFinished instance.\n */\n async wait(): Promise<CommandFinished> {\n return this;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","serialized: SerializedCommand","WORKFLOW_DESERIALIZE","resolveSignal"],"sources":["../src/command.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { APIClient, type CommandData } from \"./api-client/index.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\nimport { resolveSignal, type Signal } from \"./utils/resolveSignal.js\";\n\n/**\n * Cached output from a command execution.\n */\nexport interface CommandOutput {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Serialized representation of a Command for @workflow/serde.\n */\nexport interface SerializedCommand {\n sandboxId: string;\n cmd: CommandData;\n /** Cached output, included if output was fetched before serialization */\n output?: CommandOutput;\n}\n\n/**\n * Serialized representation of a CommandFinished for @workflow/serde.\n */\nexport interface SerializedCommandFinished extends SerializedCommand {\n exitCode: number;\n}\n\n/**\n * A command executed in a Sandbox.\n *\n * For detached commands, you can {@link wait} to get a {@link CommandFinished} instance\n * with the populated exit code. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * You can iterate over command output with {@link logs}.\n *\n * @see {@link Sandbox.runCommand} to start a command.\n *\n * @hideconstructor\n */\nexport class Command {\n /**\n * Cached API client instance.\n * @internal\n */\n protected _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n protected async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * ID of the sandbox this command is running in.\n */\n protected sandboxId: string;\n\n /**\n * Data for the command execution.\n */\n protected cmd: CommandData;\n\n public exitCode: number | null;\n\n protected outputCache: Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> | null = null;\n\n /**\n * Synchronously accessible resolved output, populated after output is fetched.\n * Used for serialization.\n * @internal\n */\n protected _resolvedOutput: CommandOutput | null = null;\n\n /**\n * ID of the command execution.\n */\n get cmdId() {\n return this.cmd.id;\n }\n\n get cwd() {\n return this.cmd.cwd;\n }\n\n get startedAt() {\n return this.cmd.startedAt;\n }\n\n /**\n * @param params - Object containing the client, sandbox ID, and command data.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command is running.\n * @param params.cmd - The command data.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor({\n client,\n sandboxId,\n cmd,\n output,\n }: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n output?: CommandOutput;\n }) {\n this._client = client ?? null;\n this.sandboxId = sandboxId;\n this.cmd = cmd;\n this.exitCode = cmd.exitCode ?? null;\n if (output) {\n this._resolvedOutput = output;\n // Note: `both` is reconstructed as stdout + stderr concatenation,\n // which loses the original interleaved order of the streams.\n this.outputCache = Promise.resolve({\n stdout: output.stdout,\n stderr: output.stderr,\n both: output.stdout + output.stderr,\n });\n }\n }\n\n /**\n * Serialize a Command instance to plain data for @workflow/serde.\n *\n * @param instance - The Command instance to serialize\n * @returns A plain object containing the sandbox ID, command data, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](instance: Command): SerializedCommand {\n const serialized: SerializedCommand = {\n sandboxId: instance.sandboxId,\n cmd: instance.cmd,\n };\n if (instance._resolvedOutput) {\n serialized.output = instance._resolvedOutput;\n }\n return serialized;\n }\n\n /**\n * Deserialize plain data back into a Command instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command data\n * @returns The reconstructed Command instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedCommand): Command {\n return new Command({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n output: data.output,\n });\n }\n\n /**\n * Iterate over the output of this command.\n *\n * ```\n * for await (const log of cmd.logs()) {\n * if (log.stream === \"stdout\") {\n * process.stdout.write(log.data);\n * } else {\n * process.stderr.write(log.data);\n * }\n * }\n * ```\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel log streaming.\n * @returns An async iterable of log entries from the command output.\n *\n * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}\n * to access output as a string.\n */\n logs(opts?: { signal?: AbortSignal }) {\n if (!this._client) {\n throw new Error(\n \"logs() requires an API client. Call an async method first to initialize the client.\",\n );\n }\n return this._client.getLogs({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n signal: opts?.signal,\n });\n }\n\n /**\n * Wait for a command to exit and populate its exit code.\n *\n * This method is useful for detached commands where you need to wait\n * for completion. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * ```\n * const detachedCmd = await sandbox.runCommand({ cmd: 'sleep', args: ['5'], detached: true });\n * const result = await detachedCmd.wait();\n * if (result.exitCode !== 0) {\n * console.error(\"Something went wrong...\")\n * }\n * ```\n *\n * @param params - Optional parameters.\n * @param params.signal - An AbortSignal to cancel waiting.\n * @returns A {@link CommandFinished} instance with populated exit code.\n */\n async wait(params?: { signal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n params?.signal?.throwIfAborted();\n\n const command = await client.getCommand({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n wait: true,\n signal: params?.signal,\n });\n\n return new CommandFinished({\n client,\n sandboxId: this.sandboxId,\n cmd: command.json.command,\n exitCode: command.json.command.exitCode,\n });\n }\n\n /**\n * Get cached output, fetching logs only once and reusing for concurrent calls.\n * This prevents race conditions when stdout() and stderr() are called in parallel.\n */\n protected async getCachedOutput(opts?: { signal?: AbortSignal }): Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> {\n if (!this.outputCache) {\n this.outputCache = (async () => {\n try {\n opts?.signal?.throwIfAborted();\n // Ensure the API client is initialized before calling logs(),\n // since logs() is synchronous and requires _client to be set.\n await this.ensureClient();\n let stdout = \"\";\n let stderr = \"\";\n let both = \"\";\n for await (const log of this.logs({ signal: opts?.signal })) {\n both += log.data;\n if (log.stream === \"stdout\") {\n stdout += log.data;\n } else {\n stderr += log.data;\n }\n }\n // Store resolved output for serialization\n this._resolvedOutput = { stdout, stderr };\n return { stdout, stderr, both };\n } catch (err) {\n // Clear the promise so future calls can retry\n this.outputCache = null;\n throw err;\n }\n })();\n }\n\n return this.outputCache;\n }\n\n /**\n * Get the output of `stdout`, `stderr`, or both as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param stream - The output stream to read: \"stdout\", \"stderr\", or \"both\".\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The output of the specified stream(s) as a string.\n */\n async output(\n stream: \"stdout\" | \"stderr\" | \"both\" = \"both\",\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const cached = await this.getCachedOutput(opts);\n return cached[stream];\n }\n\n /**\n * Get the output of `stdout` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard output of the command.\n */\n async stdout(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stdout\", opts);\n }\n\n /**\n * Get the output of `stderr` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard error output of the command.\n */\n async stderr(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stderr\", opts);\n }\n\n /**\n * Kill a running command in a sandbox.\n *\n * @param signal - The signal to send the running process. Defaults to SIGTERM.\n * @param opts - Optional parameters.\n * @param opts.abortSignal - An AbortSignal to cancel the kill operation.\n * @returns Promise<void>.\n */\n async kill(signal?: Signal, opts?: { abortSignal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n await client.killCommand({\n sandboxId: this.sandboxId,\n commandId: this.cmd.id,\n signal: resolveSignal(signal ?? \"SIGTERM\"),\n abortSignal: opts?.abortSignal,\n });\n }\n}\n\n/**\n * A command that has finished executing.\n *\n * The exit code is immediately available and populated upon creation.\n * Unlike {@link Command}, you don't need to call wait() - the command\n * has already completed execution.\n *\n * @hideconstructor\n */\nexport class CommandFinished extends Command {\n /**\n * The exit code of the command. This is always populated for\n * CommandFinished instances.\n */\n public exitCode: number;\n\n /**\n * @param params - Object containing client, sandbox ID, command data, and exit code.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command ran.\n * @param params.cmd - The command data.\n * @param params.exitCode - The exit code of the completed command.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor(params: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n exitCode: number;\n output?: CommandOutput;\n }) {\n super({ ...params });\n this.exitCode = params.exitCode;\n }\n\n /**\n * Serialize a CommandFinished instance to plain data for @workflow/serde.\n *\n * @param instance - The CommandFinished instance to serialize\n * @returns A plain object containing the sandbox ID, command data, exit code, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](\n instance: CommandFinished,\n ): SerializedCommandFinished {\n return {\n ...Command[WORKFLOW_SERIALIZE](instance),\n exitCode: instance.exitCode,\n };\n }\n\n /**\n * Deserialize plain data back into a CommandFinished instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command finished data\n * @returns The reconstructed CommandFinished instance\n */\n static [WORKFLOW_DESERIALIZE](\n data: SerializedCommandFinished,\n ): CommandFinished {\n return new CommandFinished({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n exitCode: data.exitCode,\n output: data.output,\n });\n }\n\n /**\n * The wait method is not needed for CommandFinished instances since\n * the command has already completed and exitCode is populated.\n *\n * @deprecated This method is redundant for CommandFinished instances.\n * The exitCode is already available.\n * @returns This CommandFinished instance.\n */\n async wait(): Promise<CommandFinished> {\n return this;\n }\n}\n"],"mappings":"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"}
|
package/dist/command.js
CHANGED
|
@@ -170,6 +170,8 @@ var Command = class Command {
|
|
|
170
170
|
async getCachedOutput(opts) {
|
|
171
171
|
if (!this.outputCache) this.outputCache = (async () => {
|
|
172
172
|
try {
|
|
173
|
+
opts?.signal?.throwIfAborted();
|
|
174
|
+
await this.ensureClient();
|
|
173
175
|
let stdout = "";
|
|
174
176
|
let stderr = "";
|
|
175
177
|
let both = "";
|
package/dist/command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","names":["serialized: SerializedCommand"],"sources":["../src/command.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { APIClient, type CommandData } from \"./api-client/index.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\nimport { resolveSignal, type Signal } from \"./utils/resolveSignal.js\";\n\n/**\n * Cached output from a command execution.\n */\nexport interface CommandOutput {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Serialized representation of a Command for @workflow/serde.\n */\nexport interface SerializedCommand {\n sandboxId: string;\n cmd: CommandData;\n /** Cached output, included if output was fetched before serialization */\n output?: CommandOutput;\n}\n\n/**\n * Serialized representation of a CommandFinished for @workflow/serde.\n */\nexport interface SerializedCommandFinished extends SerializedCommand {\n exitCode: number;\n}\n\n/**\n * A command executed in a Sandbox.\n *\n * For detached commands, you can {@link wait} to get a {@link CommandFinished} instance\n * with the populated exit code. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * You can iterate over command output with {@link logs}.\n *\n * @see {@link Sandbox.runCommand} to start a command.\n *\n * @hideconstructor\n */\nexport class Command {\n /**\n * Cached API client instance.\n * @internal\n */\n protected _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n protected async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * ID of the sandbox this command is running in.\n */\n protected sandboxId: string;\n\n /**\n * Data for the command execution.\n */\n protected cmd: CommandData;\n\n public exitCode: number | null;\n\n protected outputCache: Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> | null = null;\n\n /**\n * Synchronously accessible resolved output, populated after output is fetched.\n * Used for serialization.\n * @internal\n */\n protected _resolvedOutput: CommandOutput | null = null;\n\n /**\n * ID of the command execution.\n */\n get cmdId() {\n return this.cmd.id;\n }\n\n get cwd() {\n return this.cmd.cwd;\n }\n\n get startedAt() {\n return this.cmd.startedAt;\n }\n\n /**\n * @param params - Object containing the client, sandbox ID, and command data.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command is running.\n * @param params.cmd - The command data.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor({\n client,\n sandboxId,\n cmd,\n output,\n }: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n output?: CommandOutput;\n }) {\n this._client = client ?? null;\n this.sandboxId = sandboxId;\n this.cmd = cmd;\n this.exitCode = cmd.exitCode ?? null;\n if (output) {\n this._resolvedOutput = output;\n // Note: `both` is reconstructed as stdout + stderr concatenation,\n // which loses the original interleaved order of the streams.\n this.outputCache = Promise.resolve({\n stdout: output.stdout,\n stderr: output.stderr,\n both: output.stdout + output.stderr,\n });\n }\n }\n\n /**\n * Serialize a Command instance to plain data for @workflow/serde.\n *\n * @param instance - The Command instance to serialize\n * @returns A plain object containing the sandbox ID, command data, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](instance: Command): SerializedCommand {\n const serialized: SerializedCommand = {\n sandboxId: instance.sandboxId,\n cmd: instance.cmd,\n };\n if (instance._resolvedOutput) {\n serialized.output = instance._resolvedOutput;\n }\n return serialized;\n }\n\n /**\n * Deserialize plain data back into a Command instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command data\n * @returns The reconstructed Command instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedCommand): Command {\n return new Command({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n output: data.output,\n });\n }\n\n /**\n * Iterate over the output of this command.\n *\n * ```\n * for await (const log of cmd.logs()) {\n * if (log.stream === \"stdout\") {\n * process.stdout.write(log.data);\n * } else {\n * process.stderr.write(log.data);\n * }\n * }\n * ```\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel log streaming.\n * @returns An async iterable of log entries from the command output.\n *\n * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}\n * to access output as a string.\n */\n logs(opts?: { signal?: AbortSignal }) {\n if (!this._client) {\n throw new Error(\n \"logs() requires an API client. Call an async method first to initialize the client.\",\n );\n }\n return this._client.getLogs({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n signal: opts?.signal,\n });\n }\n\n /**\n * Wait for a command to exit and populate its exit code.\n *\n * This method is useful for detached commands where you need to wait\n * for completion. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * ```\n * const detachedCmd = await sandbox.runCommand({ cmd: 'sleep', args: ['5'], detached: true });\n * const result = await detachedCmd.wait();\n * if (result.exitCode !== 0) {\n * console.error(\"Something went wrong...\")\n * }\n * ```\n *\n * @param params - Optional parameters.\n * @param params.signal - An AbortSignal to cancel waiting.\n * @returns A {@link CommandFinished} instance with populated exit code.\n */\n async wait(params?: { signal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n params?.signal?.throwIfAborted();\n\n const command = await client.getCommand({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n wait: true,\n signal: params?.signal,\n });\n\n return new CommandFinished({\n client,\n sandboxId: this.sandboxId,\n cmd: command.json.command,\n exitCode: command.json.command.exitCode,\n });\n }\n\n /**\n * Get cached output, fetching logs only once and reusing for concurrent calls.\n * This prevents race conditions when stdout() and stderr() are called in parallel.\n */\n protected async getCachedOutput(opts?: { signal?: AbortSignal }): Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> {\n if (!this.outputCache) {\n this.outputCache = (async () => {\n try {\n let stdout = \"\";\n let stderr = \"\";\n let both = \"\";\n for await (const log of this.logs({ signal: opts?.signal })) {\n both += log.data;\n if (log.stream === \"stdout\") {\n stdout += log.data;\n } else {\n stderr += log.data;\n }\n }\n // Store resolved output for serialization\n this._resolvedOutput = { stdout, stderr };\n return { stdout, stderr, both };\n } catch (err) {\n // Clear the promise so future calls can retry\n this.outputCache = null;\n throw err;\n }\n })();\n }\n\n return this.outputCache;\n }\n\n /**\n * Get the output of `stdout`, `stderr`, or both as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param stream - The output stream to read: \"stdout\", \"stderr\", or \"both\".\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The output of the specified stream(s) as a string.\n */\n async output(\n stream: \"stdout\" | \"stderr\" | \"both\" = \"both\",\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const cached = await this.getCachedOutput(opts);\n return cached[stream];\n }\n\n /**\n * Get the output of `stdout` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard output of the command.\n */\n async stdout(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stdout\", opts);\n }\n\n /**\n * Get the output of `stderr` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard error output of the command.\n */\n async stderr(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stderr\", opts);\n }\n\n /**\n * Kill a running command in a sandbox.\n *\n * @param signal - The signal to send the running process. Defaults to SIGTERM.\n * @param opts - Optional parameters.\n * @param opts.abortSignal - An AbortSignal to cancel the kill operation.\n * @returns Promise<void>.\n */\n async kill(signal?: Signal, opts?: { abortSignal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n await client.killCommand({\n sandboxId: this.sandboxId,\n commandId: this.cmd.id,\n signal: resolveSignal(signal ?? \"SIGTERM\"),\n abortSignal: opts?.abortSignal,\n });\n }\n}\n\n/**\n * A command that has finished executing.\n *\n * The exit code is immediately available and populated upon creation.\n * Unlike {@link Command}, you don't need to call wait() - the command\n * has already completed execution.\n *\n * @hideconstructor\n */\nexport class CommandFinished extends Command {\n /**\n * The exit code of the command. This is always populated for\n * CommandFinished instances.\n */\n public exitCode: number;\n\n /**\n * @param params - Object containing client, sandbox ID, command data, and exit code.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command ran.\n * @param params.cmd - The command data.\n * @param params.exitCode - The exit code of the completed command.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor(params: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n exitCode: number;\n output?: CommandOutput;\n }) {\n super({ ...params });\n this.exitCode = params.exitCode;\n }\n\n /**\n * Serialize a CommandFinished instance to plain data for @workflow/serde.\n *\n * @param instance - The CommandFinished instance to serialize\n * @returns A plain object containing the sandbox ID, command data, exit code, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](\n instance: CommandFinished,\n ): SerializedCommandFinished {\n return {\n ...Command[WORKFLOW_SERIALIZE](instance),\n exitCode: instance.exitCode,\n };\n }\n\n /**\n * Deserialize plain data back into a CommandFinished instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command finished data\n * @returns The reconstructed CommandFinished instance\n */\n static [WORKFLOW_DESERIALIZE](\n data: SerializedCommandFinished,\n ): CommandFinished {\n return new CommandFinished({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n exitCode: data.exitCode,\n output: data.output,\n });\n }\n\n /**\n * The wait method is not needed for CommandFinished instances since\n * the command has already completed and exitCode is populated.\n *\n * @deprecated This method is redundant for CommandFinished instances.\n * The exitCode is already available.\n * @returns This CommandFinished instance.\n */\n async wait(): Promise<CommandFinished> {\n return this;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,UAAb,MAAa,QAAQ;;;;;CAWnB,MAAgB,eAAmC;AACjD;AACA,MAAI,KAAK,QAAS,QAAO,KAAK;EAC9B,MAAM,cAAc,MAAM,gBAAgB;AAC1C,OAAK,UAAU,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AACF,SAAO,KAAK;;;;;CA+Bd,IAAI,QAAQ;AACV,SAAO,KAAK,IAAI;;CAGlB,IAAI,MAAM;AACR,SAAO,KAAK,IAAI;;CAGlB,IAAI,YAAY;AACd,SAAO,KAAK,IAAI;;;;;;;;;CAUlB,YAAY,EACV,QACA,WACA,KACA,UAMC;OA1EO,UAA4B;OA6B5B,cAIE;OAOF,kBAAwC;AAmChD,OAAK,UAAU,UAAU;AACzB,OAAK,YAAY;AACjB,OAAK,MAAM;AACX,OAAK,WAAW,IAAI,YAAY;AAChC,MAAI,QAAQ;AACV,QAAK,kBAAkB;AAGvB,QAAK,cAAc,QAAQ,QAAQ;IACjC,QAAQ,OAAO;IACf,QAAQ,OAAO;IACf,MAAM,OAAO,SAAS,OAAO;IAC9B,CAAC;;;;;;;;;CAUN,QAAQ,oBAAoB,UAAsC;EAChE,MAAMA,aAAgC;GACpC,WAAW,SAAS;GACpB,KAAK,SAAS;GACf;AACD,MAAI,SAAS,gBACX,YAAW,SAAS,SAAS;AAE/B,SAAO;;;;;;;;;;;CAYT,QAAQ,sBAAsB,MAAkC;AAC9D,SAAO,IAAI,QAAQ;GACjB,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,QAAQ,KAAK;GACd,CAAC;;;;;;;;;;;;;;;;;;;;;;CAuBJ,KAAK,MAAiC;AACpC,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MACR,sFACD;AAEH,SAAO,KAAK,QAAQ,QAAQ;GAC1B,WAAW,KAAK;GAChB,OAAO,KAAK,IAAI;GAChB,QAAQ,MAAM;GACf,CAAC;;;;;;;;;;;;;;;;;;;;;CAsBJ,MAAM,KAAK,QAAmC;AAC5C;EACA,MAAM,SAAS,MAAM,KAAK,cAAc;AACxC,UAAQ,QAAQ,gBAAgB;EAEhC,MAAM,UAAU,MAAM,OAAO,WAAW;GACtC,WAAW,KAAK;GAChB,OAAO,KAAK,IAAI;GAChB,MAAM;GACN,QAAQ,QAAQ;GACjB,CAAC;AAEF,SAAO,IAAI,gBAAgB;GACzB;GACA,WAAW,KAAK;GAChB,KAAK,QAAQ,KAAK;GAClB,UAAU,QAAQ,KAAK,QAAQ;GAChC,CAAC;;;;;;CAOJ,MAAgB,gBAAgB,MAI7B;AACD,MAAI,CAAC,KAAK,YACR,MAAK,eAAe,YAAY;AAC9B,OAAI;IACF,IAAI,SAAS;IACb,IAAI,SAAS;IACb,IAAI,OAAO;AACX,eAAW,MAAM,OAAO,KAAK,KAAK,EAAE,QAAQ,MAAM,QAAQ,CAAC,EAAE;AAC3D,aAAQ,IAAI;AACZ,SAAI,IAAI,WAAW,SACjB,WAAU,IAAI;SAEd,WAAU,IAAI;;AAIlB,SAAK,kBAAkB;KAAE;KAAQ;KAAQ;AACzC,WAAO;KAAE;KAAQ;KAAQ;KAAM;YACxB,KAAK;AAEZ,SAAK,cAAc;AACnB,UAAM;;MAEN;AAGN,SAAO,KAAK;;;;;;;;;;;;;CAcd,MAAM,OACJ,SAAuC,QACvC,MACA;AACA;AAEA,UADe,MAAM,KAAK,gBAAgB,KAAK,EACjC;;;;;;;;;;;;CAahB,MAAM,OAAO,MAAiC;AAC5C;AACA,SAAO,KAAK,OAAO,UAAU,KAAK;;;;;;;;;;;;CAapC,MAAM,OAAO,MAAiC;AAC5C;AACA,SAAO,KAAK,OAAO,UAAU,KAAK;;;;;;;;;;CAWpC,MAAM,KAAK,QAAiB,MAAsC;AAChE;AAEA,SADe,MAAM,KAAK,cAAc,EAC3B,YAAY;GACvB,WAAW,KAAK;GAChB,WAAW,KAAK,IAAI;GACpB,QAAQ,cAAc,UAAU,UAAU;GAC1C,aAAa,MAAM;GACpB,CAAC;;;;;;;;;;;;AAaN,IAAa,kBAAb,MAAa,wBAAwB,QAAQ;;;;;;;;;CAe3C,YAAY,QAMT;AACD,QAAM,EAAE,GAAG,QAAQ,CAAC;AACpB,OAAK,WAAW,OAAO;;;;;;;;CASzB,QAAQ,oBACN,UAC2B;AAC3B,SAAO;GACL,GAAG,QAAQ,oBAAoB,SAAS;GACxC,UAAU,SAAS;GACpB;;;;;;;;;;;CAYH,QAAQ,sBACN,MACiB;AACjB,SAAO,IAAI,gBAAgB;GACzB,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,UAAU,KAAK;GACf,QAAQ,KAAK;GACd,CAAC;;;;;;;;;;CAWJ,MAAM,OAAiC;AACrC,SAAO"}
|
|
1
|
+
{"version":3,"file":"command.js","names":["serialized: SerializedCommand"],"sources":["../src/command.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { APIClient, type CommandData } from \"./api-client/index.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\nimport { resolveSignal, type Signal } from \"./utils/resolveSignal.js\";\n\n/**\n * Cached output from a command execution.\n */\nexport interface CommandOutput {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Serialized representation of a Command for @workflow/serde.\n */\nexport interface SerializedCommand {\n sandboxId: string;\n cmd: CommandData;\n /** Cached output, included if output was fetched before serialization */\n output?: CommandOutput;\n}\n\n/**\n * Serialized representation of a CommandFinished for @workflow/serde.\n */\nexport interface SerializedCommandFinished extends SerializedCommand {\n exitCode: number;\n}\n\n/**\n * A command executed in a Sandbox.\n *\n * For detached commands, you can {@link wait} to get a {@link CommandFinished} instance\n * with the populated exit code. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * You can iterate over command output with {@link logs}.\n *\n * @see {@link Sandbox.runCommand} to start a command.\n *\n * @hideconstructor\n */\nexport class Command {\n /**\n * Cached API client instance.\n * @internal\n */\n protected _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n protected async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * ID of the sandbox this command is running in.\n */\n protected sandboxId: string;\n\n /**\n * Data for the command execution.\n */\n protected cmd: CommandData;\n\n public exitCode: number | null;\n\n protected outputCache: Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> | null = null;\n\n /**\n * Synchronously accessible resolved output, populated after output is fetched.\n * Used for serialization.\n * @internal\n */\n protected _resolvedOutput: CommandOutput | null = null;\n\n /**\n * ID of the command execution.\n */\n get cmdId() {\n return this.cmd.id;\n }\n\n get cwd() {\n return this.cmd.cwd;\n }\n\n get startedAt() {\n return this.cmd.startedAt;\n }\n\n /**\n * @param params - Object containing the client, sandbox ID, and command data.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command is running.\n * @param params.cmd - The command data.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor({\n client,\n sandboxId,\n cmd,\n output,\n }: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n output?: CommandOutput;\n }) {\n this._client = client ?? null;\n this.sandboxId = sandboxId;\n this.cmd = cmd;\n this.exitCode = cmd.exitCode ?? null;\n if (output) {\n this._resolvedOutput = output;\n // Note: `both` is reconstructed as stdout + stderr concatenation,\n // which loses the original interleaved order of the streams.\n this.outputCache = Promise.resolve({\n stdout: output.stdout,\n stderr: output.stderr,\n both: output.stdout + output.stderr,\n });\n }\n }\n\n /**\n * Serialize a Command instance to plain data for @workflow/serde.\n *\n * @param instance - The Command instance to serialize\n * @returns A plain object containing the sandbox ID, command data, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](instance: Command): SerializedCommand {\n const serialized: SerializedCommand = {\n sandboxId: instance.sandboxId,\n cmd: instance.cmd,\n };\n if (instance._resolvedOutput) {\n serialized.output = instance._resolvedOutput;\n }\n return serialized;\n }\n\n /**\n * Deserialize plain data back into a Command instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command data\n * @returns The reconstructed Command instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedCommand): Command {\n return new Command({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n output: data.output,\n });\n }\n\n /**\n * Iterate over the output of this command.\n *\n * ```\n * for await (const log of cmd.logs()) {\n * if (log.stream === \"stdout\") {\n * process.stdout.write(log.data);\n * } else {\n * process.stderr.write(log.data);\n * }\n * }\n * ```\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel log streaming.\n * @returns An async iterable of log entries from the command output.\n *\n * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}\n * to access output as a string.\n */\n logs(opts?: { signal?: AbortSignal }) {\n if (!this._client) {\n throw new Error(\n \"logs() requires an API client. Call an async method first to initialize the client.\",\n );\n }\n return this._client.getLogs({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n signal: opts?.signal,\n });\n }\n\n /**\n * Wait for a command to exit and populate its exit code.\n *\n * This method is useful for detached commands where you need to wait\n * for completion. For non-detached commands, {@link Sandbox.runCommand}\n * automatically waits and returns a {@link CommandFinished} instance.\n *\n * ```\n * const detachedCmd = await sandbox.runCommand({ cmd: 'sleep', args: ['5'], detached: true });\n * const result = await detachedCmd.wait();\n * if (result.exitCode !== 0) {\n * console.error(\"Something went wrong...\")\n * }\n * ```\n *\n * @param params - Optional parameters.\n * @param params.signal - An AbortSignal to cancel waiting.\n * @returns A {@link CommandFinished} instance with populated exit code.\n */\n async wait(params?: { signal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n params?.signal?.throwIfAborted();\n\n const command = await client.getCommand({\n sandboxId: this.sandboxId,\n cmdId: this.cmd.id,\n wait: true,\n signal: params?.signal,\n });\n\n return new CommandFinished({\n client,\n sandboxId: this.sandboxId,\n cmd: command.json.command,\n exitCode: command.json.command.exitCode,\n });\n }\n\n /**\n * Get cached output, fetching logs only once and reusing for concurrent calls.\n * This prevents race conditions when stdout() and stderr() are called in parallel.\n */\n protected async getCachedOutput(opts?: { signal?: AbortSignal }): Promise<{\n stdout: string;\n stderr: string;\n both: string;\n }> {\n if (!this.outputCache) {\n this.outputCache = (async () => {\n try {\n opts?.signal?.throwIfAborted();\n // Ensure the API client is initialized before calling logs(),\n // since logs() is synchronous and requires _client to be set.\n await this.ensureClient();\n let stdout = \"\";\n let stderr = \"\";\n let both = \"\";\n for await (const log of this.logs({ signal: opts?.signal })) {\n both += log.data;\n if (log.stream === \"stdout\") {\n stdout += log.data;\n } else {\n stderr += log.data;\n }\n }\n // Store resolved output for serialization\n this._resolvedOutput = { stdout, stderr };\n return { stdout, stderr, both };\n } catch (err) {\n // Clear the promise so future calls can retry\n this.outputCache = null;\n throw err;\n }\n })();\n }\n\n return this.outputCache;\n }\n\n /**\n * Get the output of `stdout`, `stderr`, or both as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param stream - The output stream to read: \"stdout\", \"stderr\", or \"both\".\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The output of the specified stream(s) as a string.\n */\n async output(\n stream: \"stdout\" | \"stderr\" | \"both\" = \"both\",\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const cached = await this.getCachedOutput(opts);\n return cached[stream];\n }\n\n /**\n * Get the output of `stdout` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard output of the command.\n */\n async stdout(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stdout\", opts);\n }\n\n /**\n * Get the output of `stderr` as a string.\n *\n * NOTE: This may throw string conversion errors if the command does\n * not output valid Unicode.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel output streaming.\n * @returns The standard error output of the command.\n */\n async stderr(opts?: { signal?: AbortSignal }) {\n \"use step\";\n return this.output(\"stderr\", opts);\n }\n\n /**\n * Kill a running command in a sandbox.\n *\n * @param signal - The signal to send the running process. Defaults to SIGTERM.\n * @param opts - Optional parameters.\n * @param opts.abortSignal - An AbortSignal to cancel the kill operation.\n * @returns Promise<void>.\n */\n async kill(signal?: Signal, opts?: { abortSignal?: AbortSignal }) {\n \"use step\";\n const client = await this.ensureClient();\n await client.killCommand({\n sandboxId: this.sandboxId,\n commandId: this.cmd.id,\n signal: resolveSignal(signal ?? \"SIGTERM\"),\n abortSignal: opts?.abortSignal,\n });\n }\n}\n\n/**\n * A command that has finished executing.\n *\n * The exit code is immediately available and populated upon creation.\n * Unlike {@link Command}, you don't need to call wait() - the command\n * has already completed execution.\n *\n * @hideconstructor\n */\nexport class CommandFinished extends Command {\n /**\n * The exit code of the command. This is always populated for\n * CommandFinished instances.\n */\n public exitCode: number;\n\n /**\n * @param params - Object containing client, sandbox ID, command data, and exit code.\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.sandboxId - The ID of the sandbox where the command ran.\n * @param params.cmd - The command data.\n * @param params.exitCode - The exit code of the completed command.\n * @param params.output - Optional cached output to restore (used during deserialization).\n */\n constructor(params: {\n client?: APIClient;\n sandboxId: string;\n cmd: CommandData;\n exitCode: number;\n output?: CommandOutput;\n }) {\n super({ ...params });\n this.exitCode = params.exitCode;\n }\n\n /**\n * Serialize a CommandFinished instance to plain data for @workflow/serde.\n *\n * @param instance - The CommandFinished instance to serialize\n * @returns A plain object containing the sandbox ID, command data, exit code, and output if fetched\n */\n static [WORKFLOW_SERIALIZE](\n instance: CommandFinished,\n ): SerializedCommandFinished {\n return {\n ...Command[WORKFLOW_SERIALIZE](instance),\n exitCode: instance.exitCode,\n };\n }\n\n /**\n * Deserialize plain data back into a CommandFinished instance for @workflow/serde.\n *\n * The deserialized instance will lazily create an API client using\n * OIDC or environment credentials when needed.\n *\n * @param data - The serialized command finished data\n * @returns The reconstructed CommandFinished instance\n */\n static [WORKFLOW_DESERIALIZE](\n data: SerializedCommandFinished,\n ): CommandFinished {\n return new CommandFinished({\n sandboxId: data.sandboxId,\n cmd: data.cmd,\n exitCode: data.exitCode,\n output: data.output,\n });\n }\n\n /**\n * The wait method is not needed for CommandFinished instances since\n * the command has already completed and exitCode is populated.\n *\n * @deprecated This method is redundant for CommandFinished instances.\n * The exitCode is already available.\n * @returns This CommandFinished instance.\n */\n async wait(): Promise<CommandFinished> {\n return this;\n }\n}\n"],"mappings":"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"}
|
package/dist/version.cjs
CHANGED
package/dist/version.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"1.9.
|
|
1
|
+
{"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"1.9.3\";\n"],"mappings":";;AACA,MAAa,UAAU"}
|
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"1.9.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"1.9.3\";\n"],"mappings":";AACA,MAAa,UAAU"}
|