withruntime 0.11.2 → 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 (164) hide show
  1. package/BLAXEL.md +2 -2
  2. package/COMPATIBILITY.md +74 -9
  3. package/DAYTONA.md +33 -5
  4. package/E2B.md +85 -24
  5. package/README.md +9 -8
  6. package/README.src.md +8 -7
  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 +29 -0
  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 +14 -5
  17. package/dist/blaxel/sandbox.js +63 -7
  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 +5 -1
  29. package/dist/cli.js +405 -204
  30. package/dist/client.d.ts +2 -4
  31. package/dist/client.js +27 -11
  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 +6 -0
  51. package/dist/daytona/daytona.js +30 -3
  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 +8 -2
  59. package/dist/daytona/sandbox.js +16 -2
  60. package/dist/domains/index.d.ts +9 -0
  61. package/dist/domains/index.js +10 -0
  62. package/dist/e2b/commands.d.ts +1 -1
  63. package/dist/e2b/commands.js +6 -7
  64. package/dist/e2b/errors.d.ts +17 -0
  65. package/dist/e2b/errors.js +40 -1
  66. package/dist/e2b/index.d.ts +2 -2
  67. package/dist/e2b/index.js +1 -1
  68. package/dist/e2b/sandbox.d.ts +27 -21
  69. package/dist/e2b/sandbox.js +34 -8
  70. package/dist/e2b/unsupported.js +1 -1
  71. package/dist/errors.d.ts +10 -1
  72. package/dist/errors.js +16 -4
  73. package/dist/freestyle/index.d.ts +285 -37
  74. package/dist/freestyle/index.js +656 -144
  75. package/dist/identity.d.ts +3 -0
  76. package/dist/identity.js +27 -0
  77. package/dist/images/index.d.ts +9 -0
  78. package/dist/images/index.js +10 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/ips/index.d.ts +9 -0
  81. package/dist/ips/index.js +10 -0
  82. package/dist/jobs/index.d.ts +9 -0
  83. package/dist/jobs/index.js +10 -0
  84. package/dist/keys.js +2 -0
  85. package/dist/login.js +2 -0
  86. package/dist/machine.d.ts +12 -3
  87. package/dist/machine.js +20 -3
  88. package/dist/mcp.js +1 -1
  89. package/dist/modal/index.d.ts +339 -61
  90. package/dist/modal/index.js +781 -121
  91. package/dist/network-cli.d.ts +3 -1
  92. package/dist/network-cli.js +78 -36
  93. package/dist/networks/index.d.ts +11 -0
  94. package/dist/networks/index.js +10 -0
  95. package/dist/ports/index.d.ts +9 -0
  96. package/dist/ports/index.js +10 -0
  97. package/dist/product-names.d.ts +52 -0
  98. package/dist/product-names.js +164 -0
  99. package/dist/product-renames.d.ts +103 -0
  100. package/dist/product-renames.js +189 -0
  101. package/dist/products/apps.d.ts +176 -0
  102. package/dist/products/apps.js +165 -0
  103. package/dist/products/billing.d.ts +41 -0
  104. package/dist/products/billing.js +13 -0
  105. package/dist/products/browser.d.ts +10 -0
  106. package/dist/products/browsers.d.ts +71 -0
  107. package/dist/products/browsers.js +102 -0
  108. package/dist/products/builds.d.ts +126 -0
  109. package/dist/products/builds.js +221 -0
  110. package/dist/products/index.d.ts +3 -16
  111. package/dist/products/index.js +3 -14
  112. package/dist/products/jobs.d.ts +17 -0
  113. package/dist/products/jobs.js +27 -10
  114. package/dist/products/logs.d.ts +245 -0
  115. package/dist/products/logs.js +274 -0
  116. package/dist/products/logs.test.d.ts +1 -0
  117. package/dist/products/logs.test.js +343 -0
  118. package/dist/products/network-products.d.ts +38 -7
  119. package/dist/products/network-products.js +31 -18
  120. package/dist/products/observability.d.ts +3 -2
  121. package/dist/products/private-network.js +1 -1
  122. package/dist/products/queues.d.ts +149 -0
  123. package/dist/products/queues.js +213 -0
  124. package/dist/products/referrals.d.ts +21 -7
  125. package/dist/products/referrals.js +3 -3
  126. package/dist/products/secrets.d.ts +7 -3
  127. package/dist/products/secrets.js +39 -18
  128. package/dist/products/signed-requests.d.ts +21 -0
  129. package/dist/products/signed-requests.js +11 -0
  130. package/dist/products/streams.d.ts +73 -0
  131. package/dist/products/streams.js +17 -0
  132. package/dist/products/switching.d.ts +35 -7
  133. package/dist/products/switching.js +4 -3
  134. package/dist/products/volumes.d.ts +3 -0
  135. package/dist/record-cli.d.ts +23 -0
  136. package/dist/record-cli.js +308 -0
  137. package/dist/renamed-products.d.ts +38 -0
  138. package/dist/renamed-products.js +34 -0
  139. package/dist/runloop/index.d.ts +9 -2
  140. package/dist/runloop/index.js +29 -8
  141. package/dist/runloop/sdk.d.ts +131 -0
  142. package/dist/runloop/sdk.js +167 -0
  143. package/dist/sandbox.d.ts +3 -2
  144. package/dist/sandbox.js +14 -9
  145. package/dist/sandboxes/index.d.ts +11 -0
  146. package/dist/sandboxes/index.js +12 -0
  147. package/dist/snapshots/index.d.ts +9 -0
  148. package/dist/snapshots/index.js +11 -0
  149. package/dist/snapshots.d.ts +7 -4
  150. package/dist/sprites/index.d.ts +343 -31
  151. package/dist/sprites/index.js +866 -84
  152. package/dist/ssh.d.ts +1 -1
  153. package/dist/ssh.js +11 -11
  154. package/dist/terminal.d.ts +24 -0
  155. package/dist/terminal.js +32 -0
  156. package/dist/transport.d.ts +15 -1
  157. package/dist/transport.js +56 -3
  158. package/dist/tunnel.js +1 -1
  159. package/dist/types.d.ts +63 -0
  160. package/dist/vercel/sandbox.js +5 -3
  161. package/dist/vercel/snapshot.js +10 -2
  162. package/dist/volumes/index.d.ts +9 -0
  163. package/dist/volumes/index.js +10 -0
  164. package/package.json +48 -2
