@douglas-agent/sandbank-boxlite 0.6.1
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 +120 -0
- package/dist/adapter.d.ts +19 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +259 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +294 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/local-client.d.ts +7 -0
- package/dist/local-client.d.ts.map +1 -0
- package/dist/local-client.js +863 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# @douglas-agent/sandbank-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 @douglas-agent/sandbank-core @douglas-agent/sandbank-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 '@douglas-agent/sandbank-core'
|
|
28
|
+
import { BoxLiteAdapter } from '@douglas-agent/sandbank-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 '@douglas-agent/sandbank-core'
|
|
51
|
+
import { BoxLiteAdapter } from '@douglas-agent/sandbank-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
|
+
### Local OCI rootfs (skip registry pull)
|
|
67
|
+
|
|
68
|
+
If `image` is an absolute path, it is treated as a local OCI layout directory:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// Export image: skopeo copy docker-daemon:myimage:latest oci:~/.boxlite/myimage-oci:latest
|
|
72
|
+
const sandbox = await provider.create({ image: '/Users/you/.boxlite/myimage-oci' })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### OAuth2 authentication (remote mode)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
new BoxLiteAdapter({
|
|
79
|
+
apiUrl: 'http://boxrun.example.com:9090',
|
|
80
|
+
clientId: process.env.BOXLITE_CLIENT_ID,
|
|
81
|
+
clientSecret: process.env.BOXLITE_CLIENT_SECRET,
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Capabilities
|
|
86
|
+
|
|
87
|
+
| Capability | Remote | Local |
|
|
88
|
+
|------------|:------:|:-----:|
|
|
89
|
+
| `exec.stream` | ✅ | ✅ |
|
|
90
|
+
| `terminal` | ✅ | ✅ |
|
|
91
|
+
| `sleep` | ✅ | ✅ |
|
|
92
|
+
| `port.expose` | ✅ | ✅ |
|
|
93
|
+
| `snapshot` | ✅ | — |
|
|
94
|
+
|
|
95
|
+
## Characteristics
|
|
96
|
+
|
|
97
|
+
- **Runtime:** Micro-VM (libkrun)
|
|
98
|
+
- **Cold start:** ~3-5s
|
|
99
|
+
- **File I/O:** tar archive upload/download
|
|
100
|
+
- **Hypervisor:** Hypervisor.framework (macOS) / KVM (Linux)
|
|
101
|
+
- **Local dependency:** `boxlite` Python package (local mode only)
|
|
102
|
+
|
|
103
|
+
## Architecture
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
┌─────────────────────────────────────┐
|
|
107
|
+
│ BoxLiteAdapter │
|
|
108
|
+
│ mode: 'remote' | 'local' │
|
|
109
|
+
├──────────────┬──────────────────────┤
|
|
110
|
+
│ REST Client │ Local Client │
|
|
111
|
+
│ (fetch) │ (Python subprocess) │
|
|
112
|
+
├──────────────┼──────────────────────┤
|
|
113
|
+
│ BoxRun API │ boxlite Python SDK │
|
|
114
|
+
│ (HTTP/JSON) │ (JSON-line bridge) │
|
|
115
|
+
└──────────────┴──────────────────────┘
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AdapterSandbox, Capability, CreateConfig, ListFilter, SandboxAdapter, SandboxInfo } from '@douglas-agent/sandbank-core';
|
|
2
|
+
import type { BoxLiteAdapterConfig, BoxLiteClient } from './types.js';
|
|
3
|
+
export declare class BoxLiteAdapter implements SandboxAdapter {
|
|
4
|
+
readonly name = "boxlite";
|
|
5
|
+
readonly capabilities: ReadonlySet<Capability>;
|
|
6
|
+
private readonly client;
|
|
7
|
+
private readonly config;
|
|
8
|
+
private readonly host;
|
|
9
|
+
private readonly portMaps;
|
|
10
|
+
constructor(config: BoxLiteAdapterConfig);
|
|
11
|
+
createSandbox(config: CreateConfig): Promise<AdapterSandbox>;
|
|
12
|
+
getSandbox(id: string): Promise<AdapterSandbox>;
|
|
13
|
+
listSandboxes(filter?: ListFilter): Promise<SandboxInfo[]>;
|
|
14
|
+
destroySandbox(id: string): Promise<void>;
|
|
15
|
+
getClient(): BoxLiteClient;
|
|
16
|
+
/** Dispose the adapter and clean up resources (e.g. Python bridge process) */
|
|
17
|
+
dispose(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +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,8BAA8B,CAAA;AAIrC,OAAO,KAAK,EAAE,oBAAoB,EAAc,aAAa,EAAwB,MAAM,YAAY,CAAA;AAoKvG,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;IASlC,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAmD5D,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,SAAS,IAAI,aAAa;IAI1B,8EAA8E;IACxE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { SandboxNotFoundError, ProviderError } from '@douglas-agent/sandbank-core';
|
|
2
|
+
import { createBoxLiteRestClient } from './client.js';
|
|
3
|
+
import { createBoxLiteLocalClient } from './local-client.js';
|
|
4
|
+
/** Map BoxLite box status to Sandbank SandboxState */
|
|
5
|
+
function mapState(status) {
|
|
6
|
+
switch (status) {
|
|
7
|
+
case 'configured':
|
|
8
|
+
return 'creating';
|
|
9
|
+
case 'running':
|
|
10
|
+
return 'running';
|
|
11
|
+
case 'stopping':
|
|
12
|
+
case 'stopped':
|
|
13
|
+
case 'paused':
|
|
14
|
+
return 'stopped';
|
|
15
|
+
default:
|
|
16
|
+
return 'error';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
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';
|
|
23
|
+
try {
|
|
24
|
+
return new URL(config.apiUrl).hostname;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return config.apiUrl;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Wrap a BoxLite box into an AdapterSandbox */
|
|
31
|
+
function wrapBox(box, client, host, portMappings) {
|
|
32
|
+
return {
|
|
33
|
+
get id() { return box.id; },
|
|
34
|
+
get state() { return mapState(box.status); },
|
|
35
|
+
get createdAt() { return box.created_at; },
|
|
36
|
+
async exec(command, options) {
|
|
37
|
+
const result = await client.exec(box.id, {
|
|
38
|
+
cmd: ['bash', '-c', command],
|
|
39
|
+
working_dir: options?.cwd,
|
|
40
|
+
timeout_seconds: options?.timeout ? Math.ceil(options.timeout / 1000) : undefined,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
exitCode: result.exitCode,
|
|
44
|
+
stdout: result.stdout,
|
|
45
|
+
stderr: result.stderr,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
async execStream(command, options) {
|
|
49
|
+
return client.execStream(box.id, {
|
|
50
|
+
cmd: ['bash', '-c', command],
|
|
51
|
+
working_dir: options?.cwd,
|
|
52
|
+
timeout_seconds: options?.timeout ? Math.ceil(options.timeout / 1000) : undefined,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
async uploadArchive(archive, destDir) {
|
|
56
|
+
let data;
|
|
57
|
+
if (archive instanceof Uint8Array) {
|
|
58
|
+
data = archive;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const reader = archive.getReader();
|
|
62
|
+
const chunks = [];
|
|
63
|
+
while (true) {
|
|
64
|
+
const { done, value } = await reader.read();
|
|
65
|
+
if (done)
|
|
66
|
+
break;
|
|
67
|
+
chunks.push(value);
|
|
68
|
+
}
|
|
69
|
+
const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
70
|
+
data = new Uint8Array(totalLength);
|
|
71
|
+
let offset = 0;
|
|
72
|
+
for (const chunk of chunks) {
|
|
73
|
+
data.set(chunk, offset);
|
|
74
|
+
offset += chunk.length;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
await client.uploadFiles(box.id, destDir ?? '/', data);
|
|
78
|
+
},
|
|
79
|
+
async downloadArchive(srcDir) {
|
|
80
|
+
return client.downloadFiles(box.id, srcDir ?? '/');
|
|
81
|
+
},
|
|
82
|
+
async sleep() {
|
|
83
|
+
await client.stopBox(box.id);
|
|
84
|
+
},
|
|
85
|
+
async wake() {
|
|
86
|
+
await client.startBox(box.id);
|
|
87
|
+
},
|
|
88
|
+
async createSnapshot(name) {
|
|
89
|
+
const snapshotName = name ?? `snap-${Date.now()}`;
|
|
90
|
+
await client.createSnapshot(box.id, snapshotName);
|
|
91
|
+
return { snapshotId: snapshotName };
|
|
92
|
+
},
|
|
93
|
+
async restoreSnapshot(snapshotId) {
|
|
94
|
+
await client.restoreSnapshot(box.id, snapshotId);
|
|
95
|
+
},
|
|
96
|
+
async exposePort(port) {
|
|
97
|
+
const hostPort = portMappings.get(port) ?? port;
|
|
98
|
+
return { url: `http://${host}:${hostPort}` };
|
|
99
|
+
},
|
|
100
|
+
async startTerminal(options) {
|
|
101
|
+
const port = 7681;
|
|
102
|
+
const shell = options?.shell ?? '/bin/bash';
|
|
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'] });
|
|
105
|
+
if (check.exitCode !== 0) {
|
|
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"; }`,
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
await client.exec(box.id, {
|
|
116
|
+
cmd: ['chmod', '+x', '/usr/local/bin/ttyd'],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
await client.exec(box.id, {
|
|
120
|
+
cmd: ['bash', '-c', `nohup ttyd -W -p ${port} '${shell.replace(/'/g, "'\\''")}' > /dev/null 2>&1 &`],
|
|
121
|
+
});
|
|
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`],
|
|
124
|
+
});
|
|
125
|
+
const hostPort = portMappings.get(port) ?? port;
|
|
126
|
+
return {
|
|
127
|
+
url: `ws://${host}:${hostPort}/ws`,
|
|
128
|
+
port,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Check if an error is a 404 "not found" */
|
|
134
|
+
function isNotFound(err) {
|
|
135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
136
|
+
return msg.includes('404') || msg.includes('not found') || msg.includes('Not Found');
|
|
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
|
+
}
|
|
145
|
+
export class BoxLiteAdapter {
|
|
146
|
+
name = 'boxlite';
|
|
147
|
+
capabilities;
|
|
148
|
+
client;
|
|
149
|
+
config;
|
|
150
|
+
host;
|
|
151
|
+
portMaps = new Map();
|
|
152
|
+
constructor(config) {
|
|
153
|
+
this.config = config;
|
|
154
|
+
this.host = resolveHost(config);
|
|
155
|
+
this.client = createClient(config);
|
|
156
|
+
const caps = ['exec.stream', 'terminal', 'sleep', 'port.expose', 'snapshot'];
|
|
157
|
+
this.capabilities = new Set(caps);
|
|
158
|
+
}
|
|
159
|
+
async createSandbox(config) {
|
|
160
|
+
try {
|
|
161
|
+
// If image looks like an absolute path, treat it as a local OCI rootfs
|
|
162
|
+
const image = config.image ?? 'ubuntu:24.04';
|
|
163
|
+
const isLocalPath = image.startsWith('/');
|
|
164
|
+
const user = typeof config.user === 'string' ? config.user : config.user?.name;
|
|
165
|
+
const box = await this.client.createBox({
|
|
166
|
+
...(isLocalPath ? { rootfs_path: image } : { image }),
|
|
167
|
+
cpu: config.resources?.cpu,
|
|
168
|
+
memory_mb: config.resources?.memory,
|
|
169
|
+
disk_size_gb: config.resources?.disk,
|
|
170
|
+
env: config.env,
|
|
171
|
+
auto_remove: false,
|
|
172
|
+
ports: config.ports,
|
|
173
|
+
...(user ? { user } : {}),
|
|
174
|
+
});
|
|
175
|
+
const portMap = new Map();
|
|
176
|
+
if (config.ports) {
|
|
177
|
+
for (const [hostPort, guestPort] of config.ports) {
|
|
178
|
+
portMap.set(guestPort, hostPort);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
this.portMaps.set(box.id, portMap);
|
|
182
|
+
if (box.status === 'configured' || box.status === 'stopped') {
|
|
183
|
+
await this.client.startBox(box.id);
|
|
184
|
+
}
|
|
185
|
+
const timeoutSec = config.timeout ?? 30;
|
|
186
|
+
const maxAttempts = Math.max(1, timeoutSec);
|
|
187
|
+
let current = box;
|
|
188
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
189
|
+
current = await this.client.getBox(box.id);
|
|
190
|
+
if (current.status === 'running')
|
|
191
|
+
break;
|
|
192
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
193
|
+
}
|
|
194
|
+
if (current.status !== 'running') {
|
|
195
|
+
await this.client.deleteBox(box.id, true).catch(() => { });
|
|
196
|
+
this.portMaps.delete(box.id);
|
|
197
|
+
throw new ProviderError('boxlite', new Error(`Sandbox failed to start within ${timeoutSec}s (status: ${current.status})`));
|
|
198
|
+
}
|
|
199
|
+
return wrapBox(current, this.client, this.host, portMap);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
if (err instanceof ProviderError)
|
|
203
|
+
throw err;
|
|
204
|
+
throw new ProviderError('boxlite', err);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async getSandbox(id) {
|
|
208
|
+
try {
|
|
209
|
+
const box = await this.client.getBox(id);
|
|
210
|
+
const portMap = this.portMaps.get(id) ?? new Map();
|
|
211
|
+
return wrapBox(box, this.client, this.host, portMap);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
if (isNotFound(err))
|
|
215
|
+
throw new SandboxNotFoundError('boxlite', id);
|
|
216
|
+
throw new ProviderError('boxlite', err, id);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async listSandboxes(filter) {
|
|
220
|
+
try {
|
|
221
|
+
const boxes = await this.client.listBoxes();
|
|
222
|
+
let infos = boxes.map((b) => ({
|
|
223
|
+
id: b.id,
|
|
224
|
+
state: mapState(b.status),
|
|
225
|
+
createdAt: b.created_at,
|
|
226
|
+
image: b.image,
|
|
227
|
+
}));
|
|
228
|
+
if (filter?.state) {
|
|
229
|
+
const states = Array.isArray(filter.state) ? filter.state : [filter.state];
|
|
230
|
+
infos = infos.filter(s => states.includes(s.state));
|
|
231
|
+
}
|
|
232
|
+
if (filter?.limit) {
|
|
233
|
+
infos = infos.slice(0, filter.limit);
|
|
234
|
+
}
|
|
235
|
+
return infos;
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
throw new ProviderError('boxlite', err);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async destroySandbox(id) {
|
|
242
|
+
try {
|
|
243
|
+
await this.client.deleteBox(id, true);
|
|
244
|
+
this.portMaps.delete(id);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (isNotFound(err))
|
|
248
|
+
return;
|
|
249
|
+
throw new ProviderError('boxlite', err, id);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
getClient() {
|
|
253
|
+
return this.client;
|
|
254
|
+
}
|
|
255
|
+
/** Dispose the adapter and clean up resources (e.g. Python bridge process) */
|
|
256
|
+
async dispose() {
|
|
257
|
+
await this.client.dispose?.();
|
|
258
|
+
}
|
|
259
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
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;
|
|
7
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,CA4VlF"}
|