withruntime 0.12.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/BLAXEL.md +97 -32
  2. package/COMPATIBILITY.md +120 -5
  3. package/DAYTONA.md +34 -3
  4. package/E2B.md +34 -5
  5. package/README.md +36 -3
  6. package/README.src.md +35 -2
  7. package/VERCEL.md +52 -5
  8. package/compatibility-lock.json +71 -111
  9. package/dist/billing-cli.js +8 -4
  10. package/dist/blaxel/client.d.ts +11 -0
  11. package/dist/blaxel/client.js +28 -0
  12. package/dist/blaxel/errors.d.ts +65 -0
  13. package/dist/blaxel/errors.js +114 -0
  14. package/dist/blaxel/index.d.ts +10 -3
  15. package/dist/blaxel/index.js +23 -4
  16. package/dist/blaxel/preview.d.ts +29 -4
  17. package/dist/blaxel/preview.js +79 -5
  18. package/dist/blaxel/sandbox.d.ts +1 -1
  19. package/dist/blaxel/sandbox.js +2 -1
  20. package/dist/blaxel/session.js +4 -2
  21. package/dist/cli-extras.js +2 -1
  22. package/dist/cli-words.js +1 -0
  23. package/dist/cli.d.ts +17 -2
  24. package/dist/cli.js +291 -85
  25. package/dist/client.d.ts +1 -1
  26. package/dist/credentials.d.ts +3 -0
  27. package/dist/credentials.js +15 -0
  28. package/dist/daytona/client.d.ts +4 -0
  29. package/dist/daytona/client.js +8 -3
  30. package/dist/daytona/daytona.d.ts +4 -1
  31. package/dist/daytona/daytona.js +3 -2
  32. package/dist/e2b/sandbox.d.ts +7 -6
  33. package/dist/e2b/sandbox.js +9 -12
  34. package/dist/errors.d.ts +1 -1
  35. package/dist/errors.js +1 -1
  36. package/dist/freestyle/index.d.ts +77 -5
  37. package/dist/freestyle/index.js +380 -48
  38. package/dist/identity.js +36 -18
  39. package/dist/index.d.ts +4 -3
  40. package/dist/machine.d.ts +39 -13
  41. package/dist/machine.js +72 -25
  42. package/dist/mcp.js +157 -20
  43. package/dist/modal/index.d.ts +15 -0
  44. package/dist/modal/index.js +35 -7
  45. package/dist/morph/index.d.ts +255 -0
  46. package/dist/morph/index.js +641 -0
  47. package/dist/network-cli.js +2 -2
  48. package/dist/product-names.d.ts +2 -2
  49. package/dist/product-names.js +2 -2
  50. package/dist/products/api-keys.d.ts +73 -0
  51. package/dist/products/api-keys.js +23 -0
  52. package/dist/products/apps.d.ts +12 -31
  53. package/dist/products/apps.js +8 -113
  54. package/dist/products/balancers.d.ts +87 -0
  55. package/dist/products/balancers.js +8 -0
  56. package/dist/products/builds.d.ts +30 -0
  57. package/dist/products/builds.js +6 -0
  58. package/dist/products/index.d.ts +2 -0
  59. package/dist/products/index.js +2 -0
  60. package/dist/products/jobs.d.ts +2 -2
  61. package/dist/products/jobs.js +1 -1
  62. package/dist/products/limits.d.ts +16 -2
  63. package/dist/products/limits.js +2 -2
  64. package/dist/products/mcp.d.ts +5 -0
  65. package/dist/products/network-products.d.ts +21 -3
  66. package/dist/products/network-products.js +3 -1
  67. package/dist/products/observability.d.ts +7 -2
  68. package/dist/products/previews.d.ts +6 -3
  69. package/dist/products/previews.js +1 -1
  70. package/dist/products/resources.d.ts +54 -0
  71. package/dist/products/resources.js +122 -0
  72. package/dist/products/secrets.d.ts +22 -0
  73. package/dist/products/secrets.js +8 -0
  74. package/dist/products/volumes.d.ts +3 -0
  75. package/dist/sandbox.d.ts +4 -0
  76. package/dist/sandbox.js +27 -2
  77. package/dist/snapshots.d.ts +4 -0
  78. package/dist/ssh.d.ts +35 -0
  79. package/dist/ssh.js +93 -1
  80. package/dist/transport.d.ts +14 -8
  81. package/dist/transport.js +70 -15
  82. package/dist/types.d.ts +33 -5
  83. package/dist/vercel/command.d.ts +48 -1
  84. package/dist/vercel/command.js +237 -6
  85. package/dist/vercel/sandbox-user.js +2 -1
  86. package/dist/vercel/sandbox.d.ts +17 -3
  87. package/dist/vercel/sandbox.js +46 -3
  88. package/dist/wallet.d.ts +4 -1
  89. package/dist/wallet.js +2 -1
  90. package/package.json +7 -14
  91. package/dist/cli/logs.test.d.ts +0 -1
  92. package/dist/cli/logs.test.js +0 -272
  93. package/dist/products/logs.test.d.ts +0 -1
  94. package/dist/products/logs.test.js +0 -343
