@sandbank.dev/core 0.1.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,65 @@
1
+ # @sandbank.dev/core
2
+
3
+ > Unified sandbox SDK for AI agents — provider abstraction, capability system, and error types.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @sandbank.dev/core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { createProvider, withTerminal, connectTerminal } from '@sandbank.dev/core'
15
+ import { DaytonaAdapter } from '@sandbank.dev/daytona'
16
+
17
+ const provider = createProvider(
18
+ new DaytonaAdapter({ apiKey: process.env.DAYTONA_API_KEY! })
19
+ )
20
+
21
+ // Create and use a sandbox
22
+ const sandbox = await provider.create({ image: 'node:22' })
23
+ const { stdout } = await sandbox.exec('node --version')
24
+ await sandbox.writeFile('/app/index.js', 'console.log("hi")')
25
+
26
+ // Capability detection
27
+ const terminal = withTerminal(sandbox)
28
+ if (terminal) {
29
+ const info = await terminal.startTerminal()
30
+ const session = connectTerminal(info)
31
+ await session.ready
32
+ session.onData((data) => process.stdout.write(data))
33
+ session.write('ls\n')
34
+ }
35
+
36
+ await provider.destroy(sandbox.id)
37
+ ```
38
+
39
+ ## Capabilities
40
+
41
+ Use `hasCapability(provider, name)` to check provider support, and `withTerminal(sandbox)` / `withStreaming(sandbox)` / etc. for type-safe downcasting.
42
+
43
+ | Capability | Description |
44
+ |------------|-------------|
45
+ | `exec.stream` | Real-time stdout/stderr streaming |
46
+ | `terminal` | Interactive web terminal (ttyd) |
47
+ | `sleep` | Hibernate and wake sandboxes |
48
+ | `volumes` | Persistent volume management |
49
+ | `snapshot` | Snapshot and restore sandbox state |
50
+ | `port.expose` | Expose sandbox ports to the internet |
51
+
52
+ ## Multi-Agent Sessions
53
+
54
+ ```typescript
55
+ import { createSession } from '@sandbank.dev/core'
56
+
57
+ const session = await createSession({ provider, relay: { type: 'memory' } })
58
+ const agent = await session.spawn('worker', { image: 'node:22' })
59
+ await session.waitForAll()
60
+ await session.close()
61
+ ```
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,16 @@
1
+ import type { Capability, PortExposeSandbox, Sandbox, SandboxProvider, SleepableSandbox, SnapshotSandbox, StreamableSandbox, TerminalSandbox, VolumeProvider } from './types.js';
2
+ /** 检查 provider 是否支持某个能力 */
3
+ export declare function hasCapability(provider: SandboxProvider, capability: Capability): boolean;
4
+ /** 向下转型为支持流式执行的 Sandbox,不支持则返回 null */
5
+ export declare function withStreaming(sandbox: Sandbox): StreamableSandbox | null;
6
+ /** 向下转型为支持终端的 Sandbox,不支持则返回 null */
7
+ export declare function withTerminal(sandbox: Sandbox): TerminalSandbox | null;
8
+ /** 向下转型为支持休眠的 Sandbox,不支持则返回 null */
9
+ export declare function withSleep(sandbox: Sandbox): SleepableSandbox | null;
10
+ /** 向下转型为支持端口暴露的 Sandbox,不支持则返回 null */
11
+ export declare function withPortExpose(sandbox: Sandbox): PortExposeSandbox | null;
12
+ /** 向下转型为支持快照的 Sandbox,不支持则返回 null */
13
+ export declare function withSnapshot(sandbox: Sandbox): SnapshotSandbox | null;
14
+ /** 向下转型为支持卷管理的 Provider,不支持则返回 null */
15
+ export declare function withVolumes(provider: SandboxProvider): VolumeProvider | null;
16
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAA;AAEnB,2BAA2B;AAC3B,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,eAAe,EACzB,UAAU,EAAE,UAAU,GACrB,OAAO,CAET;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAKxE;AAED,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAKrE;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAKnE;AAED,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAKzE;AAED,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAKrE;AAED,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,GAAG,IAAI,CAK5E"}
@@ -0,0 +1,46 @@
1
+ /** 检查 provider 是否支持某个能力 */
2
+ export function hasCapability(provider, capability) {
3
+ return provider.capabilities.has(capability);
4
+ }
5
+ /** 向下转型为支持流式执行的 Sandbox,不支持则返回 null */
6
+ export function withStreaming(sandbox) {
7
+ if ('execStream' in sandbox && typeof sandbox.execStream === 'function') {
8
+ return sandbox;
9
+ }
10
+ return null;
11
+ }
12
+ /** 向下转型为支持终端的 Sandbox,不支持则返回 null */
13
+ export function withTerminal(sandbox) {
14
+ if ('startTerminal' in sandbox && typeof sandbox.startTerminal === 'function') {
15
+ return sandbox;
16
+ }
17
+ return null;
18
+ }
19
+ /** 向下转型为支持休眠的 Sandbox,不支持则返回 null */
20
+ export function withSleep(sandbox) {
21
+ if ('sleep' in sandbox && typeof sandbox.sleep === 'function') {
22
+ return sandbox;
23
+ }
24
+ return null;
25
+ }
26
+ /** 向下转型为支持端口暴露的 Sandbox,不支持则返回 null */
27
+ export function withPortExpose(sandbox) {
28
+ if ('exposePort' in sandbox && typeof sandbox.exposePort === 'function') {
29
+ return sandbox;
30
+ }
31
+ return null;
32
+ }
33
+ /** 向下转型为支持快照的 Sandbox,不支持则返回 null */
34
+ export function withSnapshot(sandbox) {
35
+ if ('createSnapshot' in sandbox && typeof sandbox.createSnapshot === 'function') {
36
+ return sandbox;
37
+ }
38
+ return null;
39
+ }
40
+ /** 向下转型为支持卷管理的 Provider,不支持则返回 null */
41
+ export function withVolumes(provider) {
42
+ if ('createVolume' in provider && typeof provider.createVolume === 'function') {
43
+ return provider;
44
+ }
45
+ return null;
46
+ }
@@ -0,0 +1,38 @@
1
+ import type { Capability, SandboxState } from './types.js';
2
+ /** 所有 SDK 错误的基类 */
3
+ export declare class SandboxError extends Error {
4
+ readonly provider: string;
5
+ readonly sandboxId?: string;
6
+ constructor(message: string, provider: string, sandboxId?: string);
7
+ }
8
+ /** 沙箱不存在(已销毁或从未创建) */
9
+ export declare class SandboxNotFoundError extends SandboxError {
10
+ constructor(provider: string, sandboxId: string);
11
+ }
12
+ /** 沙箱状态不对(如在 stopped 状态下 exec) */
13
+ export declare class SandboxStateError extends SandboxError {
14
+ readonly currentState: SandboxState;
15
+ readonly requiredState: SandboxState;
16
+ constructor(provider: string, sandboxId: string, currentState: SandboxState, requiredState: SandboxState);
17
+ }
18
+ /** 命令执行超时 */
19
+ export declare class ExecTimeoutError extends SandboxError {
20
+ readonly timeout: number;
21
+ constructor(provider: string, sandboxId: string, timeout: number);
22
+ }
23
+ /** Provider 返回速率限制 */
24
+ export declare class RateLimitError extends SandboxError {
25
+ readonly retryAfter?: number;
26
+ constructor(provider: string, retryAfter?: number);
27
+ }
28
+ /** Provider 自身错误(500、网络问题等) */
29
+ export declare class ProviderError extends SandboxError {
30
+ readonly cause: unknown;
31
+ constructor(provider: string, cause: unknown, sandboxId?: string);
32
+ }
33
+ /** 不支持的能力 */
34
+ export declare class CapabilityNotSupportedError extends SandboxError {
35
+ readonly capability: Capability;
36
+ constructor(provider: string, capability: Capability);
37
+ }
38
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE1D,mBAAmB;AACnB,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;gBAEf,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAMlE;AAED,sBAAsB;AACtB,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAIhD;AAED,kCAAkC;AAClC,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAA;gBAExB,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY;CAUzG;AAED,aAAa;AACb,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;gBAEZ,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKjE;AAED,sBAAsB;AACtB,qBAAa,cAAe,SAAQ,YAAY;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;gBAEhB,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAUlD;AAED,+BAA+B;AAC/B,qBAAa,aAAc,SAAQ,YAAY;IAC7C,SAAkB,KAAK,EAAE,OAAO,CAAA;gBAEpB,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM;CAMjE;AAED,aAAa;AACb,qBAAa,2BAA4B,SAAQ,YAAY;IAC3D,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;gBAEnB,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;CAKrD"}
package/dist/errors.js ADDED
@@ -0,0 +1,68 @@
1
+ /** 所有 SDK 错误的基类 */
2
+ export class SandboxError extends Error {
3
+ provider;
4
+ sandboxId;
5
+ constructor(message, provider, sandboxId) {
6
+ super(message);
7
+ this.name = 'SandboxError';
8
+ this.provider = provider;
9
+ this.sandboxId = sandboxId;
10
+ }
11
+ }
12
+ /** 沙箱不存在(已销毁或从未创建) */
13
+ export class SandboxNotFoundError extends SandboxError {
14
+ constructor(provider, sandboxId) {
15
+ super(`Sandbox '${sandboxId}' not found`, provider, sandboxId);
16
+ this.name = 'SandboxNotFoundError';
17
+ }
18
+ }
19
+ /** 沙箱状态不对(如在 stopped 状态下 exec) */
20
+ export class SandboxStateError extends SandboxError {
21
+ currentState;
22
+ requiredState;
23
+ constructor(provider, sandboxId, currentState, requiredState) {
24
+ super(`Sandbox '${sandboxId}' is '${currentState}', expected '${requiredState}'`, provider, sandboxId);
25
+ this.name = 'SandboxStateError';
26
+ this.currentState = currentState;
27
+ this.requiredState = requiredState;
28
+ }
29
+ }
30
+ /** 命令执行超时 */
31
+ export class ExecTimeoutError extends SandboxError {
32
+ timeout;
33
+ constructor(provider, sandboxId, timeout) {
34
+ super(`Command timed out after ${timeout}ms`, provider, sandboxId);
35
+ this.name = 'ExecTimeoutError';
36
+ this.timeout = timeout;
37
+ }
38
+ }
39
+ /** Provider 返回速率限制 */
40
+ export class RateLimitError extends SandboxError {
41
+ retryAfter;
42
+ constructor(provider, retryAfter) {
43
+ super(retryAfter
44
+ ? `Rate limited, retry after ${retryAfter}s`
45
+ : 'Rate limited', provider);
46
+ this.name = 'RateLimitError';
47
+ this.retryAfter = retryAfter;
48
+ }
49
+ }
50
+ /** Provider 自身错误(500、网络问题等) */
51
+ export class ProviderError extends SandboxError {
52
+ cause;
53
+ constructor(provider, cause, sandboxId) {
54
+ const message = cause instanceof Error ? cause.message : String(cause);
55
+ super(`Provider error: ${message}`, provider, sandboxId);
56
+ this.name = 'ProviderError';
57
+ this.cause = cause;
58
+ }
59
+ }
60
+ /** 不支持的能力 */
61
+ export class CapabilityNotSupportedError extends SandboxError {
62
+ capability;
63
+ constructor(provider, capability) {
64
+ super(`Capability '${capability}' is not supported by '${provider}'`, provider);
65
+ this.name = 'CapabilityNotSupportedError';
66
+ this.capability = capability;
67
+ }
68
+ }
@@ -0,0 +1,22 @@
1
+ import type { AdapterSandbox } from './types.js';
2
+ /**
3
+ * 基于 exec 的 writeFile 默认实现。
4
+ * 将内容 base64 编码后通过 printf | base64 -d 写入文件。
5
+ */
6
+ export declare function writeFileViaExec(sandbox: AdapterSandbox, path: string, content: string | Uint8Array): Promise<void>;
7
+ /**
8
+ * 基于 exec 的 readFile 默认实现。
9
+ * 通过 base64 编码读取文件内容。
10
+ */
11
+ export declare function readFileViaExec(sandbox: AdapterSandbox, path: string): Promise<Uint8Array>;
12
+ /**
13
+ * 基于 exec 的 uploadArchive 默认实现。
14
+ * 将 tar.gz 数据 base64 编码后传输并解压。
15
+ */
16
+ export declare function uploadArchiveViaExec(sandbox: AdapterSandbox, archive: Uint8Array | ReadableStream, destDir?: string): Promise<void>;
17
+ /**
18
+ * 基于 exec 的 downloadArchive 默认实现。
19
+ * 将指定目录打包为 tar.gz 并通过 base64 传输。
20
+ */
21
+ export declare function downloadArchiveViaExec(sandbox: AdapterSandbox, srcDir?: string): Promise<ReadableStream>;
22
+ //# sourceMappingURL=file-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-helpers.d.ts","sourceRoot":"","sources":["../src/file-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAMhD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,UAAU,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAarB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,UAAU,GAAG,cAAc,EACpC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAsCzB"}
@@ -0,0 +1,134 @@
1
+ function shellEscape(s) {
2
+ return "'" + s.replace(/'/g, "'\\''") + "'";
3
+ }
4
+ /**
5
+ * 基于 exec 的 writeFile 默认实现。
6
+ * 将内容 base64 编码后通过 printf | base64 -d 写入文件。
7
+ */
8
+ export async function writeFileViaExec(sandbox, path, content) {
9
+ const bytes = typeof content === 'string'
10
+ ? new TextEncoder().encode(content)
11
+ : content;
12
+ let binary = '';
13
+ const chunkSize = 8192;
14
+ for (let i = 0; i < bytes.length; i += chunkSize) {
15
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
16
+ }
17
+ const base64 = btoa(binary);
18
+ // 确保目标目录存在
19
+ const dir = path.substring(0, path.lastIndexOf('/'));
20
+ if (dir) {
21
+ await sandbox.exec(`mkdir -p ${shellEscape(dir)}`);
22
+ }
23
+ const result = await sandbox.exec(`printf '%s' ${shellEscape(base64)} | base64 -d > ${shellEscape(path)}`);
24
+ if (result.exitCode !== 0) {
25
+ throw new Error(`writeFile failed: ${result.stderr}`);
26
+ }
27
+ }
28
+ /**
29
+ * 基于 exec 的 readFile 默认实现。
30
+ * 通过 base64 编码读取文件内容。
31
+ */
32
+ export async function readFileViaExec(sandbox, path) {
33
+ const result = await sandbox.exec(`base64 ${shellEscape(path)}`);
34
+ if (result.exitCode !== 0) {
35
+ throw new Error(`readFile failed: ${result.stderr}`);
36
+ }
37
+ const clean = result.stdout.replace(/\s/g, '');
38
+ const binary = atob(clean);
39
+ const bytes = new Uint8Array(binary.length);
40
+ for (let i = 0; i < binary.length; i++) {
41
+ bytes[i] = binary.charCodeAt(i);
42
+ }
43
+ return bytes;
44
+ }
45
+ /**
46
+ * 基于 exec 的 uploadArchive 默认实现。
47
+ * 将 tar.gz 数据 base64 编码后传输并解压。
48
+ */
49
+ export async function uploadArchiveViaExec(sandbox, archive, destDir) {
50
+ // ReadableStream → Uint8Array
51
+ let bytes;
52
+ if (archive instanceof Uint8Array) {
53
+ bytes = archive;
54
+ }
55
+ else {
56
+ const reader = archive.getReader();
57
+ const chunks = [];
58
+ while (true) {
59
+ const { done, value } = await reader.read();
60
+ if (done)
61
+ break;
62
+ chunks.push(value);
63
+ }
64
+ const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
65
+ bytes = new Uint8Array(totalLength);
66
+ let offset = 0;
67
+ for (const chunk of chunks) {
68
+ bytes.set(chunk, offset);
69
+ offset += chunk.length;
70
+ }
71
+ }
72
+ // base64 编码
73
+ let binary = '';
74
+ const chunkSize = 8192;
75
+ for (let i = 0; i < bytes.length; i += chunkSize) {
76
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
77
+ }
78
+ const base64 = btoa(binary);
79
+ const target = destDir ?? '/';
80
+ const tmp = `/tmp/_sb_archive_${Date.now()}_${Math.random().toString(36).slice(2)}.tar.gz`;
81
+ // 写入临时文件
82
+ const writeResult = await sandbox.exec(`printf '%s' ${shellEscape(base64)} | base64 -d > ${tmp}`);
83
+ if (writeResult.exitCode !== 0) {
84
+ throw new Error(`uploadArchive: write failed: ${writeResult.stderr}`);
85
+ }
86
+ // 解压
87
+ const extractResult = await sandbox.exec(`tar xzf ${tmp} -C ${shellEscape(target)}`);
88
+ if (extractResult.exitCode !== 0) {
89
+ await sandbox.exec(`rm -f ${tmp}`);
90
+ throw new Error(`uploadArchive: extract failed: ${extractResult.stderr}`);
91
+ }
92
+ // 清理
93
+ await sandbox.exec(`rm -f ${tmp}`);
94
+ }
95
+ /**
96
+ * 基于 exec 的 downloadArchive 默认实现。
97
+ * 将指定目录打包为 tar.gz 并通过 base64 传输。
98
+ */
99
+ export async function downloadArchiveViaExec(sandbox, srcDir) {
100
+ const source = srcDir ?? '/';
101
+ const tmp = `/tmp/_sb_archive_${Date.now()}_${Math.random().toString(36).slice(2)}.tar.gz`;
102
+ // 打包
103
+ const tarResult = await sandbox.exec(`tar czf ${tmp} -C ${shellEscape(source)} .`);
104
+ if (tarResult.exitCode !== 0) {
105
+ await sandbox.exec(`rm -f ${tmp}`).catch(() => { });
106
+ throw new Error(`downloadArchive: tar failed: ${tarResult.stderr}`);
107
+ }
108
+ let readResult;
109
+ try {
110
+ // 读取 base64
111
+ readResult = await sandbox.exec(`base64 ${tmp}`);
112
+ if (readResult.exitCode !== 0) {
113
+ throw new Error(`downloadArchive: read failed: ${readResult.stderr}`);
114
+ }
115
+ }
116
+ finally {
117
+ // 清理临时文件(无论成功或失败)
118
+ await sandbox.exec(`rm -f ${tmp}`).catch(() => { });
119
+ }
120
+ // 解码为 Uint8Array
121
+ const clean = readResult.stdout.replace(/\s/g, '');
122
+ const binaryStr = atob(clean);
123
+ const bytes = new Uint8Array(binaryStr.length);
124
+ for (let i = 0; i < binaryStr.length; i++) {
125
+ bytes[i] = binaryStr.charCodeAt(i);
126
+ }
127
+ // 包装为 ReadableStream
128
+ return new ReadableStream({
129
+ start(controller) {
130
+ controller.enqueue(bytes);
131
+ controller.close();
132
+ },
133
+ });
134
+ }
@@ -0,0 +1,10 @@
1
+ export type { SandboxProvider, Sandbox, CreateConfig, ExecOptions, ExecResult, SandboxState, SandboxInfo, ListFilter, Capability, StreamableSandbox, TerminalSandbox, TerminalOptions, TerminalInfo, TerminalSession, Disposable, SleepableSandbox, PortExposeSandbox, SnapshotSandbox, VolumeProvider, VolumeConfig, VolumeInfo, SandboxAdapter, AdapterSandbox, SkillDefinition, } from './types.js';
2
+ export { createProvider } from './provider.js';
3
+ export { hasCapability, withStreaming, withTerminal, withSleep, withPortExpose, withSnapshot, withVolumes, } from './capabilities.js';
4
+ export { SandboxError, SandboxNotFoundError, SandboxStateError, ExecTimeoutError, RateLimitError, ProviderError, CapabilityNotSupportedError, } from './errors.js';
5
+ export { connectTerminal } from './terminal.js';
6
+ export { injectSkills } from './skill-inject.js';
7
+ export { writeFileViaExec, readFileViaExec, uploadArchiveViaExec, downloadArchiveViaExec } from './file-helpers.js';
8
+ export type { MessagePriority, SessionMessage, ContextStore, CompletionStatus, SandboxCompletion, SendOptions, Session, RelayConfig, CreateSessionConfig, JsonRpcRequest, JsonRpcResponse, JsonRpcNotification, JsonRpcError, Transport, } from './session-types.js';
9
+ export { createSession } from './session.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAEV,eAAe,EACf,OAAO,EACP,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAEV,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,UAAU,EAEV,cAAc,EACd,cAAc,EAEd,eAAe,GAChB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAG9C,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,2BAA2B,GAC5B,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAGnH,YAAY,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,SAAS,GACV,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ // Provider factory
2
+ export { createProvider } from './provider.js';
3
+ // Capability detection
4
+ export { hasCapability, withStreaming, withTerminal, withSleep, withPortExpose, withSnapshot, withVolumes, } from './capabilities.js';
5
+ // Errors
6
+ export { SandboxError, SandboxNotFoundError, SandboxStateError, ExecTimeoutError, RateLimitError, ProviderError, CapabilityNotSupportedError, } from './errors.js';
7
+ // Terminal session
8
+ export { connectTerminal } from './terminal.js';
9
+ // Skill injection
10
+ export { injectSkills } from './skill-inject.js';
11
+ // File helpers (for adapter authors)
12
+ export { writeFileViaExec, readFileViaExec, uploadArchiveViaExec, downloadArchiveViaExec } from './file-helpers.js';
13
+ // Session factory
14
+ export { createSession } from './session.js';
@@ -0,0 +1,4 @@
1
+ import type { SandboxAdapter, SandboxProvider } from './types.js';
2
+ /** 创建一个 SandboxProvider */
3
+ export declare function createProvider(adapter: SandboxAdapter): SandboxProvider;
4
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAQV,cAAc,EAEd,eAAe,EAMhB,MAAM,YAAY,CAAA;AA0GnB,2BAA2B;AAC3B,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAmDvE"}
@@ -0,0 +1,134 @@
1
+ import { CapabilityNotSupportedError, ProviderError } from './errors.js';
2
+ import { injectSkills } from './skill-inject.js';
3
+ import { readFileViaExec, writeFileViaExec, uploadArchiveViaExec, downloadArchiveViaExec } from './file-helpers.js';
4
+ /**
5
+ * 将 AdapterSandbox 包装为完整的 Sandbox 接口。
6
+ * 自动补充缺失的 writeFile/readFile 默认实现。
7
+ */
8
+ function wrapSandbox(raw, providerName) {
9
+ const sandbox = {
10
+ get id() { return raw.id; },
11
+ get state() { return raw.state; },
12
+ get createdAt() { return raw.createdAt; },
13
+ exec(command, options) {
14
+ return raw.exec(command, options);
15
+ },
16
+ writeFile(path, content) {
17
+ if (raw.writeFile)
18
+ return raw.writeFile(path, content);
19
+ return writeFileViaExec(raw, path, content);
20
+ },
21
+ readFile(path) {
22
+ if (raw.readFile)
23
+ return raw.readFile(path);
24
+ return readFileViaExec(raw, path);
25
+ },
26
+ uploadArchive(archive, destDir) {
27
+ if (raw.uploadArchive)
28
+ return raw.uploadArchive(archive, destDir);
29
+ return uploadArchiveViaExec(raw, archive, destDir);
30
+ },
31
+ downloadArchive(srcDir) {
32
+ if (raw.downloadArchive)
33
+ return raw.downloadArchive(srcDir);
34
+ return downloadArchiveViaExec(raw, srcDir);
35
+ },
36
+ };
37
+ // 转发可选能力方法(只有 adapter 真正实现的才转发)
38
+ if (raw.execStream) {
39
+ sandbox['execStream'] = raw.execStream.bind(raw);
40
+ }
41
+ if (raw.sleep) {
42
+ sandbox['sleep'] = raw.sleep.bind(raw);
43
+ }
44
+ if (raw.wake) {
45
+ sandbox['wake'] = raw.wake.bind(raw);
46
+ }
47
+ if (raw.startTerminal) {
48
+ sandbox['startTerminal'] = (options) => raw.startTerminal(options);
49
+ }
50
+ if (raw.exposePort) {
51
+ sandbox['exposePort'] = (port, options) => raw.exposePort(port, options);
52
+ }
53
+ if (raw.createSnapshot) {
54
+ sandbox['createSnapshot'] = (name) => raw.createSnapshot(name);
55
+ }
56
+ if (raw.restoreSnapshot) {
57
+ sandbox['restoreSnapshot'] = (snapshotId) => raw.restoreSnapshot(snapshotId);
58
+ }
59
+ return sandbox;
60
+ }
61
+ /** 能力到 AdapterSandbox 方法的映射 */
62
+ const CAPABILITY_METHOD_MAP = {
63
+ 'exec.stream': 'execStream',
64
+ 'terminal': 'startTerminal',
65
+ 'sleep': 'sleep',
66
+ 'volumes': '', // checked at adapter level, not sandbox level
67
+ 'snapshot': 'createSnapshot',
68
+ 'port.expose': 'exposePort',
69
+ };
70
+ /**
71
+ * 交叉验证 adapter 声明的能力。
72
+ *
73
+ * 对于 sandbox 级别的能力(exec.stream, terminal, sleep, snapshot, port.expose),
74
+ * 我们无法在不创建沙箱的情况下验证方法是否存在,所以信任 adapter 的声明。
75
+ *
76
+ * 对于 provider 级别的能力(volumes),直接检查 adapter 上的方法。
77
+ */
78
+ function detectCapabilities(adapter) {
79
+ const validated = new Set();
80
+ for (const cap of adapter.capabilities) {
81
+ if (cap === 'volumes') {
82
+ // volumes 是 provider 级别能力,可以直接验证
83
+ if (adapter.createVolume && adapter.deleteVolume && adapter.listVolumes) {
84
+ validated.add(cap);
85
+ }
86
+ }
87
+ else {
88
+ // sandbox 级别能力,信任 adapter 声明
89
+ validated.add(cap);
90
+ }
91
+ }
92
+ return validated;
93
+ }
94
+ /** 创建一个 SandboxProvider */
95
+ export function createProvider(adapter) {
96
+ const capabilities = detectCapabilities(adapter);
97
+ const provider = {
98
+ get name() { return adapter.name; },
99
+ get capabilities() { return capabilities; },
100
+ async create(config) {
101
+ const raw = await adapter.createSandbox(config);
102
+ const sandbox = wrapSandbox(raw, adapter.name);
103
+ if (config.skills?.length) {
104
+ try {
105
+ await injectSkills(sandbox, config.skills);
106
+ }
107
+ catch (err) {
108
+ await adapter.destroySandbox(raw.id).catch(() => { });
109
+ throw err;
110
+ }
111
+ }
112
+ return sandbox;
113
+ },
114
+ async get(id) {
115
+ const raw = await adapter.getSandbox(id);
116
+ return wrapSandbox(raw, adapter.name);
117
+ },
118
+ async list(filter) {
119
+ return adapter.listSandboxes(filter);
120
+ },
121
+ async destroy(id) {
122
+ return adapter.destroySandbox(id);
123
+ },
124
+ };
125
+ // 如果 adapter 支持 volume 操作,扩展为 VolumeProvider
126
+ if (capabilities.has('volumes') && adapter.createVolume && adapter.deleteVolume && adapter.listVolumes) {
127
+ const volumeProvider = provider;
128
+ volumeProvider.createVolume = (config) => adapter.createVolume(config);
129
+ volumeProvider.deleteVolume = (id) => adapter.deleteVolume(id);
130
+ volumeProvider.listVolumes = () => adapter.listVolumes();
131
+ return volumeProvider;
132
+ }
133
+ return provider;
134
+ }