weifuwu 0.9.6 → 0.10.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.
Files changed (55) hide show
  1. package/README.md +183 -43
  2. package/dist/agent/migrate.d.ts +1 -1
  3. package/dist/agent/rest.d.ts +1 -1
  4. package/dist/agent/run.d.ts +2 -2
  5. package/dist/agent/types.d.ts +2 -2
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.js +1890 -10615
  8. package/dist/messager/migrate.d.ts +1 -1
  9. package/dist/messager/rest.d.ts +1 -1
  10. package/dist/messager/types.d.ts +2 -1
  11. package/dist/messager/ws.d.ts +1 -1
  12. package/dist/opencode/client.d.ts +2 -0
  13. package/dist/opencode/index.d.ts +2 -0
  14. package/dist/opencode/migrate.d.ts +2 -0
  15. package/dist/opencode/permissions.d.ts +5 -0
  16. package/dist/opencode/prompt.d.ts +8 -0
  17. package/dist/opencode/rest.d.ts +15 -0
  18. package/dist/opencode/run.d.ts +13 -0
  19. package/dist/opencode/session.d.ts +26 -0
  20. package/dist/opencode/skills.d.ts +4 -0
  21. package/dist/opencode/tools/bash.d.ts +6 -0
  22. package/dist/opencode/tools/edit.d.ts +19 -0
  23. package/dist/opencode/tools/glob.d.ts +9 -0
  24. package/dist/opencode/tools/grep.d.ts +17 -0
  25. package/dist/opencode/tools/index.d.ts +12 -0
  26. package/dist/opencode/tools/question.d.ts +5 -0
  27. package/dist/opencode/tools/read.d.ts +16 -0
  28. package/dist/opencode/tools/skill.d.ts +18 -0
  29. package/dist/opencode/tools/web.d.ts +18 -0
  30. package/dist/opencode/tools/write.d.ts +13 -0
  31. package/dist/opencode/types.d.ts +90 -0
  32. package/dist/opencode/ws.d.ts +22 -0
  33. package/dist/postgres/index.d.ts +3 -1
  34. package/dist/postgres/module.d.ts +9 -0
  35. package/dist/postgres/schema/columns.d.ts +33 -0
  36. package/dist/postgres/schema/index.d.ts +4 -0
  37. package/dist/postgres/schema/sql.d.ts +7 -0
  38. package/dist/postgres/schema/table.d.ts +22 -0
  39. package/dist/postgres/types.d.ts +7 -35
  40. package/dist/queue/types.d.ts +1 -1
  41. package/dist/redis/types.d.ts +1 -1
  42. package/dist/router.d.ts +1 -1
  43. package/dist/sse.d.ts +10 -0
  44. package/dist/tenant/graphql.d.ts +1 -1
  45. package/dist/tenant/migrate.d.ts +1 -1
  46. package/dist/tenant/rest.d.ts +1 -1
  47. package/dist/tenant/types.d.ts +2 -1
  48. package/dist/tsx-context.d.ts +12 -0
  49. package/dist/tsx-instance.d.ts +32 -0
  50. package/dist/tsx.d.ts +5 -16
  51. package/dist/user/types.d.ts +2 -1
  52. package/dist/vendor.d.ts +4 -0
  53. package/dist/workflow/engine.d.ts +1 -1
  54. package/dist/workflow/route.d.ts +1 -1
  55. package/package.json +5 -4
package/README.md CHANGED
@@ -221,83 +221,203 @@ Features: MIME type detection (20+ types), ETag + If-None-Match (304), directory
221
221
 
222
222
  ## PostgreSQL
223
223
 
224
- Built-in PostgreSQL — zero config, zero ORM, zero migration files.
224
+ Built-in PostgreSQL client connection management, type-safe DDL, transactions, and module lifecycle.
225
225
 
226
226
  ```ts
227
227
  import { serve, Router, postgres } from 'weifuwu'
228
- import { z } from 'zod'
229
228
 
230
229
  const app = new Router()
231
- const pg = postgres()
230
+ const pg = postgres() // reads DATABASE_URL
231
+ app.use(pg) // injects ctx.sql into handlers
232
+ ```
232
233
 