@@ -198,3 +198,117 @@ export function codeOf(error) {
198
198
  return error.runtimeCode;
199
199
  return undefined;
200
200
  }
201
+ function readProperty(value, key) {
202
+ try {
203
+ return typeof value === "object" && value !== null
204
+ ? value[key]
205
+ : undefined;
206
+ }
207
+ catch {
208
+ return undefined;
209
+ }
210
+ }
211
+ const isCode = (value) => (typeof value === "string" && value.length > 0) ||
212
+ (typeof value === "number" && Number.isFinite(value));
213
+ const isStatus = (value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
214
+ /** True for an error this package throws for an API failure (ResponseError,
215
+ * SandboxGatewayError, SandboxCreationTimeoutError) or a raw Blaxel error body
216
+ * (`{ error, code }`, `{ code, message, status_code }`,
217
+ * `{ error: { code, message, status } }`). False for plain errors, strings and
218
+ * NotSupportedError, which is a refusal made here, not an API failure. Never
219
+ * throws. */
220
+ export function isBlaxelError(err) {
221
+ try {
222
+ if (err instanceof Error)
223
+ return err instanceof ResponseError || err instanceof SandboxCreationTimeoutError;
224
+ if (typeof err !== "object" || err === null || Array.isArray(err))
225
+ return false;
226
+ const error = readProperty(err, "error");
227
+ if (typeof error === "string")
228
+ return isStatus(readProperty(err, "code"));
229
+ if (typeof readProperty(err, "code") === "string" &&
230
+ typeof readProperty(err, "message") === "string" &&
231
+ isStatus(readProperty(err, "status_code")))
232
+ return true;
233
+ return (typeof error === "object" &&
234
+ error !== null &&
235
+ !Array.isArray(error) &&
236
+ typeof readProperty(error, "code") === "string" &&
237
+ typeof readProperty(error, "message") === "string" &&
238
+ isStatus(readProperty(error, "status")));
239
+ }
240
+ catch {
241
+ return false;
242
+ }
243
+ }
244
+ function errorCandidates(err) {
245
+ const bodies = [err, readProperty(err, "error"), readProperty(err, "data")];
246
+ return bodies.flatMap((body) => [body, readProperty(body, "error")]);
247
+ }
248
+ function readHeader(err, name) {
249
+ try {
250
+ const headers = readProperty(readProperty(err, "response"), "headers");
251
+ const get = readProperty(headers, "get");
252
+ if (typeof get !== "function")
253
+ return undefined;
254
+ const value = Reflect.apply(get, headers, [name]);
255
+ return typeof value === "string" && value.length > 0 ? value : undefined;
256
+ }
257
+ catch {
258
+ return undefined;
259
+ }
260
+ }
261
+ /** The error's code, then its body's, then the X-Blaxel-Error-Code header. A
262
+ * ResponseError from Runtime answers its HTTP status (its `code`); Runtime's
263
+ * own code is `runtimeCode`. Never throws. */
264
+ export function getBlaxelErrorCode(err) {
265
+ for (const candidate of errorCandidates(err)) {
266
+ const code = readProperty(candidate, "code");
267
+ if (isCode(code))
268
+ return code;
269
+ }
270
+ return readHeader(err, "x-blaxel-error-code");
271
+ }
272
+ /** The HTTP status: the error's, its response's, then its body's (`status`,
273
+ * `status_code` or a numeric `code`). Never throws. */
274
+ export function getBlaxelErrorStatus(err) {
275
+ const status = readProperty(err, "status");
276
+ if (isStatus(status))
277
+ return status;
278
+ const responseStatus = readProperty(readProperty(err, "response"), "status");
279
+ if (isStatus(responseStatus))
280
+ return responseStatus;
281
+ for (const candidate of errorCandidates(err))
282
+ for (const key of ["status", "status_code", "code"]) {
283
+ const value = readProperty(candidate, key);
284
+ if (isStatus(value))
285
+ return value;
286
+ }
287
+ return undefined;
288
+ }
289
+ /** The error's message, else its body's message or error text. Never throws. */
290
+ export function getBlaxelErrorMessage(err) {
291
+ for (const candidate of errorCandidates(err)) {
292
+ const message = typeof candidate === "string" ? candidate : readProperty(candidate, "message");
293
+ if (typeof message === "string" && message.length > 0)
294
+ return message;
295
+ }
296
+ return undefined;
297
+ }
298
+ /** Blaxel's request id headers (X-Cf-Request-Id, X-Amz-Cf-Id, CF-Ray) on the
299
+ * error's response, else the Runtime request id a ResponseError carries
300
+ * (`requestId`). Never throws. */
301
+ export function getBlaxelErrorRequestId(err) {
302
+ for (const name of ["x-cf-request-id", "x-amz-cf-id", "cf-ray"]) {
303
+ const value = readHeader(err, name);
304
+ if (value !== undefined)
305
+ return value;
306
+ }
307
+ try {
308
+ const runtime = err instanceof ResponseError ? err.requestId : undefined;
309
+ return typeof runtime === "string" && runtime.length > 0 ? runtime : undefined;
310
+ }
311
+ catch {
312
+ return undefined;
313
+ }
314
+ }
@@ -3,9 +3,9 @@ export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durati
3
3
  export { CodeInterpreter } from "./interpreter.js";
4
4
  export { SandboxFileSystem, retryOnTransient, type File, type FileWithContent, type Subdirectory, type Directory, type SuccessResponse, type FindMatch, type FindResponse, type FuzzySearchMatch, type FuzzySearchResponse, type ContentSearchMatch, type ContentSearchResponse, type CopyResponse, type WatchEvent, type SandboxFilesystemFile, type FilesystemSearchOptions, type FilesystemFindOptions, type FilesystemGrepOptions, } from "./filesystem.js";
5
5
  export { SandboxProcess, type ProcessRequest, type ProcessResponse, type ProcessRequestWithLog, type ProcessResponseWithLog, type PostProcessResponse, type GetProcessResponse, type GetProcessByIdentifierResponse, type DeleteProcessByIdentifierResponse, type DeleteProcessByIdentifierKillResponse, type ProcessLogs, } from "./process.js";
6
- export { SandboxPreview, SandboxPreviews, SandboxPreviewToken, SandboxPreviewTokens, type Preview, type PreviewMetadata, type PreviewSpec, type PreviewToken, } from "./preview.js";
6
+ export { SandboxPreview, SandboxPreviews, SandboxPreviewToken, SandboxPreviewTokens, type Preview, type PreviewMetadata, type PreviewSpec, type PreviewToken, type SandboxPreviewCreateConfiguration, } from "./preview.js";
7
7
  export { Snapshot, SandboxSnapshotsResource, type Env, type SandboxSnapshot, type SandboxSnapshotSource, type SandboxSnapshotSpec, type SandboxForkResponse, type SandboxRestoreResponse, type SnapshotCreateConfiguration, type SnapshotForkOptions, type SnapshotListQuery, } from "./snapshot.js";
8
- export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } from "./errors.js";
8
+ export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, isBlaxelError, getBlaxelErrorCode, getBlaxelErrorStatus, getBlaxelErrorMessage, getBlaxelErrorRequestId, type BlaxelGatewayErrorCode, type BlaxelAuthErrorCode, type BlaxelSandboxCreationErrorCode, type BlaxelErrorCode, type BlaxelErrorCodeValue, type BlaxelApiErrorBody, type BlaxelActionErrorBody, type BlaxelPlatformErrorBody, type BlaxelSandboxApiErrorBody, type BlaxelErrorLike, } from "./errors.js";
9
9
  export { createPaginatedList, unwrapListData, type PaginatedList, type PaginatedListMeta, type ListResponse, type CursorPaginationQuery, type AutoPagingEachCallback, type AutoPagingToArrayOptions, } from "./pagination.js";
