@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/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, unsupported } from './errors.js';
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
- /** Sandbox id alias used by SDK-compatible code. */
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
- /** Alias for `betaPause`. */
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
- /** Alias for `betaPause`. */
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
- if (typeof network !== 'object' || network === null)
682
- unsupported('network callable rules');
683
- if (network.rules !== undefined)
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
+ }
@@ -12,10 +12,20 @@ export interface BuildInfo {
12
12
  /** Build identifier. */
13
13
  buildId: string;
14
14
  }
15
- export interface LogEntry {
16
- timestamp?: Date;
17
- level: string;
18
- message: string;
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
- fromImage(_baseImage: string, _credentials?: {
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
- fromAWSRegistry(_image: string, _credentials: {
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
- fromGCPRegistry(_image: string, _credentials: {
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;