233
- const User = pg.table('users', {
234
- id: z.number().optional(), // → SERIAL PRIMARY KEY
235
- name: z.string().min(1), // → TEXT NOT NULL
236
- email: z.string().email(), // → TEXT NOT NULL
237
- age: z.number().optional(), // → INTEGER
238
- })
234
+ ### Type-safe DDL with schema builder
239
235
 
240
- await pg.migrate()
241
- // Auto-creates tables / adds missing columns via information_schema
242
- app.use(pg) // injects ctx.sql into handlers
236
+ Define tables declaratively with type inference — no raw SQL for common operations, no Zod needed:
237
+
238
+ ```ts
239
+ import { pgTable, serial, uuid, text, integer, boolean, timestamptz, jsonb, sql } from 'weifuwu'
240
+
241
+ const users = pgTable('_users', {
242
+ id: serial('id').primaryKey(),
243
+ name: text('name').notNull(),
244
+ email: text('email').unique().notNull(),
245
+ age: integer('age'),
246
+ active: boolean('active').default(true),
247
+ createdAt: timestamptz('created_at').default(sql`NOW()`),
248
+ metadata: jsonb<{ role: string }>('metadata'),
249
+ })
243
250
  ```
244
251
 
245
- ### 6 methods — HTTP semantics
252
+ Supports 10 column types:
253
+ | Builder | DDL | TS Type |
254
+ |---------|-----|---------|
255
+ | `serial()` | `SERIAL` | `number` |
256
+ | `uuid()` | `UUID` | `string` |
257
+ | `text()` | `TEXT` | `string` |
258
+ | `integer()` | `INTEGER` | `number` |
259
+ | `boolean()` | `BOOLEAN` | `boolean` |
260
+ | `timestamptz()` | `TIMESTAMPTZ` | `string` |
261
+ | `jsonb<T>()` | `JSONB` | `T` |
262
+ | `textArray()` | `TEXT[]` | `string[]` |
263
+ | `vector(name, dims)` | `vector(N)` | `number[]` |
246
264
 
