weifuwu 0.6.0 → 0.8.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 +337 -1
- 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/deploy/config.d.ts +2 -0
- package/dist/deploy/gateway.d.ts +2 -0
- package/dist/deploy/index.d.ts +4 -0
- package/dist/deploy/manager.d.ts +16 -0
- package/dist/deploy/process.d.ts +14 -0
- package/dist/deploy/types.d.ts +62 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +11771 -208
- 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/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/package.json +2 -1
package/README.md
CHANGED
|
@@ -19,13 +19,16 @@ 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
|
|
23
25
|
- **Redis** — `redis()` — ioredis client, `ctx.redis`, middleware
|
|
24
26
|
- **Queue** — `queue()` — Redis-backed job queue with immediate, delayed, and cron scheduling
|
|
25
27
|
- **Auth** — `user()` — register/login/JWT + OAuth2 Server (authorization code + PKCE + client_credentials)
|
|
26
28
|
- **Static files** — `serveStatic()` with ETag, 304, MIME, directory index
|
|
27
29
|
- **Cookie** — `getCookies()`, `setCookie()`, `deleteCookie()` — immutable
|
|
28
30
|
- **Error handling** — global `onError()`
|
|
31
|
+
- **Deploy** — `deploy()` — self-hosted PaaS: multi-app reverse proxy, subdomain routing, zero-downtime updates, auto SSL, Git-based deployment
|
|
29
32
|
- **Zero build** — native TypeScript in Node.js v24+
|
|
30
33
|
- **Zero deps** (core) — only `node:http` and `node:stream`
|
|
31
34
|
|
|
@@ -363,6 +366,263 @@ app.get('/api', auth({ verify: (token) => auth.verify(token) }), handler)
|
|
|
363
366
|
|
|
364
367
|
For `client_credentials` tokens (machine-to-machine), `verify()` returns `null` since no user is associated.
|
|
365
368
|
|
|
369
|
+
## Tenant BaaS
|
|
370
|
+
|
|
371
|
+
Built-in multi-tenant backend-as-a-service — define tables at runtime via API, get RESTful CRUD + GraphQL automatically, with row-level tenant isolation.
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import { serve, Router, postgres, user, tenant } from 'weifuwu'
|
|
375
|
+
|
|
376
|
+
const pg = postgres()
|
|
377
|
+
const u = user({ pg, jwtSecret: process.env.JWT_SECRET! })
|
|
378
|
+
const t = tenant({ pg, usersTable: '_users' })
|
|
379
|
+
|
|
380
|
+
await pg.migrate()
|
|
381
|
+
await u.migrate()
|
|
382
|
+
await t.migrate() // creates _tenants, _tenant_members, _user_tables
|
|
383
|
+
|
|
384
|
+
const app = new Router()
|
|
385
|
+
app.use('/auth', u.router())
|
|
386
|
+
app.use('/api', u.middleware()) // → ctx.user
|
|
387
|
+
app.use('/api', t.middleware()) // → ctx.tenant
|
|
388
|
+
app.use('/api', t.router()) // → management + data CRUD
|
|
389
|
+
app.use('/graphql', t.graphql()) // → dynamic GraphQL
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### System tables
|
|
393
|
+
|
|
394
|
+
| Table | Purpose |
|
|
395
|
+
|-------|---------|
|
|
396
|
+
| `_tenants` | Tenant records (`id TEXT PK DEFAULT gen_random_uuid()`, `name`, `created_at`) |
|
|
397
|
+
| `_tenant_members` | User-tenant membership (`tenant_id`, `user_id`, `role`) |
|
|
398
|
+
| `_user_tables` | Dynamic table definitions (`tenant_id`, `slug`, `fields JSONB`) |
|
|
399
|
+
|
|
400
|
+
### Dynamic table API
|
|
401
|
+
|
|
402
|
+
Create a table at runtime:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
POST /api/tables
|
|
406
|
+
{
|
|
407
|
+
"slug": "articles",
|
|
408
|
+
"fields": [
|
|
409
|
+
{ "name": "title", "type": "string", "required": true },
|
|
410
|
+
{ "name": "content", "type": "text" },
|
|
411
|
+
{ "name": "status", "type": "enum", "options": ["draft", "published"], "default": "draft" },
|
|
412
|
+
{ "name": "views", "type": "integer", "default": 0 },
|
|
413
|
+
{ "name": "embedding", "type": "vector", "dimensions": 1536, "index": "hnsw" }
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
→ 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.
|
|
419
|
+
|
|
420
|
+
### Field types
|
|
421
|
+
|
|
422
|
+
| type | PostgreSQL | Index support |
|
|
423
|
+
|------|-----------|---------------|
|
|
424
|
+
| `string` | `TEXT` | `true`, `unique` |
|
|
425
|
+
| `integer` | `INTEGER` | `true`, `desc`, `unique` |
|
|
426
|
+
| `float` | `DOUBLE PRECISION` | `true`, `desc` |
|
|
427
|
+
| `boolean` | `BOOLEAN` | `true` |
|
|
428
|
+
| `text` | `TEXT` | `true` |
|
|
429
|
+
| `datetime` | `TIMESTAMPTZ` | `true`, `desc` |
|
|
430
|
+
| `date` | `DATE` | `true`, `desc` |
|
|
431
|
+
| `enum` | `TEXT` (with validation) | `true` |
|
|
432
|
+
| `json` | `JSONB` | `gin` |
|
|
433
|
+
| `vector` | `vector(n)` (pgvector) | `hnsw` (HNSW, vector_cosine_ops) |
|
|
434
|
+
|
|
435
|
+
### Relationships
|
|
436
|
+
|
|
437
|
+
Declare a foreign key via the `relation` field:
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{ "name": "article_id", "type": "integer", "relation": { "table": "articles", "onDelete": "cascade" } }
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Supported relationship patterns:
|
|
444
|
+
|
|
445
|
+
| Pattern | Detection | REST | GraphQL |
|
|
446
|
+
|---------|-----------|------|---------|
|
|
447
|
+
| **belongs_to** | Field with `relation` | — | `comment.article` resolver |
|
|
448
|
+
| **has_many** | Another table has a relation pointing here | `GET /api/articles/:id/comments` | `article.comments` resolver |
|
|
449
|
+
| **M2M** | Junction table with exactly two relation fields | `GET /api/articles/:id/tags` (bypasses junction) | `article.tags` / `tag.articles` resolver |
|
|
450
|
+
| **Self-ref** | Relation field pointing to same table | — | With depth control |
|
|
451
|
+
|
|
452
|
+
### RESTful API
|
|
453
|
+
|
|
454
|
+
All routes require `ctx.tenant` (set by `t.middleware()`). All queries automatically filter by `tenant_id`.
|
|
455
|
+
|
|
456
|
+
| Route | Method | Description |
|
|
457
|
+
|-------|--------|-------------|
|
|
458
|
+
| `/sys/tenants` | POST | Create tenant, caller becomes admin |
|
|
459
|
+
| `/sys/tenants` | GET | List user's tenants |
|
|
460
|
+
| `/sys/tenants/invite` | POST | Invite user by email (admin) |
|
|
461
|
+
| `/sys/tenants/members/:userId` | DELETE | Remove member (admin) |
|
|
462
|
+
| `/sys/tables` | POST/GET | Create / list dynamic tables |
|
|
463
|
+
| `/sys/tables/:slug` | GET/PATCH/DELETE | Get schema / add fields / drop table |
|
|
464
|
+
| `/:slug` | GET | List rows (limit, offset, sort) |
|
|
465
|
+
| `/:slug` | POST | Create row |
|
|
466
|
+
| `/:slug/:id` | GET/PATCH/DELETE | Get / update / delete row |
|
|
467
|
+
| `/:slug/:id/:_nested` | GET | List related rows (has_many / M2M) |
|
|
468
|
+
| `/:slug/:id/:_nested` | POST | Create related row (auto-fills relation field) |
|
|
469
|
+
|
|
470
|
+
### Vector search
|
|
471
|
+
|
|
472
|
+
```http
|
|
473
|
+
GET /api/articles?search_vector=[0.1,0.2,...]&search_field=embedding&search_limit=10
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Returns rows ordered by cosine distance (`<=>`), includes `_distance` field. Supports `l2` (`<->`) and `ip` (`<#>`):
|
|
477
|
+
|
|
478
|
+
```http
|
|
479
|
+
GET /api/articles?search_vector=[...]&search_field=embedding&search_distance=l2
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### GraphQL
|
|
483
|
+
|
|
484
|
+
Dynamic GraphQL schema generated per-request based on the authenticated tenant's tables:
|
|
485
|
+
|
|
486
|
+
```graphql
|
|
487
|
+
type Article {
|
|
488
|
+
id: ID!
|
|
489
|
+
title: String!
|
|
490
|
+
content: String
|
|
491
|
+
status: String
|
|
492
|
+
comments(limit: Int, offset: Int): [Comment!]!
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
type Query {
|
|
496
|
+
articles(limit: Int, offset: Int): [Article!]!
|
|
497
|
+
getArticle(id: ID!): Article
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
type Mutation {
|
|
501
|
+
createArticle(data: CreateArticleInput!): Article!
|
|
502
|
+
updateArticle(id: ID!, data: PatchArticleInput!): Article!
|
|
503
|
+
deleteArticle(id: ID!): Boolean!
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Built with `graphql-js` native constructors (`GraphQLObjectType`), no SDL generation, no `makeExecutableSchema`.
|
|
508
|
+
|
|
509
|
+
### Middleware
|
|
510
|
+
|
|
511
|
+
`t.middleware()` extracts the tenant context:
|
|
512
|
+
|
|
513
|
+
1. Requires `ctx.user` (from `u.middleware()`)
|
|
514
|
+
2. Looks up user's tenant memberships
|
|
515
|
+
3. Single tenant → automatically set `ctx.tenant`
|
|
516
|
+
4. Multiple tenants → require `X-Tenant-ID` header, return 300 with tenant list if missing
|
|
517
|
+
5. No tenants → 403
|
|
518
|
+
|
|
519
|
+
### Tenant lifecycle
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
const t = tenant({ pg, usersTable: '_users' })
|
|
523
|
+
|
|
524
|
+
// Create a tenant — the caller becomes admin
|
|
525
|
+
const tenant = await (await fetch('http://localhost/api/sys/tenants', {
|
|
526
|
+
method: 'POST',
|
|
527
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer <jwt>' },
|
|
528
|
+
body: JSON.stringify({ name: 'Acme Corp' }),
|
|
529
|
+
})).json()
|
|
530
|
+
// → { id: "uuid", name: "Acme Corp", created_at: "..." }
|
|
531
|
+
|
|
532
|
+
// Invite a member
|
|
533
|
+
await fetch('http://localhost/api/sys/tenants/invite', {
|
|
534
|
+
method: 'POST',
|
|
535
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer <jwt>' },
|
|
536
|
+
body: JSON.stringify({ email: 'colleague@acme.com', role: 'member' }),
|
|
537
|
+
})
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
## AI Agent
|
|
541
|
+
|
|
542
|
+
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.
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
import { agent } from 'weifuwu'
|
|
546
|
+
|
|
547
|
+
const agents = agent({ pg })
|
|
548
|
+
|
|
549
|
+
await agents.migrate()
|
|
550
|
+
app.use('/api', agents.router())
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
| Type | Description | Execution |
|
|
554
|
+
|------|-------------|-----------|
|
|
555
|
+
| `chat` | Pure conversation | `streamText()` / `generateText()` |
|
|
556
|
+
| `workflow` | Tool-calling agent | `streamText({ tools })` |
|
|
557
|
+
|
|
558
|
+
### Knowledge (RAG)
|
|
559
|
+
|
|
560
|
+
Add documents to any agent — `searchKnowledge` tool auto-injected:
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
await agents.addKnowledge(agentId, 'Title', 'Document content...')
|
|
564
|
+
// The agent automatically calls searchKnowledge when answering
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Streaming
|
|
568
|
+
|
|
569
|
+
```http
|
|
570
|
+
POST /agents/:id/run { input: "hello", stream: true }
|
|
571
|
+
→ event-stream (fullStream SSE: text-delta, tool-call, tool-result, finish)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Programmatic API
|
|
575
|
+
|
|
576
|
+
```ts
|
|
577
|
+
const result = await agents.run(agentId, { input: 'hello', stream: false })
|
|
578
|
+
// { output: "Hello!", elapsed: 1234 }
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## Messager
|
|
582
|
+
|
|
583
|
+
Real-time chat with channels, WebSocket, and agent routing.
|
|
584
|
+
|
|
585
|
+
```ts
|
|
586
|
+
import { messager, agent } from 'weifuwu'
|
|
587
|
+
|
|
588
|
+
const agents = agent({ pg })
|
|
589
|
+
const msg = messager({ pg, agents })
|
|
590
|
+
|
|
591
|
+
await msg.migrate()
|
|
592
|
+
app.use('/api', msg.router())
|
|
593
|
+
app.ws('/ws', u.middleware(), msg.wsHandler())
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Channels
|
|
597
|
+
|
|
598
|
+
```http
|
|
599
|
+
POST /channels name, type (channel|dm), members
|
|
600
|
+
GET /channels
|
|
601
|
+
GET /channels/:id
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Messages
|
|
605
|
+
|
|
606
|
+
```http
|
|
607
|
+
GET /channels/:id/messages ?limit=50&before={id}
|
|
608
|
+
POST /channels/:id/messages content, sender_type, type
|
|
609
|
+
POST /channels/:id/read last_message_id
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### WebSocket
|
|
613
|
+
|
|
614
|
+
```json
|
|
615
|
+
{ "type": "message", "channel_id": 1, "content": "Hi" }
|
|
616
|
+
{ "type": "typing", "channel_id": 1, "is_typing": true }
|
|
617
|
+
{ "type": "read", "channel_id": 1, "last_message_id": 42 }
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Programmatic send
|
|
621
|
+
|
|
622
|
+
```ts
|
|
623
|
+
await msg.send(channelId, 'System message', { sender_type: 'system' })
|
|
624
|
+
```
|
|
625
|
+
|
|
366
626
|
## WebSocket
|
|
367
627
|
message(ws, ctx, data) {
|
|
368
628
|
ws.send(`echo: ${data}`)
|
|
@@ -739,6 +999,42 @@ const app = new Router()
|
|
|
739
999
|
.get('/crash', () => { throw new Error('boom') })
|
|
740
1000
|
```
|
|
741
1001
|
|
|
1002
|
+
## Deploy
|
|
1003
|
+
|
|
1004
|
+
See [deploy.md](./deploy.md) for complete documentation — VPS setup, subdomain routing, blue-green zero-downtime, WebSocket bridge, Git webhook, auto SSL, and management API.
|
|
1005
|
+
|
|
1006
|
+
Quick start on a fresh VPS:
|
|
1007
|
+
|
|
1008
|
+
```bash
|
|
1009
|
+
# 1. Install Node.js
|
|
1010
|
+
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
|
|
1011
|
+
apt-get install -y nodejs git
|
|
1012
|
+
|
|
1013
|
+
# 2. Create deploy project
|
|
1014
|
+
mkdir -p /opt/deploy && cd /opt/deploy
|
|
1015
|
+
npm init -y && npm install weifuwu
|
|
1016
|
+
|
|
1017
|
+
# 3. Write deploy.ts
|
|
1018
|
+
cat > deploy.ts << 'EOF'
|
|
1019
|
+
import { deploy, defineConfig } from 'weifuwu'
|
|
1020
|
+
await deploy(defineConfig({
|
|
1021
|
+
domain: 'example.com',
|
|
1022
|
+
deployToken: process.env.DEPLOY_TOKEN,
|
|
1023
|
+
apps: {
|
|
1024
|
+
blog: {
|
|
1025
|
+
repo: 'https://github.com/me/my-blog.git',
|
|
1026
|
+
subdomain: 'blog',
|
|
1027
|
+
entry: 'app.ts',
|
|
1028
|
+
port: 3001,
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
}))
|
|
1032
|
+
EOF
|
|
1033
|
+
|
|
1034
|
+
# 4. Run
|
|
1035
|
+
DEPLOY_TOKEN='my-secret' node deploy.ts
|
|
1036
|
+
```
|
|
1037
|
+
|
|
742
1038
|
## API
|
|
743
1039
|
|
|
744
1040
|
### `serve(handler, options?)`
|
|
@@ -764,6 +1060,36 @@ Returns `{ stop, port, hostname, ready }`.
|
|
|
764
1060
|
|
|
765
1061
|
Returns `UserModule` — `{ router, middleware, migrate, register, login, verify, registerClient, getClient, revokeClient, close }`.
|
|
766
1062
|
|
|
1063
|
+
### `tenant(options)`
|
|
1064
|
+
|
|
1065
|
+
| Option | Default | Description |
|
|
1066
|
+
|--------|---------|-------------|
|
|
1067
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1068
|
+
| `usersTable` | — | Users table name (matching the `table` option passed to `user()`) |
|
|
1069
|
+
|
|
1070
|
+
Returns `TenantModule` — `{ migrate, middleware, router, graphql, close }`.
|
|
1071
|
+
|
|
1072
|
+
### `agent(options)`
|
|
1073
|
+
|
|
1074
|
+
| Option | Default | Description |
|
|
1075
|
+
|--------|---------|-------------|
|
|
1076
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1077
|
+
| `model` | env `OPENAI_MODEL` → Ollama | `LanguageModel` from ai SDK |
|
|
1078
|
+
| `embeddingModel` | env `OPENAI_EMBEDDING_MODEL` → Ollama | `EmbeddingModel` for knowledge RAG |
|
|
1079
|
+
| `embeddingDimension` | `1024` | Vector dimension for pgvector |
|
|
1080
|
+
| `tools` | — | Tools for workflow-type agents (ai SDK `Tool` objects) |
|
|
1081
|
+
|
|
1082
|
+
Returns `AgentModule` — `{ migrate, router, run, addKnowledge, close }`.
|
|
1083
|
+
|
|
1084
|
+
### `messager(options)`
|
|
1085
|
+
|
|
1086
|
+
| Option | Default | Description |
|
|
1087
|
+
|--------|---------|-------------|
|
|
1088
|
+
| `pg` | — | PostgreSQL client from `postgres()` |
|
|
1089
|
+
| `agents` | — | `AgentModule` instance (enables agent message routing) |
|
|
1090
|
+
|
|
1091
|
+
Returns `MessagerModule` — `{ migrate, router, wsHandler, send, close }`.
|
|
1092
|
+
|
|
767
1093
|
### `tsx(options)`
|
|
768
1094
|
|
|
769
1095
|
| Option | Default | Description |
|
|
@@ -803,10 +1129,20 @@ Returns `Promise<Router>`.
|
|
|
803
1129
|
| `redis(options?)` | Redis client (ioredis) — injects `ctx.redis` |
|
|
804
1130
|
| `queue(options?)` | Redis-backed job queue — immediate, delayed, cron scheduling |
|
|
805
1131
|
| `user(options)` | Built-in authentication (password + OAuth2 Server + JWT, middleware) |
|
|
1132
|
+
| `tenant(options)` | Multi-tenant BaaS — dynamic tables, REST + GraphQL auto-generation, row-level isolation |
|
|
1133
|
+
| `agent(options)` | AI Agent — chat/workflow/knowledge, Ollama-ready, programmatic API |
|
|
1134
|
+
| `messager(options)` | Real-time messaging — channels, WebSocket, agent routing, webhooks |
|
|
806
1135
|
| `graphql(handler)` | GraphQL endpoint (GET/POST + GraphiQL) |
|
|
807
1136
|
| `ai(handler)` | AI streaming endpoint (POST) |
|
|
808
1137
|
| `workflow(handler)` | Workflow engine (POST + SSE) |
|
|
809
1138
|
|
|
1139
|
+
### Deploy
|
|
1140
|
+
|
|
1141
|
+
| Import | Description |
|
|
1142
|
+
|--------|-------------|
|
|
1143
|
+
| `deploy(config)` | Start the deployment platform — see [deploy.md](./deploy.md) |
|
|
1144
|
+
| `defineConfig(config)` | Type-safe config helper with validation — see [deploy.md](./deploy.md) |
|
|
1145
|
+
|
|
810
1146
|
### Utilities
|
|
811
1147
|
|
|
812
1148
|
| Function | Description |
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DeployConfig, DeployServer } from './types.ts';
|
|
2
|
+
export { defineConfig } from './config.ts';
|
|
3
|
+
export type { DeployConfig, AppConfig, DeployServer, AppStatus, GatewayResult } from './types.ts';
|
|
4
|
+
export declare function deploy(config: DeployConfig): Promise<DeployServer>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Router } from '../router.ts';
|
|
2
|
+
import type { DeployConfig, AppStatus } from './types.ts';
|
|
3
|
+
export interface AppRuntime {
|
|
4
|
+
config: import('./types.ts').AppConfig;
|
|
5
|
+
status: AppStatus;
|
|
6
|
+
logs: string[];
|
|
7
|
+
process: import('node:child_process').ChildProcess | null;
|
|
8
|
+
currentPort: number;
|
|
9
|
+
startedAt: number | null;
|
|
10
|
+
restartCount: number;
|
|
11
|
+
restartTimer: ReturnType<typeof setTimeout> | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function createManager(config: DeployConfig, apps: Map<string, AppRuntime>, manager: {
|
|
14
|
+
deployApp(name: string): Promise<void>;
|
|
15
|
+
reloadConfig(): Promise<void>;
|
|
16
|
+
}): Router;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
export interface ManagedProcess {
|
|
3
|
+
child: ChildProcess;
|
|
4
|
+
port: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function forkApp(opts: {
|
|
7
|
+
cwd: string;
|
|
8
|
+
entry: string;
|
|
9
|
+
port: number;
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
onLog?: (line: string) => void;
|
|
12
|
+
}): ManagedProcess;
|
|
13
|
+
export declare function stopProcess(mp: ManagedProcess, timeout?: number): Promise<void>;
|
|
14
|
+
export declare function healthCheck(port: number, path?: string): Promise<boolean>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
import type { Handler } from '../types.ts';
|
|
4
|
+
export interface DeployConfig {
|
|
5
|
+
domain: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
ssl?: {
|
|
8
|
+
email: string;
|
|
9
|
+
staging?: boolean;
|
|
10
|
+
};
|
|
11
|
+
deployToken?: string;
|
|
12
|
+
webhookSecret?: string;
|
|
13
|
+
appsDir?: string;
|
|
14
|
+
defaultApp?: string;
|
|
15
|
+
apps: Record<string, AppConfig>;
|
|
16
|
+
}
|
|
17
|
+
export interface AppConfig {
|
|
18
|
+
repo: string;
|
|
19
|
+
branch?: string;
|
|
20
|
+
subdomain?: string;
|
|
21
|
+
path?: string;
|
|
22
|
+
port: number;
|
|
23
|
+
ports?: [number, number];
|
|
24
|
+
entry: string;
|
|
25
|
+
env?: Record<string, string>;
|
|
26
|
+
healthEndpoint?: string;
|
|
27
|
+
buildCommand?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AppStatus {
|
|
30
|
+
name: string;
|
|
31
|
+
status: 'starting' | 'running' | 'stopped' | 'error';
|
|
32
|
+
port: number;
|
|
33
|
+
subdomain?: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
pid?: number;
|
|
36
|
+
uptime?: number;
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface DeployServer {
|
|
40
|
+
stop(): Promise<void>;
|
|
41
|
+
ready: Promise<void>;
|
|
42
|
+
url: string;
|
|
43
|
+
apps: {
|
|
44
|
+
list(): AppStatus[];
|
|
45
|
+
status(name: string): AppStatus | undefined;
|
|
46
|
+
deploy(name: string): Promise<void>;
|
|
47
|
+
restart(name: string): Promise<void>;
|
|
48
|
+
stop(name: string): Promise<void>;
|
|
49
|
+
start(name: string): Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export interface GatewayResult {
|
|
53
|
+
handler: Handler;
|
|
54
|
+
wsHandler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void;
|
|
55
|
+
}
|
|
56
|
+
declare module '../types.ts' {
|
|
57
|
+
interface Context {
|
|
58
|
+
deploy?: {
|
|
59
|
+
appName?: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -33,3 +33,11 @@ export { redis } from './redis/index.ts';
|
|
|
33
33
|
export type { RedisOptions, RedisClient } from './redis/types.ts';
|
|
34
34
|
export { queue } from './queue/index.ts';
|
|
35
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';
|
|
42
|
+
export { deploy, defineConfig } from './deploy/index.ts';
|
|
43
|
+
export type { DeployConfig, AppConfig, DeployServer, AppStatus } from './deploy/types.ts';
|