withruntime 0.11.1 → 0.12.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 (176) hide show
  1. package/BLAXEL.md +3 -3
  2. package/COMPATIBILITY.md +74 -9
  3. package/DAYTONA.md +33 -5
  4. package/E2B.md +137 -48
  5. package/README.md +11 -9
  6. package/README.src.md +11 -9
  7. package/VERCEL.md +43 -5
  8. package/compatibility-lock.json +58 -49
  9. package/dist/billing-cli.d.ts +1 -1
  10. package/dist/billing-cli.js +77 -1
  11. package/dist/blaxel/errors.d.ts +14 -0
  12. package/dist/blaxel/errors.js +33 -4
  13. package/dist/blaxel/index.d.ts +2 -2
  14. package/dist/blaxel/index.js +4 -4
  15. package/dist/blaxel/interpreter.d.ts +2 -5
  16. package/dist/blaxel/sandbox.d.ts +15 -7
  17. package/dist/blaxel/sandbox.js +64 -9
  18. package/dist/blaxel/snapshot.d.ts +4 -0
  19. package/dist/blaxel/snapshot.js +4 -0
  20. package/dist/cli/logs.d.ts +21 -0
  21. package/dist/cli/logs.js +188 -0
  22. package/dist/cli/logs.test.d.ts +1 -0
  23. package/dist/cli/logs.test.js +272 -0
  24. package/dist/cli-extras.d.ts +3 -3
  25. package/dist/cli-extras.js +17 -12
  26. package/dist/cli-words.d.ts +10 -1
  27. package/dist/cli-words.js +18 -0
  28. package/dist/cli.d.ts +6 -2
  29. package/dist/cli.js +468 -219
  30. package/dist/client.d.ts +13 -63
  31. package/dist/client.js +35 -124
  32. package/dist/cloudflare/errors.d.ts +143 -0
  33. package/dist/cloudflare/errors.js +167 -0
  34. package/dist/cloudflare/index.d.ts +175 -6
  35. package/dist/cloudflare/index.js +416 -26
  36. package/dist/cloudflare/interpreter.d.ts +119 -0
  37. package/dist/cloudflare/interpreter.js +223 -0
  38. package/dist/cloudflare/streams.d.ts +38 -0
  39. package/dist/cloudflare/streams.js +145 -0
  40. package/dist/cloudflare/watch.d.ts +9 -0
  41. package/dist/cloudflare/watch.js +24 -0
  42. package/dist/codesandbox/index.d.ts +216 -28
  43. package/dist/codesandbox/index.js +478 -78
  44. package/dist/compat/core.d.ts +18 -0
  45. package/dist/compat/core.js +29 -0
  46. package/dist/compat/files.d.ts +1 -0
  47. package/dist/compat/files.js +25 -5
  48. package/dist/daytona/computer-use.d.ts +13 -4
  49. package/dist/daytona/computer-use.js +66 -8
  50. package/dist/daytona/daytona.d.ts +7 -2
  51. package/dist/daytona/daytona.js +31 -5
  52. package/dist/daytona/errors.d.ts +11 -0
  53. package/dist/daytona/errors.js +18 -0
  54. package/dist/daytona/filesystem.js +2 -1
  55. package/dist/daytona/image.d.ts +13 -9
  56. package/dist/daytona/image.js +18 -12
  57. package/dist/daytona/index.d.ts +1 -1
  58. package/dist/daytona/sandbox.d.ts +9 -3
  59. package/dist/daytona/sandbox.js +17 -3
  60. package/dist/domains/index.d.ts +9 -0
  61. package/dist/domains/index.js +10 -0
  62. package/dist/e2b/client.d.ts +15 -2
  63. package/dist/e2b/client.js +61 -7
  64. package/dist/e2b/commands.d.ts +4 -1
  65. package/dist/e2b/commands.js +17 -7
  66. package/dist/e2b/errors.d.ts +43 -2
  67. package/dist/e2b/errors.js +78 -6
  68. package/dist/e2b/filesystem.d.ts +1 -1
  69. package/dist/e2b/filesystem.js +82 -16
  70. package/dist/e2b/index.d.ts +3 -3
  71. package/dist/e2b/index.js +2 -2
  72. package/dist/e2b/sandbox.d.ts +50 -35
  73. package/dist/e2b/sandbox.js +228 -185
  74. package/dist/e2b/unsupported.d.ts +19 -0
  75. package/dist/e2b/unsupported.js +22 -1
  76. package/dist/e2b/users.d.ts +9 -0
  77. package/dist/e2b/users.js +22 -0
  78. package/dist/errors.d.ts +14 -5
  79. package/dist/errors.js +28 -14
  80. package/dist/freestyle/index.d.ts +285 -37
  81. package/dist/freestyle/index.js +656 -144
  82. package/dist/identity.d.ts +3 -0
  83. package/dist/identity.js +27 -0
  84. package/dist/images/index.d.ts +9 -0
  85. package/dist/images/index.js +10 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/ips/index.d.ts +9 -0
  88. package/dist/ips/index.js +10 -0
  89. package/dist/jobs/index.d.ts +9 -0
  90. package/dist/jobs/index.js +10 -0
  91. package/dist/keys.js +2 -0
  92. package/dist/login.js +2 -0
  93. package/dist/machine.d.ts +189 -0
  94. package/dist/machine.js +521 -0
  95. package/dist/mcp.js +1 -1
  96. package/dist/modal/index.d.ts +339 -61
  97. package/dist/modal/index.js +781 -121
  98. package/dist/network-cli.d.ts +3 -1
  99. package/dist/network-cli.js +78 -36
  100. package/dist/networks/index.d.ts +11 -0
  101. package/dist/networks/index.js +10 -0
  102. package/dist/openai-agents/index.d.ts +1 -1
  103. package/dist/openai-agents/index.js +1 -1
  104. package/dist/ports/index.d.ts +9 -0
  105. package/dist/ports/index.js +10 -0
  106. package/dist/product-names.d.ts +52 -0
  107. package/dist/product-names.js +164 -0
  108. package/dist/product-renames.d.ts +103 -0
  109. package/dist/product-renames.js +189 -0
  110. package/dist/products/apps.d.ts +176 -0
  111. package/dist/products/apps.js +165 -0
  112. package/dist/products/billing.d.ts +41 -0
  113. package/dist/products/billing.js +13 -0
  114. package/dist/products/browser.d.ts +10 -0
  115. package/dist/products/browsers.d.ts +71 -0
  116. package/dist/products/browsers.js +102 -0
  117. package/dist/products/builds.d.ts +126 -0
  118. package/dist/products/builds.js +221 -0
  119. package/dist/products/index.d.ts +3 -16
  120. package/dist/products/index.js +3 -14
  121. package/dist/products/jobs.d.ts +19 -3
  122. package/dist/products/jobs.js +28 -12
  123. package/dist/products/limits.d.ts +4 -10
  124. package/dist/products/logs.d.ts +245 -0
  125. package/dist/products/logs.js +274 -0
  126. package/dist/products/logs.test.d.ts +1 -0
  127. package/dist/products/logs.test.js +343 -0
  128. package/dist/products/network-products.d.ts +38 -7
  129. package/dist/products/network-products.js +31 -18
  130. package/dist/products/network.d.ts +1 -1
  131. package/dist/products/observability.d.ts +3 -2
  132. package/dist/products/private-network.js +1 -1
  133. package/dist/products/queues.d.ts +149 -0
  134. package/dist/products/queues.js +213 -0
  135. package/dist/products/referrals.d.ts +21 -7
  136. package/dist/products/referrals.js +3 -3
  137. package/dist/products/secrets.d.ts +8 -4
  138. package/dist/products/secrets.js +39 -18
  139. package/dist/products/signed-requests.d.ts +21 -0
  140. package/dist/products/signed-requests.js +11 -0
  141. package/dist/products/streams.d.ts +73 -0
  142. package/dist/products/streams.js +17 -0
  143. package/dist/products/switching.d.ts +37 -10
  144. package/dist/products/switching.js +4 -3
  145. package/dist/products/volumes.d.ts +3 -0
  146. package/dist/record-cli.d.ts +23 -0
  147. package/dist/record-cli.js +308 -0
  148. package/dist/renamed-products.d.ts +38 -0
  149. package/dist/renamed-products.js +34 -0
  150. package/dist/runloop/index.d.ts +9 -2
  151. package/dist/runloop/index.js +29 -8
  152. package/dist/runloop/sdk.d.ts +131 -0
  153. package/dist/runloop/sdk.js +167 -0
  154. package/dist/sandbox.d.ts +10 -94
  155. package/dist/sandbox.js +22 -371
  156. package/dist/sandboxes/index.d.ts +11 -0
  157. package/dist/sandboxes/index.js +12 -0
  158. package/dist/snapshots/index.d.ts +9 -0
  159. package/dist/snapshots/index.js +11 -0
  160. package/dist/snapshots.d.ts +7 -4
  161. package/dist/sprites/index.d.ts +343 -31
  162. package/dist/sprites/index.js +866 -84
  163. package/dist/ssh.d.ts +1 -1
  164. package/dist/ssh.js +11 -11
  165. package/dist/terminal.d.ts +24 -0
  166. package/dist/terminal.js +32 -0
  167. package/dist/transport.d.ts +18 -2
  168. package/dist/transport.js +61 -6
  169. package/dist/tunnel.js +1 -1
  170. package/dist/types.d.ts +116 -8
  171. package/dist/vercel/sandbox.d.ts +1 -2
  172. package/dist/vercel/sandbox.js +6 -5
  173. package/dist/vercel/snapshot.js +10 -2
  174. package/dist/volumes/index.d.ts +9 -0
  175. package/dist/volumes/index.js +10 -0
  176. package/package.json +48 -2
