sandlot 0.1.4 → 0.2.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/dist/browser/bundler.d.ts +68 -0
- package/dist/browser/bundler.d.ts.map +1 -0
- package/dist/browser/executor.d.ts +46 -0
- package/dist/browser/executor.d.ts.map +1 -0
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2692 -0
- package/dist/browser/preset.d.ts +63 -0
- package/dist/browser/preset.d.ts.map +1 -0
- package/dist/commands/index.d.ts +20 -11
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/types.d.ts +31 -132
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/core/bundler-utils.d.ts +142 -0
- package/dist/core/bundler-utils.d.ts.map +1 -0
- package/dist/core/esm-types-resolver.d.ts +125 -0
- package/dist/core/esm-types-resolver.d.ts.map +1 -0
- package/dist/core/executor.d.ts +35 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/{fs.d.ts → core/fs.d.ts} +27 -29
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/sandbox.d.ts +30 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandlot.d.ts +30 -0
- package/dist/core/sandlot.d.ts.map +1 -0
- package/dist/core/shared-module-registry.d.ts +46 -0
- package/dist/core/shared-module-registry.d.ts.map +1 -0
- package/dist/core/typechecker.d.ts +60 -0
- package/dist/core/typechecker.d.ts.map +1 -0
- package/dist/index.d.ts +11 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1399 -2010
- package/dist/node/bundler.d.ts +48 -0
- package/dist/node/bundler.d.ts.map +1 -0
- package/dist/node/executor.d.ts +48 -0
- package/dist/node/executor.d.ts.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2646 -0
- package/dist/node/preset.d.ts +62 -0
- package/dist/node/preset.d.ts.map +1 -0
- package/dist/types.d.ts +525 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +16 -6
- package/src/browser/bundler.ts +294 -0
- package/src/browser/executor.ts +71 -0
- package/src/browser/index.ts +57 -0
- package/src/browser/preset.ts +179 -0
- package/src/commands/index.ts +526 -43
- package/src/commands/types.ts +82 -146
- package/src/core/bundler-utils.ts +630 -0
- package/src/core/esm-types-resolver.ts +432 -0
- package/src/core/executor.ts +161 -0
- package/src/{fs.ts → core/fs.ts} +59 -37
- package/src/core/sandbox.ts +621 -0
- package/src/core/sandlot.ts +77 -0
- package/src/core/shared-module-registry.ts +138 -0
- package/src/core/typechecker.ts +607 -0
- package/src/index.ts +104 -139
- package/src/node/bundler.ts +194 -0
- package/src/node/executor.ts +87 -0
- package/src/node/index.ts +39 -0
- package/src/node/preset.ts +178 -0
- package/src/types.ts +668 -0
- package/README.md +0 -243
- package/dist/build-emitter.d.ts +0 -47
- package/dist/build-emitter.d.ts.map +0 -1
- package/dist/builder.d.ts +0 -370
- package/dist/builder.d.ts.map +0 -1
- package/dist/bundler.d.ts +0 -152
- package/dist/bundler.d.ts.map +0 -1
- package/dist/commands/compile.d.ts +0 -13
- package/dist/commands/compile.d.ts.map +0 -1
- package/dist/commands/packages.d.ts +0 -17
- package/dist/commands/packages.d.ts.map +0 -1
- package/dist/commands/run.d.ts +0 -40
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands.d.ts +0 -179
- package/dist/commands.d.ts.map +0 -1
- package/dist/fs.d.ts.map +0 -1
- package/dist/internal.d.ts +0 -79
- package/dist/internal.d.ts.map +0 -1
- package/dist/internal.js +0 -1942
- package/dist/loader.d.ts +0 -164
- package/dist/loader.d.ts.map +0 -1
- package/dist/packages.d.ts +0 -199
- package/dist/packages.d.ts.map +0 -1
- package/dist/runner.d.ts +0 -314
- package/dist/runner.d.ts.map +0 -1
- package/dist/sandbox-manager.d.ts +0 -261
- package/dist/sandbox-manager.d.ts.map +0 -1
- package/dist/sandbox.d.ts +0 -267
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/shared-modules.d.ts +0 -148
- package/dist/shared-modules.d.ts.map +0 -1
- package/dist/shared-resources.d.ts +0 -102
- package/dist/shared-resources.d.ts.map +0 -1
- package/dist/ts-libs.d.ts +0 -85
- package/dist/ts-libs.d.ts.map +0 -1
- package/dist/typechecker.d.ts +0 -127
- package/dist/typechecker.d.ts.map +0 -1
- package/src/build-emitter.ts +0 -64
- package/src/builder.ts +0 -498
- package/src/bundler.ts +0 -575
- package/src/commands/compile.ts +0 -236
- package/src/commands/packages.ts +0 -154
- package/src/commands/run.ts +0 -245
- package/src/internal.ts +0 -119
- package/src/loader.ts +0 -229
- package/src/packages.ts +0 -936
- package/src/sandbox.ts +0 -398
- package/src/shared-modules.ts +0 -280
- package/src/shared-resources.ts +0 -166
- package/src/ts-libs.ts +0 -218
- package/src/typechecker.ts +0 -635
package/src/builder.ts
DELETED
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* High-level builder for sandbox-based agent workflows.
|
|
3
|
-
*
|
|
4
|
-
* Provides a simple API that handles sandbox creation, build result capture,
|
|
5
|
-
* validation, and timeout/cancellation—letting the caller focus on their agent logic.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { createSandbox, type Sandbox, type SandboxOptions } from "./sandbox";
|
|
9
|
-
import type { BundleResult } from "./bundler";
|
|
10
|
-
import type { BuildOutput } from "./commands/types";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Result of running a builder.
|
|
14
|
-
*
|
|
15
|
-
* @typeParam T - The return type of the build function
|
|
16
|
-
* @typeParam M - The type of the validated module (defaults to Record<string, unknown>)
|
|
17
|
-
*/
|
|
18
|
-
export interface BuildResult<T, M = Record<string, unknown>> {
|
|
19
|
-
/**
|
|
20
|
-
* Whatever the `build` function returned, or `undefined` if it threw.
|
|
21
|
-
*/
|
|
22
|
-
result: T | undefined;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* The error thrown by the `build` function, or `null` if it succeeded.
|
|
26
|
-
*
|
|
27
|
-
* When an error occurs, the bundle and module may still be available
|
|
28
|
-
* if a build succeeded before the error was thrown.
|
|
29
|
-
*/
|
|
30
|
-
error: Error | null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The last successful build result, or null if no build succeeded.
|
|
34
|
-
*
|
|
35
|
-
* This captures the bundle from any successful `build` command executed
|
|
36
|
-
* during the run. If multiple builds succeed, this contains the last one.
|
|
37
|
-
*
|
|
38
|
-
* **Important**: This is available even if the `build` function threw an error,
|
|
39
|
-
* as long as a build succeeded before the error occurred.
|
|
40
|
-
*/
|
|
41
|
-
bundle: BundleResult | null;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The loaded module exports, or null if no bundle was produced.
|
|
45
|
-
*
|
|
46
|
-
* When a build succeeds, the bundle is automatically loaded. If a `validate`
|
|
47
|
-
* function was provided, the module is passed through it and the result is
|
|
48
|
-
* available here with the validated type.
|
|
49
|
-
*
|
|
50
|
-
* @example Without validation
|
|
51
|
-
* ```ts
|
|
52
|
-
* const result = await runAgent("Create a counter");
|
|
53
|
-
* result.module // Record<string, unknown> | null
|
|
54
|
-
* ```
|
|
55
|
-
*
|
|
56
|
-
* @example With validation
|
|
57
|
-
* ```ts
|
|
58
|
-
* const result = await runAgent("Create a counter", {
|
|
59
|
-
* validate: (mod) => ({ App: mod.App as React.ComponentType }),
|
|
60
|
-
* });
|
|
61
|
-
* result.module // { App: React.ComponentType } | null
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
module: M | null;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The sandbox that was used.
|
|
68
|
-
*
|
|
69
|
-
* Useful when the builder created an ephemeral sandbox—you can inspect
|
|
70
|
-
* its state, save it, or reuse it for another build.
|
|
71
|
-
*/
|
|
72
|
-
sandbox: Sandbox;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Options passed when calling the builder function.
|
|
77
|
-
*
|
|
78
|
-
* @typeParam M - The type returned by the validate function (if provided)
|
|
79
|
-
*/
|
|
80
|
-
export interface BuildCallOptions<M = Record<string, unknown>> {
|
|
81
|
-
/**
|
|
82
|
-
* Validation function for the built module.
|
|
83
|
-
*
|
|
84
|
-
* When provided, this function runs as part of the build command—after
|
|
85
|
-
* bundling and loading, but before the build is considered successful.
|
|
86
|
-
* If validation throws, the build fails and the agent sees the error,
|
|
87
|
-
* giving it a chance to fix the code and try again.
|
|
88
|
-
*
|
|
89
|
-
* The return type determines the type of `result.module`.
|
|
90
|
-
*
|
|
91
|
-
* @example Simple validation
|
|
92
|
-
* ```ts
|
|
93
|
-
* const result = await runAgent("Create a counter", {
|
|
94
|
-
* validate: (mod) => {
|
|
95
|
-
* if (typeof mod.App !== 'function') {
|
|
96
|
-
* throw new Error("Module must export an App component");
|
|
97
|
-
* }
|
|
98
|
-
* return { App: mod.App as React.ComponentType };
|
|
99
|
-
* },
|
|
100
|
-
* });
|
|
101
|
-
* // If validation fails, agent sees: "Build failed: Validation error."
|
|
102
|
-
* // Agent can fix the code and try again
|
|
103
|
-
* result.module?.App // React.ComponentType (after successful build)
|
|
104
|
-
* ```
|
|
105
|
-
*
|
|
106
|
-
* @example With Zod
|
|
107
|
-
* ```ts
|
|
108
|
-
* import { z } from 'zod';
|
|
109
|
-
*
|
|
110
|
-
* const CounterSchema = z.object({
|
|
111
|
-
* App: z.custom<React.ComponentType>((v) => typeof v === 'function'),
|
|
112
|
-
* initialCount: z.number().optional(),
|
|
113
|
-
* });
|
|
114
|
-
*
|
|
115
|
-
* const result = await runAgent("Create a counter", {
|
|
116
|
-
* validate: (mod) => CounterSchema.parse(mod),
|
|
117
|
-
* });
|
|
118
|
-
* result.module?.App // React.ComponentType
|
|
119
|
-
* result.module?.initialCount // number | undefined
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
validate?: (module: Record<string, unknown>) => M;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Maximum time in milliseconds for the build to complete.
|
|
126
|
-
*
|
|
127
|
-
* If the timeout is exceeded, the build is aborted and an error is thrown.
|
|
128
|
-
* The agent's partial work may still be available in the sandbox.
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```ts
|
|
132
|
-
* const result = await runAgent("Create a complex dashboard", {
|
|
133
|
-
* timeout: 300_000, // 5 minutes
|
|
134
|
-
* });
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
timeout?: number;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* AbortSignal for cancelling the build.
|
|
141
|
-
*
|
|
142
|
-
* If the signal is aborted, the build stops and an error is thrown.
|
|
143
|
-
* Useful for user-initiated cancellation or external timeout management.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```ts
|
|
147
|
-
* const controller = new AbortController();
|
|
148
|
-
*
|
|
149
|
-
* // Start the build
|
|
150
|
-
* const promise = runAgent("Create a counter", {
|
|
151
|
-
* signal: controller.signal,
|
|
152
|
-
* });
|
|
153
|
-
*
|
|
154
|
-
* // Cancel after 10 seconds
|
|
155
|
-
* setTimeout(() => controller.abort(), 10_000);
|
|
156
|
-
*
|
|
157
|
-
* try {
|
|
158
|
-
* const result = await promise;
|
|
159
|
-
* } catch (err) {
|
|
160
|
-
* if (err.name === 'AbortError') {
|
|
161
|
-
* console.log('Build was cancelled');
|
|
162
|
-
* }
|
|
163
|
-
* }
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
signal?: AbortSignal;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Options for creating a reusable builder function.
|
|
171
|
-
*/
|
|
172
|
-
export interface CreateBuilderOptions<T> {
|
|
173
|
-
/**
|
|
174
|
-
* Existing sandbox to reuse across all calls.
|
|
175
|
-
*
|
|
176
|
-
* When provided, the same sandbox is used for every call to the returned
|
|
177
|
-
* builder function. Files and state persist between runs—useful for
|
|
178
|
-
* iterative workflows where you want to build on previous work.
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* ```ts
|
|
182
|
-
* const sandbox = await createSandbox({
|
|
183
|
-
* initialFiles: { '/src/utils.ts': 'export const PI = 3.14;' },
|
|
184
|
-
* });
|
|
185
|
-
*
|
|
186
|
-
* const runAgent = createBuilder({
|
|
187
|
-
* sandbox, // Reused across all calls
|
|
188
|
-
* build: async (sb, prompt) => myAgent.run(sb, prompt),
|
|
189
|
-
* });
|
|
190
|
-
*
|
|
191
|
-
* await runAgent("Create a circle component"); // Uses existing utils.ts
|
|
192
|
-
* await runAgent("Add a square component"); // Still has circle + utils
|
|
193
|
-
* ```
|
|
194
|
-
*/
|
|
195
|
-
sandbox?: Sandbox;
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Options for creating fresh sandboxes (only used if `sandbox` is not provided).
|
|
199
|
-
*
|
|
200
|
-
* Each call to the returned builder function creates a new sandbox with
|
|
201
|
-
* these options. Use this when you want isolated runs.
|
|
202
|
-
*
|
|
203
|
-
* @example
|
|
204
|
-
* ```ts
|
|
205
|
-
* const runAgent = createBuilder({
|
|
206
|
-
* sandboxOptions: {
|
|
207
|
-
* sharedModules: ['react', 'react-dom/client'],
|
|
208
|
-
* },
|
|
209
|
-
* build: async (sandbox, prompt) => myAgent.run(sandbox, prompt),
|
|
210
|
-
* });
|
|
211
|
-
*
|
|
212
|
-
* // Each call gets a fresh sandbox
|
|
213
|
-
* await runAgent("Create a counter"); // Fresh sandbox
|
|
214
|
-
* await runAgent("Create a todo list"); // Another fresh sandbox
|
|
215
|
-
* ```
|
|
216
|
-
*/
|
|
217
|
-
sandboxOptions?: SandboxOptions;
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* The function to build with the sandbox and prompt.
|
|
221
|
-
*
|
|
222
|
-
* This receives the sandbox and the prompt string, and should return
|
|
223
|
-
* whatever result your agent produces.
|
|
224
|
-
*
|
|
225
|
-
* @example
|
|
226
|
-
* ```ts
|
|
227
|
-
* const runAgent = createBuilder({
|
|
228
|
-
* build: async (sandbox, prompt) => {
|
|
229
|
-
* const agent = new MyAgent({
|
|
230
|
-
* tools: { bash: sandbox.bash.exec },
|
|
231
|
-
* });
|
|
232
|
-
* return agent.run(prompt);
|
|
233
|
-
* },
|
|
234
|
-
* });
|
|
235
|
-
* ```
|
|
236
|
-
*/
|
|
237
|
-
build: (sandbox: Sandbox, prompt: string) => Promise<T>;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* The builder function returned by `createBuilder`.
|
|
242
|
-
*
|
|
243
|
-
* Can be called with just a prompt, or with options including validation,
|
|
244
|
-
* timeout, and abort signal.
|
|
245
|
-
*/
|
|
246
|
-
export interface BuilderFn<T> {
|
|
247
|
-
/**
|
|
248
|
-
* Run the builder with a prompt (no options).
|
|
249
|
-
* Module will be typed as `Record<string, unknown>`.
|
|
250
|
-
*/
|
|
251
|
-
(prompt: string): Promise<BuildResult<T>>;
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Run the builder with a prompt and options.
|
|
255
|
-
* If `validate` is provided, module will be typed based on its return type.
|
|
256
|
-
*/
|
|
257
|
-
<M = Record<string, unknown>>(prompt: string, options: BuildCallOptions<M>): Promise<BuildResult<T, M>>;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Create a reusable builder function for running prompts in a sandbox.
|
|
262
|
-
*
|
|
263
|
-
* This is the main API for sandlot. Define your agent logic once, then call
|
|
264
|
-
* the returned function with different prompts. Each call:
|
|
265
|
-
*
|
|
266
|
-
* 1. Creates a sandbox (or uses one you provide)
|
|
267
|
-
* 2. Sets up build result capture
|
|
268
|
-
* 3. Runs your build function with the prompt
|
|
269
|
-
* 4. Returns everything: your result, the bundle, the module, and the sandbox
|
|
270
|
-
*
|
|
271
|
-
* **Sandbox behavior:**
|
|
272
|
-
* - If you provide `sandbox`: The same sandbox is reused for every call
|
|
273
|
-
* (files persist, good for iteration)
|
|
274
|
-
* - If you provide `sandboxOptions` (or nothing): A fresh sandbox is created
|
|
275
|
-
* for each call (isolated runs)
|
|
276
|
-
*
|
|
277
|
-
* **Validation behavior:**
|
|
278
|
-
* When you provide a `validate` function, it runs as part of the build command.
|
|
279
|
-
* If validation fails, the build fails and the agent sees the error—giving it
|
|
280
|
-
* a chance to fix the code and try again. This is more powerful than post-hoc
|
|
281
|
-
* validation because the agent can iterate on validation errors.
|
|
282
|
-
*
|
|
283
|
-
* @example Basic usage
|
|
284
|
-
* ```ts
|
|
285
|
-
* import { createBuilder } from 'sandlot';
|
|
286
|
-
*
|
|
287
|
-
* const runAgent = createBuilder({
|
|
288
|
-
* sandboxOptions: { sharedModules: ['react', 'react-dom/client'] },
|
|
289
|
-
* build: async (sandbox, prompt) => {
|
|
290
|
-
* const agent = new MyAgent({ tools: { bash: sandbox.bash.exec } });
|
|
291
|
-
* return agent.run(prompt);
|
|
292
|
-
* },
|
|
293
|
-
* });
|
|
294
|
-
*
|
|
295
|
-
* // Use it multiple times with different prompts
|
|
296
|
-
* const result1 = await runAgent("Create a counter component");
|
|
297
|
-
* const result2 = await runAgent("Create a todo list");
|
|
298
|
-
*
|
|
299
|
-
* if (result1.module?.App) {
|
|
300
|
-
* const Counter = result1.module.App as React.ComponentType;
|
|
301
|
-
* }
|
|
302
|
-
* ```
|
|
303
|
-
*
|
|
304
|
-
* @example With validation (agent can fix validation errors)
|
|
305
|
-
* ```ts
|
|
306
|
-
* const runAgent = createBuilder({
|
|
307
|
-
* sandboxOptions: { sharedModules: ['react'] },
|
|
308
|
-
* build: async (sandbox, prompt) => myAgent.run(sandbox, prompt),
|
|
309
|
-
* });
|
|
310
|
-
*
|
|
311
|
-
* // Validation runs during build - agent sees errors and can fix them
|
|
312
|
-
* const counter = await runAgent("Create a counter", {
|
|
313
|
-
* validate: (mod) => {
|
|
314
|
-
* if (typeof mod.App !== 'function') {
|
|
315
|
-
* throw new Error("Must export an App component");
|
|
316
|
-
* }
|
|
317
|
-
* return { App: mod.App as React.ComponentType };
|
|
318
|
-
* },
|
|
319
|
-
* });
|
|
320
|
-
* // If agent's first attempt fails validation, it sees:
|
|
321
|
-
* // "Build failed: Validation error. Must export an App component"
|
|
322
|
-
* // Agent can then fix the code and run build again
|
|
323
|
-
* ```
|
|
324
|
-
*
|
|
325
|
-
* @example With Zod validation
|
|
326
|
-
* ```ts
|
|
327
|
-
* import { z } from 'zod';
|
|
328
|
-
*
|
|
329
|
-
* const CounterSchema = z.object({
|
|
330
|
-
* App: z.custom<React.ComponentType>((v) => typeof v === 'function'),
|
|
331
|
-
* initialCount: z.number().default(0),
|
|
332
|
-
* });
|
|
333
|
-
*
|
|
334
|
-
* const result = await runAgent("Create a counter", {
|
|
335
|
-
* validate: (mod) => CounterSchema.parse(mod),
|
|
336
|
-
* });
|
|
337
|
-
* // result.module is fully typed from Zod inference
|
|
338
|
-
* ```
|
|
339
|
-
*
|
|
340
|
-
* @example Iterative workflow with shared sandbox
|
|
341
|
-
* ```ts
|
|
342
|
-
* const sandbox = await createSandbox();
|
|
343
|
-
*
|
|
344
|
-
* const runAgent = createBuilder({
|
|
345
|
-
* sandbox, // Same sandbox for all calls
|
|
346
|
-
* build: async (sb, prompt) => myAgent.run(sb, prompt),
|
|
347
|
-
* });
|
|
348
|
-
*
|
|
349
|
-
* // First prompt creates initial component
|
|
350
|
-
* await runAgent("Create a button component");
|
|
351
|
-
*
|
|
352
|
-
* // Second prompt can build on the first
|
|
353
|
-
* await runAgent("Add a click counter to the button");
|
|
354
|
-
* ```
|
|
355
|
-
*
|
|
356
|
-
* @example With timeout
|
|
357
|
-
* ```ts
|
|
358
|
-
* // Complex tasks get more time
|
|
359
|
-
* const result = await runAgent("Create a full dashboard with charts", {
|
|
360
|
-
* timeout: 300_000, // 5 minutes
|
|
361
|
-
* });
|
|
362
|
-
* ```
|
|
363
|
-
*
|
|
364
|
-
* @example With abort signal for cancellation
|
|
365
|
-
* ```ts
|
|
366
|
-
* const controller = new AbortController();
|
|
367
|
-
*
|
|
368
|
-
* // Start the build
|
|
369
|
-
* const promise = runAgent("Create a counter", {
|
|
370
|
-
* signal: controller.signal,
|
|
371
|
-
* });
|
|
372
|
-
*
|
|
373
|
-
* // User clicks cancel button
|
|
374
|
-
* cancelButton.onclick = () => controller.abort();
|
|
375
|
-
*
|
|
376
|
-
* try {
|
|
377
|
-
* const result = await promise;
|
|
378
|
-
* } catch (err) {
|
|
379
|
-
* if (err.name === 'AbortError') {
|
|
380
|
-
* console.log('Build was cancelled by user');
|
|
381
|
-
* }
|
|
382
|
-
* }
|
|
383
|
-
* ```
|
|
384
|
-
*/
|
|
385
|
-
export function createBuilder<T>(options: CreateBuilderOptions<T>): BuilderFn<T> {
|
|
386
|
-
// Implementation handles both overloads
|
|
387
|
-
return (async <M>(
|
|
388
|
-
prompt: string,
|
|
389
|
-
callOptions?: BuildCallOptions<M>
|
|
390
|
-
): Promise<BuildResult<T, M>> => {
|
|
391
|
-
// Reuse provided sandbox, or create fresh each call
|
|
392
|
-
const sandbox =
|
|
393
|
-
options.sandbox ?? (await createSandbox(options.sandboxOptions));
|
|
394
|
-
|
|
395
|
-
// Set validation function on sandbox before running agent
|
|
396
|
-
// This makes validation part of the build command
|
|
397
|
-
if (callOptions?.validate) {
|
|
398
|
-
sandbox.setValidation(callOptions.validate as (mod: Record<string, unknown>) => Record<string, unknown>);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Set up build capture - module is loaded during build command
|
|
402
|
-
// Use object wrapper to avoid TypeScript narrowing issues with callbacks
|
|
403
|
-
const captured: { output: BuildOutput | null } = { output: null };
|
|
404
|
-
const unsubscribe = sandbox.onBuild((output) => {
|
|
405
|
-
captured.output = output;
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// Set up abort handling
|
|
409
|
-
const { timeout, signal } = callOptions ?? {};
|
|
410
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
411
|
-
let abortController: AbortController | undefined;
|
|
412
|
-
|
|
413
|
-
// Create a combined abort signal if timeout or signal is provided
|
|
414
|
-
if (timeout !== undefined || signal !== undefined) {
|
|
415
|
-
abortController = new AbortController();
|
|
416
|
-
|
|
417
|
-
// Set up timeout
|
|
418
|
-
if (timeout !== undefined) {
|
|
419
|
-
timeoutId = setTimeout(() => {
|
|
420
|
-
abortController!.abort(new Error(`Build timed out after ${timeout}ms`));
|
|
421
|
-
}, timeout);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Forward external signal to our controller
|
|
425
|
-
if (signal !== undefined) {
|
|
426
|
-
if (signal.aborted) {
|
|
427
|
-
abortController.abort(signal.reason);
|
|
428
|
-
} else {
|
|
429
|
-
signal.addEventListener("abort", () => {
|
|
430
|
-
abortController!.abort(signal.reason);
|
|
431
|
-
}, { once: true });
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
let result: T | undefined;
|
|
437
|
-
let error: Error | null = null;
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
// Create the build promise
|
|
441
|
-
const buildPromise = options.build(sandbox, prompt);
|
|
442
|
-
|
|
443
|
-
// If we have an abort controller, race against it
|
|
444
|
-
if (abortController) {
|
|
445
|
-
const abortPromise = new Promise<never>((_, reject) => {
|
|
446
|
-
abortController!.signal.addEventListener("abort", () => {
|
|
447
|
-
const err = abortController!.signal.reason instanceof Error
|
|
448
|
-
? abortController!.signal.reason
|
|
449
|
-
: new Error("Build aborted");
|
|
450
|
-
err.name = "AbortError";
|
|
451
|
-
reject(err);
|
|
452
|
-
}, { once: true });
|
|
453
|
-
|
|
454
|
-
// If already aborted, reject immediately
|
|
455
|
-
if (abortController!.signal.aborted) {
|
|
456
|
-
const err = abortController!.signal.reason instanceof Error
|
|
457
|
-
? abortController!.signal.reason
|
|
458
|
-
: new Error("Build aborted");
|
|
459
|
-
err.name = "AbortError";
|
|
460
|
-
reject(err);
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
result = await Promise.race([buildPromise, abortPromise]);
|
|
465
|
-
} else {
|
|
466
|
-
result = await buildPromise;
|
|
467
|
-
}
|
|
468
|
-
} catch (err) {
|
|
469
|
-
// Capture the error but the module may still be available
|
|
470
|
-
// if a build succeeded before the error was thrown
|
|
471
|
-
error = err instanceof Error ? err : new Error(String(err));
|
|
472
|
-
} finally {
|
|
473
|
-
// Clean up timeout
|
|
474
|
-
if (timeoutId !== undefined) {
|
|
475
|
-
clearTimeout(timeoutId);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Always clean up the subscription
|
|
479
|
-
unsubscribe();
|
|
480
|
-
|
|
481
|
-
// Always clear validation after the run
|
|
482
|
-
if (callOptions?.validate) {
|
|
483
|
-
sandbox.clearValidation();
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Extract the build output
|
|
488
|
-
const buildOutput = captured.output;
|
|
489
|
-
|
|
490
|
-
return {
|
|
491
|
-
result,
|
|
492
|
-
error,
|
|
493
|
-
bundle: buildOutput?.bundle ?? null,
|
|
494
|
-
module: (buildOutput?.module ?? null) as M | null,
|
|
495
|
-
sandbox,
|
|
496
|
-
};
|
|
497
|
-
}) as BuilderFn<T>;
|
|
498
|
-
}
|