@flue/sdk 0.1.0 → 0.1.2

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-BD0MEuO3.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,56 +8,57 @@ import { packageUpSync } from "package-up";
9
8
  var CloudflarePlugin = class {
10
9
  name = "cloudflare";
11
10
  generateEntryPoint(ctx) {
12
- const { agents, roles, options, resolveSDKImport } = ctx;
13
- const modelProvider = options.model?.provider ?? "anthropic";
14
- const modelId = options.model?.modelId ?? "claude-haiku-4-5";
11
+ const { agents, roles } = ctx;
15
12
  const rolesJson = JSON.stringify(roles);
16
13
  const webhookAgents = agents.filter((a) => a.triggers.webhook);
17
- const agentImports = agents.map((a) => {
18
- return `import ${agentVarName$1(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
19
- }).join("\n");
20
- const manifest = JSON.stringify({ agents: agents.map((a) => ({
21
- name: a.name,
22
- triggers: a.triggers
23
- })) }, null, 2);
24
- const agentClasses = webhookAgents.map((a) => {
25
- const className = agentClassName(a.name);
26
- const handlerVar = agentVarName$1(a.name);
27
- return `export class ${className} extends Agent {
28
- async onRequest(request) {
29
- return handleAgentRequest(request, this, ${JSON.stringify(a.name)}, ${handlerVar});
30
- }
31
- }`;
32
- }).join("\n\n");
33
14
  return `
34
15
  // Auto-generated by @flue/sdk build (cloudflare)
35
16
  import { Agent, routeAgentRequest } from 'agents';
36
17
  import { Bash, InMemoryFs } from 'just-bash';
37
18
  import { getModel } from '@mariozechner/pi-ai';
38
- import { createFlueContext } from '${resolveSDKImport("client")}';
39
- import { InMemorySessionStore } from '${resolveSDKImport("session")}';
40
- import { bashToSessionEnv } from '${resolveSDKImport("sandbox")}';
19
+ import { createFlueContext, InMemorySessionStore, bashToSessionEnv } from '@flue/sdk/internal';
41
20
  import { setCloudflareContext, clearCloudflareContext, cfSandboxToSessionEnv } from '@flue/sdk/cloudflare';
42
21
 
43
- ${agentImports}
22
+ ${agents.map((a) => {
23
+ return `import ${agentVarName$1(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
24
+ }).join("\n")}
44
25
 
45
26
  // ─── Config ─────────────────────────────────────────────────────────────────
46
27
 
47
28
  const roles = ${rolesJson};
48
29
  const skills = {};
49
30
  const systemPrompt = '';
50
- const manifest = ${manifest};
31
+ const manifest = ${JSON.stringify({ agents: agents.map((a) => ({
32
+ name: a.name,
33
+ triggers: a.triggers
34
+ })) }, null, 2)};
51
35
 
52
36
  // ─── Infrastructure ─────────────────────────────────────────────────────────
53
37
 
54
- const model = getModel(${JSON.stringify(modelProvider)}, ${JSON.stringify(modelId)});
38
+ // No build-time model default. The user sets model at runtime via
39
+ // \`init({ model: "provider/model-id" })\` for a session default, or via
40
+ // \`{ model: "provider/model-id" }\` on any individual prompt/skill/task call.
41
+ const model = undefined;
55
42
 
56
43
  function resolveModel(modelString) {
57
44
  const slash = modelString.indexOf('/');
58
- if (slash !== -1) {
59
- return getModel(modelString.slice(0, slash), modelString.slice(slash + 1));
45
+ if (slash === -1) {
46
+ throw new Error(
47
+ '[flue] Invalid model "' + modelString + '". ' +
48
+ 'Use the "provider/model-id" format (e.g. "anthropic/claude-haiku-4-5").'
49
+ );
60
50
  }
61
- return getModel(${JSON.stringify(modelProvider)}, modelString);
51
+ const provider = modelString.slice(0, slash);
52
+ const modelId = modelString.slice(slash + 1);
53
+ const resolved = getModel(provider, modelId);
54
+ if (!resolved) {
55
+ throw new Error(
56
+ '[flue] Unknown model "' + modelString + '". ' +
57
+ 'Provider "' + provider + '" / model id "' + modelId + '" ' +
58
+ 'is not registered with @mariozechner/pi-ai.'
59
+ );
60
+ }
61
+ return resolved;
62
62
  }
63
63
 
64
64
  // ─── Sandbox Environments ───────────────────────────────────────────────────
@@ -265,7 +265,15 @@ async function handleAgentRequest(request, doInstance, agentName, handler) {
265
265
 
266
266
  // ─── Per-Agent Durable Object Classes ──────────────────────────────────────
267
267
 
268
- ${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")}
269
277
 
270
278
  // Re-export Sandbox DO class for wrangler binding
271
279
  export { Sandbox } from '@cloudflare/sandbox';
@@ -370,20 +378,9 @@ function agentClassName(name) {
370
378
  var NodePlugin = class {
371
379
  name = "node";
372
380
  generateEntryPoint(ctx) {
373
- const { agents, roles, options, resolveSDKImport } = ctx;
374
- const modelProvider = options.model?.provider ?? "anthropic";
375
- const modelId = options.model?.modelId ?? "claude-haiku-4-5";
381
+ const { agents, roles } = ctx;
376
382
  const rolesJson = JSON.stringify(roles);
377
383
  const webhookAgents = agents.filter((a) => a.triggers.webhook);
378
- const agentImports = agents.map((a) => {
379
- return `import ${agentVarName(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
380
- }).join("\n");
381
- const handlerMapEntries = agents.map((a) => ` ${JSON.stringify(a.name)}: ${agentVarName(a.name)},`).join("\n");
382
- const webhookNames = JSON.stringify(webhookAgents.map((a) => a.name));
383
- const manifest = JSON.stringify({ agents: agents.map((a) => ({
384
- name: a.name,
385
- triggers: a.triggers
386
- })) }, null, 2);
387
384
  return `
388
385
  // Auto-generated by @flue/sdk build (node)
389
386
  import { Hono } from 'hono';
@@ -391,12 +388,12 @@ import { streamSSE } from 'hono/streaming';
391
388
  import { serve } from '@hono/node-server';
392
389
  import { Bash, InMemoryFs, MountableFs, ReadWriteFs } from 'just-bash';
393
390
  import { getModel } from '@mariozechner/pi-ai';
394
- import { createFlueContext } from '${resolveSDKImport("client")}';
395
- import { InMemorySessionStore } from '${resolveSDKImport("session")}';
396
- import { bashToSessionEnv } from '${resolveSDKImport("sandbox")}';
391
+ import { createFlueContext, InMemorySessionStore, bashToSessionEnv } from '@flue/sdk/internal';
397
392
  import { randomUUID } from 'node:crypto';
398
393
 
399
- ${agentImports}
394
+ ${agents.map((a) => {
395
+ return `import ${agentVarName(a.name)} from '${a.filePath.replace(/\\/g, "/")}';`;
396
+ }).join("\n")}
400
397
 
401
398
  // ─── Config ─────────────────────────────────────────────────────────────────
402
399
 
@@ -405,23 +402,49 @@ const roles = ${rolesJson};
405
402
  const systemPrompt = '';
406
403
 
407
404
  const handlers = {
408
- ${handlerMapEntries}
405
+ ${agents.map((a) => ` ${JSON.stringify(a.name)}: ${agentVarName(a.name)},`).join("\n")}
409
406
  };
410
407
 
411
- const webhookAgents = new Set(${webhookNames});
408
+ const webhookAgents = new Set(${JSON.stringify(webhookAgents.map((a) => a.name))});
409
+
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';
412
416
 
413
- const manifest = ${manifest};
417
+ const manifest = ${JSON.stringify({ agents: agents.map((a) => ({
418
+ name: a.name,
419
+ triggers: a.triggers
420
+ })) }, null, 2)};
414
421
 
415
422
  // ─── Infrastructure ─────────────────────────────────────────────────────────
416
423
 
417
- const model = getModel(${JSON.stringify(modelProvider)}, ${JSON.stringify(modelId)});
424
+ // No build-time model default. The user sets model at runtime via
425
+ // \`init({ model: "provider/model-id" })\` for a session default, or via
426
+ // \`{ model: "provider/model-id" }\` on any individual prompt/skill/task call.
427
+ const model = undefined;
418
428
 
419
429
  function resolveModel(modelString) {
420
430
  const slash = modelString.indexOf('/');
421
- if (slash !== -1) {
422
- return getModel(modelString.slice(0, slash), modelString.slice(slash + 1));
431
+ if (slash === -1) {
432
+ throw new Error(
433
+ '[flue] Invalid model "' + modelString + '". ' +
434
+ 'Use the "provider/model-id" format (e.g. "anthropic/claude-haiku-4-5").'
435
+ );
436
+ }
437
+ const provider = modelString.slice(0, slash);
438
+ const modelId = modelString.slice(slash + 1);
439
+ const resolved = getModel(provider, modelId);
440
+ if (!resolved) {
441
+ throw new Error(
442
+ '[flue] Unknown model "' + modelString + '". ' +
443
+ 'Provider "' + provider + '" / model id "' + modelId + '" ' +
444
+ 'is not registered with @mariozechner/pi-ai.'
445
+ );
423
446
  }
424
- return getModel(${JSON.stringify(modelProvider)}, modelString);
447
+ return resolved;
425
448
  }
426
449
 
427
450
  // ─── Sandbox Environments ───────────────────────────────────────────────────
@@ -493,7 +516,7 @@ app.post('/agents/:name/:sessionId', async (c) => {
493
516
  if (!handlers[name]) {
494
517
  return c.json({ error: 'Agent not found' }, 404);
495
518
  }
496
- if (!webhookAgents.has(name)) {
519
+ if (!webhookAgents.has(name) && !isLocalMode) {
497
520
  return c.json({ error: 'Agent "' + name + '" is not web-accessible (no webhook trigger)' }, 404);
498
521
  }
499
522
 
@@ -572,7 +595,12 @@ const port = parseInt(process.env.PORT || '3000', 10);
572
595
 
573
596
  const server = serve({ fetch: app.fetch, port });
574
597
  console.log('[flue] Server listening on http://localhost:' + port);
575
- 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
+ }
576
604
 
577
605
  process.on('SIGINT', () => { server.close(); process.exit(0); });
578
606
  process.on('SIGTERM', () => { server.close(); process.exit(0); });
@@ -603,13 +631,14 @@ async function build(options) {
603
631
  const roles = discoverRoles(agentDir);
604
632
  const agents = discoverAgents(agentDir);
605
633
  if (agents.length === 0) throw new Error(`No agents found in ${path.join(agentDir, ".flue/agents/")}`);
606
- 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)`);
607
634
  const webhookAgents = agents.filter((a) => a.triggers.webhook);
608
635
  const cronAgents = agents.filter((a) => a.triggers.cron);
636
+ const triggerlessAgents = agents.filter((a) => !a.triggers.webhook && !a.triggers.cron);
609
637
  console.log(`[flue] Found ${Object.keys(roles).length} role(s): ${Object.keys(roles).join(", ") || "(none)"}`);
610
638
  console.log(`[flue] Found ${agents.length} agent(s): ${agents.map((a) => a.name).join(", ")}`);
611
639
  if (webhookAgents.length > 0) console.log(`[flue] Webhook agents: ${webhookAgents.map((a) => a.name).join(", ")}`);
612
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(", ")}`);
613
642
  console.log(`[flue] AGENTS.md and .agents/skills/ will be discovered at runtime from session cwd`);
614
643
  const distDir = path.join(agentDir, "dist");
615
644
  fs.mkdirSync(distDir, { recursive: true });
@@ -624,8 +653,7 @@ async function build(options) {
624
653
  agents,
625
654
  roles,
626
655
  agentDir,
627
- options,
628
- resolveSDKImport: resolveSDKImportFn
656
+ options
629
657
  };
630
658
  const serverCode = plugin.generateEntryPoint(ctx);
631
659
  const entryPath = path.join(distDir, "_entry_server.ts");
@@ -758,18 +786,6 @@ function getSDKDir() {
758
786
  return __dirname;
759
787
  }
760
788
  }
761
- function getSDKSrcDir() {
762
- const thisDir = getSDKDir();
763
- if (thisDir.endsWith("/dist") || thisDir.endsWith("\\dist")) {
764
- const srcDir = path.join(path.dirname(thisDir), "src");
765
- if (fs.existsSync(srcDir)) return srcDir;
766
- }
767
- return thisDir;
768
- }
769
- function resolveSDKImportFn(module) {
770
- const srcDir = getSDKSrcDir();
771
- return path.join(srcDir, `${module}.ts`).replace(/\\/g, "/");
772
- }
773
789
 
774
790
  //#endregion
775
- 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-C8tsaK1j.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-BRLCNVG1.mjs";
3
+ import { bashToSessionEnv } from "./sandbox.mjs";
4
+ import { createFlueContext } from "./client.mjs";
5
+
6
+ export { InMemorySessionStore, bashToSessionEnv, createFlueContext };
@@ -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-xNvqlohs.mjs";
1
+ import { b as SessionEnv, c as CommandDef, r as BashLike, u as FileStat, v as SandboxFactory, w as ShellResult } from "./types-C8tsaK1j.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-BD0MEuO3.mjs";
1
+ import "./agent-BYG0nVbQ.mjs";
2
+ import { r as normalizePath } from "./session-BRLCNVG1.mjs";
2
3
 
3
4
  //#region src/sandbox.ts
4
5
  function bashToSessionEnv(bash) {