10
10
  export { normalizePorts, normalizeEnvs, normalizeVolumes, type Status, type Port, type EnvVar, type ExpirationPolicy, type SandboxLifecycle, type SandboxNetwork, type VolumeBinding, type VolumeAttachment, type Metadata, type SandboxRuntime, type SandboxSpec, type CoreEvent, type Sandbox, type SandboxConfiguration, type SandboxCreateConfiguration, type SandboxUpdateMetadata, type SandboxUpdateNetwork, type SessionCreateOptions, type SessionWithToken, } from "./types.js";
11
11
  export { initialize, type Config, type WithRuntime, type RuntimeOptions } from "./client.js";
@@ -35,11 +35,13 @@ export declare const logger: {
35
35
  error: (...message: unknown[]) => void;
36
36
  };
37
37
  /** Blaxel's settings, as far as they mean anything on Runtime. `setConfig`
38
- * takes a Runtime key as `apikey`; anything else throws NotSupportedError. */
38
+ * takes a Runtime key as `apikey`; `integration` is kept but never sent;
39
+ * anything else throws NotSupportedError. */
39
40
  export declare const settings: {
40
41
  readonly env: string;
41
42
  readonly region: string | undefined;
42
43
  readonly workspace: string;
44
+ integration: string;
43
45
  setConfig(config: Config): void;
44
46
  authenticate(): Promise<void>;
45
47
  readonly [key: string]: unknown;
@@ -124,6 +126,7 @@ export declare const updateDrive: import("./unsupported.js").Unsupported;
124
126
  export declare const createDriveAccessToken: import("./unsupported.js").Unsupported;
125
127
  export declare const getDriveByExternalId: import("./unsupported.js").Unsupported;
126
128
  export declare const getDriveJwks: import("./unsupported.js").Unsupported;
129
+ export declare const deleteDriveFolder: import("./unsupported.js").Unsupported;
127
130
  export declare const listAllEgressGateways: import("./unsupported.js").Unsupported;
128
131
  export declare const getEgressGatewayUsage: import("./unsupported.js").Unsupported;
129
132
  export declare const listAllEgressIps: import("./unsupported.js").Unsupported;
@@ -214,6 +217,10 @@ export declare const getSandboxByExternalId: import("./unsupported.js").Unsuppor
214
217
  export declare const listScheduleExecutions: import("./unsupported.js").Unsupported;
215
218
  export declare const listSchedules: import("./unsupported.js").Unsupported;
216
219
  export declare const getSandboxScheduleMetrics: import("./unsupported.js").Unsupported;
220
+ export declare const listSecrets: import("./unsupported.js").Unsupported;
221
+ export declare const upsertSecret: import("./unsupported.js").Unsupported;
222
+ export declare const deleteSecret: import("./unsupported.js").Unsupported;
223
+ export declare const deleteSecretByQuery: import("./unsupported.js").Unsupported;
217
224
  export declare const getWorkspaceServiceAccounts: import("./unsupported.js").Unsupported;
218
225
  export declare const createWorkspaceServiceAccount: import("./unsupported.js").Unsupported;
219
226
  export declare const deleteWorkspaceServiceAccount: import("./unsupported.js").Unsupported;
@@ -4,11 +4,11 @@
4
4
  * import { SandboxInstance } from "withruntime/blaxel"; // was: from "@blaxel/core"
5
5
  *
6
6
  * and set RUNTIME_API_KEY, or run `npx withruntime login` once. A Blaxel key
7
- * (BL_API_KEY) is never sent anywhere. Every name `@blaxel/core` 0.3.25
7
+ * (BL_API_KEY) is never sent anywhere. Every name `@blaxel/core` 0.3.30
8
8
  * exports can be imported; what Runtime cannot do the way Blaxel does throws
9
9
  * NotSupportedError, naming what to use. */
10
10
  import { RuntimeError } from "../errors.js";
11
- import { initialize } from "./client.js";
11
+ import { initialize, integrationToken, setIntegrationToken } from "./client.js";
12
12
  import { NotSupportedError } from "./errors.js";
13
13
  import { CODEGEN, DRIVES, SCHEDULES, SYSTEM, unsupportedExport } from "./unsupported.js";
14
14
  export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, MAX_CREATION_TIMEOUT_SECONDS, } from "./sandbox.js";