247
- ```ts
248
- User.get(1) // GET /users/:id
249
- User.list({ name: 'a' }, // GET /users?name=a
250
- { limit: 10, offset: 0, sort: { id: 'desc' } })
251
- // → { rows: User[], count: number }
265
+ Column constraints chainable: `.primaryKey()`, `.notNull()`, `.nullable()`, `.default(value | sql\`...\`)`, `.unique()`, `.references(table, column?, onDelete?)`.
252
266
 
253
- User.create({ name: 'A', email: 'a@b.com' }) // POST /users
254
- User.patch(1, { name: 'B' }) // PATCH /users/:id
255
- User.remove(1) // DELETE /users/:id
267
+ ### DDL execution
268
+
269
+ ```ts
270
+ await users.create() // CREATE TABLE IF NOT EXISTS
271
+ await users.createIndex('email') // CREATE INDEX
272
+ await users.createUniqueIndex('slug') // CREATE UNIQUE INDEX
273
+ await users.createIndex('created_at', { desc: true })
274
+ await users.createIndex(['a', 'b']) // multi-column
275
+ await users.createIndex('embedding', { // pgvector HNSW
276
+ type: 'hnsw', operator: 'vector_cosine_ops',
277
+ })
278
+ await users.drop({ cascade: true })
256
279
  ```
257
280
 
258
- Every method validates input against your zod schema automatically. Complex queries use `ctx.sql`:
281
+ ### Complex queries use raw SQL
259
282
 
260
283
  ```ts
261
284
  app.get('/users/stats', async (req, ctx) => {
262
285
  const rows = await ctx.sql`
263
286
  SELECT u.*, count(p.id) as posts
264
- FROM users u LEFT JOIN posts p ON p.user_id = u.id
287
+ FROM ${users} u LEFT JOIN posts p ON p.user_id = u.id
265
288
  GROUP BY u.id
266
289
  `
267
290
  return Response.json(rows)
268
291
  })
269
292
  ```
270
293
 
271
- ### Migration-free sync
294
+ ### Transactions
272
295
 
273
- `pg.migrate()` queries `information_schema.columns` and only runs the DDL needed:
296
+ ```ts
297
+ const result = await pg.transaction(async (tx) => {
298
+ const [user] = await tx`INSERT INTO "_users" (...) VALUES (...) RETURNING *`
299
+ const [wallet] = await tx`INSERT INTO "_wallets" ("user_id") VALUES (${user.id}) RETURNING *`
300
+ return { user, wallet }
301
+ })
302
+ ```
274
303
 
275
- - **Table missing** → `CREATE TABLE IF NOT EXISTS`
276
- - **Column missing** → `ALTER TABLE ADD COLUMN IF NOT EXISTS`
277
- - **Existing** → no-op
304
+ ### Connection lifecycle
278
305
 
279
- Safe for production: never drops or alters existing columns. Destructive operations (rename, type change, drop) are done via `ctx.sql`.
306
+ ```ts
307
+ const pg = postgres() // reads DATABASE_URL
308
+ const pg = postgres('postgres://...') // explicit connection
309
+ const pg = postgres({
310
+ connection: 'postgres://...',
311
+ max: 10, // pool size
312
+ ssl: { rejectUnauthorized: false }, // SSL options
313
+ idle_timeout: 30, // idle timeout (s)
314
+ connect_timeout: 10, // connection timeout (s)
315
+ closeTimeout: 5, // close grace period (s)
316
+ signal: ac.signal, // abort → sql.end()
317
+ })
318
+ await pg.close()
319
+ ```
280
320
 
281
- ### Connection lifecycle
321
+ ### Module base class
322
+
323
+ Every database module (`opencode`, `messager`, `tenant`, `agent`, `user`) extends `PgModule`:
282
324
 
283
325
  ```ts
284
- const pg = postgres() // reads DATABASE_URL
285
- const pg = postgres('postgres://...') // explicit connection
286
- const pg = postgres({ signal: ac.signal }) // abort → sql.end()
287
- await pg.close() // explicit close
326
+ import { PgModule } from 'weifuwu'
327
+
328
+ class MyModule extends PgModule {
329
+ constructor(pg: PostgresClient) {
330
+ super(pg) // sets this.sql = pg.sql
331
+ }
332
+ async migrate() { /* override */ }
333
+ // close() inherited — calls pg.close() automatically
334
+ }
288
335
  ```
289
336
 
290
- ### Primary keys
337
+ ## Opencode
338
+
339
+ AI programming assistant — chat with LLM agents that have access to filesystem tools, skills, and isolated session workspaces.
291
340
 
292
- | zod field | PostgreSQL |
293
- |-----------|-----------|
294
- | `id: z.number().optional()` | `SERIAL PRIMARY KEY` |
295
- | `id: z.string().uuid().optional()` | `UUID PRIMARY KEY DEFAULT gen_random_uuid()` |
296
- | `id: z.string()` | `TEXT PRIMARY KEY` (you pass the value) |
341
+ ```ts
342
+ import { serve, Router, postgres, opencode } from 'weifuwu'
343
+
344
+ const app = new Router()
345
+ const pg = postgres()
346
+ const oc = await opencode({ pg, permissions: { ... } })
347
+
348
+ await oc.migrate()
349
+ app.use('/opencode', await oc.router())
350
+ app.ws('/opencode', oc.wsHandler())
351
+
352
+ serve(app.handler(), { port: 3000, websocket: app.websocketHandler() })
353
+ ```
297
354
 
298
- ## Authentication
355
+ ### Session-isolated workspaces
299
356
 
300
- Built-in user management password login, JWT, and OAuth2 Server. Zero config beyond PostgreSQL and a secret key.
357
+ Each session gets its own sandbox directory tools operate within it, files cannot escape:
358
+
359
+ ```
360
+ cwd/.sessions/opencode/1/ ← session 1's workspace
361
+ cwd/.sessions/opencode/2/ ← session 2's workspace
362
+ cwd/.sessions/chat/3/ ← different mount point
363
+ ```
364
+
365
+ Workspaces are computed from `cwd { ctx.mountPath } { sessionId }`. The system prompt shows the session's workspace so the LLM knows where it is.
366
+
367
+ ### Tools
368
+
369
+ | Tool | Description |
370
+ |------|-------------|
371
+ | `bash` | Execute shell commands in the workspace |
372
+ | `read` | Read files with offset/limit |
373
+ | `write` | Create or overwrite files |
374
+ | `edit` | Exact string replacements |
375
+ | `grep` | Regex content search |
376
+ | `glob` | Glob pattern file search |
377
+ | `web` | Fetch URL content |
378
+ | `question` | Ask the user for input |
379
+ | `skill` | Load a skill on demand |
380
+
381
+ ### Skills
382
+
383
+ Skills are discovered from filesystem and loaded on demand via the `skill` tool — no system prompt bloat:
384
+
385
+ - Project: `.opencode/skills/{name}/SKILL.md`
386
+ - Global: `~/.config/opencode/skills/{name}/SKILL.md`
387
+ - Also reads: `.claude/skills/`, `.agents/skills/` (project + global)
388
+
389
+ ```ts
390
+ const oc = await opencode({
391
+ pg,
392
+ skills: [{ name: 'git', description: 'Git workflow', content: '...' }],
393
+ })
394
+ ```
395
+
396
+ ### Permissions
397
+
398
+ Control tool access per conversation:
399
+
400
+ ```ts
401
+ const oc = await opencode({
402
+ pg,
403
+ permissions: {
404
+ bash: { allow: true },
405
+ read: { allow: true },
406
+ write: { allow: false },
407
+ edit: { allow: false },
408
+ skill: { '*': { allow: true }, 'internal-*': { allow: false } },
409
+ },
410
+ })
411
+ ```
412
+
413
+ ### Workspace isolation
414
+
415
+ ```ts
416
+ const oc = await opencode({ pg, permissions })
417
+ // All sessions inherit the instance's workspace (default: process.cwd())
418
+ // Sessions cannot override their workspace
419
+ // Different mount points = different opencode() instances = isolated workspaces
420
+ ```
301
421
 
302
422
  ```ts
303
423
  import { serve, Router, postgres, user } from 'weifuwu'
@@ -1218,6 +1338,21 @@ Returns `TenantModule` — `{ migrate, middleware, router, graphql, close }`.
1218
1338
 
1219
1339
  Returns `AgentModule` — `{ migrate, router, run, addKnowledge, close }`.
1220
1340
 
1341
+ ### `opencode(options)`
1342
+
1343
+ | Option | Default | Description |
1344
+ |--------|---------|-------------|
1345
+ | `pg` | — | PostgreSQL client from `postgres()` |
1346
+ | `workspace` | `process.cwd()` | Base directory for `.sessions` |
1347
+ | `model` | `'deepseek-v4-flash'` | LLM model name |
1348
+ | `baseURL` | env `DEEPSEEK_BASE_URL` | API base URL |
1349
+ | `apiKey` | env `DEEPSEEK_API_KEY` | API key |
1350
+ | `systemPrompt` | — | Custom system prompt |
1351
+ | `skills` | `[]` | Static skill definitions |
1352
+ | `permissions` | — | Tool permission config |
1353
+
1354
+ Returns `OpencodeModule` — `{ migrate, router, wsHandler, close }`.
1355
+
1221
1356
  ### `messager(options)`
1222
1357
 
1223
1358
  | Option | Default | Description |
@@ -1278,13 +1413,14 @@ serve(app.handler(), { websocket: app.websocketHandler() })
1278
1413
 
1279
1414
  | Import | Description |
1280
1415
  |--------|-------------|
1281
- | `postgres(options?)` | PostgreSQL connection + auto-migration + 6 CRUD methods |
1416
+ | `postgres(options?)` | PostgreSQL connection + DDL schema builder + transactions + module lifecycle |
1282
1417
  | `redis(options?)` | Redis client (ioredis) — injects `ctx.redis` |
1283
1418
  | `queue(options?)` | Redis-backed job queue — immediate, delayed, cron scheduling |
1284
1419
  | `user(options)` | Built-in authentication (password + OAuth2 Server + JWT, middleware) |
1285
1420
  | `tenant(options)` | Multi-tenant BaaS — dynamic tables, REST + GraphQL auto-generation, row-level isolation |
1286
1421
  | `agent(options)` | AI Agent — chat/workflow/knowledge, Ollama-ready, programmatic API |
1287
1422
  | `messager(options)` | Real-time messaging — channels, WebSocket, agent routing, webhooks |
1423
+ | `opencode(options)` | AI programming assistant — chat agents with tools, skills, permissions, isolated workspaces |
1288
1424
  | `graphql(handler)` | GraphQL endpoint (GET/POST + GraphiQL) |
1289
1425
  | `ai(handler)` | AI streaming endpoint (POST) |
1290
1426
  | `workflow(handler)` | Workflow engine (POST + SSE) |
@@ -1308,6 +1444,10 @@ serve(app.handler(), { websocket: app.websocketHandler() })
1308
1444
  | `createWorkflowEngine(options)` | Programmatic workflow engine |
1309
1445
  | `createSSEManager()` | SSE event manager for workflows |
1310
1446
  | `tool(def)` | Define a workflow tool |
1447
+ | `pgTable(name, columns)` | Type-safe table schema definition with DDL generation |
1448
+ | `serial()`, `uuid()`, `text()`, `integer()`, `boolean()`, `timestamptz()`, `jsonb()`, `textArray()`, `vector()` | Column type builders |
1449
+ | `sql(strings, ...)` | SQL expression literal for table defaults (e.g. `sql\`NOW()\``) |
1450
+ | `PgModule` | Base class for database-backed modules (provides `sql`, `close()`) |
1311
1451
 
