@flue/sdk 0.3.11 → 0.4.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 +14 -23
- package/dist/abort-Bg3qsAkU.mjs +43 -0
- package/dist/app.d.mts +106 -0
- package/dist/app.mjs +4 -0
- package/dist/client.d.mts +9 -3
- package/dist/client.mjs +10 -24
- package/dist/cloudflare/index.d.mts +10 -6
- package/dist/cloudflare/index.mjs +388 -26
- package/dist/cloudflare-model-BeiZ1pLz.d.mts +6 -0
- package/dist/config.d.mts +133 -0
- package/dist/config.mjs +195 -0
- package/dist/flue-app-CG8i4wNG.d.mts +184 -0
- package/dist/flue-app-DeTOZjPs.mjs +730 -0
- package/dist/index.d.mts +41 -19
- package/dist/index.mjs +434 -594
- package/dist/internal.d.mts +9 -272
- package/dist/internal.mjs +16 -430
- package/dist/{mcp-CcRxAwXW.d.mts → mcp-C3UBXVkR.d.mts} +1 -1
- package/dist/{mcp-DmDTeVXW.mjs → mcp-DM6yv_Qc.mjs} +19 -33
- package/dist/node/index.d.mts +8 -12
- package/dist/node/index.mjs +94 -64
- package/dist/providers-DeFRIwp0.mjs +158 -0
- package/dist/result-K1IRhWKM.mjs +685 -0
- package/dist/sandbox.d.mts +25 -4
- package/dist/sandbox.mjs +44 -62
- package/dist/{session-DlwIt7wq.mjs → session-CFOByKnM.mjs} +488 -263
- package/dist/types-BAmV4f3Q.d.mts +727 -0
- package/package.json +12 -1
- package/dist/agent-Cahthgu3.mjs +0 -453
- package/dist/command-helpers-eVG1-Iru.d.mts +0 -21
- package/dist/command-helpers-hTZKWK13.mjs +0 -37
- package/dist/types-DGpyKMFm.d.mts +0 -508
package/dist/node/index.mjs
CHANGED
|
@@ -1,75 +1,105 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import
|
|
1
|
+
import { t as abortErrorFor } from "../abort-Bg3qsAkU.mjs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { exec } from "node:child_process";
|
|
3
4
|
import { promisify } from "node:util";
|
|
5
|
+
import * as fs from "node:fs/promises";
|
|
4
6
|
|
|
5
|
-
//#region src/node/
|
|
7
|
+
//#region src/node/local-env.ts
|
|
6
8
|
/**
|
|
7
|
-
* Node
|
|
9
|
+
* Pure-Node `SessionEnv` backed by the host filesystem and `child_process`.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* defineCommand('gh', async (args) => ({ stdout: '...' }));
|
|
13
|
-
* ```
|
|
11
|
+
* Powers `init({ sandbox: 'local' })` on the Node target. No sandboxing,
|
|
12
|
+
* no virtual filesystem, no just-bash — `exec` shells out via the user's
|
|
13
|
+
* default shell, file methods call `node:fs/promises` directly.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `return { stdout, stderr, exitCode: 0 }` boilerplate required.
|
|
15
|
+
* Use this when you're running flue inside an external sandbox (Daytona,
|
|
16
|
+
* E2B, a container, a CI runner, etc.) and want flue itself to operate on
|
|
17
|
+
* the host filesystem without an additional layer of isolation.
|
|
19
18
|
*/
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* or add their own (e.g. `GH_TOKEN`) — via `options.env`. Anything not listed
|
|
25
|
-
* here (API keys, tokens, secrets, etc.) stays on the host and is NEVER
|
|
26
|
-
* exposed to the spawned process unless the caller opts in explicitly.
|
|
27
|
-
*
|
|
28
|
-
* If you need full control over the env, use the function form:
|
|
29
|
-
* `defineCommand('gh', async (args) => { ... })`.
|
|
30
|
-
*/
|
|
31
|
-
const DEFAULT_ENV = {
|
|
32
|
-
PATH: process.env.PATH,
|
|
33
|
-
HOME: process.env.HOME,
|
|
34
|
-
USER: process.env.USER,
|
|
35
|
-
LOGNAME: process.env.LOGNAME,
|
|
36
|
-
HOSTNAME: process.env.HOSTNAME,
|
|
37
|
-
SHELL: process.env.SHELL,
|
|
38
|
-
LANG: process.env.LANG,
|
|
39
|
-
LC_ALL: process.env.LC_ALL,
|
|
40
|
-
LC_CTYPE: process.env.LC_CTYPE,
|
|
41
|
-
TZ: process.env.TZ,
|
|
42
|
-
TERM: process.env.TERM,
|
|
43
|
-
TMPDIR: process.env.TMPDIR,
|
|
44
|
-
TMP: process.env.TMP,
|
|
45
|
-
TEMP: process.env.TEMP
|
|
46
|
-
};
|
|
47
|
-
function defineCommand(name, arg) {
|
|
48
|
-
if (typeof arg === "function") return {
|
|
49
|
-
name,
|
|
50
|
-
execute: normalizeExecutor(arg)
|
|
51
|
-
};
|
|
52
|
-
const userOpts = arg ?? {};
|
|
53
|
-
const mergedOpts = {
|
|
54
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
55
|
-
...userOpts,
|
|
56
|
-
env: {
|
|
57
|
-
...DEFAULT_ENV,
|
|
58
|
-
...userOpts.env ?? {}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const executor = async (args) => {
|
|
62
|
-
const { stdout, stderr } = await execFileAsync(name, args, mergedOpts);
|
|
63
|
-
return {
|
|
64
|
-
stdout: String(stdout ?? ""),
|
|
65
|
-
stderr: String(stderr ?? "")
|
|
66
|
-
};
|
|
67
|
-
};
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
function createLocalSessionEnv(options = {}) {
|
|
21
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
22
|
+
const resolvePath = (p) => path.isAbsolute(p) ? p : path.resolve(cwd, p);
|
|
68
23
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
24
|
+
async exec(command, opts) {
|
|
25
|
+
const signal = opts?.signal;
|
|
26
|
+
if (signal?.aborted) throw abortErrorFor(signal);
|
|
27
|
+
const timeoutSignal = typeof opts?.timeout === "number" ? AbortSignal.timeout(opts.timeout * 1e3) : void 0;
|
|
28
|
+
const mergedSignal = signal && timeoutSignal ? AbortSignal.any([signal, timeoutSignal]) : signal ?? timeoutSignal;
|
|
29
|
+
try {
|
|
30
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
31
|
+
cwd: opts?.cwd ? resolvePath(opts.cwd) : cwd,
|
|
32
|
+
env: opts?.env ? {
|
|
33
|
+
...process.env,
|
|
34
|
+
...opts.env
|
|
35
|
+
} : process.env,
|
|
36
|
+
signal: mergedSignal,
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
maxBuffer: 64 * 1024 * 1024
|
|
39
|
+
});
|
|
40
|
+
if (signal?.aborted) throw abortErrorFor(signal);
|
|
41
|
+
return {
|
|
42
|
+
stdout,
|
|
43
|
+
stderr,
|
|
44
|
+
exitCode: 0
|
|
45
|
+
};
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (signal?.aborted) throw abortErrorFor(signal);
|
|
48
|
+
if (err && typeof err === "object" && "code" in err) return {
|
|
49
|
+
stdout: typeof err.stdout === "string" ? err.stdout : "",
|
|
50
|
+
stderr: typeof err.stderr === "string" ? err.stderr : String(err.message ?? ""),
|
|
51
|
+
exitCode: typeof err.code === "number" ? err.code : 1
|
|
52
|
+
};
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async readFile(p) {
|
|
57
|
+
return fs.readFile(resolvePath(p), "utf8");
|
|
58
|
+
},
|
|
59
|
+
async readFileBuffer(p) {
|
|
60
|
+
const buf = await fs.readFile(resolvePath(p));
|
|
61
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
62
|
+
},
|
|
63
|
+
async writeFile(p, content) {
|
|
64
|
+
const resolved = resolvePath(p);
|
|
65
|
+
const dir = path.dirname(resolved);
|
|
66
|
+
if (dir && dir !== resolved) await fs.mkdir(dir, { recursive: true });
|
|
67
|
+
await fs.writeFile(resolved, content);
|
|
68
|
+
},
|
|
69
|
+
async stat(p) {
|
|
70
|
+
const s = await fs.stat(resolvePath(p));
|
|
71
|
+
return {
|
|
72
|
+
isFile: s.isFile(),
|
|
73
|
+
isDirectory: s.isDirectory(),
|
|
74
|
+
isSymbolicLink: s.isSymbolicLink(),
|
|
75
|
+
size: s.size,
|
|
76
|
+
mtime: s.mtime
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
async readdir(p) {
|
|
80
|
+
return fs.readdir(resolvePath(p));
|
|
81
|
+
},
|
|
82
|
+
async exists(p) {
|
|
83
|
+
try {
|
|
84
|
+
await fs.access(resolvePath(p));
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
async mkdir(p, opts) {
|
|
91
|
+
await fs.mkdir(resolvePath(p), { recursive: opts?.recursive ?? false });
|
|
92
|
+
},
|
|
93
|
+
async rm(p, opts) {
|
|
94
|
+
await fs.rm(resolvePath(p), {
|
|
95
|
+
recursive: opts?.recursive ?? false,
|
|
96
|
+
force: opts?.force ?? false
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
cwd,
|
|
100
|
+
resolvePath
|
|
71
101
|
};
|
|
72
102
|
}
|
|
73
103
|
|
|
74
104
|
//#endregion
|
|
75
|
-
export {
|
|
105
|
+
export { createLocalSessionEnv };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { registerApiProvider } from "@mariozechner/pi-ai";
|
|
2
|
+
|
|
3
|
+
//#region src/cloudflare-model.ts
|
|
4
|
+
/** Pi-ai `Api` slug for the binding-backed Workers AI provider. */
|
|
5
|
+
const CLOUDFLARE_AI_BINDING_API = "cloudflare-ai-binding";
|
|
6
|
+
/** Provider name surfaced on AssistantMessage records and usage logs. */
|
|
7
|
+
const CLOUDFLARE_AI_BINDING_PROVIDER = "workers-ai";
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/runtime/providers.ts
|
|
11
|
+
/** Runtime provider registries consumed by `resolveModel` and Session. */
|
|
12
|
+
/**
|
|
13
|
+
* pi-ai's open-ended `Api` type prevents direct discriminator narrowing.
|
|
14
|
+
*/
|
|
15
|
+
function isCloudflareBindingRegistration(def) {
|
|
16
|
+
return def.api === CLOUDFLARE_AI_BINDING_API;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* URL-prefix registry populated at module init by `app.ts` and generated
|
|
20
|
+
* server entries.
|
|
21
|
+
*/
|
|
22
|
+
const userModels = /* @__PURE__ */ new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Register a Flue-level model provider keyed by URL prefix.
|
|
25
|
+
*
|
|
26
|
+
* Last-write-wins. On Cloudflare, the generated entry reserves the
|
|
27
|
+
* `cloudflare` prefix for the built-in Workers AI binding integration.
|
|
28
|
+
*/
|
|
29
|
+
function registerProvider(name, registration) {
|
|
30
|
+
userModels.set(name, registration);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Look up a registration apiKey by the resolved pi-ai provider slug.
|
|
34
|
+
*/
|
|
35
|
+
function getRegisteredApiKey(provider) {
|
|
36
|
+
for (const [name, def] of userModels) {
|
|
37
|
+
if (effectiveProviderSlug(name, def) !== provider) continue;
|
|
38
|
+
if (!isCloudflareBindingRegistration(def)) return def.apiKey;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Re-export of pi-ai's `registerApiProvider`. Use to register a brand-new
|
|
43
|
+
* wire-protocol handler for an `api` slug pi-ai doesn't ship. Then call
|
|
44
|
+
* {@link registerProvider} to alias a URL prefix to that api.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* registerApiProvider({ api: 'my-novel-api', stream, streamSimple });
|
|
48
|
+
* registerProvider('thing', { api: 'my-novel-api', baseUrl: '...', apiKey: '...' });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* pi-ai's registry is also module-scoped and last-write-wins. Calling
|
|
52
|
+
* `registerApiProvider` repeatedly with the same `api` string overwrites,
|
|
53
|
+
* so generated code can register on every isolate boot without dedupe
|
|
54
|
+
* bookkeeping.
|
|
55
|
+
*/
|
|
56
|
+
const registerApiProvider$1 = registerApiProvider;
|
|
57
|
+
const providerOverrides = /* @__PURE__ */ new Map();
|
|
58
|
+
/**
|
|
59
|
+
* Patch transport-level settings on an existing provider while preserving its
|
|
60
|
+
* resolved Model metadata (cost, context window, token limits, etc.).
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { configureProvider } from '@flue/sdk/app';
|
|
64
|
+
*
|
|
65
|
+
* configureProvider('anthropic', {
|
|
66
|
+
* baseUrl: 'https://gateway.example.com/anthropic',
|
|
67
|
+
* apiKey: process.env.GATEWAY_KEY,
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* Keyed by the resolved `Model.provider` value, not necessarily the URL
|
|
72
|
+
* prefix. Last-write-wins.
|
|
73
|
+
*/
|
|
74
|
+
function configureProvider(provider, settings) {
|
|
75
|
+
providerOverrides.set(provider, settings);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Internal read accessor for provider overrides.
|
|
79
|
+
*/
|
|
80
|
+
function getProviderConfiguration(provider) {
|
|
81
|
+
return providerOverrides.get(provider);
|
|
82
|
+
}
|
|
83
|
+
/** Attach a Workers AI binding to a Model literal. */
|
|
84
|
+
function attachModelBinding(model, binding) {
|
|
85
|
+
return {
|
|
86
|
+
...model,
|
|
87
|
+
binding
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read a Workers AI binding off a resolved Model, or `undefined` if no
|
|
92
|
+
* usable binding is attached.
|
|
93
|
+
*/
|
|
94
|
+
function getModelBinding(model) {
|
|
95
|
+
const candidate = model.binding;
|
|
96
|
+
if (!candidate || typeof candidate.run !== "function") return;
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve `'name/modelId'` against the URL-prefix registry.
|
|
101
|
+
*/
|
|
102
|
+
function resolveRegisteredModel(name, modelId) {
|
|
103
|
+
const def = userModels.get(name);
|
|
104
|
+
if (!def) return void 0;
|
|
105
|
+
return buildModelFromRegistration(name, def, modelId);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Construct a pi-ai Model from a registered provider template. User-defined
|
|
109
|
+
* providers do not have catalog metadata, so cost and context limits default
|
|
110
|
+
* to zero. apiKey flows through `getApiKey`; it is not part of pi-ai's Model.
|
|
111
|
+
*/
|
|
112
|
+
function buildModelFromRegistration(name, def, modelId) {
|
|
113
|
+
if (isCloudflareBindingRegistration(def)) return attachModelBinding({
|
|
114
|
+
id: modelId,
|
|
115
|
+
name: modelId,
|
|
116
|
+
api: CLOUDFLARE_AI_BINDING_API,
|
|
117
|
+
provider: def.provider ?? CLOUDFLARE_AI_BINDING_PROVIDER,
|
|
118
|
+
baseUrl: "",
|
|
119
|
+
reasoning: false,
|
|
120
|
+
input: ["text"],
|
|
121
|
+
cost: {
|
|
122
|
+
input: 0,
|
|
123
|
+
output: 0,
|
|
124
|
+
cacheRead: 0,
|
|
125
|
+
cacheWrite: 0
|
|
126
|
+
},
|
|
127
|
+
contextWindow: 0,
|
|
128
|
+
maxTokens: 0
|
|
129
|
+
}, def.binding);
|
|
130
|
+
return {
|
|
131
|
+
id: modelId,
|
|
132
|
+
name: modelId,
|
|
133
|
+
api: def.api,
|
|
134
|
+
provider: def.provider ?? name,
|
|
135
|
+
baseUrl: def.baseUrl,
|
|
136
|
+
reasoning: false,
|
|
137
|
+
input: ["text"],
|
|
138
|
+
cost: {
|
|
139
|
+
input: 0,
|
|
140
|
+
output: 0,
|
|
141
|
+
cacheRead: 0,
|
|
142
|
+
cacheWrite: 0
|
|
143
|
+
},
|
|
144
|
+
contextWindow: 0,
|
|
145
|
+
maxTokens: 0,
|
|
146
|
+
headers: def.headers
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Compute the provider slug emitted on the resolved Model.
|
|
151
|
+
*/
|
|
152
|
+
function effectiveProviderSlug(name, def) {
|
|
153
|
+
if (isCloudflareBindingRegistration(def)) return def.provider ?? CLOUDFLARE_AI_BINDING_PROVIDER;
|
|
154
|
+
return def.provider ?? name;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
export { registerApiProvider$1 as a, CLOUDFLARE_AI_BINDING_API as c, getRegisteredApiKey as i, getModelBinding as n, registerProvider as o, getProviderConfiguration as r, resolveRegisteredModel as s, configureProvider as t };
|