withruntime 0.17.2 → 0.21.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 (52) hide show
  1. package/COMPATIBILITY.md +32 -1
  2. package/README.md +19 -0
  3. package/README.src.md +19 -0
  4. package/dist/agents/index.d.ts +11 -0
  5. package/dist/agents/index.js +12 -0
  6. package/dist/agents-cli.d.ts +10 -0
  7. package/dist/agents-cli.js +362 -0
  8. package/dist/apps-cli.d.ts +14 -0
  9. package/dist/apps-cli.js +737 -0
  10. package/dist/builds/index.d.ts +10 -0
  11. package/dist/builds/index.js +11 -0
  12. package/dist/cli-words.js +11 -0
  13. package/dist/cli.d.ts +4 -0
  14. package/dist/cli.js +104 -6
  15. package/dist/client.d.ts +19 -0
  16. package/dist/client.js +20 -0
  17. package/dist/cloudflare/errors.d.ts +23 -0
  18. package/dist/cloudflare/errors.js +16 -0
  19. package/dist/cloudflare/index.d.ts +82 -9
  20. package/dist/cloudflare/index.js +162 -38
  21. package/dist/cloudflare/interpreter.js +14 -5
  22. package/dist/cloudflare/mime.d.ts +1 -0
  23. package/dist/cloudflare/mime.js +66 -0
  24. package/dist/cloudflare/streams.d.ts +8 -0
  25. package/dist/cloudflare/streams.js +30 -0
  26. package/dist/cloudflare/types.d.ts +338 -0
  27. package/dist/cloudflare/types.js +80 -0
  28. package/dist/cloudflare/worker.d.ts +297 -10
  29. package/dist/cloudflare/worker.js +203 -17
  30. package/dist/containers/index.d.ts +10 -0
  31. package/dist/containers/index.js +11 -0
  32. package/dist/daytona/sandbox.d.ts +2 -1
  33. package/dist/daytona/sandbox.js +2 -1
  34. package/dist/identity.js +18 -2
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.js +1 -0
  37. package/dist/product-names.js +3 -0
  38. package/dist/products/agents.d.ts +272 -0
  39. package/dist/products/agents.js +251 -0
  40. package/dist/products/builds.d.ts +11 -1
  41. package/dist/products/builds.js +7 -0
  42. package/dist/products/containers.d.ts +71 -0
  43. package/dist/products/containers.js +6 -0
  44. package/dist/products/index.d.ts +6 -0
  45. package/dist/products/index.js +7 -0
  46. package/dist/sandbox.d.ts +4 -0
  47. package/dist/transport.d.ts +2 -2
  48. package/dist/transport.js +1 -1
  49. package/dist/tunnel.js +1 -1
  50. package/dist/types.d.ts +11 -0
  51. package/dist/vercel/errors.d.ts +1 -1
  52. package/package.json +16 -1
@@ -1,12 +1,47 @@
1
- /** Workers conditional entry. The Durable Object coordinates requests and stores
2
- * configuration; Runtime owns compute. Existing Container/Wrangler declarations
3
- * still need removal during infrastructure migration. */
4
- import { DurableObject } from "cloudflare:workers";
5
- import { only, CompatibilityError } from "../compat/core.js";
1
+ /** Workers conditional entry: what a Worker imports as `withruntime/cloudflare`.
2
+ * `Sandbox` is the Durable Object class the Worker exports and binds, as with
3
+ * @cloudflare/sandbox; it keeps the sandbox's name and preview tokens, and
4
+ * Runtime runs the sandbox. The Wrangler config drops its `containers` block. */
5
+ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
6
+ import { only, refusal, CompatibilityError } from "../compat/core.js";
6
7
  import { Runtime } from "../client.js";
7
8
  import { workerSandboxIdentity } from "./identity.js";
8
- import { Sandbox as RuntimeSandbox, sanitizeSandboxId, validateSandboxConfiguration, } from "./index.js";
9
+ import { Sandbox as RuntimeSandbox, SandboxSecurityError, sanitizeSandboxId, validateSandboxConfiguration, } from "./index.js";
9
10
  export * from "./index.js";
