matchlock-sdk 0.1.22

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,57 @@
1
+ # Matchlock TypeScript SDK
2
+
3
+ TypeScript client for [Matchlock](https://github.com/jingkaihe/matchlock), with feature parity across the existing Go and Python SDKs.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js 22+
8
+ - `matchlock` CLI installed and available on `PATH` (or configured with `binaryPath`)
9
+
10
+ ## Install (from source)
11
+
12
+ ```bash
13
+ cd sdk/typescript
14
+ npm install
15
+ npm run build
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```ts
21
+ import { Client, Sandbox } from "matchlock-sdk";
22
+
23
+ const sandbox = new Sandbox("alpine:latest")
24
+ .withCPUs(2)
25
+ .withMemory(1024)
26
+ .allowHost("api.openai.com")
27
+ .addSecret("API_KEY", process.env.API_KEY ?? "", "api.openai.com");
28
+
29
+ const client = new Client();
30
+
31
+ try {
32
+ await client.launch(sandbox);
33
+ const result = await client.exec("echo hello from sandbox");
34
+ console.log(result.stdout);
35
+ } finally {
36
+ await client.close();
37
+ }
38
+ ```
39
+
40
+ ## Highlights
41
+
42
+ - Fluent sandbox builder (`Sandbox`) with network, secrets, mounts, env, VFS hooks, image config
43
+ - JSON-RPC `create`, `exec`, `exec_stream`, `write_file`, `read_file`, `list_files`, `port_forward`, `cancel`, `close`
44
+ - Streaming stdout/stderr via `execStream`
45
+ - Local VFS callbacks (`hook`, `dangerousHook`, `mutateHook`, `actionHook`)
46
+ - Port forwarding API parity (`portForward`, `portForwardWithAddresses`)
47
+ - Lifecycle control (`close`, `remove`, `vmId`)
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ cd sdk/typescript
53
+ npm install
54
+ npm run typecheck
55
+ npm test
56
+ npm run build
57
+ ```
@@ -0,0 +1,37 @@
1
+ import type { CreateOptions, ImageConfig, MountConfig, VFSInterceptionConfig } from "./types";
2
+ export declare function cloneCreateOptions(opts: CreateOptions): CreateOptions;
3
+ export declare class Sandbox {
4
+ private readonly opts;
5
+ constructor(image: string);
6
+ withPrivileged(): Sandbox;
7
+ withCPUs(cpus: number): Sandbox;
8
+ withMemory(mb: number): Sandbox;
9
+ withDiskSize(mb: number): Sandbox;
10
+ withTimeout(seconds: number): Sandbox;
11
+ withWorkspace(path: string): Sandbox;
12
+ withVFSInterception(config: VFSInterceptionConfig): Sandbox;
13
+ withEnv(name: string, value: string): Sandbox;
14
+ withEnvMap(env: Record<string, string>): Sandbox;
15
+ allowHost(...hosts: string[]): Sandbox;
16
+ addHost(host: string, ip: string): Sandbox;
17
+ blockPrivateIPs(): Sandbox;
18
+ withBlockPrivateIPs(enabled: boolean): Sandbox;
19
+ allowPrivateIPs(): Sandbox;
20
+ unsetBlockPrivateIPs(): Sandbox;
21
+ addSecret(name: string, value: string, ...hosts: string[]): Sandbox;
22
+ withDNSServers(...servers: string[]): Sandbox;
23
+ withHostname(hostname: string): Sandbox;
24
+ withNetworkMTU(mtu: number): Sandbox;
25
+ withPortForward(localPort: number, remotePort: number): Sandbox;
26
+ withPortForwardAddresses(...addresses: string[]): Sandbox;
27
+ mount(guestPath: string, config: MountConfig): Sandbox;
28
+ mountHostDir(guestPath: string, hostPath: string): Sandbox;
29
+ mountHostDirReadonly(guestPath: string, hostPath: string): Sandbox;
30
+ mountMemory(guestPath: string): Sandbox;
31
+ mountOverlay(guestPath: string, hostPath: string): Sandbox;
32
+ withUser(user: string): Sandbox;
33
+ withEntrypoint(...entrypoint: string[]): Sandbox;
34
+ withImageConfig(config: ImageConfig | undefined | null): Sandbox;
35
+ options(): CreateOptions;
36
+ }
37
+ export declare function createSandbox(image: string): Sandbox;
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Sandbox = void 0;
4
+ exports.cloneCreateOptions = cloneCreateOptions;
5
+ exports.createSandbox = createSandbox;
6
+ function cloneCreateOptions(opts) {
7
+ return {
8
+ ...opts,
9
+ allowedHosts: opts.allowedHosts ? [...opts.allowedHosts] : undefined,
10
+ addHosts: opts.addHosts ? opts.addHosts.map((x) => ({ ...x })) : undefined,
11
+ mounts: opts.mounts
12
+ ? Object.fromEntries(Object.entries(opts.mounts).map(([k, v]) => [k, { ...v }]))
13
+ : undefined,
14
+ env: opts.env ? { ...opts.env } : undefined,
15
+ secrets: opts.secrets
16
+ ? opts.secrets.map((s) => ({ ...s, hosts: s.hosts ? [...s.hosts] : undefined }))
17
+ : undefined,
18
+ vfsInterception: opts.vfsInterception
19
+ ? {
20
+ emitEvents: opts.vfsInterception.emitEvents,
21
+ rules: opts.vfsInterception.rules
22
+ ? opts.vfsInterception.rules.map((r) => ({ ...r, ops: r.ops ? [...r.ops] : undefined }))
23
+ : undefined,
24
+ }
25
+ : undefined,
26
+ dnsServers: opts.dnsServers ? [...opts.dnsServers] : undefined,
27
+ portForwards: opts.portForwards
28
+ ? opts.portForwards.map((pf) => ({ ...pf }))
29
+ : undefined,
30
+ portForwardAddresses: opts.portForwardAddresses
31
+ ? [...opts.portForwardAddresses]
32
+ : undefined,
33
+ imageConfig: opts.imageConfig
34
+ ? {
35
+ ...opts.imageConfig,
36
+ entrypoint: opts.imageConfig.entrypoint
37
+ ? [...opts.imageConfig.entrypoint]
38
+ : undefined,
39
+ cmd: opts.imageConfig.cmd ? [...opts.imageConfig.cmd] : undefined,
40
+ env: opts.imageConfig.env ? { ...opts.imageConfig.env } : undefined,
41
+ }
42
+ : undefined,
43
+ };
44
+ }
45
+ class Sandbox {
46
+ opts;
47
+ constructor(image) {
48
+ this.opts = { image };
49
+ }
50
+ withPrivileged() {
51
+ this.opts.privileged = true;
52
+ return this;
53
+ }
54
+ withCPUs(cpus) {
55
+ this.opts.cpus = cpus;
56
+ return this;
57
+ }
58
+ withMemory(mb) {
59
+ this.opts.memoryMb = mb;
60
+ return this;
61
+ }
62
+ withDiskSize(mb) {
63
+ this.opts.diskSizeMb = mb;
64
+ return this;
65
+ }
66
+ withTimeout(seconds) {
67
+ this.opts.timeoutSeconds = seconds;
68
+ return this;
69
+ }
70
+ withWorkspace(path) {
71
+ this.opts.workspace = path;
72
+ return this;
73
+ }
74
+ withVFSInterception(config) {
75
+ this.opts.vfsInterception = config;
76
+ return this;
77
+ }
78
+ withEnv(name, value) {
79
+ this.opts.env = this.opts.env ?? {};
80
+ this.opts.env[name] = value;
81
+ return this;
82
+ }
83
+ withEnvMap(env) {
84
+ this.opts.env = this.opts.env ?? {};
85
+ Object.assign(this.opts.env, env);
86
+ return this;
87
+ }
88
+ allowHost(...hosts) {
89
+ this.opts.allowedHosts = this.opts.allowedHosts ?? [];
90
+ this.opts.allowedHosts.push(...hosts);
91
+ return this;
92
+ }
93
+ addHost(host, ip) {
94
+ this.opts.addHosts = this.opts.addHosts ?? [];
95
+ this.opts.addHosts.push({ host, ip });
96
+ return this;
97
+ }
98
+ blockPrivateIPs() {
99
+ return this.withBlockPrivateIPs(true);
100
+ }
101
+ withBlockPrivateIPs(enabled) {
102
+ this.opts.blockPrivateIPs = enabled;
103
+ this.opts.blockPrivateIPsSet = true;
104
+ return this;
105
+ }
106
+ allowPrivateIPs() {
107
+ return this.withBlockPrivateIPs(false);
108
+ }
109
+ unsetBlockPrivateIPs() {
110
+ this.opts.blockPrivateIPs = false;
111
+ this.opts.blockPrivateIPsSet = false;
112
+ return this;
113
+ }
114
+ addSecret(name, value, ...hosts) {
115
+ this.opts.secrets = this.opts.secrets ?? [];
116
+ const secret = { name, value, hosts };
117
+ this.opts.secrets.push(secret);
118
+ return this;
119
+ }
120
+ withDNSServers(...servers) {
121
+ this.opts.dnsServers = this.opts.dnsServers ?? [];
122
+ this.opts.dnsServers.push(...servers);
123
+ return this;
124
+ }
125
+ withHostname(hostname) {
126
+ this.opts.hostname = hostname;
127
+ return this;
128
+ }
129
+ withNetworkMTU(mtu) {
130
+ this.opts.networkMtu = mtu;
131
+ return this;
132
+ }
133
+ withPortForward(localPort, remotePort) {
134
+ this.opts.portForwards = this.opts.portForwards ?? [];
135
+ this.opts.portForwards.push({ localPort, remotePort });
136
+ return this;
137
+ }
138
+ withPortForwardAddresses(...addresses) {
139
+ this.opts.portForwardAddresses = this.opts.portForwardAddresses ?? [];
140
+ this.opts.portForwardAddresses.push(...addresses);
141
+ return this;
142
+ }
143
+ mount(guestPath, config) {
144
+ this.opts.mounts = this.opts.mounts ?? {};
145
+ this.opts.mounts[guestPath] = { ...config };
146
+ return this;
147
+ }
148
+ mountHostDir(guestPath, hostPath) {
149
+ return this.mount(guestPath, { type: "host_fs", hostPath });
150
+ }
151
+ mountHostDirReadonly(guestPath, hostPath) {
152
+ return this.mount(guestPath, { type: "host_fs", hostPath, readonly: true });
153
+ }
154
+ mountMemory(guestPath) {
155
+ return this.mount(guestPath, { type: "memory" });
156
+ }
157
+ mountOverlay(guestPath, hostPath) {
158
+ return this.mount(guestPath, { type: "overlay", hostPath });
159
+ }
160
+ withUser(user) {
161
+ this.opts.imageConfig = this.opts.imageConfig ?? {};
162
+ this.opts.imageConfig.user = user;
163
+ return this;
164
+ }
165
+ withEntrypoint(...entrypoint) {
166
+ this.opts.imageConfig = this.opts.imageConfig ?? {};
167
+ this.opts.imageConfig.entrypoint = [...entrypoint];
168
+ return this;
169
+ }
170
+ withImageConfig(config) {
171
+ if (!config) {
172
+ return this;
173
+ }
174
+ this.opts.imageConfig = this.opts.imageConfig ?? {};
175
+ if (config.user) {
176
+ this.opts.imageConfig.user = config.user;
177
+ }
178
+ if (config.workingDir) {
179
+ this.opts.imageConfig.workingDir = config.workingDir;
180
+ }
181
+ if (config.entrypoint && config.entrypoint.length > 0) {
182
+ this.opts.imageConfig.entrypoint = [...config.entrypoint];
183
+ }
184
+ if (config.cmd && config.cmd.length > 0) {
185
+ this.opts.imageConfig.cmd = [...config.cmd];
186
+ }
187
+ if (config.env && Object.keys(config.env).length > 0) {
188
+ this.opts.imageConfig.env = { ...config.env };
189
+ }
190
+ return this;
191
+ }
192
+ options() {
193
+ return cloneCreateOptions(this.opts);
194
+ }
195
+ }
196
+ exports.Sandbox = Sandbox;
197
+ function createSandbox(image) {
198
+ return new Sandbox(image);
199
+ }
@@ -0,0 +1,65 @@
1
+ import { Sandbox } from "./builder";
2
+ import { type BinaryLike, type Config, type CreateOptions, type ExecOptions, type ExecResult, type ExecStreamOptions, type ExecStreamResult, type FileInfo, type PortForwardBinding, type RequestOptions, type StreamWriter } from "./types";
3
+ export declare function defaultConfig(config?: Config): Required<Config>;
4
+ export declare class Client {
5
+ private readonly config;
6
+ private process;
7
+ private requestID;
8
+ private pending;
9
+ private writeLock;
10
+ private readBuffer;
11
+ private vmIDValue;
12
+ private lastVMID;
13
+ private closed;
14
+ private closing;
15
+ private vfsHooks;
16
+ private vfsMutateHooks;
17
+ private vfsActionHooks;
18
+ private vfsHookActive;
19
+ constructor(config?: Config);
20
+ get vmId(): string;
21
+ start(): Promise<void>;
22
+ close(timeoutSeconds?: number): Promise<void>;
23
+ remove(): Promise<void>;
24
+ launch(sandbox: Sandbox): Promise<string>;
25
+ create(opts?: CreateOptions): Promise<string>;
26
+ private resolveCreateBlockPrivateIPs;
27
+ private buildCreateNetworkParams;
28
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
29
+ execWithDir(command: string, workingDir?: string, options?: RequestOptions): Promise<ExecResult>;
30
+ execStream(command: string, options?: ExecStreamOptions): Promise<ExecStreamResult>;
31
+ execStreamWithDir(command: string, workingDir?: string, stdout?: StreamWriter, stderr?: StreamWriter, options?: RequestOptions): Promise<ExecStreamResult>;
32
+ writeFile(path: string, content: BinaryLike, options?: RequestOptions): Promise<void>;
33
+ writeFileMode(path: string, content: BinaryLike, mode: number, options?: RequestOptions): Promise<void>;
34
+ readFile(path: string, options?: RequestOptions): Promise<Buffer>;
35
+ listFiles(path: string, options?: RequestOptions): Promise<FileInfo[]>;
36
+ portForward(...specs: string[]): Promise<PortForwardBinding[]>;
37
+ portForwardWithAddresses(addresses: string[] | undefined, ...specs: string[]): Promise<PortForwardBinding[]>;
38
+ private portForwardMappings;
39
+ private parsePortForwards;
40
+ private parsePortForward;
41
+ private parsePort;
42
+ private sendRequest;
43
+ private sendCancelRequest;
44
+ private enqueueWrite;
45
+ private processReadBuffer;
46
+ private handleMessage;
47
+ private handleNotification;
48
+ private handleVFSFileEventNotification;
49
+ private handleVFSFileEvent;
50
+ private runVFSSafeHooksForEvent;
51
+ private runSingleVFSHook;
52
+ private matchesVFSHook;
53
+ private applyLocalWriteMutations;
54
+ private applyLocalActionHooks;
55
+ private compileVFSHooks;
56
+ private validateLocalAfterRule;
57
+ private ruleToWire;
58
+ private setLocalVFSHooks;
59
+ private validateAddHost;
60
+ private isValidIP;
61
+ private writeStreamChunk;
62
+ private isRunning;
63
+ private waitForProcessExit;
64
+ private handleProcessClosed;
65
+ }