@agentick/sandbox 0.2.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 +251 -0
- package/dist/component.d.ts +43 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +41 -0
- package/dist/component.js.map +1 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +32 -0
- package/dist/context.js.map +1 -0
- package/dist/edit.d.ts +62 -0
- package/dist/edit.d.ts.map +1 -0
- package/dist/edit.js +270 -0
- package/dist/edit.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.d.ts +29 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +49 -0
- package/dist/testing.js.map +1 -0
- package/dist/tools.d.ts +37 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +93 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +156 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +65 -0
- package/src/index.ts +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# @agentick/sandbox
|
|
2
|
+
|
|
3
|
+
Sandbox primitive layer for Agentick. Provides types, React context, a `<Sandbox>` JSX component, and pre-built tools (Shell, ReadFile, WriteFile, EditFile) for sandboxed code execution.
|
|
4
|
+
|
|
5
|
+
Provider adapters (`@agentick/sandbox-local`, `@agentick/sandbox-docker`, etc.) implement `SandboxProvider` and plug in via the `provider` prop.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @agentick/sandbox
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Sandbox, Shell, ReadFile, WriteFile, EditFile } from "@agentick/sandbox";
|
|
17
|
+
import { localProvider } from "@agentick/sandbox-local";
|
|
18
|
+
|
|
19
|
+
function CodingAgent() {
|
|
20
|
+
return (
|
|
21
|
+
<Sandbox provider={localProvider()} workspace="/tmp/project">
|
|
22
|
+
<Shell />
|
|
23
|
+
<ReadFile />
|
|
24
|
+
<WriteFile />
|
|
25
|
+
<EditFile />
|
|
26
|
+
<System>You are a coding assistant with sandbox access.</System>
|
|
27
|
+
</Sandbox>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Component
|
|
33
|
+
|
|
34
|
+
### `<Sandbox>`
|
|
35
|
+
|
|
36
|
+
Creates a sandbox instance and provides it to children via React context.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<Sandbox
|
|
40
|
+
provider={localProvider()} // Required — SandboxProvider implementation
|
|
41
|
+
workspace="/tmp/project" // Path or true for auto temp dir (default: true)
|
|
42
|
+
mounts={[{ host: "./src", sandbox: "/app/src", mode: "rw" }]}
|
|
43
|
+
allow={{ fs: true, net: false }} // Advisory permissions
|
|
44
|
+
env={{ NODE_ENV: "development" }} // Env vars (string or () => string)
|
|
45
|
+
limits={{ memory: 512_000_000 }} // Resource constraints
|
|
46
|
+
setup={async (sb) => {
|
|
47
|
+
// Post-creation setup
|
|
48
|
+
await sb.exec("npm install");
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</Sandbox>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Uses `useData` for async initialization and `useOnUnmount` for cleanup.
|
|
56
|
+
|
|
57
|
+
## Hook
|
|
58
|
+
|
|
59
|
+
### `useSandbox()`
|
|
60
|
+
|
|
61
|
+
Access the nearest `Sandbox` from the component tree. Throws if no provider is found.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useSandbox } from "@agentick/sandbox";
|
|
65
|
+
|
|
66
|
+
const sandbox = useSandbox();
|
|
67
|
+
const result = await sandbox.exec("ls -la");
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Primary use: the `use()` hook on `createTool` for tree-scoped sandbox access.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const MyTool = createTool({
|
|
74
|
+
name: "my_tool",
|
|
75
|
+
description: "Custom sandbox tool",
|
|
76
|
+
input: z.object({ query: z.string() }),
|
|
77
|
+
use: () => ({ sandbox: useSandbox() }),
|
|
78
|
+
handler: async ({ query }, deps) => {
|
|
79
|
+
const result = await deps!.sandbox.exec(`grep -r "${query}" .`);
|
|
80
|
+
return [{ type: "text", text: result.stdout }];
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Tools
|
|
86
|
+
|
|
87
|
+
Four pre-built tools, all using `use()` + `useSandbox()` for tree-scoped access:
|
|
88
|
+
|
|
89
|
+
| Tool | Name | Description |
|
|
90
|
+
| ----------- | ------------ | ------------------------------ |
|
|
91
|
+
| `Shell` | `shell` | Execute a shell command |
|
|
92
|
+
| `ReadFile` | `read_file` | Read file contents |
|
|
93
|
+
| `WriteFile` | `write_file` | Write content to a file |
|
|
94
|
+
| `EditFile` | `edit_file` | Apply surgical edits to a file |
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { Shell, ReadFile, WriteFile, EditFile } from "@agentick/sandbox";
|
|
98
|
+
|
|
99
|
+
// Include all tools
|
|
100
|
+
<Sandbox provider={provider}>
|
|
101
|
+
<Shell />
|
|
102
|
+
<ReadFile />
|
|
103
|
+
<WriteFile />
|
|
104
|
+
<EditFile />
|
|
105
|
+
<MyAgent />
|
|
106
|
+
</Sandbox>
|
|
107
|
+
|
|
108
|
+
// Or pick specific tools
|
|
109
|
+
<Sandbox provider={provider}>
|
|
110
|
+
<Shell />
|
|
111
|
+
<ReadFile />
|
|
112
|
+
<MyAgent />
|
|
113
|
+
</Sandbox>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Tree Scoping
|
|
117
|
+
|
|
118
|
+
Multiple sandboxes in the same tree work naturally. Each tool accesses its nearest `<Sandbox>` provider:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<Sandbox provider={localProvider()}>
|
|
122
|
+
<Shell /> {/* Uses local sandbox */}
|
|
123
|
+
<ReadFile />
|
|
124
|
+
</Sandbox>
|
|
125
|
+
<Sandbox provider={dockerProvider()}>
|
|
126
|
+
<Shell /> {/* Uses Docker sandbox */}
|
|
127
|
+
<WriteFile />
|
|
128
|
+
</Sandbox>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Edit Utilities
|
|
132
|
+
|
|
133
|
+
Surgical code editing with 3-level matching that recovers from trailing whitespace, indentation mismatch, and CRLF/LF differences.
|
|
134
|
+
|
|
135
|
+
### `applyEdits(source, edits)`
|
|
136
|
+
|
|
137
|
+
Pure transform, no I/O. Matching strategy per edit (in order):
|
|
138
|
+
|
|
139
|
+
1. Exact byte match
|
|
140
|
+
2. Line-normalized (trailing whitespace stripped)
|
|
141
|
+
3. Indent-adjusted (leading whitespace baseline stripped)
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { applyEdits } from "@agentick/sandbox";
|
|
145
|
+
|
|
146
|
+
const result = applyEdits(source, [
|
|
147
|
+
{ old: "return 1;", new: "return 2;" },
|
|
148
|
+
{ old: "oldName", new: "newName", all: true },
|
|
149
|
+
]);
|
|
150
|
+
// result.content, result.applied, result.changes
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `editFile(path, edits)`
|
|
154
|
+
|
|
155
|
+
File wrapper. Reads, applies edits, writes atomically (temp + rename).
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { editFile } from "@agentick/sandbox";
|
|
159
|
+
|
|
160
|
+
await editFile("/path/to/file.ts", [{ old: "const x = 1;", new: "const x = 42;" }]);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Types
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import type {
|
|
167
|
+
// Core types
|
|
168
|
+
SandboxHandle, // Runtime handle: exec, readFile, writeFile, editFile, destroy
|
|
169
|
+
SandboxProvider, // Factory: name, create, restore?, destroy?
|
|
170
|
+
SandboxCreateOptions, // Passed to provider.create()
|
|
171
|
+
SandboxConfig, // Component-level config
|
|
172
|
+
SandboxSnapshot, // Serializable state for persistence
|
|
173
|
+
|
|
174
|
+
// Execution
|
|
175
|
+
ExecOptions, // Per-command: cwd, env, timeout
|
|
176
|
+
ExecResult, // Output: stdout, stderr, exitCode
|
|
177
|
+
OutputChunk, // Streaming: stream, data
|
|
178
|
+
|
|
179
|
+
// Configuration
|
|
180
|
+
Mount, // Host<->sandbox path mapping
|
|
181
|
+
Permissions, // Advisory: fs, net, childProcess, inheritEnv
|
|
182
|
+
ResourceLimits, // Constraints: memory, cpu, timeout, disk, maxProcesses
|
|
183
|
+
|
|
184
|
+
// Edit
|
|
185
|
+
Edit, // { old, new, all? }
|
|
186
|
+
EditResult, // { content, applied, changes }
|
|
187
|
+
EditChange, // { line, removed, added }
|
|
188
|
+
} from "@agentick/sandbox";
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Implementing a Provider
|
|
192
|
+
|
|
193
|
+
Provider adapters implement `SandboxProvider`:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import type { SandboxProvider, SandboxHandle, SandboxCreateOptions } from "@agentick/sandbox";
|
|
197
|
+
import { applyEdits } from "@agentick/sandbox";
|
|
198
|
+
|
|
199
|
+
export function myProvider(): SandboxProvider {
|
|
200
|
+
return {
|
|
201
|
+
name: "my-provider",
|
|
202
|
+
async create(options: SandboxCreateOptions): Promise<SandboxHandle> {
|
|
203
|
+
// Set up sandbox environment...
|
|
204
|
+
return {
|
|
205
|
+
id: crypto.randomUUID(),
|
|
206
|
+
workspacePath: "/sandbox/workspace",
|
|
207
|
+
async exec(command, opts) {
|
|
208
|
+
/* ... */
|
|
209
|
+
},
|
|
210
|
+
async readFile(path) {
|
|
211
|
+
/* ... */
|
|
212
|
+
},
|
|
213
|
+
async writeFile(path, content) {
|
|
214
|
+
/* ... */
|
|
215
|
+
},
|
|
216
|
+
async editFile(path, edits) {
|
|
217
|
+
const source = await this.readFile(path);
|
|
218
|
+
const result = applyEdits(source, edits);
|
|
219
|
+
if (result.applied > 0) await this.writeFile(path, result.content);
|
|
220
|
+
return result;
|
|
221
|
+
},
|
|
222
|
+
async destroy() {
|
|
223
|
+
/* ... */
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Testing
|
|
232
|
+
|
|
233
|
+
Import test utilities from `@agentick/sandbox/testing`:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import { createMockSandbox, createMockProvider } from "@agentick/sandbox/testing";
|
|
237
|
+
|
|
238
|
+
const sandbox = createMockSandbox({
|
|
239
|
+
exec: vi.fn().mockResolvedValue({ stdout: "hello", stderr: "", exitCode: 0 }),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const provider = createMockProvider({
|
|
243
|
+
create: vi.fn().mockResolvedValue(sandbox),
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Both return objects with `vi.fn()` stubs and sensible defaults. Override any method via the options parameter.
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Component
|
|
3
|
+
*
|
|
4
|
+
* JSX component that creates and provides a Sandbox instance to its children.
|
|
5
|
+
* Uses useData for async initialization and useOnUnmount for cleanup.
|
|
6
|
+
*/
|
|
7
|
+
import type React from "react";
|
|
8
|
+
import type { SandboxProvider, Mount, Permissions, ResourceLimits, Sandbox as SandboxHandle } from "./types";
|
|
9
|
+
export interface SandboxProps {
|
|
10
|
+
/** The sandbox provider to use (e.g. localProvider()). */
|
|
11
|
+
provider: SandboxProvider;
|
|
12
|
+
/** Workspace path, or true for auto temp directory. Default: true. */
|
|
13
|
+
workspace?: string | true;
|
|
14
|
+
/** Host↔sandbox path mappings. */
|
|
15
|
+
mounts?: Mount[];
|
|
16
|
+
/** Advisory permissions. */
|
|
17
|
+
allow?: Permissions;
|
|
18
|
+
/** Environment variables. Functions are resolved at creation time. */
|
|
19
|
+
env?: Record<string, string | (() => string)>;
|
|
20
|
+
/** Post-creation setup callback. */
|
|
21
|
+
setup?: (sandbox: SandboxHandle) => Promise<void>;
|
|
22
|
+
/** Resource constraints. */
|
|
23
|
+
limits?: ResourceLimits;
|
|
24
|
+
/** Whether to persist sandbox state in snapshots. Default: false. */
|
|
25
|
+
persist?: boolean;
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a sandbox instance and provides it to children via context.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <Sandbox provider={localProvider()}>
|
|
34
|
+
* <Shell />
|
|
35
|
+
* <ReadFile />
|
|
36
|
+
* <WriteFile />
|
|
37
|
+
* <EditFile />
|
|
38
|
+
* <MyAgent />
|
|
39
|
+
* </Sandbox>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function Sandbox({ provider, workspace, mounts, allow, env, setup, limits, children, }: SandboxProps): React.ReactElement;
|
|
43
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACV,eAAe,EAEf,KAAK,EACL,WAAW,EACX,cAAc,EACd,OAAO,IAAI,aAAa,EACzB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,QAAQ,EAAE,eAAe,CAAC;IAE1B,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B,kCAAkC;IAClC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IAEjB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IAEpB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC;IAE9C,oCAAoC;IACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD,4BAA4B;IAC5B,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,SAAgB,EAChB,MAAM,EACN,KAAK,EACL,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,GACT,EAAE,YAAY,GAAG,KAAK,CAAC,YAAY,CA2BnC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useData, useOnUnmount } from "@agentick/core";
|
|
3
|
+
import { SandboxContext } from "./context";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a sandbox instance and provides it to children via context.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <Sandbox provider={localProvider()}>
|
|
10
|
+
* <Shell />
|
|
11
|
+
* <ReadFile />
|
|
12
|
+
* <WriteFile />
|
|
13
|
+
* <EditFile />
|
|
14
|
+
* <MyAgent />
|
|
15
|
+
* </Sandbox>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function Sandbox({ provider, workspace = true, mounts, allow, env, setup, limits, children, }) {
|
|
19
|
+
// Resolve env functions to plain strings
|
|
20
|
+
const resolvedEnv = env
|
|
21
|
+
? Object.fromEntries(Object.entries(env).map(([k, v]) => [k, typeof v === "function" ? v() : v]))
|
|
22
|
+
: undefined;
|
|
23
|
+
const options = {
|
|
24
|
+
workspace,
|
|
25
|
+
mounts,
|
|
26
|
+
permissions: allow,
|
|
27
|
+
env: resolvedEnv,
|
|
28
|
+
limits,
|
|
29
|
+
};
|
|
30
|
+
const sandbox = useData("sandbox", async () => {
|
|
31
|
+
const sb = await provider.create(options);
|
|
32
|
+
if (setup)
|
|
33
|
+
await setup(sb);
|
|
34
|
+
return sb;
|
|
35
|
+
});
|
|
36
|
+
useOnUnmount(() => {
|
|
37
|
+
sandbox.destroy();
|
|
38
|
+
});
|
|
39
|
+
return _jsx(SandboxContext.Provider, { value: sandbox, children: children });
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.tsx"],"names":[],"mappings":";AAQA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAsC3C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,QAAQ,EACR,SAAS,GAAG,IAAI,EAChB,MAAM,EACN,KAAK,EACL,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,GACK;IACb,yCAAyC;IACzC,MAAM,WAAW,GAAG,GAAG;QACrB,CAAC,CAAC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC5E;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,OAAO,GAAyB;QACpC,SAAS;QACT,MAAM;QACN,WAAW,EAAE,KAAK;QAClB,GAAG,EAAE,WAAW;QAChB,MAAM;KACP,CAAC;IAEF,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,KAAK;YAAE,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,GAAG,EAAE;QAChB,OAAO,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,OAAO,YAAG,QAAQ,GAA2B,CAAC;AACvF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Context
|
|
3
|
+
*
|
|
4
|
+
* React context and hook for accessing the nearest Sandbox in the component tree.
|
|
5
|
+
*/
|
|
6
|
+
import type { Sandbox } from "./types";
|
|
7
|
+
export declare const SandboxContext: import("react").Context<Sandbox | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Access the nearest Sandbox from the component tree.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if no `<Sandbox>` provider is found in the tree.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const Shell = createTool({
|
|
16
|
+
* name: 'shell',
|
|
17
|
+
* use: () => ({ sandbox: useSandbox() }),
|
|
18
|
+
* handler: async ({ command }, deps) => {
|
|
19
|
+
* const result = await deps!.sandbox.exec(command);
|
|
20
|
+
* return [{ type: 'text', text: result.stdout }];
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function useSandbox(): Sandbox;
|
|
26
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,eAAO,MAAM,cAAc,yCAAsC,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAQpC"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Context
|
|
3
|
+
*
|
|
4
|
+
* React context and hook for accessing the nearest Sandbox in the component tree.
|
|
5
|
+
*/
|
|
6
|
+
import { createContext, useContext } from "react";
|
|
7
|
+
export const SandboxContext = createContext(null);
|
|
8
|
+
/**
|
|
9
|
+
* Access the nearest Sandbox from the component tree.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if no `<Sandbox>` provider is found in the tree.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const Shell = createTool({
|
|
16
|
+
* name: 'shell',
|
|
17
|
+
* use: () => ({ sandbox: useSandbox() }),
|
|
18
|
+
* handler: async ({ command }, deps) => {
|
|
19
|
+
* const result = await deps!.sandbox.exec(command);
|
|
20
|
+
* return [{ type: 'text', text: result.stdout }];
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function useSandbox() {
|
|
26
|
+
const sandbox = useContext(SandboxContext);
|
|
27
|
+
if (!sandbox) {
|
|
28
|
+
throw new Error("useSandbox(): No sandbox found. Wrap your component tree with <Sandbox provider={...}>.");
|
|
29
|
+
}
|
|
30
|
+
return sandbox;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAGlD,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAiB,IAAI,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/edit.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surgical Code Editing
|
|
3
|
+
*
|
|
4
|
+
* LLM-driven code editing with layered matching that recovers from
|
|
5
|
+
* trailing whitespace, indentation mismatch, and CRLF/LF differences.
|
|
6
|
+
*
|
|
7
|
+
* Pure transform (`applyEdits`) + file wrapper (`editFile`) with atomic writes.
|
|
8
|
+
*/
|
|
9
|
+
export interface Edit {
|
|
10
|
+
/** Exact string to find in the source. */
|
|
11
|
+
old: string;
|
|
12
|
+
/** Replacement string. Empty string deletes the match. */
|
|
13
|
+
new: string;
|
|
14
|
+
/** Replace ALL occurrences. Default false — requires unique match. */
|
|
15
|
+
all?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface EditChange {
|
|
18
|
+
/** 1-based line where the change starts. */
|
|
19
|
+
line: number;
|
|
20
|
+
/** Lines removed. */
|
|
21
|
+
removed: number;
|
|
22
|
+
/** Lines added. */
|
|
23
|
+
added: number;
|
|
24
|
+
}
|
|
25
|
+
export interface EditResult {
|
|
26
|
+
/** Resulting content after edits. */
|
|
27
|
+
content: string;
|
|
28
|
+
/** Total number of replacements applied. */
|
|
29
|
+
applied: number;
|
|
30
|
+
/** Per-replacement details in document order. */
|
|
31
|
+
changes: EditChange[];
|
|
32
|
+
}
|
|
33
|
+
export declare class EditError extends Error {
|
|
34
|
+
readonly editIndex: number;
|
|
35
|
+
readonly detail?: {
|
|
36
|
+
closest?: string;
|
|
37
|
+
line?: number;
|
|
38
|
+
} | undefined;
|
|
39
|
+
constructor(message: string, editIndex: number, detail?: {
|
|
40
|
+
closest?: string;
|
|
41
|
+
line?: number;
|
|
42
|
+
} | undefined);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Apply edits to a source string. Pure function, no I/O.
|
|
46
|
+
*
|
|
47
|
+
* Matching strategy per edit (in order):
|
|
48
|
+
* 1. Exact byte match
|
|
49
|
+
* 2. Line-normalized (trailing whitespace stripped)
|
|
50
|
+
* 3. Indent-adjusted (leading whitespace baseline stripped, new indentation adjusted)
|
|
51
|
+
*
|
|
52
|
+
* Multi-edit: all matches resolved against original source,
|
|
53
|
+
* validated for overlap, applied bottom-to-top.
|
|
54
|
+
*
|
|
55
|
+
* @throws EditError on match failure or overlapping edits
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyEdits(source: string, edits: Edit[]): EditResult;
|
|
58
|
+
/**
|
|
59
|
+
* Edit a file on disk. Reads, applies edits, writes atomically (temp + rename).
|
|
60
|
+
*/
|
|
61
|
+
export declare function editFile(path: string, edits: Edit[]): Promise<EditResult>;
|
|
62
|
+
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,IAAI;IACnB,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,qBAAa,SAAU,SAAQ,KAAK;aAGhB,SAAS,EAAE,MAAM;aACjB,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;gBAF5D,OAAO,EAAE,MAAM,EACC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,YAAA;CAK/D;AAyHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,CAsJpE;AAID;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB/E"}
|