@watasu/sdk 0.1.40 → 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;
@@ -111,7 +123,7 @@ export class Sandbox {
111
123
  this.terminal = new TerminalManager(this.pty);
112
124
  this.git = new Git(dataPlane);
113
125
  }
114
- /** Sandbox id alias used by SDK-compatible code. */
126
+ /** Unique sandbox identifier. */
115
127
  get id() {
116
128
  return this.sandboxId;
117
129
  }
@@ -331,6 +343,10 @@ export class Sandbox {
331
343
  });
332
344
  return sandboxInfo(record(payload.sandbox ?? payload));
333
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
+ }
334
350
  /** Fetch the latest control-plane metadata for this sandbox. */
335
351
  async getInfo(opts = {}) {
336
352
  const payload = await this.control.get(`/sandboxes/${this.sandboxId}`, {
@@ -351,10 +367,6 @@ export class Sandbox {
351
367
  async deleteSnapshot(snapshotId, opts = {}) {
352
368
  return Sandbox.deleteSnapshot(snapshotId, { ...this.configOptions(), ...opts });
353
369
  }
354
- /** Watasu-native alias for `createSnapshot`. */
355
- async checkpoint(opts = {}) {
356
- return this.createSnapshot(opts);
357
- }
358
370
  /** List checkpoints for this sandbox using snapshot naming. */
359
371
  listSnapshots(opts = {}) {
360
372
  return Sandbox.listSnapshots({ ...this.configOptions(), ...opts, sandboxId: this.sandboxId });
@@ -688,23 +700,37 @@ function putIfPresent(target, key, value) {
688
700
  function networkUpdatePayload(network) {
689
701
  if (network === undefined)
690
702
  return {};
691
- if (typeof network !== 'object' || network === null)
692
- unsupported('network callable rules');
693
- if (network.rules !== undefined)
694
- unsupported('network rules');
695
- if (network.maskRequestHost !== undefined)
696
- unsupported('network request host masking');
703
+ const rules = network.rules instanceof Map
704
+ ? network.rules
705
+ : new Map(Object.entries(network.rules ?? {}));
697
706
  return compactRecord({
698
- allow_out: network.allowOut,
699
- deny_out: network.denyOut,
707
+ allow_out: resolveNetworkSelector(network.allowOut, rules),
708
+ deny_out: resolveNetworkSelector(network.denyOut, rules),
700
709
  allow_internet_access: network.allowInternetAccess,
701
710
  allow_package_registry_access: network.allowPackageRegistryAccess,
702
711
  allow_public_traffic: network.allowPublicTraffic,
703
712
  egress_profile: network.egressProfile,
704
713
  egress_profiles: network.egressProfiles,
705
714
  network_class: network.networkClass,
715
+ rules: network.rules === undefined ? undefined : resolveNetworkRules(rules),
716
+ mask_request_host: network.maskRequestHost,
706
717
  });
707
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
+ }
708
734
  function record(value) {
709
735
  return value && typeof value === 'object' ? value : {};
710
736
  }
@@ -726,3 +752,11 @@ function routeTokenFromDataPlaneUrl(value, dataPlaneDomain) {
726
752
  const token = host.slice(0, -suffix.length);
727
753
  return token || undefined;
728
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[];
@@ -111,6 +128,8 @@ export declare function waitForTimeout(timeout: number): ReadyCmd;
111
128
  /** Chainable template builder for Watasu package-spec template builds. */
112
129
  export declare class TemplateBase {
113
130
  private base;
131
+ private fromImageReference;
132
+ private fromImageRegistry;
114
133
  private packages;
115
134
  private files;
116
135
  private setup;
@@ -135,24 +154,34 @@ export declare class TemplateBase {
135
154
  static getTags(templateId: string, options?: ConnectionOpts): Promise<TemplateTag[]>;
136
155
  static toJSON(template: TemplateClass): Promise<string>;
137
156
  static toDockerfile(template: TemplateClass): string;
157
+ /** Request a Debian public base image. The Watasu API fails closed until OCI image import is enabled. */
138
158
  fromDebianImage(_variant?: string): TemplateBuilder;
159
+ /** Request an Ubuntu public base image. The Watasu API fails closed until OCI image import is enabled. */
139
160
  fromUbuntuImage(_variant?: string): TemplateBuilder;
161
+ /** Request a Python public base image. The Watasu API fails closed until OCI image import is enabled. */
140
162
  fromPythonImage(_version?: string): TemplateBuilder;
163
+ /** Request a Node.js public base image. The Watasu API fails closed until OCI image import is enabled. */
141
164
  fromNodeImage(_variant?: string): TemplateBuilder;
165
+ /** Request a Bun public base image. The Watasu API fails closed until OCI image import is enabled. */
142
166
  fromBunImage(_variant?: string): TemplateBuilder;
167
+ /** Start from the Watasu platform base template. */
143
168
  fromBaseImage(): TemplateBuilder;
144
- 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?: {
145
171
  username: string;
146
172
  password: string;
147
173
  }): TemplateBuilder;
148
- 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: {
149
176
  accessKeyId: string;
150
177
  secretAccessKey: string;
151
178
  region: string;
152
179
  }): TemplateBuilder;
153
- 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: {
154
182
  serviceAccountJSON: object | string;
155
183
  }): TemplateBuilder;
184
+ /** Start from a ready Watasu template slug, tag, or version id. */
156
185
  fromTemplate(template: string): TemplateBuilder;
157
186
  fromDockerfile(dockerfileContentOrPath: string): TemplateBuilder;
158
187
  copy(src: string | string[], dest: string, options?: CopyOptions): TemplateBuilder;
package/dist/template.js CHANGED
@@ -1,8 +1,45 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { ConnectionConfig } from './connectionConfig.js';
4
- import { InvalidArgumentError, NotFoundError, SandboxError, unsupported } from './errors.js';
4
+ import { InvalidArgumentError, NotFoundError, SandboxError, TemplateError } from './errors.js';
5
5
  import { ControlClient, withQuery } from './transport.js';
6
+ export class LogEntry {
7
+ timestamp;
8
+ level;
9
+ message;
10
+ constructor(timestamp = new Date(), level = 'info', message = '') {
11
+ this.timestamp = timestamp;
12
+ this.level = level;
13
+ this.message = message;
14
+ }
15
+ toString() {
16
+ return `[${this.timestamp.toISOString()}] ${this.level}: ${this.message}`;
17
+ }
18
+ }
19
+ export class LogEntryStart extends LogEntry {
20
+ constructor(timestamp = new Date(), message = 'Build started') {
21
+ super(timestamp, 'info', message);
22
+ }
23
+ }
24
+ export class LogEntryEnd extends LogEntry {
25
+ constructor(timestamp = new Date(), message = 'Build finished') {
26
+ super(timestamp, 'info', message);
27
+ }
28
+ }
29
+ export function defaultBuildLogger(options = {}) {
30
+ const order = { debug: 10, info: 20, warn: 30, error: 40 };
31
+ const minLevel = options.minLevel ?? 'info';
32
+ return (entry) => {
33
+ if (order[entry.level] < order[minLevel])
34
+ return;
35
+ if (entry.level === 'error')
36
+ console.error(entry.toString());
37
+ else if (entry.level === 'warn')
38
+ console.warn(entry.toString());
39
+ else
40
+ console.log(entry.toString());
41
+ };
42
+ }
6
43
  /** Ready-check command wrapper accepted by template builders. */
7
44
  export class ReadyCmd {
8
45
  cmd;
@@ -38,6 +75,8 @@ export function waitForTimeout(timeout) {
38
75
  /** Chainable template builder for Watasu package-spec template builds. */
39
76
  export class TemplateBase {
40
77
  base;
78
+ fromImageReference;
79
+ fromImageRegistry;
41
80
  packages = {};
42
81
  files = [];
43
82
  setup = [];
@@ -55,10 +94,10 @@ export class TemplateBase {
55
94
  }
56
95
  static async build(template, nameOrOptions, options = {}) {
57
96
  const { name, buildOptions } = normalizeBuildArguments(nameOrOptions, options);
58
- buildOptions.onBuildLogs?.({ timestamp: new Date(), level: 'info', message: 'Build started' });
97
+ buildOptions.onBuildLogs?.(new LogEntryStart());
59
98
  const data = await TemplateBase.buildInBackground(template, name, buildOptions);
60
99
  await waitForBuildFinish(data, buildOptions);
61
- buildOptions.onBuildLogs?.({ timestamp: new Date(), level: 'info', message: 'Build finished' });
100
+ buildOptions.onBuildLogs?.(new LogEntryEnd());
62
101
  return data;
63
102
  }
64
103
  static async buildInBackground(template, nameOrOptions, options = {}) {
@@ -133,42 +172,73 @@ export class TemplateBase {
133
172
  static toDockerfile(template) {
134
173
  return template.toDockerfile();
135
174
  }
175
+ /** Request a Debian public base image. The Watasu API fails closed until OCI image import is enabled. */
136
176
  fromDebianImage(_variant = 'stable') {
137
- this.base = 'base';
138
- return this;
177
+ return this.fromImage(`debian:${_variant}`);
139
178
  }
179
+ /** Request an Ubuntu public base image. The Watasu API fails closed until OCI image import is enabled. */
140
180
  fromUbuntuImage(_variant = 'latest') {
141
- this.base = 'base';
142
- return this;
181
+ return this.fromImage(`ubuntu:${_variant}`);
143
182
  }
183
+ /** Request a Python public base image. The Watasu API fails closed until OCI image import is enabled. */
144
184
  fromPythonImage(_version = '3') {
145
- this.base = this.base ?? 'base';
146
- return this;
185
+ return this.fromImage(`python:${_version}`);
147
186
  }
187
+ /** Request a Node.js public base image. The Watasu API fails closed until OCI image import is enabled. */
148
188
  fromNodeImage(_variant = 'lts') {
149
- this.base = this.base ?? 'base';
150
- return this;
189
+ return this.fromImage(`node:${_variant}`);
151
190
  }
191
+ /** Request a Bun public base image. The Watasu API fails closed until OCI image import is enabled. */
152
192
  fromBunImage(_variant = 'latest') {
153
- this.base = this.base ?? 'base';
154
- return this;
193
+ return this.fromImage(`oven/bun:${_variant}`);
155
194
  }
195
+ /** Start from the Watasu platform base template. */
156
196
  fromBaseImage() {
157
- this.base = 'base';
158
- return this;
197
+ return this.fromTemplate('base');
159
198
  }
160
- fromImage(_baseImage, _credentials) {
161
- this.base = this.base ?? 'base';
199
+ /** Request a public container image base. The Watasu API fails closed until OCI image import is enabled. */
200
+ fromImage(baseImage, credentials) {
201
+ if (credentials && (!credentials.username || !credentials.password)) {
202
+ throw new InvalidArgumentError('Both username and password are required when providing registry credentials');
203
+ }
204
+ this.fromImageReference = baseImage;
205
+ this.base = undefined;
206
+ this.fromImageRegistry = credentials
207
+ ? {
208
+ type: 'registry',
209
+ username: credentials.username,
210
+ password: credentials.password,
211
+ }
212
+ : undefined;
162
213
  return this;
163
214
  }
164
- fromAWSRegistry(_image, _credentials) {
165
- unsupported('Template.fromAWSRegistry');
215
+ /** Request an AWS registry image base. The Watasu API fails closed until registry image import is enabled. */
216
+ fromAWSRegistry(image, credentials) {
217
+ this.fromImageReference = image;
218
+ this.base = undefined;
219
+ this.fromImageRegistry = {
220
+ type: 'aws',
221
+ aws_access_key_id: credentials.accessKeyId,
222
+ aws_secret_access_key: credentials.secretAccessKey,
223
+ aws_region: credentials.region,
224
+ };
225
+ return this;
166
226
  }
167
- fromGCPRegistry(_image, _credentials) {
168
- unsupported('Template.fromGCPRegistry');
227
+ /** Request a GCP registry image base. The Watasu API fails closed until registry image import is enabled. */
228
+ fromGCPRegistry(image, credentials) {
229
+ this.fromImageReference = image;
230
+ this.base = undefined;
231
+ this.fromImageRegistry = {
232
+ type: 'gcp',
233
+ service_account_json: credentials.serviceAccountJSON,
234
+ };
235
+ return this;
169
236
  }
237
+ /** Start from a ready Watasu template slug, tag, or version id. */
170
238
  fromTemplate(template) {
171
239
  this.base = template;
240
+ this.fromImageReference = undefined;
241
+ this.fromImageRegistry = undefined;
172
242
  return this;
173
243
  }
174
244
  fromDockerfile(dockerfileContentOrPath) {
@@ -303,7 +373,11 @@ export class TemplateBase {
303
373
  toBuildSpec() {
304
374
  const spec = {};
305
375
  if (this.base)
306
- spec.base = this.base;
376
+ spec.from_template = this.base;
377
+ if (this.fromImageReference)
378
+ spec.from_image = this.fromImageReference;
379
+ if (this.fromImageRegistry)
380
+ spec.from_image_registry = this.fromImageRegistry;
307
381
  if (Object.keys(this.packages).length > 0)
308
382
  spec.packages = this.packages;
309
383
  if (this.files.length > 0)
@@ -379,7 +453,7 @@ export class TemplateBase {
379
453
  : commandText;
380
454
  }
381
455
  toDockerfile() {
382
- const lines = ['FROM base'];
456
+ const lines = [`FROM ${this.fromImageReference ?? this.base ?? 'base'}`];
383
457
  for (const packageName of this.packages.apt ?? [])
384
458
  lines.push(`RUN apt-get update && apt-get install -y ${packageName}`);
385
459
  for (const packageName of this.packages.pip ?? [])
@@ -553,7 +627,7 @@ async function waitForBuildFinish(data, options) {
553
627
  if (status === 'ready')
554
628
  return;
555
629
  if (status === 'error')
556
- throw new SandboxError(buildStatus.reason?.message ?? 'Template build failed');
630
+ throw new TemplateError(buildStatus.reason?.message ?? 'Template build failed');
557
631
  await new Promise((resolve) => setTimeout(resolve, 200));
558
632
  }
559
633
  }
@@ -600,11 +674,12 @@ function buildStatusReason(payload) {
600
674
  }
601
675
  function logEntry(payload) {
602
676
  const timestamp = stringValue(payload.timestamp);
603
- return {
604
- timestamp: timestamp ? new Date(timestamp) : undefined,
605
- level: stringValue(payload.level) ?? 'info',
606
- message: stringValue(payload.message) ?? '',
607
- };
677
+ return new LogEntry(timestamp ? new Date(timestamp) : new Date(), logEntryLevel(stringValue(payload.level)), stringValue(payload.message) ?? '');
678
+ }
679
+ function logEntryLevel(value) {
680
+ if (value === 'debug' || value === 'info' || value === 'warn' || value === 'error')
681
+ return value;
682
+ return 'info';
608
683
  }
609
684
  function templateTag(payload) {
610
685
  return {
@@ -15,6 +15,7 @@ export type TerminalOpts = {
15
15
  cwd?: string;
16
16
  envs?: Record<string, string>;
17
17
  timeoutMs?: number;
18
+ requestTimeoutMs?: number;
18
19
  };
19
20
  /** A running terminal session in a sandbox. */
20
21
  export declare class Terminal {
package/dist/terminal.js CHANGED
@@ -32,7 +32,10 @@ export class Terminal {
32
32
  return this.waitPromise;
33
33
  }
34
34
  async waitOnce() {
35
- await this.handle.wait().catch((error) => {
35
+ await this.handle.wait().then((result) => {
36
+ if (this.output.data.length === 0 && result.stdout)
37
+ this.output.addData(result.stdout);
38
+ }).catch((error) => {
36
39
  if (typeof error?.stdout === 'string')
37
40
  this.output.addData(error.stdout);
38
41
  else
@@ -62,6 +65,7 @@ export class TerminalManager {
62
65
  envs: opts.envs,
63
66
  size: opts.size,
64
67
  timeoutMs: opts.timeoutMs,
68
+ requestTimeoutMs: opts.requestTimeoutMs,
65
69
  onData: async (bytes) => {
66
70
  const data = new TextDecoder().decode(bytes);
67
71
  output.addData(data);
package/dist/volume.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { Blob } from 'node:buffer';
2
2
  import { ConnectionConfig, type ConnectionOpts } from './connectionConfig.js';
3
3
  import { ControlClient } from './transport.js';
4
- export type VolumeFileType = 'file' | 'directory' | 'symlink' | string;
4
+ export declare enum VolumeFileType {
5
+ UNKNOWN = "unknown",
6
+ FILE = "file",
7
+ DIRECTORY = "directory",
8
+ SYMLINK = "symlink"
9
+ }
5
10
  export type VolumeReadFormat = 'text' | 'bytes' | 'blob' | 'stream';
6
11
  export type VolumeWriteData = string | Uint8Array | ArrayBuffer | Blob;
7
12
  /** Control-plane metadata for a persistent Watasu volume. */
@@ -23,7 +28,7 @@ export interface VolumeInfo {
23
28
  export interface VolumeEntryStat {
24
29
  path: string;
25
30
  name: string;
26
- type: VolumeFileType;
31
+ type: VolumeFileType | string;
27
32
  size?: number;
28
33
  mode?: number;
29
34
  uid?: number;
@@ -36,8 +41,8 @@ export interface VolumeEntryStat {
36
41
  export interface VolumeApiParams extends ConnectionOpts {
37
42
  team?: string;
38
43
  }
39
- export interface VolumeConnectionConfig extends ConnectionOpts {
40
- }
44
+ export type VolumeConnectionConfig = ConnectionOpts;
45
+ export declare const VolumeConnectionConfig: typeof ConnectionConfig;
41
46
  export interface VolumeListOpts extends ConnectionOpts {
42
47
  team?: string;
43
48
  }
@@ -58,6 +63,12 @@ export interface VolumeMetadataOpts extends ConnectionOpts {
58
63
  gid?: number;
59
64
  mode?: number | string;
60
65
  }
66
+ export type VolumeAndToken = VolumeInfo & {
67
+ token: string;
68
+ };
69
+ export type VolumeApiOpts = ConnectionOpts;
70
+ export type VolumeMetadataOptions = Omit<VolumeMetadataOpts, keyof ConnectionOpts>;
71
+ export type VolumeWriteOptions = Omit<VolumeWriteFileOpts, keyof ConnectionOpts>;
61
72
  /** Persistent volume that can be mounted into sandboxes and edited while detached. */
62
73
  export declare class Volume {
63
74
  readonly volumeId: string;
package/dist/volume.js CHANGED
@@ -3,6 +3,14 @@ import { ConnectionConfig } from './connectionConfig.js';
3
3
  import { NotFoundError, SandboxError } from './errors.js';
4
4
  import { base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
5
5
  import { ControlClient, withQuery } from './transport.js';
6
+ export var VolumeFileType;
7
+ (function (VolumeFileType) {
8
+ VolumeFileType["UNKNOWN"] = "unknown";
9
+ VolumeFileType["FILE"] = "file";
10
+ VolumeFileType["DIRECTORY"] = "directory";
11
+ VolumeFileType["SYMLINK"] = "symlink";
12
+ })(VolumeFileType || (VolumeFileType = {}));
13
+ export const VolumeConnectionConfig = ConnectionConfig;
6
14
  /** Persistent volume that can be mounted into sandboxes and edited while detached. */
7
15
  export class Volume {
8
16
  volumeId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@watasu/sdk",
3
- "version": "0.1.40",
3
+ "version": "0.1.50",
4
4
  "type": "module",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "description": "TypeScript SDK for Watasu",
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "scripts": {
25
25
  "build": "tsc -p tsconfig.json",
26
- "test": "npm run build && node --test test/*.mjs"
26
+ "test": "npm run build && tsc -p tsconfig.test.json && node --test test/*.mjs"
27
27
  },
28
28
  "dependencies": {
29
29
  "ws": "^8.18.3"