@sandbank.dev/boxlite 0.1.1 → 0.3.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/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # @sandbank.dev/boxlite
2
+
3
+ > BoxLite bare-metal micro-VM sandbox adapter for [Sandbank](../../README.md).
4
+
5
+ BoxLite provides lightweight micro-VMs using libkrun (Hypervisor.framework on macOS, KVM on Linux). This adapter supports two modes of operation:
6
+
7
+ - **Remote mode** — Connect to a [BoxRun](https://github.com/nicholasgasior/boxlite) REST API server
8
+ - **Local mode** — Run VMs directly on the local machine via the boxlite Python SDK
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @sandbank.dev/core @sandbank.dev/boxlite
14
+ ```
15
+
16
+ For local mode, you also need the boxlite Python package:
17
+
18
+ ```bash
19
+ pip install boxlite
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Remote mode (BoxRun REST API)
25
+
26
+ ```typescript
27
+ import { createProvider } from '@sandbank.dev/core'
28
+ import { BoxLiteAdapter } from '@sandbank.dev/boxlite'
29
+
30
+ const provider = createProvider(
31
+ new BoxLiteAdapter({
32
+ apiUrl: 'http://localhost:9090',
33
+ apiToken: process.env.BOXLITE_API_TOKEN,
34
+ prefix: 'default', // multi-tenant prefix (optional)
35
+ })
36
+ )
37
+
38
+ const sandbox = await provider.create({
39
+ image: 'ubuntu:24.04',
40
+ resources: { cpu: 2, memory: 1024 },
41
+ })
42
+
43
+ const { stdout } = await sandbox.exec('uname -a')
44
+ await provider.destroy(sandbox.id)
45
+ ```
46
+
47
+ ### Local mode (Python SDK)
48
+
49
+ ```typescript
50
+ import { createProvider } from '@sandbank.dev/core'
51
+ import { BoxLiteAdapter } from '@sandbank.dev/boxlite'
52
+
53
+ const provider = createProvider(
54
+ new BoxLiteAdapter({
55
+ mode: 'local',
56
+ pythonPath: '/usr/bin/python3', // optional, defaults to 'python3'
57
+ boxliteHome: '~/.boxlite', // optional
58
+ })
59
+ )
60
+
61
+ const sandbox = await provider.create({ image: 'ubuntu:24.04' })
62
+ const { stdout } = await sandbox.exec('echo hello')
63
+ await provider.destroy(sandbox.id)
64
+ ```
65
+
66
+ ### OAuth2 authentication (remote mode)
67
+
68
+ ```typescript
69
+ new BoxLiteAdapter({
70
+ apiUrl: 'http://boxrun.example.com:9090',
71
+ clientId: process.env.BOXLITE_CLIENT_ID,
72
+ clientSecret: process.env.BOXLITE_CLIENT_SECRET,
73
+ })
74
+ ```
75
+
76
+ ## Capabilities
77
+
78
+ | Capability | Remote | Local |
79
+ |------------|:------:|:-----:|
80
+ | `exec.stream` | ✅ | ✅ |
81
+ | `terminal` | ✅ | ✅ |
82
+ | `sleep` | ✅ | ✅ |
83
+ | `port.expose` | ✅ | ✅ |
84
+ | `snapshot` | ✅ | — |
85
+
86
+ ## Characteristics
87
+
88
+ - **Runtime:** Micro-VM (libkrun)
89
+ - **Cold start:** ~3-5s
90
+ - **File I/O:** tar archive upload/download
91
+ - **Hypervisor:** Hypervisor.framework (macOS) / KVM (Linux)
92
+ - **Local dependency:** `boxlite` Python package (local mode only)
93
+
94
+ ## Architecture
95
+
96
+ ```
97
+ ┌─────────────────────────────────────┐
98
+ │ BoxLiteAdapter │
99
+ │ mode: 'remote' | 'local' │
100
+ ├──────────────┬──────────────────────┤
101
+ │ REST Client │ Local Client │
102
+ │ (fetch) │ (Python subprocess) │
103
+ ├──────────────┼──────────────────────┤
104
+ │ BoxRun API │ boxlite Python SDK │
105
+ │ (HTTP/JSON) │ (JSON-line bridge) │
106
+ └──────────────┴──────────────────────┘
107
+ ```
108
+
109
+ ## License
110
+
111
+ MIT
package/dist/adapter.d.ts CHANGED
@@ -5,12 +5,14 @@ export declare class BoxLiteAdapter implements SandboxAdapter {
5
5
  readonly capabilities: ReadonlySet<Capability>;
6
6
  private readonly client;
7
7
  private readonly config;
8
- /** Track port mappings per box: boxId → Map<guestPort, hostPort> */
8
+ private readonly host;
9
9
  private readonly portMaps;
10
10
  constructor(config: BoxLiteAdapterConfig);
11
11
  createSandbox(config: CreateConfig): Promise<AdapterSandbox>;
12
12
  getSandbox(id: string): Promise<AdapterSandbox>;
13
13
  listSandboxes(filter?: ListFilter): Promise<SandboxInfo[]>;
14
14
  destroySandbox(id: string): Promise<void>;
15
+ /** Dispose the adapter and clean up resources (e.g. Python bridge process) */
16
+ dispose(): Promise<void>;
15
17
  }
16
18
  //# sourceMappingURL=adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EAGZ,UAAU,EACV,cAAc,EACd,WAAW,EAIZ,MAAM,oBAAoB,CAAA;AAG3B,OAAO,KAAK,EAAE,oBAAoB,EAAc,MAAM,YAAY,CAAA;AAsKlE,qBAAa,cAAe,YAAW,cAAc;IACnD,QAAQ,CAAC,IAAI,aAAY;IACzB,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,CAM5C;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;gBAEtD,MAAM,EAAE,oBAAoB;IAKlC,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IA0C5D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAW/C,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA0B1D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAShD"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EAGZ,UAAU,EACV,cAAc,EACd,WAAW,EAIZ,MAAM,oBAAoB,CAAA;AAI3B,OAAO,KAAK,EAAE,oBAAoB,EAA6B,MAAM,YAAY,CAAA;AAoKjF,qBAAa,cAAe,YAAW,cAAc;IACnD,QAAQ,CAAC,IAAI,aAAY;IACzB,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,CAAA;IAE9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;gBAEtD,MAAM,EAAE,oBAAoB;IAalC,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAuC5D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAW/C,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA0B1D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C,8EAA8E;IACxE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
package/dist/adapter.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SandboxNotFoundError, ProviderError } from '@sandbank.dev/core';
2
- import { createBoxLiteClient } from './client.js';
2
+ import { createBoxLiteRestClient } from './client.js';
3
+ import { createBoxLiteLocalClient } from './local-client.js';
3
4
  /** Map BoxLite box status to Sandbank SandboxState */
