weifuwu 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ Everything follows the same `(req, ctx) => Response` contract. The Router handle
24
24
  - **PostgreSQL** — schema builder with type-safe DDL, CRUD (`read`/`readMany`, `insertMany`, `update`/`updateMany`, `delete`/`deleteMany`), WHERE helpers (`eq`, `gte`, `contains`, `and`, `or`), transactions, vector search
25
25
  - **Auth** — password + JWT + OAuth2 Server (authorization code / PKCE / client_credentials)
26
26
  - **Real-time** — WebSocket, messaging channels with agent routing
27
- - **AI** — streaming endpoint, DAG workflow tool, AI agents with RAG and tool-use
27
+ - **AI** — streaming endpoint, DAG workflow tool, AI agents with RAG and tool-use — re-exports `streamText`, `tool`, `openai` and more from AI SDK
28
28
  - **Data** — Redis client, job queue with cron scheduling
29
29
  - **Multi-tenant BaaS** — dynamic tables, auto REST + GraphQL, row-level isolation
30
30
  - **Deploy** — self-hosted PaaS: multi-app proxy, zero-downtime updates, auto SSL
@@ -49,8 +49,7 @@ serve((req, ctx) => new Response('Hello, World!'), { port: 3000 })
49
49
  ### Full app
50
50
 
51
51
  ```ts
52
- import { serve, Router, postgres, user, aiStream, graphql } from 'weifuwu'
53
- import { openai } from '@ai-sdk/openai'
52
+ import { serve, Router, postgres, user, aiStream, graphql, openai } from 'weifuwu'
54
53
 
55
54
  const app = new Router()
56
55
  const pg = postgres()
@@ -147,6 +146,11 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
147
146
  | `serial()`, `uuid()`, `text()`, ... | Column type builders |
148
147
  | `eq()`, `gte()`, `contains()`, `and()` ... | WHERE clause helpers — same API as Drizzle |
149
148
  | `PgModule` | Base class for DB-backed modules |
149
+ | `streamText()` / `generateText()` / `streamObject()` / `generateObject()` | AI SDK — text/structured generation |
150
+ | `tool()` | AI SDK — tool definition |
151
+ | `embed()` / `embedMany()` | AI SDK — text embeddings |
152
+ | `smoothStream()` | AI SDK — smooth streaming middleware |
153
+ | `openai` / `createOpenAI()` | OpenAI provider for AI SDK |
150
154
 
151
155
  ---
152
156
 
@@ -155,7 +159,7 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
155
159
  Optional module that organizes service logic as **Worker + Function + Trigger**, plus a pure WebSocket SDK for connecting remote workers. Built-in `stream::*` functions for hierarchical real-time data.
156
160
 
157
161
  ```ts
158
- import { serve, Router, iii, createWorker as Worker, registerWorker } from 'weifuwu'
162
+ import { serve, Router, iii, createWorker, registerWorker } from 'weifuwu'
159
163
 
160
164
  // Engine
161
165
  const engine = iii({ pg, redis })
@@ -164,7 +168,7 @@ app.use('/iii', engine.router())
164
168
  serve(app.handler(), { port: 3000, websocket: app.websocketHandler() })
165
169
 
166
170
  // Local worker
167
- const w = new Worker('orders')
171
+ const w = createWorker('orders')
168
172
  w.registerFunction('orders::create', async (payload) => {
169
173
  return db.query('INSERT INTO orders ...', [payload.items])
170
174
  })