11
+ /** Cloudflare's egress entrypoint for outbound interception, which Runtime
12
+ * does with network rules and secrets instead. */
13
+ export class ContainerProxy extends WorkerEntrypoint {
14
+ fetch() {
15
+ return Promise.reject(refusal("Cloudflare", "ContainerProxy", OUTBOUND));
16
+ }
17
+ }
18
+ const OUTBOUND = "set the sandbox's network rules with Runtime's network API, and store keys as Runtime secrets, which Runtime's proxy adds to the requests you name";
19
+ const TERMINAL = "open a terminal with the native Runtime sandbox's terminal()";
20
+ /** Members of Cloudflare's Durable Object a Runtime sandbox has no counterpart
21
+ * for, refused with what to use instead. */
22
+ const REFUSED = {
23
+ callTunnels: "use exposePort for a public URL",
24
+ setContainerTimeouts: "a Runtime sandbox has no container start to tune",
25
+ setTransport: "Runtime's API is the only transport",
26
+ containerFetch: "fetch the URL exposePort returns",
27
+ getContainerPlacementId: "a Runtime sandbox's id is its place",
28
+ createBackup: "snapshot the sandbox with Runtime's snapshots, or keep the folder on a volume",
29
+ restoreBackup: "create a sandbox from a Runtime snapshot",
30
+ terminal: TERMINAL,
31
+ };
32
+ /** Hooks and outbound settings a subclass of Cloudflare's class may set,
33
+ * which would otherwise be silently ignored. */
34
+ const SUBCLASS = {
35
+ onStart: "a Runtime sandbox has no Durable Object lifecycle hooks",
36
+ onStop: "a Runtime sandbox has no Durable Object lifecycle hooks",
37
+ onError: "a Runtime sandbox has no Durable Object lifecycle hooks",
38
+ onActivityExpired: "set sleepAfter or keepAlive instead",
39
+ interceptHttps: OUTBOUND,
40
+ enableInternet: OUTBOUND,
41
+ allowedHosts: OUTBOUND,
42
+ deniedHosts: OUTBOUND,
43
+ };
44
+ const STATIC_OUTBOUND = ["outboundByHost", "outbound", "outboundHandlers"];
10
45
  const CONFIGURATION_KEY = "runtime.configuration";
11
46
  const CONFIGURATION_INTENT_KEY = "runtime.configuration.pending";
12
47
  /** Preserve the original failure even when durable recovery also fails. */
@@ -58,20 +93,46 @@ function sessionTarget(session) {
58
93
  exec: (command, options) => session.exec(command, options),
59
94
  execStream: (command, options) => session.execStream(command, options),
60
95
  startProcess: async (command, options) => processTarget(await session.startProcess(command, options)),
96
+ listProcesses: async () => (await session.listProcesses()).map((p) => processTarget(p)),
97
+ getProcess: async (id) => {
98
+ const p = await session.getProcess(id);
99
+ return p ? processTarget(p) : null;
100
+ },
101
+ killProcess: (id, signal) => session.killProcess(id, signal),
102
+ killAllProcesses: () => session.killAllProcesses(),
103
+ cleanupCompletedProcesses: () => session.cleanupCompletedProcesses(),
104
+ getProcessLogs: (id) => session.getProcessLogs(id),
105
+ streamProcessLogs: (id) => session.streamProcessLogs(id),
61
106
  writeFile: (...args) => session.writeFile(...args),
62
107
  readFile,
108
+ readFileStream: (path) => session.readFileStream(path),
63
109
  mkdir: (...args) => session.mkdir(...args),
64
110
  deleteFile: (path) => session.deleteFile(path),
65
111
  renameFile: (path, to) => session.renameFile(path, to),
66
112
  moveFile: (path, to) => session.renameFile(path, to),
67
113
  exists: (path) => session.exists(path),
68
114
  listFiles: (...args) => session.listFiles(...args),
115
+ gitCheckout: (repoUrl, options) => session.gitCheckout(repoUrl, options),
116
+ watch: (path, options) => session.watch(path, options),
117
+ checkChanges: (path, options) => session.checkChanges(path, options),
69
118
  setEnvVars: (...args) => session.setEnvVars(...args),
119
+ createCodeContext: (options) => session.createCodeContext(options),
120
+ runCode: (code, options) => session.runCode(code, options),
121
+ runCodeStream: (code, options) => session.runCodeStream(code, options),
122
+ listCodeContexts: () => session.listCodeContexts(),
123
+ deleteCodeContext: (id) => session.deleteCodeContext(id),
124
+ mountBucket: (bucket, path, options) => session.mountBucket(bucket, path, options),
125
+ unmountBucket: (path) => session.unmountBucket(path),
126
+ terminal: () => Promise.reject(refusal("Cloudflare", "ExecutionSession.terminal", TERMINAL)),
70
127
  };