@@ -17,7 +17,7 @@ export { SandboxFileSystem, retryOnTransient, } from "./filesystem.js";
17
17
  export { SandboxProcess, } from "./process.js";
18
18
  export { SandboxPreview, SandboxPreviews, SandboxPreviewToken, SandboxPreviewTokens, } from "./preview.js";
19
19
  export { Snapshot, SandboxSnapshotsResource, } from "./snapshot.js";
20
- export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } from "./errors.js";
20
+ export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, isBlaxelError, getBlaxelErrorCode, getBlaxelErrorStatus, getBlaxelErrorMessage, getBlaxelErrorRequestId, } from "./errors.js";
21
21
  export { createPaginatedList, unwrapListData, } from "./pagination.js";
22
22
  export { normalizePorts, normalizeEnvs, normalizeVolumes, } from "./types.js";
23
23
  export { initialize } from "./client.js";
@@ -54,7 +54,8 @@ export const logger = {
54
54
  error: (...message) => sink.error(text(message)),
55
55
  };
56
56
  /** Blaxel's settings, as far as they mean anything on Runtime. `setConfig`
57
- * takes a Runtime key as `apikey`; anything else throws NotSupportedError. */
57
+ * takes a Runtime key as `apikey`; `integration` is kept but never sent;
58
+ * anything else throws NotSupportedError. */
58
59
  export const settings = new Proxy({
59
60
  get env() {
60
61
  return "prod";
@@ -65,6 +66,14 @@ export const settings = new Proxy({
65
66
  get workspace() {
66
67
  return env.BL_WORKSPACE ?? "";
67
68
  },
69
+ /* Blaxel's product token for an integration (0.3.29): checked and kept
70
+ as Blaxel does, never sent; Runtime does not attribute traffic by it. */
71
+ get integration() {
72
+ return integrationToken();
73
+ },
74
+ set integration(value) {
75
+ setIntegrationToken(value);
76
+ },
68
77
  setConfig: (config) => initialize(config),
69
78
  authenticate,
70
79
  }, {
@@ -176,6 +185,7 @@ export const updateDrive = api("updateDrive");
176
185
  export const createDriveAccessToken = api("createDriveAccessToken");
177
186
  export const getDriveByExternalId = api("getDriveByExternalId");
178
187
  export const getDriveJwks = api("getDriveJwks");
188
+ export const deleteDriveFolder = api("deleteDriveFolder");
179
189
  export const listAllEgressGateways = api("listAllEgressGateways");
180
190
  export const getEgressGatewayUsage = api("getEgressGatewayUsage");
181
191
  export const listAllEgressIps = api("listAllEgressIps");
@@ -266,6 +276,15 @@ export const getSandboxByExternalId = api("getSandboxByExternalId");
266
276
  export const listScheduleExecutions = api("listScheduleExecutions");
267
277
  export const listSchedules = api("listSchedules");
268
278
  export const getSandboxScheduleMetrics = api("getSandboxScheduleMetrics");
279
+ /* @blaxel/core 0.3.30's workspace secrets, which Blaxel's proxy puts into a
280
+ sandbox's routing as {{SECRET:name}}. Runtime's secrets are bound to the
281
+ hosts they may reach, which Blaxel's calls do not name. */
282
+ const SECRETS = 'Use Runtime secrets, which name the hosts a secret may reach: runtime.secrets.set(name, { value, hosts }) (`import { Runtime } from "withruntime"`); the egress proxy adds the value on HTTPS to those hosts and the sandbox never sees it.';
283
+ const secretApi = (name) => unsupportedExport(`API function ${name}`, SECRETS);
284
+ export const listSecrets = secretApi("listSecrets");
285
+ export const upsertSecret = secretApi("upsertSecret");
286
+ export const deleteSecret = secretApi("deleteSecret");
287
+ export const deleteSecretByQuery = secretApi("deleteSecretByQuery");
269
288
  export const getWorkspaceServiceAccounts = api("getWorkspaceServiceAccounts");
270
289
  export const createWorkspaceServiceAccount = api("createWorkspaceServiceAccount");
271
290
  export const deleteWorkspaceServiceAccount = api("deleteWorkspaceServiceAccount");
@@ -30,6 +30,14 @@ export type Preview = {
30
30
  spec: PreviewSpec;
31
31
  status?: string;
32
32
  };
33
+ /** @blaxel/core 0.3.29's shorthand for `previews.create` and
34
+ * `createIfNotExists`: a port, with a name (`preview-<port>` when left out)
35
+ * and public (false when left out). */
36
+ export type SandboxPreviewCreateConfiguration = {
37
+ port: number;
38
+ name?: string;
39
+ public?: boolean;
40
+ };
33
41
  export type PreviewToken = {
34
42
  metadata: {
35
43
  name: string;
@@ -45,6 +53,9 @@ export type PreviewToken = {
45
53
  /** What the previews need from their sandbox. */
46
54
  export interface PreviewContext {
47
55
  readonly sandboxName: string;
56
+ /** The Runtime sandbox's id, when known: tokens made for a sandbox are
57
+ * never reused for another of the same name. */
58
+ readonly sandboxId?: string;
48
59
  run<T>(work: (runtime: RuntimeSandbox) => Promise<T>): Promise<T>;
49
60
  /** The sandbox's labels as last read. */
50
61
  labels(): Record<string, string>;
@@ -56,6 +67,7 @@ export interface PreviewContext {
56
67
  export declare class SandboxPreviewToken {
57
68
  #private;
58
69
  constructor(previewToken: PreviewToken);
70
+ get name(): string;
59
71
  get value(): string;
60
72
  get expiresAt(): string | Date;
61
73
  get expired(): boolean;
@@ -70,6 +82,13 @@ export declare class SandboxPreviewTokens {
70
82
  /** A token bounded by `expiresAt`; native signing floors milliseconds and
71
83
  * caps it to the caller's session. Older servers refuse without a TTL fallback. */
72
84
  create(expiresAt: Date): Promise<SandboxPreviewToken>;
85
+ /** Blaxel's (0.3.29): the latest token lasting at least `minValidity` ms
86
+ * more (an hour unless given) and not past `expiresAt` (a day from now
87
+ * unless given), else a new one until `expiresAt`. Blaxel picks among the
88
+ * preview's listed tokens; Runtime keeps no list, so the one reused is the
89
+ * token this call made last in this process for this sandbox's port, and a
90
+ * new process makes a new one. Arguments are checked as Blaxel does. */
91
+ createIfExpired(expiresAt?: Date, minValidity?: number): Promise<SandboxPreviewToken>;
73
92
  list(): Promise<never>;
74
93
  /** Revokes the preview's tokens. Runtime cannot revoke one token alone, so
75
94
  * every token issued for this preview stops working. */
@@ -85,6 +104,8 @@ export declare class SandboxPreview {
85
104
  get name(): string;
86
105
  get metadata(): PreviewMetadata;
87
106
  get spec(): PreviewSpec;
107
+ /** The preview's address (`spec.url`), or `""`. */
108
+ get url(): string;
88
109
  }
89
110
  /** `sandbox.previews`. */
90
111
  export declare class SandboxPreviews {
@@ -94,10 +115,14 @@ export declare class SandboxPreviews {
94
115
  /** Every shared port. One Runtime shared without a Blaxel name is named
95
116
  * `preview-<port>`. */
96
117
  list(): Promise<SandboxPreview[]>;
97
- /** Shares `spec.port`: public with `spec.public`, else behind a token. */
98
- create(preview: Preview, _force?: boolean): Promise<SandboxPreview>;
99
- /** The named preview, or a new one when there is none. */
100
- createIfNotExists(preview: Preview, force?: boolean): Promise<SandboxPreview>;
118
+ /** Shares `spec.port`: public with `spec.public`, else behind a token.
119
+ * Blaxel's shorthand `{ port, name?, public? }` is a private
120
+ * `preview-<port>` unless it says otherwise. */
121
+ create(input: Preview | SandboxPreviewCreateConfiguration, _force?: boolean): Promise<SandboxPreview>;
122
+ /** The named preview, or a new one when there is none. An existing
123
+ * preview is answered as it is: the shorthand's private default applies
124
+ * only to a new one. */
125
+ createIfNotExists(input: Preview | SandboxPreviewCreateConfiguration, force?: boolean): Promise<SandboxPreview>;
101
126
  get(previewName: string): Promise<SandboxPreview>;
102
127
  /** Stops sharing the port; its open connections are closed before this returns. */
103
128
  delete(previewName: string): Promise<Preview>;
@@ -1,5 +1,22 @@
1
1
  import { guard, isNotFound, NotSupportedError, responseError } from "./errors.js";
2
2
  import { PREVIEW } from "./lookup.js";
3
+ const isShorthand = (preview) => !("metadata" in preview || "spec" in preview);
4
+ /** Blaxel's shorthand as the full shape, with Blaxel's checks and messages. */
5
+ function normalizePreview(preview) {
6
+ if (!isShorthand(preview))
7
+ return preview;
8
+ const short = preview;
9
+ if (!Number.isInteger(short.port) || short.port < 1 || short.port > 65535)
10
+ throw new RangeError("Preview port must be an integer between 1 and 65535");
11
+ if (short.name !== undefined && (typeof short.name !== "string" || short.name.length === 0))
12
+ throw new RangeError("Preview name must be a nonempty string");
13
+ if (short.public !== undefined && typeof short.public !== "boolean")
14
+ throw new RangeError("Preview public must be a boolean");
15
+ return {
16
+ metadata: { name: short.name ?? `preview-${short.port}` },
17
+ spec: { port: short.port, public: short.public ?? false },
18
+ };
19
+ }
3
20
  function checkName(name) {
4
21
  if (!/^[A-Za-z0-9._-]{1,48}$/.test(name))
5
22
  throw new NotSupportedError(`The preview name "${name}"`, "Use 1 to 48 letters, digits, dots, dashes or underscores.");
@@ -27,6 +44,9 @@ export class SandboxPreviewToken {
27
44
  constructor(previewToken) {
28
45
  this.#token = previewToken;
29
46
  }
47
+ get name() {
48
+ return this.#token.metadata?.name ?? "";
49
+ }
30
50
  get value() {
31
51
  return this.#token.spec.token ?? "";
32
52
  }
@@ -38,6 +58,9 @@ export class SandboxPreviewToken {
38
58
  return at !== undefined && Date.parse(at) <= Date.now();
39
59
  }
40
60
  }
61
+ /** The token createIfExpired made last, per sandbox and port, in this
62
+ * process: Runtime keeps no list of the tokens it signed. */
63
+ const madeTokens = new Map();
41
64
  /** A preview's tokens. Runtime signs a token for a duration; it keeps no
42
65
  * list of the tokens issued. */
43
66
  export class SandboxPreviewTokens {
@@ -53,6 +76,9 @@ export class SandboxPreviewTokens {
53
76
  get resourceName() {
54
77
  return this.#ctx.sandboxName;
55
78
  }
79
+ #key() {
80
+ return `${this.#ctx.sandboxId ?? this.#ctx.sandboxName}:${this.#preview.spec.port}`;
81
+ }
56
82
  /** A token bounded by `expiresAt`; native signing floors milliseconds and
57
83
  * caps it to the caller's session. Older servers refuse without a TTL fallback. */
58
84
  async create(expiresAt) {
@@ -73,6 +99,35 @@ export class SandboxPreviewTokens {
73
99
  spec: { token: got.token, expiresAt: got.tokenExpiresAt },
74
100
  });
75
101
  }
102
+ /** Blaxel's (0.3.29): the latest token lasting at least `minValidity` ms
103
+ * more (an hour unless given) and not past `expiresAt` (a day from now
104
+ * unless given), else a new one until `expiresAt`. Blaxel picks among the
105
+ * preview's listed tokens; Runtime keeps no list, so the one reused is the
106
+ * token this call made last in this process for this sandbox's port, and a
107
+ * new process makes a new one. Arguments are checked as Blaxel does. */
108
+ async createIfExpired(expiresAt, minValidity = 3_600_000) {
109
+ const now = Date.now();
110
+ if (!Number.isFinite(minValidity) || minValidity < 0)
111
+ throw new RangeError("minValidity must be finite and non-negative");
112
+ const requested = expiresAt ?? new Date(now + 86_400_000);
113
+ const ceiling = requested instanceof Date ? requested.getTime() : Number.NaN;
114
+ if (!Number.isFinite(ceiling) || ceiling <= now || ceiling < now + minValidity)
115
+ throw new RangeError("expiresAt must be a valid future date at least minValidity from now");
116
+ if (this.#preview.spec.public === true)
117
+ throw new Error("Cannot create or reuse a token for a public preview");
118
+ const key = this.#key();
119
+ const kept = madeTokens.get(key);
120
+ if (kept &&
121
+ kept.expiresAt > now &&
122
+ kept.expiresAt >= now + minValidity &&
123
+ kept.expiresAt <= ceiling)
124
+ return kept.token;
125
+ const token = await this.create(requested);
126
+ const until = Date.parse(String(token.expiresAt));
127
+ if (Number.isFinite(until))
128
+ madeTokens.set(key, { token, expiresAt: until });
129
+ return token;
130
+ }
76
131
  list() {
77
132
  return Promise.reject(new NotSupportedError("Listing a preview's tokens", "Runtime keeps no list of issued tokens: keep the ones you create, or create a new one."));
78
133
  }
@@ -81,6 +136,7 @@ export class SandboxPreviewTokens {
81
136
  async delete(_tokenName) {
82
137
  const port = this.#preview.spec.port;
83
138
  await this.#ctx.run((runtime) => runtime.previews.rotate(port));
139
+ madeTokens.delete(this.#key());
84
140
  return { message: "Every token of this preview was revoked" };
85
141
  }
86
142
  }
@@ -101,6 +157,10 @@ export class SandboxPreview {
101
157
  get spec() {
102
158
  return this.#preview.spec;
103
159
  }
160
+ /** The preview's address (`spec.url`), or `""`. */
161
+ get url() {
162
+ return this.#preview.spec?.url ?? "";
163
+ }
104
164
  }
105
165
  /** `sandbox.previews`. */
106
166
  export class SandboxPreviews {
@@ -133,8 +193,11 @@ export class SandboxPreviews {
133
193
  const names = this.#names();
134
194
  return previews.map((one) => new SandboxPreview(modelOf(one, names.get(one.port) ?? `preview-${one.port}`, this.sandboxName), this.#ctx));
135
195
  }
136
- /** Shares `spec.port`: public with `spec.public`, else behind a token. */
137
- async create(preview, _force) {
196
+ /** Shares `spec.port`: public with `spec.public`, else behind a token.
197
+ * Blaxel's shorthand `{ port, name?, public? }` is a private
198
+ * `preview-<port>` unless it says otherwise. */
199
+ async create(input, _force) {
200
+ const preview = input && typeof input === "object" ? normalizePreview(input) : input;
138
201
  const spec = preview?.spec ?? {};
139
202
  const name = preview?.metadata?.name;
140
203
  const refusals = [
@@ -177,15 +240,26 @@ export class SandboxPreviews {
177
240
  ]);
178
241
  return new SandboxPreview(modelOf(made, name ?? `preview-${port}`, this.sandboxName), this.#ctx);
179
242
  }
180
- /** The named preview, or a new one when there is none. */
181
- async createIfNotExists(preview, force) {
243
+ /** The named preview, or a new one when there is none. An existing
244
+ * preview is answered as it is: the shorthand's private default applies
245
+ * only to a new one. */
246
+ async createIfNotExists(input, force) {
247
+ const preview = normalizePreview(input);
182
248
  try {
183
249
  return await this.get(preview.metadata.name);
184
250
  }
185
251
  catch (error) {
186
252
  if (!isNotFound(error))
187
253
  throw error;
188
- return this.create(preview, force);
254
+ }
255
+ try {
256
+ return await this.create(preview, force);
257
+ }
258
+ catch (error) {
259
+ // Blaxel's race: another caller made it first.
260
+ if (!isShorthand(input) || error?.status !== 409)
261
+ throw error;
262
+ return this.get(preview.metadata.name);
189
263
  }
190
264
  }
191
265
  async get(previewName) {
@@ -139,7 +139,7 @@ export declare class SandboxInstance {
139
139
  get expiresIn(): number | undefined;
140
140
  /** Creates a sandbox and answers once it runs. Blaxel's defaults: the base
141
141
  * image (Runtime's stock image) and 4096 MB, with vCPUs at one per 2048 MB.
142
- * Funding is left to Runtime: the included usage first, then prepaid credit. A name another live sandbox holds is refused
142
+ * Funding is left to Runtime: the free usage first, then prepaid credit. A name another live sandbox holds is refused
143
143
  * with a 409, as in Blaxel. */
144
144
  static create<T extends typeof SandboxInstance>(this: T, sandbox?: SandboxCreateConfiguration | SandboxModel, options?: SandboxCreateOptions): Promise<InstanceType<T>>;
145
145
  /** The named sandbox, or a new one when no live sandbox has the name.
@@ -332,6 +332,7 @@ export class SandboxInstance {
332
332
  });
333
333
  this.previews = new SandboxPreviews({
334
334
  sandboxName: this.name,
335
+ sandboxId: this.#rt.id,
335
336
  run,
336
337
  labels: () => this.#rt.info.labels,
337
338
  setLabels: (changes) => this.#setLabels(changes),
@@ -573,7 +574,7 @@ export class SandboxInstance {
573
574
  // ---- create, get, list ---------------------------------------------------
574
575
  /** Creates a sandbox and answers once it runs. Blaxel's defaults: the base
575
576
  * image (Runtime's stock image) and 4096 MB, with vCPUs at one per 2048 MB.
576
- * Funding is left to Runtime: the included usage first, then prepaid credit. A name another live sandbox holds is refused
577
+ * Funding is left to Runtime: the free usage first, then prepaid credit. A name another live sandbox holds is refused
577
578
  * with a 409, as in Blaxel. */
578
579
  static async create(sandbox, options = {}) {
579
580
  const { createIfNotExist = false, safe = false } = options;
@@ -44,8 +44,10 @@ function origins(headers = {}) {
44
44
  return found;
45
45
  }
46
46
  function seconds(expiresAt) {
47
- const at = expiresAt ?? new Date(Date.now() + DAY_MS);
48
- const left = Math.floor((at.getTime() - Date.now()) / 1000);
47
+ // Blaxel's default is a day; counted from a moment ago it would be a second short.
48
+ if (!expiresAt)
49
+ return DAY_MS / 1000;
50
+ const left = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
49
51
  if (!Number.isFinite(left) || left < 60)
50
52
  throw responseError(400, "A session must last at least a minute.");
51
53
  if (left > DAY_MS / 1000 + 5)
@@ -108,7 +108,7 @@ export async function mcpCommand(sbx, argv, out, kit) {
108
108
  };
109
109
  switch (action) {
110
110
  case "start": {
111
- const args = parse(rest, ["replace"], ["secret", "env", "option", "port"]);
111
+ const args = parse(rest, ["replace", "public"], ["secret", "env", "option", "port"]);
112
112
  if (!args.positional.length)
113
113
  throw usage("Name the servers: runtime sandboxes mcp <id> start github fetch");
114
114
  const scoped = (name) => {
@@ -133,6 +133,7 @@ export async function mcpCommand(sbx, argv, out, kit) {
133
133
  const gw = await sbx.mcp.start(servers, {
134
134
  ...(integer(args, "port") ? { port: integer(args, "port") } : {}),
135
135
  ...(has(args, "replace") ? { replace: true } : {}),
136
+ ...(has(args, "public") ? { public: true } : {}),
136
137
  });
137
138
  print(`${show(gw)}\nServers are installing; \`${me} sandboxes mcp ${sbx.id}\` shows when they are ready.`, gw);
138
139
  return 0;
package/dist/cli-words.js CHANGED
@@ -24,6 +24,7 @@ const OPTIONS = {
24
24
  funding: { flag: "--trial / --paid" },
25
25
  image: { flag: "--image" },
26
26
  snapshot: { flag: "--snapshot" },
27
+ spreadFrom: { flag: "--spread-from" },
27
28
  volumes: { flag: "--volume" },
28
29
  network: { flag: "--allow / --deny / --connect / --no-internet" },
29
30
  maxCostMicros: { flag: "--max-cost", micros: true },
package/dist/cli.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { RuntimeError } from "./errors.js";
3
3
  import type { OutOfMemory, Usage } from "./types.js";
4
4
  import type { SwitchProvider } from "./products/switching.js";
5
- import type { SecretRule } from "./products/secrets.js";
5
+ import type { SecretRule, SecretScope } from "./products/secrets.js";
6
6
  type Out = {
7
7
  json: boolean;
8
8
  write: (text: string) => void;
@@ -30,7 +30,7 @@ declare const kit: {
30
30
  };
31
31
  export type CliKit = typeof kit;
32
32
  export declare function run(argv: string[], env?: NodeJS.ProcessEnv, out?: Out): Promise<number>;
33
- /** `runtime usage` for a person: the balance, the included usage and what each kind
33
+ /** `runtime usage` for a person: the balance, the free usage and what each kind
34
34
  * of resource was charged, in dollars. --json keeps every figure, and each
35
35
  * resource with its rates and CPU time. */
36
36
  /** One row per resource, for a spreadsheet: when it ran, how big it was, how
@@ -44,12 +44,27 @@ export declare function usageCsv(u: Usage): string;
44
44
  export declare function outOfMemoryLines(memory: OutOfMemory | undefined): string | undefined;
45
45
  /** How to move a rival's code across, when one import does it. */
46
46
  export declare function dropInLines(provider: SwitchProvider | undefined): string[];
47
+ /** A secret's value, from standard input only, so it never sits in shell
48
+ * history or a process list. Piped: everything, less one trailing newline.
49
+ * A terminal: one line, not shown. */
50
+ /** A pasted secret as the header it becomes: no line breaks (a token copied
51
+ * from a terminal arrives wrapped), no bracketed-paste markers, no space at
52
+ * either end. 13 October 2026: a user's pasted Claude token was refused as
53
+ * "not visible ASCII". A header cannot hold a line break, so none is lost. */
54
+ export declare function cleanSecret(raw: string): string;
47
55
  /** `--allow` values as secret rules: each is `[METHOD[,METHOD]...] PATH`, and
48
56
  * rules naming the same methods share one rule's paths. Only the shape is read
49
57
  * here; the API holds every rule to the same check the host makes
50
58
  * (ARCHITECTURE.md section 10, "Per-path rules on an egress secret") and says
51
59
  * which one it refused. */
52
60
  export declare function parseSecretRules(values: string[]): SecretRule[];
61
+ /** `--only-label role=hub,tier=x --only-sandbox <id>`: a sandbox whose labels
62
+ * hold every pair of any one --only-label gets the secret, and so does each
63
+ * --only-sandbox. `--all-sandboxes`: every sandbox again. Nothing: undefined,
64
+ * so a replacement keeps the scope it has. */
65
+ export declare function parseSecretScope(labels: string[] | undefined, sandboxes: string[] | undefined, all: boolean): SecretScope | undefined;
66
+ /** Who gets a secret, in a few words. */
67
+ export declare const scopeText: (only: SecretScope | undefined) => string;
53
68
  export declare function secretsCommand(argv: string[], env: NodeJS.ProcessEnv, out: Out,
54
69
  /** Tests give the value here; the CLI reads standard input. */
55
70
  read?: () => Promise<string>): Promise<number>;