@flue/sdk 0.3.0 → 0.3.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 +24 -9
- package/dist/agent-BB4lwAd5.mjs +453 -0
- package/dist/client.d.mts +26 -0
- package/dist/client.mjs +78 -0
- package/dist/cloudflare/index.d.mts +35 -0
- package/dist/cloudflare/index.mjs +241 -0
- package/dist/command-helpers-DdAfbnom.d.mts +21 -0
- package/dist/command-helpers-hTZKWK13.mjs +37 -0
- package/dist/index.d.mts +78 -0
- package/dist/index.mjs +1568 -0
- package/dist/internal.d.mts +29 -0
- package/dist/internal.mjs +39 -0
- package/dist/mcp-BVF-sOBZ.d.mts +22 -0
- package/dist/mcp-DOgMtp8y.mjs +285 -0
- package/dist/node/index.d.mts +14 -0
- package/dist/node/index.mjs +75 -0
- package/dist/sandbox.d.mts +29 -0
- package/dist/sandbox.mjs +132 -0
- package/dist/session-DukL3zwF.mjs +1303 -0
- package/dist/types-T8pE1xIS.d.mts +461 -0
- package/package.json +11 -3
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import "../agent-BB4lwAd5.mjs";
|
|
2
|
+
import "../session-DukL3zwF.mjs";
|
|
3
|
+
import { createSandboxSessionEnv } from "../sandbox.mjs";
|
|
4
|
+
import { t as normalizeExecutor } from "../command-helpers-hTZKWK13.mjs";
|
|
5
|
+
import { Workspace, WorkspaceFileSystem } from "@cloudflare/shell";
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
+
|
|
8
|
+
//#region src/cloudflare/context.ts
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare environment context injection.
|
|
11
|
+
*
|
|
12
|
+
* Durable Objects are single-threaded, but async executions can still interleave
|
|
13
|
+
* at await points. AsyncLocalStorage keeps Cloudflare runtime primitives scoped
|
|
14
|
+
* to the request/fiber that invoked them instead of sharing a module global.
|
|
15
|
+
*/
|
|
16
|
+
const contextStorage = new AsyncLocalStorage();
|
|
17
|
+
function runWithCloudflareContext(ctx, fn) {
|
|
18
|
+
return contextStorage.run(ctx, fn);
|
|
19
|
+
}
|
|
20
|
+
function getCloudflareContext() {
|
|
21
|
+
const ctx = contextStorage.getStore();
|
|
22
|
+
if (!ctx) throw new Error("[flue:cloudflare] Not running in a Cloudflare context. This function can only be called inside a Cloudflare Worker or Durable Object.");
|
|
23
|
+
return ctx;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/cloudflare/virtual-sandbox.ts
|
|
28
|
+
/**
|
|
29
|
+
* In-process just-bash sandbox for Cloudflare Workers (no container).
|
|
30
|
+
* Without args: empty in-memory. With R2 bucket: persistent files via DO SQLite + R2.
|
|
31
|
+
*/
|
|
32
|
+
function adaptStat(cfStat) {
|
|
33
|
+
return {
|
|
34
|
+
isFile: cfStat.type === "file",
|
|
35
|
+
isDirectory: cfStat.type === "directory",
|
|
36
|
+
isSymbolicLink: cfStat.type === "symlink",
|
|
37
|
+
mode: cfStat.mode ?? (cfStat.type === "directory" ? 493 : 420),
|
|
38
|
+
size: cfStat.size,
|
|
39
|
+
mtime: cfStat.mtime
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function adaptToJustBash(cfFs) {
|
|
43
|
+
return {
|
|
44
|
+
readFile: (path, _opts) => cfFs.readFile(path),
|
|
45
|
+
readFileBuffer: (path) => cfFs.readFileBytes(path),
|
|
46
|
+
async writeFile(path, content, _opts) {
|
|
47
|
+
if (typeof content === "string") await cfFs.writeFile(path, content);
|
|
48
|
+
else await cfFs.writeFileBytes(path, content);
|
|
49
|
+
},
|
|
50
|
+
appendFile: (path, content, _opts) => cfFs.appendFile(path, content),
|
|
51
|
+
exists: (path) => cfFs.exists(path),
|
|
52
|
+
async stat(path) {
|
|
53
|
+
return adaptStat(await cfFs.stat(path));
|
|
54
|
+
},
|
|
55
|
+
async lstat(path) {
|
|
56
|
+
return adaptStat(await cfFs.lstat(path));
|
|
57
|
+
},
|
|
58
|
+
mkdir: (path, opts) => cfFs.mkdir(path, opts),
|
|
59
|
+
readdir: (path) => cfFs.readdir(path),
|
|
60
|
+
async readdirWithFileTypes(path) {
|
|
61
|
+
return (await cfFs.readdirWithFileTypes(path)).map((e) => ({
|
|
62
|
+
name: e.name,
|
|
63
|
+
isFile: e.type === "file",
|
|
64
|
+
isDirectory: e.type === "directory",
|
|
65
|
+
isSymbolicLink: e.type === "symlink"
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
rm: (path, opts) => cfFs.rm(path, opts),
|
|
69
|
+
cp: (src, dest, opts) => cfFs.cp(src, dest, opts),
|
|
70
|
+
mv: (src, dest) => cfFs.mv(src, dest),
|
|
71
|
+
resolvePath: (base, path) => cfFs.resolvePath(base, path),
|
|
72
|
+
getAllPaths: () => [],
|
|
73
|
+
async chmod(_path, _mode) {},
|
|
74
|
+
symlink: (target, linkPath) => cfFs.symlink(target, linkPath),
|
|
75
|
+
async link(existingPath, newPath) {
|
|
76
|
+
const content = await cfFs.readFileBytes(existingPath);
|
|
77
|
+
await cfFs.writeFileBytes(newPath, content);
|
|
78
|
+
},
|
|
79
|
+
readlink: (path) => cfFs.readlink(path),
|
|
80
|
+
realpath: (path) => cfFs.realpath(path),
|
|
81
|
+
async utimes(_path, _atime, _mtime) {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async function getVirtualSandbox(bucket, options) {
|
|
85
|
+
if (bucket === void 0) {
|
|
86
|
+
const { Bash, InMemoryFs } = await import(
|
|
87
|
+
/* @vite-ignore */
|
|
88
|
+
"just-bash"
|
|
89
|
+
);
|
|
90
|
+
const fs = new InMemoryFs();
|
|
91
|
+
return () => new Bash({
|
|
92
|
+
fs,
|
|
93
|
+
network: { dangerouslyAllowFullInternetAccess: true }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const { storage } = getCloudflareContext();
|
|
97
|
+
const prefix = options?.prefix ?? "default";
|
|
98
|
+
const r2Adapter = adaptToJustBash(new WorkspaceFileSystem(new Workspace({
|
|
99
|
+
sql: storage.sql,
|
|
100
|
+
r2: bucket,
|
|
101
|
+
name: () => prefix
|
|
102
|
+
})));
|
|
103
|
+
const { Bash, MountableFs, InMemoryFs } = await import(
|
|
104
|
+
/* @vite-ignore */
|
|
105
|
+
"just-bash"
|
|
106
|
+
);
|
|
107
|
+
const fs = new MountableFs({ base: new InMemoryFs() });
|
|
108
|
+
fs.mount("/workspace", r2Adapter);
|
|
109
|
+
return () => new Bash({
|
|
110
|
+
fs,
|
|
111
|
+
cwd: "/workspace",
|
|
112
|
+
network: { dangerouslyAllowFullInternetAccess: true }
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/cloudflare/define-command.ts
|
|
118
|
+
/**
|
|
119
|
+
* Cloudflare-specific `defineCommand`. Function form only — Workers cannot
|
|
120
|
+
* spawn host processes, so there is no pass-through sugar. The user supplies
|
|
121
|
+
* an executor (typically `fetch`-based or SDK-based) and benefits from
|
|
122
|
+
* return-shape normalization plus automatic throw-catching.
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* const issues = defineCommand('issues', async (args) => {
|
|
126
|
+
* const res = await fetch(`https://api.github.com/...`);
|
|
127
|
+
* return { stdout: await res.text() };
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
function defineCommand(name, execute) {
|
|
132
|
+
return {
|
|
133
|
+
name,
|
|
134
|
+
execute: normalizeExecutor(execute)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/cloudflare/cf-sandbox.ts
|
|
140
|
+
/** Wraps a @cloudflare/sandbox instance (from getSandbox()) into SessionEnv. */
|
|
141
|
+
async function cfSandboxToSessionEnv(sandbox, cwd = "/workspace") {
|
|
142
|
+
return createSandboxSessionEnv({
|
|
143
|
+
async readFile(path) {
|
|
144
|
+
return (await sandbox.readFile(path)).content;
|
|
145
|
+
},
|
|
146
|
+
async readFileBuffer(path) {
|
|
147
|
+
const file = await sandbox.readFile(path, { encoding: "base64" });
|
|
148
|
+
const binary = atob(file.content);
|
|
149
|
+
const bytes = new Uint8Array(binary.length);
|
|
150
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
151
|
+
return bytes;
|
|
152
|
+
},
|
|
153
|
+
async writeFile(path, content) {
|
|
154
|
+
if (typeof content === "string") await sandbox.writeFile(path, content);
|
|
155
|
+
else {
|
|
156
|
+
let binary = "";
|
|
157
|
+
for (let i = 0; i < content.length; i++) binary += String.fromCharCode(content[i]);
|
|
158
|
+
const b64 = btoa(binary);
|
|
159
|
+
await sandbox.writeFile(path, b64, { encoding: "base64" });
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async stat(path) {
|
|
163
|
+
const result = await sandbox.exec(`stat -c '{"size":%s,"isDir":%F}' '${path.replace(/'/g, "'\\''")}'`);
|
|
164
|
+
if (!result.success) throw new Error(`stat failed for ${path}: ${result.stderr}`);
|
|
165
|
+
try {
|
|
166
|
+
const raw = result.stdout.trim();
|
|
167
|
+
const sizeMatch = raw.match(/"size":(\d+)/);
|
|
168
|
+
const isDir = raw.includes("directory");
|
|
169
|
+
return {
|
|
170
|
+
isFile: !isDir,
|
|
171
|
+
isDirectory: isDir,
|
|
172
|
+
isSymbolicLink: false,
|
|
173
|
+
size: sizeMatch ? parseInt(sizeMatch[1], 10) : 0,
|
|
174
|
+
mtime: /* @__PURE__ */ new Date()
|
|
175
|
+
};
|
|
176
|
+
} catch {
|
|
177
|
+
throw new Error(`Failed to parse stat output for ${path}: ${result.stdout}`);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
async readdir(path) {
|
|
181
|
+
const result = await sandbox.exec(`ls -1 '${path.replace(/'/g, "'\\''")}'`);
|
|
182
|
+
if (!result.success) throw new Error(`readdir failed for ${path}: ${result.stderr}`);
|
|
183
|
+
return result.stdout.trim().split("\n").filter((s) => s.length > 0);
|
|
184
|
+
},
|
|
185
|
+
async exists(path) {
|
|
186
|
+
return (await sandbox.exists(path)).exists;
|
|
187
|
+
},
|
|
188
|
+
async mkdir(path, opts) {
|
|
189
|
+
await sandbox.mkdir(path, opts);
|
|
190
|
+
},
|
|
191
|
+
async rm(path, opts) {
|
|
192
|
+
if (opts?.recursive || opts?.force) {
|
|
193
|
+
const flags = [opts.recursive ? "-r" : "", opts.force ? "-f" : ""].filter(Boolean).join("");
|
|
194
|
+
await sandbox.exec(`rm ${flags} '${path.replace(/'/g, "'\\''")}'`);
|
|
195
|
+
} else await sandbox.deleteFile(path);
|
|
196
|
+
},
|
|
197
|
+
async exec(command, execOpts) {
|
|
198
|
+
const result = await sandbox.exec(command, {
|
|
199
|
+
cwd: execOpts?.cwd,
|
|
200
|
+
env: execOpts?.env
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
stdout: result.stdout ?? "",
|
|
204
|
+
stderr: result.stderr ?? "",
|
|
205
|
+
exitCode: result.exitCode ?? (result.success ? 0 : 1)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}, cwd);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/cloudflare/session-store.ts
|
|
213
|
+
function store() {
|
|
214
|
+
return {
|
|
215
|
+
async save(id, data) {
|
|
216
|
+
const { agentInstance } = getCloudflareContext();
|
|
217
|
+
const sessions = { ...agentInstance.state?.sessions ?? {} };
|
|
218
|
+
sessions[id] = data;
|
|
219
|
+
agentInstance.setState({
|
|
220
|
+
...agentInstance.state,
|
|
221
|
+
sessions
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
async load(id) {
|
|
225
|
+
const { agentInstance } = getCloudflareContext();
|
|
226
|
+
return agentInstance.state?.sessions?.[id] ?? null;
|
|
227
|
+
},
|
|
228
|
+
async delete(id) {
|
|
229
|
+
const { agentInstance } = getCloudflareContext();
|
|
230
|
+
const sessions = { ...agentInstance.state?.sessions ?? {} };
|
|
231
|
+
delete sessions[id];
|
|
232
|
+
agentInstance.setState({
|
|
233
|
+
...agentInstance.state,
|
|
234
|
+
sessions
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
export { cfSandboxToSessionEnv, defineCommand, getCloudflareContext, getVirtualSandbox, runWithCloudflareContext, store };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { D as ShellResult } from "./types-T8pE1xIS.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/command-helpers.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Loose return shape accepted from user-supplied command executors. All forms
|
|
6
|
+
* are normalized to a full `ShellResult` by `normalizeExecutor()`.
|
|
7
|
+
*/
|
|
8
|
+
type CommandExecutorResult = ShellResult | {
|
|
9
|
+
stdout?: string;
|
|
10
|
+
stderr?: string;
|
|
11
|
+
exitCode?: number;
|
|
12
|
+
} | string | void;
|
|
13
|
+
/**
|
|
14
|
+
* User-supplied command executor. Can return a full `ShellResult`, a partial
|
|
15
|
+
* `{ stdout?, stderr?, exitCode? }` object, a bare string (treated as stdout),
|
|
16
|
+
* or void (empty success). Thrown errors are caught and converted to an
|
|
17
|
+
* `exitCode`-bearing `ShellResult` — no `try`/`catch` needed at the call site.
|
|
18
|
+
*/
|
|
19
|
+
type CommandExecutor = (args: string[]) => Promise<CommandExecutorResult>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { CommandExecutor as t };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/command-helpers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a user-supplied `CommandExecutor` to always resolve with a full
|
|
4
|
+
* `ShellResult`. Applies loose-return normalization and catches throws.
|
|
5
|
+
*/
|
|
6
|
+
function normalizeExecutor(executor) {
|
|
7
|
+
return async (args) => {
|
|
8
|
+
try {
|
|
9
|
+
const raw = await executor(args);
|
|
10
|
+
if (raw === void 0 || raw === null) return {
|
|
11
|
+
stdout: "",
|
|
12
|
+
stderr: "",
|
|
13
|
+
exitCode: 0
|
|
14
|
+
};
|
|
15
|
+
if (typeof raw === "string") return {
|
|
16
|
+
stdout: raw,
|
|
17
|
+
stderr: "",
|
|
18
|
+
exitCode: 0
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
stdout: raw.stdout ?? "",
|
|
22
|
+
stderr: raw.stderr ?? "",
|
|
23
|
+
exitCode: raw.exitCode ?? 0
|
|
24
|
+
};
|
|
25
|
+
} catch (err) {
|
|
26
|
+
const e = err ?? {};
|
|
27
|
+
return {
|
|
28
|
+
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
29
|
+
stderr: typeof e.stderr === "string" ? e.stderr : String(err),
|
|
30
|
+
exitCode: typeof e.code === "number" ? e.code : 1
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { normalizeExecutor as t };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { A as TaskOptions, C as SessionEnv, D as ShellResult, E as ShellOptions, M as ToolParameters, O as Skill, S as SessionData, T as SessionStore, _ as FlueSessions, a as BashLike, b as Role, c as BuildPlugin, d as FileStat, f as FlueAgent, g as FlueSession, h as FlueEventCallback, i as BashFactory, j as ToolDef, k as SkillOptions, l as Command, m as FlueEvent, n as AgentInfo, o as BuildContext, p as FlueContext, r as AgentInit, s as BuildOptions, t as AgentConfig, u as CommandDef, v as PromptOptions, w as SessionOptions, x as SandboxFactory, y as PromptResponse } from "./types-T8pE1xIS.mjs";
|
|
2
|
+
import { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
3
|
+
|
|
4
|
+
//#region src/build.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Result returned by {@link build}. `changed` indicates whether any file in
|
|
7
|
+
* `dist/` was actually modified. Callers (notably the dev server) use this to
|
|
8
|
+
* skip restarting downstream processes for no-op rebuilds on agent body edits.
|
|
9
|
+
*/
|
|
10
|
+
interface BuildResult {
|
|
11
|
+
changed: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build a workspace into a deployable artifact.
|
|
15
|
+
*
|
|
16
|
+
* `options.workspaceDir` is treated as an explicit workspace root — the directory
|
|
17
|
+
* directly containing agents/ and roles/. No .flue/ waterfall is performed here;
|
|
18
|
+
* callers that want waterfall behavior (e.g. the CLI when --workspace is omitted)
|
|
19
|
+
* should use `resolveWorkspaceFromCwd` first.
|
|
20
|
+
*
|
|
21
|
+
* AGENTS.md and .agents/skills/ are NOT bundled — discovered at runtime from session cwd.
|
|
22
|
+
*/
|
|
23
|
+
declare function build(options: BuildOptions): Promise<BuildResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a Flue workspace directory from the current working directory,
|
|
26
|
+
* using the two-layout convention. Intended for the CLI when `--workspace` is
|
|
27
|
+
* not provided — callers that pass an explicit workspace path should skip this
|
|
28
|
+
* and pass the path straight to `build()`.
|
|
29
|
+
*
|
|
30
|
+
* Two supported layouts, checked in order:
|
|
31
|
+
* 1. `<cwd>/.flue/` — use this when Flue is embedded in an existing project.
|
|
32
|
+
* 2. `<cwd>/` — use this when the project itself is the Flue workspace.
|
|
33
|
+
*
|
|
34
|
+
* If `.flue/` exists, it wins unconditionally — no mixing with the bare layout.
|
|
35
|
+
* Returns null if neither is present so the caller can produce a helpful error.
|
|
36
|
+
*/
|
|
37
|
+
declare function resolveWorkspaceFromCwd(cwd: string): string | null;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/dev.d.ts
|
|
40
|
+
interface DevOptions {
|
|
41
|
+
workspaceDir: string;
|
|
42
|
+
outputDir: string;
|
|
43
|
+
target: 'node' | 'cloudflare';
|
|
44
|
+
/** Defaults to 3583 ("FLUE" on a phone keypad). */
|
|
45
|
+
port?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Default port for `flue dev`. F=3, L=5, U=8, E=3 on a phone keypad. */
|
|
48
|
+
declare const DEFAULT_DEV_PORT = 3583;
|
|
49
|
+
/**
|
|
50
|
+
* Start a Flue dev server. Resolves only when the server is shut down (e.g.
|
|
51
|
+
* via SIGINT). Errors during the initial build/start are thrown synchronously;
|
|
52
|
+
* errors during subsequent rebuilds are logged but do NOT exit the dev server
|
|
53
|
+
* — the user is editing code, after all, and we want to recover when they fix it.
|
|
54
|
+
*/
|
|
55
|
+
declare function dev(options: DevOptions): Promise<void>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/agent.d.ts
|
|
58
|
+
declare const BUILTIN_TOOL_NAMES: Set<string>;
|
|
59
|
+
interface TaskToolParams {
|
|
60
|
+
prompt: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
role?: string;
|
|
63
|
+
cwd?: string;
|
|
64
|
+
}
|
|
65
|
+
interface TaskToolResultDetails {
|
|
66
|
+
taskId: string;
|
|
67
|
+
sessionId: string;
|
|
68
|
+
messageId?: string;
|
|
69
|
+
role?: string;
|
|
70
|
+
cwd?: string;
|
|
71
|
+
}
|
|
72
|
+
interface CreateToolsOptions {
|
|
73
|
+
task?: (params: TaskToolParams, signal?: AbortSignal) => Promise<AgentToolResult<TaskToolResultDetails>>;
|
|
74
|
+
roles?: Record<string, Role>;
|
|
75
|
+
}
|
|
76
|
+
declare function createTools(env: SessionEnv, options?: CreateToolsOptions): AgentTool<any>[];
|
|
77
|
+
//#endregion
|
|
78
|
+
export { type AgentConfig, type AgentInfo, type AgentInit, BUILTIN_TOOL_NAMES, type BashFactory, type BashLike, type BuildContext, type BuildOptions, type BuildPlugin, type Command, type CommandDef, DEFAULT_DEV_PORT, type DevOptions, type FileStat, type FlueAgent, type FlueContext, type FlueEvent, type FlueEventCallback, type FlueSession, type FlueSessions, type PromptOptions, type PromptResponse, type Role, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type Skill, type SkillOptions, type TaskOptions, type ToolDef, type ToolParameters, build, createTools, dev, resolveWorkspaceFromCwd };
|