withruntime 0.7.1 → 0.8.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 (42) hide show
  1. package/BLAXEL.md +210 -0
  2. package/README.md +18 -0
  3. package/dist/ai-harness/index.js +5 -0
  4. package/dist/blaxel/client.d.ts +48 -0
  5. package/dist/blaxel/client.js +58 -0
  6. package/dist/blaxel/context.d.ts +59 -0
  7. package/dist/blaxel/context.js +162 -0
  8. package/dist/blaxel/errors.d.ts +51 -0
  9. package/dist/blaxel/errors.js +148 -0
  10. package/dist/blaxel/filesystem.d.ts +147 -0
  11. package/dist/blaxel/filesystem.js +585 -0
  12. package/dist/blaxel/index.d.ts +280 -0
  13. package/dist/blaxel/index.js +332 -0
  14. package/dist/blaxel/interpreter.d.ts +74 -0
  15. package/dist/blaxel/interpreter.js +256 -0
  16. package/dist/blaxel/lookup.d.ts +37 -0
  17. package/dist/blaxel/lookup.js +82 -0
  18. package/dist/blaxel/pagination.d.ts +39 -0
  19. package/dist/blaxel/pagination.js +60 -0
  20. package/dist/blaxel/preview.d.ts +110 -0
  21. package/dist/blaxel/preview.js +228 -0
  22. package/dist/blaxel/process.d.ts +126 -0
  23. package/dist/blaxel/process.js +526 -0
  24. package/dist/blaxel/sandbox.d.ts +173 -0
  25. package/dist/blaxel/sandbox.js +768 -0
  26. package/dist/blaxel/snapshot.d.ts +119 -0
  27. package/dist/blaxel/snapshot.js +197 -0
  28. package/dist/blaxel/types.d.ts +145 -0
  29. package/dist/blaxel/types.js +60 -0
  30. package/dist/blaxel/unsupported.d.ts +22 -0
  31. package/dist/blaxel/unsupported.js +34 -0
  32. package/dist/cli.js +49 -15
  33. package/dist/login.d.ts +9 -36
  34. package/dist/login.js +0 -22
  35. package/dist/products/switching.d.ts +2 -2
  36. package/dist/sandbox.d.ts +3 -2
  37. package/dist/sandbox.js +3 -2
  38. package/dist/tar.js +72 -14
  39. package/dist/transport.d.ts +1 -1
  40. package/dist/transport.js +1 -1
  41. package/dist/types.d.ts +14 -1
  42. package/package.json +10 -2