@@ -24,6 +24,39 @@ export type Topup = {
24
24
  }[];
25
25
  }[];
26
26
  };
27
+ /** Auto top-up: when credit falls below `below`, the saved card is charged
28
+ * `amount` plus tax, within `monthlyLimit` a calendar month (UTC). Each
29
+ * automatic top-up adds 5% more as credit, and while it is on the account
30
+ * may run 200 sandboxes at once. Dollars are numbers in whole cents. */
31
+ export type AutoTopUp = {
32
+ /** Charging now. */
33
+ enabled: boolean;
34
+ /** Asked to turn on; turns on once a person saves a card at `saveCardAt`. */
35
+ waitingForCard: boolean;
36
+ below: number | null;
37
+ amount: number | null;
38
+ monthlyLimit: number | null;
39
+ /** Dollars added this month, a charge in flight included. */
40
+ thisMonth: number;
41
+ card: boolean;
42
+ /** Why it turned itself off; null while on. */
43
+ offReason: "declined" | "authentication_required" | "card_removed" | "no_address" | "blocked" | "failed" | null;
44
+ offAt: string | null;
45
+ /** Where a person saves the card, while none is saved. */
46
+ saveCardAt: string | null;
47
+ };
48
+ /** A figure left out keeps the one saved, or takes its default. */
49
+ export type AutoTopUpInput = {
50
+ enabled: boolean;
51
+ /** Charge when credit falls below this: $10 at least, and the default. */
52
+ below?: number;
53
+ /** Each time, before tax: $20 (the default) to $10,000, and at most $500
54
+ * in an account's first week. */
55
+ amount?: number;
56
+ /** The most a calendar month (UTC): $100 at least and at least `amount`;
57
+ * $500 by default. */
58
+ monthlyLimit?: number;
59
+ };
27
60
  export declare function billing(t: Transport): {
28
61
  /** Open a stablecoin top-up of `usd` dollars ($10 to $10,000). Pass the
29
62
  * same `idempotencyKey` when retrying to get the same top-up back. */
@@ -32,4 +65,12 @@ export declare function billing(t: Transport): {
32
65
  }, options?: RequestOptions) => Promise<Topup>;
33
66
  /** Read a top-up: whether its payment has arrived. */
34
67
  topupStatus: (purchaseId: string, options?: RequestOptions) => Promise<Topup>;
