weifuwu 0.3.0 → 0.5.0

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.js CHANGED
@@ -107,9 +107,6 @@ function serve(handler, options) {
107
107
 
108
108
  // router.ts
109
109
  import { WebSocketServer } from "ws";
110
- import { buildSchema, graphql } from "graphql";
111
- import { makeExecutableSchema } from "@graphql-tools/schema";
112
- import { streamText } from "ai";
113
110
  var createTrieNode = () => ({
114
111
  children: /* @__PURE__ */ new Map(),
115
112
  handlers: /* @__PURE__ */ new Map(),
@@ -261,47 +258,6 @@ var Router = class _Router {
261
258
  if (middlewares.length > 0) node.middlewares = middlewares;
262
259
  return this;
263
260
  }
264
- graphql(path, ...args) {
265
- const options = args.pop();
266
- const middlewares = args;
267
- const schema = typeof options.schema === "string" ? options.resolvers ? makeExecutableSchema({
268
- typeDefs: options.schema,
269
- resolvers: options.resolvers
270
- }) : buildSchema(options.schema) : options.schema;
271
- const handler = (req, ctx) => {
272
- const url = new URL(req.url);
273
- if (options.graphiql && req.method === "GET" && !url.searchParams.has("query")) {
274
- return new Response(getGraphiQLHtml(url.pathname), {
275
- status: 200,
276
- headers: { "Content-Type": "text/html" }
277
- });
278
- }
279
- if (req.method !== "GET" && req.method !== "POST") {
280
- return new Response("Not Found", { status: 404 });
281
- }
282
- const paramsPromise = req.method === "GET" ? Promise.resolve(parseGraphQLParamsFromGet(url)) : parseGraphQLParamsFromPost(req);
283
- return paramsPromise.then((params) => {
284
- if (!params) {
285
- return Response.json(
286
- { errors: [{ message: "Missing query" }] },
287
- { status: 400 }
288
- );
289
- }
290
- return executeGraphQLQuery(schema, params, options, req, ctx);
291
- });
292
- };
293
- return this.all(path, ...middlewares, handler);
294
- }
295
- ai(path, ...args) {
296
- const handler = args.pop();
297
- const middlewares = args;
298
- const routeHandler = async (req, ctx) => {
299
- const options = await handler(req, ctx);
300
- const result = streamText(options);
301
- return result.toTextStreamResponse();
302
- };
303
- return this.post(path, ...middlewares, routeHandler);
304
- }
305
261
  handler() {
306
262
  return (req, ctx) => {
307
263
  const url = new URL(req.url);
@@ -506,100 +462,6 @@ function sendHttpResponseOnSocket(socket, response) {
506
462
  socket.end();
507
463
  });
508
464
  }
509
- function parseGraphQLParamsFromGet(url) {
510
- const query = url.searchParams.get("query");
511
- if (!query) return null;
512
- const variablesStr = url.searchParams.get("variables");
513
- let variables = {};
514
- if (variablesStr) {
515
- try {
516
- variables = JSON.parse(variablesStr);
517
- } catch {
518
- return null;
519
- }
520
- }
521
- return {
522
- query,
523
- variables,
524
- operationName: url.searchParams.get("operationName") || void 0
525
- };
526
- }
527
- async function parseGraphQLParamsFromPost(req) {
528
- try {
529
- const body = await req.json();
530
- if (!body.query) return null;
531
- return {
532
- query: body.query,
533
- variables: body.variables || {},
534
- operationName: body.operationName
535
- };
536
- } catch {
537
- return null;
538
- }
539
- }
540
- async function executeGraphQLQuery(schema, params, options, req, ctx) {
541
- const contextValue = options.context ? await options.context(req, ctx) : ctx;
542
- const result = await graphql({
543
- schema,
544
- source: params.query,
545
- rootValue: options.rootValue,
546
- contextValue,
547
- variableValues: params.variables,
548
- operationName: params.operationName
549
- });
550
- return Response.json(result, { status: result.errors ? 400 : 200 });
551
- }
552
- function getGraphiQLHtml(endpoint) {
553
- return `<!doctype html>
554
- <html lang="en">
555
- <head>
556
- <meta charset="UTF-8" />
557
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
558
- <title>GraphiQL</title>
559
- <style>
560
- body { margin: 0; }
561
- #graphiql { height: 100dvh; }
562
- </style>
563
- <link rel="stylesheet" href="https://esm.sh/graphiql@5.2.2/dist/style.css" />
564
- <script type="importmap">
565
- {
566
- "imports": {
567
- "react": "https://esm.sh/react@19.2.5",
568
- "react/": "https://esm.sh/react@19.2.5/",
569
- "react-dom": "https://esm.sh/react-dom@19.2.5",
570
- "react-dom/": "https://esm.sh/react-dom@19.2.5/",
571
- "graphiql": "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql",
572
- "graphiql/": "https://esm.sh/graphiql@5.2.2/",
573
- "@graphiql/react": "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",
574
- "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql",
575
- "graphql": "https://esm.sh/graphql@16.13.2",
576
- "@emotion/is-prop-valid": "data:text/javascript,"
577
- }
578
- }
579
- </script>
580
- <script type="module">
581
- import React from 'react';
582
- import ReactDOM from 'react-dom/client';
583
- import { GraphiQL } from 'graphiql';
584
- import { createGraphiQLFetcher } from '@graphiql/toolkit';
585
- import 'graphiql/setup-workers/esm.sh';
586
-
587
- const fetcher = createGraphiQLFetcher({ url: "${endpoint}" });
588
-
589
- function App() {
590
- return React.createElement(GraphiQL, { fetcher });
591
- }
592
-
593
- const container = document.getElementById('graphiql');
594
- const root = ReactDOM.createRoot(container);
595
- root.render(React.createElement(App));
596
- </script>
597
- </head>
598
- <body>
599
- <div id="graphiql">Loading\u2026</div>
600
- </body>
601
- </html>`;
602
- }
603
465
 
604
466
  // tsx.ts
605
467
  import { createElement, createContext, useContext } from "react";
@@ -1466,21 +1328,920 @@ function compress(options) {
1466
1328
  });
1467
1329
  };
1468
1330
  }
