@flue/sdk 0.1.1 → 0.1.3
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 +4 -9
- package/dist/agent-BYG0nVbQ.mjs +432 -0
- package/dist/client.d.mts +2 -7
- package/dist/client.mjs +4 -9
- package/dist/cloudflare/index.d.mts +6 -2
- package/dist/cloudflare/index.mjs +26 -2
- package/dist/command-helpers-BPcSV93o.d.mts +21 -0
- package/dist/command-helpers-CxRhK1my.mjs +37 -0
- package/dist/index.d.mts +2 -13
- package/dist/index.mjs +48 -60
- package/dist/internal.d.mts +15 -0
- package/dist/internal.mjs +6 -0
- package/dist/node/index.d.mts +14 -0
- package/dist/node/index.mjs +75 -0
- package/dist/sandbox.d.mts +1 -1
- package/dist/sandbox.mjs +2 -1
- package/dist/{session-0gnaB_aY.mjs → session-CiAMTsLZ.mjs} +28 -438
- package/dist/{types-C97_qJ21.d.mts → types-BZPltYah.d.mts} +8 -2
- package/package.json +9 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { createFlueContext } from "./client.mjs";
|
|
1
|
+
import { a as parseFrontmatterFile, n as createTools, t as BUILTIN_TOOL_NAMES } from "./agent-BYG0nVbQ.mjs";
|
|
3
2
|
import * as esbuild from "esbuild";
|
|
4
3
|
import * as fs from "node:fs";
|
|
5
4
|
import * as path from "node:path";
|
|
@@ -9,43 +8,30 @@ import { packageUpSync } from "package-up";
|
|
|
9
8
|
var CloudflarePlugin = class {
|
|
10
9
|
name = "cloudflare";
|
|
11
10
|
generateEntryPoint(ctx) {
|
|
12
|
-
const { agents, roles
|
|
11
|
+
const { agents, roles } = ctx;
|
|
13
12
|
const rolesJson = JSON.stringify(roles);
|
|
14
13
|
const webhookAgents = agents.filter((a) => a.triggers.webhook);
|
|
15
|
-
const agentImports = agents.map((a) => {
|
|
16
|
-
return `import ${agentVarName$1(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
|
|
17
|
-
}).join("\n");
|
|
18
|
-
const manifest = JSON.stringify({ agents: agents.map((a) => ({
|
|
19
|
-
name: a.name,
|
|
20
|
-
triggers: a.triggers
|
|
21
|
-
})) }, null, 2);
|
|
22
|
-
const agentClasses = webhookAgents.map((a) => {
|
|
23
|
-
const className = agentClassName(a.name);
|
|
24
|
-
const handlerVar = agentVarName$1(a.name);
|
|
25
|
-
return `export class ${className} extends Agent {
|
|
26
|
-
async onRequest(request) {
|
|
27
|
-
return handleAgentRequest(request, this, ${JSON.stringify(a.name)}, ${handlerVar});
|
|
28
|
-
}
|
|
29
|
-
}`;
|
|
30
|
-
}).join("\n\n");
|
|
31
14
|
return `
|
|
32
15
|
// Auto-generated by @flue/sdk build (cloudflare)
|
|
33
16
|
import { Agent, routeAgentRequest } from 'agents';
|
|
34
17
|
import { Bash, InMemoryFs } from 'just-bash';
|
|
35
18
|
import { getModel } from '@mariozechner/pi-ai';
|
|
36
|
-
import { createFlueContext } from '
|
|
37
|
-
import { InMemorySessionStore } from '${resolveSDKImport("session")}';
|
|
38
|
-
import { bashToSessionEnv } from '${resolveSDKImport("sandbox")}';
|
|
19
|
+
import { createFlueContext, InMemorySessionStore, bashToSessionEnv } from '@flue/sdk/internal';
|
|
39
20
|
import { setCloudflareContext, clearCloudflareContext, cfSandboxToSessionEnv } from '@flue/sdk/cloudflare';
|
|
40
21
|
|
|
41
|
-
${
|
|
22
|
+
${agents.map((a) => {
|
|
23
|
+
return `import ${agentVarName$1(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
|
|
24
|
+
}).join("\n")}
|
|
42
25
|
|
|
43
26
|
// ─── Config ─────────────────────────────────────────────────────────────────
|
|
44
27
|
|
|
45
28
|
const roles = ${rolesJson};
|
|
46
29
|
const skills = {};
|
|
47
30
|
const systemPrompt = '';
|
|
48
|
-
const manifest = ${
|
|
31
|
+
const manifest = ${JSON.stringify({ agents: agents.map((a) => ({
|
|
32
|
+
name: a.name,
|
|
33
|
+
triggers: a.triggers
|
|
34
|
+
})) }, null, 2)};
|
|
49
35
|
|
|
50
36
|
// ─── Infrastructure ─────────────────────────────────────────────────────────
|
|
51
37
|
|
|
@@ -279,7 +265,15 @@ async function handleAgentRequest(request, doInstance, agentName, handler) {
|
|
|
279
265
|
|
|
280
266
|
// ─── Per-Agent Durable Object Classes ──────────────────────────────────────
|
|
281
267
|
|
|
282
|
-
${
|
|
268
|
+
${webhookAgents.map((a) => {
|
|
269
|
+
const className = agentClassName(a.name);
|
|
270
|
+
const handlerVar = agentVarName$1(a.name);
|
|
271
|
+
return `export class ${className} extends Agent {
|
|
272
|
+
async onRequest(request) {
|
|
273
|
+
return handleAgentRequest(request, this, ${JSON.stringify(a.name)}, ${handlerVar});
|
|
274
|
+
}
|
|
275
|
+
}`;
|
|
276
|
+
}).join("\n\n")}
|
|
283
277
|
|
|
284
278
|
// Re-export Sandbox DO class for wrangler binding
|
|
285
279
|
export { Sandbox } from '@cloudflare/sandbox';
|
|
@@ -384,18 +378,9 @@ function agentClassName(name) {
|
|
|
384
378
|
var NodePlugin = class {
|
|
385
379
|
name = "node";
|
|
386
380
|
generateEntryPoint(ctx) {
|
|
387
|
-
const { agents, roles
|
|
381
|
+
const { agents, roles } = ctx;
|
|
388
382
|
const rolesJson = JSON.stringify(roles);
|
|
389
383
|
const webhookAgents = agents.filter((a) => a.triggers.webhook);
|
|
390
|
-
const agentImports = agents.map((a) => {
|
|
391
|
-
return `import ${agentVarName(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
|
|
392
|
-
}).join("\n");
|
|
393
|
-
const handlerMapEntries = agents.map((a) => ` ${JSON.stringify(a.name)}: ${agentVarName(a.name)},`).join("\n");
|
|
394
|
-
const webhookNames = JSON.stringify(webhookAgents.map((a) => a.name));
|
|
395
|
-
const manifest = JSON.stringify({ agents: agents.map((a) => ({
|
|
396
|
-
name: a.name,
|
|
397
|
-
triggers: a.triggers
|
|
398
|
-
})) }, null, 2);
|
|
399
384
|
return `
|
|
400
385
|
// Auto-generated by @flue/sdk build (node)
|
|
401
386
|
import { Hono } from 'hono';
|
|
@@ -403,12 +388,12 @@ import { streamSSE } from 'hono/streaming';
|
|
|
403
388
|
import { serve } from '@hono/node-server';
|
|
404
389
|
import { Bash, InMemoryFs, MountableFs, ReadWriteFs } from 'just-bash';
|
|
405
390
|
import { getModel } from '@mariozechner/pi-ai';
|
|
406
|
-
import { createFlueContext } from '
|
|
407
|
-
import { InMemorySessionStore } from '${resolveSDKImport("session")}';
|
|
408
|
-
import { bashToSessionEnv } from '${resolveSDKImport("sandbox")}';
|
|
391
|
+
import { createFlueContext, InMemorySessionStore, bashToSessionEnv } from '@flue/sdk/internal';
|
|
409
392
|
import { randomUUID } from 'node:crypto';
|
|
410
393
|
|
|
411
|
-
${
|
|
394
|
+
${agents.map((a) => {
|
|
395
|
+
return `import ${agentVarName(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
|
|
396
|
+
}).join("\n")}
|
|
412
397
|
|
|
413
398
|
// ─── Config ─────────────────────────────────────────────────────────────────
|
|
414
399
|
|
|
@@ -417,12 +402,22 @@ const roles = ${rolesJson};
|
|
|
417
402
|
const systemPrompt = '';
|
|
418
403
|
|
|
419
404
|
const handlers = {
|
|
420
|
-
${
|
|
405
|
+
${agents.map((a) => ` ${JSON.stringify(a.name)}: ${agentVarName(a.name)},`).join("\n")}
|
|
421
406
|
};
|
|
422
407
|
|
|
423
|
-
const webhookAgents = new Set(${
|
|
408
|
+
const webhookAgents = new Set(${JSON.stringify(webhookAgents.map((a) => a.name))});
|
|
424
409
|
|
|
425
|
-
|
|
410
|
+
// When the CLI starts this server via \`flue run\`, it sets FLUE_MODE=local.
|
|
411
|
+
// In local mode the HTTP route accepts any registered agent (including
|
|
412
|
+
// trigger-less CI-only agents). In any other mode the route is restricted to
|
|
413
|
+
// agents with \`webhook: true\`, preventing accidental public exposure of
|
|
414
|
+
// agents that the user only intended to invoke from their CI pipeline.
|
|
415
|
+
const isLocalMode = process.env.FLUE_MODE === 'local';
|
|
416
|
+
|
|
417
|
+
const manifest = ${JSON.stringify({ agents: agents.map((a) => ({
|
|
418
|
+
name: a.name,
|
|
419
|
+
triggers: a.triggers
|
|
420
|
+
})) }, null, 2)};
|
|
426
421
|
|
|
427
422
|
// ─── Infrastructure ─────────────────────────────────────────────────────────
|
|
428
423
|
|
|
@@ -521,7 +516,7 @@ app.post('/agents/:name/:sessionId', async (c) => {
|
|
|
521
516
|
if (!handlers[name]) {
|
|
522
517
|
return c.json({ error: 'Agent not found' }, 404);
|
|
523
518
|
}
|
|
524
|
-
if (!webhookAgents.has(name)) {
|
|
519
|
+
if (!webhookAgents.has(name) && !isLocalMode) {
|
|
525
520
|
return c.json({ error: 'Agent "' + name + '" is not web-accessible (no webhook trigger)' }, 404);
|
|
526
521
|
}
|
|
527
522
|
|
|
@@ -600,7 +595,12 @@ const port = parseInt(process.env.PORT || '3000', 10);
|
|
|
600
595
|
|
|
601
596
|
const server = serve({ fetch: app.fetch, port });
|
|
602
597
|
console.log('[flue] Server listening on http://localhost:' + port);
|
|
603
|
-
|
|
598
|
+
if (isLocalMode) {
|
|
599
|
+
console.log('[flue] Mode: local (all agents invokable, including trigger-less)');
|
|
600
|
+
console.log('[flue] Available agents: ' + ${JSON.stringify(agents.map((a) => a.name).join(", "))});
|
|
601
|
+
} else {
|
|
602
|
+
console.log('[flue] Available agents: ' + ${JSON.stringify(webhookAgents.map((a) => a.name).join(", "))});
|
|
603
|
+
}
|
|
604
604
|
|
|
605
605
|
process.on('SIGINT', () => { server.close(); process.exit(0); });
|
|
606
606
|
process.on('SIGTERM', () => { server.close(); process.exit(0); });
|
|
@@ -631,13 +631,14 @@ async function build(options) {
|
|
|
631
631
|
const roles = discoverRoles(agentDir);
|
|
632
632
|
const agents = discoverAgents(agentDir);
|
|
633
633
|
if (agents.length === 0) throw new Error(`No agents found in ${path.join(agentDir, ".flue/agents/")}`);
|
|
634
|
-
for (const agent of agents) if (!(agent.triggers.webhook || agent.triggers.cron)) throw new Error(`[flue] Agent "${agent.name}" has no triggers configured. Add a triggers export to your agent file:\n\n export const triggers = { webhook: true };\n\nAvailable triggers: webhook (HTTP endpoint), cron (scheduled)`);
|
|
635
634
|
const webhookAgents = agents.filter((a) => a.triggers.webhook);
|
|
636
635
|
const cronAgents = agents.filter((a) => a.triggers.cron);
|
|
636
|
+
const triggerlessAgents = agents.filter((a) => !a.triggers.webhook && !a.triggers.cron);
|
|
637
637
|
console.log(`[flue] Found ${Object.keys(roles).length} role(s): ${Object.keys(roles).join(", ") || "(none)"}`);
|
|
638
638
|
console.log(`[flue] Found ${agents.length} agent(s): ${agents.map((a) => a.name).join(", ")}`);
|
|
639
639
|
if (webhookAgents.length > 0) console.log(`[flue] Webhook agents: ${webhookAgents.map((a) => a.name).join(", ")}`);
|
|
640
640
|
if (cronAgents.length > 0) console.log(`[flue] Cron agents (manifest only): ${cronAgents.map((a) => `${a.name} (${a.triggers.cron})`).join(", ")}`);
|
|
641
|
+
if (triggerlessAgents.length > 0) console.log(`[flue] CLI-only agents (no HTTP route in deployed build): ${triggerlessAgents.map((a) => a.name).join(", ")}`);
|
|
641
642
|
console.log(`[flue] AGENTS.md and .agents/skills/ will be discovered at runtime from session cwd`);
|
|
642
643
|
const distDir = path.join(agentDir, "dist");
|
|
643
644
|
fs.mkdirSync(distDir, { recursive: true });
|
|
@@ -652,8 +653,7 @@ async function build(options) {
|
|
|
652
653
|
agents,
|
|
653
654
|
roles,
|
|
654
655
|
agentDir,
|
|
655
|
-
options
|
|
656
|
-
resolveSDKImport: resolveSDKImportFn
|
|
656
|
+
options
|
|
657
657
|
};
|
|
658
658
|
const serverCode = plugin.generateEntryPoint(ctx);
|
|
659
659
|
const entryPath = path.join(distDir, "_entry_server.ts");
|
|
@@ -786,18 +786,6 @@ function getSDKDir() {
|
|
|
786
786
|
return __dirname;
|
|
787
787
|
}
|
|
788
788
|
}
|
|
789
|
-
function getSDKSrcDir() {
|
|
790
|
-
const thisDir = getSDKDir();
|
|
791
|
-
if (thisDir.endsWith("/dist") || thisDir.endsWith("\\dist")) {
|
|
792
|
-
const srcDir = path.join(path.dirname(thisDir), "src");
|
|
793
|
-
if (fs.existsSync(srcDir)) return srcDir;
|
|
794
|
-
}
|
|
795
|
-
return thisDir;
|
|
796
|
-
}
|
|
797
|
-
function resolveSDKImportFn(module) {
|
|
798
|
-
const srcDir = getSDKSrcDir();
|
|
799
|
-
return path.join(srcDir, `${module}.ts`).replace(/\\/g, "/");
|
|
800
|
-
}
|
|
801
789
|
|
|
802
790
|
//#endregion
|
|
803
|
-
export { BUILTIN_TOOL_NAMES,
|
|
791
|
+
export { BUILTIN_TOOL_NAMES, build, createTools };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { S as SessionStore, y as SessionData } from "./types-BZPltYah.mjs";
|
|
2
|
+
import { FlueContextConfig, FlueContextInternal, createFlueContext } from "./client.mjs";
|
|
3
|
+
import { bashToSessionEnv } from "./sandbox.mjs";
|
|
4
|
+
import "valibot";
|
|
5
|
+
|
|
6
|
+
//#region src/session.d.ts
|
|
7
|
+
/** In-memory session store. Sessions persist for the lifetime of the process. */
|
|
8
|
+
declare class InMemorySessionStore implements SessionStore {
|
|
9
|
+
private store;
|
|
10
|
+
save(id: string, data: SessionData): Promise<void>;
|
|
11
|
+
load(id: string): Promise<SessionData | null>;
|
|
12
|
+
delete(id: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { type FlueContextConfig, type FlueContextInternal, InMemorySessionStore, bashToSessionEnv, createFlueContext };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "./agent-BYG0nVbQ.mjs";
|
|
2
|
+
import { t as InMemorySessionStore } from "./session-CiAMTsLZ.mjs";
|
|
3
|
+
import { bashToSessionEnv } from "./sandbox.mjs";
|
|
4
|
+
import { createFlueContext } from "./client.mjs";
|
|
5
|
+
|
|
6
|
+
export { InMemorySessionStore, bashToSessionEnv, createFlueContext };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { s as Command } from "../types-BZPltYah.mjs";
|
|
2
|
+
import { t as CommandExecutor } from "../command-helpers-BPcSV93o.mjs";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
//#region src/node/define-command.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options forwarded directly to Node's `child_process.execFile`. Full pass-through.
|
|
8
|
+
*/
|
|
9
|
+
type CommandOptions = NonNullable<Parameters<typeof execFile>[2]>;
|
|
10
|
+
declare function defineCommand(name: string): Command;
|
|
11
|
+
declare function defineCommand(name: string, options: CommandOptions): Command;
|
|
12
|
+
declare function defineCommand(name: string, execute: CommandExecutor): Command;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { type CommandOptions, defineCommand };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { t as normalizeExecutor } from "../command-helpers-CxRhK1my.mjs";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
//#region src/node/define-command.ts
|
|
6
|
+
/**
|
|
7
|
+
* Node-specific `defineCommand`. Supports three forms:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* defineCommand('agent-browser');
|
|
11
|
+
* defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
|
|
12
|
+
* defineCommand('gh', async (args) => ({ stdout: '...' }));
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Forms A and B shell out via `child_process.execFile`. Form C lets the user
|
|
16
|
+
* implement the command however they like. All three forms benefit from
|
|
17
|
+
* return-shape normalization and throw-catching — no `try`/`catch` or
|
|
18
|
+
* `return { stdout, stderr, exitCode: 0 }` boilerplate required.
|
|
19
|
+
*/
|
|
20
|
+
const execFileAsync = promisify(execFile);
|
|
21
|
+
/**
|
|
22
|
+
* Essential, non-sensitive environment variables automatically forwarded to
|
|
23
|
+
* pass-through commands (forms A and B). Users can override any of these —
|
|
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
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
name,
|
|
70
|
+
execute: normalizeExecutor(executor)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { defineCommand };
|
package/dist/sandbox.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as SessionEnv, c as CommandDef, r as BashLike, u as FileStat, v as SandboxFactory, w as ShellResult } from "./types-
|
|
1
|
+
import { b as SessionEnv, c as CommandDef, r as BashLike, u as FileStat, v as SandboxFactory, w as ShellResult } from "./types-BZPltYah.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/sandbox.d.ts
|
|
4
4
|
declare function bashToSessionEnv(bash: BashLike): SessionEnv;
|
package/dist/sandbox.mjs
CHANGED