withruntime 0.3.1 → 0.5.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.
- package/DAYTONA.md +129 -0
- package/E2B.md +146 -0
- package/README.md +84 -12
- package/VERCEL.md +127 -0
- package/dist/ai/index.d.ts +33 -0
- package/dist/ai/index.js +38 -0
- package/dist/claude-agent-sdk/index.d.ts +7 -0
- package/dist/claude-agent-sdk/index.js +65 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +949 -105
- package/dist/client.d.ts +25 -2
- package/dist/client.js +14 -1
- package/dist/credentials.d.ts +10 -0
- package/dist/credentials.js +56 -1
- package/dist/daytona/client.d.ts +44 -0
- package/dist/daytona/client.js +40 -0
- package/dist/daytona/context.d.ts +26 -0
- package/dist/daytona/context.js +22 -0
- package/dist/daytona/daytona.d.ts +185 -0
- package/dist/daytona/daytona.js +416 -0
- package/dist/daytona/errors.d.ts +106 -0
- package/dist/daytona/errors.js +182 -0
- package/dist/daytona/filesystem.d.ts +84 -0
- package/dist/daytona/filesystem.js +206 -0
- package/dist/daytona/git.d.ts +33 -0
- package/dist/daytona/git.js +128 -0
- package/dist/daytona/image.d.ts +45 -0
- package/dist/daytona/image.js +140 -0
- package/dist/daytona/index.d.ts +50 -0
- package/dist/daytona/index.js +50 -0
- package/dist/daytona/process.d.ts +86 -0
- package/dist/daytona/process.js +339 -0
- package/dist/daytona/sandbox.d.ts +196 -0
- package/dist/daytona/sandbox.js +448 -0
- package/dist/e2b/client.d.ts +58 -0
- package/dist/e2b/client.js +66 -0
- package/dist/e2b/code-interpreter.d.ts +144 -0
- package/dist/e2b/code-interpreter.js +244 -0
- package/dist/e2b/commands.d.ts +100 -0
- package/dist/e2b/commands.js +268 -0
- package/dist/e2b/errors.d.ts +72 -0
- package/dist/e2b/errors.js +151 -0
- package/dist/e2b/filesystem.d.ts +82 -0
- package/dist/e2b/filesystem.js +143 -0
- package/dist/e2b/index.d.ts +28 -0
- package/dist/e2b/index.js +61 -0
- package/dist/e2b/sandbox.d.ts +203 -0
- package/dist/e2b/sandbox.js +490 -0
- package/dist/e2b/unsupported.d.ts +19 -0
- package/dist/e2b/unsupported.js +27 -0
- package/dist/errors.d.ts +10 -4
- package/dist/errors.js +26 -6
- package/dist/image-context.d.ts +39 -0
- package/dist/image-context.js +176 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +17 -2
- package/dist/keys.d.ts +43 -0
- package/dist/keys.js +196 -0
- package/dist/login.js +27 -6
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +4 -3
- package/dist/openai-agents/index.d.ts +132 -0
- package/dist/openai-agents/index.js +791 -0
- package/dist/products/audit.d.ts +48 -0
- package/dist/products/audit.js +10 -0
- package/dist/products/images.d.ts +113 -7
- package/dist/products/images.js +186 -27
- package/dist/products/index.d.ts +11 -0
- package/dist/products/index.js +17 -1
- package/dist/products/observability.d.ts +186 -0
- package/dist/products/observability.js +117 -0
- package/dist/products/secrets.d.ts +40 -0
- package/dist/products/secrets.js +24 -0
- package/dist/products/switching.d.ts +89 -0
- package/dist/products/switching.js +25 -0
- package/dist/proxy.d.ts +30 -0
- package/dist/proxy.js +178 -0
- package/dist/sandbox.d.ts +30 -2
- package/dist/sandbox.js +101 -1
- package/dist/ssh.d.ts +21 -0
- package/dist/ssh.js +312 -0
- package/dist/tar.d.ts +1 -0
- package/dist/tar.js +4 -4
- package/dist/tools/index.d.ts +62 -0
- package/dist/tools/index.js +180 -0
- package/dist/transport.d.ts +12 -2
- package/dist/transport.js +76 -16
- package/dist/tunnel.d.ts +67 -0
- package/dist/tunnel.js +311 -0
- package/dist/types.d.ts +57 -1
- package/dist/vercel/client.d.ts +40 -0
- package/dist/vercel/client.js +39 -0
- package/dist/vercel/command.d.ts +70 -0
- package/dist/vercel/command.js +148 -0
- package/dist/vercel/errors.d.ts +53 -0
- package/dist/vercel/errors.js +113 -0
- package/dist/vercel/filesystem.d.ts +150 -0
- package/dist/vercel/filesystem.js +248 -0
- package/dist/vercel/index.d.ts +30 -0
- package/dist/vercel/index.js +38 -0
- package/dist/vercel/sandbox.d.ts +317 -0
- package/dist/vercel/sandbox.js +726 -0
- package/dist/vercel/snapshot.d.ts +93 -0
- package/dist/vercel/snapshot.js +123 -0
- package/package.json +69 -5
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
export type MetricRange = "15m" | "1h" | "6h" | "24h" | "7d" | "30d";
|
|
4
|
+
export type MetricPoint = {
|
|
5
|
+
/** Start of the bucket, ISO 8601. */
|
|
6
|
+
at: string;
|
|
7
|
+
/** Average CPU, percent of all the sandbox's vCPUs (0-100). */
|
|
8
|
+
cpuPercent: number | null;
|
|
9
|
+
/** The same average as a number of cores. */
|
|
10
|
+
cpuCores: number | null;
|
|
11
|
+
/** The busiest interval between two readings in the bucket. */
|
|
12
|
+
cpuPeakPercent: number | null;
|
|
13
|
+
/** Average resident memory, bytes. */
|
|
14
|
+
memoryBytes: number;
|
|
15
|
+
memoryPeakBytes: number;
|
|
16
|
+
/** Host readings in the bucket. */
|
|
17
|
+
samples: number;
|
|
18
|
+
};
|
|
19
|
+
export type SandboxMetrics = {
|
|
20
|
+
sandboxId: string;
|
|
21
|
+
range: MetricRange;
|
|
22
|
+
stepSeconds: number;
|
|
23
|
+
since: string;
|
|
24
|
+
until: string;
|
|
25
|
+
vcpu: number;
|
|
26
|
+
memoryLimitBytes: number;
|
|
27
|
+
diskLimitBytes: number;
|
|
28
|
+
state: string;
|
|
29
|
+
/** The newest reading, or null when there is none in the last 15 minutes. */
|
|
30
|
+
latest: MetricPoint | null;
|
|
31
|
+
points: MetricPoint[];
|
|
32
|
+
};
|
|
33
|
+
export type WebhookEventType = "sandbox.created" | "sandbox.running" | "sandbox.paused" | "sandbox.woken" | "sandbox.stopped" | "sandbox.start_failed" | "sandbox.wake_failed" | "snapshot.ready" | "snapshot.failed" | "snapshot.deleted" | "volume.ready" | "volume.failed" | "volume.deleted";
|
|
34
|
+
/** An event as /v1/events lists it and a webhook delivers it. */
|
|
35
|
+
export type RuntimeEvent = {
|
|
36
|
+
id: string;
|
|
37
|
+
type: WebhookEventType | "webhook.test";
|
|
38
|
+
createdAt: string;
|
|
39
|
+
resourceId?: string | null;
|
|
40
|
+
/** The resource as it was: `data.sandbox`, `data.snapshot` or `data.volume`. */
|
|
41
|
+
data: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
export type Webhook = {
|
|
44
|
+
id: string;
|
|
45
|
+
url: string;
|
|
46
|
+
description: string | null;
|
|
47
|
+
events: Array<WebhookEventType | "*">;
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
/** The secret's last four characters. */
|
|
50
|
+
secretHint: string;
|
|
51
|
+
previousSecretExpiresAt: string | null;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
updatedAt: string;
|
|
54
|
+
lastSuccessAt: string | null;
|
|
55
|
+
lastFailureAt: string | null;
|
|
56
|
+
/** The first failure since the last success, or null while it succeeds. */
|
|
57
|
+
failingSince: string | null;
|
|
58
|
+
/** The signing secret: only on create and rotateSecret. Keep it. */
|
|
59
|
+
secret?: string;
|
|
60
|
+
};
|
|
61
|
+
export type WebhookDelivery = {
|
|
62
|
+
id: string;
|
|
63
|
+
endpointId: string;
|
|
64
|
+
eventId: string;
|
|
65
|
+
eventType: string;
|
|
66
|
+
resourceId: string | null;
|
|
67
|
+
state: "pending" | "succeeded" | "failed" | "cancelled";
|
|
68
|
+
attempts: number;
|
|
69
|
+
maxAttempts: number;
|
|
70
|
+
nextAttemptAt: string | null;
|
|
71
|
+
/** The HTTP status your endpoint answered. */
|
|
72
|
+
lastStatus: number | null;
|
|
73
|
+
lastError: string | null;
|
|
74
|
+
lastDurationMs: number | null;
|
|
75
|
+
lastAttemptAt: string | null;
|
|
76
|
+
deliveredAt: string | null;
|
|
77
|
+
createdAt: string;
|
|
78
|
+
};
|
|
79
|
+
export type OtelExport = {
|
|
80
|
+
id: string;
|
|
81
|
+
endpoint: string;
|
|
82
|
+
/** Header names only; values are never shown back. */
|
|
83
|
+
headerNames: string[];
|
|
84
|
+
signals: Array<"logs" | "metrics">;
|
|
85
|
+
enabled: boolean;
|
|
86
|
+
createdAt: string;
|
|
87
|
+
updatedAt: string;
|
|
88
|
+
lastAttemptAt: string | null;
|
|
89
|
+
lastSuccessAt: string | null;
|
|
90
|
+
lastError: string | null;
|
|
91
|
+
failures: number;
|
|
92
|
+
exportedEvents: number;
|
|
93
|
+
exportedPoints: number;
|
|
94
|
+
};
|
|
95
|
+
type List<T> = {
|
|
96
|
+
data: T[];
|
|
97
|
+
nextCursor: string | null;
|
|
98
|
+
};
|
|
99
|
+
/** `sbx.metrics()`: this sandbox's measured CPU and memory. */
|
|
100
|
+
export declare function sandboxMetrics(t: Transport, sandbox: Sandbox): (options?: {
|
|
101
|
+
range?: MetricRange;
|
|
102
|
+
} & RequestOptions) => Promise<SandboxMetrics>;
|
|
103
|
+
/** `runtime.events.list()`: lifecycle events, newest first. */
|
|
104
|
+
export declare function events(t: Transport): {
|
|
105
|
+
list: (filter?: {
|
|
106
|
+
resourceId?: string;
|
|
107
|
+
type?: WebhookEventType;
|
|
108
|
+
cursor?: string;
|
|
109
|
+
limit?: number;
|
|
110
|
+
}, options?: RequestOptions) => Promise<List<RuntimeEvent>>;
|
|
111
|
+
};
|
|
112
|
+
/** `runtime.webhooks`: signed lifecycle events POSTed to your URL. */
|
|
113
|
+
export declare function webhooks(t: Transport): {
|
|
114
|
+
/** Returns the webhook with `secret`, shown this once. */
|
|
115
|
+
create: (input: {
|
|
116
|
+
url: string;
|
|
117
|
+
events?: Array<WebhookEventType | "*">;
|
|
118
|
+
description?: string;
|
|
119
|
+
}, options?: RequestOptions) => Promise<Webhook>;
|
|
120
|
+
list: (options?: RequestOptions) => Promise<List<Webhook>>;
|
|
121
|
+
get: (id: string, options?: RequestOptions) => Promise<Webhook>;
|
|
122
|
+
update: (id: string, patch: {
|
|
123
|
+
url?: string;
|
|
124
|
+
events?: Array<WebhookEventType | "*">;
|
|
125
|
+
description?: string | null;
|
|
126
|
+
enabled?: boolean;
|
|
127
|
+
}, options?: RequestOptions) => Promise<Webhook>;
|
|
128
|
+
/** A new secret, returned once. The old one keeps signing beside it for
|
|
129
|
+
* `keepPreviousSeconds` (a day by default, a week at most; 0 ends it now). */
|
|
130
|
+
rotateSecret: (id: string, input?: {
|
|
131
|
+
keepPreviousSeconds?: number;
|
|
132
|
+
}, options?: RequestOptions) => Promise<Webhook>;
|
|
133
|
+
delete: (id: string, options?: RequestOptions) => Promise<Webhook>;
|
|
134
|
+
/** Sends a signed `webhook.test` now and answers how your endpoint replied. */
|
|
135
|
+
test: (id: string, options?: RequestOptions) => Promise<WebhookDelivery>;
|
|
136
|
+
deliveries: (id: string, filter?: {
|
|
137
|
+
state?: WebhookDelivery["state"];
|
|
138
|
+
cursor?: string;
|
|
139
|
+
limit?: number;
|
|
140
|
+
}, options?: RequestOptions) => Promise<List<WebhookDelivery>>;
|
|
141
|
+
/** Sends one delivery again, once, now. */
|
|
142
|
+
retry: (deliveryId: string, options?: RequestOptions) => Promise<WebhookDelivery>;
|
|
143
|
+
};
|
|
144
|
+
/** `runtime.otel`: push events (as logs) and CPU and memory (as metrics) to an
|
|
145
|
+
* OpenTelemetry endpoint over OTLP/HTTP. */
|
|
146
|
+
export declare function otel(t: Transport): {
|
|
147
|
+
create: (input: {
|
|
148
|
+
/** The OTLP/HTTP base URL, as OTEL_EXPORTER_OTLP_ENDPOINT. */
|
|
149
|
+
endpoint: string;
|
|
150
|
+
/** For the endpoint's authentication; never shown back. */
|
|
151
|
+
headers?: Record<string, string>;
|
|
152
|
+
signals?: Array<"logs" | "metrics">;
|
|
153
|
+
}, options?: RequestOptions) => Promise<OtelExport>;
|
|
154
|
+
list: (options?: RequestOptions) => Promise<List<OtelExport>>;
|
|
155
|
+
get: (id: string, options?: RequestOptions) => Promise<OtelExport>;
|
|
156
|
+
update: (id: string, patch: {
|
|
157
|
+
endpoint?: string;
|
|
158
|
+
headers?: Record<string, string>;
|
|
159
|
+
signals?: Array<"logs" | "metrics">;
|
|
160
|
+
enabled?: boolean;
|
|
161
|
+
}, options?: RequestOptions) => Promise<OtelExport>;
|
|
162
|
+
/** Push now instead of at the next interval. */
|
|
163
|
+
flush: (id: string, options?: RequestOptions) => Promise<OtelExport>;
|
|
164
|
+
delete: (id: string, options?: RequestOptions) => Promise<OtelExport>;
|
|
165
|
+
};
|
|
166
|
+
export declare class WebhookVerificationError extends Error {
|
|
167
|
+
constructor(message: string);
|
|
168
|
+
}
|
|
169
|
+
/** Checks a webhook's `Runtime-Signature` header against the raw body and
|
|
170
|
+
* your secret (or secrets, during a rotation), and returns the event. Throws
|
|
171
|
+
* `WebhookVerificationError` when the signature does not match or is older
|
|
172
|
+
* than `toleranceSeconds` (300 by default), which stops a captured delivery
|
|
173
|
+
* being replayed. Pass the body exactly as received, before any JSON parsing.
|
|
174
|
+
*
|
|
175
|
+
* app.post("/hooks/runtime", express.raw({ type: "application/json" }), async (req, res) => {
|
|
176
|
+
* const event = await verifyWebhook(req.body, req.get("runtime-signature"), process.env.RUNTIME_WEBHOOK_SECRET!);
|
|
177
|
+
* if (event.type === "sandbox.stopped") ...
|
|
178
|
+
* res.sendStatus(204);
|
|
179
|
+
* });
|
|
180
|
+
*
|
|
181
|
+
* Works wherever Web Crypto does: Node 18+, Bun, Deno, Cloudflare Workers, browsers. */
|
|
182
|
+
export declare function verifyWebhook(body: string | Uint8Array, header: string | null | undefined, secret: string | readonly string[], options?: {
|
|
183
|
+
toleranceSeconds?: number;
|
|
184
|
+
now?: number;
|
|
185
|
+
}): Promise<RuntimeEvent>;
|
|
186
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** `sbx.metrics()`: this sandbox's measured CPU and memory. */
|
|
2
|
+
export function sandboxMetrics(t, sandbox) {
|
|
3
|
+
return (options = {}) => {
|
|
4
|
+
const { range, ...rest } = options;
|
|
5
|
+
return t.json({
|
|
6
|
+
method: "GET",
|
|
7
|
+
path: `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/metrics`,
|
|
8
|
+
query: { range },
|
|
9
|
+
...rest,
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** `runtime.events.list()`: lifecycle events, newest first. */
|
|
14
|
+
export function events(t) {
|
|
15
|
+
return {
|
|
16
|
+
list: (filter = {}, options) => t.json({ method: "GET", path: "/v1/events", query: filter, ...options }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** `runtime.webhooks`: signed lifecycle events POSTed to your URL. */
|
|
20
|
+
export function webhooks(t) {
|
|
21
|
+
const one = (id, verb = "") => `/v1/webhooks/${encodeURIComponent(id)}${verb}`;
|
|
22
|
+
return {
|
|
23
|
+
/** Returns the webhook with `secret`, shown this once. */
|
|
24
|
+
create: (input, options) => t.json({ method: "POST", path: "/v1/webhooks", body: input, ...options }),
|
|
25
|
+
list: (options) => t.json({ method: "GET", path: "/v1/webhooks", ...options }),
|
|
26
|
+
get: (id, options) => t.json({ method: "GET", path: one(id), ...options }),
|
|
27
|
+
update: (id, patch, options) => t.json({ method: "POST", path: one(id, ":update"), body: patch, ...options }),
|
|
28
|
+
/** A new secret, returned once. The old one keeps signing beside it for
|
|
29
|
+
* `keepPreviousSeconds` (a day by default, a week at most; 0 ends it now). */
|
|
30
|
+
rotateSecret: (id, input = {}, options) => t.json({ method: "POST", path: one(id, ":rotate-secret"), body: input, ...options }),
|
|
31
|
+
delete: (id, options) => t.json({ method: "POST", path: one(id, ":delete"), ...options }),
|
|
32
|
+
/** Sends a signed `webhook.test` now and answers how your endpoint replied. */
|
|
33
|
+
test: (id, options) => t.json({ method: "POST", path: one(id, ":test"), wait: 10, ...options }),
|
|
34
|
+
deliveries: (id, filter = {}, options) => t.json({
|
|
35
|
+
method: "GET",
|
|
36
|
+
path: one(id, "/deliveries"),
|
|
37
|
+
query: filter,
|
|
38
|
+
...options,
|
|
39
|
+
}),
|
|
40
|
+
/** Sends one delivery again, once, now. */
|
|
41
|
+
retry: (deliveryId, options) => t.json({
|
|
42
|
+
method: "POST",
|
|
43
|
+
path: `/v1/webhook-deliveries/${encodeURIComponent(deliveryId)}:retry`,
|
|
44
|
+
...options,
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** `runtime.otel`: push events (as logs) and CPU and memory (as metrics) to an
|
|
49
|
+
* OpenTelemetry endpoint over OTLP/HTTP. */
|
|
50
|
+
export function otel(t) {
|
|
51
|
+
const one = (id, verb = "") => `/v1/otel-exports/${encodeURIComponent(id)}${verb}`;
|
|
52
|
+
return {
|
|
53
|
+
create: (input, options) => t.json({ method: "POST", path: "/v1/otel-exports", body: input, ...options }),
|
|
54
|
+
list: (options) => t.json({ method: "GET", path: "/v1/otel-exports", ...options }),
|
|
55
|
+
get: (id, options) => t.json({ method: "GET", path: one(id), ...options }),
|
|
56
|
+
update: (id, patch, options) => t.json({ method: "POST", path: one(id, ":update"), body: patch, ...options }),
|
|
57
|
+
/** Push now instead of at the next interval. */
|
|
58
|
+
flush: (id, options) => t.json({ method: "POST", path: one(id, ":flush"), ...options }),
|
|
59
|
+
delete: (id, options) => t.json({ method: "POST", path: one(id, ":delete"), ...options }),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// ------------------------------------------------------------------ verify
|
|
63
|
+
export class WebhookVerificationError extends Error {
|
|
64
|
+
constructor(message) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = "WebhookVerificationError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const hex = (bytes) => [...new Uint8Array(bytes)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
70
|
+
function sameText(a, b) {
|
|
71
|
+
if (a.length !== b.length)
|
|
72
|
+
return false;
|
|
73
|
+
let diff = 0;
|
|
74
|
+
for (let i = 0; i < a.length; i++)
|
|
75
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
76
|
+
return diff === 0;
|
|
77
|
+
}
|
|
78
|
+
/** Checks a webhook's `Runtime-Signature` header against the raw body and
|
|
79
|
+
* your secret (or secrets, during a rotation), and returns the event. Throws
|
|
80
|
+
* `WebhookVerificationError` when the signature does not match or is older
|
|
81
|
+
* than `toleranceSeconds` (300 by default), which stops a captured delivery
|
|
82
|
+
* being replayed. Pass the body exactly as received, before any JSON parsing.
|
|
83
|
+
*
|
|
84
|
+
* app.post("/hooks/runtime", express.raw({ type: "application/json" }), async (req, res) => {
|
|
85
|
+
* const event = await verifyWebhook(req.body, req.get("runtime-signature"), process.env.RUNTIME_WEBHOOK_SECRET!);
|
|
86
|
+
* if (event.type === "sandbox.stopped") ...
|
|
87
|
+
* res.sendStatus(204);
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* Works wherever Web Crypto does: Node 18+, Bun, Deno, Cloudflare Workers, browsers. */
|
|
91
|
+
export async function verifyWebhook(body, header, secret, options = {}) {
|
|
92
|
+
const text = typeof body === "string" ? body : new TextDecoder().decode(body);
|
|
93
|
+
if (!header)
|
|
94
|
+
throw new WebhookVerificationError("Missing Runtime-Signature header.");
|
|
95
|
+
const parts = header.split(",").map((part) => part.trim().split("="));
|
|
96
|
+
const t = Number(parts.find(([key]) => key === "t")?.[1]);
|
|
97
|
+
const given = parts.filter(([key, value]) => key === "v1" && value).map(([, value]) => value);
|
|
98
|
+
if (!Number.isSafeInteger(t) || !given.length)
|
|
99
|
+
throw new WebhookVerificationError("Malformed Runtime-Signature header.");
|
|
100
|
+
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
101
|
+
if (Math.abs(now - t) > (options.toleranceSeconds ?? 300))
|
|
102
|
+
throw new WebhookVerificationError("The signature is too old; the delivery may be a replay.");
|
|
103
|
+
const encoder = new TextEncoder();
|
|
104
|
+
for (const key of typeof secret === "string" ? [secret] : secret) {
|
|
105
|
+
const hmac = await crypto.subtle.importKey("raw", encoder.encode(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
106
|
+
const expected = hex(await crypto.subtle.sign("HMAC", hmac, encoder.encode(`${t}.${text}`)));
|
|
107
|
+
if (given.some((value) => sameText(value, expected))) {
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(text);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new WebhookVerificationError("The body is not JSON.");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw new WebhookVerificationError("No signature matches the secret.");
|
|
117
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
2
|
+
/** A secret your sandboxes use without seeing it. Every sandbox of your
|
|
3
|
+
* organization has an environment variable of the secret's name holding
|
|
4
|
+
* `placeholder`; the egress proxy puts the value into HTTPS requests to
|
|
5
|
+
* `hosts` (in the URL and headers), or sets `header` on every such request.
|
|
6
|
+
* The value is never returned. */
|
|
7
|
+
export type Secret = {
|
|
8
|
+
name: string;
|
|
9
|
+
hosts: string[];
|
|
10
|
+
header?: string;
|
|
11
|
+
format?: string;
|
|
12
|
+
placeholder: string;
|
|
13
|
+
valueBytes: number;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
};
|
|
17
|
+
export type SetSecret = {
|
|
18
|
+
/** Visible ASCII and spaces, at most 8 KiB. Stored sealed; never returned. */
|
|
19
|
+
value: string;
|
|
20
|
+
/** Where the value may go: `api.openai.com`, `*.github.com`. 1 to 16. */
|
|
21
|
+
hosts: string[];
|
|
22
|
+
/** Set this header on every request to the hosts, replacing the sandbox's own. */
|
|
23
|
+
header?: string;
|
|
24
|
+
/** With `header`: its value, `{value}` where the secret goes. Default `{value}`. */
|
|
25
|
+
format?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function secrets(t: Transport): {
|
|
28
|
+
/** Store or replace a secret. Replacing keeps its placeholder. */
|
|
29
|
+
set: (name: string, input: SetSecret, options?: RequestOptions) => Promise<Secret & {
|
|
30
|
+
enforced: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/** Names, hosts and placeholders. Never values. */
|
|
33
|
+
list: (options?: RequestOptions) => Promise<Secret[]>;
|
|
34
|
+
/** Delete a secret: its value is erased and its placeholder stops working. */
|
|
35
|
+
delete: (name: string, options?: RequestOptions) => Promise<{
|
|
36
|
+
name: string;
|
|
37
|
+
deleted: true;
|
|
38
|
+
enforced: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function secrets(t) {
|
|
2
|
+
const path = (name) => `/v1/egress-secrets/${encodeURIComponent(name)}`;
|
|
3
|
+
return {
|
|
4
|
+
/** Store or replace a secret. Replacing keeps its placeholder. */
|
|
5
|
+
set: (name, input, options) => t.json({
|
|
6
|
+
method: "PUT",
|
|
7
|
+
path: path(name),
|
|
8
|
+
body: input,
|
|
9
|
+
...options,
|
|
10
|
+
}),
|
|
11
|
+
/** Names, hosts and placeholders. Never values. */
|
|
12
|
+
list: async (options) => (await t.json({
|
|
13
|
+
method: "GET",
|
|
14
|
+
path: "/v1/egress-secrets",
|
|
15
|
+
...options,
|
|
16
|
+
})).secrets,
|
|
17
|
+
/** Delete a secret: its value is erased and its placeholder stops working. */
|
|
18
|
+
delete: (name, options) => t.json({
|
|
19
|
+
method: "DELETE",
|
|
20
|
+
path: path(name),
|
|
21
|
+
...options,
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
2
|
+
/** The rivals a comparison prices. `fly` is Sprites; `fly-machines` is
|
|
3
|
+
* Machines, priced only at the size Fly publishes a rate for here. The server
|
|
4
|
+
* prices every rival its rates table carries and names them when it refuses
|
|
5
|
+
* one, so any string is sent as it is. */
|
|
6
|
+
export type CompareProvider = "e2b" | "daytona" | "vercel" | "modal" | "cloudflare" | "fly" | "fly-machines" | (string & {});
|
|
7
|
+
/** The rivals a switch can be recorded from. */
|
|
8
|
+
export type SwitchProvider = "e2b" | "daytona" | "vercel" | "modal" | "cloudflare" | "fly";
|
|
9
|
+
export type SwitchingSummary = {
|
|
10
|
+
/** False while the programme is paused. */
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
/** The most a first top-up is matched to. */
|
|
13
|
+
maxMicros: string;
|
|
14
|
+
providers: SwitchProvider[];
|
|
15
|
+
/** Whether this organization can record a switch now: none recorded, and
|
|
16
|
+
* no top-up paid yet. */
|
|
17
|
+
eligible: boolean;
|
|
18
|
+
switch: {
|
|
19
|
+
provider: SwitchProvider;
|
|
20
|
+
/** superseded: a referral matched the first top-up instead, which is
|
|
21
|
+
* never less. The two are never both paid. */
|
|
22
|
+
status: "pending" | "paid" | "superseded" | "refused" | "reversed";
|
|
23
|
+
refusedReason: "program_off" | "suspended" | "same_card" | "fraud" | null;
|
|
24
|
+
recordedAt: string;
|
|
25
|
+
/** The switching credit this organization got and kept. */
|
|
26
|
+
creditMicros: string;
|
|
27
|
+
} | null;
|
|
28
|
+
};
|
|
29
|
+
export type UsageComparison = {
|
|
30
|
+
provider: CompareProvider;
|
|
31
|
+
rival: {
|
|
32
|
+
name: string;
|
|
33
|
+
/** ISO date the rival's published rates were read. */
|
|
34
|
+
checked: string;
|
|
35
|
+
rates: string[];
|
|
36
|
+
planFee: string;
|
|
37
|
+
sources: {
|
|
38
|
+
label: string;
|
|
39
|
+
url: string;
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
|
+
/** usage: your settled sandboxes. example: none in the window yet, so a
|
|
43
|
+
* stated example workload is priced instead (see `note`). */
|
|
44
|
+
basis: "usage" | "example";
|
|
45
|
+
window: {
|
|
46
|
+
days: number;
|
|
47
|
+
from: string;
|
|
48
|
+
to: string;
|
|
49
|
+
};
|
|
50
|
+
usage: {
|
|
51
|
+
sandboxes: number;
|
|
52
|
+
runSeconds: number;
|
|
53
|
+
activeCpuSeconds: number;
|
|
54
|
+
unpricedSandboxes: number;
|
|
55
|
+
};
|
|
56
|
+
runtimeMicros: string;
|
|
57
|
+
rivalMicros: string;
|
|
58
|
+
/** Rival less Runtime: negative when the rival would cost less. */
|
|
59
|
+
savingMicros: string;
|
|
60
|
+
savingPercent: number | null;
|
|
61
|
+
/** Scaled to 30 days from the days the usage covers; null for the example. */
|
|
62
|
+
perMonth: {
|
|
63
|
+
fromDays: number;
|
|
64
|
+
runtimeMicros: string;
|
|
65
|
+
rivalMicros: string;
|
|
66
|
+
savingMicros: string;
|
|
67
|
+
} | null;
|
|
68
|
+
/** What was compared, at which rates, and what was left out. */
|
|
69
|
+
note: string;
|
|
70
|
+
switching: SwitchingSummary;
|
|
71
|
+
};
|
|
72
|
+
/** `runtime.switching`: compare your usage with a rival's published rates,
|
|
73
|
+
* and record the rival you are leaving so your first top-up is matched, up to
|
|
74
|
+
* $100, once the payment settles. */
|
|
75
|
+
export declare function switching(t: Transport): {
|
|
76
|
+
/** Your settled sandbox usage over the last `days` (default 30, at most
|
|
77
|
+
* 90), priced on Runtime and at the rival, with the saving. */
|
|
78
|
+
compare: (input: {
|
|
79
|
+
provider: CompareProvider;
|
|
80
|
+
days?: number;
|
|
81
|
+
}, options?: RequestOptions) => Promise<UsageComparison>;
|
|
82
|
+
/** Whether a switch can still be recorded, and what it paid. */
|
|
83
|
+
get: (options?: RequestOptions) => Promise<SwitchingSummary>;
|
|
84
|
+
/** Once per organization, before its first top-up. Recording the same
|
|
85
|
+
* provider again answers the same summary; another fails with 409. */
|
|
86
|
+
record: (input: {
|
|
87
|
+
provider: SwitchProvider;
|
|
88
|
+
}, options?: RequestOptions) => Promise<SwitchingSummary>;
|
|
89
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** `runtime.switching`: compare your usage with a rival's published rates,
|
|
2
|
+
* and record the rival you are leaving so your first top-up is matched, up to
|
|
3
|
+
* $100, once the payment settles. */
|
|
4
|
+
export function switching(t) {
|
|
5
|
+
return {
|
|
6
|
+
/** Your settled sandbox usage over the last `days` (default 30, at most
|
|
7
|
+
* 90), priced on Runtime and at the rival, with the saving. */
|
|
8
|
+
compare: (input, options) => t.json({
|
|
9
|
+
method: "GET",
|
|
10
|
+
path: "/v1/usage/compare",
|
|
11
|
+
query: { provider: input.provider, days: input.days },
|
|
12
|
+
...options,
|
|
13
|
+
}),
|
|
14
|
+
/** Whether a switch can still be recorded, and what it paid. */
|
|
15
|
+
get: (options) => t.json({ method: "GET", path: "/v1/switching", ...options }),
|
|
16
|
+
/** Once per organization, before its first top-up. Recording the same
|
|
17
|
+
* provider again answers the same summary; another fails with 409. */
|
|
18
|
+
record: (input, options) => t.json({
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: "/v1/switching",
|
|
21
|
+
body: { provider: input.provider },
|
|
22
|
+
...options,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type Env = Record<string, string | undefined>;
|
|
2
|
+
/** How a call to an address leaves this machine. */
|
|
3
|
+
export type Route = {
|
|
4
|
+
/** The proxy's address, credentials included, to connect through. */
|
|
5
|
+
proxy?: string;
|
|
6
|
+
/** The proxy as it may be shown: no user name or password. */
|
|
7
|
+
shown?: string;
|
|
8
|
+
/** The variable the proxy or the exemption came from. */
|
|
9
|
+
variable?: string;
|
|
10
|
+
/** Set when NO_PROXY exempts the host: the entry that matched. */
|
|
11
|
+
exempt?: string;
|
|
12
|
+
/** HTTP_PROXY was set but HTTPS_PROXY was not, for an HTTPS address. */
|
|
13
|
+
httpOnly?: string;
|
|
14
|
+
};
|
|
15
|
+
/** Whether a NO_PROXY list names this host (and port). Returns the entry. */
|
|
16
|
+
export declare function exemption(list: string, hostname: string, port: string): string | undefined;
|
|
17
|
+
/** The route the SDK takes to `target`, read from the environment now. */
|
|
18
|
+
export declare function routeFor(target: string, env?: Env): Route;
|
|
19
|
+
/** Words for a failed connection: the route it took, and what to check.
|
|
20
|
+
* `cause` is the error fetch threw, or the status a proxy answered with (Bun's
|
|
21
|
+
* fetch hands a refused tunnel back as a response). */
|
|
22
|
+
export declare function describeRoute(target: string, cause: unknown, env?: Env): {
|
|
23
|
+
via: string;
|
|
24
|
+
hint?: string;
|
|
25
|
+
};
|
|
26
|
+
/** fetch, through the proxy the environment names for the address. */
|
|
27
|
+
export declare const envFetch: typeof fetch;
|
|
28
|
+
/** A WebSocket, through the proxy the environment names for the address. */
|
|
29
|
+
export declare function openWebSocket(url: string, headers: Record<string, string>): Promise<WebSocket>;
|
|
30
|
+
export {};
|