@@ -1,11 +1,11 @@
1
1
  import { type Config } from "./client.js";
2
- export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, type SandboxListQuery, type SandboxForkOptions, type SandboxArchiveOptions, } from "./sandbox.js";
2
+ export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, type SandboxListQuery, type SandboxForkOptions, type SandboxArchiveOptions, MAX_CREATION_TIMEOUT_SECONDS, type SandboxCreateOptions, } from "./sandbox.js";
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
6
  export { SandboxPreview, SandboxPreviews, SandboxPreviewToken, SandboxPreviewTokens, type Preview, type PreviewMetadata, type PreviewSpec, type PreviewToken, } 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, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } from "./errors.js";
8
+ export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } 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";
@@ -4,20 +4,20 @@
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.23
7
+ * (BL_API_KEY) is never sent anywhere. Every name `@blaxel/core` 0.3.25
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
11
  import { initialize } from "./client.js";
12
12
  import { NotSupportedError } from "./errors.js";
13
13
  import { CODEGEN, DRIVES, SCHEDULES, SYSTEM, unsupportedExport } from "./unsupported.js";
14
- export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, } from "./sandbox.js";
14
+ export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, MAX_CREATION_TIMEOUT_SECONDS, } from "./sandbox.js";
15
15
  export { CodeInterpreter } from "./interpreter.js";
