@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.
@@ -0,0 +1,509 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { tmpdir } from 'node:os';
4
+ import { writeFileSync, unlinkSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ // ─── Python bridge script (embedded) ──────────────────────────────────────────
7
+ const BRIDGE_SCRIPT = `#!/usr/bin/env python3
8
+ """boxlite_bridge.py — JSON-line bridge between TypeScript and boxlite Python SDK.
9
+
10
+ Protocol:
11
+ → stdin: one JSON object per line {id, action, ...params}
12
+ ← stdout: one JSON object per line {id, result} | {id, error}
13
+ First output line: {ready: true, version: "..."}
14
+ """
15
+ import asyncio, json, sys, os, traceback
16
+ from datetime import datetime, timezone
17
+
18
+ try:
19
+ import boxlite
20
+ except ImportError:
21
+ sys.stdout.write(json.dumps({
22
+ "ready": False,
23
+ "error": "boxlite Python package not found. Install with: pip install boxlite"
24
+ }) + "\\n")
25
+ sys.stdout.flush()
26
+ sys.exit(1)
27
+
28
+
29
+ class Bridge:
30
+ def __init__(self, home=None):
31
+ self._home = home or os.environ.get("BOXLITE_HOME", os.path.expanduser("~/.boxlite"))
32
+ self._runtime = None
33
+ self._boxes = {} # box_id -> box object
34
+ self._simple_boxes = {} # box_id -> SimpleBox (for cleanup)
35
+
36
+ async def _ensure_runtime(self):
37
+ if self._runtime is not None:
38
+ return
39
+
40
+ # Try BoxliteRuntime (full API)
41
+ for attr in ("BoxliteRuntime", "Runtime", "runtime"):
42
+ cls = getattr(boxlite, attr, None)
43
+ if cls is None:
44
+ continue
45
+ try:
46
+ rt = cls(home=self._home) if callable(cls) else cls
47
+ if asyncio.iscoroutinefunction(getattr(rt, "start", None)):
48
+ await rt.start()
49
+ self._runtime = rt
50
+ return
51
+ except Exception:
52
+ continue
53
+
54
+ # Fallback: no runtime, use SimpleBox per box
55
+ self._runtime = "simple_box"
56
+
57
+ async def create(self, params):
58
+ await self._ensure_runtime()
59
+
60
+ image = params["image"]
61
+ kwargs = {"image": image}
62
+ for k in ("cpu", "memory_mb", "disk_size_gb", "env", "working_dir"):
63
+ if params.get(k) is not None:
64
+ kwargs[k] = params[k]
65
+
66
+ now = datetime.now(timezone.utc).isoformat()
67
+
68
+ if self._runtime == "simple_box":
69
+ sb = boxlite.SimpleBox(**kwargs)
70
+ box = await sb.__aenter__()
71
+ box_id = str(getattr(box, "id", None)
72
+ or getattr(getattr(box, "_box", None), "id", None)
73
+ or id(box))
74
+ self._boxes[box_id] = box
75
+ self._simple_boxes[box_id] = sb
76
+ else:
77
+ # Try runtime.create / runtime.create_box
78
+ create_fn = getattr(self._runtime, "create", None) or getattr(self._runtime, "create_box", None)
79
+ if create_fn is None:
80
+ raise RuntimeError("boxlite runtime has no create/create_box method")
81
+ box = await create_fn(**kwargs)
82
+ box_id = str(box.id)
83
+ self._boxes[box_id] = box
84
+
85
+ return {
86
+ "id": box_id,
87
+ "status": "running",
88
+ "image": image,
89
+ "cpu": kwargs.get("cpu", 1),
90
+ "memory_mb": kwargs.get("memory_mb", 512),
91
+ "created_at": now,
92
+ "name": None,
93
+ }
94
+
95
+ async def get(self, box_id):
96
+ box = self._boxes.get(box_id)
97
+ if box is None:
98
+ raise ValueError(f"Box not found: {box_id}")
99
+ return {
100
+ "id": box_id,
101
+ "status": str(getattr(box, "status", "running")),
102
+ "image": getattr(box, "image", "unknown"),
103
+ "cpu": getattr(box, "cpu", 1),
104
+ "memory_mb": getattr(box, "memory_mb", 512),
105
+ "created_at": str(getattr(box, "created_at", "")),
106
+ "name": getattr(box, "name", None),
107
+ }
108
+
109
+ async def list_boxes(self):
110
+ results = []
111
+ for box_id, box in self._boxes.items():
112
+ results.append({
113
+ "id": box_id,
114
+ "status": str(getattr(box, "status", "running")),
115
+ "image": getattr(box, "image", "unknown"),
116
+ "cpu": getattr(box, "cpu", 1),
117
+ "memory_mb": getattr(box, "memory_mb", 512),
118
+ "created_at": str(getattr(box, "created_at", "")),
119
+ "name": getattr(box, "name", None),
120
+ })
121
+ return results
122
+
123
+ async def exec_cmd(self, box_id, cmd, **kwargs):
124
+ box = self._boxes.get(box_id)
125
+ if box is None:
126
+ raise ValueError(f"Box not found: {box_id}")
127
+
128
+ result = None
129
+ errors = []
130
+
131
+ # Strategy 1: box.exec(*cmd)
132
+ try:
133
+ result = await box.exec(*cmd)
134
+ except Exception as e:
135
+ errors.append(f"box.exec(*cmd): {e}")
136
+
137
+ # Strategy 2: box.exec(cmd[0], args=cmd[1:])
138
+ if result is None:
139
+ try:
140
+ result = await box.exec(cmd[0], args=cmd[1:])
141
+ except Exception as e:
142
+ errors.append(f"box.exec(cmd[0], args=...): {e}")
143
+
144
+ # Strategy 3: box._box.exec(cmd[0], args=cmd[1:])
145
+ if result is None and hasattr(box, "_box"):
146
+ try:
147
+ exec_obj = await box._box.exec(cmd[0], args=cmd[1:])
148
+ if hasattr(exec_obj, "wait"):
149
+ result = await exec_obj.wait()
150
+ else:
151
+ result = exec_obj
152
+ except Exception as e:
153
+ errors.append(f"box._box.exec(...): {e}")
154
+
155
+ if result is None:
156
+ raise RuntimeError(f"All exec strategies failed: {'; '.join(errors)}")
157
+
158
+ return {
159
+ "stdout": str(getattr(result, "stdout", "") or ""),
160
+ "stderr": str(getattr(result, "stderr", "") or ""),
161
+ "exit_code": int(getattr(result, "exit_code",
162
+ getattr(result, "returncode", 0)) or 0),
163
+ }
164
+
165
+ async def destroy(self, box_id):
166
+ box = self._boxes.pop(box_id, None)
167
+ sb = self._simple_boxes.pop(box_id, None)
168
+
169
+ if sb is not None:
170
+ try:
171
+ await sb.__aexit__(None, None, None)
172
+ except Exception:
173
+ pass
174
+ return
175
+
176
+ if box is not None and self._runtime != "simple_box":
177
+ for method_name in ("destroy", "delete", "remove"):
178
+ fn = getattr(self._runtime, method_name, None)
179
+ if fn is not None:
180
+ try:
181
+ await fn(box_id)
182
+ return
183
+ except Exception:
184
+ continue
185
+ if hasattr(box, "destroy"):
186
+ await box.destroy()
187
+ elif hasattr(box, "stop"):
188
+ await box.stop()
189
+
190
+ async def stop(self, box_id):
191
+ box = self._boxes.get(box_id)
192
+ if box and hasattr(box, "stop"):
193
+ await box.stop()
194
+
195
+ async def start(self, box_id):
196
+ box = self._boxes.get(box_id)
197
+ if box and hasattr(box, "start"):
198
+ await box.start()
199
+
200
+ async def cleanup(self):
201
+ for box_id in list(self._boxes.keys()):
202
+ try:
203
+ await self.destroy(box_id)
204
+ except Exception:
205
+ pass
206
+
207
+
208
+ def write_json(obj):
209
+ sys.stdout.write(json.dumps(obj) + "\\n")
210
+ sys.stdout.flush()
211
+
212
+
213
+ async def main():
214
+ home = os.environ.get("BOXLITE_BRIDGE_HOME")
215
+ bridge = Bridge(home=home)
216
+ loop = asyncio.get_running_loop()
217
+
218
+ write_json({"ready": True, "version": getattr(boxlite, "__version__", "unknown")})
219
+
220
+ while True:
221
+ line = await loop.run_in_executor(None, sys.stdin.readline)
222
+ if not line:
223
+ break
224
+ line = line.strip()
225
+ if not line:
226
+ continue
227
+
228
+ req_id = 0
229
+ try:
230
+ cmd = json.loads(line)
231
+ req_id = cmd.get("id", 0)
232
+ action = cmd.get("action", "")
233
+
234
+ if action == "create":
235
+ result = await bridge.create(cmd)
236
+ elif action == "get":
237
+ result = await bridge.get(cmd["box_id"])
238
+ elif action == "list":
239
+ result = await bridge.list_boxes()
240
+ elif action == "exec":
241
+ result = await bridge.exec_cmd(cmd["box_id"], cmd["cmd"])
242
+ elif action == "destroy":
243
+ await bridge.destroy(cmd["box_id"])
244
+ result = {}
245
+ elif action == "start":
246
+ await bridge.start(cmd["box_id"])
247
+ result = {}
248
+ elif action == "stop":
249
+ await bridge.stop(cmd["box_id"])
250
+ result = {}
251
+ elif action == "ping":
252
+ result = {"pong": True}
253
+ else:
254
+ raise ValueError(f"Unknown action: {action}")
255
+
256
+ write_json({"id": req_id, "result": result})
257
+ except Exception as e:
258
+ write_json({"id": req_id, "error": f"{type(e).__name__}: {e}"})
259
+
260
+ await bridge.cleanup()
261
+
262
+
263
+ if __name__ == "__main__":
264
+ asyncio.run(main())
265
+ `;
266
+ /**
267
+ * Create a BoxLite local client that communicates with the boxlite Python SDK
268
+ * via a JSON-line subprocess bridge.
269
+ */
270
+ export function createBoxLiteLocalClient(config) {
271
+ const pythonPath = config.pythonPath ?? 'python3';
272
+ const boxliteHome = config.boxliteHome;
273
+ let process = null;
274
+ let readline = null;
275
+ let requestId = 0;
276
+ let readyPromise = null;
277
+ const pending = new Map();
278
+ // Write bridge script to a temp file
279
+ let bridgeScriptPath = null;
280
+ function getBridgeScriptPath() {
281
+ if (bridgeScriptPath)
282
+ return bridgeScriptPath;
283
+ bridgeScriptPath = join(tmpdir(), `boxlite-bridge-${process?.pid ?? Date.now()}.py`);
284
+ writeFileSync(bridgeScriptPath, BRIDGE_SCRIPT, 'utf-8');
285
+ return bridgeScriptPath;
286
+ }
287
+ function ensureBridge() {
288
+ if (readyPromise)
289
+ return readyPromise;
290
+ readyPromise = new Promise((resolveReady, rejectReady) => {
291
+ const scriptPath = getBridgeScriptPath();
292
+ const env = { ...globalThis.process.env };
293
+ if (boxliteHome) {
294
+ env['BOXLITE_BRIDGE_HOME'] = boxliteHome;
295
+ }
296
+ process = spawn(pythonPath, [scriptPath], {
297
+ stdio: ['pipe', 'pipe', 'pipe'],
298
+ env,
299
+ });
300
+ // Collect stderr for error reporting
301
+ let stderrBuf = '';
302
+ process.stderr?.on('data', (chunk) => {
303
+ stderrBuf += chunk.toString();
304
+ });
305
+ process.on('error', (err) => {
306
+ rejectReady(new Error(`Failed to start boxlite bridge: ${err.message}`));
307
+ cleanup();
308
+ });
309
+ process.on('exit', (code) => {
310
+ if (code !== 0 && code !== null) {
311
+ const msg = stderrBuf || `Bridge exited with code ${code}`;
312
+ rejectReady(new Error(`BoxLite bridge error: ${msg}`));
313
+ // Reject all pending requests
314
+ for (const [id, req] of pending) {
315
+ req.reject(new Error(`BoxLite bridge exited unexpectedly: ${msg}`));
316
+ clearTimeout(req.timer);
317
+ pending.delete(id);
318
+ }
319
+ }
320
+ cleanup();
321
+ });
322
+ readline = createInterface({ input: process.stdout });
323
+ let gotReady = false;
324
+ readline.on('line', (line) => {
325
+ let msg;
326
+ try {
327
+ msg = JSON.parse(line);
328
+ }
329
+ catch {
330
+ return; // Ignore non-JSON output
331
+ }
332
+ // Handle ready signal
333
+ if (!gotReady && 'ready' in msg) {
334
+ gotReady = true;
335
+ if (msg.ready) {
336
+ resolveReady();
337
+ }
338
+ else {
339
+ rejectReady(new Error(`BoxLite bridge init failed: ${msg.error ?? 'unknown error'}`));
340
+ }
341
+ return;
342
+ }
343
+ // Handle response to a request
344
+ const id = msg.id;
345
+ if (id === undefined)
346
+ return;
347
+ const req = pending.get(id);
348
+ if (!req)
349
+ return;
350
+ pending.delete(id);
351
+ clearTimeout(req.timer);
352
+ if (msg.error) {
353
+ req.reject(new Error(`BoxLite local: ${msg.error}`));
354
+ }
355
+ else {
356
+ req.resolve(msg.result);
357
+ }
358
+ });
359
+ });
360
+ return readyPromise;
361
+ }
362
+ function cleanup() {
363
+ if (bridgeScriptPath) {
364
+ try {
365
+ unlinkSync(bridgeScriptPath);
366
+ }
367
+ catch { /* ignore */ }
368
+ bridgeScriptPath = null;
369
+ }
370
+ readline?.close();
371
+ readline = null;
372
+ process = null;
373
+ readyPromise = null;
374
+ }
375
+ async function send(command, timeoutMs = 300_000) {
376
+ await ensureBridge();
377
+ if (!process?.stdin?.writable) {
378
+ throw new Error('BoxLite bridge is not running');
379
+ }
380
+ const id = ++requestId;
381
+ return new Promise((resolve, reject) => {
382
+ const timer = setTimeout(() => {
383
+ pending.delete(id);
384
+ reject(new Error(`BoxLite bridge request timed out after ${timeoutMs}ms`));
385
+ }, timeoutMs);
386
+ pending.set(id, {
387
+ resolve: resolve,
388
+ reject,
389
+ timer,
390
+ });
391
+ process.stdin.write(JSON.stringify({ id, ...command }) + '\n');
392
+ });
393
+ }
394
+ // ─── BoxLiteClient implementation ───
395
+ return {
396
+ async createBox(params) {
397
+ return send({ action: 'create', ...params });
398
+ },
399
+ async getBox(boxId) {
400
+ return send({ action: 'get', box_id: boxId });
401
+ },
402
+ async listBoxes() {
403
+ return send({ action: 'list' });
404
+ },
405
+ async deleteBox(boxId) {
406
+ await send({ action: 'destroy', box_id: boxId });
407
+ },
408
+ async startBox(boxId) {
409
+ await send({ action: 'start', box_id: boxId });
410
+ },
411
+ async stopBox(boxId) {
412
+ await send({ action: 'stop', box_id: boxId });
413
+ },
414
+ async exec(boxId, req) {
415
+ const timeoutMs = (req.timeout_seconds ?? 300) * 1000;
416
+ const result = await send({ action: 'exec', box_id: boxId, cmd: req.cmd }, timeoutMs);
417
+ return {
418
+ stdout: result.stdout ?? '',
419
+ stderr: result.stderr ?? '',
420
+ exitCode: result.exit_code ?? 0,
421
+ };
422
+ },
423
+ async execStream(boxId, req) {
424
+ const result = await this.exec(boxId, req);
425
+ const encoder = new TextEncoder();
426
+ return new ReadableStream({
427
+ start(controller) {
428
+ if (result.stdout)
429
+ controller.enqueue(encoder.encode(result.stdout));
430
+ if (result.stderr)
431
+ controller.enqueue(encoder.encode(result.stderr));
432
+ controller.close();
433
+ },
434
+ });
435
+ },
436
+ async uploadFiles(boxId, path, tarData) {
437
+ // Pipe tar data through exec: base64 decode → tar extract
438
+ const b64 = Buffer.from(tarData).toString('base64');
439
+ // Split into chunks to avoid shell argument limit
440
+ const chunkSize = 50_000;
441
+ const chunks = [];
442
+ for (let i = 0; i < b64.length; i += chunkSize) {
443
+ chunks.push(b64.slice(i, i + chunkSize));
444
+ }
445
+ if (chunks.length === 1) {
446
+ await this.exec(boxId, {
447
+ cmd: ['bash', '-c', `echo '${chunks[0]}' | base64 -d | tar xf - -C '${path}'`],
448
+ });
449
+ }
450
+ else {
451
+ // Write base64 to a temp file in chunks, then decode
452
+ const tmpFile = `/tmp/.boxlite-upload-${Date.now()}`;
453
+ for (const chunk of chunks) {
454
+ await this.exec(boxId, {
455
+ cmd: ['bash', '-c', `printf '%s' '${chunk}' >> ${tmpFile}`],
456
+ });
457
+ }
458
+ await this.exec(boxId, {
459
+ cmd: ['bash', '-c', `base64 -d ${tmpFile} | tar xf - -C '${path}' && rm -f ${tmpFile}`],
460
+ });
461
+ }
462
+ },
463
+ async downloadFiles(boxId, path) {
464
+ const result = await this.exec(boxId, {
465
+ cmd: ['bash', '-c', `tar cf - -C '${path}' . 2>/dev/null | base64`],
466
+ });
467
+ const data = Buffer.from(result.stdout.trim(), 'base64');
468
+ return new ReadableStream({
469
+ start(controller) {
470
+ controller.enqueue(new Uint8Array(data));
471
+ controller.close();
472
+ },
473
+ });
474
+ },
475
+ async createSnapshot(boxId, name) {
476
+ throw new Error('Snapshots are not yet supported in local mode');
477
+ },
478
+ async restoreSnapshot(boxId, name) {
479
+ throw new Error('Snapshots are not yet supported in local mode');
480
+ },
481
+ async listSnapshots(boxId) {
482
+ throw new Error('Snapshots are not yet supported in local mode');
483
+ },
484
+ async deleteSnapshot(boxId, name) {
485
+ throw new Error('Snapshots are not yet supported in local mode');
486
+ },
487
+ async dispose() {
488
+ if (process?.stdin?.writable) {
489
+ process.stdin.end();
490
+ }
491
+ // Give the bridge a moment to cleanup
492
+ await new Promise(resolve => {
493
+ if (!process) {
494
+ resolve();
495
+ return;
496
+ }
497
+ const timeout = setTimeout(() => {
498
+ process?.kill();
499
+ resolve();
500
+ }, 3000);
501
+ process.on('exit', () => {
502
+ clearTimeout(timeout);
503
+ resolve();
504
+ });
505
+ });
506
+ cleanup();
507
+ },
508
+ };
509
+ }
package/dist/types.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- /** BoxLite adapter configuration */
2
- export interface BoxLiteAdapterConfig {
3
- /** BoxLite API base URL, e.g. 'http://localhost:8080' */
1
+ /** Remote mode: connect to a BoxRun REST API */
2
+ export interface BoxLiteRemoteConfig {
3
+ mode?: 'remote';
4
+ /** BoxRun REST API base URL, e.g. 'http://localhost:8090' */
4
5
  apiUrl: string;
5
- /** Multi-tenant prefix, defaults to 'default' */
6
+ /** Multi-tenant prefix (e.g. 'default') */
6
7
  prefix?: string;
7
8
  /** Bearer token (if already obtained) */
8
9
  apiToken?: string;
@@ -11,27 +12,73 @@ export interface BoxLiteAdapterConfig {
11
12
  /** OAuth2 client secret (for automatic token acquisition) */
12
13
  clientSecret?: string;
13
14
  }
15
+ /** Local mode: use boxlite Python SDK directly on this machine */
16
+ export interface BoxLiteLocalConfig {
17
+ mode: 'local';
18
+ /** Path to Python 3.10+ interpreter (default: 'python3') */
19
+ pythonPath?: string;
20
+ /** BoxLite home directory (default: '~/.boxlite') */
21
+ boxliteHome?: string;
22
+ }
23
+ /** BoxLite adapter configuration — remote (BoxRun REST API) or local (Python SDK) */
24
+ export type BoxLiteAdapterConfig = BoxLiteRemoteConfig | BoxLiteLocalConfig;
25
+ export interface BoxLiteClient {
26
+ createBox(params: BoxLiteCreateParams): Promise<BoxLiteBox>;
27
+ getBox(boxId: string): Promise<BoxLiteBox>;
28
+ listBoxes(status?: string, pageSize?: number): Promise<BoxLiteBox[]>;
29
+ deleteBox(boxId: string, force?: boolean): Promise<void>;
30
+ startBox(boxId: string): Promise<void>;
31
+ stopBox(boxId: string): Promise<void>;
32
+ exec(boxId: string, req: BoxLiteExecRequest): Promise<{
33
+ stdout: string;
34
+ stderr: string;
35
+ exitCode: number;
36
+ }>;
37
+ execStream(boxId: string, req: BoxLiteExecRequest): Promise<ReadableStream<Uint8Array>>;
38
+ uploadFiles(boxId: string, path: string, tarData: Uint8Array): Promise<void>;
39
+ downloadFiles(boxId: string, path: string): Promise<ReadableStream<Uint8Array>>;
40
+ createSnapshot(boxId: string, name: string): Promise<BoxLiteSnapshot>;
41
+ restoreSnapshot(boxId: string, name: string): Promise<void>;
42
+ listSnapshots(boxId: string): Promise<BoxLiteSnapshot[]>;
43
+ deleteSnapshot(boxId: string, name: string): Promise<void>;
44
+ /** Dispose of the client (cleanup subprocess, etc.) */
45
+ dispose?(): Promise<void>;
46
+ }
14
47
  export interface BoxLiteBox {
15
- box_id: string;
48
+ id: string;
49
+ boxlite_id?: string;
16
50
  name: string | null;
17
51
  status: BoxStatus;
18
52
  created_at: string;
19
- updated_at?: string;
53
+ started_at?: string | null;
54
+ stopped_at?: string | null;
20
55
  image: string;
21
- cpus: number;
22
- memory_mib: number;
56
+ cpu: number;
57
+ memory_mb: number;
58
+ disk_size_gb?: number;
59
+ workdir?: string;
60
+ env?: Record<string, string> | null;
61
+ network?: boolean;
62
+ error_code?: string | null;
63
+ error_message?: string | null;
64
+ volumes?: unknown;
23
65
  }
24
66
  export type BoxStatus = 'configured' | 'running' | 'stopping' | 'stopped' | 'paused' | 'unknown';
25
67
  export interface BoxLiteExecRequest {
26
- command: string;
27
- args?: string[];
68
+ cmd: string[];
28
69
  env?: Record<string, string>;
29
70
  timeout_seconds?: number;
30
71
  working_dir?: string;
31
72
  tty?: boolean;
32
73
  }
33
74
  export interface BoxLiteExecution {
34
- execution_id: string;
75
+ id: string;
76
+ box_id?: string;
77
+ cmd?: string[];
78
+ status: string;
79
+ exit_code: number | null;
80
+ stdout?: string;
81
+ stderr?: string;
35
82
  }
36
83
  export interface BoxLiteSnapshot {
37
84
  id: string;
@@ -45,8 +92,8 @@ export interface BoxLiteSnapshot {
45
92
  export interface BoxLiteCreateParams {
46
93
  image: string;
47
94
  name?: string;
48
- cpus?: number;
49
- memory_mib?: number;
95
+ cpu?: number;
96
+ memory_mb?: number;
50
97
  disk_size_gb?: number;
51
98
  working_dir?: string;
52
99
  env?: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAID,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,SAAS,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,SAAS,GACjB,YAAY,GACZ,SAAS,GACT,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,CAAA;AAEb,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1 +1,2 @@
1
+ // --- Adapter configuration (discriminated union) ---
1
2
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandbank.dev/boxlite",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "BoxLite bare-metal sandbox adapter for Sandbank",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,23 +10,32 @@
10
10
  "url": "https://github.com/chekusu/sandbank.git",
11
11
  "directory": "packages/boxlite"
12
12
  },
13
- "keywords": ["sandbox", "ai-agent", "boxlite", "bare-metal", "kvm"],
13
+ "keywords": [
14
+ "sandbox",
15
+ "ai-agent",
16
+ "boxlite",
17
+ "bare-metal",
18
+ "kvm"
19
+ ],
14
20
  "exports": {
15
21
  ".": {
16
22
  "types": "./dist/index.d.ts",
17
23
  "import": "./dist/index.js"
18
24
  }
19
25
  },
20
- "files": ["dist"],
26
+ "files": [
27
+ "dist"
28
+ ],
21
29
  "scripts": {
22
30
  "build": "tsc",
23
31
  "typecheck": "tsc --noEmit",
24
32
  "clean": "rm -rf dist"
25
33
  },
26
34
  "dependencies": {
27
- "@sandbank.dev/core": "^0.1.1"
35
+ "@sandbank.dev/core": "^0.2.0"
28
36
  },
29
37
  "devDependencies": {
38
+ "@types/node": "^25.3.0",
30
39
  "typescript": "^5.7.3"
31
40
  }
32
41
  }