weifuwu 0.10.0 → 0.12.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
@@ -63,6 +63,11 @@ async function sendResponse(res, response) {
63
63
  }
64
64
  res.end();
65
65
  }
66
+ async function createTestServer(handler) {
67
+ const server = serve(handler, { port: 0 });
68
+ await server.ready;
69
+ return { server, url: `http://localhost:${server.port}` };
70
+ }
66
71
  function serve(handler, options) {
67
72
  const port = options?.port ?? 0;
68
73
  const hostname = options?.hostname ?? "0.0.0.0";
@@ -329,13 +334,13 @@ var Router = class _Router {
329
334
  const mw = match.middlewares[index++];
330
335
  return mw(innerReq, ctx2, dispatch);
331
336
  }
332
- return await new Promise((resolve9) => {
337
+ return await new Promise((resolve10) => {
333
338
  try {
334
339
  upgradeSocket(router.wss, req, socket, head, match.handler, ctx2);
335
- resolve9(new Response(null, { status: 101 }));
340
+ resolve10(new Response(null, { status: 101 }));
336
341
  } catch {
337
342
  socket.destroy();
338
- resolve9(new Response("WebSocket upgrade failed", { status: 500 }));
343
+ resolve10(new Response("WebSocket upgrade failed", { status: 500 }));
339
344
  }
340
345
  });
341
346
  };
@@ -1946,37 +1951,39 @@ function graphql(handler) {
1946
1951
  }
1947
1952
  return executeQuery(schema, params, options, req, ctx);
1948
1953
  });
1949
- return r;
1954
+ return { router: () => r };
1950
1955
  }
1951
1956
 
1952
1957
  // ai.ts