@@ -0,0 +1,119 @@
1
+ import type { Runtime } from "../client.js";
2
+ import type { Snapshot as RuntimeSnapshot } from "../snapshots.js";
3
+ import { type RuntimeOptions, type RuntimeSandbox } from "./client.js";
4
+ import { type PaginatedList } from "./pagination.js";
5
+ export type SandboxSnapshotSource = {
6
+ readonly deleted?: boolean;
7
+ kind?: "sandbox";
8
+ name: string;
9
+ };
10
+ export type SandboxSnapshotSpec = {
11
+ image?: string;
12
+ memory?: number;
13
+ region?: string;
14
+ };
15
+ export type SandboxSnapshot = {
16
+ createdAt: string;
17
+ createdBy?: string;
18
+ readonly id: string;
19
+ name: string;
20
+ sandboxName?: string;
21
+ source?: SandboxSnapshotSource;
22
+ spec?: SandboxSnapshotSpec;
23
+ status: string;
24
+ workspace: string;
25
+ };
26
+ export type SandboxForkResponse = {
27
+ name?: string;
28
+ snapshotId?: string;
29
+ type?: "sandbox" | "application";
30
+ };
31
+ export type SandboxRestoreResponse = {
32
+ name: string;
33
+ snapshotId: string;
34
+ };
35
+ export type Env = {
36
+ name?: string;
37
+ secret?: boolean;
38
+ value?: string;
39
+ };
40
+ export type SnapshotCreateConfiguration = {
41
+ name?: string;
42
+ source: {
43
+ name: string;
44
+ kind?: "sandbox";
45
+ };
46
+ };
47
+ export type SnapshotForkOptions = {
48
+ targetType?: "sandbox" | "application";
49
+ port?: number;
50
+ traffic?: number;
51
+ customDomain?: string;
52
+ prefix?: string;
53
+ envs?: Env[];
54
+ };
55
+ export type SnapshotListQuery = {
56
+ cursor?: string;
57
+ limit?: number;
58
+ sort?: string;
59
+ q?: string;
60
+ anchor?: "end";
61
+ };
62
+ /** Paused sandboxes are kept this long when no TTL says otherwise. */
63
+ export declare const KEEP_DAYS = 365;
64
+ export declare function snapshotModel(snapshot: RuntimeSnapshot, sandboxName?: string): SandboxSnapshot;
65
+ /** Envs as a record; a nameless one is refused. */
66
+ export declare function envRecord(envs: Env[] | undefined): Record<string, string>;
67
+ /** Appends envs to a sandbox's env file: they win over the ones it has. */
68
+ export declare function appendEnvs(runtime: RuntimeSandbox, envs: Record<string, string>): Promise<void>;
69
+ /** A new sandbox named `name`, started from a snapshot, behaving as a Blaxel
70
+ * sandbox. */
71
+ export declare function startFromSnapshot(client: Runtime, snapshotId: string, name: string, labels: Record<string, string>, envs: Record<string, string>): Promise<RuntimeSandbox>;
72
+ declare function refuseApplication(options?: {
73
+ targetType?: string;
74
+ }): void;
75
+ /** A snapshot, as Blaxel's Snapshot class. */
76
+ export declare class Snapshot {
77
+ #private;
78
+ constructor(snapshot: SandboxSnapshot, client: Runtime);
79
+ get name(): string;
80
+ get id(): string;
81
+ get status(): string;
82
+ get workspace(): string;
83
+ get createdAt(): string;
84
+ get source(): SandboxSnapshotSource | undefined;
85
+ get spec(): SandboxSnapshotSpec | undefined;
86
+ /** Takes a snapshot of the sandbox named in `source`. */
87
+ static create(config: SnapshotCreateConfiguration, options?: RuntimeOptions): Promise<Snapshot>;
88
+ /** A snapshot by its id. */
89
+ static get(snapshotId: string, options?: RuntimeOptions): Promise<Snapshot>;
90
+ /** The account's snapshots, a page at a time; `for await` walks them all. */
91
+ static list(query?: SnapshotListQuery, options?: RuntimeOptions): Promise<PaginatedList<Snapshot>>;
92
+ static delete(snapshotId: string, options?: RuntimeOptions): Promise<void>;
93
+ delete(): Promise<void>;
94
+ /** A new sandbox named `targetName`, started from this snapshot. */
95
+ fork(targetName: string, options?: SnapshotForkOptions): Promise<SandboxForkResponse>;
96
+ }
97
+ /** What `sandbox.snapshots` needs from its sandbox. */
98
+ export interface SnapshotContext {
99
+ readonly sandboxName: string;
100
+ readonly sandboxId: string;
101
+ readonly client: Runtime;
102
+ run<T>(work: (runtime: RuntimeSandbox) => Promise<T>): Promise<T>;
103
+ labels(): Record<string, string>;
104
+ }
105
+ /** `sandbox.snapshots`: this sandbox's snapshots, by name. */
106
+ export declare class SandboxSnapshotsResource {
107
+ #private;
108
+ constructor(ctx: SnapshotContext);
109
+ get sandboxName(): string;
110
+ create(name?: string): Promise<Snapshot>;
111
+ list(): Promise<Snapshot[]>;
112
+ /** This sandbox's newest snapshot with that name, or with that id. */
113
+ get(snapshotName: string): Promise<Snapshot>;
114
+ /** The id of a snapshot named, or given by id, for a fork. */
115
+ idOf(nameOrId: string): Promise<string>;
116
+ delete(snapshotName: string): Promise<void>;
117
+ restore(_snapshotName: string): Promise<never>;
118
+ }
119
+ export { refuseApplication };
@@ -0,0 +1,197 @@
1
+ import { clientFor, } from "./client.js";
2
+ import { envFileCommand, envFileLines } from "./context.js";
3
+ import { guard, isNotFound, NotSupportedError, responseError } from "./errors.js";
4
+ import { findSandbox, LEASE_SECONDS, STANDBY, userLabels, whenNameFree } from "./lookup.js";
5
+ import { paginate } from "./pagination.js";
6
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7
+ /** A Blaxel snapshot is kept until deleted: Runtime's longest. */
8
+ const SNAPSHOT_DAYS = 365;
9
+ /** Paused sandboxes are kept this long when no TTL says otherwise. */
10
+ export const KEEP_DAYS = 365;
11
+ export function snapshotModel(snapshot, sandboxName) {
12
+ return {
13
+ id: snapshot.id,
14
+ name: snapshot.name ?? snapshot.id,
15
+ createdAt: snapshot.createdAt,
16
+ status: snapshot.state.toUpperCase(),
17
+ workspace: "",
18
+ ...(sandboxName ? { sandboxName } : {}),
19
+ source: { kind: "sandbox", name: sandboxName ?? snapshot.sourceSandboxId },
20
+ spec: { memory: snapshot.shape?.memoryMiB, region: "us-was-1" },
21
+ };
22
+ }
23
+ /** Envs as a record; a nameless one is refused. */
24
+ export function envRecord(envs) {
25
+ const out = {};
26
+ for (const env of envs ?? []) {
27
+ if (!env.name)
28
+ throw responseError(400, "every env needs a name");
29
+ out[env.name] = env.value ?? "";
30
+ }
31
+ return out;
32
+ }
33
+ /** Appends envs to a sandbox's env file: they win over the ones it has. */
34
+ export async function appendEnvs(runtime, envs) {
35
+ if (!Object.keys(envs).length)
36
+ return;
37
+ const result = await guard(() => runtime.exec(envFileCommand(true), { stdin: envFileLines(envs) }));
38
+ if (result.exitCode !== 0)
39
+ throw responseError(500, `Setting the sandbox's envs failed: ${result.stderr.trim()}`);
40
+ }
41
+ /** A new sandbox named `name`, started from a snapshot, behaving as a Blaxel
42
+ * sandbox. */
43
+ export async function startFromSnapshot(client, snapshotId, name, labels, envs) {
44
+ const input = {
45
+ snapshot: snapshotId,
46
+ name,
47
+ labels,
48
+ ...STANDBY,
49
+ timeoutSeconds: LEASE_SECONDS,
50
+ onLeaseEnd: "pause",
51
+ };
52
+ const runtime = await guard(() => whenNameFree(client, () => client.sandboxes.create(input)));
53
+ try {
54
+ await Promise.all([
55
+ appendEnvs(runtime, envs),
56
+ runtime.info.funding === "paid" ? guard(() => runtime.setRetention(KEEP_DAYS)) : undefined,
57
+ ]);
58
+ }
59
+ catch (error) {
60
+ await runtime.stop({ wait: false }).catch(() => undefined);
61
+ throw error;
62
+ }
63
+ return runtime;
64
+ }
65
+ function refuseApplication(options = {}) {
66
+ if (options.targetType === "application")
67
+ throw new NotSupportedError("Forking into an application", "Fork into a sandbox and share its port with sandbox.previews.create.");
68
+ }
69
+ /** A snapshot, as Blaxel's Snapshot class. */
70
+ export class Snapshot {
71
+ #snapshot;
72
+ #client;
73
+ constructor(snapshot, client) {
74
+ this.#snapshot = snapshot;
75
+ this.#client = client;
76
+ }
77
+ get name() {
78
+ return this.#snapshot.name;
79
+ }
80
+ get id() {
81
+ return this.#snapshot.id;
82
+ }
83
+ get status() {
84
+ return this.#snapshot.status;
85
+ }
86
+ get workspace() {
87
+ return this.#snapshot.workspace;
88
+ }
89
+ get createdAt() {
90
+ return this.#snapshot.createdAt;
91
+ }
92
+ get source() {
93
+ return this.#snapshot.source;
94
+ }
95
+ get spec() {
96
+ return this.#snapshot.spec;
97
+ }
98
+ /** Takes a snapshot of the sandbox named in `source`. */
99
+ static async create(config, options = {}) {
100
+ if (config.source.kind !== undefined && config.source.kind !== "sandbox")
101
+ throw new NotSupportedError(`A snapshot of a ${String(config.source.kind)}`, "Snapshot a sandbox.");
102
+ const client = clientFor(options);
103
+ const runtime = await guard(() => findSandbox(client, config.source.name));
104
+ const taken = await guard(() => runtime.snapshot({
105
+ ...(config.name ? { name: config.name } : {}),
106
+ labels: userLabels(runtime.info.labels),
107
+ retentionDays: SNAPSHOT_DAYS,
108
+ }));
109
+ return new Snapshot(snapshotModel(taken, config.source.name), client);
110
+ }
111
+ /** A snapshot by its id. */
112
+ static async get(snapshotId, options = {}) {
113
+ const client = clientFor(options);
114
+ return new Snapshot(snapshotModel(await guard(() => client.snapshots.get(snapshotId))), client);
115
+ }
116
+ /** The account's snapshots, a page at a time; `for await` walks them all. */
117
+ static async list(query = {}, options = {}) {
118
+ for (const field of ["cursor", "q", "anchor"])
119
+ if (query[field] !== undefined)
120
+ throw new NotSupportedError(`Listing snapshots by ${field}`, "List them (oldest first) and filter the result yourself; nextPage() walks the pages.");
121
+ if (query.sort !== undefined && query.sort !== "createdAt:asc")
122
+ throw new NotSupportedError(`Listing snapshots sorted by ${query.sort}`, "Runtime lists them oldest first: sort the result yourself.");
123
+ const client = clientFor(options);
124
+ const page = await guard(() => client.snapshots.list(query.limit ? { limit: Math.min(query.limit, 100) } : {}));
125
+ return paginate(page, (one) => new Snapshot(snapshotModel(one), client));
126
+ }
127
+ static async delete(snapshotId, options = {}) {
128
+ const client = clientFor(options);
129
+ await guard(() => client.snapshots.delete(snapshotId));
130
+ }
131
+ async delete() {
132
+ await guard(() => this.#client.snapshots.delete(this.id));
133
+ }
134
+ /** A new sandbox named `targetName`, started from this snapshot. */
135
+ async fork(targetName, options = {}) {
136
+ refuseApplication(options);
137
+ await startFromSnapshot(this.#client, this.id, targetName, {}, envRecord(options.envs));
138
+ return { name: targetName, snapshotId: this.id, type: "sandbox" };
139
+ }
140
+ }
141
+ /** `sandbox.snapshots`: this sandbox's snapshots, by name. */
142
+ export class SandboxSnapshotsResource {
143
+ #ctx;
144
+ constructor(ctx) {
145
+ this.#ctx = ctx;
146
+ }
147
+ get sandboxName() {
148
+ return this.#ctx.sandboxName;
149
+ }
150
+ async create(name) {
151
+ const taken = await this.#ctx.run((runtime) => runtime.snapshot({
152
+ ...(name ? { name } : {}),
153
+ labels: userLabels(this.#ctx.labels()),
154
+ retentionDays: SNAPSHOT_DAYS,
155
+ }));
156
+ return new Snapshot(snapshotModel(taken, this.sandboxName), this.#ctx.client);
157
+ }
158
+ async list() {
159
+ const page = await guard(() => this.#ctx.client.snapshots.list({ sandboxId: this.#ctx.sandboxId }));
160
+ const all = await guard(() => page.toArray());
161
+ return all.map((one) => new Snapshot(snapshotModel(one, this.sandboxName), this.#ctx.client));
162
+ }
163
+ /** This sandbox's newest snapshot with that name, or with that id. */
164
+ async get(snapshotName) {
165
+ return new Snapshot(snapshotModel(await this.#find(snapshotName), this.sandboxName), this.#ctx.client);
166
+ }
167
+ async #find(nameOrId) {
168
+ const client = this.#ctx.client;
169
+ const page = await guard(() => client.snapshots.list({ sandboxId: this.#ctx.sandboxId, name: nameOrId }));
170
+ const named = page.data.filter((one) => one.name === nameOrId).at(-1);
171
+ if (named)
172
+ return named;
173
+ if (UUID.test(nameOrId))
174
+ try {
175
+ const byId = await guard(() => client.snapshots.get(nameOrId));
176
+ if (byId.sourceSandboxId === this.#ctx.sandboxId)
177
+ return byId;
178
+ }
179
+ catch (error) {
180
+ if (!isNotFound(error))
181
+ throw error;
182
+ }
183
+ throw responseError(404, `snapshot ${nameOrId} not found`);
184
+ }
185
+ /** The id of a snapshot named, or given by id, for a fork. */
186
+ async idOf(nameOrId) {
187
+ return (await this.#find(nameOrId)).id;
188
+ }
189
+ async delete(snapshotName) {
190
+ const found = await this.#find(snapshotName);
191
+ await guard(() => this.#ctx.client.snapshots.delete(found.id));
192
+ }
193
+ restore(_snapshotName) {
194
+ return Promise.reject(new NotSupportedError("Restoring a sandbox to its snapshot in place", "Start a new sandbox from it: sandbox.fork(newName, { snapshotId }) or snapshot.fork(newName). The new sandbox has its own name and preview addresses."));
195
+ }
196
+ }
197
+ export { refuseApplication };
@@ -0,0 +1,145 @@
1
+ import type { WithRuntime } from "./client.js";
2
+ import type { Env } from "./snapshot.js";
3
+ export type Status = "DELETING" | "TERMINATED" | "FAILED" | "DEACTIVATED" | "DEACTIVATING" | "UPLOADING" | "BUILDING" | "DEPLOYING" | "DEPLOYED" | "BUILT" | "ARCHIVING" | "ARCHIVED" | "UNARCHIVING";
4
+ export type Port = {
5
+ name?: string;
6
+ protocol?: "HTTP" | "TCP" | "UDP" | "TLS";
7
+ target: number;
8
+ };
9
+ export type EnvVar = {
10
+ name: string;
11
+ value: string;
12
+ };
13
+ export type ExpirationPolicy = {
14
+ action?: "delete";
15
+ type?: "ttl-idle" | "ttl-max-age" | "date";
16
+ value?: string;
17
+ };
18
+ export type SandboxLifecycle = {
19
+ expirationPolicies?: Array<ExpirationPolicy>;
20
+ terminatedRetention?: string;
21
+ };
22
+ export type SandboxNetwork = {
23
+ allowedDomains?: Array<string>;
24
+ egress?: unknown;
25
+ firewall?: unknown;
26
+ forbiddenDomains?: Array<string>;
27
+ proxy?: unknown;
28
+ subnet?: string;
29
+ };
30
+ export type VolumeBinding = {
31
+ name: string;
32
+ mountPath: string;
33
+ readOnly?: boolean;
34
+ type?: "persistent" | "ephemeral";
35
+ sizeMb?: number;
36
+ };
37
+ export type VolumeAttachment = {
38
+ mountPath?: string;
39
+ name?: string;
40
+ readOnly?: boolean;
41
+ sizeMb?: number;
42
+ type?: "persistent" | "ephemeral";
43
+ };
44
+ export type Metadata = {
45
+ name: string;
46
+ displayName?: string;
47
+ externalId?: string;
48
+ labels?: Record<string, string>;
49
+ createdAt?: string;
50
+ updatedAt?: string;
51
+ createdBy?: string;
52
+ updatedBy?: string;
53
+ readonly plan?: string;
54
+ readonly url?: string;
55
+ readonly workspace?: string;
56
+ };
57
+ export type SandboxRuntime = {
58
+ envs?: Array<Env>;
59
+ expires?: string;
60
+ extraArgs?: {
61
+ [key: string]: string;
62
+ };
63
+ image?: string;
64
+ memory?: number;
65
+ ports?: Array<Port>;
66
+ terminationGracePeriodSeconds?: number;
67
+ ttl?: string;
68
+ };
69
+ export type SandboxSpec = {
70
+ enabled?: boolean;
71
+ lifecycle?: SandboxLifecycle;
72
+ network?: SandboxNetwork;
73
+ region?: string;
74
+ runtime?: SandboxRuntime;
75
+ volumes?: Array<VolumeAttachment>;
76
+ vpc?: string;
77
+ };
78
+ export type CoreEvent = {
79
+ canaryRevision?: string;
80
+ message?: string;
81
+ revision?: string;
82
+ status?: string;
83
+ time?: string;
84
+ type?: string;
85
+ };
86
+ export type Sandbox = {
87
+ events?: Array<CoreEvent>;
88
+ readonly expiresIn?: number;
89
+ readonly lastUsedAt?: string;
90
+ metadata: Metadata;
91
+ spec: SandboxSpec;
92
+ state?: "RUNNING" | "STANDBY";
93
+ status?: Status;
94
+ };
95
+ export type SandboxConfiguration = {
96
+ forceUrl?: string;
97
+ headers?: Record<string, string>;
98
+ params?: Record<string, string>;
99
+ } & Sandbox;
100
+ export type SandboxCreateConfiguration = {
101
+ name?: string;
102
+ /** A Blaxel stock image (Runtime's stock image) or a ready Runtime image's name. */
103
+ image?: string;
104
+ /** MB; vCPUs follow as memory / 2048, at least 1. */
105
+ memory?: number;
106
+ ports?: (Port | Record<string, unknown>)[];
107
+ envs?: EnvVar[];
108
+ volumes?: (VolumeBinding | VolumeAttachment)[];
109
+ ttl?: string;
110
+ expires?: Date;
111
+ region?: string;
112
+ lifecycle?: SandboxLifecycle;
113
+ network?: SandboxNetwork;
114
+ snapshotEnabled?: boolean;
115
+ labels?: Record<string, string>;
116
+ extraArgs?: Record<string, string>;
117
+ externalId?: string;
118
+ /** Runtime-only: an explicit client, or fields for the create. */
119
+ withruntime?: WithRuntime;
120
+ };
121
+ export type SandboxUpdateMetadata = {
122
+ labels?: Record<string, string>;
123
+ displayName?: string;
124
+ externalId?: string;
125
+ };
126
+ export type SandboxUpdateNetwork = {
127
+ network?: SandboxNetwork;
128
+ };
129
+ export interface SessionCreateOptions {
130
+ expiresAt?: Date;
131
+ responseHeaders?: Record<string, string>;
132
+ requestHeaders?: Record<string, string>;
133
+ }
134
+ export interface SessionWithToken {
135
+ name: string;
136
+ url: string;
137
+ token: string;
138
+ expiresAt: Date;
139
+ }
140
+ /** Ports as Port objects, HTTP by default. */
141
+ export declare function normalizePorts(ports?: (Port | Record<string, unknown>)[]): Port[] | undefined;
142
+ /** Envs checked for a string name and value. */
143
+ export declare function normalizeEnvs(envs?: EnvVar[]): EnvVar[] | undefined;
144
+ /** Volume bindings as attachments. */
145
+ export declare function normalizeVolumes(volumes?: (VolumeBinding | VolumeAttachment)[]): VolumeAttachment[] | undefined;
@@ -0,0 +1,60 @@
1
+ /** Ports as Port objects, HTTP by default. */
2
+ export function normalizePorts(ports) {
3
+ if (!ports || ports.length === 0)
4
+ return undefined;
5
+ return ports.map((port) => {
6
+ if (typeof port !== "object" ||
7
+ port === null ||
8
+ !("name" in port || "target" in port || "protocol" in port))
9
+ throw new Error(`Invalid port type: ${typeof port}. Expected Port object or object with port properties.`);
10
+ if (typeof port.target !== "number")
11
+ throw new Error(`Port target must be a number: ${JSON.stringify(port)}`);
12
+ return {
13
+ ...(typeof port.name === "string" ? { name: port.name } : {}),
14
+ target: port.target,
15
+ protocol: (typeof port.protocol === "string" ? port.protocol : "HTTP"),
16
+ };
17
+ });
18
+ }
19
+ /** Envs checked for a string name and value. */
20
+ export function normalizeEnvs(envs) {
21
+ if (!envs || envs.length === 0)
22
+ return undefined;
23
+ return envs.map((env) => {
24
+ if (typeof env !== "object" || env === null)
25
+ throw new Error(`Invalid env type: ${typeof env}. Expected object with 'name' and 'value' keys.`);
26
+ if (!("name" in env) || !("value" in env))
27
+ throw new Error(`Environment variable object must have 'name' and 'value' keys: ${JSON.stringify(env)}`);
28
+ if (typeof env.name !== "string" || typeof env.value !== "string")
29
+ throw new Error(`Environment variable 'name' and 'value' must be strings: ${JSON.stringify(env)}`);
30
+ return { name: env.name, value: env.value };
31
+ });
32
+ }
33
+ /** Volume bindings as attachments. */
34
+ export function normalizeVolumes(volumes) {
35
+ if (!volumes || volumes.length === 0)
36
+ return undefined;
37
+ return volumes.map((volume) => {
38
+ if (typeof volume !== "object" || volume === null)
39
+ throw new Error(`Invalid volume type: ${typeof volume}. Expected object with 'name' and 'mountPath' keys.`);
40
+ if (!("name" in volume) || !("mountPath" in volume))
41
+ throw new Error(`Volume binding object must have 'name' and 'mountPath' keys: ${JSON.stringify(volume)}`);
42
+ if (typeof volume.name !== "string" || typeof volume.mountPath !== "string")
43
+ throw new Error(`Volume binding 'name' and 'mountPath' must be strings: ${JSON.stringify(volume)}`);
44
+ const out = {
45
+ name: volume.name,
46
+ mountPath: volume.mountPath,
47
+ readOnly: volume.readOnly ?? false,
48
+ };
49
+ if (volume.type)
50
+ out.type = volume.type;
51
+ if (out.type === "ephemeral") {
52
+ if (typeof volume.sizeMb !== "number" || !(volume.sizeMb > 0))
53
+ throw new Error(`Ephemeral volume '${volume.name}' must have a positive 'sizeMb': ${JSON.stringify(volume)}`);
54
+ out.sizeMb = volume.sizeMb;
55
+ }
56
+ else if (typeof volume.sizeMb === "number")
57
+ out.sizeMb = volume.sizeMb;
58
+ return out;
59
+ });
60
+ }
@@ -0,0 +1,22 @@
1
+ /** A stand-in for a Blaxel export Runtime has no counterpart for. Importing
2
+ * it works, so a module that only mentions it still loads; calling it,
3
+ * building it or reading anything from it throws NotSupportedError naming the
4
+ * alternative. */
5
+ export type Unsupported = {
6
+ (...args: unknown[]): never;
7
+ new (...args: unknown[]): never;
8
+ readonly [key: string]: (...args: unknown[]) => never;
9
+ };
10
+ export declare function unsupportedExport(name: string, alternative: string): Unsupported;
11
+ /** A sandbox part Runtime has no counterpart for (`sandbox.sessions`,
12
+ * `sandbox.codegen`, ...): every method answers a rejected promise with
13
+ * NotSupportedError naming the alternative. */
14
+ export declare function unsupportedPart(name: string, alternative: string): UnsupportedPart;
15
+ export type UnsupportedPart = {
16
+ readonly [method: string]: (...args: unknown[]) => Promise<never>;
17
+ };
18
+ export declare const SESSIONS = "Share a port with sandbox.previews.create({ metadata: { name }, spec: { port, public: false } }) and a token from preview.tokens.create(expiresAt); drive the sandbox itself through this SDK.";
19
+ export declare const CODEGEN = "Runtime has no hosted edit or rerank models: apply edits with sandbox.fs.write, and rank files with your own model.";
20
+ export declare const SCHEDULES = "Schedule the call from your own scheduler (cron, a queue) and run it with sandbox.process.exec.";
21
+ export declare const SYSTEM = "Runtime keeps each sandbox's agent current itself; there is nothing to upgrade.";
22
+ export declare const DRIVES = "Use a Runtime volume: create one with runtime.volumes.create({ name }) and pass volumes: [{ name, mountPath }] at create.";
@@ -0,0 +1,34 @@
1
+ import { NotSupportedError } from "./errors.js";
2
+ export function unsupportedExport(name, alternative) {
3
+ const fail = () => {
4
+ throw new NotSupportedError(`Blaxel's ${name}`, alternative);
5
+ };
6
+ return new Proxy(function unsupported() { }, {
7
+ apply: fail,
8
+ construct: fail,
9
+ get: (_target, property) => {
10
+ // Let the module system, inspectors and promise checks look at it
11
+ // without tripping: only a real use throws.
12
+ if (typeof property === "symbol" || property === "then" || property === "prototype")
13
+ return undefined;
14
+ return fail();
15
+ },
16
+ });
17
+ }
18
+ /** A sandbox part Runtime has no counterpart for (`sandbox.sessions`,
19
+ * `sandbox.codegen`, ...): every method answers a rejected promise with
20
+ * NotSupportedError naming the alternative. */
21
+ export function unsupportedPart(name, alternative) {
22
+ return new Proxy({}, {
23
+ get: (_target, property) => {
24
+ if (typeof property === "symbol" || property === "then")
25
+ return undefined;
26
+ return () => Promise.reject(new NotSupportedError(`Blaxel's ${name}.${String(property)}`, alternative));
27
+ },
28
+ });
29
+ }
30
+ export const SESSIONS = "Share a port with sandbox.previews.create({ metadata: { name }, spec: { port, public: false } }) and a token from preview.tokens.create(expiresAt); drive the sandbox itself through this SDK.";
31
+ export const CODEGEN = "Runtime has no hosted edit or rerank models: apply edits with sandbox.fs.write, and rank files with your own model.";
32
+ export const SCHEDULES = "Schedule the call from your own scheduler (cron, a queue) and run it with sandbox.process.exec.";
33
+ export const SYSTEM = "Runtime keeps each sandbox's agent current itself; there is nothing to upgrade.";
34
+ export const DRIVES = "Use a Runtime volume: create one with runtime.volumes.create({ name }) and pass volumes: [{ name, mountPath }] at create.";