71
128
  }
129
+ /** A Process as plain data and callable members, as Cloudflare's RPC returns
130
+ * one. `pid` is absent: Runtime does not report it (Cloudflare's type has it
131
+ * optional). */
72
132
  function processTarget(process) {
73
133
  return {
74
134
  id: process.id,
135
+ pid: undefined,
75
136
  command: process.command,
76
137
  status: process.status,
77
138
  startTime: process.startTime,
@@ -83,6 +144,7 @@ function processTarget(process) {
83
144
  getLogs: () => process.getLogs(),
84
145
  waitForExit: (timeout) => process.waitForExit(timeout),
85
146
  waitForLog: (pattern, timeout) => process.waitForLog(pattern, timeout),
147
+ waitForPort: (port, options) => process.waitForPort(port, options),
86
148
  };
87
149
  }
88
150
  export class Sandbox extends DurableObject {
@@ -126,6 +188,7 @@ export class Sandbox extends DurableObject {
126
188
  throw new Error("Call getSandbox(namespace, id) before using the Durable Object");
127
189
  if (!this.env.RUNTIME_API_KEY?.startsWith("rtcloud_"))
128
190
  throw new Error("Set the RUNTIME_API_KEY Worker secret to a Runtime API key");
191
+ this.refuseSubclassSettings();
129
192
  this.facade ??= new RuntimeSandbox(new Runtime({ apiKey: this.env.RUNTIME_API_KEY, baseUrl: this.env.RUNTIME_API_URL }), workerSandboxIdentity(this.ctx.id.toString(), this.configuration), { ...this.configuration });
130
193
  return this.facade;
131
194
  }
@@ -308,34 +371,99 @@ export class Sandbox extends DurableObject {
308
371
  async setEnvVars(...args) {
309
372
  return (await this.sandbox()).setEnvVars(...args);
310
373
  }
374
+ // The rest of Cloudflare's 0.x Sandbox, each a call on the Runtime sandbox.
375
+ async setLabels(labels) {
376
+ return (await this.sandbox()).setLabels(labels);
377
+ }
378
+ async execWithSessionToken(command, sessionId, options) {
379
+ return (await this.sandbox(options?.signal)).execWithSessionToken(command, sessionId, options);
380
+ }
381
+ async execStreamWithSessionToken(command, sessionId, options) {
382
+ return (await this.sandbox(options?.signal)).execStreamWithSessionToken(command, sessionId, options);
383
+ }
384
+ async streamProcessLogs(processId, options) {
385
+ return (await this.sandbox(options?.signal)).streamProcessLogs(processId, options);
386
+ }
387
+ async cleanupCompletedProcesses(sessionId) {
388
+ return (await this.sandbox()).cleanupCompletedProcesses(sessionId);
389
+ }
390
+ async readFileStream(path, options) {
391
+ return (await this.sandbox()).readFileStream(path, options);
392
+ }
393
+ async gitCheckout(repoUrl, options) {
394
+ return (await this.sandbox()).gitCheckout(repoUrl, options);
395
+ }
396
+ async watch(path, options) {
397
+ return (await this.sandbox()).watch(path, options);
398
+ }
399
+ async checkChanges(path, options) {
400
+ return (await this.sandbox()).checkChanges(path, options);
401
+ }
402
+ async mountBucket(bucket, mountPath, options) {
403
+ return (await this.sandbox()).mountBucket(bucket, mountPath, options);
404
+ }
405
+ async unmountBucket(mountPath) {
406
+ return (await this.sandbox()).unmountBucket(mountPath);
407
+ }
408
+ async createCodeContext(options) {
409
+ return (await this.sandbox()).createCodeContext(options);
410
+ }
411
+ async runCode(code, options) {
412
+ return (await this.sandbox(options?.signal)).runCode(code, options);
413
+ }
414
+ async runCodeStream(code, options) {
415
+ return (await this.sandbox(options?.signal)).runCodeStream(code, options);
416
+ }
417
+ async listCodeContexts() {
418
+ return (await this.sandbox()).listCodeContexts();
419
+ }
420
+ async deleteCodeContext(contextId) {
421
+ return (await this.sandbox()).deleteCodeContext(contextId);
422
+ }
423
+ /** Stops the sandbox; its files stay, and the next call starts it again. */
424
+ async stop(signal) {
425
+ return (await this.sandbox()).stop(signal);
426
+ }
427
+ /** A subclass's Cloudflare-only hooks and outbound settings would do
428
+ * nothing here, so the first call refuses them by name. */
429
+ refuseSubclassSettings() {
430
+ for (const [member, alternative] of Object.entries(SUBCLASS))
431
+ if (this[member] !== undefined)
432
+ throw refusal("Cloudflare", `Sandbox.${member}`, alternative);
433
+ const statics = this.constructor;
434
+ for (const member of STATIC_OUTBOUND)
435
+ if (statics[member] !== undefined)
436
+ throw refusal("Cloudflare", `Sandbox.${member}`, OUTBOUND);
437
+ }
311
438
  exposePort(port, options) {
312
439
  return this.previewOperation(() => this.expose(port, options));
313
440
  }
314
441
  async expose(port, options) {
315
442
  only("Cloudflare Worker exposePort", options, ["name", "hostname", "token"]);
316
443
  if (!Number.isInteger(port) || port < 1024 || port > 65535 || port === 3000)
317
- throw new TypeError("Preview port must be 1024-65535, excluding 3000");
318
- if (!/^[a-z0-9.-]+$/i.test(options.hostname) ||
444
+ throw new SandboxSecurityError(`Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`, "INVALID_PORT");
445
+ if (!/^[a-z0-9.-]+(:\d{1,5})?$/i.test(options.hostname) ||
319
446
  options.hostname.startsWith(".") ||
320
- options.hostname.endsWith("."))
447
+ /\.(:|$)/.test(options.hostname))
321
448
  throw new TypeError("Preview hostname must be a DNS hostname");
322
449
  if (options.hostname.endsWith(".workers.dev"))
323
- throw new Error("Port exposure requires a custom domain; .workers.dev does not support wildcard preview subdomains");
450
+ throw new Error("Port exposure requires a custom domain. .workers.dev domains do not support wildcard subdomains required for port proxying.");
451
+ if (options.token !== undefined)
452
+ validateToken(options.token);
324
453
  const existing = await this.ctx.storage.get(`runtime.preview.${port}`);
325
- const token = options.token ?? existing?.token ?? crypto.randomUUID().replaceAll("-", "");
454
+ const token = options.token ?? existing?.token ?? newToken();
326
455
  const ports = await this.ctx.storage.list({
327
456
  prefix: "runtime.preview.",
328
457
  });
329
- if ([...ports.values()].some((p) => p.port !== port && p.token === token))
330
- throw new Error(`Token '${token}' is already in use by another port`);
331
- if (!/^[a-z0-9_]{1,63}$/.test(token))
332
- throw new TypeError("Preview token must contain 1-63 lowercase letters, digits or underscores");
458
+ const taken = [...ports.values()].find((p) => p.port !== port && p.token === token);
459
+ if (taken)
460
+ throw new SandboxSecurityError(`Token '${token}' is already in use by port ${taken.port}. Please use a different token.`, "TOKEN_IN_USE");
333
461
  const sandbox = await this.sandbox();
334
462
  const name = this.configuration.sandboxName.name;
335
463
  if (name !== name.toLowerCase())
336
464
  throw new CompatibilityError("Cloudflare", "uppercase sandbox IDs in preview hostnames; use normalizeId:true");
337
465
  const native = await (await sandbox.native()).previews.create(port, { visibility: "private" });
338
- const url = `https://${port}-${name}-${token}.${options.hostname}`;
466
+ const url = previewUrl(port, name, options.hostname, token);
339
467
  await this.ctx.storage.put(`runtime.preview.${port}`, {
340
468
  token,
341
469
  url,
@@ -366,7 +494,7 @@ export class Sandbox extends DurableObject {
366
494
  prefix: "runtime.preview.",
367
495
  })).values(),
368
496
  ].map((p) => ({
369
- url: `https://${p.port}-${this.configuration.sandboxName.name}-${p.token}.${hostname}`,
497
+ url: previewUrl(p.port, this.configuration.sandboxName.name, hostname, p.token),
370
498
  port: p.port,
371
499
  status: "active",
372
500
  }));
@@ -410,6 +538,9 @@ export class Sandbox extends DurableObject {
410
538
  }
411
539
  }
412
540
  export function getSandbox(namespace, id, options = {}) {
541
+ for (const [option, alternative] of Object.entries(OPTIONS))
542
+ if (options[option] !== undefined)
543
+ throw refusal("Cloudflare", `getSandbox ${option}`, alternative);
413
544
  only("Cloudflare Worker getSandbox", options, ["normalizeId", "sleepAfter", "keepAlive"]);
414
545
  sanitizeSandboxId(id);
415
546
  const name = options.normalizeId ? id.toLowerCase() : id;
@@ -422,6 +553,16 @@ export function getSandbox(namespace, id, options = {}) {
422
553
  // Await configuration for every operation so no request can race name setup.
423
554
  return new Proxy(stub, {
424
555
  get(target, key) {
556
+ // Cloudflare's getSandbox adds these on the Worker side; an RPC stub
557
+ // would answer every name, so refuse them here, by name.
558
+ if (key === "tunnels")
559
+ return tunnels;
560
+ if (key === "wsConnect")
561
+ return () => Promise.reject(refusal("Cloudflare", "Sandbox.wsConnect", "connect to the URL exposePort returns"));
562
+ if (key === "terminal")
563
+ return () => Promise.reject(refusal("Cloudflare", "Sandbox.terminal", TERMINAL));
564
+ if (key === "client")
565
+ throw refusal("Cloudflare", "Sandbox.client", "Runtime's API is the client");
425
566
  const value = Reflect.get(target, key);
426
567
  if (typeof value !== "function" || key === "then")
427
568
  return value;
@@ -429,6 +570,51 @@ export function getSandbox(namespace, id, options = {}) {
429
570
  },
430
571
  });
431
572
  }
573
+ for (const [member, alternative] of Object.entries(REFUSED))
574
+ Object.defineProperty(Sandbox.prototype, member, {
575
+ configurable: true,
576
+ writable: true,
577
+ value: () => Promise.reject(refusal("Cloudflare", `Sandbox.${member}`, alternative)),
578
+ });
579
+ /** getSandbox options Cloudflare's class reads that a Runtime sandbox has no
580
+ * counterpart for. */
581
+ const OPTIONS = {
582
+ containerTimeouts: "a Runtime sandbox has no container start to tune",
583
+ transport: "Runtime's API is the only transport",
584
+ enableDefaultSession: "commands share the default session; use createSession() for a separate shell",
585
+ labels: "call sandbox.setLabels(labels)",
586
+ };
587
+ const TUNNELS = "use exposePort for a URL to a port; Runtime serves it itself";
588
+ const tunnels = Object.fromEntries(["get", "list", "destroy"].map((method) => [
589
+ method,
590
+ () => Promise.reject(refusal("Cloudflare", `Sandbox.tunnels.${method}`, TUNNELS)),
591
+ ]));
592
+ /** Cloudflare's preview URL for a port: `http` and the given port on a local
593
+ * hostname, `https` otherwise, built as its SDK builds it. */
594
+ function previewUrl(port, sandboxId, hostname, token) {
595
+ const id = sanitizeSandboxId(sandboxId).toLowerCase();
596
+ const [host = "", mainPort] = hostname.split(":");
597
+ const local = ["localhost", "127.0.0.1", "0.0.0.0"].includes(host);
598
+ const base = new URL(local ? `http://${host}:${mainPort || "80"}` : `https://${hostname}`);
599
+ base.hostname = `${port}-${id}-${token}.${host}`;
600
+ return base.toString();
601
+ }
602
+ function validateToken(token) {
603
+ if (token.length === 0)
604
+ throw new SandboxSecurityError("Custom token cannot be empty.", "INVALID_TOKEN");
605
+ if (token.length > 16)
606
+ throw new SandboxSecurityError(`Custom token too long. Maximum 16 characters allowed. Received: ${token.length} characters.`, "INVALID_TOKEN");
607
+ if (!/^[a-z0-9_]+$/.test(token))
608
+ throw new SandboxSecurityError("Custom token must contain only lowercase letters (a-z), numbers (0-9), and underscores (_). Invalid token provided.", "INVALID_TOKEN");
609
+ }
610
+ /** 16 characters of a-z, 0-9 and _, as Cloudflare's tokens are. */
611
+ function newToken() {
612
+ const bytes = crypto.getRandomValues(new Uint8Array(12));
613
+ return btoa(String.fromCharCode(...bytes))
614
+ .replace(/[+/]/g, "_")
615
+ .replace(/=/g, "")
616
+ .toLowerCase();
617
+ }
432
618
  function previewRoute(url) {
433
619
  const label = url.hostname.split(".")[0] ?? "";
434
620
  const match = /^(\d{4,5})-([a-z0-9_-]+)-([a-z0-9_]{1,63})$/.exec(label);
@@ -0,0 +1,10 @@
1
+ /** `withruntime/containers`: Containers alone. One OCI image's process on its
2
+ * own microVM, at the sandbox rates. In a private beta.
3
+ *
4
+ * import { containers } from "withruntime/containers";
5
+ * const client = containers(); // RUNTIME_API_KEY, or this machine's saved connection
6
+ */
7
+ import { Runtime, type RuntimeOptions } from "../client.js";
8
+ export type * from "../products/containers.js";
9
+ /** `runtime.containers` of a client made with `options`. */
10
+ export declare function containers(options?: RuntimeOptions): Runtime["containers"];
@@ -0,0 +1,11 @@
1
+ /** `withruntime/containers`: Containers alone. One OCI image's process on its
2
+ * own microVM, at the sandbox rates. In a private beta.
3
+ *
4
+ * import { containers } from "withruntime/containers";
5
+ * const client = containers(); // RUNTIME_API_KEY, or this machine's saved connection
6
+ */
7
+ import { Runtime } from "../client.js";
8
+ /** `runtime.containers` of a client made with `options`. */
9
+ export function containers(options) {
10
+ return new Runtime(options).containers;
11
+ }
@@ -126,7 +126,8 @@ export declare class Sandbox {
126
126
  /** Pauses the sandbox with its memory. */
127
127
  pause(_timeout?: number): Promise<void>;
128
128
  /** Runtime has no archive tier: a stopped sandbox stays paused, kept for
129
- * its retention (30 days paid, 7 without credit, or autoDeleteInterval). */
129
+ * its retention (while the account has credit, 7 days without, or
130
+ * autoDeleteInterval rounded up to whole days). */
130
131
  archive(): Promise<void>;
131
132
  /** Ends the sandbox for good. */
132
133
  delete(_timeout?: number): Promise<void>;
@@ -270,7 +270,8 @@ export class Sandbox {
270
270
  this.#pausedByPause = true;
271
271
  }
272
272
  /** Runtime has no archive tier: a stopped sandbox stays paused, kept for
273
- * its retention (30 days paid, 7 without credit, or autoDeleteInterval). */
273
+ * its retention (while the account has credit, 7 days without, or
274
+ * autoDeleteInterval rounded up to whole days). */
274
275
  async archive() {
275
276
  await this.stop();
276
277
  }
package/dist/identity.js CHANGED
@@ -5,8 +5,24 @@ async function requestCredentials() {
5
5
  const token = process.env.RUNTIME_ID_TOKEN_REQUEST_TOKEN;
6
6
  if (url && token)
7
7
  return { url, token };
8
- // A process started before the guest wrote its environment still finds it.
9
- return fileCredentials();
8
+ // A process started without them (a server started before the guest wrote
9
+ // its environment, a sandbox whose guest software predates the variables)
10
+ // asks the host, which answers every running sandbox whatever its image or
11
+ // age, then the guest's file. Only inside a sandbox: elsewhere nothing is
12
+ // asked over the network.
13
+ if (!(await exists(sandboxMarker.path)))
14
+ return null;
15
+ return currentCredentials();
16
+ }
17
+ async function exists(path) {
18
+ try {
19
+ const { access } = await import("node:fs/promises");
20
+ await access(path);
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
10
26
  }
11
27
  async function fileCredentials() {
12
28
  try {
package/dist/index.d.ts CHANGED
@@ -76,3 +76,6 @@ export { openWalletAccount, claimWalletAccount, rekeyWalletAccount, type OpenedW
76
76
  export type { Topup } from "./products/billing.js";
77
77
  export type { NewKey, LimitedKey, LimitedKeyAction, MadeKey, ListedKey, KeyUsage, } from "./products/api-keys.js";
78
78
  export type { SsoConnection, SsoStatus } from "./products/sso.js";
79
+ export type { Agent, AgentDefinition, AgentEvent, AgentSession, AgentSessionHandle, AgentSessionState, AgentTemplate, AgentTemplateKey, AgentTrigger, AgentTriggerKind, AgentUse, CreateAgent, CreateAgentTrigger, StartAgentSession, } from "./products/agents.js";
80
+ export { AGENT_TEMPLATES, AGENT_USES } from "./products/agents.js";
81
+ export type { Container, ContainerState, CreateContainer, UpdateContainer, } from "./products/containers.js";
package/dist/index.js CHANGED
@@ -48,3 +48,4 @@ export class Sandbox extends SandboxClass {
48
48
  }
49
49
  export { identityToken } from "./identity.js";
50
50
  export { openWalletAccount, claimWalletAccount, rekeyWalletAccount, } from "./wallet.js";
51
+ export { AGENT_TEMPLATES, AGENT_USES } from "./products/agents.js";
@@ -34,6 +34,8 @@ export const PRODUCT_NAMES = [
34
34
  { name: "jobs", title: "Jobs", engine: "machine", singular: "job", old: was("job") },
35
35
  { name: "workflows", title: "Workflows", engine: "machine", singular: "workflow", old: [] },
36
36
  { name: "builds", title: "Builds", engine: "machine", singular: "build", old: [] },
37
+ { name: "agents", title: "Agents", engine: "machine", singular: "agent", old: [] },
38
+ { name: "ci", title: "CI/CD", engine: "machine", old: [] },
37
39
  // Disk
38
40
  { name: "volumes", title: "Volumes", engine: "disk", singular: "volume", old: was("volume") },
39
41
  {
@@ -91,6 +93,7 @@ export const PRODUCT_NAMES = [
91
93
  { name: "usage", title: "Usage", engine: "meter", old: [] },
92
94
  { name: "billing", title: "Billing", engine: "meter", old: [] },
93
95
  { name: "metrics", title: "Metrics", engine: "meter", old: [] },
96
+ { name: "plans", title: "Plans", engine: "meter", singular: "plan", old: [] },
94
97
  // Database (not now: CLOUD.md section 2)
95
98
  { name: "postgres", title: "Postgres", engine: "database", old: [] },
96
99
  { name: "mysql", title: "MySQL", engine: "database", old: [] },