68
+ /** Auto top-up's settings. A person saves its card on the website. */
69
+ autoTopUp: {
70
+ get: (options?: RequestOptions) => Promise<AutoTopUp>;
71
+ /** Set the three figures, and turn it on (once a card is saved) or keep it off. */
72
+ set: (input: AutoTopUpInput, options?: RequestOptions) => Promise<AutoTopUp>;
73
+ /** Turn it off. The figures and the card are kept; running sandboxes are never stopped. */
74
+ off: (options?: RequestOptions) => Promise<AutoTopUp>;
75
+ };
35
76
  };
@@ -9,5 +9,18 @@ export function billing(t) {
9
9
  path: `/v1/billing/topups/${encodeURIComponent(purchaseId)}`,
10
10
  ...options,
11
11
  }),
12
+ /** Auto top-up's settings. A person saves its card on the website. */
13
+ autoTopUp: {
14
+ get: (options) => t.json({ method: "GET", path: "/v1/billing/auto-top-up", ...options }),
15
+ /** Set the three figures, and turn it on (once a card is saved) or keep it off. */
16
+ set: (input, options) => t.json({
17
+ method: "PUT",
18
+ path: "/v1/billing/auto-top-up",
19
+ body: input,
20
+ ...options,
21
+ }),
22
+ /** Turn it off. The figures and the card are kept; running sandboxes are never stopped. */
23
+ off: (options) => t.json({ method: "DELETE", path: "/v1/billing/auto-top-up", ...options }),
24
+ },
12
25
  };
13
26
  }
@@ -18,6 +18,11 @@ export type SandboxBrowser = {
18
18
  expiresAt: string | null;
19
19
  /** headless: false only: the desktop's live view. */
20
20
  streamUrl?: string;
21
+ /** start() only: the sandbox's memory, which is the browser's. */
22
+ memoryMiB?: number;
23
+ /** start() only, under 2 GiB: heavy apps such as Google Sheets, YouTube
24
+ * and StackBlitz need a 2 GiB sandbox; 1 GiB suits light pages. */
25
+ hint?: string;
21
26
  };