1953
- var _ai = {
1954
- streamText: null
1955
- };
1956
- async function ai(handler) {
1957
- if (!_ai.streamText) {
1958
- _ai.streamText = (await import("ai")).streamText;
1959
- }
1958
+ var _ai = {};
1959
+ async function getStreamText() {
1960
+ if (!_ai.streamText) _ai.streamText = (await import("ai")).streamText;
1961
+ return _ai.streamText;
1962
+ }
1963
+ async function getStreamObject() {
1964
+ if (!_ai.streamObject) _ai.streamObject = (await import("ai")).streamObject;
1965
+ return _ai.streamObject;
1966
+ }
1967
+ async function aiStream(handler) {
1960
1968
  const r = new Router();
1961
1969
  r.post("/", async (req, ctx) => {
1962
1970
  const options = await handler(req, ctx);
1963
- const result = _ai.streamText(options);
1971
+ if (options.schema) {
1972
+ const streamObject = await getStreamObject();
1973
+ const { schema, ...params } = options;
1974
+ const result2 = streamObject({ ...params, schema, output: "object" });
1975
+ return result2.toTextStreamResponse();
1976
+ }
1977
+ const streamText3 = await getStreamText();
1978
+ const result = streamText3(options);
1964
1979
  return result.toTextStreamResponse();
1965
1980
  });
1966
- return r;
1981
+ return { router: () => r };
1967
1982
  }
1968
1983
 
1969
- // workflow/tool.ts
1970
- function tool(def) {
1971
- return {
1972
- name: def.name ?? "",
1973
- description: def.description,
1974
- inputSchema: def.inputSchema,
1975
- execute: def.execute
1976
- };
1977
- }
1978
-
1979
- // workflow/reference.ts
1984
+ // ai/workflow.ts
1985
+ import { tool, generateText } from "ai";
1986
+ import { z } from "zod";
1980
1987
  var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1981
1988
  function getByPath(obj, path2) {
1982
1989
  let current = obj;
@@ -1993,33 +2000,21 @@ function getByPath(obj, path2) {
1993
2000
  }
1994
2001
  function resolveRef(path2, ctx) {
1995
2002
  if (path2.startsWith("$nodes.")) {
1996
- const afterNodes = path2.slice(7);
1997
- const dotIdx = afterNodes.indexOf(".");
1998
- if (dotIdx === -1) {
1999
- return ctx.nodeOutputs.get(afterNodes);
2000
- }
2001
- const id2 = afterNodes.slice(0, dotIdx);
2002
- const propPath = afterNodes.slice(dotIdx + 1);
2003
+ const after = path2.slice(7);
2004
+ const dot = after.indexOf(".");
2005
+ if (dot === -1) return ctx.nodeOutputs.get(after);
2006
+ const id2 = after.slice(0, dot);
2007
+ const propPath = after.slice(dot + 1);
2003
2008
  const output = ctx.nodeOutputs.get(id2);
2004
- if (output === void 0) {
2005
- throw new Error(`Node "${id2}" has no output yet`);
2006
- }
2007
- if (propPath.startsWith("output")) {
2008
- return getByPath(output, propPath.slice(7).split(".").filter(Boolean));
2009
- }
2010
- return getByPath(output, propPath.split("."));
2009
+ if (output === void 0) throw new Error(`Node "${id2}" has no output yet`);
2010
+ return getByPath(output, propPath.startsWith("output") ? propPath.slice(7).split(".").filter(Boolean) : propPath.split("."));
2011
2011
  }
2012
2012
  if (path2.startsWith("$var.")) {
2013
2013
  const name = path2.slice(5);
2014
- if (!ctx.variables.has(name)) {
2015
- throw new Error(`Variable "${name}" is not defined`);
2016
- }
2014
+ if (!ctx.variables.has(name)) throw new Error(`Variable "${name}" is not defined`);
2017
2015
  return ctx.variables.get(name);
2018
2016
  }
2019
- if (path2.startsWith("$input.")) {
2020
- const key = path2.slice(7);
2021
- return ctx.input[key];
2022
- }
2017
+ if (path2.startsWith("$input.")) return ctx.input[path2.slice(7)];
2023
2018
  if (path2 === "true") return true;
2024
2019
  if (path2 === "false") return false;
2025
2020
  if (path2 === "null") return null;
@@ -2028,23 +2023,15 @@ function resolveRef(path2, ctx) {
2028
2023
  return path2;
2029
2024
  }
2030
2025
  function resolveValue(v, ctx) {
2031
- if (typeof v === "string" && v.startsWith("$")) {
2032
- return resolveRef(v, ctx);
2033
- }
2034
- if (Array.isArray(v)) {
2035
- return v.map((item) => resolveValue(item, ctx));
2036
- }
2026
+ if (typeof v === "string" && v.startsWith("$")) return resolveRef(v, ctx);
2027
+ if (Array.isArray(v)) return v.map((item) => resolveValue(item, ctx));
2037
2028
  if (typeof v === "object" && v !== null) {
2038
2029
  const result = {};
2039
- for (const [k, val] of Object.entries(v)) {
2040
- result[k] = resolveValue(val, ctx);
2041
- }
2030
+ for (const [k, val] of Object.entries(v)) result[k] = resolveValue(val, ctx);
2042
2031
  return result;
2043
2032
  }
2044
2033
  return v;
2045
2034
  }
2046
-
2047
- // workflow/nodes.ts
2048
2035
  function evaluateExpression(expr, ctx) {
2049
2036
  const operators = [
2050
2037
  { op: "===", fn: (a, b) => a === b },
@@ -2074,19 +2061,13 @@ function evaluateExpression(expr, ctx) {
2074
2061
  bestFn = fn;
2075
2062
  }
2076
2063
  }
2077
- function resolveOperand(raw) {
2078
- const trimmed2 = raw.trim();
2079
- if (/^[\d.]+$/.test(trimmed2)) return Number(trimmed2);
2080
- const subIdx = operators.findIndex(({ op }) => trimmed2.includes(op));
2081
- if (subIdx !== -1) return evaluateExpression(trimmed2, ctx);
2082
- return resolveValue(trimmed2, ctx);
2083
- }
2084
2064
  if (bestIdx > 0 && bestOp && bestFn) {
2085
- const leftRaw = expr.slice(0, bestIdx).trim();
2086
- const rightRaw = expr.slice(bestIdx + bestOp.length).trim();
2087
- const left = resolveOperand(leftRaw);
2088
- const right = resolveOperand(rightRaw);
2089
- return bestFn(left, right);
2065
+ const left = expr.slice(0, bestIdx).trim();
2066
+ const right = expr.slice(bestIdx + bestOp.length).trim();
2067
+ const isExpr = (s) => /[+\-*/%&|><=!]/.test(s) && !/^[\d.]+$/.test(s);
2068
+ const lv = isExpr(left) ? evaluateExpression(left, ctx) : /^[\d.]+$/.test(left) ? Number(left) : resolveValue(left, ctx);
2069
+ const rv = isExpr(right) ? evaluateExpression(right, ctx) : /^[\d.]+$/.test(right) ? Number(right) : resolveValue(right, ctx);
2070
+ return bestFn(lv, rv);
2090
2071
  }
2091
2072
  const trimmed = expr.trim();
2092
2073
  if (trimmed === "true") return true;
@@ -2096,466 +2077,448 @@ function evaluateExpression(expr, ctx) {
2096
2077
  if (!isNaN(num) && trimmed !== "") return num;
2097
2078
  return resolveValue(expr, ctx);
2098
2079
  }
2099
- async function executeEval(node, ctx) {
2100
- const expression = node.input.expression;
2101
- if (!expression) throw new Error('eval node requires "expression" field');
2102
- const result = evaluateExpression(expression, ctx);
2103
- return { result };
2104
- }
2105
- async function executeSet(node, ctx) {
2106
- const name = node.input.name;
2107
- const value = node.input.value;
2108
- if (!name) throw new Error('set node requires "name" field');
2109
- let resolved;
2110
- if (typeof value === "string") {
2111
- resolved = evaluateExpression(value, ctx);
2112
- } else {
2113
- resolved = resolveValue(value ?? null, ctx);
2114
- }
2115
- ctx.variables.set(name, resolved);
2116
- return resolved;
2117
- }
2118
- async function executeGet(node, ctx) {
2119
- const name = node.input.name;
2120
- if (!name) throw new Error('get node requires "name" field');
2121
- if (!ctx.variables.has(name)) {
2122
- throw new Error(`Variable "${name}" is not defined`);
2123
- }
2124
- return ctx.variables.get(name);
2125
- }
2126
- async function executeIf(node, ctx) {
2127
- const conditions = node.conditions ?? [];
2128
- for (const condition of conditions) {
2129
- const test = typeof condition.test === "string" ? Boolean(resolveValue(condition.test, ctx)) : condition.test;
2130
- if (test && condition.body) {
2131
- let lastOutput = void 0;
2132
- for (const bodyNode of condition.body) {
2133
- lastOutput = await executeNode(bodyNode, ctx);
2134
- }
2135
- return lastOutput;
2080
+ async function executeNode(node, ctx) {
2081
+ const { tool: nodeType, input, conditions, body } = node;
2082
+ ctx.stepCount++;
2083
+ if (ctx.stepCount > ctx.maxSteps) throw new Error(`Step limit exceeded (${ctx.maxSteps})`);
2084
+ switch (nodeType) {
2085
+ case "eval": {
2086
+ const expression = input.expression;
2087
+ if (!expression) throw new Error('eval node requires "expression"');
2088
+ return { result: evaluateExpression(expression, ctx) };
2089
+ }
2090
+ case "set": {
2091
+ const name = input.name;
2092
+ if (!name) throw new Error('set node requires "name"');
2093
+ const value = typeof input.value === "string" ? evaluateExpression(input.value, ctx) : resolveValue(input.value, ctx);
2094
+ ctx.variables.set(name, value);
2095
+ return value;
2096
+ }
2097
+ case "get": {
2098
+ const name = input.name;
2099
+ if (!name) throw new Error('get node requires "name"');
2100
+ if (!ctx.variables.has(name)) throw new Error(`Variable "${name}" is not defined`);
2101
+ return ctx.variables.get(name);
2102
+ }
2103
+ case "if": {
2104
+ for (const c of conditions ?? []) {
2105
+ const test = typeof c.test === "string" ? Boolean(resolveValue(c.test, ctx)) : c.test;
2106
+ if (test && c.body) {
2107
+ let last;
2108
+ for (const n of c.body) {
2109
+ last = await executeNode(n, ctx);
2110
+ ctx.nodeOutputs.set(n.id, last);
2111
+ }
2112
+ return last;
2113
+ }
2114
+ }
2115
+ return void 0;
2136
2116
  }
2137
- }
2138
- return void 0;
2139
- }
2140
- async function executeWhile(node, ctx) {
2141
- const conditionExpr = node.input.condition;
2142
- if (!conditionExpr) throw new Error('while node requires "condition" field');
2143
- let lastOutput = void 0;
2144
- let iterations = 0;
2145
- const maxIterations = 1e3;
2146
- while (iterations < maxIterations) {
2147
- iterations++;
2148
- ctx.stepCount++;
2149
- if (ctx.stepCount > ctx.maxSteps) {
2150
- throw new Error(`Step limit exceeded (${ctx.maxSteps})`);
2151
- }
2152
- const condition = Boolean(evaluateExpression(conditionExpr, ctx));
2153
- if (!condition) break;
2154
- for (const bodyNode of node.body ?? []) {
2155
- lastOutput = await executeNode(bodyNode, ctx);
2156
- }
2157
- }
2158
- return lastOutput;
2159
- }
2160
- async function executeCall(node, ctx) {
2161
- const toolName = node.input.tool;
2162
- const args = node.input.args ?? {};
2163
- if (toolName && ctx.toolRegistry.has(toolName)) {
2164
- const tool12 = ctx.toolRegistry.get(toolName);
2165
- const resolvedInput = resolveValue(args, ctx);
2166
- const parsed = tool12.inputSchema.parse(resolvedInput);
2167
- return tool12.execute(parsed, {
2168
- nodeId: node.id,
2169
- workflowId: ctx.workflowId,
2170
- onStream: async (event) => {
2171
- if (ctx.sseManager && ctx.workflowId) {
2172
- ctx.sseManager.send(ctx.workflowId, { event: "llm-stream", data: { nodeId: node.id, ...event } });
2117
+ case "while": {
2118
+ const conditionExpr = input.condition;
2119
+ if (!conditionExpr) throw new Error('while node requires "condition"');
2120
+ let last;
2121
+ let iters = 0;
2122
+ while (iters < 1e3) {
2123
+ iters++;
2124
+ ctx.stepCount++;
2125
+ if (ctx.stepCount > ctx.maxSteps) throw new Error(`Step limit exceeded`);
2126
+ if (!Boolean(evaluateExpression(conditionExpr, ctx))) break;
2127
+ for (const n of body ?? []) {
2128
+ last = await executeNode(n, ctx);
2129
+ ctx.nodeOutputs.set(n.id, last);
2173
2130
  }
2174
2131
  }
2175
- });
2176
- }
2177
- const functionName = node.input.function;
2178
- if (functionName && ctx.functions[functionName]) {
2179
- const fn = ctx.functions[functionName];
2180
- const prevFunctions = ctx.functions;
2181
- const prevInput = ctx.input;
2182
- ctx.input = resolveValue(args, ctx);
2183
- let lastOutput = void 0;
2184
- for (const bodyNode of fn.workflow.nodes) {
2185
- lastOutput = await executeNode(bodyNode, ctx);
2186
- }
2187
- ctx.input = prevInput;
2188
- return lastOutput;
2189
- }
2190
- throw new Error(`call node: tool "${toolName ?? functionName}" not found`);
2191
- }
2192
- async function executeHttp(node, ctx) {
2193
- const input = resolveValue(node.input, ctx);
2194
- const url = input.url;
2195
- if (!url) throw new Error('http node requires "url" field');
2196
- const controller = new AbortController();
2197
- const timeout = input.timeout ?? 3e4;
2198
- const timer = setTimeout(() => controller.abort(), timeout);
2199
- try {
2200
- const fetchInit = {
2201
- method: input.method ?? "GET",
2202
- headers: input.headers ?? {},
2203
- signal: controller.signal
2204
- };
2205
- if (input.body && fetchInit.method !== "GET") {
2206
- fetchInit.body = JSON.stringify(input.body);
2132
+ return last;
2207
2133
  }
2208
- const response = await fetch(url, fetchInit);
2209
- const contentType = response.headers.get("content-type") ?? "";
2210
- const body = contentType.includes("application/json") ? await response.json() : await response.text();
2211
- return {
2212
- status: response.status,
2213
- statusText: response.statusText,
2214
- headers: Object.fromEntries(response.headers.entries()),
2215
- body
2216
- };
2217
- } finally {
2218
- clearTimeout(timer);
2219
- }
2220
- }
2221
- var executors = {
2222
- eval: executeEval,
2223
- set: executeSet,
2224
- get: executeGet,
2225
- if: executeIf,
2226
- while: executeWhile,
2227
- call: executeCall,
2228
- http: executeHttp
2229
- };
2230
- async function executeNode(node, ctx) {
2231
- const executor = executors[node.tool];
2232
- if (!executor) {
2233
- throw new Error(`Unknown node type: "${node.tool}"`);
2134
+ case "call": {
2135
+ const toolName = input.tool;
2136
+ const args = resolveValue(input.args ?? {}, ctx);
2137
+ if (ctx.toolRegistry.has(toolName)) {
2138
+ const t = ctx.toolRegistry.get(toolName);
2139
+ const parsed = t.parameters?.parse ? t.parameters.parse(args) : args;
2140
+ return await t.execute(parsed, { toolCallId: node.id });
2141
+ }
2142
+ throw new Error(`call node: tool "${toolName}" not found`);
2143
+ }
2144
+ case "http": {
2145
+ const opts = resolveValue(input, ctx);
2146
+ const url = opts.url;
2147
+ if (!url) throw new Error('http node requires "url"');
2148
+ const controller = new AbortController();
2149
+ const timer = setTimeout(() => controller.abort(), opts.timeout ?? 3e4);
2150
+ try {
2151
+ const res = await fetch(url, {
2152
+ method: opts.method ?? "GET",
2153
+ headers: opts.headers ?? {},
2154
+ body: opts.method !== "GET" ? JSON.stringify(opts.body) : void 0,
2155
+ signal: controller.signal
2156
+ });
2157
+ const ct = res.headers.get("content-type") ?? "";
2158
+ return { status: res.status, body: ct.includes("json") ? await res.json() : await res.text() };
2159
+ } finally {
2160
+ clearTimeout(timer);
2161
+ }
2162
+ }
2163
+ default:
2164
+ throw new Error(`Unknown node type: "${nodeType}"`);
2234
2165
  }
2235
- return executor(node, ctx);
2236
2166
  }
2237
-
2238
- // workflow/llm.ts
2239
- function buildToolsDescription(tools) {
2240
- return Object.entries(tools).map(([key, t]) => {
2241
- const name = t.name || key;
2242
- const schema = t.inputSchema;
2243
- return `- ${name}: ${t.description}
2244
- Input schema: describe as JSON object fields`;
2245
- }).join("\n");
2246
- }
2247
- var SYSTEM_PROMPT_TEMPLATE = `You are a workflow generator. Given a user goal and available tools, output a workflow JSON.
2248
-
2249
- Available tools:
2250
- {{TOOLS}}
2251
-
2252
- Workflow format:
2253
- {
2254
- "name": "workflow name",
2255
- "nodes": [
2256
- {
2257
- "id": "step1",
2258
- "tool": "set",
2259
- "input": { "name": "varName", "value": "initialValue" }
2260
- },
2261
- {
2262
- "id": "step2",
2263
- "tool": "call",
2264
- "input": { "tool": "toolName", "args": { "param1": "$var.varName" } }
2265
- },
2266
- {
2267
- "id": "step3",
2268
- "tool": "if",
2269
- "input": {},
2270
- "conditions": [
2271
- { "test": "$nodes.step2.output.someField", "body": [
2272
- { "id": "step4", "tool": "call", "input": { "tool": "toolName", "args": {} } }
2273
- ]}
2274
- ]
2167
+ function runWorkflow(opts = {}) {
2168
+ const toolRegistry = /* @__PURE__ */ new Map();
2169
+ if (opts.tools) {
2170
+ for (const [key, t] of Object.entries(opts.tools)) {
2171
+ toolRegistry.set(key, t);
2172
+ }
2173
+ }
2174
+ return tool({
2175
+ description: "Execute a multi-step workflow. Supports eval, set, get, if, while, call, http nodes. Use $var.x for variables, $nodes.id.output for previous node results, $input.x for input parameters. Call nodes invoke registered tools.",
2176
+ inputSchema: z.object({
2177
+ goal: z.string().describe("What the workflow should accomplish"),
2178
+ nodes: z.array(z.object({
2179
+ id: z.string(),
2180
+ tool: z.string(),
2181
+ input: z.record(z.string(), z.unknown()).optional(),
2182
+ conditions: z.array(z.object({ test: z.any(), body: z.any() })).optional(),
2183
+ body: z.array(z.any()).optional()
2184
+ })).optional().describe("Workflow nodes. Skip this and provide model for LLM to generate from goal.")
2185
+ }),
2186
+ execute: async (input) => {
2187
+ let nodes;
2188
+ if (input.nodes && input.nodes.length > 0) {
2189
+ nodes = input.nodes;
2190
+ } else if (opts.model) {
2191
+ const toolsDesc = Object.entries(opts.tools ?? {}).map(([k, t]) => `- ${k}: ${t.description}`).join("\n");
2192
+ const result = await generateText({
2193
+ model: opts.model,
2194
+ system: [
2195
+ "You are a workflow generator. Given a user goal and available tools, output a workflow JSON.",
2196
+ "",
2197
+ "Available tools:",
2198
+ toolsDesc,
2199
+ "",
2200
+ "Node types: eval (expression), set (variable), get (variable), if (condition), while (loop), call (tool), http (request).",
2201
+ "Reference syntax: $var.name, $nodes.id.output, $nodes.id.output.field, $input.field",
2202
+ "Output ONLY valid JSON. No explanation, no markdown."
2203
+ ].filter(Boolean).join("\n"),
2204
+ messages: [{ role: "user", content: input.goal }]
2205
+ });
2206
+ const text2 = result.text.trim();
2207
+ const jsonStart = text2.indexOf("{");
2208
+ const jsonEnd = text2.lastIndexOf("}");
2209
+ if (jsonStart === -1 || jsonEnd === -1) throw new Error("LLM did not return valid JSON");
2210
+ const parsed = JSON.parse(text2.slice(jsonStart, jsonEnd + 1));
2211
+ nodes = parsed.nodes ?? parsed.workflow?.nodes ?? [];
2212
+ if (!Array.isArray(nodes)) throw new Error("Generated workflow has no nodes array");
2213
+ } else {
2214
+ throw new Error('Provide either "nodes" or a "model" to generate the workflow from "goal"');
2215
+ }
2216
+ const ctx = {
2217
+ variables: /* @__PURE__ */ new Map(),
2218
+ nodeOutputs: /* @__PURE__ */ new Map(),
2219
+ stepCount: 0,
2220
+ maxSteps: opts.maxSteps ?? 200,
2221
+ toolRegistry
2222
+ };
2223
+ let lastOutput;
2224
+ for (const n of nodes) {
2225
+ const output = await executeNode(n, ctx);
2226
+ ctx.nodeOutputs.set(n.id, output);
2227
+ lastOutput = output;
2228
+ }
2229
+ return { result: lastOutput, nodeOutputs: Object.fromEntries(ctx.nodeOutputs) };
2275
2230
  }
2276
- ]
2231
+ });
2277
2232
  }
2278
2233
 
2279
- Node types:
2280
- - eval: evaluate an expression. input: { expression: "..." }
2281
- - set: assign a variable. input: { name, value }
2282
- - get: read a variable. input: { name }
2283
- - if: conditional branch. input: {}, conditions: [{ test, body }]
2284
- - while: loop. input: { condition }, body: [nodes]
2285
- - call: call a registered tool. input: { tool, args }
2286
- - http: HTTP request. input: { url, method?, headers?, body? }
2287
-
2288
- Reference syntax:
2289
- - $var.name - read a variable
2290
- - $nodes.id.output - output of a previous node
2291
- - $nodes.id.output.field - specific field of a node's output
2292
- - $input.field - workflow input parameter
2234
+ // postgres/client.ts
2235
+ import postgresFactory from "postgres";
2293
2236
 
2294
- Output ONLY valid JSON. No explanation, no markdown.`;
2295
- async function generateWorkflow(goal, tools, generateFn) {
2296
- const toolsDesc = buildToolsDescription(tools);
2297
- const system = SYSTEM_PROMPT_TEMPLATE.replace("{{TOOLS}}", toolsDesc);
2298
- const result = await generateFn({
2299
- system,
2300
- messages: [{ role: "user", content: goal }]
2301
- });
2302
- const text2 = result.text.trim();
2303
- const jsonStart = text2.indexOf("{");
2304
- const jsonEnd = text2.lastIndexOf("}");
2305
- if (jsonStart === -1 || jsonEnd === -1) {
2306
- throw new Error(`LLM output is not valid JSON: ${text2.slice(0, 200)}`);
2307
- }
2308
- const jsonStr = text2.slice(jsonStart, jsonEnd + 1);
2309
- try {
2310
- const workflow2 = JSON.parse(jsonStr);
2311
- if (!workflow2.nodes || !Array.isArray(workflow2.nodes)) {
2312
- throw new Error("Generated workflow has no nodes array");
2313
- }
2314
- return workflow2;
2315
- } catch (err) {
2316
- if (err instanceof SyntaxError) {
2317
- throw new Error(`Failed to parse LLM output as JSON: ${err.message}`);
2237
+ // postgres/schema/sql.ts
2238
+ var SQL = class {
2239
+ strings;
2240
+ values;
2241
+ constructor(strings, values) {
2242
+ this.strings = strings;
2243
+ this.values = values;
2244
+ }
2245
+ toSQL() {
2246
+ let result = "";
2247
+ for (let i = 0; i < this.strings.length; i++) {
2248
+ result += this.strings[i];
2249
+ if (i < this.values.length) {
2250
+ result += String(this.values[i]);
2251
+ }
2318
2252
  }
2319
- throw err;
2253
+ return result;
2320
2254
  }
2255
+ };
2256
+ function sql(strings, ...values) {
2257
+ return new SQL(strings, values);
2321
2258
  }
2322
2259
 
2323
- // workflow/engine.ts
2324
- import { generateText } from "ai";
2325
- function createWorkflowEngine(options) {
2326
- const toolRegistry = /* @__PURE__ */ new Map();
2327
- for (const [key, t] of Object.entries(options.tools)) {
2328
- t.name = t.name || key;
2329
- toolRegistry.set(t.name, t);
2330
- }
2331
- const states = /* @__PURE__ */ new Map();
2332
- async function execute(workflow2, opts) {
2333
- const ctx = {
2334
- variables: /* @__PURE__ */ new Map(),
2335
- nodeOutputs: /* @__PURE__ */ new Map(),
2336
- functions: workflow2.functions ?? {},
2337
- stepCount: 0,
2338
- maxSteps: opts?.maxSteps ?? 1e3,
2339
- input: opts?.initialInput ?? {},
2340
- toolRegistry,
2341
- sseManager: options.sseManager,
2342
- workflowId: opts?.workflowId
2343
- };
2344
- let lastOutput = void 0;
2345
- for (const node of workflow2.nodes) {
2346
- ctx.stepCount++;
2347
- if (ctx.stepCount > ctx.maxSteps) {
2348
- throw new Error(`Step limit exceeded (${ctx.maxSteps})`);
2349
- }
2350
- options.sseManager?.send(ctx.workflowId ?? "", { event: "node-start", data: { nodeId: node.id, tool: node.tool, input: node.input } });
2351
- const output = await executeNode(node, ctx);
2352
- ctx.nodeOutputs.set(node.id, output);
2353
- lastOutput = output;
2354
- options.sseManager?.send(ctx.workflowId ?? "", { event: "node-end", data: { nodeId: node.id, output } });
2355
- }
2356
- return lastOutput;
2357
- }
2358
- async function runAsync(workflowId, workflow2, opts) {
2359
- const state = {
2360
- workflowId,
2361
- status: "running",
2362
- goal: workflow2.name ?? "",
2363
- startTime: Date.now()
2364
- };
2365
- states.set(workflowId, state);
2366
- const sse = options.sseManager;
2367
- sse?.send(workflowId, { event: "workflow-start", data: { workflowId, goal: state.goal } });
2368
- try {
2369
- const result = await execute(workflow2, { ...opts, workflowId });
2370
- state.status = "completed";
2371
- state.result = result;
2372
- state.endTime = Date.now();
2373
- sse?.send(workflowId, { event: "complete", data: { result, duration: state.endTime - state.startTime } });
2374
- } catch (err) {
2375
- state.status = "error";
2376
- state.error = err instanceof Error ? err.message : String(err);
2377
- state.endTime = Date.now();
2378
- sse?.send(workflowId, { event: "error", data: { error: state.error } });
2379
- } finally {
2380
- sse?.close(workflowId);
2381
- }
2260
+ // postgres/schema/columns.ts
2261
+ var ColumnBuilder = class {
2262
+ name;
2263
+ sqlType;
2264
+ isPrimaryKey = false;
2265
+ isNullable = true;
2266
+ isUnique = false;
2267
+ isAutoGenerate = false;
2268
+ defaultExpr = null;
2269
+ ref = null;
2270
+ constructor(name, sqlType) {
2271
+ this.name = name;
2272
+ this.sqlType = sqlType;
2273
+ }
2274
+ primaryKey() {
2275
+ this.isPrimaryKey = true;
2276
+ this.isNullable = false;
2277
+ return this;
2382
2278
  }
2383
- async function generateWorkflow2(goal) {
2384
- if (!options.model) {
2385
- throw new Error('LLM model is required for generateWorkflow. Pass "model" to createWorkflowEngine.');
2386
- }
2387
- return generateWorkflow(goal, options.tools, async (prompt) => {
2388
- const result = await generateText({
2389
- model: options.model,
2390
- system: prompt.system,
2391
- messages: prompt.messages
2392
- });
2393
- return { text: result.text };
2394
- });
2279
+ notNull() {
2280
+ this.isNullable = false;
2281
+ return this;
2395
2282
  }
2396
- return {
2397
- execute,
2398
- runAsync,
2399
- generateWorkflow: generateWorkflow2,
2400
- getState(workflowId) {
2401
- return states.get(workflowId);
2283
+ nullable() {
2284
+ this.isNullable = true;
2285
+ return this;
2286
+ }
2287
+ default(expr) {
2288
+ if (expr instanceof SQL) {
2289
+ this.defaultExpr = expr.toSQL();
2290
+ } else if (typeof expr === "string") {
2291
+ this.defaultExpr = `'${expr.replace(/'/g, "''")}'`;
2292
+ } else {
2293
+ this.defaultExpr = String(expr);
2402
2294
  }
2403
- };
2295
+ return this;
2296
+ }
2297
+ unique() {
2298
+ this.isUnique = true;
2299
+ return this;
2300
+ }
2301
+ references(table, column = "id", onDelete) {
2302
+ this.ref = { table, column, onDelete };
2303
+ return this;
2304
+ }
2305
+ };
2306
+ function col(name, sqlType) {
2307
+ return new ColumnBuilder(name, sqlType);
2404
2308
  }
2405
-
2406
- // sse.ts
2407
- var encoder = new TextEncoder();
2408
- function formatSSE(event, data) {
2409
- return `event: ${event}
2410
- data: ${JSON.stringify(data)}
2411
-
2412
- `;
2309
+ function serial(name) {
2310
+ const c = col(name, "SERIAL");
2311
+ c.isAutoGenerate = true;
2312
+ return c;
2413
2313
  }
2414
- function formatSSEData(data) {
2415
- return `data: ${JSON.stringify(data)}
2416
-
2417
- `;
2314
+ function uuid(name) {
2315
+ return col(name, "UUID");
2418
2316
  }
2419
- function createSSEStream(iterable, opts) {
2420
- return new Response(
2421
- new ReadableStream({
2422
- async start(controller) {
2423
- try {
2424
- for await (const event of iterable) {
2425
- const text2 = event.type ? formatSSE(event.type, event) : formatSSEData(event);
2426
- controller.enqueue(encoder.encode(text2));
2427
- }
2428
- } catch (e) {
2429
- if (e.name !== "AbortError") {
2430
- controller.enqueue(
2431
- encoder.encode(formatSSE("error", { error: e.message }))
2432
- );
2433
- }
2434
- } finally {
2435
- controller.close();
2436
- }
2437
- }
2438
- }),
2439
- {
2440
- status: opts?.status ?? 200,
2441
- headers: {
2442
- "Content-Type": "text/event-stream",
2443
- "Cache-Control": "no-cache",
2444
- Connection: "keep-alive",
2445
- ...opts?.headers
2446
- }
2447
- }
2448
- );
2317
+ function text(name) {
2318
+ return col(name, "TEXT");
2319
+ }
2320
+ function integer(name) {
2321
+ return col(name, "INTEGER");
2322
+ }
2323
+ function boolean_(name) {
2324
+ return col(name, "BOOLEAN");
2325
+ }
2326
+ function timestamptz(name) {
2327
+ return col(name, "TIMESTAMPTZ");
2328
+ }
2329
+ function jsonb(name) {
2330
+ return col(name, "JSONB");
2331
+ }
2332
+ function textArray(name) {
2333
+ return col(name, "TEXT[]");
2334
+ }
2335
+ function vector(name, dims) {
2336
+ return col(name, `vector(${dims})`);
2337
+ }
2338
+ function toDDL(col2) {
2339
+ const parts = [`"${col2.name}"`, col2.sqlType];
2340
+ if (col2.isPrimaryKey) parts.push("PRIMARY KEY");
2341
+ if (!col2.isPrimaryKey && !col2.isNullable) parts.push("NOT NULL");
2342
+ if (col2.isUnique) parts.push("UNIQUE");
2343
+ if (col2.defaultExpr) parts.push(`DEFAULT ${col2.defaultExpr}`);
2344
+ if (col2.ref) {
2345
+ parts.push(`REFERENCES "${col2.ref.table}"("${col2.ref.column}")`);
2346
+ if (col2.ref.onDelete) parts.push(`ON DELETE ${col2.ref.onDelete.toUpperCase()}`);
2347
+ }
2348
+ return parts.join(" ");
2449
2349
  }
2450
2350
 
2451
- // workflow/sse.ts
2452
- function createSSEManager() {
2453
- const streams = /* @__PURE__ */ new Map();
2454
- const encoder2 = new TextEncoder();
2455
- function createStream(workflowId) {
2456
- const state = {
2457
- controller: null,
2458
- encoder: encoder2,
2459
- closed: false,
2460
- buffer: []
2461
- };
2462
- const stream = new ReadableStream({
2463
- start(controller) {
2464
- state.controller = controller;
2465
- streams.set(workflowId, state);
2466
- for (const chunk of state.buffer) {
2467
- try {
2468
- controller.enqueue(chunk);
2469
- } catch {
2470
- break;
2471
- }
2472
- }
2473
- state.buffer = [];
2474
- },
2475
- cancel() {
2476
- state.closed = true;
2477
- streams.delete(workflowId);
2478
- }
2479
- });
2480
- return stream;
2351
+ // postgres/schema/table.ts
2352
+ var Table = class {
2353
+ tableName;
2354
+ columns;
2355
+ colEntries;
2356
+ constructor(tableName, builders) {
2357
+ this.tableName = tableName;
2358
+ this.columns = Object.values(builders);
2359
+ this.colEntries = Object.entries(builders).map(([prop, col2]) => ({
2360
+ prop,
2361
+ db: col2.name,
2362
+ auto: col2.isAutoGenerate
2363
+ }));
2481
2364
  }
2482
- function send(workflowId, event) {
2483
- const state = streams.get(workflowId);
2484
- if (!state || state.closed) return;
2485
- const chunk = encoder2.encode(formatSSE(event.event, event.data));
2486
- if (state.controller) {
2487
- try {
2488
- state.controller.enqueue(chunk);
2489
- } catch {
2490
- state.closed = true;
2491
- streams.delete(workflowId);
2365
+ async create(sql2) {
2366
+ const colDDL = this.columns.map(toDDL);
2367
+ const ddl = `CREATE TABLE IF NOT EXISTS "${this.tableName}" (
2368
+ ${colDDL.join(",\n ")}
2369
+ )`;
2370
+ await sql2.unsafe(ddl);
2371
+ }
2372
+ async drop(sql2, opts) {
2373
+ const cascade = opts?.cascade ? " CASCADE" : "";
2374
+ await sql2.unsafe(`DROP TABLE IF EXISTS "${this.tableName}"${cascade}`);
2375
+ }
2376
+ async createIndex(sql2, columns, opts) {
2377
+ const cols = Array.isArray(columns) ? columns : [columns];
2378
+ const name = `"${this.tableName}_${cols.join("_")}${opts?.unique ? "_uidx" : "_idx"}"`;
2379
+ const unique = opts?.unique ? "UNIQUE" : "";
2380
+ const using = opts?.type ? `USING ${opts.type.toUpperCase()}` : "";
2381
+ const colList = cols.map((c) => opts?.desc ? `"${c}" DESC` : `"${c}"`).join(", ");
2382
+ const operator = opts?.operator ? ` ${opts.operator}` : "";
2383
+ const ddl = `CREATE ${unique} INDEX IF NOT EXISTS ${name} ON "${this.tableName}" ${using} (${colList}${operator})`.replace(/\s+/g, " ");
2384
+ await sql2.unsafe(ddl);
2385
+ }
2386
+ async createUniqueIndex(sql2, columns) {
2387
+ await this.createIndex(sql2, columns, { unique: true });
2388
+ }
2389
+ // --- CRUD ---
2390
+ async insert(sql2, data) {
2391
+ const filtered = {};
2392
+ for (const { prop, db, auto } of this.colEntries) {
2393
+ if (auto) continue;
2394
+ if (prop in data) {
2395
+ filtered[db] = data[prop];
2492
2396
  }
2493
- } else {
2494
- state.buffer.push(chunk);
2495
2397
  }
2398
+ const [row] = await sql2`
2399
+ INSERT INTO ${sql2(this.tableName)} ${sql2(filtered)} RETURNING *
2400
+ `;
2401
+ return row;
2496
2402
  }
2497
- function close(workflowId) {
2498
- const state = streams.get(workflowId);
2499
- if (!state) return;
2500
- state.closed = true;
2501
- streams.delete(workflowId);
2502
- try {
2503
- state.controller?.close();
2504
- } catch {
2505
- }
2403
+ async findById(sql2, id2) {
2404
+ const [row] = await sql2`
2405
+ SELECT * FROM ${sql2(this.tableName)}
2406
+ WHERE ${sql2("id")} = ${id2} LIMIT 1
2407
+ `;
2408
+ return row ?? void 0;
2506
2409
  }
2507
- return { createStream, send, close };
2508
- }
2509
-
2510
- // workflow/route.ts
2511
- import crypto from "node:crypto";
2512
- function workflow(handler) {
2513
- const r = new Router();
2514
- const sseManager = createSSEManager();
2515
- r.get("/:workflowId/events", async (req, ctx) => {
2516
- const stream = sseManager.createStream(ctx.params.workflowId);
2517
- return new Response(stream, {
2518
- headers: {
2519
- "Content-Type": "text/event-stream",
2520
- "Cache-Control": "no-cache",
2521
- "Connection": "keep-alive"
2410
+ async find(sql2, where, opts) {
2411
+ const conditions = [];
2412
+ const values = [];
2413
+ for (const [prop, value] of Object.entries(where || {})) {
2414
+ if (value === void 0) continue;
2415
+ const entry = this.colEntries.find((e) => e.prop === prop);
2416
+ const db = entry ? entry.db : prop;
2417
+ conditions.push(`"${db}" = $${conditions.length + 1}`);
2418
+ values.push(value);
2419
+ }
2420
+ let query = `SELECT * FROM "${this.tableName}"`;
2421
+ if (conditions.length > 0) query += ` WHERE ${conditions.join(" AND ")}`;
2422
+ if (opts?.orderBy) {
2423
+ const orders = Object.entries(opts.orderBy).map(([prop, dir]) => {
2424
+ const entry = this.colEntries.find((e) => e.prop === prop);
2425
+ return `"${entry?.db || prop}" ${dir.toUpperCase()}`;
2426
+ }).join(", ");
2427
+ query += ` ORDER BY ${orders}`;
2428
+ }
2429
+ if (opts?.limit) query += ` LIMIT ${opts.limit}`;
2430
+ if (opts?.offset) query += ` OFFSET ${opts.offset}`;
2431
+ if (conditions.length > 0 || opts?.orderBy || opts?.limit !== void 0 || opts?.offset !== void 0) {
2432
+ const rows2 = await sql2.unsafe(query, values);
2433
+ return rows2;
2434
+ }
2435
+ const rows = await sql2`SELECT * FROM ${sql2(this.tableName)}`;
2436
+ return rows;
2437
+ }
2438
+ async update(sql2, where, data) {
2439
+ const sets = [];
2440
+ const setValues = [];
2441
+ for (const { prop, db } of this.colEntries) {
2442
+ if (prop in data && data[prop] !== void 0) {
2443
+ const val = data[prop];
2444
+ if (val instanceof SQL) {
2445
+ sets.push(`"${db}" = ${val.toSQL()}`);
2446
+ } else {
2447
+ sets.push(`"${db}" = $${sets.length + 1}`);
2448
+ setValues.push(val);
2449
+ }
2522
2450
  }
2523
- });
2524
- });
2525
- r.post("/", async (req, ctx) => {
2526
- const options = await handler(req, ctx);
2527
- const engine = createWorkflowEngine({
2528
- tools: options.tools,
2529
- model: options.model,
2530
- sseManager: options.stream ? sseManager : void 0
2531
- });
2532
- const body = await req.json();
2533
- let wf;
2534
- if (body.goal && options.model) {
2535
- wf = await engine.generateWorkflow(body.goal);
2536
- } else if (body.workflow) {
2537
- wf = body.workflow;
2538
- } else if (body.nodes) {
2539
- wf = { nodes: body.nodes };
2540
- } else {
2541
- return Response.json(
2542
- { error: 'Provide "goal" (with model) or "workflow"/"nodes"' },
2543
- { status: 400 }
2544
- );
2545
2451
  }
2546
- if (options.stream && sseManager) {
2547
- const workflowId = crypto.randomUUID();
2548
- engine.runAsync(workflowId, wf);
2549
- return Response.json({ workflowId, eventsUrl: `/${workflowId}/events` });
2452
+ const values = [...setValues];
2453
+ const wConditions = [];
2454
+ for (const [prop, value] of Object.entries(where)) {
2455
+ if (value === void 0) continue;
2456
+ const entry = this.colEntries.find((e) => e.prop === prop);
2457
+ const db = entry ? entry.db : prop;
2458
+ wConditions.push(`"${db}" = $${values.length + 1}`);
2459
+ values.push(value);
2550
2460
  }
2551
- const result = await engine.execute(wf);
2552
- return Response.json({ workflow: wf, result });
2553
- });
2554
- return r;
2461
+ if (sets.length === 0 || wConditions.length === 0) return void 0;
2462
+ const query = `UPDATE "${this.tableName}" SET ${sets.join(", ")} WHERE ${wConditions.join(" AND ")} RETURNING *`;
2463
+ const rows = await sql2.unsafe(query, values);
2464
+ return rows[0] ?? void 0;
2465
+ }
2466
+ async delete(sql2, where) {
2467
+ const conditions = [];
2468
+ const values = [];
2469
+ for (const [prop, value] of Object.entries(where)) {
2470
+ if (value === void 0) continue;
2471
+ const entry = this.colEntries.find((e) => e.prop === prop);
2472
+ const db = entry ? entry.db : prop;
2473
+ conditions.push(`"${db}" = $${conditions.length + 1}`);
2474
+ values.push(value);
2475
+ }
2476
+ if (conditions.length === 0) return false;
2477
+ const query = `DELETE FROM "${this.tableName}" WHERE ${conditions.join(" AND ")} RETURNING 1`;
2478
+ const rows = await sql2.unsafe(query, values);
2479
+ return rows.length > 0;
2480
+ }
2481
+ };
2482
+ var BoundTable = class {
2483
+ inner;
2484
+ sql;
2485
+ constructor(sql2, tableName, builders) {
2486
+ this.inner = new Table(tableName, builders);
2487
+ this.sql = sql2;
2488
+ }
2489
+ async create() {
2490
+ await this.inner.create(this.sql);
2491
+ }
2492
+ async drop(opts) {
2493
+ await this.inner.drop(this.sql, opts);
2494
+ }
2495
+ async createIndex(columns, opts) {
2496
+ await this.inner.createIndex(this.sql, columns, opts);
2497
+ }
2498
+ async createUniqueIndex(columns) {
2499
+ await this.inner.createUniqueIndex(this.sql, columns);
2500
+ }
2501
+ async insert(data) {
2502
+ return await this.inner.insert(this.sql, data);
2503
+ }
2504
+ async findById(id2) {
2505
+ return await this.inner.findById(this.sql, id2);
2506
+ }
2507
+ async find(where, opts) {
2508
+ return await this.inner.find(this.sql, where, opts);
2509
+ }
2510
+ async update(where, data) {
2511
+ return await this.inner.update(this.sql, where, data);
2512
+ }
2513
+ async delete(where) {
2514
+ return await this.inner.delete(this.sql, where);
2515
+ }
2516
+ };
2517
+ function pgTable(tableName, builders) {
2518
+ return new Table(tableName, builders);
2555
2519
  }
2556
2520
 
2557
2521
  // postgres/client.ts
2558
- import postgresFactory from "postgres";
2559
2522
  function postgres(opts) {
2560
2523
  const options = typeof opts === "string" ? { connection: opts } : opts ?? {};
2561
2524
  const connection = options.connection ?? process.env.DATABASE_URL;
@@ -2581,6 +2544,9 @@ function postgres(opts) {
2581
2544
  return next(req, ctx);
2582
2545
  });
2583
2546
  mw.sql = sql2;
2547
+ mw.table = ((tableName, builders) => {
2548
+ return new BoundTable(sql2, tableName, builders);
2549
+ });
2584
2550
  mw.migrate = async () => {
2585
2551
  };
2586
2552
  mw.transaction = (async (fn) => {
@@ -2608,66 +2574,62 @@ var PgModule = class {
2608
2574
  // user/client.ts
2609
2575
  import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
2610
2576
  import jwt2 from "jsonwebtoken";
2611
- import { z } from "zod";
2577
+ import { z as z2 } from "zod";
2612
2578
 
2613
2579
  // user/migrate.ts
2614
2580
  async function migrate(opts) {
2615
- const { pg, usersTable, oauth2 } = opts;
2616
- await pg.sql.unsafe(`
2617
- CREATE TABLE IF NOT EXISTS "${usersTable}" (
2618
- "id" SERIAL PRIMARY KEY,
2619
- "email" TEXT UNIQUE NOT NULL,
2620
- "password" TEXT NOT NULL,
2621
- "name" TEXT NOT NULL,
2622
- "role" TEXT NOT NULL DEFAULT 'user',
2623
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
2624
- "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
2625
- )
2626
- `);
2581
+ const { pg, usersTable: name, oauth2 } = opts;
2582
+ const users = pgTable(name, {
2583
+ id: serial("id").primaryKey(),
2584
+ email: text("email").unique().notNull(),
2585
+ password: text("password").notNull(),
2586
+ name: text("name").notNull(),
2587
+ role: text("role").default("user"),
2588
+ created_at: timestamptz("created_at").default(sql`NOW()`),
2589
+ updated_at: timestamptz("updated_at").default(sql`NOW()`)
2590
+ });
2591
+ await users.create(pg.sql);
2627
2592
  if (!oauth2) return;
2628
- await pg.sql.unsafe(`
2629
- CREATE TABLE IF NOT EXISTS "_oauth2_clients" (
2630
- "id" SERIAL PRIMARY KEY,
2631
- "name" TEXT NOT NULL,
2632
- "client_id" TEXT UNIQUE NOT NULL,
2633
- "client_secret" TEXT NOT NULL,
2634
- "redirect_uris" TEXT[] NOT NULL,
2635
- "scopes" TEXT DEFAULT '',
2636
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
2637
- )
2638
- `);
2639
- await pg.sql.unsafe(`
2640
- CREATE TABLE IF NOT EXISTS "_oauth2_codes" (
2641
- "id" SERIAL PRIMARY KEY,
2642
- "code" TEXT UNIQUE NOT NULL,
2643
- "client_id" TEXT NOT NULL,
2644
- "user_id" INTEGER NOT NULL REFERENCES "${usersTable}"("id"),
2645
- "redirect_uri" TEXT NOT NULL,
2646
- "code_challenge" TEXT,
2647
- "code_challenge_method" TEXT,
2648
- "scope" TEXT,
2649
- "expires_at" TIMESTAMPTZ NOT NULL,
2650
- "used" BOOLEAN NOT NULL DEFAULT FALSE
2651
- )
2652
- `);
2653
- await pg.sql.unsafe(`
2654
- CREATE TABLE IF NOT EXISTS "_oauth2_tokens" (
2655
- "id" SERIAL PRIMARY KEY,
2656
- "token" TEXT UNIQUE NOT NULL,
2657
- "client_id" TEXT NOT NULL,
2658
- "user_id" INTEGER REFERENCES "${usersTable}"("id"),
2659
- "scope" TEXT,
2660
- "expires_at" TIMESTAMPTZ NOT NULL,
2661
- "revoked" BOOLEAN NOT NULL DEFAULT FALSE
2662
- )
2663
- `);
2593
+ const clients2 = pgTable("_oauth2_clients", {
2594
+ id: serial("id").primaryKey(),
2595
+ name: text("name").notNull(),
2596
+ client_id: text("client_id").unique().notNull(),
2597
+ client_secret: text("client_secret").notNull(),
2598
+ redirect_uris: textArray("redirect_uris").notNull(),
2599
+ scopes: text("scopes").default(""),
2600
+ created_at: timestamptz("created_at").default(sql`NOW()`)
2601
+ });
2602
+ await clients2.create(pg.sql);
2603
+ const codes = pgTable("_oauth2_codes", {
2604
+ id: serial("id").primaryKey(),
2605
+ code: text("code").unique().notNull(),
2606
+ client_id: text("client_id").notNull(),
2607
+ user_id: integer("user_id").notNull().references(name, "id"),
2608
+ redirect_uri: text("redirect_uri").notNull(),
2609
+ code_challenge: text("code_challenge"),
2610
+ code_challenge_method: text("code_challenge_method"),
2611
+ scope: text("scope"),
2612
+ expires_at: timestamptz("expires_at").notNull(),
2613
+ used: boolean_("used").default(false)
2614
+ });
2615
+ await codes.create(pg.sql);
2616
+ const tokens = pgTable("_oauth2_tokens", {
2617
+ id: serial("id").primaryKey(),
2618
+ token: text("token").unique().notNull(),
2619
+ client_id: text("client_id").notNull(),
2620
+ user_id: integer("user_id").references(name, "id"),
2621
+ scope: text("scope"),
2622
+ expires_at: timestamptz("expires_at").notNull(),
2623
+ revoked: boolean_("revoked").default(false)
2624
+ });
2625
+ await tokens.create(pg.sql);
2664
2626
  }
2665
2627
 
2666
2628
  // user/oauth2.ts
2667
- import crypto2 from "node:crypto";
2629
+ import crypto from "node:crypto";
2668
2630
  import jwt from "jsonwebtoken";
2669
2631
  function createOAuth2Server(deps) {
2670
- const { pg, usersTable, jwtSecret, expiresIn } = deps;
2632
+ const { pg, users, jwtSecret, expiresIn } = deps;
2671
2633
  async function getClient(clientId) {
2672
2634
  const [row] = await pg.sql`
2673
2635
  SELECT * FROM "_oauth2_clients" WHERE "client_id" = ${clientId} LIMIT 1
@@ -2683,8 +2645,8 @@ function createOAuth2Server(deps) {
2683
2645
  };
2684
2646
  }
2685
2647
  async function registerClient(data) {
2686
- const clientId = crypto2.randomUUID();
2687
- const clientSecret = crypto2.randomBytes(32).toString("hex");
2648
+ const clientId = crypto.randomUUID();
2649
+ const clientSecret = crypto.randomBytes(32).toString("hex");
2688
2650
  const [row] = await pg.sql`
2689
2651
  INSERT INTO "_oauth2_clients" ("name", "client_id", "client_secret", "redirect_uris")
2690
2652
  VALUES (${data.name}, ${clientId}, ${clientSecret}, ${pg.sql.array(data.redirectUris)})
@@ -2832,7 +2794,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
2832
2794
  const loc2 = `${redirectUri}?error=access_denied${state ? `&state=${state}` : ""}`;
2833
2795
  return Response.redirect(loc2, 302);
2834
2796
  }
2835
- const code = crypto2.randomUUID();
2797
+ const code = crypto.randomUUID();
2836
2798
  const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
2837
2799
  await pg.sql`
2838
2800
  INSERT INTO "_oauth2_codes" ("code", "client_id", "user_id", "redirect_uri", "code_challenge", "code_challenge_method", "scope", "expires_at")
@@ -2897,16 +2859,14 @@ h2{color:#dc2626}.desc{color:#555}</style>
2897
2859
  if (stored.code_challenge_method === "plain") {
2898
2860
  expected = codeVerifier;
2899
2861
  } else {
2900
- expected = crypto2.createHash("sha256").update(codeVerifier).digest().toString("base64url");
2862
+ expected = crypto.createHash("sha256").update(codeVerifier).digest().toString("base64url");
2901
2863
  }
2902
2864
  if (expected !== stored.code_challenge) {
2903
2865
  return Response.json({ error: "invalid_grant", error_description: "code_verifier mismatch" }, { status: 400 });
2904
2866
  }
2905
2867
  }
2906
2868
  await pg.sql`UPDATE "_oauth2_codes" SET "used" = TRUE WHERE "id" = ${stored.id}`;
2907
- const [user2] = await pg.sql`
2908
- SELECT * FROM ${pg.sql(usersTable)} WHERE "id" = ${stored.user_id} LIMIT 1
2909
- `;
2869
+ const user2 = await users.findById(stored.user_id);
2910
2870
  if (!user2) {
2911
2871
  return Response.json({ error: "invalid_grant" }, { status: 400 });
2912
2872
  }
@@ -2916,7 +2876,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
2916
2876
  jwtSecret,
2917
2877
  { expiresIn }
2918
2878
  );
2919
- const refreshToken = crypto2.randomUUID();
2879
+ const refreshToken = crypto.randomUUID();
2920
2880
  const refreshExpires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3);
2921
2881
  await pg.sql`
2922
2882
  INSERT INTO "_oauth2_tokens" ("token", "client_id", "user_id", "scope", "expires_at")
@@ -2954,14 +2914,14 @@ h2{color:#dc2626}.desc{color:#555}</style>
2954
2914
  }
2955
2915
 
2956
2916
  // user/client.ts
2957
- var RegisterSchema = z.object({
2958
- email: z.string().email(),
2959
- password: z.string().min(6),
2960
- name: z.string().min(1)
2917
+ var RegisterSchema = z2.object({
2918
+ email: z2.string().email(),
2919
+ password: z2.string().min(6),
2920
+ name: z2.string().min(1)
2961
2921
  });
2962
- var LoginSchema = z.object({
2963
- email: z.string().email(),
2964
- password: z.string().min(1)
2922
+ var LoginSchema = z2.object({
2923
+ email: z2.string().email(),
2924
+ password: z2.string().min(1)
2965
2925
  });
2966
2926
  function hashPassword(password) {
2967
2927
  const salt = randomBytes(16).toString("hex");
@@ -2981,9 +2941,18 @@ function user(options) {
2981
2941
  const expiresIn = options.expiresIn ?? "24h";
2982
2942
  const oauth2Enabled = options.oauth2?.server ?? false;
2983
2943
  const base = new PgModule(pg);
2944
+ const users = pg.table(table, {
2945
+ id: serial("id").primaryKey(),
2946
+ email: text("email").unique().notNull(),
2947
+ password: text("password").notNull(),
2948
+ name: text("name").notNull(),
2949
+ role: text("role").default("user"),
2950
+ created_at: timestamptz("created_at").default(sql`NOW()`),
2951
+ updated_at: timestamptz("updated_at").default(sql`NOW()`)
2952
+ });
2984
2953
  let oauth2 = null;
2985
2954
  if (oauth2Enabled) {
2986
- oauth2 = createOAuth2Server({ pg, usersTable: table, jwtSecret: secret, expiresIn });
2955
+ oauth2 = createOAuth2Server({ pg, users, jwtSecret: secret, expiresIn });
2987
2956
  }
2988
2957
  async function migrate6() {
2989
2958
  await migrate({ pg, usersTable: table, oauth2: oauth2Enabled });
@@ -3000,12 +2969,11 @@ function user(options) {
3000
2969
  return user2;
3001
2970
  }
3002
2971
  async function findByEmail(email) {
3003
- const [row] = await pg.sql`SELECT * FROM ${pg.sql(table)} WHERE "email" = ${email} LIMIT 1`;
3004
- return row;
2972
+ const rows = await users.find({ email });
2973
+ return rows[0];
3005
2974
  }
3006
2975
  async function findById(id2) {
3007
- const [row] = await pg.sql`SELECT * FROM ${pg.sql(table)} WHERE "id" = ${id2} LIMIT 1`;
3008
- return row;
2976
+ return await users.findById(id2);
3009
2977
  }
3010
2978
  async function register(data) {
3011
2979
  const { email, password, name } = RegisterSchema.parse(data);
@@ -3016,16 +2984,15 @@ function user(options) {
3016
2984
  throw err;
3017
2985
  }
3018
2986
  const hashed = hashPassword(password);
3019
- const [row] = await pg.sql`
3020
- INSERT INTO ${pg.sql(table)} ("email", "password", "name") VALUES (${email}, ${hashed}, ${name}) RETURNING *
3021
- `;
2987
+ const row = await users.insert({ email, password: hashed, name });
3022
2988
  const userData = row;
3023
2989
  const token = signToken(userData);
3024
2990
  return { user: stripPassword(userData), token };
3025
2991
  }
3026
2992
  async function login(data) {
3027
2993
  const { email, password } = LoginSchema.parse(data);
3028
- const row = await findByEmail(email);
2994
+ const rows = await users.find({ email });
2995
+ const row = rows[0];
3029
2996
  if (!row) {
3030
2997
  const err = new Error("Invalid email or password");
3031
2998
  err.status = 401;
@@ -3074,7 +3041,7 @@ function user(options) {
3074
3041
  const result = await register(body);
3075
3042
  return Response.json(result, { status: 201 });
3076
3043
  } catch (err) {
3077
- if (err instanceof z.ZodError) {
3044
+ if (err instanceof z2.ZodError) {
3078
3045
  return Response.json({ error: "Validation failed", issues: err.issues }, { status: 400 });
3079
3046
  }
3080
3047
  const status = err.status ?? 500;
@@ -3089,7 +3056,7 @@ function user(options) {
3089
3056
  res.headers.set("Set-Cookie", `session=${result.token}; HttpOnly; SameSite=Lax; Path=/`);
3090
3057
  return res;
3091
3058
  } catch (err) {
3092
- if (err instanceof z.ZodError) {
3059
+ if (err instanceof z2.ZodError) {
3093
3060
  return Response.json({ error: "Validation failed", issues: err.issues }, { status: 400 });
3094
3061
  }
3095
3062
  const status = err.status ?? 500;
@@ -3143,7 +3110,7 @@ function redis(opts) {
3143
3110
 
3144
3111
  // queue/index.ts
3145
3112
  import { Redis as IORedis2 } from "ioredis";
3146
- import crypto3 from "node:crypto";
3113
+ import crypto2 from "node:crypto";
3147
3114
  function cronNext(expr, from = /* @__PURE__ */ new Date()) {
3148
3115
  const parts = expr.trim().split(/\s+/);
3149
3116
  if (parts.length !== 5) throw new Error(`Invalid cron expression "${expr}": expected 5 fields`);
@@ -3234,7 +3201,7 @@ function queue(opts) {
3234
3201
  if (job.schedule) {
3235
3202
  try {
3236
3203
  const nextRun = cronNext(job.schedule);
3237
- const nextJob = { ...job, id: crypto3.randomUUID(), runAt: nextRun, createdAt: Date.now() };
3204
+ const nextJob = { ...job, id: crypto2.randomUUID(), runAt: nextRun, createdAt: Date.now() };
3238
3205
  redis2.zadd(jobKey, nextRun, JSON.stringify(nextJob)).catch(() => {
3239
3206
  });
3240
3207
  } catch {
@@ -3252,7 +3219,7 @@ function queue(opts) {
3252
3219
  }
3253
3220
  }
3254
3221
  mw.add = function add(type, payload, opts2) {
3255
- const id2 = crypto3.randomUUID();
3222
+ const id2 = crypto2.randomUUID();
3256
3223
  let runAt;
3257
3224
  if (opts2?.schedule) {
3258
3225
  runAt = cronNext(opts2.schedule);
@@ -3289,46 +3256,39 @@ function queue(opts) {
3289
3256
 
3290
3257
  // tenant/migrate.ts
3291
3258
  async function migrate2(opts) {
3292
- const { sql: sql2, usersTable } = opts;
3259
+ const { sql: sql2 } = opts;
3293
3260
  await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "vector"`);
3294
- await sql2.unsafe(`
3295
- CREATE TABLE IF NOT EXISTS "_tenants" (
3296
- "id" TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
3297
- "name" TEXT NOT NULL,
3298
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
3299
- )
3300
- `);
3301
- await sql2.unsafe(`
3302
- CREATE TABLE IF NOT EXISTS "_tenant_members" (
3303
- "id" SERIAL PRIMARY KEY,
3304
- "tenant_id" TEXT NOT NULL REFERENCES "_tenants"("id") ON DELETE CASCADE,
3305
- "user_id" INTEGER NOT NULL,
3306
- "role" TEXT NOT NULL DEFAULT 'member',
3307
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
3308
- UNIQUE("tenant_id", "user_id")
3309
- )
3310
- `);
3311
- await sql2.unsafe(`
3312
- CREATE INDEX IF NOT EXISTS "_tenant_members_user_id_idx" ON "_tenant_members" ("user_id")
3313
- `);
3314
- await sql2.unsafe(`
3315
- CREATE TABLE IF NOT EXISTS "_user_tables" (
3316
- "id" SERIAL PRIMARY KEY,
3317
- "tenant_id" TEXT NOT NULL REFERENCES "_tenants"("id") ON DELETE CASCADE,
3318
- "slug" TEXT NOT NULL,
3319
- "label" TEXT NOT NULL DEFAULT '',
3320
- "fields" JSONB NOT NULL DEFAULT '[]',
3321
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
3322
- UNIQUE("tenant_id", "slug")
3323
- )
3324
- `);
3325
- await sql2.unsafe(`
3326
- CREATE INDEX IF NOT EXISTS "_user_tables_tenant_id_idx" ON "_user_tables" ("tenant_id")
3327
- `);
3261
+ const tenants = pgTable("_tenants", {
3262
+ id: text("id").primaryKey().default(sql`gen_random_uuid()`),
3263
+ name: text("name").notNull(),
3264
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3265
+ });
3266
+ await tenants.create(sql2);
3267
+ const members = pgTable("_tenant_members", {
3268
+ id: serial("id").primaryKey(),
3269
+ tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
3270
+ user_id: integer("user_id").notNull(),
3271
+ role: text("role").notNull().default("member"),
3272
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3273
+ });
3274
+ await members.create(sql2);
3275
+ await members.createIndex(sql2, "user_id");
3276
+ await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_tenant_members_unique_idx" ON "_tenant_members" ("tenant_id", "user_id")`);
3277
+ const tables = pgTable("_user_tables", {
3278
+ id: serial("id").primaryKey(),
3279
+ tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
3280
+ slug: text("slug").notNull(),
3281
+ label: text("label").notNull().default(""),
3282
+ fields: jsonb("fields").notNull().default(sql`'[]'::jsonb`),
3283
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3284
+ });
3285
+ await tables.create(sql2);
3286
+ await tables.createIndex(sql2, "tenant_id");
3287
+ await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_user_tables_unique_idx" ON "_user_tables" ("tenant_id", "slug")`);
3328
3288
  }
3329
3289
 
3330
3290
  // tenant/rest.ts
3331
- import { z as z2 } from "zod";
3291
+ import { z as z3 } from "zod";
3332
3292
 
3333
3293
  // tenant/utils.ts
3334
3294
  function internalTableName(tenantId, slug) {
@@ -3489,25 +3449,25 @@ function zodType(field) {
3489
3449
  let t;
3490
3450
  switch (field.type) {
3491
3451
  case "integer":
3492
- t = z2.number().int();
3452
+ t = z3.number().int();
3493
3453
  break;
3494
3454
  case "float":
3495
- t = z2.number();
3455
+ t = z3.number();
3496
3456
  break;
3497
3457
  case "boolean":
3498
- t = z2.boolean();
3458
+ t = z3.boolean();
3499
3459
  break;
3500
3460
  case "enum":
3501
- t = field.options && field.options.length > 0 ? z2.enum(field.options) : z2.string();
3461
+ t = field.options && field.options.length > 0 ? z3.enum(field.options) : z3.string();
3502
3462
  break;
3503
3463
  case "json":
3504
- t = z2.record(z2.string(), z2.unknown());
3464
+ t = z3.record(z3.string(), z3.unknown());
3505
3465
  break;
3506
3466
  case "vector":
3507
- t = z2.array(z2.number());
3467
+ t = z3.array(z3.number());
3508
3468
  break;
3509
3469
  default:
3510
- t = z2.string();
3470
+ t = z3.string();
3511
3471
  }
3512
3472
  if (!field.required) {
3513
3473
  if (field.default !== void 0) {
@@ -3714,7 +3674,7 @@ function buildRouter(sql2, usersTable) {
3714
3674
  for (const f of table.fields) {
3715
3675
  shape[f.name] = zodType(f);
3716
3676
  }
3717
- const zodSchema = z2.object(shape);
3677
+ const zodSchema = z3.object(shape);
3718
3678
  const parsed = zodSchema.parse(data);
3719
3679
  parsed.tenant_id = ctx.tenant.id;
3720
3680
  delete parsed.id;
@@ -3742,7 +3702,7 @@ function buildRouter(sql2, usersTable) {
3742
3702
  for (const f of table.fields) {
3743
3703
  shape[f.name] = zodType(f);
3744
3704
  }
3745
- const zodSchema = z2.object(shape).partial();
3705
+ const zodSchema = z3.object(shape).partial();
3746
3706
  const parsed = zodSchema.parse(data);
3747
3707
  delete parsed.id;
3748
3708
  delete parsed.tenant_id;
@@ -3832,7 +3792,7 @@ function buildRouter(sql2, usersTable) {
3832
3792
  for (const f of childTable.fields) {
3833
3793
  shape[f.name] = zodType(f);
3834
3794
  }
3835
- const zodSchema = z2.object(shape);
3795
+ const zodSchema = z3.object(shape);
3836
3796
  const parsed = zodSchema.parse(body);
3837
3797
  parsed.tenant_id = ctx.tenant.id;
3838
3798
  parsed[relField.name] = parentId;
@@ -4209,38 +4169,32 @@ import { createOpenAI } from "@ai-sdk/openai";
4209
4169
  // agent/migrate.ts
4210
4170
  async function migrate3(opts) {
4211
4171
  const { sql: sql2, embeddingDimension } = opts;
4212
- await sql2.unsafe(`
4213
- CREATE TABLE IF NOT EXISTS "_agents" (
4214
- "id" SERIAL PRIMARY KEY,
4215
- "tenant_id" TEXT,
4216
- "name" TEXT NOT NULL,
4217
- "description" TEXT NOT NULL DEFAULT '',
4218
- "type" TEXT NOT NULL DEFAULT 'chat',
4219
- "model" TEXT NOT NULL DEFAULT '',
4220
- "system_prompt" TEXT NOT NULL DEFAULT '',
4221
- "owner_id" INTEGER NOT NULL,
4222
- "active" BOOLEAN NOT NULL DEFAULT true,
4223
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
4224
- "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
4225
- )
4226
- `);
4227
- await sql2.unsafe(`
4228
- CREATE INDEX IF NOT EXISTS "_agents_tenant_id_idx" ON "_agents" ("tenant_id")
4229
- `);
4230
- await sql2.unsafe(`
4231
- CREATE TABLE IF NOT EXISTS "_knowledge_documents" (
4232
- "id" SERIAL PRIMARY KEY,
4233
- "agent_id" INTEGER NOT NULL REFERENCES "_agents"("id") ON DELETE CASCADE,
4234
- "title" TEXT NOT NULL DEFAULT '',
4235
- "content" TEXT NOT NULL,
4236
- "embedding" vector(${embeddingDimension}),
4237
- "metadata" JSONB NOT NULL DEFAULT '{}',
4238
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
4239
- )
4240
- `);
4241
- await sql2.unsafe(`
4242
- CREATE INDEX IF NOT EXISTS "_knowledge_documents_agent_id_idx" ON "_knowledge_documents" ("agent_id")
4243
- `);
4172
+ const agents = pgTable("_agents", {
4173
+ id: serial("id").primaryKey(),
4174
+ tenant_id: text("tenant_id"),
4175
+ name: text("name").notNull(),
4176
+ description: text("description").notNull().default(""),
4177
+ type: text("type").notNull().default("chat"),
4178
+ model: text("model").notNull().default(""),
4179
+ system_prompt: text("system_prompt").notNull().default(""),
4180
+ owner_id: integer("owner_id").notNull(),
4181
+ active: boolean_("active").notNull().default(true),
4182
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`),
4183
+ updated_at: timestamptz("updated_at").notNull().default(sql`NOW()`)
4184
+ });
4185
+ await agents.create(sql2);
4186
+ await agents.createIndex(sql2, "tenant_id");
4187
+ const docs = pgTable("_knowledge_documents", {
4188
+ id: serial("id").primaryKey(),
4189
+ agent_id: integer("agent_id").notNull().references("_agents", "id", "cascade"),
4190
+ title: text("title").notNull().default(""),
4191
+ content: text("content").notNull(),
4192
+ embedding: vector("embedding", embeddingDimension),
4193
+ metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
4194
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4195
+ });
4196
+ await docs.create(sql2);
4197
+ await docs.createIndex(sql2, "agent_id");
4244
4198
  }
4245
4199
 
4246
4200
  // agent/rest.ts
@@ -4350,7 +4304,54 @@ function buildRouter2(deps) {
4350
4304
 
4351
4305
  // agent/run.ts
4352
4306
  import { streamText, generateText as generateText2, embed } from "ai";
4353
- import { z as z3 } from "zod";
4307
+ import { z as z4 } from "zod";
4308
+
4309
+ // sse.ts
4310
+ var encoder = new TextEncoder();
4311
+ function formatSSE(event, data) {
4312
+ return `event: ${event}
4313
+ data: ${JSON.stringify(data)}
4314
+
4315
+ `;
4316
+ }
4317
+ function formatSSEData(data) {
4318
+ return `data: ${JSON.stringify(data)}
4319
+
4320
+ `;
4321
+ }
4322
+ function createSSEStream(iterable, opts) {
4323
+ return new Response(
4324
+ new ReadableStream({
4325
+ async start(controller) {
4326
+ try {
4327
+ for await (const event of iterable) {
4328
+ const text2 = event.type ? formatSSE(event.type, event) : formatSSEData(event);
4329
+ controller.enqueue(encoder.encode(text2));
4330
+ }
4331
+ } catch (e) {
4332
+ if (e.name !== "AbortError") {
4333
+ controller.enqueue(
4334
+ encoder.encode(formatSSE("error", { error: e.message }))
4335
+ );
4336
+ }
4337
+ } finally {
4338
+ controller.close();
4339
+ }
4340
+ }
4341
+ }),
4342
+ {
4343
+ status: opts?.status ?? 200,
4344
+ headers: {
4345
+ "Content-Type": "text/event-stream",
4346
+ "Cache-Control": "no-cache",
4347
+ Connection: "keep-alive",
4348
+ ...opts?.headers
4349
+ }
4350
+ }
4351
+ );
4352
+ }
4353
+
4354
+ // agent/run.ts
4354
4355
  function chunkContent(content, chunkSize = 512, overlap = 64) {
4355
4356
  const paragraphs = content.split(/\n\n+/);
4356
4357
  const chunks = [];
@@ -4390,26 +4391,26 @@ function createRunner(deps) {
4390
4391
  const embedModel = getEmbeddingModel();
4391
4392
  const start = Date.now();
4392
4393
  const hasKB = await hasKnowledgeDocs(sql2, agentId);
4393
- const messages = params.messages ?? [];
4394
- if (messages.length === 0) {
4395
- messages.push({ role: "user", content: params.input });
4394
+ const messages2 = params.messages ?? [];
4395
+ if (messages2.length === 0) {
4396
+ messages2.push({ role: "user", content: params.input });
4396
4397
  }
4397
4398
  const tools = {};
4398
4399
  if (hasKB) {
4399
4400
  tools.searchKnowledge = {
4400
4401
  description: "\u641C\u7D22\u77E5\u8BC6\u5E93\u6587\u6863\u83B7\u53D6\u4E0E\u67E5\u8BE2\u76F8\u5173\u7684\u4FE1\u606F\u3002\u56DE\u7B54\u7528\u6237\u95EE\u9898\u524D\u5FC5\u987B\u5148\u641C\u7D22\u77E5\u8BC6\u5E93\u3002\u5982\u679C\u641C\u7D22\u7ED3\u679C\u4E0D\u8DB3\u4EE5\u56DE\u7B54\uFF0C\u544A\u8BC9\u7528\u6237\u4F60\u4E0D\u77E5\u9053\u3002",
4401
- parameters: z3.object({
4402
- query: z3.string().describe("\u641C\u7D22\u5173\u952E\u8BCD\uFF0C\u5E94\u8BE5\u7528\u4E2D\u6587\u63D0\u95EE"),
4403
- limit: z3.number().default(5).describe("\u8FD4\u56DE\u7ED3\u679C\u6570\u91CF")
4402
+ parameters: z4.object({
4403
+ query: z4.string().describe("\u641C\u7D22\u5173\u952E\u8BCD\uFF0C\u5E94\u8BE5\u7528\u4E2D\u6587\u63D0\u95EE"),
4404
+ limit: z4.number().default(5).describe("\u8FD4\u56DE\u7ED3\u679C\u6570\u91CF")
4404
4405
  }),
4405
4406
  execute: async ({ query, limit }) => {
4406
4407
  return searchKnowledge(sql2, embedModel, agentId, query, limit);
4407
4408
  }
4408
4409
  };
4409
4410
  }
4410
- if (agent2.type === "workflow" && userTools) {
4411
- for (const [key, tool12] of Object.entries(userTools)) {
4412
- tools[key] = tool12;
4411
+ if (agent2.type === "tool-use" && userTools) {
4412
+ for (const [key, tool11] of Object.entries(userTools)) {
4413
+ tools[key] = tool11;
4413
4414
  }
4414
4415
  }
4415
4416
  const system = agent2.system_prompt || void 0;
@@ -4417,7 +4418,7 @@ function createRunner(deps) {
4417
4418
  const result = streamText({
4418
4419
  model,
4419
4420
  system,
4420
- messages,
4421
+ messages: messages2,
4421
4422
  tools: Object.keys(tools).length > 0 ? tools : void 0
4422
4423
  });
4423
4424
  const fullStream = result.fullStream;
@@ -4440,7 +4441,7 @@ function createRunner(deps) {
4440
4441
  const result = await generateText2({
4441
4442
  model,
4442
4443
  system,
4443
- messages,
4444
+ messages: messages2,
4444
4445
  tools: Object.keys(tools).length > 0 ? tools : void 0
4445
4446
  });
4446
4447
  return { output: result.text, elapsed: Date.now() - start };
@@ -4511,52 +4512,43 @@ function agent(options) {
4511
4512
 
4512
4513
  // messager/migrate.ts
4513
4514
  async function migrate4(sql2) {
4514
- await sql2.unsafe(`
4515
- CREATE TABLE IF NOT EXISTS "_channels" (
4516
- "id" SERIAL PRIMARY KEY,
4517
- "tenant_id" TEXT,
4518
- "name" TEXT NOT NULL DEFAULT '',
4519
- "type" TEXT NOT NULL DEFAULT 'channel',
4520
- "created_by" INTEGER NOT NULL,
4521
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
4522
- )
4523
- `);
4524
- await sql2.unsafe(`
4525
- CREATE INDEX IF NOT EXISTS "_channels_tenant_id_idx" ON "_channels" ("tenant_id")
4526
- `);
4527
- await sql2.unsafe(`
4528
- CREATE TABLE IF NOT EXISTS "_channel_members" (
4529
- "id" SERIAL PRIMARY KEY,
4530
- "channel_id" INTEGER NOT NULL REFERENCES "_channels"("id") ON DELETE CASCADE,
4531
- "member_id" INTEGER NOT NULL,
4532
- "member_type" TEXT NOT NULL DEFAULT 'user',
4533
- "role" TEXT NOT NULL DEFAULT 'member',
4534
- "last_read_id" INTEGER DEFAULT NULL,
4535
- "last_read_at" TIMESTAMPTZ DEFAULT NULL,
4536
- UNIQUE("channel_id", "member_id", "member_type")
4537
- )
4538
- `);
4539
- await sql2.unsafe(`
4540
- CREATE INDEX IF NOT EXISTS "_channel_members_user_idx" ON "_channel_members" ("member_id")
4541
- `);
4542
- await sql2.unsafe(`
4543
- CREATE TABLE IF NOT EXISTS "_messages" (
4544
- "id" SERIAL PRIMARY KEY,
4545
- "channel_id" INTEGER NOT NULL REFERENCES "_channels"("id") ON DELETE CASCADE,
4546
- "sender_id" INTEGER NOT NULL,
4547
- "sender_type" TEXT NOT NULL DEFAULT 'user',
4548
- "type" TEXT NOT NULL DEFAULT 'text',
4549
- "content" TEXT NOT NULL DEFAULT '',
4550
- "file_url" TEXT DEFAULT NULL,
4551
- "file_name" TEXT DEFAULT NULL,
4552
- "file_size" INTEGER DEFAULT NULL,
4553
- "mime_type" TEXT DEFAULT NULL,
4554
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
4555
- )
4556
- `);
4557
- await sql2.unsafe(`
4558
- CREATE INDEX IF NOT EXISTS "_messages_channel_created_idx" ON "_messages" ("channel_id", "created_at" DESC)
4559
- `);
4515
+ const channels2 = pgTable("_channels", {
4516
+ id: serial("id").primaryKey(),
4517
+ tenant_id: text("tenant_id"),
4518
+ name: text("name").notNull().default(""),
4519
+ type: text("type").notNull().default("channel"),
4520
+ created_by: integer("created_by").notNull(),
4521
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4522
+ });
4523
+ await channels2.create(sql2);
4524
+ await channels2.createIndex(sql2, "tenant_id");
4525
+ const members = pgTable("_channel_members", {
4526
+ id: serial("id").primaryKey(),
4527
+ channel_id: integer("channel_id").notNull().references("_channels", "id", "cascade"),
4528
+ member_id: integer("member_id").notNull(),
4529
+ member_type: text("member_type").notNull().default("user"),
4530
+ role: text("role").notNull().default("member"),
4531
+ last_read_id: integer("last_read_id"),
4532
+ last_read_at: timestamptz("last_read_at")
4533
+ });
4534
+ await members.create(sql2);
4535
+ await members.createIndex(sql2, "member_id");
4536
+ await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_channel_members_unique_idx" ON "_channel_members" ("channel_id", "member_id", "member_type")`);
4537
+ const messages2 = pgTable("_messages", {
4538
+ id: serial("id").primaryKey(),
4539
+ channel_id: integer("channel_id").notNull().references("_channels", "id", "cascade"),
4540
+ sender_id: integer("sender_id").notNull(),
4541
+ sender_type: text("sender_type").notNull().default("user"),
4542
+ type: text("type").notNull().default("text"),
4543
+ content: text("content").notNull().default(""),
4544
+ file_url: text("file_url"),
4545
+ file_name: text("file_name"),
4546
+ file_size: integer("file_size"),
4547
+ mime_type: text("mime_type"),
4548
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4549
+ });
4550
+ await messages2.create(sql2);
4551
+ await messages2.createIndex(sql2, ["channel_id", "created_at"], { desc: true });
4560
4552
  }
4561
4553
 
4562
4554
  // messager/ws.ts
@@ -4950,7 +4942,7 @@ function createGateway(config, getPort) {
4950
4942
  }
4951
4943
 
4952
4944
  // deploy/manager.ts
4953
- import crypto4 from "node:crypto";
4945
+ import crypto3 from "node:crypto";
4954
4946
  function createManager(config, apps, manager) {
4955
4947
  const router = new Router();
4956
4948
  const auth2 = (req, ctx, next) => {
@@ -4959,7 +4951,7 @@ function createManager(config, apps, manager) {
4959
4951
  const token = header.replace("Bearer ", "");
4960
4952
  const tokenBuf = Buffer.from(token);
4961
4953
  const secretBuf = Buffer.from(config.deployToken);
4962
- if (tokenBuf.length !== secretBuf.length || !crypto4.timingSafeEqual(tokenBuf, secretBuf)) {
4954
+ if (tokenBuf.length !== secretBuf.length || !crypto3.timingSafeEqual(tokenBuf, secretBuf)) {
4963
4955
  return Response.json({ error: "Unauthorized" }, { status: 401 });
4964
4956
  }
4965
4957
  return next(req, ctx);
@@ -5058,10 +5050,10 @@ function createManager(config, apps, manager) {
5058
5050
  const rawBody = await req.text();
5059
5051
  if (config.webhookSecret) {
5060
5052
  const sig = req.headers.get("x-hub-signature-256") ?? "";
5061
- const expected = `sha256=${crypto4.createHmac("sha256", config.webhookSecret).update(rawBody).digest("hex")}`;
5053
+ const expected = `sha256=${crypto3.createHmac("sha256", config.webhookSecret).update(rawBody).digest("hex")}`;
5062
5054
  const sigBuf = Buffer.from(sig);
5063
5055
  const expectedBuf = Buffer.from(expected);
5064
- if (sigBuf.length !== expectedBuf.length || !crypto4.timingSafeEqual(sigBuf, expectedBuf)) {
5056
+ if (sigBuf.length !== expectedBuf.length || !crypto3.timingSafeEqual(sigBuf, expectedBuf)) {
5065
5057
  return Response.json({ error: "invalid signature" }, { status: 401 });
5066
5058
  }
5067
5059
  }
@@ -5114,14 +5106,14 @@ function forkApp(opts) {
5114
5106
  return { child, port: opts.port };
5115
5107
  }
5116
5108
  function stopProcess(mp, timeout = 1e4) {
5117
- return new Promise((resolve9) => {
5109
+ return new Promise((resolve10) => {
5118
5110
  const timer = setTimeout(() => {
5119
5111
  mp.child.kill("SIGKILL");
5120
- resolve9();
5112
+ resolve10();
5121
5113
  }, timeout);
5122
5114
  mp.child.on("exit", () => {
5123
5115
  clearTimeout(timer);
5124
- resolve9();
5116
+ resolve10();
5125
5117
  });
5126
5118
  mp.child.kill("SIGTERM");
5127
5119
  });
@@ -5340,7 +5332,7 @@ async function deploy(config) {
5340
5332
  });
5341
5333
  const portSuffix = config.port !== 80 ? `:${config.port}` : "";
5342
5334
  return {
5343
- stop: async () => {
5335
+ close: async () => {
5344
5336
  for (const [, app] of apps) {
5345
5337
  if (app.restartTimer) clearTimeout(app.restartTimer);
5346
5338
  if (app.process) {
@@ -5413,47 +5405,63 @@ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
5413
5405
 
5414
5406
  // opencode/migrate.ts
5415
5407
  async function migrate5(sql2) {
5416
- await sql2.unsafe(`
5417
- CREATE TABLE IF NOT EXISTS "_opencode_sessions" (
5418
- "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
5419
- "tenant_id" TEXT,
5420
- "user_id" INTEGER NOT NULL DEFAULT 0,
5421
- "title" TEXT,
5422
- "agent_type" TEXT NOT NULL DEFAULT 'build',
5423
- "model" TEXT NOT NULL DEFAULT 'deepseek-v4-flash',
5424
- "system_prompt" TEXT,
5425
- "workspace" TEXT,
5426
- "metadata" JSONB NOT NULL DEFAULT '{}',
5427
- "active" BOOLEAN NOT NULL DEFAULT true,
5428
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5429
- "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
5430
- )
5431
- `);
5432
- await sql2.unsafe(`
5433
- CREATE INDEX IF NOT EXISTS "_opencode_sessions_user_idx" ON "_opencode_sessions" ("user_id")
5434
- `);
5435
- await sql2.unsafe(`
5436
- CREATE TABLE IF NOT EXISTS "_opencode_messages" (
5437
- "id" SERIAL PRIMARY KEY,
5438
- "session_id" UUID NOT NULL REFERENCES "_opencode_sessions"("id") ON DELETE CASCADE,
5439
- "role" TEXT NOT NULL,
5440
- "content" TEXT,
5441
- "tool_calls" JSONB,
5442
- "tool_results" JSONB,
5443
- "tokens_in" INTEGER NOT NULL DEFAULT 0,
5444
- "tokens_out" INTEGER NOT NULL DEFAULT 0,
5445
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
5446
- )
5447
- `);
5448
- await sql2.unsafe(`
5449
- CREATE INDEX IF NOT EXISTS "_opencode_messages_session_idx" ON "_opencode_messages" ("session_id", "created_at")
5450
- `);
5408
+ const sessions2 = pgTable("_opencode_sessions", {
5409
+ id: uuid("id").default(sql`gen_random_uuid()`).primaryKey(),
5410
+ tenant_id: text("tenant_id"),
5411
+ user_id: integer("user_id").default(0),
5412
+ title: text("title"),
5413
+ agent_type: text("agent_type").default("build"),
5414
+ model: text("model").default("deepseek-v4-flash"),
5415
+ system_prompt: text("system_prompt"),
5416
+ workspace: text("workspace"),
5417
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
5418
+ active: boolean_("active").default(true),
5419
+ created_at: timestamptz("created_at").default(sql`NOW()`),
5420
+ updated_at: timestamptz("updated_at").default(sql`NOW()`)
5421
+ });
5422
+ await sessions2.create(sql2);
5423
+ await sessions2.createIndex(sql2, "user_id");
5424
+ const messages2 = pgTable("_opencode_messages", {
5425
+ id: serial("id").primaryKey(),
5426
+ session_id: uuid("session_id").notNull().references("_opencode_sessions", "id", "cascade"),
5427
+ role: text("role").notNull(),
5428
+ content: text("content"),
5429
+ tool_calls: jsonb("tool_calls"),
5430
+ tool_results: jsonb("tool_results"),
5431
+ tokens_in: integer("tokens_in").default(0),
5432
+ tokens_out: integer("tokens_out").default(0),
5433
+ created_at: timestamptz("created_at").default(sql`NOW()`)
5434
+ });
5435
+ await messages2.create(sql2);
5436
+ await messages2.createIndex(sql2, ["session_id", "created_at"]);
5451
5437
  }
5452
5438
 
5453
5439
  // opencode/session.ts
5454
5440
  import { randomUUID as randomUUID2 } from "node:crypto";
5455
5441
  import { join as join3 } from "node:path";
5456
5442
  import { mkdir as mkdir2 } from "node:fs/promises";
5443
+ var sessions = pgTable("_opencode_sessions", {
5444
+ id: uuid("id"),
5445
+ user_id: integer("user_id"),
5446
+ title: text("title"),
5447
+ model: text("model"),
5448
+ workspace: text("workspace"),
5449
+ system_prompt: text("system_prompt"),
5450
+ active: boolean_("active"),
5451
+ updated_at: timestamptz("updated_at"),
5452
+ created_at: timestamptz("created_at")
5453
+ });
5454
+ var messages = pgTable("_opencode_messages", {
5455
+ id: serial("id"),
5456
+ session_id: uuid("session_id"),
5457
+ role: text("role"),
5458
+ content: text("content"),
5459
+ tool_calls: jsonb("tool_calls"),
5460
+ tool_results: jsonb("tool_results"),
5461
+ tokens_in: integer("tokens_in"),
5462
+ tokens_out: integer("tokens_out"),
5463
+ created_at: timestamptz("created_at")
5464
+ });
5457
5465
  async function createSession(sql2, opts, cwd, mountPath) {
5458
5466
  const id2 = randomUUID2();
5459
5467
  const ws = computeSessionWorkspace(cwd, mountPath, id2);
@@ -5470,27 +5478,26 @@ function computeSessionWorkspace(cwd, mountPath, sessionId) {
5470
5478
  return join3(cwd, ".sessions", name, sessionId);
5471
5479
  }
5472
5480
  async function getSession(sql2, id2) {
5473
- const [row] = await sql2`SELECT * FROM "_opencode_sessions" WHERE id = ${id2} AND active = true LIMIT 1`;
5474
- return row ?? null;
5481
+ const rows = await sessions.find(sql2, { id: id2, active: true });
5482
+ return rows[0] ?? null;
5475
5483
  }
5476
5484
  async function listSessions(sql2, userId) {
5485
+ const opts = { orderBy: { updated_at: "desc" } };
5477
5486
  if (userId !== void 0) {
5478
- const rows2 = await sql2`SELECT * FROM "_opencode_sessions" WHERE user_id = ${userId} AND active = true ORDER BY updated_at DESC`;
5487
+ const rows2 = await sessions.find(sql2, { user_id: userId, active: true }, opts);
5479
5488
  return rows2;
5480
5489
  }
5481
- const rows = await sql2`SELECT * FROM "_opencode_sessions" WHERE active = true ORDER BY updated_at DESC`;
5490
+ const rows = await sessions.find(sql2, { active: true }, opts);
5482
5491
  return rows;
5483
5492
  }
5484
5493
  async function deleteSession(sql2, id2) {
5485
- await sql2`UPDATE "_opencode_sessions" SET active = false, updated_at = NOW() WHERE id = ${id2}`;
5494
+ await sessions.update(sql2, { id: id2 }, { active: false, updated_at: sql`NOW()` });
5486
5495
  }
5487
5496
  async function getHistory(sql2, sessionId, limit = 50) {
5488
- const rows = await sql2`
5489
- SELECT * FROM "_opencode_messages"
5490
- WHERE session_id = ${sessionId}
5491
- ORDER BY created_at ASC
5492
- LIMIT ${limit}
5493
- `;
5497
+ const rows = await messages.find(sql2, { session_id: sessionId }, {
5498
+ orderBy: { created_at: "asc" },
5499
+ limit
5500
+ });
5494
5501
  return rows;
5495
5502
  }
5496
5503
  async function addTextMessage(sql2, sessionId, role, content, tokensIn = 0, tokensOut = 0) {
@@ -5513,7 +5520,7 @@ async function addToolMessages(sql2, sessionId, toolCalls, toolResults) {
5513
5520
  // opencode/run.ts
5514
5521
  import { streamText as streamText2, stepCountIs } from "ai";
5515
5522
  async function* executeGenerator(opts) {
5516
- const { sessionId, input, model, tools, systemPrompt, messages, sql: sql2, abortSignal } = opts;
5523
+ const { sessionId, input, model, tools, systemPrompt, messages: messages2, sql: sql2, abortSignal } = opts;
5517
5524
  const lastStepToolCalls = [];
5518
5525
  let currentAssistantText = "";
5519
5526
  let currentUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
@@ -5521,7 +5528,7 @@ async function* executeGenerator(opts) {
5521
5528
  model,
5522
5529
  system: systemPrompt,
5523
5530
  messages: [
5524
- ...messages.map((m) => {
5531
+ ...messages2.map((m) => {
5525
5532
  if (m.role === "user") return { role: "user", content: m.content ?? "" };
5526
5533
  if (m.role === "assistant") return { role: "assistant", content: m.content ?? "" };
5527
5534
  return { role: "user", content: "" };
@@ -5649,26 +5656,26 @@ function isSkillAllowed(name, perms) {
5649
5656
  }
5650
5657
 
5651
5658
  // opencode/tools/bash.ts
5652
- import { tool as tool3 } from "ai";
5653
- import { z as z4 } from "zod";
5659
+ import { tool as tool2 } from "ai";
5660
+ import { z as z5 } from "zod";
5654
5661
  import { exec } from "node:child_process";
5655
5662
  function createBashTool(ctx) {
5656
- return tool3({
5663
+ return tool2({
5657
5664
  description: "Execute a shell command in the workspace directory. Returns stdout, stderr, and exit code.",
5658
- inputSchema: z4.object({
5659
- command: z4.string().describe("The shell command to execute"),
5660
- timeout: z4.number().default(30).describe("Timeout in seconds"),
5661
- workdir: z4.string().optional().describe("Subdirectory relative to workspace")
5665
+ inputSchema: z5.object({
5666
+ command: z5.string().describe("The shell command to execute"),
5667
+ timeout: z5.number().default(30).describe("Timeout in seconds"),
5668
+ workdir: z5.string().optional().describe("Subdirectory relative to workspace")
5662
5669
  }),
5663
5670
  execute: async ({ command, timeout, workdir }) => {
5664
5671
  if (!isCommandAllowed(command)) {
5665
5672
  return { stdout: "", stderr: "Command denied: potentially dangerous command", exitCode: 1 };
5666
5673
  }
5667
5674
  const cwd = workdir ? `${ctx.workspace}/${workdir}` : ctx.workspace;
5668
- return new Promise((resolve9) => {
5675
+ return new Promise((resolve10) => {
5669
5676
  const child = exec(command, { cwd, timeout: timeout * 1e3, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
5670
5677
  const truncated = stdout.length > 1e6 || stderr.length > 1e6;
5671
- resolve9({
5678
+ resolve10({
5672
5679
  stdout: stdout.slice(0, 1e6),
5673
5680
  stderr: stderr.slice(0, 1e6),
5674
5681
  exitCode: error?.code ?? 0,
@@ -5682,17 +5689,17 @@ function createBashTool(ctx) {
5682
5689
  }
5683
5690
 
5684
5691
  // opencode/tools/read.ts
5685
- import { tool as tool4 } from "ai";
5686
- import { z as z5 } from "zod";
5692
+ import { tool as tool3 } from "ai";
5693
+ import { z as z6 } from "zod";
5687
5694
  import { readFileSync as readFileSync2 } from "node:fs";
5688
5695
  import { resolve as resolve3 } from "node:path";
5689
5696
  function createReadTool(ctx) {
5690
- return tool4({
5697
+ return tool3({
5691
5698
  description: "Read file contents. Supports offset and limit for reading specific line ranges.",
5692
- inputSchema: z5.object({
5693
- path: z5.string().describe("File path relative to workspace"),
5694
- offset: z5.number().optional().describe("Starting line number (1-indexed)"),
5695
- limit: z5.number().optional().describe("Number of lines to read")
5699
+ inputSchema: z6.object({
5700
+ path: z6.string().describe("File path relative to workspace"),
5701
+ offset: z6.number().optional().describe("Starting line number (1-indexed)"),
5702
+ limit: z6.number().optional().describe("Number of lines to read")
5696
5703
  }),
5697
5704
  execute: async ({ path: path2, offset, limit }) => {
5698
5705
  const resolved = resolve3(ctx.workspace, path2);
@@ -5724,16 +5731,16 @@ function createReadTool(ctx) {
5724
5731
  }
5725
5732
 
5726
5733
  // opencode/tools/write.ts
5727
- import { tool as tool5 } from "ai";
5728
- import { z as z6 } from "zod";
5734
+ import { tool as tool4 } from "ai";
5735
+ import { z as z7 } from "zod";
5729
5736
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
5730
5737
  import { resolve as resolve4, dirname as dirname2 } from "node:path";
5731
5738
  function createWriteTool(ctx) {
5732
- return tool5({
5739
+ return tool4({
5733
5740
  description: "Create or overwrite a file. Parent directories are created automatically.",
5734
- inputSchema: z6.object({
5735
- path: z6.string().describe("File path relative to workspace"),
5736
- content: z6.string().describe("File content")
5741
+ inputSchema: z7.object({
5742
+ path: z7.string().describe("File path relative to workspace"),
5743
+ content: z7.string().describe("File content")
5737
5744
  }),
5738
5745
  execute: async ({ path: path2, content }) => {
5739
5746
  const resolved = resolve4(ctx.workspace, path2);
@@ -5748,18 +5755,18 @@ function createWriteTool(ctx) {
5748
5755
  }
5749
5756
 
5750
5757
  // opencode/tools/edit.ts
5751
- import { tool as tool6 } from "ai";
5752
- import { z as z7 } from "zod";
5758
+ import { tool as tool5 } from "ai";
5759
+ import { z as z8 } from "zod";
5753
5760
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
5754
5761
  import { resolve as resolve5 } from "node:path";
5755
5762
  function createEditTool(ctx) {
5756
- return tool6({
5763
+ return tool5({
5757
5764
  description: "Perform exact string replacements in a file. If oldString appears multiple times, provide more surrounding context.",
5758
- inputSchema: z7.object({
5759
- path: z7.string().describe("File path relative to workspace"),
5760
- oldString: z7.string().describe("The exact text to replace"),
5761
- newString: z7.string().describe("The replacement text"),
5762
- replaceAll: z7.boolean().default(false).describe("Replace all occurrences")
5765
+ inputSchema: z8.object({
5766
+ path: z8.string().describe("File path relative to workspace"),
5767
+ oldString: z8.string().describe("The exact text to replace"),
5768
+ newString: z8.string().describe("The replacement text"),
5769
+ replaceAll: z8.boolean().default(false).describe("Replace all occurrences")
5763
5770
  }),
5764
5771
  execute: async ({ path: path2, oldString, newString, replaceAll }) => {
5765
5772
  const resolved = resolve5(ctx.workspace, path2);
@@ -5792,19 +5799,19 @@ function createEditTool(ctx) {
5792
5799
  }
5793
5800
 
5794
5801
  // opencode/tools/grep.ts
5795
- import { tool as tool7 } from "ai";
5796
- import { z as z8 } from "zod";
5802
+ import { tool as tool6 } from "ai";
5803
+ import { z as z9 } from "zod";
5797
5804
  import { execSync as execSync2 } from "node:child_process";
5798
5805
  import { resolve as resolve6 } from "node:path";
5799
5806
  import { existsSync as existsSync2 } from "node:fs";
5800
5807
  function createGrepTool(ctx) {
5801
- return tool7({
5808
+ return tool6({
5802
5809
  description: "Search file contents using regex. Supports file type filtering and context lines.",
5803
- inputSchema: z8.object({
5804
- pattern: z8.string().describe("The regex pattern to search for"),
5805
- include: z8.string().optional().describe('File glob filter (e.g. "*.ts")'),
5806
- path: z8.string().optional().describe("Subdirectory relative to workspace"),
5807
- context: z8.number().default(0).describe("Number of context lines before and after each match")
5810
+ inputSchema: z9.object({
5811
+ pattern: z9.string().describe("The regex pattern to search for"),
5812
+ include: z9.string().optional().describe('File glob filter (e.g. "*.ts")'),
5813
+ path: z9.string().optional().describe("Subdirectory relative to workspace"),
5814
+ context: z9.number().default(0).describe("Number of context lines before and after each match")
5808
5815
  }),
5809
5816
  execute: async ({ pattern, include, path: path2, context }) => {
5810
5817
  const searchDir = path2 ? resolve6(ctx.workspace, path2) : ctx.workspace;
@@ -5830,16 +5837,16 @@ function createGrepTool(ctx) {
5830
5837
  }
5831
5838
 
5832
5839
  // opencode/tools/glob.ts
5833
- import { tool as tool8 } from "ai";
5834
- import { z as z9 } from "zod";
5840
+ import { tool as tool7 } from "ai";
5841
+ import { z as z10 } from "zod";
5835
5842
  import { execSync as execSync3 } from "node:child_process";
5836
5843
  import { resolve as resolve7 } from "node:path";
5837
5844
  function createGlobTool(ctx) {
5838
- return tool8({
5845
+ return tool7({
5839
5846
  description: "Find files matching a glob pattern.",
5840
- inputSchema: z9.object({
5841
- pattern: z9.string().describe('Glob pattern (e.g. "src/**/*.ts")'),
5842
- path: z9.string().optional().describe("Subdirectory relative to workspace")
5847
+ inputSchema: z10.object({
5848
+ pattern: z10.string().describe('Glob pattern (e.g. "src/**/*.ts")'),
5849
+ path: z10.string().optional().describe("Subdirectory relative to workspace")
5843
5850
  }),
5844
5851
  execute: async ({ pattern, path: path2 }) => {
5845
5852
  const searchDir = path2 ? resolve7(ctx.workspace, path2) : ctx.workspace;
@@ -5858,13 +5865,13 @@ function createGlobTool(ctx) {
5858
5865
  }
5859
5866
 
5860
5867
  // opencode/tools/web.ts
5861
- import { tool as tool9 } from "ai";
5862
- import { z as z10 } from "zod";
5868
+ import { tool as tool8 } from "ai";
5869
+ import { z as z11 } from "zod";
5863
5870
  function createWebTool(ctx) {
5864
- return tool9({
5871
+ return tool8({
5865
5872
  description: "Fetch a URL and return the content as text.",
5866
- inputSchema: z10.object({
5867
- url: z10.string().describe("The URL to fetch")
5873
+ inputSchema: z11.object({
5874
+ url: z11.string().describe("The URL to fetch")
5868
5875
  }),
5869
5876
  execute: async ({ url }) => {
5870
5877
  try {
@@ -5886,17 +5893,17 @@ function createWebTool(ctx) {
5886
5893
  }
5887
5894
 
5888
5895
  // opencode/tools/question.ts
5889
- import { tool as tool10 } from "ai";
5890
- import { z as z11 } from "zod";
5896
+ import { tool as tool9 } from "ai";
5897
+ import { z as z12 } from "zod";
5891
5898
  function createQuestionTool(ctx) {
5892
- return tool10({
5899
+ return tool9({
5893
5900
  description: "Ask the user a question when you need more information to proceed.",
5894
- inputSchema: z11.object({
5895
- question: z11.string().describe("The question to ask the user"),
5896
- options: z11.array(z11.string()).optional().describe("Optional multiple choice options")
5901
+ inputSchema: z12.object({
5902
+ question: z12.string().describe("The question to ask the user"),
5903
+ options: z12.array(z12.string()).optional().describe("Optional multiple choice options")
5897
5904
  }),
5898
5905
  execute: async ({ question, options }, { toolCallId }) => {
5899
- return new Promise((resolve9, reject) => {
5906
+ return new Promise((resolve10, reject) => {
5900
5907
  const timeout = setTimeout(() => {
5901
5908
  ctx.pendingQuestions.delete(toolCallId);
5902
5909
  reject(new Error("Question timed out"));
@@ -5904,7 +5911,7 @@ function createQuestionTool(ctx) {
5904
5911
  ctx.pendingQuestions.set(toolCallId, {
5905
5912
  resolve: (answer) => {
5906
5913
  clearTimeout(timeout);
5907
- resolve9(answer);
5914
+ resolve10(answer);
5908
5915
  },
5909
5916
  reject: (err) => {
5910
5917
  clearTimeout(timeout);
@@ -5917,8 +5924,8 @@ function createQuestionTool(ctx) {
5917
5924
  }
5918
5925
 
5919
5926
  // opencode/tools/skill.ts
5920
- import { tool as tool11 } from "ai";
5921
- import { z as z12 } from "zod";
5927
+ import { tool as tool10 } from "ai";
5928
+ import { z as z13 } from "zod";
5922
5929
  function createSkillTool(ctx) {
5923
5930
  const skills = ctx.skillsRegistry.list();
5924
5931
  const availableList = skills.map(
@@ -5932,10 +5939,10 @@ function createSkillTool(ctx) {
5932
5939
  <available_skills>
5933
5940
  ${availableList}
5934
5941
  </available_skills>` : "No skills available.";
5935
- return tool11({
5942
+ return tool10({
5936
5943
  description,
5937
- inputSchema: z12.object({
5938
- name: z12.string().describe("The name of the skill to load")
5944
+ inputSchema: z13.object({
5945
+ name: z13.string().describe("The name of the skill to load")
5939
5946
  }),
5940
5947
  execute: async ({ name }) => {
5941
5948
  if (!isSkillAllowed(name, ctx.permissions)) {
@@ -6001,15 +6008,15 @@ async function buildRouter4(deps) {
6001
6008
  return Response.json(session, { status: 201 });
6002
6009
  });
6003
6010
  router.get("/sessions", async () => {
6004
- const sessions = await listSessions(sql2);
6005
- return Response.json(sessions);
6011
+ const sessions2 = await listSessions(sql2);
6012
+ return Response.json(sessions2);
6006
6013
  });
6007
6014
  router.get("/sessions/:id", async (_req, ctx) => {
6008
6015
  const id2 = ctx.params.id;
6009
6016
  const session = await getSession(sql2, id2);
6010
6017
  if (!session) return new Response("Not Found", { status: 404 });
6011
- const messages = await getHistory(sql2, id2);
6012
- return Response.json({ session, messages });
6018
+ const messages2 = await getHistory(sql2, id2);
6019
+ return Response.json({ session, messages: messages2 });
6013
6020
  });
6014
6021
  router.delete("/sessions/:id", async (_req, ctx) => {
6015
6022
  const id2 = ctx.params.id;
@@ -6289,38 +6296,138 @@ async function opencode(options) {
6289
6296
  close: () => base.close()
6290
6297
  };
6291
6298
  }
6299
+
6300
+ // health.ts
6301
+ function health(options) {
6302
+ const path2 = options?.path ?? "/health";
6303
+ const r = new Router();
6304
+ const handler = async () => {
6305
+ try {
6306
+ await options?.check?.();
6307
+ return new Response("OK", { status: 200 });
6308
+ } catch {
6309
+ return new Response("Service Unavailable", { status: 503 });
6310
+ }
6311
+ };
6312
+ r.get(path2, handler);
6313
+ r.head(path2, handler);
6314
+ return r;
6315
+ }
6316
+
6317
+ // i18n.ts
6318
+ import { readFile as readFile2 } from "node:fs/promises";
6319
+ import { existsSync as existsSync3 } from "node:fs";
6320
+ import { join as join4, resolve as resolve9 } from "node:path";
6321
+ function i18n(options) {
6322
+ const dir = resolve9(options.dir);
6323
+ const defaultLocale = options.defaultLocale ?? "en";
6324
+ const cache = /* @__PURE__ */ new Map();
6325
+ async function load(locale) {
6326
+ const cached = cache.get(locale);
6327
+ if (cached) return cached;
6328
+ const filePath = join4(dir, `${locale}.json`);
6329
+ if (!existsSync3(filePath)) return {};
6330
+ try {
6331
+ const content = await readFile2(filePath, "utf-8");
6332
+ const data = JSON.parse(content);
6333
+ cache.set(locale, data);
6334
+ return data;
6335
+ } catch {
6336
+ return {};
6337
+ }
6338
+ }
6339
+ function detect(req) {
6340
+ const url = new URL(req.url);
6341
+ const fromCookie = extractCookie(req, "locale");
6342
+ if (fromCookie) return fromCookie;
6343
+ const fromHeader = req.headers.get("Accept-Language")?.split(",")[0]?.split("-")[0];
6344
+ if (fromHeader) return fromHeader;
6345
+ return defaultLocale;
6346
+ }
6347
+ function t(localeMsgs, key, params) {
6348
+ let msg = localeMsgs[key];
6349
+ if (msg === void 0) msg = key;
6350
+ if (params) {
6351
+ for (const [k, v] of Object.entries(params)) {
6352
+ msg = msg.replace(`{${k}}`, v);
6353
+ }
6354
+ }
6355
+ return msg;
6356
+ }
6357
+ return async (req, ctx, next) => {
6358
+ const locale = detect(req);
6359
+ const msgs = await load(locale);
6360
+ ctx.locale = locale;
6361
+ ctx.t = (key, params) => t(msgs, key, params);
6362
+ return next(req, ctx);
6363
+ };
6364
+ }
6365
+ function extractCookie(req, name) {
6366
+ const cookie = req.headers.get("cookie");
6367
+ if (!cookie) return null;
6368
+ for (const part of cookie.split(";")) {
6369
+ const [k, v] = part.trim().split("=");
6370
+ if (k === name && v) return decodeURIComponent(v);
6371
+ }
6372
+ return null;
6373
+ }
6374
+
6375
+ // mailer.ts
6376
+ import { createTransport } from "nodemailer";
6377
+ function mailer(options) {
6378
+ const sender = options.send;
6379
+ const from = options.from;
6380
+ let transporter = null;
6381
+ if (!sender && options.transport) {
6382
+ transporter = typeof options.transport === "string" ? createTransport(options.transport) : options.transport;
6383
+ }
6384
+ async function send(opts) {
6385
+ if (sender) {
6386
+ await sender(opts);
6387
+ return;
6388
+ }
6389
+ if (!transporter) {
6390
+ throw new Error("mailer: no transport configured \u2014 provide `transport` or `send` option");
6391
+ }
6392
+ await transporter.sendMail({ ...opts, from: opts.from ?? from });
6393
+ }
6394
+ async function close() {
6395
+ if (transporter) transporter.close();
6396
+ }
6397
+ return { send, close };
6398
+ }
6292
6399
  export {
6293
6400
  Router,
6294
6401
  TsxContext,
6295
6402
  agent,
6296
- ai,
6403
+ aiStream,
6297
6404
  auth,
6298
6405
  compress,
6299
6406
  cors,
6300
- createSSEManager,
6301
- createWorkflowEngine,
6407
+ createTestServer,
6302
6408
  defineConfig,
6303
6409
  deleteCookie,
6304
6410
  deploy,
6305
- generateWorkflow,
6306
6411
  getCookies,
6307
6412
  graphql,
6413
+ health,
6414
+ i18n,
6308
6415
  logger,
6416
+ mailer,
6309
6417
  messager,
6310
6418
  opencode,
6311
6419
  postgres,
6312
6420
  queue,
6313
6421
  rateLimit,
6314
6422
  redis,
6423
+ runWorkflow,
6315
6424
  serve,
6316
6425
  serveStatic,
6317
6426
  setCookie,
6318
6427
  tenant,
6319
- tool,
6320
6428
  tsx,
6321
6429
  upload,
6322
6430
  useTsx,
6323
6431
  user,
6324
- validate,
6325
- workflow
6432
+ validate
6326
6433
  };