weifuwu 0.5.1 → 0.7.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/README.md +411 -6
- package/dist/agent/client.d.ts +2 -0
- package/dist/agent/index.d.ts +2 -0
- package/dist/agent/migrate.d.ts +6 -0
- package/dist/agent/rest.d.ts +12 -0
- package/dist/agent/run.d.ts +14 -0
- package/dist/agent/types.d.ts +51 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +24342 -1927
- package/dist/messager/client.d.ts +2 -0
- package/dist/messager/index.d.ts +2 -0
- package/dist/messager/migrate.d.ts +2 -0
- package/dist/messager/rest.d.ts +9 -0
- package/dist/messager/types.d.ts +53 -0
- package/dist/messager/ws.d.ts +9 -0
- package/dist/queue/index.d.ts +2 -0
- package/dist/queue/types.d.ts +32 -0
- package/dist/redis/index.d.ts +3 -0
- package/dist/redis/types.d.ts +16 -0
- package/dist/tenant/client.d.ts +2 -0
- package/dist/tenant/graphql.d.ts +3 -0
- package/dist/tenant/index.d.ts +2 -0
- package/dist/tenant/migrate.d.ts +6 -0
- package/dist/tenant/rest.d.ts +3 -0
- package/dist/tenant/schema.d.ts +5 -0
- package/dist/tenant/types.d.ts +47 -0
- package/dist/tenant/utils.d.ts +10 -0
- package/dist/user/client.d.ts +2 -0
- package/dist/user/index.d.ts +2 -0
- package/dist/user/migrate.d.ts +6 -0
- package/dist/user/oauth2.d.ts +20 -0
- package/dist/user/types.d.ts +54 -0
- package/dist/workflow/engine.d.ts +1 -1
- package/dist/workflow/llm.d.ts +1 -1
- package/dist/workflow/route.d.ts +1 -1
- package/dist/workflow/types.d.ts +2 -2
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -19,7 +19,12 @@ Everything follows the same `(req, ctx) => Response` contract. The Router handle
|
|
|
19
19
|
- **GraphQL** — `graphql(handler)` sub-Router with GraphiQL IDE
|
|
20
20
|
- **AI streaming** — `ai(handler)` sub-Router via Vercel AI SDK
|
|
21
21
|
- **AI workflows** — `workflow(handler)` sub-Router — intent-to-execution pipelines with `tool()` + SSE
|
|
22
|
-
- **
|
|
22
|
+
- **AI Agent** — `agent()` — server-side AI agents with chat/workflow/knowledge types, OpenAI-compatible, Ollama-ready
|
|
23
|
+
- **Messaging** — `messager()` — real-time chat with channels, WebSocket, agent routing, webhook support
|
|
24
|
+
- **Tenant BaaS** — `tenant()` — multi-tenant dynamic tables, auto REST + GraphQL, row-level isolation, pgvector/HNSW
|
|
25
|
+
- **Redis** — `redis()` — ioredis client, `ctx.redis`, middleware
|
|
26
|
+
- **Queue** — `queue()` — Redis-backed job queue with immediate, delayed, and cron scheduling
|
|
27
|
+
- **Auth** — `user()` — register/login/JWT + OAuth2 Server (authorization code + PKCE + client_credentials)
|
|
23
28
|
- **Static files** — `serveStatic()` with ETag, 304, MIME, directory index
|
|
24
29
|
- **Cookie** — `getCookies()`, `setCookie()`, `deleteCookie()` — immutable
|
|
25
30
|
- **Error handling** — global `onError()`
|
|
@@ -258,14 +263,366 @@ await pg.close() // explicit close
|
|
|
258
263
|
| `id: z.string().uuid().optional()` | `UUID PRIMARY KEY DEFAULT gen_random_uuid()` |
|
|
259
264
|
| `id: z.string()` | `TEXT PRIMARY KEY` (you pass the value) |
|
|
260
265
|
|
|
261
|
-
##
|
|
266
|
+
## Authentication
|
|
267
|
+
|
|
268
|
+
Built-in user management — password login, JWT, and OAuth2 Server. Zero config beyond PostgreSQL and a secret key.
|
|
262
269
|
|
|
263
270
|
```ts
|
|
271
|
+
import { serve, Router, postgres, user } from 'weifuwu'
|
|
272
|
+
|
|
264
273
|
const app = new Router()
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
274
|
+
const pg = postgres()
|
|
275
|
+
await pg.migrate()
|
|
276
|
+
|
|
277
|
+
const auth = user({ pg, jwtSecret: process.env.JWT_SECRET! })
|
|
278
|
+
|
|
279
|
+
// POST /auth/register { email, password, name }
|
|
280
|
+
// POST /auth/login { email, password }
|
|
281
|
+
// GET /auth/oauth/authorize?client_id=...&redirect_uri=...&response_type=code
|
|
282
|
+
// POST /auth/oauth/consent
|
|
283
|
+
// POST /auth/oauth/token (grant_type=authorization_code|client_credentials)
|
|
284
|
+
app.use('/auth', auth.router())
|
|
285
|
+
|
|
286
|
+
// Protected routes — verifies JWT, sets ctx.user
|
|
287
|
+
app.get('/me', auth.middleware(), async (req, ctx) => {
|
|
288
|
+
return Response.json(ctx.user)
|
|
289
|
+
// { id, email, name, role }
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Password hashing uses `crypto.scryptSync` + `timingSafeEqual` (Node.js built-in, zero deps). JWT tokens use the `jsonwebtoken` package. The users table (`_users` by default) is auto-created on first `migrate()`.
|
|
294
|
+
|
|
295
|
+
### OAuth2 Server
|
|
296
|
+
|
|
297
|
+
Enable OAuth2 Server to let third-party apps (SPA, mobile, microservices) authenticate users through your app.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const auth = user({
|
|
301
|
+
pg,
|
|
302
|
+
jwtSecret: process.env.JWT_SECRET!,
|
|
303
|
+
oauth2: { server: true },
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
await auth.migrate() // creates _users + _oauth2_clients + _oauth2_codes + _oauth2_tokens
|
|
307
|
+
|
|
308
|
+
// Register a client app (programmatic — CLI, admin UI, seed script)
|
|
309
|
+
const client = await auth.registerClient({
|
|
310
|
+
name: 'My SPA',
|
|
311
|
+
redirectUris: ['https://myapp.com/callback'],
|
|
312
|
+
})
|
|
313
|
+
// → { clientId, clientSecret, name, redirectUris }
|
|
314
|
+
|
|
315
|
+
// Use auth middleware to protect routes — OAuth2 JWT tokens work seamlessly
|
|
316
|
+
app.get('/api/data', auth.middleware(), handler)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Supported Grant Types
|
|
320
|
+
|
|
321
|
+
| Grant | Use Case | PKCE |
|
|
322
|
+
|-------|----------|------|
|
|
323
|
+
| `authorization_code` (with client_secret) | Server-side apps | Optional |
|
|
324
|
+
| `authorization_code` (with `code_challenge`/`code_verifier`) | SPA / Mobile apps | Required |
|
|
325
|
+
| `client_credentials` | Machine-to-machine | — |
|
|
326
|
+
|
|
327
|
+
#### Flow (Authorization Code + PKCE)
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
1. 第三方 App 引导用户:
|
|
331
|
+
GET /oauth/authorize?client_id=xxx&redirect_uri=https://app.com/cb
|
|
332
|
+
&response_type=code&code_challenge=S256&state=yyy
|
|
333
|
+
|
|
334
|
+
2. 用户未登录 → 302 到 /login?redirect=... → 登录后自动回到授权页
|
|
335
|
+
|
|
336
|
+
3. 用户确认授权 → POST /oauth/consent { approve: true, client_id, ... }
|
|
337
|
+
302 redirect_uri?code=xxx&state=yyy
|
|
338
|
+
|
|
339
|
+
4. 第三方 App POST /oauth/token
|
|
340
|
+
{ grant_type: authorization_code, code, client_id, client_secret,
|
|
341
|
+
redirect_uri, code_verifier }
|
|
342
|
+
→ { access_token, token_type: "Bearer", expires_in, refresh_token }
|
|
343
|
+
|
|
344
|
+
5. access_token 是标准 JWT,auth.middleware() 和 auth.verify() 直接可用
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### Client Management
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const client = await auth.registerClient({ name, redirectUris })
|
|
351
|
+
const found = await auth.getClient(client.clientId)
|
|
352
|
+
await auth.revokeClient(client.clientId)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### Using OAuth2 Tokens with the Built-in Auth Middleware
|
|
356
|
+
|
|
357
|
+
OAuth2 Server 签发的 `access_token` 与密码登录的 JWT 使用同一 `jwtSecret`,payload 向下兼容(`sub`、`email`、`role`),所以 `auth()` 无需任何修改即可验证 OAuth2 签发的 token:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { auth } from 'weifuwu'
|
|
361
|
+
|
|
362
|
+
// 同一个 auth() 中间件同时支持密码登录 JWT 和 OAuth2 JWT
|
|
363
|
+
app.get('/api', auth({ verify: (token) => auth.verify(token) }), handler)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
For `client_credentials` tokens (machine-to-machine), `verify()` returns `null` since no user is associated.
|
|
367
|
+
|
|
368
|
+
## Tenant BaaS
|
|
369
|
+
|
|
370
|
+
Built-in multi-tenant backend-as-a-service — define tables at runtime via API, get RESTful CRUD + GraphQL automatically, with row-level tenant isolation.
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
import { serve, Router, postgres, user, tenant } from 'weifuwu'
|
|
374
|
+
|
|
375
|
+
const pg = postgres()
|
|
376
|
+
const u = user({ pg, jwtSecret: process.env.JWT_SECRET! })
|
|
377
|
+
const t = tenant({ pg, usersTable: '_users' })
|
|
378
|
+
|
|
379
|
+
await pg.migrate()
|
|
380
|
+
await u.migrate()
|
|
381
|
+
await t.migrate() // creates _tenants, _tenant_members, _user_tables
|
|
382
|
+
|
|
383
|
+
const app = new Router()
|
|
384
|
+
app.use('/auth', u.router())
|
|
385
|
+
app.use('/api', u.middleware()) // → ctx.user
|
|
386
|
+
app.use('/api', t.middleware()) // → ctx.tenant
|
|
387
|
+
app.use('/api', t.router()) // → management + data CRUD
|
|
388
|
+
app.use('/graphql', t.graphql()) // → dynamic GraphQL
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### System tables
|
|
392
|
+
|
|
393
|
+
| Table | Purpose |
|
|
394
|
+
|-------|---------|
|
|
395
|
+
| `_tenants` | Tenant records (`id TEXT PK DEFAULT gen_random_uuid()`, `name`, `created_at`) |
|
|
396
|
+
| `_tenant_members` | User-tenant membership (`tenant_id`, `user_id`, `role`) |
|
|
397
|
+
| `_user_tables` | Dynamic table definitions (`tenant_id`, `slug`, `fields JSONB`) |
|
|
398
|
+
|
|
399
|
+
### Dynamic table API
|
|
400
|
+
|
|
401
|
+
Create a table at runtime:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
POST /api/tables
|
|
405
|
+
{
|
|
406
|
+
"slug": "articles",
|
|
407
|
+
"fields": [
|
|
408
|
+
{ "name": "title", "type": "string", "required": true },
|
|
409
|
+
{ "name": "content", "type": "text" },
|
|
410
|
+
{ "name": "status", "type": "enum", "options": ["draft", "published"], "default": "draft" },
|
|
411
|
+
{ "name": "views", "type": "integer", "default": 0 },
|
|
412
|
+
{ "name": "embedding", "type": "vector", "dimensions": 1536, "index": "hnsw" }
|
|
413
|
+
]
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
→ Creates a PostgreSQL table with `id SERIAL PK`, `tenant_id TEXT NOT NULL`, and the specified columns, plus indexes. The table name is internally scoped to the tenant.
|
|
418
|
+
|
|
419
|
+
### Field types
|
|
420
|
+
|
|
421
|
+
| type | PostgreSQL | Index support |
|
|
422
|
+
|------|-----------|---------------|
|
|
423
|
+
| `string` | `TEXT` | `true`, `unique` |
|
|
424
|
+
| `integer` | `INTEGER` | `true`, `desc`, `unique` |
|
|
425
|
+
| `float` | `DOUBLE PRECISION` | `true`, `desc` |
|
|
426
|
+
| `boolean` | `BOOLEAN` | `true` |
|
|
427
|
+
| `text` | `TEXT` | `true` |
|
|
428
|
+
| `datetime` | `TIMESTAMPTZ` | `true`, `desc` |
|
|
429
|
+
| `date` | `DATE` | `true`, `desc` |
|
|
430
|
+
| `enum` | `TEXT` (with validation) | `true` |
|
|
431
|
+
| `json` | `JSONB` | `gin` |
|
|
432
|
+
| `vector` | `vector(n)` (pgvector) | `hnsw` (HNSW, vector_cosine_ops) |
|
|
433
|
+
|
|
434
|
+
### Relationships
|
|
435
|
+
|
|
436
|
+
Declare a foreign key via the `relation` field:
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{ "name": "article_id", "type": "integer", "relation": { "table": "articles", "onDelete": "cascade" } }
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Supported relationship patterns:
|
|
443
|
+
|
|
444
|
+
| Pattern | Detection | REST | GraphQL |
|
|
445
|
+
|---------|-----------|------|---------|
|
|
446
|
+
| **belongs_to** | Field with `relation` | — | `comment.article` resolver |
|
|
447
|
+
| **has_many** | Another table has a relation pointing here | `GET /api/articles/:id/comments` | `article.comments` resolver |
|
|
448
|
+
| **M2M** | Junction table with exactly two relation fields | `GET /api/articles/:id/tags` (bypasses junction) | `article.tags` / `tag.articles` resolver |
|
|
449
|
+
| **Self-ref** | Relation field pointing to same table | — | With depth control |
|
|
450
|
+
|
|
451
|
+
### RESTful API
|
|
452
|
+
|
|
453
|
+
All routes require `ctx.tenant` (set by `t.middleware()`). All queries automatically filter by `tenant_id`.
|
|
454
|
+
|
|
455
|
+
| Route | Method | Description |
|
|
456
|
+
|-------|--------|-------------|
|
|
457
|
+
| `/sys/tenants` | POST | Create tenant, caller becomes admin |
|
|
458
|
+
| `/sys/tenants` | GET | List user's tenants |
|
|
459
|
+
| `/sys/tenants/invite` | POST | Invite user by email (admin) |
|
|
460
|
+
| `/sys/tenants/members/:userId` | DELETE | Remove member (admin) |
|
|
461
|
+
| `/sys/tables` | POST/GET | Create / list dynamic tables |
|
|
462
|
+
| `/sys/tables/:slug` | GET/PATCH/DELETE | Get schema / add fields / drop table |
|
|
463
|
+
| `/:slug` | GET | List rows (limit, offset, sort) |
|
|
464
|
+
| `/:slug` | POST | Create row |
|
|
465
|
+
| `/:slug/:id` | GET/PATCH/DELETE | Get / update / delete row |
|
|
466
|
+
| `/:slug/:id/:_nested` | GET | List related rows (has_many / M2M) |
|
|
467
|
+
| `/:slug/:id/:_nested` | POST | Create related row (auto-fills relation field) |
|
|
468
|
+
|
|
469
|
+
### Vector search
|
|
470
|
+
|
|
471
|
+
```http
|
|
472
|
+
GET /api/articles?search_vector=[0.1,0.2,...]&search_field=embedding&search_limit=10
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Returns rows ordered by cosine distance (`<=>`), includes `_distance` field. Supports `l2` (`<->`) and `ip` (`<#>`):
|
|
476
|
+
|
|
477
|
+
```http
|
|
478
|
+
GET /api/articles?search_vector=[...]&search_field=embedding&search_distance=l2
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### GraphQL
|
|
482
|
+
|
|
483
|
+
Dynamic GraphQL schema generated per-request based on the authenticated tenant's tables:
|
|
484
|
+
|
|
485
|
+
```graphql
|
|
486
|
+
type Article {
|
|
487
|
+
id: ID!
|
|
488
|
+
title: String!
|
|
489
|
+
content: String
|
|
490
|
+
status: String
|
|
491
|
+
comments(limit: Int, offset: Int): [Comment!]!
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
type Query {
|
|
495
|
+
articles(limit: Int, offset: Int): [Article!]!
|
|
496
|
+
getArticle(id: ID!): Article
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
type Mutation {
|
|
500
|
+
createArticle(data: CreateArticleInput!): Article!
|
|
501
|
+
updateArticle(id: ID!, data: PatchArticleInput!): Article!
|
|
502
|
+
deleteArticle(id: ID!): Boolean!
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Built with `graphql-js` native constructors (`GraphQLObjectType`), no SDL generation, no `makeExecutableSchema`.
|
|
507
|
+
|
|
508
|
+
### Middleware
|
|
509
|
+
|
|
510
|
+
`t.middleware()` extracts the tenant context:
|
|
511
|
+
|
|
512
|
+
1. Requires `ctx.user` (from `u.middleware()`)
|
|
513
|
+
2. Looks up user's tenant memberships
|
|
514
|
+
3. Single tenant → automatically set `ctx.tenant`
|
|
515
|
+
4. Multiple tenants → require `X-Tenant-ID` header, return 300 with tenant list if missing
|
|
516
|
+
5. No tenants → 403
|
|
517
|
+
|
|
518
|
+
### Tenant lifecycle
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
const t = tenant({ pg, usersTable: '_users' })
|
|
522
|
+
|
|
523
|
+
// Create a tenant — the caller becomes admin
|
|
524
|
+
const tenant = await (await fetch('http://localhost/api/sys/tenants', {
|
|
525
|
+
method: 'POST',
|
|
526
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer <jwt>' },
|
|
527
|
+
body: JSON.stringify({ name: 'Acme Corp' }),
|
|
528
|
+
})).json()
|
|
529
|
+
// → { id: "uuid", name: "Acme Corp", created_at: "..." }
|
|
530
|
+
|
|
531
|
+
// Invite a member
|
|
532
|
+
await fetch('http://localhost/api/sys/tenants/invite', {
|
|
533
|
+
method: 'POST',
|
|
534
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer <jwt>' },
|
|
535
|
+
body: JSON.stringify({ email: 'colleague@acme.com', role: 'member' }),
|
|
536
|
+
})
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
## AI Agent
|
|
540
|
+
|
|
541
|
+
Server-side AI agents with OpenAI-compatible API. Built-in chat, workflow (tool-calling), and knowledge (RAG) types. Works out of the box with Ollama or any OpenAI-compatible provider.
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
import { agent } from 'weifuwu'
|
|
545
|
+
|
|
546
|
+
const agents = agent({ pg })
|
|
547
|
+
|
|
548
|
+
await agents.migrate()
|
|
549
|
+
app.use('/api', agents.router())
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
| Type | Description | Execution |
|
|
553
|
+
|------|-------------|-----------|
|
|
554
|
+
| `chat` | Pure conversation | `streamText()` / `generateText()` |
|
|
555
|
+
| `workflow` | Tool-calling agent | `streamText({ tools })` |
|
|
556
|
+
|
|
557
|
+
### Knowledge (RAG)
|
|
558
|
+
|
|
559
|
+
Add documents to any agent — `searchKnowledge` tool auto-injected:
|
|
560
|
+
|
|
561
|
+
```ts
|
|
562
|
+
await agents.addKnowledge(agentId, 'Title', 'Document content...')
|
|
563
|
+
// The agent automatically calls searchKnowledge when answering
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Streaming
|
|
567
|
+
|
|
568
|
+
```http
|
|
569
|
+
POST /agents/:id/run { input: "hello", stream: true }
|
|
570
|
+
→ event-stream (fullStream SSE: text-delta, tool-call, tool-result, finish)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Programmatic API
|
|
574
|
+
|
|
575
|
+
```ts
|
|
576
|
+
const result = await agents.run(agentId, { input: 'hello', stream: false })
|
|
577
|
+
// { output: "Hello!", elapsed: 1234 }
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## Messager
|
|
581
|
+
|
|
582
|
+
Real-time chat with channels, WebSocket, and agent routing.
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
import { messager, agent } from 'weifuwu'
|
|
586
|
+
|
|
587
|
+
const agents = agent({ pg })
|
|
588
|
+
const msg = messager({ pg, agents })
|
|
589
|
+
|
|
590
|
+
await msg.migrate()
|
|
591
|
+
app.use('/api', msg.router())
|
|
592
|
+
app.ws('/ws', u.middleware(), msg.wsHandler())
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Channels
|
|
596
|
+
|
|
597
|
+
```http
|
|
598
|
+
POST /channels name, type (channel|dm), members
|
|
599
|
+
GET /channels
|
|
600
|
+
GET /channels/:id
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Messages
|
|
604
|
+
|
|
605
|
+
```http
|
|
606
|
+
GET /channels/:id/messages ?limit=50&before={id}
|
|
607
|
+
POST /channels/:id/messages content, sender_type, type
|
|
608
|
+
POST /channels/:id/read last_message_id
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### WebSocket
|
|
612
|
+
|
|
613
|
+
```json
|
|
614
|
+
{ "type": "message", "channel_id": 1, "content": "Hi" }
|
|
615
|
+
{ "type": "typing", "channel_id": 1, "is_typing": true }
|
|
616
|
+
{ "type": "read", "channel_id": 1, "last_message_id": 42 }
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Programmatic send
|
|
620
|
+
|
|
621
|
+
```ts
|
|
622
|
+
await msg.send(channelId, 'System message', { sender_type: 'system' })
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
## WebSocket
|
|
269
626
|
message(ws, ctx, data) {
|
|
270
627
|
ws.send(`echo: ${data}`)
|
|
271
628
|
},
|
|
@@ -654,6 +1011,48 @@ const app = new Router()
|
|
|
654
1011
|
|
|
655
1012
|
Returns `{ stop, port, hostname, ready }`.
|
|
656
1013
|
|
|
1014
|
+
### `user(options)`
|
|
1015
|
+
|
|
1016
|
+
| Option | Default | Description |
|
|
1017
|
+
|--------|---------|-------------|
|
|
1018
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1019
|
+
| `jwtSecret` | — | Secret key for JWT signing |
|
|
1020
|
+
| `table` | `'_users'` | Users table name |
|
|
1021
|
+
| `expiresIn` | `'24h'` | JWT expiration |
|
|
1022
|
+
| `oauth2.server` | `false` | Enable OAuth2 Server |
|
|
1023
|
+
|
|
1024
|
+
Returns `UserModule` — `{ router, middleware, migrate, register, login, verify, registerClient, getClient, revokeClient, close }`.
|
|
1025
|
+
|
|
1026
|
+
### `tenant(options)`
|
|
1027
|
+
|
|
1028
|
+
| Option | Default | Description |
|
|
1029
|
+
|--------|---------|-------------|
|
|
1030
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1031
|
+
| `usersTable` | — | Users table name (matching the `table` option passed to `user()`) |
|
|
1032
|
+
|
|
1033
|
+
Returns `TenantModule` — `{ migrate, middleware, router, graphql, close }`.
|
|
1034
|
+
|
|
1035
|
+
### `agent(options)`
|
|
1036
|
+
|
|
1037
|
+
| Option | Default | Description |
|
|
1038
|
+
|--------|---------|-------------|
|
|
1039
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1040
|
+
| `model` | env `OPENAI_MODEL` → Ollama | `LanguageModel` from ai SDK |
|
|
1041
|
+
| `embeddingModel` | env `OPENAI_EMBEDDING_MODEL` → Ollama | `EmbeddingModel` for knowledge RAG |
|
|
1042
|
+
| `embeddingDimension` | `1024` | Vector dimension for pgvector |
|
|
1043
|
+
| `tools` | — | Tools for workflow-type agents (ai SDK `Tool` objects) |
|
|
1044
|
+
|
|
1045
|
+
Returns `AgentModule` — `{ migrate, router, run, addKnowledge, close }`.
|
|
1046
|
+
|
|
1047
|
+
### `messager(options)`
|
|
1048
|
+
|
|
1049
|
+
| Option | Default | Description |
|
|
1050
|
+
|--------|---------|-------------|
|
|
1051
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1052
|
+
| `agents` | — | `AgentModule` instance (enables agent message routing) |
|
|
1053
|
+
|
|
1054
|
+
Returns `MessagerModule` — `{ migrate, router, wsHandler, send, close }`.
|
|
1055
|
+
|
|
657
1056
|
### `tsx(options)`
|
|
658
1057
|
|
|
659
1058
|
| Option | Default | Description |
|
|
@@ -690,6 +1089,12 @@ Returns `Promise<Router>`.
|
|
|
690
1089
|
| Import | Description |
|
|
691
1090
|
|--------|-------------|
|
|
692
1091
|
| `postgres(options?)` | PostgreSQL connection + auto-migration + 6 CRUD methods |
|
|
1092
|
+
| `redis(options?)` | Redis client (ioredis) — injects `ctx.redis` |
|
|
1093
|
+
| `queue(options?)` | Redis-backed job queue — immediate, delayed, cron scheduling |
|
|
1094
|
+
| `user(options)` | Built-in authentication (password + OAuth2 Server + JWT, middleware) |
|
|
1095
|
+
| `tenant(options)` | Multi-tenant BaaS — dynamic tables, REST + GraphQL auto-generation, row-level isolation |
|
|
1096
|
+
| `agent(options)` | AI Agent — chat/workflow/knowledge, Ollama-ready, programmatic API |
|
|
1097
|
+
| `messager(options)` | Real-time messaging — channels, WebSocket, agent routing, webhooks |
|
|
693
1098
|
| `graphql(handler)` | GraphQL endpoint (GET/POST + GraphiQL) |
|
|
694
1099
|
| `ai(handler)` | AI streaming endpoint (POST) |
|
|
695
1100
|
| `workflow(handler)` | Workflow engine (POST + SSE) |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Sql } from 'postgres';
|
|
2
|
+
import { Router } from '../router.ts';
|
|
3
|
+
import type { RunParams } from './types.ts';
|
|
4
|
+
interface RestDeps {
|
|
5
|
+
sql: Sql<{}>;
|
|
6
|
+
runner: {
|
|
7
|
+
run: (agentId: number, params: RunParams) => Promise<any>;
|
|
8
|
+
addKnowledge: (agentId: number, title: string, content: string) => Promise<any>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare function buildRouter(deps: RestDeps): Router;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LanguageModel, EmbeddingModel, Tool } from 'ai';
|
|
2
|
+
import type { Sql } from 'postgres';
|
|
3
|
+
import type { RunParams, RunResult, KnowledgeDoc } from './types.ts';
|
|
4
|
+
interface RunnerDeps {
|
|
5
|
+
sql: Sql<{}>;
|
|
6
|
+
getModel: () => LanguageModel;
|
|
7
|
+
getEmbeddingModel: () => EmbeddingModel;
|
|
8
|
+
userTools?: Record<string, Tool>;
|
|
9
|
+
}
|
|
10
|
+
export declare function createRunner(deps: RunnerDeps): {
|
|
11
|
+
run: (agentId: number, params: RunParams) => Promise<RunResult>;
|
|
12
|
+
addKnowledge: (agentId: number, title: string, content: string) => Promise<KnowledgeDoc>;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { LanguageModel, EmbeddingModel, Tool } from 'ai';
|
|
2
|
+
export interface AgentConfig {
|
|
3
|
+
id: number;
|
|
4
|
+
tenant_id: string | null;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
type: 'chat' | 'workflow';
|
|
8
|
+
model: string;
|
|
9
|
+
system_prompt: string;
|
|
10
|
+
owner_id: number;
|
|
11
|
+
active: boolean;
|
|
12
|
+
created_at: string;
|
|
13
|
+
updated_at: string;
|
|
14
|
+
}
|
|
15
|
+
export interface KnowledgeDoc {
|
|
16
|
+
id: number;
|
|
17
|
+
agent_id: number;
|
|
18
|
+
title: string;
|
|
19
|
+
content: string;
|
|
20
|
+
embedding?: number[];
|
|
21
|
+
metadata: Record<string, unknown>;
|
|
22
|
+
created_at: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RunParams {
|
|
25
|
+
input: string;
|
|
26
|
+
stream?: boolean;
|
|
27
|
+
messages?: Array<{
|
|
28
|
+
role: string;
|
|
29
|
+
content: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export type RunResult = {
|
|
33
|
+
output: string;
|
|
34
|
+
elapsed: number;
|
|
35
|
+
} | {
|
|
36
|
+
stream: ReadableStream<Uint8Array>;
|
|
37
|
+
};
|
|
38
|
+
export interface AgentOptions {
|
|
39
|
+
pg: any;
|
|
40
|
+
model?: LanguageModel;
|
|
41
|
+
embeddingModel?: EmbeddingModel;
|
|
42
|
+
embeddingDimension?: number;
|
|
43
|
+
tools?: Record<string, Tool>;
|
|
44
|
+
}
|
|
45
|
+
export interface AgentModule {
|
|
46
|
+
migrate: () => Promise<void>;
|
|
47
|
+
router: () => any;
|
|
48
|
+
run: (agentId: number, params: RunParams) => Promise<RunResult>;
|
|
49
|
+
addKnowledge: (agentId: number, title: string, content: string) => Promise<KnowledgeDoc>;
|
|
50
|
+
close: () => Promise<void>;
|
|
51
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,3 +27,15 @@ export { tool, createWorkflowEngine, createSSEManager, generateWorkflow, workflo
|
|
|
27
27
|
export type { Tool, Workflow, WorkflowEngine, WorkflowState, SSEEvent, WorkflowOptions, WorkflowHandler } from './workflow/index.ts';
|
|
28
28
|
export { postgres } from './postgres/index.ts';
|
|
29
29
|
export type { PostgresOptions, PostgresClient, TableProxy, ListOptions, TableBuilder } from './postgres/types.ts';
|
|
30
|
+
export { user } from './user/index.ts';
|
|
31
|
+
export type { UserOptions, UserData, UserModule, OAuth2Client } from './user/types.ts';
|
|
32
|
+
export { redis } from './redis/index.ts';
|
|
33
|
+
export type { RedisOptions, RedisClient } from './redis/types.ts';
|
|
34
|
+
export { queue } from './queue/index.ts';
|
|
35
|
+
export type { QueueOptions, QueueJob, Queue } from './queue/types.ts';
|
|
36
|
+
export { tenant } from './tenant/index.ts';
|
|
37
|
+
export type { TenantOptions, TenantModule, TenantContext, FieldDef, FieldType, RelationDef, UserTableRow } from './tenant/types.ts';
|
|
38
|
+
export { agent } from './agent/index.ts';
|
|
39
|
+
export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult, KnowledgeDoc } from './agent/types.ts';
|
|
40
|
+
export { messager } from './messager/index.ts';
|
|
41
|
+
export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message } from './messager/types.ts';
|