16
16
  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, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } from "./errors.js";
20
+ export { ResponseError, SandboxCreationTimeoutError, isCreationTimeoutError, SandboxGatewayError, isGatewayError, isGatewayTimeout, CredentialsError, NotSupportedError, } 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";
@@ -88,7 +88,7 @@ const MODELS = "Call your model provider's SDK directly; Runtime does not proxy
88
88
  const TOOLS = "Give your agent Runtime's sandbox tools: `import { tools } from \"withruntime/tools\"`, or Runtime's MCP server (`npx withruntime mcp`).";
89
89
  const MCP = "Serve MCP from a sandbox and share its port with sandbox.previews.create, or use Runtime's own MCP server (`npx withruntime mcp`).";
90
90
  const JOBS = "Run the batch as processes in sandboxes: SandboxInstance.create, then sandbox.process.exec for each task.";
91
- const IMAGES = "Build a Runtime image and create sandboxes from it by name: `npx withruntime image build --dockerfile Dockerfile --name <image>` or runtime.images.build({ name, dockerfile | image }).";
91
+ const IMAGES = "Build a Runtime image and create sandboxes from it by name: `npx withruntime images build --dockerfile Dockerfile --name <image>` or runtime.images.build({ name, dockerfile | image }).";
92
92
  const VOLUMES = 'Use Runtime volumes: runtime.volumes.create({ name }) (`import { Runtime } from "withruntime"`), then pass volumes: [{ name, mountPath }] at create.';
93
93
  const HOSTING = "Serve it from a sandbox and share its port with sandbox.previews.create.";
94
94
  const TELEMETRY = "Send your own traces; Runtime exports sandbox metrics and events with runtime.otel (OpenTelemetry).";
@@ -1,4 +1,4 @@
1
- import { SandboxInstance } from "./sandbox.js";
1
+ import { SandboxInstance, type SandboxCreateOptions } from "./sandbox.js";
2
2
  import type { Port, Sandbox as SandboxModel, SandboxCreateConfiguration, SandboxLifecycle } from "./types.js";