4
5
  function mapState(status) {
5
6
  switch (status) {
@@ -15,26 +16,26 @@ function mapState(status) {
15
16
  return 'error';
16
17
  }
17
18
  }
18
- /** Extract host from API URL for port exposure */
19
- function getApiHost(apiUrl) {
19
+ /** Resolve the host used for port exposure and terminal URLs */
20
+ function resolveHost(config) {
21
+ if (config.mode === 'local')
22
+ return '127.0.0.1';
20
23
  try {
21
- const url = new URL(apiUrl);
22
- return url.hostname;
24
+ return new URL(config.apiUrl).hostname;
23
25
  }
24
26
  catch {
25
- return apiUrl;
27
+ return config.apiUrl;
26
28
  }
27
29
  }
28
30
  /** Wrap a BoxLite box into an AdapterSandbox */
29
- function wrapBox(box, client, config, portMappings) {
31
+ function wrapBox(box, client, host, portMappings) {
30
32
  return {
31
- get id() { return box.box_id; },
33
+ get id() { return box.id; },
32
34
  get state() { return mapState(box.status); },
33
35
  get createdAt() { return box.created_at; },
34
36
  async exec(command, options) {
35
- const result = await client.exec(box.box_id, {
36
- command: 'bash',
37
- args: ['-c', command],
37
+ const result = await client.exec(box.id, {
38
+ cmd: ['bash', '-c', command],
38
39
  working_dir: options?.cwd,
39
40
  timeout_seconds: options?.timeout ? Math.ceil(options.timeout / 1000) : undefined,
40
41
  });
@@ -45,9 +46,8 @@ function wrapBox(box, client, config, portMappings) {
45
46
  };
46
47
  },
47
48
  async execStream(command, options) {
48
- return client.execStream(box.box_id, {
49
- command: 'bash',
50
- args: ['-c', command],
49
+ return client.execStream(box.id, {
50
+ cmd: ['bash', '-c', command],
51
51
  working_dir: options?.cwd,
52
52
  timeout_seconds: options?.timeout ? Math.ceil(options.timeout / 1000) : undefined,
53
53
  });
@@ -58,7 +58,6 @@ function wrapBox(box, client, config, portMappings) {
58
58
  data = archive;
59
59
  }
60
60
  else {
61
- // Collect ReadableStream into Uint8Array
62
61
  const reader = archive.getReader();
63
62
  const chunks = [];
64
63
  while (true) {
@@ -75,62 +74,55 @@ function wrapBox(box, client, config, portMappings) {
75
74
  offset += chunk.length;
76
75
  }
77
76
  }
78
- await client.uploadFiles(box.box_id, destDir ?? '/', data);
77
+ await client.uploadFiles(box.id, destDir ?? '/', data);
79
78
  },
80
79
  async downloadArchive(srcDir) {
81
- return client.downloadFiles(box.box_id, srcDir ?? '/');
80
+ return client.downloadFiles(box.id, srcDir ?? '/');
82
81
  },
83
82
  async sleep() {
84
- await client.stopBox(box.box_id);
83
+ await client.stopBox(box.id);
85
84
  },
86
85
  async wake() {
87
- await client.startBox(box.box_id);
86
+ await client.startBox(box.id);
88
87
  },
89
88
  async createSnapshot(name) {
90
89
  const snapshotName = name ?? `snap-${Date.now()}`;
91
- await client.createSnapshot(box.box_id, snapshotName);
90
+ await client.createSnapshot(box.id, snapshotName);
92
91
  return { snapshotId: snapshotName };
93
92
  },
94
93
  async restoreSnapshot(snapshotId) {
95
- await client.restoreSnapshot(box.box_id, snapshotId);
94
+ await client.restoreSnapshot(box.id, snapshotId);
96
95
  },
97
96
  async exposePort(port) {
98
97
  const hostPort = portMappings.get(port) ?? port;
99
- const host = getApiHost(config.apiUrl);
100
98
  return { url: `http://${host}:${hostPort}` };
101
99
  },
102
100
  async startTerminal(options) {
103
101
  const port = 7681;
104
102
  const shell = options?.shell ?? '/bin/bash';
105
- const ttydUrl = 'https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64';
106
- // 1. Ensure ttyd is available
107
- const check = await client.exec(box.box_id, { command: 'which', args: ['ttyd'] });
103
+ const ttydBase = 'https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd';
104
+ const check = await client.exec(box.id, { cmd: ['which', 'ttyd'] });
108
105
  if (check.exitCode !== 0) {
109
- await client.exec(box.box_id, {
110
- command: 'bash',
111
- args: ['-c',
112
- `command -v curl > /dev/null && curl -sL ${ttydUrl} -o /usr/local/bin/ttyd`
113
- + ` || { command -v wget > /dev/null && wget -qO /usr/local/bin/ttyd ${ttydUrl}; }`
114
- + ` || { apt-get update -qq && apt-get install -y -qq wget > /dev/null && wget -qO /usr/local/bin/ttyd ${ttydUrl}; }`,
106
+ await client.exec(box.id, {
107
+ cmd: ['bash', '-c',
108
+ `ARCH=$(uname -m); case "$ARCH" in aarch64|arm64) ARCH=aarch64;; x86_64) ARCH=x86_64;; *) echo "Unsupported arch: $ARCH" >&2; exit 1;; esac; `
109
+ + `TTYD_URL="${ttydBase}.$ARCH"; `
110
+ + `command -v curl > /dev/null && curl -sL "$TTYD_URL" -o /usr/local/bin/ttyd`
111
+ + ` || { command -v wget > /dev/null && wget -qO /usr/local/bin/ttyd "$TTYD_URL"; }`
112
+ + ` || { apt-get update -qq && apt-get install -y -qq wget > /dev/null && wget -qO /usr/local/bin/ttyd "$TTYD_URL"; }`,
115
113
  ],
116
114
  });
117
- await client.exec(box.box_id, {
118
- command: 'chmod',
119
- args: ['+x', '/usr/local/bin/ttyd'],
115
+ await client.exec(box.id, {
116
+ cmd: ['chmod', '+x', '/usr/local/bin/ttyd'],
120
117
  });
121
118
  }
122
- // 2. Start ttyd in background (-W enables write)
123
- await client.exec(box.box_id, {
124
- command: 'bash',
125
- args: ['-c', `nohup ttyd -W -p ${port} '${shell.replace(/'/g, "'\\''")}' > /dev/null 2>&1 &`],
119
+ await client.exec(box.id, {
120
+ cmd: ['bash', '-c', `nohup ttyd -W -p ${port} '${shell.replace(/'/g, "'\\''")}' > /dev/null 2>&1 &`],
126
121
  });
127
- // 3. Wait for ttyd to be ready
128
- await client.exec(box.box_id, {
129
- command: 'bash',
130
- args: ['-c', `for i in $(seq 1 20); do pgrep -x ttyd > /dev/null && break || sleep 0.5; done`],
122
+ await client.exec(box.id, {
123
+ cmd: ['bash', '-c', `for i in $(seq 1 20); do pgrep -x ttyd > /dev/null && break || sleep 0.5; done`],
131
124
  });
132
125
  const hostPort = portMappings.get(port) ?? port;
133
- const host = getApiHost(config.apiUrl);
134
126
  return {
135
127
  url: `ws://${host}:${hostPort}/ws`,
136
128
  port,
@@ -143,55 +135,60 @@ function isNotFound(err) {
143
135
  const msg = err instanceof Error ? err.message : String(err);
144
136
  return msg.includes('404') || msg.includes('not found') || msg.includes('Not Found');
145
137
  }
138
+ /** Create the appropriate client based on config mode */
139
+ function createClient(config) {
140
+ if (config.mode === 'local') {
141
+ return createBoxLiteLocalClient(config);
142
+ }
143
+ return createBoxLiteRestClient(config);
144
+ }
146
145
  export class BoxLiteAdapter {
147
146
  name = 'boxlite';
148
- capabilities = new Set([
149
- 'exec.stream',
150
- 'terminal',
151
- 'sleep',
152
- 'snapshot',
153
- 'port.expose',
154
- ]);
147
+ capabilities;
155
148
  client;
156
149
  config;
157
- /** Track port mappings per box: boxId → Map<guestPort, hostPort> */
150
+ host;
158
151
  portMaps = new Map();
159
152
  constructor(config) {
160
153
  this.config = config;
161
- this.client = createBoxLiteClient(config);
154
+ this.host = resolveHost(config);
155
+ this.client = createClient(config);
156
+ // Local mode: snapshots not supported yet
157
+ const caps = ['exec.stream', 'terminal', 'sleep', 'port.expose'];
158
+ if (config.mode !== 'local') {
159
+ caps.push('snapshot');
160
+ }
161
+ this.capabilities = new Set(caps);
162
162
  }
163
163
  async createSandbox(config) {
164
164
  try {
165
165
  const box = await this.client.createBox({
166
166
  image: config.image,
167
- cpus: config.resources?.cpu,
168
- memory_mib: config.resources?.memory,
167
+ cpu: config.resources?.cpu,
168
+ memory_mb: config.resources?.memory,
169
169
  env: config.env,
170
170
  auto_remove: false,
171
171
  });
172
- // Store port mappings if they were specified at creation
173
172
  const portMap = new Map();
174
- this.portMaps.set(box.box_id, portMap);
175
- // Start the box if it was created in configured state
173
+ this.portMaps.set(box.id, portMap);
176
174
  if (box.status === 'configured' || box.status === 'stopped') {
177
- await this.client.startBox(box.box_id);
175
+ await this.client.startBox(box.id);
178
176
  }
179
- // Wait for box to be running (timeout is in seconds per CreateConfig docs)
180
177
  const timeoutSec = config.timeout ?? 30;
181
178
  const maxAttempts = Math.max(1, timeoutSec);
182
179
  let current = box;
183
180
  for (let i = 0; i < maxAttempts; i++) {
184
- current = await this.client.getBox(box.box_id);
181
+ current = await this.client.getBox(box.id);
185
182
  if (current.status === 'running')
186
183
  break;
187
184
  await new Promise(r => setTimeout(r, 1000));
188
185
  }
189
186
  if (current.status !== 'running') {
190
- await this.client.deleteBox(box.box_id, true).catch(() => { });
191
- this.portMaps.delete(box.box_id);
187
+ await this.client.deleteBox(box.id, true).catch(() => { });
188
+ this.portMaps.delete(box.id);
192
189
  throw new ProviderError('boxlite', new Error(`Sandbox failed to start within ${timeoutSec}s (status: ${current.status})`));
193
190
  }
194
- return wrapBox(current, this.client, this.config, portMap);
191
+ return wrapBox(current, this.client, this.host, portMap);
195
192
  }
196
193
  catch (err) {
197
194
  if (err instanceof ProviderError)
@@ -203,7 +200,7 @@ export class BoxLiteAdapter {
203
200
  try {
204
201
  const box = await this.client.getBox(id);
205
202
  const portMap = this.portMaps.get(id) ?? new Map();
206
- return wrapBox(box, this.client, this.config, portMap);
203
+ return wrapBox(box, this.client, this.host, portMap);
207
204
  }
208
205
  catch (err) {
209
206
  if (isNotFound(err))
@@ -215,7 +212,7 @@ export class BoxLiteAdapter {
215
212
  try {
216
213
  const boxes = await this.client.listBoxes();
217
214
  let infos = boxes.map((b) => ({
218
- id: b.box_id,
215
+ id: b.id,
219
216
  state: mapState(b.status),
220
217
  createdAt: b.created_at,
221
218
  image: b.image,
@@ -244,4 +241,8 @@ export class BoxLiteAdapter {
244
241
  throw new ProviderError('boxlite', err, id);
245
242
  }
246
243
  }
244
+ /** Dispose the adapter and clean up resources (e.g. Python bridge process) */
245
+ async dispose() {
246
+ await this.client.dispose?.();
247
+ }
247
248
  }
package/dist/client.d.ts CHANGED
@@ -1,23 +1,7 @@
1
- import type { BoxLiteAdapterConfig, BoxLiteBox, BoxLiteCreateParams, BoxLiteExecRequest, BoxLiteSnapshot } from './types.js';
2
- export declare function createBoxLiteClient(config: BoxLiteAdapterConfig): {
3
- createBox(params: BoxLiteCreateParams): Promise<BoxLiteBox>;
4
- getBox(boxId: string): Promise<BoxLiteBox>;
5
- listBoxes(status?: string, pageSize?: number): Promise<BoxLiteBox[]>;
6
- deleteBox(boxId: string, force?: boolean): Promise<void>;
7
- startBox(boxId: string): Promise<void>;
8
- stopBox(boxId: string): Promise<void>;
9
- exec(boxId: string, req: BoxLiteExecRequest): Promise<{
10
- stdout: string;
11
- stderr: string;
12
- exitCode: number;
13
- }>;
14
- execStream(boxId: string, req: BoxLiteExecRequest): Promise<ReadableStream<Uint8Array>>;
15
- uploadFiles(boxId: string, path: string, tarData: Uint8Array): Promise<void>;
16
- downloadFiles(boxId: string, path: string): Promise<ReadableStream<Uint8Array>>;
17
- createSnapshot(boxId: string, name: string): Promise<BoxLiteSnapshot>;
18
- restoreSnapshot(boxId: string, name: string): Promise<void>;
19
- listSnapshots(boxId: string): Promise<BoxLiteSnapshot[]>;
20
- deleteSnapshot(boxId: string, name: string): Promise<void>;
21
- };
22
- export type BoxLiteClient = ReturnType<typeof createBoxLiteClient>;
1
+ import type { BoxLiteClient, BoxLiteRemoteConfig } from './types.js';
2
+ /**
3
+ * Create a BoxLite REST client for communicating with a BoxRun REST API.
4
+ * Used in remote mode.
5
+ */
6
+ export declare function createBoxLiteRestClient(config: BoxLiteRemoteConfig): BoxLiteClient;
23
7
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAElB,eAAe,EAEhB,MAAM,YAAY,CAAA;AAEnB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB;sBA4HpC,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;kBAO7C,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;uBAIvB,MAAM,aAAa,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;qBASnD,MAAM,oBAAkB,OAAO,CAAC,IAAI,CAAC;oBAMtC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;mBAIvB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gBAOlC,MAAM,OACR,kBAAkB,GACtB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;sBAwBvD,MAAM,OACR,kBAAkB,GACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;uBAuEb,MAAM,QAAQ,MAAM,WAAW,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;yBAkBvD,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;0BAwBzD,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;2BAO9C,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;yBAMtC,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;0BAIlC,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAMnE;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EAIb,mBAAmB,EAGpB,MAAM,YAAY,CAAA;AAEnB;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAgQlF"}