22
27
  export type BrowserStartOptions = {
23
28
  /** true (the default): no window. false: on the sandbox's desktop, with a live view. */
@@ -52,6 +57,11 @@ export declare function sandboxBrowser(t: Transport, sandbox: Sandbox): {
52
57
  expiresAt: string | null;
53
58
  /** headless: false only: the desktop's live view. */
54
59
  streamUrl?: string;
60
+ /** start() only: the sandbox's memory, which is the browser's. */
61
+ memoryMiB?: number;
62
+ /** start() only, under 2 GiB: heavy apps such as Google Sheets, YouTube
63
+ * and StackBlitz need a 2 GiB sandbox; 1 GiB suits light pages. */
64
+ hint?: string;
55
65
  }>;
56
66
  /** Whether it runs, and a fresh cdpUrl when it does. */
57
67
  get: (options?: RequestOptions) => Promise<SandboxBrowser>;
@@ -0,0 +1,71 @@
1
+ import { Page } from "../page.js";
2
+ import type { RequestOptions, Transport } from "../transport.js";
3
+ export type CreateBrowser = {
4
+ /** 1024–16384 MiB; the server defaults to 2048. */
5
+ memoryMiB?: number;
6
+ /** The server defaults to 1 vCPU. */
7
+ vcpu?: number;
8
+ /** Defaults to true; false requests a visible browser. */
9
+ headless?: boolean;
10
+ width?: number;
11
+ height?: number;
12
+ timeoutSeconds?: number;
13
+ idlePauseSeconds?: number;
14
+ name?: string;
15
+ labels?: Record<string, string>;
16
+ };
17
+ export type UpdateBrowser = {
18
+ name?: string | null;
19
+ labels?: Record<string, string>;
20
+ };
21
+ export type ListBrowsers = {
22
+ limit?: number;
23
+ cursor?: string;
24
+ };
25
+ /** A browser resource and its current connection credentials. */
26
+ export type BrowserInfo = {
27
+ id: string;
28
+ name: string | null;
29
+ labels: Record<string, string>;
30
+ state: string;
31
+ sandboxId: string;
32
+ memoryMiB: number;
33
+ vcpu: number;
34
+ headless: boolean;
35
+ version: string;
36
+ cdpUrl: string;
37
+ httpUrl: string;
38
+ headers: Record<string, string>;
39
+ expiresAt: string | null;
40
+ liveViewUrl?: string;
41
+ hint?: string;
42
+ };
43
+ export type BrowserConnectOptions = {
44
+ playwright: [endpointURL: string, options: {
45
+ headers: Record<string, string>;
46
+ }];
47
+ puppeteer: {
48
+ browserWSEndpoint: string;
49
+ headers: Record<string, string>;
50
+ };
51
+ };
52
+ export type Browser = BrowserInfo & {
53
+ /** End this browser; concurrent calls share one request, and failures can be retried. */
54
+ delete(options?: RequestOptions): Promise<void>;
55
+ /** Get Playwright's argument tuple and Puppeteer's options, including authentication headers. */
56
+ connectOptions(): BrowserConnectOptions;
57
+ /** End this browser on scope exit; an already missing browser counts as cleaned up. */
58
+ [Symbol.asyncDispose](): Promise<void>;
59
+ };
60
+ /** Browsers follow the shared resource interface: id, name, labels, state. */
61
+ export declare function browsers(t: Transport): {
62
+ /** Create a browser ready to connect; omitted fields use the server's defaults. */
63
+ create(input?: CreateBrowser, options?: RequestOptions): Promise<Browser>;
64
+ /** Fetch a browser by id with fresh CDP connection credentials. */
65
+ get(id: string, options?: RequestOptions): Promise<Browser>;
66
+ list: (query?: ListBrowsers, options?: RequestOptions) => Promise<Page<Browser>>;
67
+ /** Change a browser's name or labels without changing its compute settings. */
68
+ update(id: string, input: UpdateBrowser, options?: RequestOptions): Promise<Browser>;
69
+ /** End a browser by id; pass RequestOptions to bound or deduplicate the request. */
70
+ delete: (id: string, options?: RequestOptions) => Promise<void>;
71
+ };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Managed Chromium browsers with authenticated Chrome DevTools Protocol (CDP) connections.
3
+ *
4
+ * await using browser = await runtime.browsers.create();
5
+ * const playwrightBrowser = await chromium.connectOverCDP(...browser.connectOptions().playwright);
6
+ * const puppeteerBrowser = await puppeteer.connect(browser.connectOptions().puppeteer);
7
+ *
8
+ * Use `get(browser.id)` for fresh connection credentials after `expiresAt`.
9
+ */
10
+ import { RuntimeError } from "../errors.js";
11
+ import { Page } from "../page.js";
12
+ /** Browsers follow the shared resource interface: id, name, labels, state. */
13
+ export function browsers(t) {
14
+ const path = (id) => `/v1/browsers/${encodeURIComponent(id)}`;
15
+ /** End a browser by id; API errors retain their code and recovery hint. */
16
+ const remove = async (id, options) => {
17
+ await t.json({ method: "DELETE", path: path(id), ...options });
18
+ };
19
+ const wrap = (info) => {
20
+ let deleted = false;
21
+ let deleting;
22
+ const browser = {
23
+ ...info,
24
+ delete(options) {
25
+ if (deleted)
26
+ return Promise.resolve();
27
+ if (!deleting) {
28
+ deleting = remove(info.id, options)
29
+ .then(() => {
30
+ deleted = true;
31
+ })
32
+ .catch((error) => {
33
+ if (error instanceof RuntimeError && error.status === 404)
34
+ deleted = true;
35
+ throw error;
36
+ })
37
+ .finally(() => {
38
+ deleting = undefined;
39
+ });
40
+ }
41
+ return deleting;
42
+ },
43
+ connectOptions() {
44
+ return {
45
+ playwright: [browser.cdpUrl, { headers: { ...browser.headers } }],
46
+ puppeteer: {
47
+ browserWSEndpoint: browser.cdpUrl,
48
+ headers: { ...browser.headers },
49
+ },
50
+ };
51
+ },
52
+ async [Symbol.asyncDispose]() {
53
+ // Creation's signal/key belong to creation: an aborted task must still clean up.
54
+ try {
55
+ await browser.delete();
56
+ }
57
+ catch (error) {
58
+ if (!(error instanceof RuntimeError && error.status === 404))
59
+ throw error;
60
+ }
61
+ },
62
+ };
63
+ return browser;
64
+ };
65
+ /** Fetch one page; `for await (const browser of await list())` follows every cursor. */
66
+ const list = async (query = {}, options) => {
67
+ const page = await t.json({
68
+ method: "GET",
69
+ path: "/v1/browsers",
70
+ query,
71
+ ...options,
72
+ });
73
+ return new Page(page.data.map(wrap), page.nextCursor, (cursor) => list({ ...query, cursor }, options));
74
+ };
75
+ return {
76
+ /** Create a browser ready to connect; omitted fields use the server's defaults. */
77
+ async create(input = {}, options) {
78
+ return wrap(await t.json({
79
+ method: "POST",
80
+ path: "/v1/browsers",
81
+ body: input,
82
+ ...options,
83
+ }));
84
+ },
85
+ /** Fetch a browser by id with fresh CDP connection credentials. */
86
+ async get(id, options) {
87
+ return wrap(await t.json({ method: "GET", path: path(id), ...options }));
88
+ },
89
+ list,
90
+ /** Change a browser's name or labels without changing its compute settings. */
91
+ async update(id, input, options) {
92
+ return wrap(await t.json({
93
+ method: "POST",
94
+ path: `${path(id)}:update`,
95
+ body: input,
96
+ ...options,
97
+ }));
98
+ },
99
+ /** End a browser by id; pass RequestOptions to bound or deduplicate the request. */
100
+ delete: remove,
101
+ };
102
+ }
@@ -0,0 +1,126 @@
1
+ import { Page } from "../page.js";
2
+ import { type RequestOptions, type Transport } from "../transport.js";
3
+ export type BuildState = "queued" | "fetching" | "building" | "deploying" | "succeeded" | "failed" | "cancelled" | "superseded";
4
+ type Resource = {
5
+ id: string;
6
+ name: string | null;
7
+ labels: Record<string, string>;
8
+ createdAt: number;
9
+ updatedAt: number;
10
+ deletedAt: number | null;
11
+ error: {
12
+ code: string;
13
+ message: string;
14
+ } | null;
15
+ };
16
+ export type Build = Resource & {
17
+ repositoryId: string;
18
+ ref: string;
19
+ commit: string | null;
20
+ state: BuildState;
21
+ reason: string | null;
22
+ };
23
+ export type CreateBuild = {
24
+ repositoryId: string;
25
+ ref: string;
26
+ name?: string;
27
+ labels?: Record<string, string>;
28
+ };
29
+ export type ConnectRepository = {
30
+ provider: "github" | "git";
31
+ repository: string;
32
+ deploy: `app:${string}` | `function:${string}` | `container:${string}`;
33
+ productionBranch?: string;
34
+ previews?: boolean;
35
+ /** The folder the build runs in; the repository's root by default. */
36
+ rootDirectory?: string;
37
+ /** Leave out to choose by the lockfile (bun, pnpm, Yarn or npm). */
38
+ installCommand?: string | null;
39
+ buildCommand?: string | null;
40
+ name?: string;
41
+ labels?: Record<string, string>;
42
+ };
43
+ export type Repository = Resource & {
44
+ provider: "github" | "git";
45
+ repository: string;
46
+ deploy: ConnectRepository["deploy"];
47
+ productionBranch: string;
48
+ previews: boolean;
49
+ rootDirectory: string;
50
+ installCommand: string | null;
51
+ buildCommand: string | null;
52
+ state: "connected" | "disconnected";
53
+ };
54
+ export type ListBuilds = {
55
+ limit?: number;
56
+ cursor?: string;
57
+ name?: string;
58
+ label?: string[];
59
+ state?: BuildState[];
60
+ };
61
+ export type ListRepositories = Omit<ListBuilds, "state"> & {
62
+ state?: Repository["state"][];
63
+ };
64
+ export type BuildLine = {
65
+ cursor: string | null;
66
+ at: number;
67
+ step: string | null;
68
+ text: string;
69
+ stream: "stdout" | "stderr";
70
+ };
71
+ export type FailingStep = {
72
+ name: string;
73
+ exitCode: number | null;
74
+ lastLines: string[];
75
+ };
76
+ export type BuildLog = {
77
+ lines: BuildLine[];
78
+ cursor: string | null;
79
+ state: BuildState;
80
+ reason: string | null;
81
+ failingStep: FailingStep | null;
82
+ hasMore: boolean;
83
+ nextCursor: string | null;
84
+ };
85
+ export type BuildLogEvent = ({
86
+ type: "line";
87
+ } & BuildLine & {
88
+ cursor: string;
89
+ }) | {
90
+ type: "gap";
91
+ cursor: string | null;
92
+ earliestCursor: string;
93
+ } | {
94
+ type: "terminal";
95
+ cursor: string | null;
96
+ state: "succeeded" | "failed" | "cancelled" | "superseded";
97
+ reason: string;
98
+ failingStep: FailingStep | null;
99
+ };
100
+ /** cursor resumes a follow; pageCursor pages older search results. */
101
+ export type LogOptions = {
102
+ follow?: boolean;
103
+ cursor?: string;
104
+ pageCursor?: string;
105
+ };
106
+ export declare function builds(t: Transport): {
107
+ create: (input: CreateBuild, options?: RequestOptions) => Promise<Build>;
108
+ get: (id: string, options?: RequestOptions) => Promise<Build>;
109
+ list: (query?: ListBuilds, options?: RequestOptions) => Promise<Page<Build>>;
110
+ logs: {
111
+ (id: string, query: LogOptions & {
112
+ follow: true;
113
+ }, options?: RequestOptions): AsyncGenerator<BuildLogEvent>;
114
+ (id: string, query?: LogOptions & {
115
+ follow?: false;
116
+ }, options?: RequestOptions): Promise<BuildLog>;
117
+ (id: string, query: LogOptions, options?: RequestOptions): Promise<BuildLog> | AsyncGenerator<BuildLogEvent>;
118
+ };
119
+ cancel: (id: string, options?: RequestOptions) => Promise<Build>;
120
+ repositories: {
121
+ connect: (input: ConnectRepository, options?: RequestOptions) => Promise<Repository>;
122
+ list: (query?: ListRepositories, options?: RequestOptions) => Promise<Page<Repository>>;
123
+ disconnect: (id: string, options?: RequestOptions) => Promise<Repository>;
124
+ };
125
+ };
126
+ export {};
@@ -0,0 +1,221 @@
1
+ /** Builds uses the shared request keys and paged lists.
2
+ * Unwired until the build planner's production adapter is ready. */
3
+ import { ConnectionError, RuntimeError } from "../errors.js";
4
+ import { Page } from "../page.js";
5
+ import { timeoutSignal } from "../transport.js";
6
+ const terminal = new Set(["succeeded", "failed", "cancelled", "superseded"]);
7
+ function validFailure(value) {
8
+ if (value === null)
9
+ return true;
10
+ if (!value || typeof value !== "object")
11
+ return false;
12
+ const step = value;
13
+ return (typeof step.name === "string" &&
14
+ (step.exitCode === null || Number.isSafeInteger(step.exitCode)) &&
15
+ Array.isArray(step.lastLines) &&
16
+ step.lastLines.length <= 20 &&
17
+ step.lastLines.every((line) => typeof line === "string"));
18
+ }
19
+ function failure(code, hint, cursor) {
20
+ return new RuntimeError({ code, status: 0, message: hint, hint, details: { cursor } });
21
+ }
22
+ function validCursor(value) {
23
+ return (typeof value === "string" &&
24
+ value.length > 0 &&
25
+ value.length <= 4096 &&
26
+ ![...value].some((c) => c.charCodeAt(0) < 0x20 || c.charCodeAt(0) === 0x7f));
27
+ }
28
+ /** Why a wait ended: the signal's own reason, always as an Error. */
29
+ const abortReason = (signal) => signal?.reason instanceof Error ? signal.reason : new Error(String(signal?.reason ?? "aborted"));
30
+ async function* boundedEvents(source, signal) {
31
+ try {
32
+ for (;;) {
33
+ signal?.throwIfAborted();
34
+ let abort = () => { };
35
+ let next;
36
+ try {
37
+ next = await Promise.race([
38
+ source.next(),
39
+ new Promise((_, reject) => {
40
+ abort = () => reject(abortReason(signal));
41
+ signal?.addEventListener("abort", abort, { once: true });
42
+ if (signal?.aborted)
43
+ abort();
44
+ }),
45
+ ]);
46
+ }
47
+ finally {
48
+ signal?.removeEventListener("abort", abort);
49
+ }
50
+ if (next.done)
51
+ return;
52
+ yield next.value;
53
+ }
54
+ }
55
+ finally {
56
+ // An injected transport can ignore abort while next() is pending. It must
57
+ // not prevent the caller's deadline from resolving; request its cleanup.
58
+ const closed = source.return(undefined);
59
+ if (signal?.aborted)
60
+ void closed.catch(() => { });
61
+ else
62
+ await closed;
63
+ }
64
+ }
65
+ export function builds(t) {
66
+ const path = (id) => `/v1/builds/${encodeURIComponent(id)}`;
67
+ const list = async (query = {}, options) => {
68
+ const page = await t.json({
69
+ method: "GET",
70
+ path: "/v1/builds",
71
+ query,
72
+ ...options,
73
+ });
74
+ return new Page(page.data, page.nextCursor, (cursor) => list({ ...query, cursor }, options));
75
+ };
76
+ const listRepositories = async (query = {}, options) => {
77
+ const page = await t.json({
78
+ method: "GET",
79
+ path: "/v1/repositories",
80
+ query,
81
+ ...options,
82
+ });
83
+ return new Page(page.data, page.nextCursor, (cursor) => listRepositories({ ...query, cursor }, options));
84
+ };
85
+ async function* follow(id, query, options = {}) {
86
+ let cursor = query.cursor ?? null;
87
+ if ((cursor !== null && !validCursor(cursor)) || query.pageCursor !== undefined)
88
+ throw failure("invalid_log_options", "Use an opaque cursor for follow; pageCursor is only for search pages.", cursor);
89
+ const seen = new Set(cursor === null ? [] : [cursor]);
90
+ let failures = 0;
91
+ // Bound the entire follow when the caller sets a deadline, including
92
+ // reconnections. Transport's per-request deadline alone resets on retry.
93
+ const signal = (options.timeoutMs ?? 120_000) > 0
94
+ ? AbortSignal.any([
95
+ ...(options.signal ? [options.signal] : []),
96
+ timeoutSignal(options.timeoutMs ?? 120_000),
97
+ ])
98
+ : options.signal;
99
+ for (;;) {
100
+ signal?.throwIfAborted();
101
+ let continued = false;
102
+ try {
103
+ for await (const event of boundedEvents(t.events({
104
+ method: "GET",
105
+ path: `${path(id)}/logs`,
106
+ query: { follow: true, ...(cursor === null ? {} : { cursor }) },
107
+ ...options,
108
+ ...(signal ? { signal } : {}),
109
+ }), signal)) {
110
+ signal?.throwIfAborted();
111
+ if (!event || typeof event !== "object")
112
+ throw failure("build_log_invalid", "Retry the log from the last received cursor.", cursor);
113
+ if (event.cursor !== null && !validCursor(event.cursor))
114
+ throw failure("build_log_invalid", "Retry from the last received cursor.", cursor);
115
+ if (event.type === "line") {
116
+ if (typeof event.text !== "string" ||
117
+ !["stdout", "stderr"].includes(event.stream) ||
118
+ !Number.isSafeInteger(event.at) ||
119
+ event.at < 0 ||
120
+ (event.step !== null && typeof event.step !== "string") ||
121
+ !validCursor(event.cursor))
122
+ throw failure("build_log_invalid", "Retry the log from the last received cursor.", cursor);
123
+ // Opaque physical positions cannot be ordered or incremented.
124
+ if (seen.has(event.cursor))
125
+ continue;
126
+ seen.add(event.cursor);
127
+ if (seen.size > 10_000)
128
+ seen.delete(seen.values().next().value);
129
+ cursor = event.cursor;
130
+ failures = 0;
131
+ yield event;
132
+ }
133
+ else if (event.type === "gap") {
134
+ if (!validCursor(event.earliestCursor) || (cursor !== null && event.cursor !== cursor))
135
+ throw failure("build_log_invalid", "Retry from the last received cursor.", cursor);
136
+ cursor = event.cursor;
137
+ yield event;
138
+ }
139
+ else if (event.type === "terminal") {
140
+ if ((cursor !== null && event.cursor !== cursor) ||
141
+ !terminal.has(event.state) ||
142
+ typeof event.reason !== "string" ||
143
+ !event.reason ||
144
+ !validFailure(event.failingStep) ||
145
+ (event.state !== "failed" && event.failingStep !== null))
146
+ throw failure("build_log_invalid", "Retry the log from the last received cursor.", cursor);
147
+ yield event;
148
+ return;
149
+ }
150
+ else if (event.type === "continue") {
151
+ if (cursor !== null && event.cursor !== cursor)
152
+ throw failure("build_log_gap", "Resume from details.cursor; report a persistent missing log line.", cursor);
153
+ cursor = event.cursor;
154
+ continued = true;
155
+ break;
156
+ }
157
+ else if (event.type === "error") {
158
+ if (event.cursor !== cursor ||
159
+ typeof event.code !== "string" ||
160
+ !event.code ||
161
+ typeof event.hint !== "string" ||
162
+ !event.hint)
163
+ throw failure("build_log_invalid", "Retry the log from the last received cursor.", cursor);
164
+ throw failure(event.code, event.hint, cursor);
165
+ }
166
+ else
167
+ throw failure("build_log_invalid", "Retry the log from the last received cursor.", cursor);
168
+ }
169
+ }
170
+ catch (error) {
171
+ signal?.throwIfAborted();
172
+ if (error instanceof SyntaxError)
173
+ throw failure("build_log_invalid", "The log contained invalid JSON; retry from details.cursor.", cursor);
174
+ // HTTP refusals and protocol errors are actionable, not connection
175
+ // loss. The transport itself retries retryable HTTP responses.
176
+ if (!(error instanceof TypeError) && !(error instanceof ConnectionError))
177
+ throw error;
178
+ }
179
+ if (!continued && ++failures > 3)
180
+ throw failure("build_log_interrupted", "Reconnect with logs(id, { follow: true, cursor: details.cursor }).", cursor);
181
+ // Prevent a disconnected service or repeated continuations busy-looping.
182
+ await new Promise((resolve, reject) => {
183
+ const done = () => {
184
+ signal?.removeEventListener("abort", abort);
185
+ resolve();
186
+ };
187
+ const timer = setTimeout(done, 50 * Math.max(1, failures));
188
+ const abort = () => {
189
+ clearTimeout(timer);
190
+ signal?.removeEventListener("abort", abort);
191
+ reject(abortReason(signal));
192
+ };
193
+ if (signal?.aborted)
194
+ abort();
195
+ else
196
+ signal?.addEventListener("abort", abort, { once: true });
197
+ });
198
+ }
199
+ }
200
+ function logs(id, query = {}, options) {
201
+ return query.follow
202
+ ? follow(id, query, options)
203
+ : t.json({ method: "GET", path: `${path(id)}/logs`, query, ...options });
204
+ }
205
+ return {
206
+ create: (input, options) => t.json({ method: "POST", path: "/v1/builds", body: input, ...options }),
207
+ get: (id, options) => t.json({ method: "GET", path: path(id), ...options }),
208
+ list,
209
+ logs,
210
+ cancel: (id, options) => t.json({ method: "POST", path: `${path(id)}:cancel`, ...options }),
211
+ repositories: {
212
+ connect: (input, options) => t.json({ method: "POST", path: "/v1/repositories", body: input, ...options }),
213
+ list: listRepositories,
214
+ disconnect: (id, options) => t.json({
215
+ method: "DELETE",
216
+ path: `/v1/repositories/${encodeURIComponent(id)}`,
217
+ ...options,
218
+ }),
219
+ },
220
+ };
221
+ }
@@ -5,7 +5,7 @@ import { sandboxDesktop } from "./desktop.js";
5
5
  import { sandboxBrowser } from "./browser.js";
6
6
  import { sandboxInterpreter } from "./interpreter.js";
7
7
  import { sandboxNetwork } from "./network.js";
8
- import { addresses, domains, network, ports, tunnel } from "./network-products.js";
8
+ import { domains, ips, networks, ports } from "./network-products.js";
9
9
  import { sandboxPreviews } from "./previews.js";
10
10
  import { referrals } from "./referrals.js";
11
11
  import { billing } from "./billing.js";
@@ -37,19 +37,8 @@ export declare const clientExtensions: {
37
37
  sso: typeof sso;
38
38
  domains: typeof domains;
39
39
  ports: typeof ports;
40
- addresses: typeof addresses;
41
- tunnel: typeof tunnel;
42
- network: typeof network;
43
- };
44
- /** Canonical CLI product names share the existing factory instance. Account
45
- * commands such as secrets, events and billing retain their current names. */
46
- export declare const clientProductAliases: {
47
- readonly image: "images";
48
- readonly volume: "volumes";
49
- readonly job: "jobs";
50
- readonly domain: "domains";
51
- readonly port: "ports";
52
- readonly address: "addresses";
40
+ ips: typeof ips;
41
+ networks: typeof networks;
53
42
  };
54
43
  export declare const sandboxExtensions: {
55
44
  interpreter: typeof sandboxInterpreter;
@@ -64,8 +53,6 @@ export declare const sandboxExtensions: {
64
53
  };
65
54
  export type ClientExtensions = {
66
55
  readonly [K in keyof typeof clientExtensions]: ReturnType<(typeof clientExtensions)[K]>;
67
- } & {
68
- readonly [K in keyof typeof clientProductAliases]: ReturnType<(typeof clientExtensions)[(typeof clientProductAliases)[K]]>;
69
56
  };
70
57
  export type SandboxExtensions = {
71
58
  readonly [K in keyof typeof sandboxExtensions]: ReturnType<(typeof sandboxExtensions)[K]>;