1331
+
1332
+ // graphql.ts
1333
+ import { buildSchema, graphql as executeGraphQL } from "graphql";
1334
+ import { makeExecutableSchema } from "@graphql-tools/schema";
1335
+ function parseParamsFromGet(url) {
1336
+ const query = url.searchParams.get("query");
1337
+ if (!query) return null;
1338
+ let variables = {};
1339
+ const variablesStr = url.searchParams.get("variables");
1340
+ if (variablesStr) {
1341
+ try {
1342
+ variables = JSON.parse(variablesStr);
1343
+ } catch {
1344
+ return null;
1345
+ }
1346
+ }
1347
+ return { query, variables, operationName: url.searchParams.get("operationName") || void 0 };
1348
+ }
1349
+ async function parseParamsFromPost(req) {
1350
+ try {
1351
+ const body = await req.json();
1352
+ if (!body.query) return null;
1353
+ return { query: body.query, variables: body.variables || {}, operationName: body.operationName };
1354
+ } catch {
1355
+ return null;
1356
+ }
1357
+ }
1358
+ function buildSchemaFromOptions(options) {
1359
+ if (typeof options.schema === "string") {
1360
+ return options.resolvers ? makeExecutableSchema({ typeDefs: options.schema, resolvers: options.resolvers }) : buildSchema(options.schema);
1361
+ }
1362
+ return options.schema;
1363
+ }
1364
+ async function executeQuery(schema, params, options, req, ctx) {
1365
+ const contextValue = options.context ? await options.context(req, ctx) : ctx;
1366
+ const result = await executeGraphQL({
1367
+ schema,
1368
+ source: params.query,
1369
+ rootValue: options.rootValue,
1370
+ contextValue,
1371
+ variableValues: params.variables,
1372
+ operationName: params.operationName
1373
+ });
1374
+ return Response.json(result, { status: result.errors ? 400 : 200 });
1375
+ }
1376
+ function graphiqlHTML(endpoint) {
1377
+ return `<!doctype html>
1378
+ <html lang="en">
1379
+ <head>
1380
+ <meta charset="UTF-8" />
1381
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1382
+ <title>GraphiQL</title>
1383
+ <style>body { margin: 0; } #graphiql { height: 100dvh; }</style>
1384
+ <link rel="stylesheet" href="https://esm.sh/graphiql@5.2.2/dist/style.css" />
1385
+ <script type="importmap">
1386
+ {
1387
+ "imports": {
1388
+ "react": "https://esm.sh/react@19.2.5",
1389
+ "react/": "https://esm.sh/react@19.2.5/",
1390
+ "react-dom": "https://esm.sh/react-dom@19.2.5",
1391
+ "react-dom/": "https://esm.sh/react-dom@19.2.5/",
1392
+ "graphiql": "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql",
1393
+ "graphiql/": "https://esm.sh/graphiql@5.2.2/",
1394
+ "@graphiql/react": "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",
1395
+ "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql",
1396
+ "graphql": "https://esm.sh/graphql@16.13.2",
1397
+ "@emotion/is-prop-valid": "data:text/javascript,"
1398
+ }
1399
+ }
1400
+ </script>
1401
+ <script type="module">
1402
+ import React from 'react';
1403
+ import ReactDOM from 'react-dom/client';
1404
+ import { GraphiQL } from 'graphiql';
1405
+ import { createGraphiQLFetcher } from '@graphiql/toolkit';
1406
+ import 'graphiql/setup-workers/esm.sh';
1407
+
1408
+ const fetcher = createGraphiQLFetcher({ url: "${endpoint}" });
1409
+
1410
+ function App() {
1411
+ return React.createElement(GraphiQL, { fetcher });
1412
+ }
1413
+
1414
+ const container = document.getElementById('graphiql');
1415
+ const root = ReactDOM.createRoot(container);
1416
+ root.render(React.createElement(App));
1417
+ </script>
1418
+ </head>
1419
+ <body>
1420
+ <div id="graphiql">Loading\u2026</div>
1421
+ </body>
1422
+ </html>`;
1423
+ }
1424
+ function graphql(handler) {
1425
+ const r = new Router();
1426
+ r.get("/", async (req, ctx) => {
1427
+ const options = await handler(req, ctx);
1428
+ const schema = buildSchemaFromOptions(options);
1429
+ const url = new URL(req.url);
1430
+ if (options.graphiql && !url.searchParams.has("query")) {
1431
+ return new Response(graphiqlHTML(url.pathname), {
1432
+ status: 200,
1433
+ headers: { "Content-Type": "text/html" }
1434
+ });
1435
+ }
1436
+ const params = parseParamsFromGet(url);
1437
+ if (!params) {
1438
+ return Response.json({ errors: [{ message: "Missing query" }] }, { status: 400 });
1439
+ }
1440
+ return executeQuery(schema, params, options, req, ctx);
1441
+ });
1442
+ r.post("/", async (req, ctx) => {
1443
+ const options = await handler(req, ctx);
1444
+ const schema = buildSchemaFromOptions(options);
1445
+ const params = await parseParamsFromPost(req);
1446
+ if (!params) {
1447
+ return Response.json({ errors: [{ message: "Missing query" }] }, { status: 400 });
1448
+ }
1449
+ return executeQuery(schema, params, options, req, ctx);
1450
+ });
1451
+ return r;
1452
+ }
1453
+
1454
+ // ai.ts
1455
+ import { streamText } from "ai";
1456
+ function ai(handler) {
1457
+ const r = new Router();
1458
+ r.post("/", async (req, ctx) => {
1459
+ const options = await handler(req, ctx);
1460
+ const result = streamText(options);
1461
+ return result.toTextStreamResponse();
1462
+ });
1463
+ return r;
1464
+ }
1465
+
1466
+ // workflow/tool.ts
1467
+ function tool(def) {
1468
+ return {
1469
+ name: def.name ?? "",
1470
+ description: def.description,
1471
+ inputSchema: def.inputSchema,
1472
+ execute: def.execute
1473
+ };
1474
+ }
1475
+
1476
+ // workflow/reference.ts
1477
+ function getByPath(obj, path) {
1478
+ let current = obj;
1479
+ for (const key of path) {
1480
+ if (current === null || current === void 0) return void 0;
1481
+ if (typeof current === "object" && key in current) {
1482
+ current = current[key];
1483
+ } else {
1484
+ return void 0;
1485
+ }
1486
+ }
1487
+ return current;
1488
+ }
1489
+ function resolveRef(path, ctx) {
1490
+ if (path.startsWith("$nodes.")) {
1491
+ const afterNodes = path.slice(7);
1492
+ const dotIdx = afterNodes.indexOf(".");
1493
+ if (dotIdx === -1) {
1494
+ return ctx.nodeOutputs.get(afterNodes);
1495
+ }
1496
+ const id2 = afterNodes.slice(0, dotIdx);
1497
+ const propPath = afterNodes.slice(dotIdx + 1);
1498
+ const output = ctx.nodeOutputs.get(id2);
1499
+ if (output === void 0) {
1500
+ throw new Error(`Node "${id2}" has no output yet`);
1501
+ }
1502
+ if (propPath.startsWith("output")) {
1503
+ return getByPath(output, propPath.slice(7).split(".").filter(Boolean));
1504
+ }
1505
+ return getByPath(output, propPath.split("."));
1506
+ }
1507
+ if (path.startsWith("$var.")) {
1508
+ const name = path.slice(5);
1509
+ if (!ctx.variables.has(name)) {
1510
+ throw new Error(`Variable "${name}" is not defined`);
1511
+ }
1512
+ return ctx.variables.get(name);
1513
+ }
1514
+ if (path.startsWith("$input.")) {
1515
+ const key = path.slice(7);
1516
+ return ctx.input[key];
1517
+ }
1518
+ if (path === "true") return true;
1519
+ if (path === "false") return false;
1520
+ if (path === "null") return null;
1521
+ const num = Number(path);
1522
+ if (!isNaN(num) && path.trim() !== "") return num;
1523
+ return path;
1524
+ }
1525
+ function resolveValue(v, ctx) {
1526
+ if (typeof v === "string" && v.startsWith("$")) {
1527
+ return resolveRef(v, ctx);
1528
+ }
1529
+ if (Array.isArray(v)) {
1530
+ return v.map((item) => resolveValue(item, ctx));
1531
+ }
1532
+ if (typeof v === "object" && v !== null) {
1533
+ const result = {};
1534
+ for (const [k, val] of Object.entries(v)) {
1535
+ result[k] = resolveValue(val, ctx);
1536
+ }
1537
+ return result;
1538
+ }
1539
+ return v;
1540
+ }
1541
+
1542
+ // workflow/nodes.ts
1543
+ function evaluateExpression(expr, ctx) {
1544
+ const operators = [
1545
+ { op: "===", fn: (a, b) => a === b },
1546
+ { op: "!==", fn: (a, b) => a !== b },
1547
+ { op: ">=", fn: (a, b) => Number(a) >= Number(b) },
1548
+ { op: "<=", fn: (a, b) => Number(a) <= Number(b) },
1549
+ { op: ">", fn: (a, b) => Number(a) > Number(b) },
1550
+ { op: "<", fn: (a, b) => Number(a) < Number(b) },
1551
+ { op: "==", fn: (a, b) => a == b },
1552
+ { op: "!=", fn: (a, b) => a != b },
1553
+ { op: "+", fn: (a, b) => Number(a) + Number(b) },
1554
+ { op: "-", fn: (a, b) => Number(a) - Number(b) },
1555
+ { op: "*", fn: (a, b) => Number(a) * Number(b) },
1556
+ { op: "/", fn: (a, b) => Number(a) / Number(b) },
1557
+ { op: "%", fn: (a, b) => Number(a) % Number(b) },
1558
+ { op: "&&", fn: (a, b) => Boolean(a) && Boolean(b) },
1559
+ { op: "||", fn: (a, b) => Boolean(a) || Boolean(b) }
1560
+ ];
1561
+ for (const { op, fn } of operators) {
1562
+ const idx = expr.indexOf(op);
1563
+ if (idx > 0) {
1564
+ const leftRaw = expr.slice(0, idx).trim();
1565
+ const rightRaw = expr.slice(idx + op.length).trim();
1566
+ const left = resolveValue(leftRaw, ctx);
1567
+ const right = resolveValue(rightRaw, ctx);
1568
+ return fn(left, right);
1569
+ }
1570
+ }
1571
+ const trimmed = expr.trim();
1572
+ if (trimmed === "true") return true;
1573
+ if (trimmed === "false") return false;
1574
+ if (trimmed === "null") return null;
1575
+ const num = Number(trimmed);
1576
+ if (!isNaN(num) && trimmed !== "") return num;
1577
+ return resolveValue(expr, ctx);
1578
+ }
1579
+ async function executeEval(node, ctx) {
1580
+ const expression = node.input.expression;
1581
+ if (!expression) throw new Error('eval node requires "expression" field');
1582
+ const result = evaluateExpression(expression, ctx);
1583
+ return { result };
1584
+ }
1585
+ async function executeSet(node, ctx) {
1586
+ const name = node.input.name;
1587
+ const value = node.input.value;
1588
+ if (!name) throw new Error('set node requires "name" field');
1589
+ let resolved;
1590
+ if (typeof value === "string") {
1591
+ resolved = evaluateExpression(value, ctx);
1592
+ } else {
1593
+ resolved = resolveValue(value ?? null, ctx);
1594
+ }
1595
+ ctx.variables.set(name, resolved);
1596
+ return resolved;
1597
+ }
1598
+ async function executeGet(node, ctx) {
1599
+ const name = node.input.name;
1600
+ if (!name) throw new Error('get node requires "name" field');
1601
+ if (!ctx.variables.has(name)) {
1602
+ throw new Error(`Variable "${name}" is not defined`);
1603
+ }
1604
+ return ctx.variables.get(name);
1605
+ }
1606
+ async function executeIf(node, ctx) {
1607
+ const conditions = node.conditions ?? [];
1608
+ for (const condition of conditions) {
1609
+ const test = typeof condition.test === "string" ? Boolean(resolveValue(condition.test, ctx)) : condition.test;
1610
+ if (test && condition.body) {
1611
+ let lastOutput = void 0;
1612
+ for (const bodyNode of condition.body) {
1613
+ lastOutput = await executeNode(bodyNode, ctx);
1614
+ }
1615
+ return lastOutput;
1616
+ }
1617
+ }
1618
+ return void 0;
1619
+ }
1620
+ async function executeWhile(node, ctx) {
1621
+ const conditionExpr = node.input.condition;
1622
+ if (!conditionExpr) throw new Error('while node requires "condition" field');
1623
+ let lastOutput = void 0;
1624
+ let iterations = 0;
1625
+ const maxIterations = 1e3;
1626
+ while (iterations < maxIterations) {
1627
+ iterations++;
1628
+ ctx.stepCount++;
1629
+ if (ctx.stepCount > ctx.maxSteps) {
1630
+ throw new Error(`Step limit exceeded (${ctx.maxSteps})`);
1631
+ }
1632
+ const condition = Boolean(evaluateExpression(conditionExpr, ctx));
1633
+ if (!condition) break;
1634
+ for (const bodyNode of node.body ?? []) {
1635
+ lastOutput = await executeNode(bodyNode, ctx);
1636
+ }
1637
+ }
1638
+ return lastOutput;
1639
+ }
1640
+ async function executeCall(node, ctx) {
1641
+ const toolName = node.input.tool;
1642
+ const args = node.input.args ?? {};
1643
+ if (toolName && ctx.toolRegistry.has(toolName)) {
1644
+ const tool3 = ctx.toolRegistry.get(toolName);
1645
+ const resolvedInput = resolveValue(args, ctx);
1646
+ const parsed = tool3.inputSchema.parse(resolvedInput);
1647
+ return tool3.execute(parsed, {
1648
+ nodeId: node.id,
1649
+ workflowId: ctx.workflowId,
1650
+ onStream: async (event) => {
1651
+ if (ctx.sseManager && ctx.workflowId) {
1652
+ ctx.sseManager.send(ctx.workflowId, { event: "llm-stream", data: { nodeId: node.id, ...event } });
1653
+ }
1654
+ }
1655
+ });
1656
+ }
1657
+ const functionName = node.input.function;
1658
+ if (functionName && ctx.functions[functionName]) {
1659
+ const fn = ctx.functions[functionName];
1660
+ const prevFunctions = ctx.functions;
1661
+ const prevInput = ctx.input;
1662
+ ctx.input = resolveValue(args, ctx);
1663
+ let lastOutput = void 0;
1664
+ for (const bodyNode of fn.workflow.nodes) {
1665
+ lastOutput = await executeNode(bodyNode, ctx);
1666
+ }
1667
+ ctx.input = prevInput;
1668
+ return lastOutput;
1669
+ }
1670
+ throw new Error(`call node: tool "${toolName ?? functionName}" not found`);
1671
+ }
1672
+ async function executeHttp(node, ctx) {
1673
+ const input = resolveValue(node.input, ctx);
1674
+ const url = input.url;
1675
+ if (!url) throw new Error('http node requires "url" field');
1676
+ const controller = new AbortController();
1677
+ const timeout = input.timeout ?? 3e4;
1678
+ const timer = setTimeout(() => controller.abort(), timeout);
1679
+ try {
1680
+ const fetchInit = {
1681
+ method: input.method ?? "GET",
1682
+ headers: input.headers ?? {},
1683
+ signal: controller.signal
1684
+ };
1685
+ if (input.body && fetchInit.method !== "GET") {
1686
+ fetchInit.body = JSON.stringify(input.body);
1687
+ }
1688
+ const response = await fetch(url, fetchInit);
1689
+ const contentType = response.headers.get("content-type") ?? "";
1690
+ const body = contentType.includes("application/json") ? await response.json() : await response.text();
1691
+ return {
1692
+ status: response.status,
1693
+ statusText: response.statusText,
1694
+ headers: Object.fromEntries(response.headers.entries()),
1695
+ body
1696
+ };
1697
+ } finally {
1698
+ clearTimeout(timer);
1699
+ }
1700
+ }
1701
+ var executors = {
1702
+ eval: executeEval,
1703
+ set: executeSet,
1704
+ get: executeGet,
1705
+ if: executeIf,
1706
+ while: executeWhile,
1707
+ call: executeCall,
1708
+ http: executeHttp
1709
+ };
1710
+ async function executeNode(node, ctx) {
1711
+ const executor = executors[node.tool];
1712
+ if (!executor) {
1713
+ throw new Error(`Unknown node type: "${node.tool}"`);
1714
+ }
1715
+ return executor(node, ctx);
1716
+ }
1717
+
1718
+ // workflow/llm.ts
1719
+ function buildToolsDescription(tools) {
1720
+ return Object.entries(tools).map(([key, t]) => {
1721
+ const name = t.name || key;
1722
+ const schema = t.inputSchema;
1723
+ return `- ${name}: ${t.description}
1724
+ Input schema: describe as JSON object fields`;
1725
+ }).join("\n");
1726
+ }
1727
+ var SYSTEM_PROMPT_TEMPLATE = `You are a workflow generator. Given a user goal and available tools, output a workflow JSON.
1728
+
1729
+ Available tools:
1730
+ {{TOOLS}}
1731
+
1732
+ Workflow format:
1733
+ {
1734
+ "name": "workflow name",
1735
+ "nodes": [
1736
+ {
1737
+ "id": "step1",
1738
+ "tool": "set",
1739
+ "input": { "name": "varName", "value": "initialValue" }
1740
+ },
1741
+ {
1742
+ "id": "step2",
1743
+ "tool": "call",
1744
+ "input": { "tool": "toolName", "args": { "param1": "$var.varName" } }
1745
+ },
1746
+ {
1747
+ "id": "step3",
1748
+ "tool": "if",
1749
+ "input": {},
1750
+ "conditions": [
1751
+ { "test": "$nodes.step2.output.someField", "body": [
1752
+ { "id": "step4", "tool": "call", "input": { "tool": "toolName", "args": {} } }
1753
+ ]}
1754
+ ]
1755
+ }
1756
+ ]
1757
+ }
1758
+
1759
+ Node types:
1760
+ - eval: evaluate an expression. input: { expression: "..." }
1761
+ - set: assign a variable. input: { name, value }
1762
+ - get: read a variable. input: { name }
1763
+ - if: conditional branch. input: {}, conditions: [{ test, body }]
1764
+ - while: loop. input: { condition }, body: [nodes]
1765
+ - call: call a registered tool. input: { tool, args }
1766
+ - http: HTTP request. input: { url, method?, headers?, body? }
1767
+
1768
+ Reference syntax:
1769
+ - $var.name - read a variable
1770
+ - $nodes.id.output - output of a previous node
1771
+ - $nodes.id.output.field - specific field of a node's output
1772
+ - $input.field - workflow input parameter
1773
+
1774
+ Output ONLY valid JSON. No explanation, no markdown.`;
1775
+ async function generateWorkflow(goal, tools, generateFn) {
1776
+ const toolsDesc = buildToolsDescription(tools);
1777
+ const system = SYSTEM_PROMPT_TEMPLATE.replace("{{TOOLS}}", toolsDesc);
1778
+ const result = await generateFn({
1779
+ system,
1780
+ messages: [{ role: "user", content: goal }]
1781
+ });
1782
+ const text = result.text.trim();
1783
+ const jsonStart = text.indexOf("{");
1784
+ const jsonEnd = text.lastIndexOf("}");
1785
+ if (jsonStart === -1 || jsonEnd === -1) {
1786
+ throw new Error(`LLM output is not valid JSON: ${text.slice(0, 200)}`);
1787
+ }
1788
+ const jsonStr = text.slice(jsonStart, jsonEnd + 1);
1789
+ try {
1790
+ const workflow2 = JSON.parse(jsonStr);
1791
+ if (!workflow2.nodes || !Array.isArray(workflow2.nodes)) {
1792
+ throw new Error("Generated workflow has no nodes array");
1793
+ }
1794
+ return workflow2;
1795
+ } catch (err) {
1796
+ if (err instanceof SyntaxError) {
1797
+ throw new Error(`Failed to parse LLM output as JSON: ${err.message}`);
1798
+ }
1799
+ throw err;
1800
+ }
1801
+ }
1802
+
1803
+ // workflow/engine.ts
1804
+ import { generateText } from "ai";
1805
+ function createWorkflowEngine(options) {
1806
+ const toolRegistry = /* @__PURE__ */ new Map();
1807
+ for (const [key, t] of Object.entries(options.tools)) {
1808
+ t.name = t.name || key;
1809
+ toolRegistry.set(t.name, t);
1810
+ }
1811
+ const states = /* @__PURE__ */ new Map();
1812
+ async function execute(workflow2, opts) {
1813
+ const ctx = {
1814
+ variables: /* @__PURE__ */ new Map(),
1815
+ nodeOutputs: /* @__PURE__ */ new Map(),
1816
+ functions: workflow2.functions ?? {},
1817
+ stepCount: 0,
1818
+ maxSteps: opts?.maxSteps ?? 1e3,
1819
+ input: opts?.initialInput ?? {},
1820
+ toolRegistry,
1821
+ sseManager: options.sseManager,
1822
+ workflowId: opts?.workflowId
1823
+ };
1824
+ let lastOutput = void 0;
1825
+ for (const node of workflow2.nodes) {
1826
+ ctx.stepCount++;
1827
+ if (ctx.stepCount > ctx.maxSteps) {
1828
+ throw new Error(`Step limit exceeded (${ctx.maxSteps})`);
1829
+ }
1830
+ options.sseManager?.send(ctx.workflowId ?? "", { event: "node-start", data: { nodeId: node.id, tool: node.tool, input: node.input } });
1831
+ const output = await executeNode(node, ctx);
1832
+ ctx.nodeOutputs.set(node.id, output);
1833
+ lastOutput = output;
1834
+ options.sseManager?.send(ctx.workflowId ?? "", { event: "node-end", data: { nodeId: node.id, output } });
1835
+ }
1836
+ return lastOutput;
1837
+ }
1838
+ async function runAsync(workflowId, workflow2, opts) {
1839
+ const state = {
1840
+ workflowId,
1841
+ status: "running",
1842
+ goal: workflow2.name ?? "",
1843
+ startTime: Date.now()
1844
+ };
1845
+ states.set(workflowId, state);
1846
+ const sse = options.sseManager;
1847
+ sse?.send(workflowId, { event: "workflow-start", data: { workflowId, goal: state.goal } });
1848
+ try {
1849
+ const result = await execute(workflow2, { ...opts, workflowId });
1850
+ state.status = "completed";
1851
+ state.result = result;
1852
+ state.endTime = Date.now();
1853
+ sse?.send(workflowId, { event: "complete", data: { result, duration: state.endTime - state.startTime } });
1854
+ } catch (err) {
1855
+ state.status = "error";
1856
+ state.error = err instanceof Error ? err.message : String(err);
1857
+ state.endTime = Date.now();
1858
+ sse?.send(workflowId, { event: "error", data: { error: state.error } });
1859
+ } finally {
1860
+ sse?.close(workflowId);
1861
+ }
1862
+ }
1863
+ async function generateWorkflow2(goal) {
1864
+ if (!options.model) {
1865
+ throw new Error('LLM model is required for generateWorkflow. Pass "model" to createWorkflowEngine.');
1866
+ }
1867
+ return generateWorkflow(goal, options.tools, async (prompt) => {
1868
+ const result = await generateText({
1869
+ model: options.model,
1870
+ system: prompt.system,
1871
+ messages: prompt.messages
1872
+ });
1873
+ return { text: result.text };
1874
+ });
1875
+ }
1876
+ return {
1877
+ execute,
1878
+ runAsync,
1879
+ generateWorkflow: generateWorkflow2,
1880
+ getState(workflowId) {
1881
+ return states.get(workflowId);
1882
+ }
1883
+ };
1884
+ }
1885
+
1886
+ // workflow/sse.ts
1887
+ function createSSEManager() {
1888
+ const streams = /* @__PURE__ */ new Map();
1889
+ const encoder = new TextEncoder();
1890
+ function createStream(workflowId) {
1891
+ const state = {
1892
+ controller: null,
1893
+ encoder,
1894
+ closed: false,
1895
+ buffer: []
1896
+ };
1897
+ const stream = new ReadableStream({
1898
+ start(controller) {
1899
+ state.controller = controller;
1900
+ streams.set(workflowId, state);
1901
+ for (const event of state.buffer) {
1902
+ try {
1903
+ controller.enqueue(encoder.encode(event));
1904
+ } catch {
1905
+ break;
1906
+ }
1907
+ }
1908
+ state.buffer = [];
1909
+ },
1910
+ cancel() {
1911
+ state.closed = true;
1912
+ streams.delete(workflowId);
1913
+ }
1914
+ });
1915
+ return stream;
1916
+ }
1917
+ function send(workflowId, event) {
1918
+ const state = streams.get(workflowId);
1919
+ if (!state || state.closed) return;
1920
+ const data = `event: ${event.event}
1921
+ data: ${JSON.stringify(event.data)}
1922
+
1923
+ `;
1924
+ if (state.controller) {
1925
+ try {
1926
+ state.controller.enqueue(encoder.encode(data));
1927
+ } catch {
1928
+ state.closed = true;
1929
+ streams.delete(workflowId);
1930
+ }
1931
+ } else {
1932
+ state.buffer.push(data);
1933
+ }
1934
+ }
1935
+ function close(workflowId) {
1936
+ const state = streams.get(workflowId);
1937
+ if (!state) return;
1938
+ state.closed = true;
1939
+ streams.delete(workflowId);
1940
+ try {
1941
+ state.controller?.close();
1942
+ } catch {
1943
+ }
1944
+ }
1945
+ return { createStream, send, close };
1946
+ }
1947
+
1948
+ // workflow/route.ts
1949
+ function workflow(handler) {
1950
+ const r = new Router();
1951
+ const sseManager = createSSEManager();
1952
+ r.get("/:workflowId/events", async (req, ctx) => {
1953
+ const stream = sseManager.createStream(ctx.params.workflowId);
1954
+ return new Response(stream, {
1955
+ headers: {
1956
+ "Content-Type": "text/event-stream",
1957
+ "Cache-Control": "no-cache",
1958
+ "Connection": "keep-alive"
1959
+ }
1960
+ });
1961
+ });
1962
+ r.post("/", async (req, ctx) => {
1963
+ const options = await handler(req, ctx);
1964
+ const engine = createWorkflowEngine({
1965
+ tools: options.tools,
1966
+ model: options.model,
1967
+ sseManager: options.stream ? sseManager : void 0
1968
+ });
1969
+ const body = await req.json();
1970
+ let wf;
1971
+ if (body.goal && options.model) {
1972
+ wf = await engine.generateWorkflow(body.goal);
1973
+ } else if (body.workflow) {
1974
+ wf = body.workflow;
1975
+ } else if (body.nodes) {
1976
+ wf = { nodes: body.nodes };
1977
+ } else {
1978
+ return Response.json(
1979
+ { error: 'Provide "goal" (with model) or "workflow"/"nodes"' },
1980
+ { status: 400 }
1981
+ );
1982
+ }
1983
+ if (options.stream && sseManager) {
1984
+ const workflowId = crypto.randomUUID();
1985
+ engine.runAsync(workflowId, wf);
1986
+ return Response.json({ workflowId, eventsUrl: `/${workflowId}/events` });
1987
+ }
1988
+ const result = await engine.execute(wf);
1989
+ return Response.json({ workflow: wf, result });
1990
+ });
1991
+ return r;
1992
+ }
1993
+
1994
+ // postgres/client.ts
1995
+ import postgresFactory from "postgres";
1996
+
1997
+ // postgres/table.ts
1998
+ import { z } from "zod";
1999
+ function unwrap(field) {
2000
+ let inner = field;
2001
+ while (inner instanceof z.ZodOptional || inner instanceof z.ZodNullable || inner instanceof z.ZodDefault || (inner.constructor.name === "ZodTransform" || inner._def?.type === "transform")) {
2002
+ if (inner._def?.innerType) {
2003
+ inner = inner._def.innerType;
2004
+ } else {
2005
+ break;
2006
+ }
2007
+ }
2008
+ return inner;
2009
+ }
2010
+ function isOptional(field) {
2011
+ let inner = field;
2012
+ while (true) {
2013
+ if (inner instanceof z.ZodOptional) return true;
2014
+ if (inner instanceof z.ZodNullable) return true;
2015
+ if (inner instanceof z.ZodDefault) {
2016
+ inner = inner._def.innerType;
2017
+ continue;
2018
+ }
2019
+ if (inner._def?.type === "transform") {
2020
+ inner = inner._def.innerType;
2021
+ continue;
2022
+ }
2023
+ break;
2024
+ }
2025
+ return false;
2026
+ }
2027
+ function hasUUIDCheck(field) {
2028
+ const checks = field._def?.checks ?? [];
2029
+ return checks.some((c) => c.type === "string" && c.def?.format === "uuid");
2030
+ }
2031
+ function detectSqlType(name, field, isPk) {
2032
+ const inner = unwrap(field);
2033
+ const nullable = isOptional(field);
2034
+ const autoGenerate = isPk && name === "id";
2035
+ if (isPk && name === "id" && inner instanceof z.ZodNumber) {
2036
+ return { sqlType: "SERIAL", nullable: false, defaultExpr: null, autoGenerate: true };
2037
+ }
2038
+ if (isPk && name === "id" && typeof BigInt !== "undefined" && inner instanceof z.ZodBigInt) {
2039
+ return { sqlType: "BIGSERIAL", nullable: false, defaultExpr: null, autoGenerate: true };
2040
+ }
2041
+ if (isPk && name === "id" && inner instanceof z.ZodString) {
2042
+ if (hasUUIDCheck(inner)) {
2043
+ return { sqlType: "UUID", nullable: false, defaultExpr: "gen_random_uuid()", autoGenerate: true };
2044
+ }
2045
+ return { sqlType: "TEXT", nullable: false, defaultExpr: null, autoGenerate: false };
2046
+ }
2047
+ let sqlType;
2048
+ if (inner instanceof z.ZodNumber) {
2049
+ sqlType = "INTEGER";
2050
+ } else if (inner instanceof z.ZodString) {
2051
+ sqlType = "TEXT";
2052
+ } else if (inner instanceof z.ZodBoolean) {
2053
+ sqlType = "BOOLEAN";
2054
+ } else if (inner instanceof z.ZodDate) {
2055
+ sqlType = "TIMESTAMPTZ";
2056
+ } else if (inner instanceof z.ZodEnum) {
2057
+ sqlType = "TEXT";
2058
+ } else if (inner instanceof z.ZodArray) {
2059
+ sqlType = "JSONB";
2060
+ } else if (inner instanceof z.ZodObject) {
2061
+ sqlType = "JSONB";
2062
+ } else {
2063
+ sqlType = "TEXT";
2064
+ }
2065
+ return { sqlType, nullable: nullable || autoGenerate, defaultExpr: null, autoGenerate };
2066
+ }
2067
+ function parseColumns(schema) {
2068
+ const pkField = Object.keys(schema).find((k) => k === "id");
2069
+ return Object.entries(schema).map(([name, field]) => {
2070
+ const isPk = name === pkField;
2071
+ const { sqlType, nullable, defaultExpr, autoGenerate } = detectSqlType(name, field, isPk);
2072
+ return { name, sqlType, nullable: nullable || autoGenerate, isPrimaryKey: isPk, defaultExpr, autoGenerate };
2073
+ });
2074
+ }
2075
+ function buildGet(sql, name, pk) {
2076
+ return async function get(id2) {
2077
+ if (!pk) throw new Error(`Table "${name}" has no primary key`);
2078
+ const [row] = await sql`SELECT * FROM ${sql(name)} WHERE ${sql(pk.name)} = ${id2} LIMIT 1`;
2079
+ return row ?? void 0;
2080
+ };
2081
+ }
2082
+ function buildList(sql, name, columns) {
2083
+ return async function list(filter = {}, opts = {}) {
2084
+ const colNames = new Set(columns.map((c) => c.name));
2085
+ const whereClauses = [];
2086
+ const whereValues = [];
2087
+ for (const [key, value] of Object.entries(filter)) {
2088
+ if (colNames.has(key)) {
2089
+ whereClauses.push(`"${key}" = $${whereValues.length + 1}`);
2090
+ whereValues.push(value);
2091
+ }
2092
+ }
2093
+ const where = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
2094
+ const sortClauses = [];
2095
+ if (opts.sort) {
2096
+ for (const [key, dir] of Object.entries(opts.sort)) {
2097
+ if (colNames.has(key)) {
2098
+ sortClauses.push(`"${key}" ${dir.toUpperCase()}`);
2099
+ }
2100
+ }
2101
+ }
2102
+ const orderBy = sortClauses.length > 0 ? `ORDER BY ${sortClauses.join(", ")}` : "";
2103
+ const limitClause = opts.limit != null ? `LIMIT ${opts.limit}` : "";
2104
+ const offsetClause = opts.offset != null ? `OFFSET ${opts.offset}` : "";
2105
+ const [rows, countResult] = await Promise.all([
2106
+ sql.unsafe(`SELECT * FROM "${name}" ${where} ${orderBy} ${limitClause} ${offsetClause}`.trim(), whereValues),
2107
+ sql.unsafe(`SELECT count(*) as count FROM "${name}" ${where}`.trim(), whereValues)
2108
+ ]);
2109
+ return { rows, count: Number(countResult[0]?.count ?? 0) };
2110
+ };
2111
+ }
2112
+ function buildCreate(sql, name, pk, zodSchema) {
2113
+ return async function create(data) {
2114
+ const validated = zodSchema.parse(data);
2115
+ if (pk?.autoGenerate) {
2116
+ delete validated[pk.name];
2117
+ }
2118
+ const [row] = await sql`INSERT INTO ${sql(name)} ${sql(validated)} RETURNING *`;
2119
+ return row;
2120
+ };
2121
+ }
2122
+ function buildPatch(sql, name, pk, zodSchema) {
2123
+ return async function patch(id2, data) {
2124
+ if (!pk) throw new Error(`Table "${name}" has no primary key`);
2125
+ const validated = zodSchema.partial().parse(data);
2126
+ delete validated[pk.name];
2127
+ if (Object.keys(validated).length === 0) {
2128
+ const [row2] = await sql`SELECT * FROM ${sql(name)} WHERE ${sql(pk.name)} = ${id2} LIMIT 1`;
2129
+ return row2 ?? void 0;
2130
+ }
2131
+ const [row] = await sql`UPDATE ${sql(name)} SET ${sql(validated)} WHERE ${sql(pk.name)} = ${id2} RETURNING *`;
2132
+ return row ?? void 0;
2133
+ };
2134
+ }
2135
+ function buildRemove(sql, name, pk) {
2136
+ return async function remove(id2) {
2137
+ if (!pk) throw new Error(`Table "${name}" has no primary key`);
2138
+ const rows = await sql`DELETE FROM ${sql(name)} WHERE ${sql(pk.name)} = ${id2} RETURNING 1`;
2139
+ return rows.length > 0;
2140
+ };
2141
+ }
2142
+ function buildTable(sql, tables) {
2143
+ return function table(name, schema) {
2144
+ const zodSchema = z.object(schema);
2145
+ const columns = parseColumns(schema);
2146
+ const pk = columns.find((c) => c.isPrimaryKey) ?? void 0;
2147
+ tables.push({ name, columns });
2148
+ return {
2149
+ $type: void 0,
2150
+ $insert: void 0,
2151
+ get: buildGet(sql, name, pk),
2152
+ list: buildList(sql, name, columns),
2153
+ create: buildCreate(sql, name, pk, zodSchema),
2154
+ patch: buildPatch(sql, name, pk, zodSchema),
2155
+ remove: buildRemove(sql, name, pk)
2156
+ };
2157
+ };
2158
+ }
2159
+
2160
+ // postgres/migrate.ts
2161
+ function toDDL(col) {
2162
+ const parts = [`"${col.name}"`, col.sqlType];
2163
+ if (col.isPrimaryKey) parts.push("PRIMARY KEY");
2164
+ if (!col.isPrimaryKey && !col.nullable) parts.push("NOT NULL");
2165
+ if (col.defaultExpr) parts.push(`DEFAULT ${col.defaultExpr}`);
2166
+ return parts.join(" ");
2167
+ }
2168
+ function createTableSQL(name, columns) {
2169
+ const cols = columns.map(toDDL);
2170
+ return `CREATE TABLE IF NOT EXISTS "${name}" (
2171
+ ${cols.join(",\n ")}
2172
+ )`;
2173
+ }
2174
+ function addColumnSQL(name, col) {
2175
+ return `ALTER TABLE "${name}" ADD COLUMN IF NOT EXISTS ${toDDL(col)}`;
2176
+ }
2177
+ async function runMigrations(sql, tables) {
2178
+ for (const table of tables) {
2179
+ const existing = await sql`
2180
+ SELECT column_name FROM information_schema.columns
2181
+ WHERE table_schema = 'public' AND table_name = ${table.name}
2182
+ `;
2183
+ if (existing.length === 0) {
2184
+ await sql.unsafe(createTableSQL(table.name, table.columns));
2185
+ } else {
2186
+ const colNames = new Set(existing.map((r) => r.column_name));
2187
+ for (const col of table.columns) {
2188
+ if (!colNames.has(col.name)) {
2189
+ await sql.unsafe(addColumnSQL(table.name, col));
2190
+ }
2191
+ }
2192
+ }
2193
+ }
2194
+ }
2195
+
2196
+ // postgres/client.ts
2197
+ function postgres(opts) {
2198
+ const options = typeof opts === "string" ? { connection: opts } : opts ?? {};
2199
+ const connection = options.connection ?? process.env.DATABASE_URL;
2200
+ if (!connection) {
2201
+ throw new Error(
2202
+ "postgres: DATABASE_URL is not set. Pass a connection string or set the DATABASE_URL environment variable."
2203
+ );
2204
+ }
2205
+ const sql = postgresFactory(connection);
2206
+ const tables = [];
2207
+ if (options.signal) {
2208
+ options.signal.addEventListener("abort", () => {
2209
+ sql.end();
2210
+ }, { once: true });
2211
+ }
2212
+ const mw = ((req, ctx, next) => {
2213
+ ctx.sql = sql;
2214
+ return next(req, ctx);
2215
+ });
2216
+ mw.sql = sql;
2217
+ mw.table = buildTable(sql, tables);
2218
+ mw.migrate = () => runMigrations(sql, tables);
2219
+ mw.close = () => sql.end({ timeout: 5 });
2220
+ return mw;
2221
+ }
1469
2222
  export {
1470
2223
  Router,
1471
2224
  TsxContext,
2225
+ ai,
1472
2226
  auth,
1473
2227
  compress,
1474
2228
  cors,
2229
+ createSSEManager,
2230
+ createWorkflowEngine,
1475
2231
  deleteCookie,
2232
+ generateWorkflow,
1476
2233
  getCookies,
2234
+ graphql,
1477
2235
  logger,
2236
+ postgres,
1478
2237
  rateLimit,
1479
2238
  serve,
1480
2239
  serveStatic,
1481
2240
  setCookie,
2241
+ tool,
1482
2242
  tsx,
1483
2243
  upload,
1484
2244
  useTsx,
1485
- validate
2245
+ validate,
2246
+ workflow
1486
2247
  };