@@ -1096,8 +1100,7 @@ export default function NotFound() {
1096
1100
  Server-sent event streaming via the Vercel AI SDK:
1097
1101
 
1098
1102
  ```ts
1099
- import { serve, Router, aiStream } from 'weifuwu'
1100
- import { openai } from '@ai-sdk/openai'
1103
+ import { serve, Router, aiStream, openai } from 'weifuwu'
1101
1104
 
1102
1105
  const app = new Router()
1103
1106
  const chat = await aiStream(async (req, ctx) => {
@@ -1114,8 +1117,7 @@ serve(app.handler(), { port: 3000 })
1114
1117
  Multi-step DAG execution engine — packaged as a single AI SDK `Tool`. Use it with `streamText()` or `generateText()` when the LLM needs conditional logic, loops, or multi-step tool orchestration.
1115
1118
 
1116
1119
  ```ts
1117
- import { tool, streamText } from 'ai'
1118
- import { runWorkflow } from 'weifuwu'
1120
+ import { tool, streamText, runWorkflow } from 'weifuwu'
1119
1121
  import { z } from 'zod'
1120
1122
 
1121
1123
  const tools = {
package/cli.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { mkdir, writeFile, copyFile, readdir } from 'node:fs/promises'
2
+ import { mkdir, writeFile, copyFile, readFile } from 'node:fs/promises'
3
+ import { existsSync } from 'node:fs'
3
4
  import { homedir } from 'node:os'
4
5
  import { join, dirname, resolve } from 'node:path'
5
6
  import { fileURLToPath } from 'node:url'
@@ -7,26 +8,20 @@ import { fileURLToPath } from 'node:url'
7
8
  const __filename = fileURLToPath(import.meta.url)
8
9
  const __dirname = dirname(__filename)
9
10
 
10
- const pkgRoot = resolve(__dirname, '..')
11
+ const pkgRoot = existsSync(join(__dirname, 'package.json')) ? __dirname : resolve(__dirname, '..')
11
12
 
12
13
  async function cmdSkill() {
13
14
  const targetDir = join(homedir(), '.agents', 'skills', 'weifuwu')
14
- const docsTarget = join(targetDir, 'docs')
15
-
16
- await mkdir(docsTarget, { recursive: true })
15
+ await mkdir(targetDir, { recursive: true })
17
16
  await copyFile(join(pkgRoot, 'README.md'), join(targetDir, 'SKILL.md'))
18
-
19
- const docDir = join(pkgRoot, 'docs')
20
- const entries = await readdir(docDir)
21
- for (const entry of entries) {
22
- if (entry.endsWith('.md')) {
23
- await copyFile(join(docDir, entry), join(docsTarget, entry))
24
- }
25
- }
26
-
27
17
  console.log('✅ Installed weifuwu skill to ~/.agents/skills/weifuwu/')
28
18
  }
29
19
 
20
+ async function cmdVersion() {
21
+ const pkg = JSON.parse(await readFile(join(pkgRoot, 'package.json'), 'utf-8'))
22
+ console.log(pkg.version)
23
+ }
24
+
30
25
  async function cmdInit(name: string) {
31
26
  const targetDir = resolve(process.cwd(), name)
32
27
  await mkdir(targetDir, { recursive: true })
@@ -78,7 +73,9 @@ async function cmdInit(name: string) {
78
73
 
79
74
  const cmd = process.argv[2]
80
75
 
81
- if (cmd === 'skill') {
76
+ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
77
+ cmdVersion().catch(console.error)
78
+ } else if (cmd === 'skill') {
82
79
  cmdSkill().catch(console.error)
83
80
  } else if (cmd === 'init') {
84
81
  const name = process.argv[3]
@@ -88,5 +85,5 @@ if (cmd === 'skill') {
88
85
  }
89
86
  cmdInit(name).catch(console.error)
90
87
  } else {
91
- console.log('\nUsage:\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n')
88
+ console.log('\nUsage:\n npx weifuwu version Print version\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n')
92
89
  }
@@ -0,0 +1,2 @@
1
+ export { streamText, generateText, generateObject, streamObject, tool, embed, embedMany, smoothStream, } from 'ai';
2
+ export { openai, createOpenAI, } from '@ai-sdk/openai';
package/dist/cli.js CHANGED
@@ -1,27 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli.ts
4
- import { mkdir, writeFile, copyFile, readdir } from "node:fs/promises";
4
+ import { mkdir, writeFile, copyFile, readFile } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
5
6
  import { homedir } from "node:os";
6
7
  import { join, dirname, resolve } from "node:path";
7
8
  import { fileURLToPath } from "node:url";
8
9
  var __filename = fileURLToPath(import.meta.url);
9
10
  var __dirname = dirname(__filename);
10
- var pkgRoot = resolve(__dirname, "..");
11
+ var pkgRoot = existsSync(join(__dirname, "package.json")) ? __dirname : resolve(__dirname, "..");
11
12
  async function cmdSkill() {
12
13
  const targetDir = join(homedir(), ".agents", "skills", "weifuwu");
13
- const docsTarget = join(targetDir, "docs");
14
- await mkdir(docsTarget, { recursive: true });
14
+ await mkdir(targetDir, { recursive: true });
15
15
  await copyFile(join(pkgRoot, "README.md"), join(targetDir, "SKILL.md"));
16
- const docDir = join(pkgRoot, "docs");
17
- const entries = await readdir(docDir);
18
- for (const entry of entries) {
19
- if (entry.endsWith(".md")) {
20
- await copyFile(join(docDir, entry), join(docsTarget, entry));
21
- }
22
- }
23
16
  console.log("\u2705 Installed weifuwu skill to ~/.agents/skills/weifuwu/");
24
17
  }
18
+ async function cmdVersion() {
19
+ const pkg = JSON.parse(await readFile(join(pkgRoot, "package.json"), "utf-8"));
20
+ console.log(pkg.version);
21
+ }
25
22
  async function cmdInit(name) {
26
23
  const targetDir = resolve(process.cwd(), name);
27
24
  await mkdir(targetDir, { recursive: true });
@@ -65,7 +62,9 @@ async function cmdInit(name) {
65
62
  console.log(`\u2705 Created ${name}/ \u2014 cd ${name} && npm install && npm run dev`);
66
63
  }
67
64
  var cmd = process.argv[2];
68
- if (cmd === "skill") {
65
+ if (cmd === "version" || cmd === "-v" || cmd === "--version") {
66
+ cmdVersion().catch(console.error);
67
+ } else if (cmd === "skill") {
69
68
  cmdSkill().catch(console.error);
70
69
  } else if (cmd === "init") {
71
70
  const name = process.argv[3];
@@ -75,5 +74,5 @@ if (cmd === "skill") {
75
74
  }
76
75
  cmdInit(name).catch(console.error);
77
76
  } else {
78
- console.log("\nUsage:\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n");
77
+ console.log("\nUsage:\n npx weifuwu version Print version\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n");
79
78
  }
package/dist/index.d.ts CHANGED
@@ -31,33 +31,34 @@ export type { GraphQLOptions, GraphQLHandler } from './graphql.ts';
31
31
  export { aiStream } from './ai.ts';
32
32
  export type { AIHandler } from './ai.ts';
33
33
  export { runWorkflow } from './ai/workflow.ts';
34
+ export { streamText, generateText, generateObject, streamObject, tool, embed, embedMany, smoothStream, openai, createOpenAI, } from './ai-sdk.ts';
34
35
  export { postgres } from './postgres/index.ts';
35
36
  export type { PostgresOptions, PostgresClient } from './postgres/types.ts';
36
37
  export { user } from './user/index.ts';
37
- export type { UserOptions, UserData, UserModule, OAuth2Client } from './user/types.ts';
38
+ export type { UserOptions, UserData, UserModule, OAuth2Client, } from './user/types.ts';
38
39
  export { redis } from './redis/index.ts';
39
40
  export type { RedisOptions, RedisClient } from './redis/types.ts';
40
41
  export { queue } from './queue/index.ts';
41
42
  export type { QueueOptions, QueueJob, Queue } from './queue/types.ts';
42
43
  export { tenant } from './tenant/index.ts';
43
- export type { TenantOptions, TenantModule, TenantContext, FieldDef, FieldType, RelationDef, UserTableRow } from './tenant/types.ts';
44
+ export type { TenantOptions, TenantModule, TenantContext, FieldDef, FieldType, RelationDef, UserTableRow, } from './tenant/types.ts';
44
45
  export { agent } from './agent/index.ts';
45
- export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult, KnowledgeDoc } from './agent/types.ts';
46
+ export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult, KnowledgeDoc, } from './agent/types.ts';
46
47
  export { messager } from './messager/index.ts';
47
- export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message } from './messager/types.ts';
48
+ export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message, } from './messager/types.ts';
48
49
  export { deploy, defineConfig } from './deploy/index.ts';
49
- export type { DeployConfig, AppConfig, DeployServer, AppStatus } from './deploy/types.ts';
50
+ export type { DeployConfig, AppConfig, DeployServer, AppStatus, } from './deploy/types.ts';
50
51
  export { opencode } from './opencode/index.ts';
51
- export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession } from './opencode/types.ts';
52
+ export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession, } from './opencode/types.ts';
52
53
  export { health } from './health.ts';