1312
1452
  Import `useTsx` and `TsxContext` from `'weifuwu'`.
1313
1453
 
@@ -1,4 +1,4 @@
1
- import type { Sql } from 'postgres';
1
+ import type { Sql } from '../vendor.ts';
2
2
  export interface MigrateOptions {
3
3
  sql: Sql<{}>;
4
4
  embeddingDimension: number;
@@ -1,4 +1,4 @@
1
- import type { Sql } from 'postgres';
1
+ import type { Sql } from '../vendor.ts';
2
2
  import { Router } from '../router.ts';
3
3
  import type { RunParams } from './types.ts';
4
4
  interface RestDeps {
@@ -1,5 +1,5 @@
1
- import type { LanguageModel, EmbeddingModel, Tool } from 'ai';
2
- import type { Sql } from 'postgres';
1
+ import type { LanguageModel, EmbeddingModel, Tool } from '../vendor.ts';
2
+ import type { Sql } from '../vendor.ts';
3
3
  import type { RunParams, RunResult, KnowledgeDoc } from './types.ts';
4
4
  interface RunnerDeps {
5
5
  sql: Sql<{}>;
@@ -1,4 +1,4 @@
1
- import type { LanguageModel, EmbeddingModel, Tool } from 'ai';
1
+ import type { LanguageModel, EmbeddingModel, Tool } from '../vendor.ts';
2
2
  export interface AgentConfig {
3
3
  id: number;
4
4
  tenant_id: string | null;
@@ -36,7 +36,7 @@ export type RunResult = {
36
36
  stream: ReadableStream<Uint8Array>;
37
37
  };
38
38
  export interface AgentOptions {
39
- pg: any;
39
+ pg: import('../postgres/types.ts').PostgresClient;
40
40
  model?: LanguageModel;
41
41
  embeddingModel?: EmbeddingModel;
42
42
  embeddingDimension?: number;
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ export type { AIHandler } from './ai.ts';
26
26
  export { tool, createWorkflowEngine, createSSEManager, generateWorkflow, workflow } from './workflow/index.ts';
27
27
  export type { Tool, Workflow, WorkflowEngine, WorkflowState, SSEEvent, WorkflowOptions, WorkflowHandler } from './workflow/index.ts';
28
28
  export { postgres } from './postgres/index.ts';
29
- export type { PostgresOptions, PostgresClient, TableProxy, ListOptions, TableBuilder } from './postgres/types.ts';
29
+ export type { PostgresOptions, PostgresClient } from './postgres/types.ts';
30
30
  export { user } from './user/index.ts';
31
31
  export type { UserOptions, UserData, UserModule, OAuth2Client } from './user/types.ts';
32
32
  export { redis } from './redis/index.ts';
@@ -41,3 +41,5 @@ export { messager } from './messager/index.ts';
41
41
  export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message } from './messager/types.ts';
42
42
  export { deploy, defineConfig } from './deploy/index.ts';
43
43
  export type { DeployConfig, AppConfig, DeployServer, AppStatus } from './deploy/types.ts';
44
+ export { opencode } from './opencode/index.ts';
45
+ export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession } from './opencode/types.ts';