@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/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { a as createTools, i as BUILTIN_TOOL_NAMES, s as parseFrontmatterFile, t as InMemorySessionStore } from "./session-0gnaB_aY.mjs";
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, resolveSDKImport } = ctx;
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 '${resolveSDKImport("client")}';
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
- ${agentImports}
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 = ${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
- ${agentClasses}
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, resolveSDKImport } = ctx;
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 '${resolveSDKImport("client")}';
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
- ${agentImports}
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
- ${handlerMapEntries}
405
+ ${agents.map((a) => ` ${JSON.stringify(a.name)}: ${agentVarName(a.name)},`).join("\n")}
421
406
  };
422
407
 
423
- const webhookAgents = new Set(${webhookNames});
408
+ const webhookAgents = new Set(${JSON.stringify(webhookAgents.map((a) => a.name))});
424
409
 
425
- const manifest = ${manifest};
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
- console.log('[flue] Available agents: ' + ${JSON.stringify(webhookAgents.map((a) => a.name).join(", "))});
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, InMemorySessionStore, build, createFlueContext, createTools };
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 };
@@ -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-C97_qJ21.mjs";
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
@@ -1,4 +1,5 @@
1
- import { r as normalizePath } from "./session-0gnaB_aY.mjs";
1
+ import "./agent-BYG0nVbQ.mjs";
2
+ import { r as normalizePath } from "./session-CiAMTsLZ.mjs";
2
3
 
3
4
  //#region src/sandbox.ts
4
5
  function bashToSessionEnv(bash) {