53
54
  export type { HealthOptions } from './health.ts';
54
55
  export { i18n } from './i18n.ts';
55
56
  export type { I18nOptions } from './i18n.ts';
56
57
  export { seo, seoMiddleware, seoTags } from './seo.ts';
57
- export type { SeoOptions, RobotsRule, SitemapUrl, SitemapConfig, SeoHeadersConfig, SeoTagsConfig } from './seo.ts';
58
+ export type { SeoOptions, RobotsRule, SitemapUrl, SitemapConfig, SeoHeadersConfig, SeoTagsConfig, } from './seo.ts';
58
59
  export { mailer } from './mailer.ts';
59
60
  export type { MailerOptions, MailOptions, Mailer } from './mailer.ts';
60
61
  export { logdb } from './logdb/index.ts';
61
- export type { LogdbOptions, LogdbModule, LogEntry, LogEntryInput } from './logdb/types.ts';
62
- export { iii, createWorker as Worker, registerWorker } from './iii/index.ts';
63
- export type { IIIModule, IIIOptions, WorkerInfo, FunctionInfo, TriggerInfo, FunctionHandler, FunctionContext, TriggerInput, RemoteWorker, TriggerRequest } from './iii/types.ts';
62
+ export type { LogdbOptions, LogdbModule, LogEntry, LogEntryInput, } from './logdb/types.ts';
63
+ export { iii, createWorker, registerWorker } from './iii/index.ts';
64
+ export type { IIIModule, IIIOptions, WorkerInfo, FunctionInfo, TriggerInfo, FunctionHandler, FunctionContext, TriggerInput, RemoteWorker, TriggerRequest, } from './iii/types.ts';
package/dist/index.js CHANGED
@@ -2109,13 +2109,13 @@ async function aiStream(handler) {
2109
2109
  r.post("/", async (req, ctx) => {
2110
2110
  const options = await handler(req, ctx);
2111
2111
  if (options.schema) {
2112
- const streamObject = await getStreamObject();
2112
+ const streamObject2 = await getStreamObject();
2113
2113
  const { schema, ...params } = options;
2114
- const result2 = streamObject({ ...params, schema, output: "object" });
2114
+ const result2 = streamObject2({ ...params, schema, output: "object" });
2115
2115
  return result2.toTextStreamResponse();
2116
2116
  }
2117
- const streamText3 = await getStreamText();
2118
- const result = streamText3(options);
2117
+ const streamText4 = await getStreamText();
2118
+ const result = streamText4(options);
2119
2119
  return result.toTextStreamResponse();
2120
2120
  });
2121
2121
  return { router: () => r };
@@ -2371,6 +2371,22 @@ function runWorkflow(opts = {}) {
2371
2371
  });