3
3
  declare class OutputMessage {
4
4
  text: string;
@@ -45,10 +45,7 @@ export declare class CodeInterpreter extends SandboxInstance {
45
45
  static readonly Context: typeof Context;
46
46
  /** Blaxel's defaults for a code interpreter: its image, port 8888, and
47
47
  * deletion after 30 idle minutes (a paused sandbox kept one day). */
48
- static create<T extends typeof SandboxInstance>(this: T, sandbox?: SandboxCreateConfiguration | SandboxModel | Record<string, unknown> | null, options?: {
49
- safe?: boolean;
50
- createIfNotExist?: boolean;
51
- }): Promise<InstanceType<T>>;
48
+ static create<T extends typeof SandboxInstance>(this: T, sandbox?: SandboxCreateConfiguration | SandboxModel | Record<string, unknown> | null, options?: SandboxCreateOptions): Promise<InstanceType<T>>;
52
49
  get _jupyterUrl(): string;
53
50
  /** Runs code in a stateful context. Errors in the code come back in
54
51
  * `execution.error`; a run past `timeout` seconds (default 60, 0 for none)
@@ -40,6 +40,18 @@ export type SandboxArchiveOptions = {
40
40
  maxWait?: number;
41
41
  interval?: number;
42
42
  };
43
+ /** The longest creation `timeout` Blaxel accepts, in seconds. */
44
+ export declare const MAX_CREATION_TIMEOUT_SECONDS = 50;
45
+ /** Options of SandboxInstance.create / createIfNotExists, as Blaxel's. */
46
+ export type SandboxCreateOptions = {
47
+ /** List `/` once it runs, and delete it if that fails. */
48
+ safe?: boolean;
49
+ /** Answer the sandbox already holding the name instead of a 409. */
50
+ createIfNotExist?: boolean;
51
+ /** Give up after this many whole seconds (1 to 50): the sandbox is deleted
52
+ * and the call rejects with SandboxCreationTimeoutError. */
53
+ timeout?: number;
54
+ };
43
55
  /** What a create asked for that no label keeps: shown by `spec` in the
44
56
  * client that created the sandbox. */
45
57
  type Asked = {
@@ -127,16 +139,12 @@ export declare class SandboxInstance {
127
139
  get expiresIn(): number | undefined;
128
140
  /** Creates a sandbox and answers once it runs. Blaxel's defaults: the base
129
141
  * image (Runtime's stock image) and 4096 MB, with vCPUs at one per 2048 MB.
130
- * Funding is left to Runtime: the free trial while the account has trial
131
- * time, then prepaid credit. A name another live sandbox holds is refused
142
+ * Funding is left to Runtime: the included usage first, then prepaid credit. A name another live sandbox holds is refused
132
143
  * with a 409, as in Blaxel. */
133
- static create<T extends typeof SandboxInstance>(this: T, sandbox?: SandboxCreateConfiguration | SandboxModel, { createIfNotExist }?: {
134
- safe?: boolean;
135
- createIfNotExist?: boolean;
136
- }): Promise<InstanceType<T>>;
144
+ static create<T extends typeof SandboxInstance>(this: T, sandbox?: SandboxCreateConfiguration | SandboxModel, options?: SandboxCreateOptions): Promise<InstanceType<T>>;
137
145
  /** The named sandbox, or a new one when no live sandbox has the name.
138
146
  * Runtime settles a race between two callers: both get the same sandbox. */
139
- static createIfNotExists<T extends typeof SandboxInstance>(this: T, sandbox: SandboxCreateConfiguration | SandboxModel): Promise<InstanceType<T>>;
147
+ static createIfNotExists<T extends typeof SandboxInstance>(this: T, sandbox: SandboxCreateConfiguration | SandboxModel, options?: Omit<SandboxCreateOptions, "createIfNotExist">): Promise<InstanceType<T>>;
140
148
  /** The live sandbox with this name (or Runtime id). It is not woken: the
141
149
  * next call wakes it. */
142
150
  static get<T extends typeof SandboxInstance>(this: T, sandboxName: string, options?: RuntimeOptions): Promise<InstanceType<T>>;
@@ -1,7 +1,7 @@
1
1
  import { RuntimeError } from "../errors.js";
2
2
  import { clientFor, defaultRegion, } from "./client.js";
3
3
  import { checkEnvNames, envFileCommand, envFileLines, parseProcessLine, toRuntimePath, } from "./context.js";
4
- import { codeOf, guard, NotSupportedError, responseError, translate } from "./errors.js";
4
+ import { codeOf, guard, NotSupportedError, responseError, SandboxCreationTimeoutError, translate, } from "./errors.js";
5
5
  import { SandboxFileSystem } from "./filesystem.js";
6
6
  import { ARCHIVED, DISPLAY_NAME, EXTERNAL_ID, IDLE_PAUSE, LABEL, findByExternalId, findSandbox, LEASE_SECONDS, ownLabels, STANDBY, userLabels, whenNameFree, } from "./lookup.js";
7
7
  import { paginate } from "./pagination.js";
@@ -38,6 +38,56 @@ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
38
38
  const DAY_MS = 86_400_000;
39
39
  /** The lease is renewed in the background once this little of it is left. */
40
40
  const RENEW_BELOW_MS = 10 * 60_000;
41
+ /** The longest creation `timeout` Blaxel accepts, in seconds. */
42
+ export const MAX_CREATION_TIMEOUT_SECONDS = 50;
43
+ /** Blaxel's own check of `timeout`, with its words. */
44
+ function creationTimeout({ timeout }) {
45
+ if (timeout === undefined)
46
+ return undefined;
47
+ if (!Number.isInteger(timeout) || timeout < 1 || timeout > MAX_CREATION_TIMEOUT_SECONDS)
48
+ throw new Error(`SandboxInstance.create: 'timeout' must be a whole number of seconds between 1 and ${MAX_CREATION_TIMEOUT_SECONDS}, got ${timeout}.`);
49
+ return timeout;
50
+ }
51
+ /** A create that must be running within `timeout` seconds of the call, the
52
+ * wait for capacity included. One that is not is deleted, so the name is
53
+ * free again, and the call rejects with SandboxCreationTimeoutError. */
54
+ async function createWithin(client, input, timeout) {
55
+ const deadline = performance.now() + timeout * 1000;
56
+ const runtime = await guard(() => whenNameFree(client, () => client.sandboxes.create({ ...input, wait: false }, { waitForCapacityMs: timeout * 1000 })));
57
+ // A sandbox the name already had is the caller's, whatever its state: it is
58
+ // answered as it is, as without a timeout, and never deleted here.
59
+ if (runtime.state === "running" || runtime.info.reused)
60
+ return runtime;
61
+ const left = deadline - performance.now();
62
+ if (left > 0)
63
+ try {
64
+ await runtime.waitFor("running", {
65
+ timeoutSeconds: Math.max(1, Math.ceil(left / 1000)),
66
+ timeoutMs: Math.ceil(left) + 5_000,
67
+ });
68
+ }
69
+ catch (error) {
70
+ if (!(error instanceof RuntimeError && error.code === "timeout")) {
71
+ await runtime.delete().catch(() => undefined);
72
+ throw translate(error);
73
+ }
74
+ }
75
+ // waitFor updated the state TypeScript narrowed above.
76
+ const state = runtime.state;
77
+ if (state === "running")
78
+ return runtime;
79
+ await runtime.delete().catch(() => undefined);
80
+ if (state !== "starting")
81
+ throw translate(new RuntimeError({
82
+ message: `Sandbox ${runtime.id} is ${runtime.state}, not running.`,
83
+ code: "start_failed",
84
+ status: 0,
85
+ hint: "Create it again; SandboxInstance.get(name) reads why it stopped.",
86
+ }));
87
+ throw new SandboxCreationTimeoutError(input.name, timeout, {
88
+ message: `It was still starting after ${timeout}s.`,
89
+ });
90
+ }
41
91
  /** The labels a new sandbox carries: the caller's, and under `blaxel/` what
42
92
  * Blaxel keeps and Runtime has no field for, so any client (the Python
43
93
  * adapter too) reads the same `spec`. */
@@ -212,7 +262,7 @@ async function resolveImage(client, image) {
212
262
  if (found && found.state === "ready")
213
263
  return { image: found.id };
214
264
  const feature = `The image ${image}, which is not a Runtime image`;
215
- const alternative = `Build it as a Runtime image named ${name}: \`npx withruntime image build --dockerfile Dockerfile --name ${name} -t ${name}:${tag}\`. The code can keep "${image}": the adapter starts from ${name}:${tag}.`;
265
+ const alternative = `Build it as a Runtime image named ${name}: \`npx withruntime images build --dockerfile Dockerfile --name ${name} -t ${name}:${tag}\`. The code can keep "${image}": the adapter starts from ${name}:${tag}.`;
216
266
  throw new NotSupportedError(feature, alternative, `${feature}, is not supported on Runtime. ${alternative}`);
217
267
  }
218
268
  /** Blaxel volumes (by name) as Runtime volumes. */
@@ -228,7 +278,7 @@ async function resolveVolumes(client, volumes) {
228
278
  return Promise.all(volumes.map(async (volume) => {
229
279
  const found = (await guard(() => client.volumes.list({ name: volume.name }))).data[0];
230
280
  if (!found)
231
- throw new NotSupportedError(`The volume ${volume.name}, which is not a Runtime volume`, `Create it first: runtime.volumes.create({ name: "${volume.name}" }) or \`npx withruntime volume create --name ${volume.name}\`.`);
281
+ throw new NotSupportedError(`The volume ${volume.name}, which is not a Runtime volume`, `Create it first: runtime.volumes.create({ name: "${volume.name}" }) or \`npx withruntime volumes create --name ${volume.name}\`.`);
232
282
  return { volumeId: found.id, path: toRuntimePath(volume.mountPath) };
233
283
  }));
234
284
  }
@@ -523,10 +573,11 @@ export class SandboxInstance {
523
573
  // ---- create, get, list ---------------------------------------------------
524
574
  /** Creates a sandbox and answers once it runs. Blaxel's defaults: the base
525
575
  * image (Runtime's stock image) and 4096 MB, with vCPUs at one per 2048 MB.
526
- * Funding is left to Runtime: the free trial while the account has trial
527
- * time, then prepaid credit. A name another live sandbox holds is refused
576
+ * Funding is left to Runtime: the included usage first, then prepaid credit. A name another live sandbox holds is refused
528
577
  * with a 409, as in Blaxel. */
529
- static async create(sandbox, { createIfNotExist = false } = {}) {
578
+ static async create(sandbox, options = {}) {
579
+ const { createIfNotExist = false, safe = false } = options;
580
+ const timeout = creationTimeout(options);
530
581
  const config = configOf(sandbox);
531
582
  const region = config.region || defaultRegion();
532
583
  checkRegion(region);
@@ -560,7 +611,9 @@ export class SandboxInstance {
560
611
  ...(createIfNotExist && config.name ? { getOrCreate: true } : {}),
561
612
  ...config.withruntime?.create,
562
613
  };
563
- const runtime = await guard(() => whenNameFree(client, () => client.sandboxes.create(input)));
614
+ const runtime = timeout === undefined
615
+ ? await guard(() => whenNameFree(client, () => client.sandboxes.create(input)))
616
+ : await createWithin(client, input, timeout);
564
617
  const asked = {
565
618
  ...(Object.keys(envs).length ? { envs } : {}),
566
619
  ...(config.network ? { network: config.network } : {}),
@@ -572,6 +625,8 @@ export class SandboxInstance {
572
625
  const instance = new this({ runtime, client, asked });
573
626
  try {
574
627
  await instance.#setUp(envs, lifetime.days);
628
+ if (safe)
629
+ await instance.fs.ls("/");
575
630
  }
576
631
  catch (error) {
577
632
  await runtime.stop({ wait: false }).catch(() => undefined);
@@ -581,8 +636,8 @@ export class SandboxInstance {
581
636
  }
582
637
  /** The named sandbox, or a new one when no live sandbox has the name.
583
638
  * Runtime settles a race between two callers: both get the same sandbox. */
584
- static async createIfNotExists(sandbox) {
585
- return this.create(sandbox, { createIfNotExist: true });
639
+ static async createIfNotExists(sandbox, options = {}) {
640
+ return this.create(sandbox, { ...options, createIfNotExist: true });
586
641
  }
587
642
  /** The live sandbox with this name (or Runtime id). It is not woken: the
588
643
  * next call wakes it. */
@@ -71,6 +71,10 @@ export declare function appendEnvs(runtime: RuntimeSandbox, envs: Record<string,
71
71
  export declare function startFromSnapshot(client: Runtime, snapshotId: string, name: string, labels: Record<string, string>, envs: Record<string, string>): Promise<RuntimeSandbox>;
72
72
  declare function refuseApplication(options?: {
73
73
  targetType?: string;
74
+ port?: number;
75
+ traffic?: number;
76
+ customDomain?: string;
77
+ prefix?: string;
74
78
  }): void;
75
79
  /** A snapshot, as Blaxel's Snapshot class. */
76
80
  export declare class Snapshot {
@@ -63,6 +63,10 @@ export async function startFromSnapshot(client, snapshotId, name, labels, envs)
63
63
  function refuseApplication(options = {}) {
64
64
  if (options.targetType === "application")
65
65
  throw new NotSupportedError("Forking into an application", "Fork into a sandbox and share its port with sandbox.previews.create.");
66
+ // Blaxel's application-fork routing: a sandbox fork has no use for them.
67
+ for (const key of ["port", "traffic", "customDomain", "prefix"])
68
+ if (options[key] !== undefined)
69
+ throw new NotSupportedError(`A fork's ${key}`, "Fork without it, then share a port of the copy with sandbox.previews.create({ metadata: { name }, spec: { port } }).");
66
70
  }
67
71
  /** A snapshot, as Blaxel's Snapshot class. */
68
72
  export class Snapshot {
@@ -0,0 +1,21 @@
1
+ import type { ObservabilityClient } from "../products/logs.js";
2
+ type Product = "logs" | "traces" | "alerts";
3
+ type Args = {
4
+ positional: string[];
5
+ flags: Map<string, string[]>;
6
+ };
7
+ type Out = {
8
+ json: boolean;
9
+ tty?: boolean;
10
+ drain?: () => Promise<void>;
11
+ write(text: string): void;
12
+ error(text: string): void;
13
+ };
14
+ export declare const LOGS_HELP = "runtime logs tail <selector|app> [--since 1h] [--grep text] [--json] [--raw]\nruntime logs query '<LogQL>' [--from <time>] [--to <time>] [--limit <n>]\nruntime logs labels | values <label>\nruntime logs views ls|get|create|update|delete [id] [--body '<JSON>']\nruntime traces get <id> | search '<TraceQL>' | tags\nruntime traces views ls|get|create|update|delete [id] [--body '<JSON>']\nruntime logs ingest --body '<OTLP JSON>'\nruntime traces ingest --body '<OTLP JSON>'\nruntime alerts ls|get|create|update|delete|test|silence [id]\n create/update: --body '<JSON: name, labels, query, condition, for, actions>'\n silence: --until <RFC3339> [--reason <text>]\nEvery command takes --json. Writes take --idempotency-key <key>.\nOn a terminal, control characters in logs show as \\uXXXX; --raw prints them as written.";
15
+ /** Same parsed argument and output ports as the existing CLI. No registration
16
+ * occurs here: the launch owner supplies a client only after launch. */
17
+ export declare function logsProductCommand(product: Product, argv: string[], args: Args, out: Out, client: () => Promise<ObservabilityClient>, options?: {
18
+ signal?: AbortSignal;
19
+ now?: number;
20
+ }): Promise<number>;
21
+ export {};
@@ -0,0 +1,188 @@
1
+ import { RuntimeError } from "../errors.js";
2
+ import { terminalSafe } from "../terminal.js";
3
+ export const LOGS_HELP = `runtime logs tail <selector|app> [--since 1h] [--grep text] [--json] [--raw]
4
+ runtime logs query '<LogQL>' [--from <time>] [--to <time>] [--limit <n>]
5
+ runtime logs labels | values <label>
6
+ runtime logs views ls|get|create|update|delete [id] [--body '<JSON>']
7
+ runtime traces get <id> | search '<TraceQL>' | tags
8
+ runtime traces views ls|get|create|update|delete [id] [--body '<JSON>']
9
+ runtime logs ingest --body '<OTLP JSON>'
10
+ runtime traces ingest --body '<OTLP JSON>'
11
+ runtime alerts ls|get|create|update|delete|test|silence [id]
12
+ create/update: --body '<JSON: name, labels, query, condition, for, actions>'
13
+ silence: --until <RFC3339> [--reason <text>]
14
+ Every command takes --json. Writes take --idempotency-key <key>.
15
+ On a terminal, control characters in logs show as \\uXXXX; --raw prints them as written.`;
16
+ const usage = (message) => new RuntimeError({ code: "usage", status: 0, message, hint: LOGS_HELP });
17
+ function since(text, now) {
18
+ const match = /^(\d+)(s|m|h|d)$/.exec(text);
19
+ if (!match)
20
+ throw usage("Use --since with seconds, minutes, hours or days, such as 1h.");
21
+ const factors = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
22
+ const at = now - Number(match[1]) * factors[match[2]];
23
+ if (!Number.isSafeInteger(at) || at < 0)
24
+ throw usage("--since is outside the supported time range.");
25
+ return new Date(at).toISOString();
26
+ }
27
+ /** Same parsed argument and output ports as the existing CLI. No registration
28
+ * occurs here: the launch owner supplies a client only after launch. */
29
+ export async function logsProductCommand(product, argv, args, out, client, options = {}) {
30
+ const verb = argv[0];
31
+ const rest = args.positional.slice(1);
32
+ const flag = (name) => args.flags.get(name)?.at(-1);
33
+ const json = out.json || flag("json") === "true";
34
+ // On a terminal, what customers' programs wrote is shown escaped, JSON
35
+ // included (an escape is still valid JSON); --raw prints it as it is.
36
+ const safe = (text) => (out.tty && flag("raw") !== "true" ? terminalSafe(text) : text);
37
+ const lines = (text) => text.split("\n").map(safe).join("\n");
38
+ const print = (value, text) => out.write(json ? safe(JSON.stringify(value)) : lines(text ?? JSON.stringify(value, null, 2)));
39
+ const need = (value, name) => {
40
+ if (!value)
41
+ throw usage(`Give ${name}.`);
42
+ return value;
43
+ };
44
+ const body = () => {
45
+ try {
46
+ const value = JSON.parse(need(flag("body"), "--body with a JSON object"));
47
+ if (!value || typeof value !== "object" || Array.isArray(value))
48
+ throw usage("--body must be a JSON object.");
49
+ return value;
50
+ }
51
+ catch (error) {
52
+ if (error instanceof RuntimeError)
53
+ throw error;
54
+ throw usage("--body is not valid JSON.");
55
+ }
56
+ };
57
+ const request = {
58
+ ...(options.signal ? { signal: options.signal } : {}),
59
+ ...(flag("idempotency-key") ? { idempotencyKey: flag("idempotency-key") } : {}),
60
+ };
61
+ if (!verb || verb === "help" || verb === "--help") {
62
+ print({ usage: LOGS_HELP }, LOGS_HELP);
63
+ return 0;
64
+ }
65
+ const runtime = await client();
66
+ if ((product === "logs" || product === "traces") && verb === "views") {
67
+ const views = runtime[product].views;
68
+ const action = need(rest[0], "a views command");
69
+ if (action === "ls") {
70
+ const result = await views.list({}, request);
71
+ print({ data: result.data, nextCursor: result.nextCursor });
72
+ }
73
+ else if (action === "create")
74
+ print(await views.create(body(), request));
75
+ else {
76
+ const id = need(rest[1], "a view id");
77
+ if (action === "get")
78
+ print(await views.get(id, request));
79
+ else if (action === "update")
80
+ print(await views.update(id, body(), request));
81
+ else if (action === "delete") {
82
+ await views.delete(id, request);
83
+ print({ deleted: id });
84
+ }
85
+ else
86
+ throw usage(`Unknown views command: ${action}.`);
87
+ }
88
+ return 0;
89
+ }
90
+ if ((product === "logs" || product === "traces") && verb === "ingest") {
91
+ const input = body();
92
+ if (product === "logs") {
93
+ if (!Array.isArray(input.resourceLogs))
94
+ throw usage("OTLP logs need resourceLogs.");
95
+ const resourceLogs = input.resourceLogs;
96
+ print(await runtime.logs.ingest({ resourceLogs }, request));
97
+ }
98
+ else {
99
+ if (!Array.isArray(input.resourceSpans))
100
+ throw usage("OTLP spans need resourceSpans.");
101
+ const resourceSpans = input.resourceSpans;
102
+ print(await runtime.traces.ingest({ resourceSpans }, request));
103
+ }
104
+ return 0;
105
+ }
106
+ if (product === "logs") {
107
+ if (verb === "tail") {
108
+ const selector = need(rest[0], "a selector or app name");
109
+ let query = selector.trim().startsWith("{") ? selector : `{app=${JSON.stringify(selector)}}`;
110
+ if (flag("grep") !== undefined)
111
+ query += ` |= ${JSON.stringify(flag("grep"))}`;
112
+ for await (const event of runtime.logs.tail(query, {
113
+ ...request,
114
+ ...(flag("since") ? { start: since(flag("since"), options.now ?? Date.now()) } : {}),
115
+ })) {
116
+ if (json)
117
+ out.write(safe(JSON.stringify(event)));
118
+ else if (event.gap) {
119
+ const text = safe(`Logs expired between ${event.from} and ${event.to}; continuing from retained logs.`);
120
+ out.write(out.tty ? `\x1b[2m${text}\x1b[0m` : text);
121
+ }
122
+ else {
123
+ const time = safe(event.time);
124
+ const text = safe(typeof event.body === "string" ? event.body : JSON.stringify(event.body));
125
+ out.write(`${out.tty ? `\x1b[2m${time}\x1b[0m` : time} ${safe(event.severityText)} ${text}`);
126
+ }
127
+ await out.drain?.();
128
+ }
129
+ }
130
+ else if (verb === "query") {
131
+ const limit = flag("limit") === undefined ? undefined : Number(flag("limit"));
132
+ if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 1))
133
+ throw usage("--limit must be a positive integer.");
134
+ print(await runtime.logs.queryRange({
135
+ query: need(rest[0], "a LogQL query"),
136
+ ...(flag("from") ? { start: flag("from") } : {}),
137
+ ...(flag("to") ? { end: flag("to") } : {}),
138
+ ...(limit !== undefined ? { limit } : {}),
139
+ }, request));
140
+ }
141
+ else if (verb === "labels")
142
+ print(await runtime.logs.labels({}, request));
143
+ else if (verb === "values")
144
+ print(await runtime.logs.labelValues(need(rest[0], "a label name"), {}, request));
145
+ else
146
+ throw usage(`Unknown logs command: ${verb}.`);
147
+ }
148
+ else if (product === "traces") {
149
+ if (verb === "get")
150
+ print(await runtime.traces.get(need(rest[0], "a trace id"), request));
151
+ else if (verb === "search")
152
+ print(await runtime.traces.search(need(rest[0], "a TraceQL query"), request));
153
+ else if (verb === "tags")
154
+ print(await runtime.traces.tags(request));
155
+ else
156
+ throw usage(`Unknown traces command: ${verb}.`);
157
+ }
158
+ else {
159
+ if (verb === "ls") {
160
+ const result = await runtime.alerts.list({}, request);
161
+ print({ data: result.data, nextCursor: result.nextCursor });
162
+ }
163
+ else if (verb === "create")
164
+ print(await runtime.alerts.create(body(), request));
165
+ else {
166
+ const id = need(rest[0], "an alert id");
167
+ if (verb === "get")
168
+ print(await runtime.alerts.get(id, request));
169
+ else if (verb === "update")
170
+ print(await runtime.alerts.update(id, body(), request));
171
+ else if (verb === "delete") {
172
+ await runtime.alerts.delete(id, request);
173
+ print({ deleted: id });
174
+ }
175
+ else if (verb === "test")
176
+ print(await runtime.alerts.test(id, request));
177
+ else if (verb === "silence") {
178
+ const until = need(flag("until"), "--until with an RFC3339 time");
179
+ if (!/T.*(?:Z|[+-]\d\d:\d\d)$/.test(until) || !Number.isFinite(Date.parse(until)))
180
+ throw usage("--until must be an RFC3339 time with timezone.");
181
+ print(await runtime.alerts.silence(id, { until, ...(flag("reason") ? { reason: flag("reason") } : {}) }, request));
182
+ }
183
+ else
184
+ throw usage(`Unknown alerts command: ${verb}.`);
185
+ }
186
+ }
187
+ return 0;
188
+ }
@@ -0,0 +1 @@
1
+ export {};