@watasu/sdk 0.1.30 → 0.1.50
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/README.md +13 -7
- package/dist/codeInterpreter.d.ts +141 -16
- package/dist/codeInterpreter.js +74 -19
- package/dist/commands.d.ts +16 -8
- package/dist/commands.js +17 -16
- package/dist/connectionConfig.d.ts +25 -0
- package/dist/connectionConfig.js +49 -2
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +54 -4
- package/dist/filesystem.d.ts +20 -20
- package/dist/filesystem.js +51 -12
- package/dist/git.d.ts +6 -4
- package/dist/git.js +7 -3
- package/dist/index.d.ts +22 -10
- package/dist/index.js +7 -5
- package/dist/process.d.ts +1 -1
- package/dist/process.js +11 -7
- package/dist/processSocket.d.ts +17 -4
- package/dist/processSocket.js +112 -8
- package/dist/pty.d.ts +3 -3
- package/dist/pty.js +10 -8
- package/dist/sandbox.d.ts +44 -30
- package/dist/sandbox.js +103 -59
- package/dist/template.d.ts +39 -9
- package/dist/template.js +117 -31
- package/dist/terminal.d.ts +3 -3
- package/dist/terminal.js +7 -4
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +23 -6
- package/dist/volume.d.ts +15 -8
- package/dist/volume.js +28 -9
- package/package.json +2 -2
package/dist/sandbox.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { Commands } from './commands.js';
|
|
2
3
|
import { ConnectionConfig, SESSION_OPERATION_REQUEST_TIMEOUT_MS } from './connectionConfig.js';
|
|
3
4
|
import { DataPlaneClient, ControlClient, withQuery } from './transport.js';
|
|
4
|
-
import { ConflictError, FileNotFoundError, NotFoundError, SandboxError
|
|
5
|
+
import { ConflictError, FileNotFoundError, NotFoundError, SandboxError } from './errors.js';
|
|
5
6
|
import { Filesystem } from './filesystem.js';
|
|
6
7
|
import { Git } from './git.js';
|
|
7
8
|
import { Pty } from './pty.js';
|
|
8
9
|
import { ProcessManager } from './process.js';
|
|
9
10
|
import { TerminalManager } from './terminal.js';
|
|
11
|
+
export const ALL_TRAFFIC = '0.0.0.0/0';
|
|
12
|
+
export async function getSignature({ path, operation, user, expirationInSeconds, envdAccessToken, }) {
|
|
13
|
+
if (!envdAccessToken)
|
|
14
|
+
throw new Error('Access token is not set and signature cannot be generated');
|
|
15
|
+
const expiration = expirationInSeconds ? Math.floor(Date.now() / 1000) + expirationInSeconds : null;
|
|
16
|
+
const signatureRaw = expiration === null
|
|
17
|
+
? `${path}:${operation}:${user ?? ''}:${envdAccessToken}`
|
|
18
|
+
: `${path}:${operation}:${user ?? ''}:${envdAccessToken}:${expiration}`;
|
|
19
|
+
const hashBase64 = await sha256(signatureRaw);
|
|
20
|
+
return { signature: `v1_${hashBase64.replace(/=+$/, '')}`, expiration };
|
|
21
|
+
}
|
|
10
22
|
/** Paginator for listing sandbox snapshots. */
|
|
11
23
|
export class SnapshotPaginator {
|
|
12
24
|
opts;
|
|
@@ -24,6 +36,7 @@ export class SnapshotPaginator {
|
|
|
24
36
|
const control = new ControlClient(config);
|
|
25
37
|
const payload = await control.get(snapshotListPath(this.opts, this.nextToken), {
|
|
26
38
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
39
|
+
signal: opts.signal,
|
|
27
40
|
});
|
|
28
41
|
this.nextToken = stringValue(payload.next_token ?? payload.nextToken);
|
|
29
42
|
this.hasNext = this.nextToken !== undefined;
|
|
@@ -57,6 +70,7 @@ export class SandboxPaginator {
|
|
|
57
70
|
const control = new ControlClient(config);
|
|
58
71
|
const payload = await control.get(sandboxListPath(this.opts, this.nextToken), {
|
|
59
72
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
73
|
+
signal: opts.signal,
|
|
60
74
|
});
|
|
61
75
|
this.nextToken = stringValue(payload.next_token ?? payload.nextToken);
|
|
62
76
|
this.hasNext = this.nextToken !== undefined;
|
|
@@ -80,14 +94,12 @@ export class Sandbox {
|
|
|
80
94
|
/** Default sandbox lifetime in milliseconds. */
|
|
81
95
|
static defaultSandboxTimeoutMs = 300_000;
|
|
82
96
|
files;
|
|
83
|
-
filesystem;
|
|
84
97
|
commands;
|
|
85
98
|
process;
|
|
86
99
|
pty;
|
|
87
100
|
terminal;
|
|
88
101
|
git;
|
|
89
102
|
cwd;
|
|
90
|
-
envVars;
|
|
91
103
|
sandboxId;
|
|
92
104
|
mcpPort = 50005;
|
|
93
105
|
mcpToken;
|
|
@@ -101,19 +113,17 @@ export class Sandbox {
|
|
|
101
113
|
this.config = opts.connectionConfig;
|
|
102
114
|
this.control = opts.control ?? new ControlClient(this.config);
|
|
103
115
|
this.envs = opts.envs ?? {};
|
|
104
|
-
this.envVars = this.envs;
|
|
105
116
|
this.sandbox = opts.sandbox ?? {};
|
|
106
117
|
const dataPlane = dataPlaneFromSession(opts.session, this.config);
|
|
107
118
|
this.dataPlane = dataPlane;
|
|
108
119
|
this.files = new Filesystem(dataPlane);
|
|
109
|
-
this.filesystem = this.files;
|
|
110
120
|
this.commands = new Commands(dataPlane, this.config, this.envs);
|
|
111
121
|
this.process = new ProcessManager(this.commands);
|
|
112
122
|
this.pty = new Pty(dataPlane, this.config);
|
|
113
123
|
this.terminal = new TerminalManager(this.pty);
|
|
114
124
|
this.git = new Git(dataPlane);
|
|
115
125
|
}
|
|
116
|
-
/**
|
|
126
|
+
/** Unique sandbox identifier. */
|
|
117
127
|
get id() {
|
|
118
128
|
return this.sandboxId;
|
|
119
129
|
}
|
|
@@ -141,6 +151,7 @@ export class Sandbox {
|
|
|
141
151
|
const response = await control.post('/sandboxes', {
|
|
142
152
|
json: sandboxPayload,
|
|
143
153
|
requestTimeoutMs: sessionOperationRequestTimeout(config, sandboxOpts),
|
|
154
|
+
signal: sandboxOpts.signal,
|
|
144
155
|
});
|
|
145
156
|
const sandbox = record(response.sandbox ?? response);
|
|
146
157
|
const sandboxId = sandbox.id ?? sandbox.sandbox_id;
|
|
@@ -160,10 +171,14 @@ export class Sandbox {
|
|
|
160
171
|
static async connect(sandboxId, opts = {}) {
|
|
161
172
|
const config = new ConnectionConfig(opts);
|
|
162
173
|
const control = new ControlClient(config);
|
|
163
|
-
const info = await control.get(`/sandboxes/${sandboxId}
|
|
174
|
+
const info = await control.get(`/sandboxes/${sandboxId}`, {
|
|
175
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
176
|
+
signal: opts.signal,
|
|
177
|
+
});
|
|
164
178
|
const response = await control.post(`/sandboxes/${sandboxId}/resume`, {
|
|
165
179
|
json: opts.timeoutMs ? { timeout: Math.ceil(opts.timeoutMs / 1000) } : {},
|
|
166
180
|
requestTimeoutMs: sessionOperationRequestTimeout(config, opts),
|
|
181
|
+
signal: opts.signal,
|
|
167
182
|
});
|
|
168
183
|
return new this({
|
|
169
184
|
sandboxId,
|
|
@@ -173,22 +188,17 @@ export class Sandbox {
|
|
|
173
188
|
sandbox: record(response.sandbox ?? info.sandbox ?? {}),
|
|
174
189
|
});
|
|
175
190
|
}
|
|
176
|
-
static async reconnect(sandboxOrOpts, opts = {}) {
|
|
177
|
-
if (typeof sandboxOrOpts === 'string')
|
|
178
|
-
return this.connect(sandboxOrOpts, opts);
|
|
179
|
-
return this.connect(sandboxOrOpts.sandboxID, sandboxOrOpts);
|
|
180
|
-
}
|
|
181
191
|
/** Refresh this sandbox's data-plane session in place. */
|
|
182
192
|
async connect(opts = {}) {
|
|
183
193
|
const response = await this.control.post(`/sandboxes/${this.sandboxId}/resume`, {
|
|
184
194
|
json: opts.timeoutMs ? { timeout: Math.ceil(opts.timeoutMs / 1000) } : {},
|
|
185
195
|
requestTimeoutMs: sessionOperationRequestTimeout(this.config, opts),
|
|
196
|
+
signal: opts.signal,
|
|
186
197
|
});
|
|
187
198
|
this.sandbox = record(response.sandbox ?? this.sandbox);
|
|
188
199
|
const dataPlane = dataPlaneFromSession(response.session, this.config);
|
|
189
200
|
this.dataPlane = dataPlane;
|
|
190
201
|
this.files = new Filesystem(dataPlane);
|
|
191
|
-
this.filesystem = this.files;
|
|
192
202
|
this.commands = new Commands(dataPlane, this.config, this.envs);
|
|
193
203
|
this.process = new ProcessManager(this.commands);
|
|
194
204
|
this.pty = new Pty(dataPlane, this.config);
|
|
@@ -207,6 +217,7 @@ export class Sandbox {
|
|
|
207
217
|
try {
|
|
208
218
|
await control.post(`/sandboxes/${sandboxId}/pause`, {
|
|
209
219
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
220
|
+
signal: opts.signal,
|
|
210
221
|
});
|
|
211
222
|
return true;
|
|
212
223
|
}
|
|
@@ -216,14 +227,18 @@ export class Sandbox {
|
|
|
216
227
|
throw error;
|
|
217
228
|
}
|
|
218
229
|
}
|
|
219
|
-
/**
|
|
230
|
+
/** Pause a sandbox by id. */
|
|
220
231
|
static async pause(sandboxId, opts = {}) {
|
|
221
232
|
return this.betaPause(sandboxId, opts);
|
|
222
233
|
}
|
|
223
234
|
/** Destroy a sandbox by id. */
|
|
224
235
|
static async kill(sandboxId, opts = {}) {
|
|
236
|
+
const requestOpts = typeof opts === 'string' ? {} : opts;
|
|
225
237
|
const control = new ControlClient(new ConnectionConfig(typeof opts === 'string' ? { apiKey: opts } : opts));
|
|
226
|
-
await control.delete(`/sandboxes/${sandboxId}
|
|
238
|
+
await control.delete(`/sandboxes/${sandboxId}`, {
|
|
239
|
+
requestTimeoutMs: requestOpts.requestTimeoutMs,
|
|
240
|
+
signal: requestOpts.signal,
|
|
241
|
+
});
|
|
227
242
|
return true;
|
|
228
243
|
}
|
|
229
244
|
/** Fetch sandbox metrics by id. */
|
|
@@ -231,6 +246,7 @@ export class Sandbox {
|
|
|
231
246
|
const control = new ControlClient(new ConnectionConfig(opts));
|
|
232
247
|
const payload = await control.get(metricsPath(sandboxId, opts), {
|
|
233
248
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
249
|
+
signal: opts.signal,
|
|
234
250
|
});
|
|
235
251
|
return metricsList(payload.metrics ?? payload);
|
|
236
252
|
}
|
|
@@ -243,19 +259,17 @@ export class Sandbox {
|
|
|
243
259
|
const response = await control.put(`/sandboxes/${sandboxId}/network`, {
|
|
244
260
|
json: networkUpdatePayload(network),
|
|
245
261
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
262
|
+
signal: opts.signal,
|
|
246
263
|
});
|
|
247
264
|
return response.sandbox === undefined ? undefined : record(response.sandbox);
|
|
248
265
|
}
|
|
249
|
-
/** Deprecated alias for `getInfo`. */
|
|
250
|
-
static async getFullInfo(sandboxId, opts = {}) {
|
|
251
|
-
return this.getInfo(sandboxId, opts);
|
|
252
|
-
}
|
|
253
266
|
/** Create a Watasu checkpoint using snapshot naming. */
|
|
254
267
|
static async createSnapshot(sandboxId, opts = {}) {
|
|
255
268
|
const control = new ControlClient(new ConnectionConfig(opts));
|
|
256
269
|
const payload = await control.post(`/sandboxes/${sandboxId}/snapshots`, {
|
|
257
270
|
json: snapshotPayload(opts),
|
|
258
271
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
272
|
+
signal: opts.signal,
|
|
259
273
|
});
|
|
260
274
|
return snapshotInfo(record(payload.sandbox_checkpoint ?? payload.snapshot ?? payload));
|
|
261
275
|
}
|
|
@@ -269,6 +283,7 @@ export class Sandbox {
|
|
|
269
283
|
try {
|
|
270
284
|
await control.delete(`/sandbox_snapshots/${snapshotId}`, {
|
|
271
285
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
286
|
+
signal: opts.signal,
|
|
272
287
|
});
|
|
273
288
|
return true;
|
|
274
289
|
}
|
|
@@ -279,8 +294,11 @@ export class Sandbox {
|
|
|
279
294
|
}
|
|
280
295
|
}
|
|
281
296
|
/** Destroy this sandbox. */
|
|
282
|
-
async kill() {
|
|
283
|
-
await this.control.delete(`/sandboxes/${this.sandboxId}
|
|
297
|
+
async kill(opts = {}) {
|
|
298
|
+
await this.control.delete(`/sandboxes/${this.sandboxId}`, {
|
|
299
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
300
|
+
signal: opts.signal,
|
|
301
|
+
});
|
|
284
302
|
return true;
|
|
285
303
|
}
|
|
286
304
|
/** Check if this sandbox is in a runtime-active lifecycle state. */
|
|
@@ -288,6 +306,7 @@ export class Sandbox {
|
|
|
288
306
|
try {
|
|
289
307
|
const payload = await this.control.get(`/sandboxes/${this.sandboxId}`, {
|
|
290
308
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
309
|
+
signal: opts.signal,
|
|
291
310
|
});
|
|
292
311
|
const item = record(payload.sandbox ?? payload);
|
|
293
312
|
return ['creating', 'ready', 'checkpointing', 'restoring', 'stopping'].includes(String(item.state ?? ''));
|
|
@@ -303,27 +322,37 @@ export class Sandbox {
|
|
|
303
322
|
const control = new ControlClient(new ConnectionConfig(opts));
|
|
304
323
|
await control.post(`/sandboxes/${sandboxId}/timeout`, {
|
|
305
324
|
json: { timeout: Math.ceil(timeoutMs / 1000) },
|
|
325
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
326
|
+
signal: opts.signal,
|
|
306
327
|
});
|
|
307
328
|
}
|
|
308
329
|
/** Set this sandbox's lifetime. */
|
|
309
|
-
async setTimeout(timeoutMs) {
|
|
330
|
+
async setTimeout(timeoutMs, opts = {}) {
|
|
310
331
|
await this.control.post(`/sandboxes/${this.sandboxId}/timeout`, {
|
|
311
332
|
json: { timeout: Math.ceil(timeoutMs / 1000) },
|
|
333
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
334
|
+
signal: opts.signal,
|
|
312
335
|
});
|
|
313
336
|
}
|
|
314
|
-
/** Keep the sandbox alive for `duration` milliseconds. */
|
|
315
|
-
async keepAlive(duration) {
|
|
316
|
-
await this.setTimeout(duration);
|
|
317
|
-
}
|
|
318
337
|
/** Fetch control-plane metadata for a sandbox by id. */
|
|
319
338
|
static async getInfo(sandboxId, opts = {}) {
|
|
320
339
|
const control = new ControlClient(new ConnectionConfig(opts));
|
|
321
|
-
const payload = await control.get(`/sandboxes/${sandboxId}
|
|
340
|
+
const payload = await control.get(`/sandboxes/${sandboxId}`, {
|
|
341
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
342
|
+
signal: opts.signal,
|
|
343
|
+
});
|
|
322
344
|
return sandboxInfo(record(payload.sandbox ?? payload));
|
|
323
345
|
}
|
|
346
|
+
/** Fetch full control-plane metadata for a sandbox by id. */
|
|
347
|
+
static async getFullInfo(sandboxId, opts = {}) {
|
|
348
|
+
return this.getInfo(sandboxId, opts);
|
|
349
|
+
}
|
|
324
350
|
/** Fetch the latest control-plane metadata for this sandbox. */
|
|
325
|
-
async getInfo() {
|
|
326
|
-
const payload = await this.control.get(`/sandboxes/${this.sandboxId}
|
|
351
|
+
async getInfo(opts = {}) {
|
|
352
|
+
const payload = await this.control.get(`/sandboxes/${this.sandboxId}`, {
|
|
353
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
354
|
+
signal: opts.signal,
|
|
355
|
+
});
|
|
327
356
|
return sandboxInfo(record(payload.sandbox ?? payload));
|
|
328
357
|
}
|
|
329
358
|
/** Fetch latest sandbox metrics. */
|
|
@@ -338,10 +367,6 @@ export class Sandbox {
|
|
|
338
367
|
async deleteSnapshot(snapshotId, opts = {}) {
|
|
339
368
|
return Sandbox.deleteSnapshot(snapshotId, { ...this.configOptions(), ...opts });
|
|
340
369
|
}
|
|
341
|
-
/** Watasu-native alias for `createSnapshot`. */
|
|
342
|
-
async checkpoint(opts = {}) {
|
|
343
|
-
return this.createSnapshot(opts);
|
|
344
|
-
}
|
|
345
370
|
/** List checkpoints for this sandbox using snapshot naming. */
|
|
346
371
|
listSnapshots(opts = {}) {
|
|
347
372
|
return Sandbox.listSnapshots({ ...this.configOptions(), ...opts, sandboxId: this.sandboxId });
|
|
@@ -355,13 +380,12 @@ export class Sandbox {
|
|
|
355
380
|
if (checkpointId === undefined)
|
|
356
381
|
throw new SandboxError('checkpointId or snapshotId is required');
|
|
357
382
|
const payload = { checkpoint_id: checkpointId };
|
|
358
|
-
if (restoreOpts.timeout !== undefined)
|
|
359
|
-
payload.timeout_seconds = restoreOpts.timeout;
|
|
360
383
|
if (restoreOpts.timeoutMs !== undefined)
|
|
361
384
|
payload.timeout_seconds = Math.ceil(restoreOpts.timeoutMs / 1000);
|
|
362
385
|
const response = await this.control.post(`/sandboxes/${this.sandboxId}/restore`, {
|
|
363
386
|
json: payload,
|
|
364
387
|
requestTimeoutMs: restoreOpts.requestTimeoutMs,
|
|
388
|
+
signal: restoreOpts.signal,
|
|
365
389
|
});
|
|
366
390
|
return sandboxInfo(record(response.sandbox ?? response));
|
|
367
391
|
}
|
|
@@ -379,12 +403,6 @@ export class Sandbox {
|
|
|
379
403
|
throw new SandboxError('port response did not include host or url');
|
|
380
404
|
return `p${port}-${routeToken}.sandbox.${this.config.dataPlaneDomain}`;
|
|
381
405
|
}
|
|
382
|
-
/** Return the public hostname for the sandbox or an exposed sandbox port. */
|
|
383
|
-
getHostname(port) {
|
|
384
|
-
if (port !== undefined)
|
|
385
|
-
return this.getHost(port);
|
|
386
|
-
return new URL(this.dataPlane.baseUrl).host;
|
|
387
|
-
}
|
|
388
406
|
/** Return the conventional MCP URL for this sandbox. */
|
|
389
407
|
getMcpUrl() {
|
|
390
408
|
return `https://${this.getHost(this.mcpPort)}/mcp`;
|
|
@@ -404,12 +422,6 @@ export class Sandbox {
|
|
|
404
422
|
throw error;
|
|
405
423
|
}
|
|
406
424
|
}
|
|
407
|
-
/** Return a protocol string for a secure or insecure sandbox URL. */
|
|
408
|
-
getProtocol(baseProtocol = 'http', secure = true) {
|
|
409
|
-
return `${baseProtocol}${secure ? 's' : ''}`;
|
|
410
|
-
}
|
|
411
|
-
/** Close the local SDK attachment. This does not destroy the sandbox. */
|
|
412
|
-
async close() { }
|
|
413
425
|
/** Get a signed URL that accepts a POST upload for a sandbox file path. */
|
|
414
426
|
async uploadUrl(path = '', opts = {}) {
|
|
415
427
|
const fileUrl = await this.fileUrl('/upload_url', path, opts);
|
|
@@ -437,7 +449,7 @@ export class Sandbox {
|
|
|
437
449
|
async betaPause(opts = {}) {
|
|
438
450
|
return Sandbox.betaPause(this.sandboxId, { ...this.configOptions(), ...opts });
|
|
439
451
|
}
|
|
440
|
-
/**
|
|
452
|
+
/** Pause this sandbox. Returns false when it was already paused. */
|
|
441
453
|
async pause(opts = {}) {
|
|
442
454
|
return this.betaPause(opts);
|
|
443
455
|
}
|
|
@@ -455,6 +467,7 @@ export class Sandbox {
|
|
|
455
467
|
expires_in_seconds: opts.expiresInSeconds,
|
|
456
468
|
}),
|
|
457
469
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
470
|
+
signal: opts.signal,
|
|
458
471
|
});
|
|
459
472
|
return fileUrlInfo(record(payload.file_url ?? payload));
|
|
460
473
|
}
|
|
@@ -463,33 +476,42 @@ export class Sandbox {
|
|
|
463
476
|
return this.dataPlane.postJson(path, {
|
|
464
477
|
json,
|
|
465
478
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
479
|
+
signal: opts.signal,
|
|
466
480
|
});
|
|
467
481
|
}
|
|
468
482
|
/** GET JSON from the sandbox data-plane runtime API. */
|
|
469
483
|
async runtimeGetJson(path, opts = {}) {
|
|
470
484
|
return this.dataPlane.getJson(path, {
|
|
471
485
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
486
|
+
signal: opts.signal,
|
|
472
487
|
});
|
|
473
488
|
}
|
|
474
489
|
/** DELETE JSON from the sandbox data-plane runtime API. */
|
|
475
490
|
async runtimeDeleteJson(path, opts = {}) {
|
|
476
491
|
return this.dataPlane.deleteJson(path, {
|
|
477
492
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
493
|
+
signal: opts.signal,
|
|
478
494
|
});
|
|
479
495
|
}
|
|
480
496
|
configOptions() {
|
|
481
497
|
return {
|
|
482
498
|
apiKey: this.config.apiKey,
|
|
483
499
|
apiUrl: this.config.apiUrl,
|
|
500
|
+
sandboxUrl: this.config.sandboxUrl,
|
|
484
501
|
dataPlaneDomain: this.config.dataPlaneDomain,
|
|
485
502
|
requestTimeoutMs: this.config.requestTimeoutMs,
|
|
503
|
+
headers: this.config.headers,
|
|
504
|
+
apiHeaders: this.config.apiHeaders,
|
|
505
|
+
debug: this.config.debug,
|
|
506
|
+
signal: this.config.signal,
|
|
507
|
+
proxy: this.config.proxy,
|
|
486
508
|
};
|
|
487
509
|
}
|
|
488
510
|
}
|
|
489
511
|
function dataPlaneFromSession(session, config) {
|
|
490
512
|
const item = record(session);
|
|
491
513
|
const token = item.token ?? item.access_token;
|
|
492
|
-
const url = item.data_plane_url;
|
|
514
|
+
const url = config.sandboxUrl ?? item.data_plane_url;
|
|
493
515
|
if (!session)
|
|
494
516
|
throw new SandboxError('sandbox session is required for data-plane operations');
|
|
495
517
|
if (typeof token !== 'string' || typeof url !== 'string') {
|
|
@@ -499,8 +521,8 @@ function dataPlaneFromSession(session, config) {
|
|
|
499
521
|
}
|
|
500
522
|
function sandboxListPath(opts, nextToken) {
|
|
501
523
|
const params = new URLSearchParams();
|
|
502
|
-
if (opts.team)
|
|
503
|
-
params.set('team', opts.team);
|
|
524
|
+
if (opts.team !== undefined)
|
|
525
|
+
params.set('team', String(opts.team));
|
|
504
526
|
if (opts.limit !== undefined)
|
|
505
527
|
params.set('limit', String(opts.limit));
|
|
506
528
|
if (nextToken)
|
|
@@ -678,23 +700,37 @@ function putIfPresent(target, key, value) {
|
|
|
678
700
|
function networkUpdatePayload(network) {
|
|
679
701
|
if (network === undefined)
|
|
680
702
|
return {};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
unsupported('network rules');
|
|
685
|
-
if (network.maskRequestHost !== undefined)
|
|
686
|
-
unsupported('network request host masking');
|
|
703
|
+
const rules = network.rules instanceof Map
|
|
704
|
+
? network.rules
|
|
705
|
+
: new Map(Object.entries(network.rules ?? {}));
|
|
687
706
|
return compactRecord({
|
|
688
|
-
allow_out: network.allowOut,
|
|
689
|
-
deny_out: network.denyOut,
|
|
707
|
+
allow_out: resolveNetworkSelector(network.allowOut, rules),
|
|
708
|
+
deny_out: resolveNetworkSelector(network.denyOut, rules),
|
|
690
709
|
allow_internet_access: network.allowInternetAccess,
|
|
691
710
|
allow_package_registry_access: network.allowPackageRegistryAccess,
|
|
692
711
|
allow_public_traffic: network.allowPublicTraffic,
|
|
693
712
|
egress_profile: network.egressProfile,
|
|
694
713
|
egress_profiles: network.egressProfiles,
|
|
695
714
|
network_class: network.networkClass,
|
|
715
|
+
rules: network.rules === undefined ? undefined : resolveNetworkRules(rules),
|
|
716
|
+
mask_request_host: network.maskRequestHost,
|
|
696
717
|
});
|
|
697
718
|
}
|
|
719
|
+
function resolveNetworkSelector(selector, rules) {
|
|
720
|
+
if (selector === undefined)
|
|
721
|
+
return undefined;
|
|
722
|
+
if (typeof selector === 'function')
|
|
723
|
+
return selector({ allTraffic: ALL_TRAFFIC, rules }).map(String);
|
|
724
|
+
if (typeof selector === 'string')
|
|
725
|
+
return [selector];
|
|
726
|
+
return selector.map(String);
|
|
727
|
+
}
|
|
728
|
+
function resolveNetworkRules(rules) {
|
|
729
|
+
return Object.fromEntries(Array.from(rules.entries()).map(([host, hostRules]) => [
|
|
730
|
+
host,
|
|
731
|
+
hostRules.map((rule) => rule.transform === undefined ? {} : { transform: rule.transform }),
|
|
732
|
+
]));
|
|
733
|
+
}
|
|
698
734
|
function record(value) {
|
|
699
735
|
return value && typeof value === 'object' ? value : {};
|
|
700
736
|
}
|
|
@@ -716,3 +752,11 @@ function routeTokenFromDataPlaneUrl(value, dataPlaneDomain) {
|
|
|
716
752
|
const token = host.slice(0, -suffix.length);
|
|
717
753
|
return token || undefined;
|
|
718
754
|
}
|
|
755
|
+
async function sha256(data) {
|
|
756
|
+
if (globalThis.crypto?.subtle) {
|
|
757
|
+
const bytes = new TextEncoder().encode(data);
|
|
758
|
+
const hash = await globalThis.crypto.subtle.digest('SHA-256', bytes);
|
|
759
|
+
return Buffer.from(hash).toString('base64');
|
|
760
|
+
}
|
|
761
|
+
return createHash('sha256').update(data, 'utf8').digest('base64');
|
|
762
|
+
}
|
package/dist/template.d.ts
CHANGED
|
@@ -12,10 +12,20 @@ export interface BuildInfo {
|
|
|
12
12
|
/** Build identifier. */
|
|
13
13
|
buildId: string;
|
|
14
14
|
}
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
export type LogEntryLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
16
|
+
export type Logger = (logEntry: LogEntry) => void;
|
|
17
|
+
export declare class LogEntry {
|
|
18
|
+
readonly timestamp: Date;
|
|
19
|
+
readonly level: LogEntryLevel;
|
|
20
|
+
readonly message: string;
|
|
21
|
+
constructor(timestamp?: Date, level?: LogEntryLevel, message?: string);
|
|
22
|
+
toString(): string;
|
|
23
|
+
}
|
|
24
|
+
export declare class LogEntryStart extends LogEntry {
|
|
25
|
+
constructor(timestamp?: Date, message?: string);
|
|
26
|
+
}
|
|
27
|
+
export declare class LogEntryEnd extends LogEntry {
|
|
28
|
+
constructor(timestamp?: Date, message?: string);
|
|
19
29
|
}
|
|
20
30
|
export interface BuildStatusReason {
|
|
21
31
|
message: string;
|
|
@@ -75,6 +85,9 @@ export type TemplateBuilder = TemplateBase;
|
|
|
75
85
|
export type TemplateFinal = TemplateBase;
|
|
76
86
|
export type TemplateFromImage = TemplateBase;
|
|
77
87
|
export type ReadyCommand = string | ReadyCmd;
|
|
88
|
+
export declare function defaultBuildLogger(options?: {
|
|
89
|
+
minLevel?: LogEntryLevel;
|
|
90
|
+
}): Logger;
|
|
78
91
|
interface TemplateFileSpec {
|
|
79
92
|
path: string;
|
|
80
93
|
content_b64: string;
|
|
@@ -84,6 +97,10 @@ interface TemplateFileSpec {
|
|
|
84
97
|
}
|
|
85
98
|
interface BuildSpec {
|
|
86
99
|
base?: string;
|
|
100
|
+
from_template?: string;
|
|
101
|
+
from_image?: string;
|
|
102
|
+
from_image_registry?: Record<string, unknown>;
|
|
103
|
+
requested_from_image?: string;
|
|
87
104
|
packages?: Record<string, string[]>;
|
|
88
105
|
files?: TemplateFileSpec[];
|
|
89
106
|
setup?: string[];
|
|
@@ -102,8 +119,6 @@ export declare class ReadyCmd {
|
|
|
102
119
|
export declare function waitForPort(port: number): ReadyCmd;
|
|
103
120
|
/** Return a ready check that waits for a URL to return an HTTP status code. */
|
|
104
121
|
export declare function waitForURL(url: string, statusCode?: number): ReadyCmd;
|
|
105
|
-
/** Alias for `waitForURL`. */
|
|
106
|
-
export declare const waitForUrl: typeof waitForURL;
|
|
107
122
|
/** Return a ready check that waits for a process name. */
|
|
108
123
|
export declare function waitForProcess(processName: string): ReadyCmd;
|
|
109
124
|
/** Return a ready check that waits for a file to exist. */
|
|
@@ -113,6 +128,8 @@ export declare function waitForTimeout(timeout: number): ReadyCmd;
|
|
|
113
128
|
/** Chainable template builder for Watasu package-spec template builds. */
|
|
114
129
|
export declare class TemplateBase {
|
|
115
130
|
private base;
|
|
131
|
+
private fromImageReference;
|
|
132
|
+
private fromImageRegistry;
|
|
116
133
|
private packages;
|
|
117
134
|
private files;
|
|
118
135
|
private setup;
|
|
@@ -137,24 +154,34 @@ export declare class TemplateBase {
|
|
|
137
154
|
static getTags(templateId: string, options?: ConnectionOpts): Promise<TemplateTag[]>;
|
|
138
155
|
static toJSON(template: TemplateClass): Promise<string>;
|
|
139
156
|
static toDockerfile(template: TemplateClass): string;
|
|
157
|
+
/** Request a Debian public base image. The Watasu API fails closed until OCI image import is enabled. */
|
|
140
158
|
fromDebianImage(_variant?: string): TemplateBuilder;
|
|
159
|
+
/** Request an Ubuntu public base image. The Watasu API fails closed until OCI image import is enabled. */
|
|
141
160
|
fromUbuntuImage(_variant?: string): TemplateBuilder;
|
|
161
|
+
/** Request a Python public base image. The Watasu API fails closed until OCI image import is enabled. */
|
|
142
162
|
fromPythonImage(_version?: string): TemplateBuilder;
|
|
163
|
+
/** Request a Node.js public base image. The Watasu API fails closed until OCI image import is enabled. */
|
|
143
164
|
fromNodeImage(_variant?: string): TemplateBuilder;
|
|
165
|
+
/** Request a Bun public base image. The Watasu API fails closed until OCI image import is enabled. */
|
|
144
166
|
fromBunImage(_variant?: string): TemplateBuilder;
|
|
167
|
+
/** Start from the Watasu platform base template. */
|
|
145
168
|
fromBaseImage(): TemplateBuilder;
|
|
146
|
-
|
|
169
|
+
/** Request a public container image base. The Watasu API fails closed until OCI image import is enabled. */
|
|
170
|
+
fromImage(baseImage: string, credentials?: {
|
|
147
171
|
username: string;
|
|
148
172
|
password: string;
|
|
149
173
|
}): TemplateBuilder;
|
|
150
|
-
|
|
174
|
+
/** Request an AWS registry image base. The Watasu API fails closed until registry image import is enabled. */
|
|
175
|
+
fromAWSRegistry(image: string, credentials: {
|
|
151
176
|
accessKeyId: string;
|
|
152
177
|
secretAccessKey: string;
|
|
153
178
|
region: string;
|
|
154
179
|
}): TemplateBuilder;
|
|
155
|
-
|
|
180
|
+
/** Request a GCP registry image base. The Watasu API fails closed until registry image import is enabled. */
|
|
181
|
+
fromGCPRegistry(image: string, credentials: {
|
|
156
182
|
serviceAccountJSON: object | string;
|
|
157
183
|
}): TemplateBuilder;
|
|
184
|
+
/** Start from a ready Watasu template slug, tag, or version id. */
|
|
158
185
|
fromTemplate(template: string): TemplateBuilder;
|
|
159
186
|
fromDockerfile(dockerfileContentOrPath: string): TemplateBuilder;
|
|
160
187
|
copy(src: string | string[], dest: string, options?: CopyOptions): TemplateBuilder;
|
|
@@ -204,10 +231,13 @@ export declare class TemplateBase {
|
|
|
204
231
|
}): TemplateBuilder;
|
|
205
232
|
setStartCmd(startCommand: string, readyCommand: ReadyCommand): TemplateFinal;
|
|
206
233
|
setReadyCmd(readyCommand: ReadyCommand): TemplateFinal;
|
|
234
|
+
betaDevContainerPrebuild(devcontainerDirectory: string): TemplateBuilder;
|
|
235
|
+
betaSetDevContainerStart(devcontainerDirectory: string): TemplateFinal;
|
|
207
236
|
setEnvs(envs: Record<string, string>): TemplateBuilder;
|
|
208
237
|
skipCache(): TemplateBuilder;
|
|
209
238
|
toBuildSpec(): BuildSpec;
|
|
210
239
|
private addPackages;
|
|
240
|
+
private requireDevContainerTemplate;
|
|
211
241
|
private addCopySource;
|
|
212
242
|
private addFileSpec;
|
|
213
243
|
private resolveContextPath;
|