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 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
- - **PostgreSQL** — `postgres()` — zod-to-DDL, auto-migration, 6 CRUD methods, `ctx.sql` escape hatch
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,2 @@
1
+ import type { AgentOptions, AgentModule } from './types.ts';
2
+ export declare function agent(options: AgentOptions): AgentModule;
@@ -0,0 +1,2 @@
1
+ export { agent } from './client.ts';
2
+ export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult } from './types.ts';
@@ -0,0 +1,6 @@
1
+ import type { Sql } from 'postgres';
2
+ export interface MigrateOptions {
3
+ sql: Sql<{}>;
4
+ embeddingDimension: number;
5
+ }
6
+ export declare function migrate(opts: MigrateOptions): Promise<void>;
@@ -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,2 @@
1
+ import type { DeployConfig } from './types.ts';
2
+ export declare function defineConfig(config: DeployConfig): DeployConfig;
@@ -0,0 +1,2 @@
1
+ import type { DeployConfig, GatewayResult } from './types.ts';
2
+ export declare function createGateway(config: DeployConfig, getPort: (name: string) => number | undefined): GatewayResult;
@@ -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';