2372
2372
  }
2373
2373
 
2374
+ // ai-sdk.ts
2375
+ import {
2376
+ streamText,
2377
+ generateText as generateText2,
2378
+ generateObject,
2379
+ streamObject,
2380
+ tool as tool2,
2381
+ embed,
2382
+ embedMany,
2383
+ smoothStream
2384
+ } from "ai";
2385
+ import {
2386
+ openai,
2387
+ createOpenAI
2388
+ } from "@ai-sdk/openai";
2389
+
2374
2390
  // postgres/client.ts
2375
2391
  import postgresFactory from "postgres";
2376
2392
 
@@ -4501,7 +4517,7 @@ function tenant(options) {
4501
4517
  }
4502
4518
 
4503
4519
  // agent/client.ts
4504
- import { createOpenAI } from "@ai-sdk/openai";
4520
+ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
4505
4521
 
4506
4522
  // agent/rest.ts
4507
4523
  function buildRouter2(deps) {
@@ -4605,7 +4621,7 @@ function buildRouter2(deps) {
4605
4621
  }
4606
4622
 
4607
4623
  // agent/run.ts
4608
- import { streamText, generateText as generateText2, embed } from "ai";
4624
+ import { streamText as streamText2, generateText as generateText3, embed as embed2 } from "ai";
4609
4625
  import { z as z4 } from "zod";
4610
4626
  function hasKnowledgeDocs(sql2, agentId) {
4611
4627
  return sql2`SELECT 1 FROM "_knowledge_documents" WHERE agent_id = ${agentId} LIMIT 1`.then((r) => r.length > 0);
@@ -4625,7 +4641,7 @@ function chunkContent(content, chunkSize = 512, overlap = 64) {
4625
4641
  return chunks;
4626
4642
  }
4627
4643
  async function searchKnowledge(sql2, embedModel, agentId, query, limit = 5) {
4628
- const { embedding } = await embed({ model: embedModel, value: query });
4644
+ const { embedding } = await embed2({ model: embedModel, value: query });
4629
4645
  const vec = `[${embedding.join(",")}]`;
4630
4646
  const docs = await sql2.unsafe(
4631
4647
  `SELECT id, title, content, metadata, embedding <=> $1::vector AS _score FROM "_knowledge_documents" WHERE agent_id = $2 ORDER BY embedding <=> $1::vector LIMIT $3`,
@@ -4664,13 +4680,13 @@ function createRunner(deps) {
4664
4680
  };
4665
4681
  }
4666
4682
  if (agent2.type === "tool-use" && userTools) {
4667
- for (const [key, tool11] of Object.entries(userTools)) {
4668
- tools[key] = tool11;
4683
+ for (const [key, tool12] of Object.entries(userTools)) {
4684
+ tools[key] = tool12;
4669
4685
  }
4670
4686
  }
4671
4687
  const system = agent2.system_prompt || void 0;
4672
4688
  if (params.stream) {
4673
- const result = streamText({
4689
+ const result = streamText2({
4674
4690
  model,
4675
4691
  system,
4676
4692
  messages: messages2,
@@ -4693,7 +4709,7 @@ function createRunner(deps) {
4693
4709
  });
4694
4710
  return { stream: sseStream };
4695
4711
  } else {
4696
- const result = await generateText2({
4712
+ const result = await generateText3({
4697
4713
  model,
4698
4714
  system,
4699
4715
  messages: messages2,
@@ -4706,14 +4722,14 @@ function createRunner(deps) {
4706
4722
  const embedModel = getEmbeddingModel();
4707
4723
  const chunks = chunkContent(content);
4708
4724
  const [first] = chunks;
4709
- const { embedding } = await embed({ model: embedModel, value: first });
4725
+ const { embedding } = await embed2({ model: embedModel, value: first });
4710
4726
  const vec = `[${embedding.join(",")}]`;
4711
4727
  const [doc] = await sql2.unsafe(
4712
4728
  `INSERT INTO "_knowledge_documents" ("agent_id", "title", "content", "embedding") VALUES ($1, $2, $3, $4::vector) RETURNING *`,
4713
4729
  [agentId, title, first, vec]
4714
4730
  );
4715
4731
  for (let i = 1; i < chunks.length; i++) {
4716
- const { embedding: emb } = await embed({ model: embedModel, value: chunks[i] });
4732
+ const { embedding: emb } = await embed2({ model: embedModel, value: chunks[i] });
4717
4733
  await sql2.unsafe(
4718
4734
  `INSERT INTO "_knowledge_documents" ("agent_id", "title", "content", "embedding") VALUES ($1, $2, $3, $4::vector)`,
4719
4735
  [agentId, `${title} (${i + 1})`, chunks[i], `[${emb.join(",")}]`]
@@ -4730,7 +4746,7 @@ function createModelsFromEnv() {
4730
4746
  const apiKey = process.env.OPENAI_API_KEY || "ollama";
4731
4747
  const modelName = process.env.OPENAI_MODEL || "qwen3:0.6b";
4732
4748
  const embedModelName = process.env.OPENAI_EMBEDDING_MODEL || "qwen3-embedding:0.6b";
4733
- const provider = createOpenAI({ baseURL, apiKey });
4749
+ const provider = createOpenAI2({ baseURL, apiKey });
4734
4750
  return {
4735
4751
  model: provider(modelName),
4736
4752
  embeddingModel: provider.embedding(embedModelName),
@@ -5685,7 +5701,7 @@ function ensureCertificates(config) {
5685
5701
  }
5686
5702
 
5687
5703
  // opencode/client.ts
5688
- import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
5704
+ import { createOpenAI as createOpenAI3 } from "@ai-sdk/openai";
5689
5705
 
5690
5706
  // opencode/session.ts
5691
5707
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -5769,13 +5785,13 @@ async function addToolMessages(sql2, sessionId, toolCalls, toolResults) {
5769
5785
  }
5770
5786
 
5771
5787
  // opencode/run.ts
5772
- import { streamText as streamText2, stepCountIs } from "ai";
5788
+ import { streamText as streamText3, stepCountIs } from "ai";
5773
5789
  async function* executeGenerator(opts) {
5774
5790
  const { sessionId, input, model, tools, systemPrompt, messages: messages2, sql: sql2, abortSignal } = opts;
5775
5791
  const lastStepToolCalls = [];
5776
5792
  let currentAssistantText = "";
5777
5793
  let currentUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
5778
- const result = streamText2({
5794
+ const result = streamText3({
5779
5795
  model,
5780
5796
  system: systemPrompt,
5781
5797
  messages: [
@@ -5907,11 +5923,11 @@ function isSkillAllowed(name, perms) {
5907
5923
  }
5908
5924
 
5909
5925
  // opencode/tools/bash.ts
5910
- import { tool as tool2 } from "ai";
5926
+ import { tool as tool3 } from "ai";
5911
5927
  import { z as z5 } from "zod";
5912
5928
  import { exec } from "node:child_process";
5913
5929
  function createBashTool(ctx) {
5914
- return tool2({
5930
+ return tool3({
5915
5931
  description: "Execute a shell command in the workspace directory. Returns stdout, stderr, and exit code.",
5916
5932
  inputSchema: z5.object({
5917
5933
  command: z5.string().describe("The shell command to execute"),
@@ -5940,12 +5956,12 @@ function createBashTool(ctx) {
5940
5956
  }
5941
5957
 
5942
5958
  // opencode/tools/read.ts
5943
- import { tool as tool3 } from "ai";
5959
+ import { tool as tool4 } from "ai";
5944
5960
  import { z as z6 } from "zod";
5945
5961
  import { readFileSync as readFileSync3 } from "node:fs";
5946
5962
  import { resolve as resolve4 } from "node:path";
5947
5963
  function createReadTool(ctx) {
5948
- return tool3({
5964
+ return tool4({
5949
5965
  description: "Read file contents. Supports offset and limit for reading specific line ranges.",
5950
5966
  inputSchema: z6.object({
5951
5967
  path: z6.string().describe("File path relative to workspace"),
@@ -5982,12 +5998,12 @@ function createReadTool(ctx) {
5982
5998
  }
5983
5999
 
5984
6000
  // opencode/tools/write.ts
5985
- import { tool as tool4 } from "ai";
6001
+ import { tool as tool5 } from "ai";
5986
6002
  import { z as z7 } from "zod";
5987
6003
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
5988
6004
  import { resolve as resolve5, dirname as dirname2 } from "node:path";
5989
6005
  function createWriteTool(ctx) {
5990
- return tool4({
6006
+ return tool5({
5991
6007
  description: "Create or overwrite a file. Parent directories are created automatically.",
5992
6008
  inputSchema: z7.object({
5993
6009
  path: z7.string().describe("File path relative to workspace"),
@@ -6006,12 +6022,12 @@ function createWriteTool(ctx) {
6006
6022
  }
6007
6023
 
6008
6024
  // opencode/tools/edit.ts
6009
- import { tool as tool5 } from "ai";
6025
+ import { tool as tool6 } from "ai";
6010
6026
  import { z as z8 } from "zod";
6011
6027
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
6012
6028
  import { resolve as resolve6 } from "node:path";
6013
6029
  function createEditTool(ctx) {
6014
- return tool5({
6030
+ return tool6({
6015
6031
  description: "Perform exact string replacements in a file. If oldString appears multiple times, provide more surrounding context.",
6016
6032
  inputSchema: z8.object({
6017
6033
  path: z8.string().describe("File path relative to workspace"),
@@ -6050,13 +6066,13 @@ function createEditTool(ctx) {
6050
6066
  }
6051
6067
 
6052
6068
  // opencode/tools/grep.ts
6053
- import { tool as tool6 } from "ai";
6069
+ import { tool as tool7 } from "ai";
6054
6070
  import { z as z9 } from "zod";
6055
6071
  import { execSync as execSync2 } from "node:child_process";
6056
6072
  import { resolve as resolve7 } from "node:path";
6057
6073
  import { existsSync as existsSync3 } from "node:fs";
6058
6074
  function createGrepTool(ctx) {
6059
- return tool6({
6075
+ return tool7({
6060
6076
  description: "Search file contents using regex. Supports file type filtering and context lines.",
6061
6077
  inputSchema: z9.object({
6062
6078
  pattern: z9.string().describe("The regex pattern to search for"),
@@ -6088,12 +6104,12 @@ function createGrepTool(ctx) {
6088
6104
  }
6089
6105
 
6090
6106
  // opencode/tools/glob.ts
6091
- import { tool as tool7 } from "ai";
6107
+ import { tool as tool8 } from "ai";
6092
6108
  import { z as z10 } from "zod";
6093
6109
  import { execSync as execSync3 } from "node:child_process";
6094
6110
  import { resolve as resolve8 } from "node:path";
6095
6111
  function createGlobTool(ctx) {
6096
- return tool7({
6112
+ return tool8({
6097
6113
  description: "Find files matching a glob pattern.",
6098
6114
  inputSchema: z10.object({
6099
6115
  pattern: z10.string().describe('Glob pattern (e.g. "src/**/*.ts")'),
@@ -6116,10 +6132,10 @@ function createGlobTool(ctx) {
6116
6132
  }
6117
6133
 
6118
6134
  // opencode/tools/web.ts
6119
- import { tool as tool8 } from "ai";
6135
+ import { tool as tool9 } from "ai";
6120
6136
  import { z as z11 } from "zod";
6121
6137
  function createWebTool(ctx) {
6122
- return tool8({
6138
+ return tool9({
6123
6139
  description: "Fetch a URL and return the content as text.",
6124
6140
  inputSchema: z11.object({
6125
6141
  url: z11.string().describe("The URL to fetch")
@@ -6144,10 +6160,10 @@ function createWebTool(ctx) {
6144
6160
  }
6145
6161
 
6146
6162
  // opencode/tools/question.ts
6147
- import { tool as tool9 } from "ai";
6163
+ import { tool as tool10 } from "ai";
6148
6164
  import { z as z12 } from "zod";
6149
6165
  function createQuestionTool(ctx) {
6150
- return tool9({
6166
+ return tool10({
6151
6167
  description: "Ask the user a question when you need more information to proceed.",
6152
6168
  inputSchema: z12.object({
6153
6169
  question: z12.string().describe("The question to ask the user"),
@@ -6175,7 +6191,7 @@ function createQuestionTool(ctx) {
6175
6191
  }
6176
6192
 
6177
6193
  // opencode/tools/skill.ts
6178
- import { tool as tool10 } from "ai";
6194
+ import { tool as tool11 } from "ai";
6179
6195
  import { z as z13 } from "zod";
6180
6196
  function createSkillTool(ctx) {
6181
6197
  const skills = ctx.skillsRegistry.list();
@@ -6190,7 +6206,7 @@ function createSkillTool(ctx) {
6190
6206
  <available_skills>
6191
6207
  ${availableList}
6192
6208
  </available_skills>` : "No skills available.";
6193
- return tool10({
6209
+ return tool11({
6194
6210
  description,
6195
6211
  inputSchema: z13.object({
6196
6212
  name: z13.string().describe("The name of the skill to load")
@@ -6536,7 +6552,7 @@ async function opencode(options) {
6536
6552
  const modelName = options.model || "deepseek-v4-flash";
6537
6553
  const [discoveredSkills] = await Promise.all([discoverSkills(workspace)]);
6538
6554
  const skillsRegistry = buildSkillRegistry(discoveredSkills, manualSkills);
6539
- const provider = createOpenAI2({ baseURL, apiKey });
6555
+ const provider = createOpenAI3({ baseURL, apiKey });
6540
6556
  const model = provider.chat(modelName);
6541
6557
  const pendingQuestions = /* @__PURE__ */ new Map();
6542
6558
  const base = new PgModule(pg);
@@ -7955,19 +7971,24 @@ function registerWorker(url) {
7955
7971
  export {
7956
7972
  Router,
7957
7973
  TsxContext,
7958
- createWorker as Worker,
7959
7974
  agent,
7960
7975
  aiStream,
7961
7976
  auth,
7962
7977
  compress,
7963
7978
  cors,
7979
+ createOpenAI,
7964
7980
  createSSEStream,
7965
7981
  createTestServer,
7982
+ createWorker,
7966
7983
  defineConfig,
7967
7984
  deleteCookie,
7968
7985
  deploy,
7986
+ embed,
7987
+ embedMany,
7969
7988
  formatSSE,
7970
7989
  formatSSEData,
7990
+ generateObject,
7991
+ generateText2 as generateText,
7971
7992
  getCookies,
7972
7993
  graphql,
7973
7994
  health,
@@ -7979,6 +8000,7 @@ export {
7979
8000
  logger,
7980
8001
  mailer,
7981
8002
  messager,
8003
+ openai,
7982
8004
  opencode,
7983
8005
  postgres,
7984
8006
  queue,
@@ -7993,7 +8015,11 @@ export {
7993
8015
  serve,
7994
8016
  serveStatic,
7995
8017
  setCookie,
8018
+ smoothStream,
8019
+ streamObject,
8020
+ streamText,
7996
8021
  tenant,
8022
+ tool2 as tool,
7997
8023
  tsx,
7998
8024
  upload,
7999
8025
  useTsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weifuwu",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "Web-standard HTTP framework for Node.js — (req, ctx) => Response",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,7 +13,6 @@
13
13
  },
14
14
  "files": [
15
15
  "dist/",
16
- "docs/",
17
16
  "cli.ts",
18
17
  "README.md",
19
18
  "LICENSE"
package/README.zh.md DELETED
@@ -1,158 +0,0 @@
1
- ---
2
- name: weifuwu
3
- description: 面向 Node.js 的 Web 标准 HTTP 框架 — (req, ctx) => Response
4
- ---
5
-
6
- # weifuwu
7
-
8
- **面向 Node.js 的 Web 标准 HTTP 框架。** `(req, ctx) => Response` — 没有框架特有的对象,只有浏览器原生支持的 Web API。
9
-
10
- ### 设计理念
11
-
12
- weifuwu 不发明自己的请求/响应抽象。`Request` 和 `Response` 就是你在 `fetch()` 中使用的那套 API——你在浏览器中学到的知识可以直接用在服务端。`ctx` 是唯一的框架对象,它只携带路由解析的结果(`params`、`query`)。
13
-
14
- 所有功能遵循相同的 `(req, ctx) => Response` 约定。Router 负责 HTTP 路由和 WebSocket。其他所有功能——认证、校验、数据库、GraphQL、AI——都是独立的模块,通过 `app.use()` 挂载。
15
-
16
- ## 功能特性
17
-
18
- - **Web 标准** — `Request` / `Response` / `ReadableStream`,零抽象
19
- - **零构建** — Node.js v24+ 原生 TypeScript,核心零依赖
20
- - **Trie 路由器** — 静态 > 参数 > 通配符,子路由挂载,WebSocket
21
- - **中间件** — 全局/路径级/路由级 — 洋葱模型,支持短路
22
- - **模块** — 认证、校验、上传、压缩、限流、Cookie、静态文件、CORS、日志
23
- - **React SSR** — `tsx()` — 页面、布局、数据加载、路由处理、Tailwind CSS、HMR
24
- - **PostgreSQL** — 类型安全 DDL + CRUD 的 schema 构建器,事务,向量搜索
25
- - **认证** — 密码 + JWT + OAuth2 服务端(authorization code / PKCE / client_credentials)
26
- - **实时** — WebSocket,带 agent 路由的消息频道
27
- - **AI** — 流式端点,DAG 工作流工具,支持 RAG 和工具调用的 AI agent
28
- - **数据** — Redis 客户端,支持 cron 调度的任务队列
29
- - **多租户 BaaS** — 动态表,自动 REST + GraphQL,行级隔离
30
- - **部署** — 自托管 PaaS:多应用代理,零停机更新,自动 SSL
31
- - **国际化** — 语言检测,JSON 翻译,`ctx.t()`
32
- - **邮件** — SMTP 或自定义传输
33
- - **健康检查** — 可配置的 `/health` 端点
34
- - **环境变量** — `loadEnv()` — 将 `.env` 文件加载到 `process.env`
35
- - **测试工具** — `createTestServer()` — 一行代码搭建测试服务
36
-
37
- ## 快速开始
38
-
39
- ### Hello World
40
-
41
- ```ts
42
- import { serve } from 'weifuwu'
43
- serve((req, ctx) => new Response('Hello, World!'), { port: 3000 })
44
- ```
45
-
46
- ### 完整应用
47
-
48
- ```ts
49
- import { serve, Router, postgres, user, aiStream, graphql } from 'weifuwu'
50
- import { openai } from '@ai-sdk/openai'
51
-
52
- const app = new Router()
53
- const pg = postgres()
54
-
55
- // 认证
56
- const auth = user({ pg, jwtSecret: process.env.JWT_SECRET! })
57
- await auth.migrate()
58
- app.use('/auth', auth.router())
59
-
60
- // AI 流式
61
- const chat = await aiStream(async (req) => ({
62
- model: openai('gpt-4o'),
63
- messages: (await req.json()).messages,
64
- }))
65
- app.use('/chat', chat.router())
66
-
67
- // GraphQL
68
- const gql = graphql(() => ({
69
- schema: `type Query { hello: String }`,
70
- resolvers: { Query: { hello: () => 'world' } },
71
- }))
72
- app.use('/graphql', gql.router())
73
-
74
- // 静态文件
75
- app.get('/static/*', serveStatic('./public'))
76
-
77
- serve(app.handler(), { port: 3000 })
78
- ```
79
-
80
- ```
81
- node app.ts
82
- ```
83
-
84
- ## 文档
85
-
86
- | 模块 | 文档 | 说明 |
87
- |--------|------|------|
88
- | **Router** | [README.md](#router) | 路由、中间件、WebSocket、错误处理 |
89
- | **Middleware** | [README.md](#middleware) | auth, cors, logger, rateLimit, compress, validate, upload, cookie, static |
90
- | **PostgreSQL** | [README.md](#postgresql) | Schema 构建器、CRUD、DDL、事务、PgModule |
91
- | **Auth & User** | [README.md](#auth--user) | 密码、JWT、OAuth2 服务端、社交登录示例 |
92
- | **React SSR** | [README.md](#react-ssr-with-tsx) | 页面、布局、数据加载、Tailwind、shadcn/ui |
93
- | **AI** | [README.md](#ai-streaming--workflow) | `aiStream()`, `runWorkflow()` |
94
- | **AI Agent** | [README.md](#ai-agent) | 对话、工具调用、RAG 知识库 |
95
- | **Opencode** | [README.md](#opencode) | 编程助手、技能、会话、权限 |
96
- | **Messager** | [README.md](#messager) | 实时聊天、频道、WebSocket、agent 路由 |
97
- | **GraphQL** | [README.md](#graphql) | 带 GraphiQL 的 GraphQL 端点 |
98
- | **Tenant BaaS** | [README.md](#tenant-baas) | 动态表、自动 REST + GraphQL、行隔离 |
99
- | **LogDB** | [README.md](#logdb--structured-event-logging) | 结构化事件日志、分区、元数据搜索 |
100
- | **Extra** | [README.md](#health-i18n-email--testing) | 健康检查、国际化、邮件、测试工具 |
101
-
102
- ### 基础设施
103
-
104
- | 模块 | 导入 | 功能 |
105
- |--------|--------|-----------|
106
- | PostgreSQL | `postgres(options?)` | 连接池 + schema 构建器 + CRUD + 事务 |
107
- | Redis | `redis(options?)` | ioredis 客户端,注入为 `ctx.redis` |
108
- | 队列 | `queue(options?)` | 基于 Redis 的任务队列,支持 cron 调度 |
109
- | 部署 | `deploy(config)` | 自托管 PaaS,详见 [deploy.md](./deploy.md) |
110
-
111
- ### 可挂载模块
112
-
113
- 所有模块遵循相同模式 — `const m = module(options)` → `app.use('/path', m.router())`:
114
-
115
- | 模块 | 用途 | 额外提供 |
116
- |--------|---------|---------------|
117
- | `user(options)` | 认证(密码 + JWT + OAuth2) | `migrate()`, `middleware()`, `register()`, `login()`, `verify()`, `close()` |
118
- | `tenant(options)` | 多租户 BaaS | `migrate()`, `middleware()`, `graphql()`, `close()` |
119
- | `agent(options)` | AI agents | `migrate()`, `run()`, `addKnowledge()`, `close()` |
120
- | `opencode(options)` | 编程助手 | `migrate()`, `wsHandler()`, `close()` |
121
- | `messager(options)` | 实时消息 | `migrate()`, `wsHandler()`, `send()`, `close()` |
122
- | `aiStream(handler)` | AI 流式端点 | — |
123
- | `graphql(handler)` | GraphQL 端点 | — |
124
- | `health(options?)` | 健康检查 | — |
125
- | `iii(options?)` | Worker/Function/Trigger 服务范式 | `migrate()`, `trigger()`, `addWorker()`, `listWorkers()`, `listFunctions()`, `listTriggers()`, `shutdown()` |
126
- | `registerWorker(url)` | 纯 WebSocket SDK(浏览器/Node) | `registerFunction()`, `registerTrigger()`, `trigger()`, `shutdown()` |
127
-
128
- ### 中间件(全部为 `(req, ctx, next) => Response`)
129
-
130
- | 中间件 | 说明 |
131
- |-----------|-------------|
132
- | `auth(options)` | Bearer token / 自定义请求头 / 验证 / 代理 |
133
- | `cors(options?)` | CORS,含预检请求、来源白名单、凭据 |
134
- | `logger(options?)` | 带耗时的请求日志 |
135
- | `rateLimit(options?)` | 基于内存的限流,带响应头 |
136
- | `compress(options?)` | Brotli / Gzip / Deflate 压缩 |
137
- | `validate(schemas)` | Zod 校验(body, query, params) |
138
- | `upload(options?)` | 多部分文件上传 |
139
- | `i18n(options)` | 国际化 — `ctx.t()`,语言检测 |
140
-
141
- ### 工具函数
142
-
143
- | 函数 | 说明 |
144
- |----------|-------------|
145
- | `serveStatic(root, options?)` | 静态文件服务 |
146
- | `loadEnv(path?)` | 加载 `.env` 文件到 `process.env` — 不覆盖、支持注释和引号 |
147
- | `getCookies(req)` / `setCookie(res, ...)` / `deleteCookie(res, ...)` | Cookie 助手 |
148
- | `mailer(options)` | 邮件发送(SMTP 或自定义) |
149
- | `createTestServer(handler)` | 启动测试服务 → `{ server, url }` |
150
- | `runWorkflow(options)` | 作为 AI SDK `Tool` 的 DAG 执行引擎 |
151
- | `pgTable(name, columns)` | 类型安全的表 schema 构建器 |
152
- | `pg.table(name, columns)` | 预绑定表(无需传 `sql` 参数) |
153
- | `serial()`, `uuid()`, `text()`, ... | 列类型构建器 |
154
- | `PgModule` | 数据库模块的基类 |
155
-
156
- ## 许可证
157
-
158
- MIT