@tool-bridge/server 0.1.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.
@@ -0,0 +1,764 @@
1
+ import * as http from 'node:http';
2
+ import { Hono } from 'hono';
3
+
4
+ /**
5
+ * 公共类型。
6
+ *
7
+ * 原样转写规范中的 TS 定义;此处仅补充实现所需的最小派生(如 ACTIONS 常量表)。
8
+ */
9
+ /** 资源 URI。 */
10
+ type URI = string;
11
+ /** 树上路径,'/' 分隔,不含保留段;如 "docs/context7"。 */
12
+ type TreePath = string;
13
+ /** ISO 8601, UTC。 */
14
+ type Timestamp = string;
15
+ interface Page<T> {
16
+ /** 存在则表示还有下一页;传回 List 继续。 */
17
+ cursor?: string;
18
+ items: T[];
19
+ }
20
+ interface ListOptions {
21
+ cursor?: string;
22
+ /** 键集合由各接口 ~help 声明;未声明的键 → invalid_argument。 */
23
+ filter?: Record<string, string>;
24
+ /** 规范默认 50、上限 200,超上限静默钳制。 */
25
+ limit?: number;
26
+ }
27
+ /** "user:alice" | "agent:researcher" | "device:build-01"。 */
28
+ type OwnerRef = string;
29
+ interface CallContext {
30
+ /** 平台→Plugin envelope 专用:本次调用命中该 plugin 的哪个 export(v2 多 export 路由)。 */
31
+ exportId?: string;
32
+ /** 本次调用使用的 SK 的 id(非明文)。 */
33
+ keyId: string;
34
+ /** 平台→Plugin envelope 专用:挂载节点 config.providerConfig 透传(每挂载非敏感配置)。 */
35
+ mountConfig?: Record<string, unknown>;
36
+ /** 平台→Plugin envelope 专用:本次调用来自哪个挂载节点(同一 plugin 可多路径挂载)。 */
37
+ mountPath?: TreePath;
38
+ owner: OwnerRef;
39
+ /** 反向注册路径收紧规则;缺省按非保留根放行。 */
40
+ registerPaths?: TreePath[];
41
+ scopes: Scope[];
42
+ /** 全链路观测。 */
43
+ traceId: string;
44
+ }
45
+ type Action = 'read' | 'write' | 'call' | 'register' | 'admin';
46
+ interface Scope {
47
+ actions: Action[];
48
+ /** 默认 allow;deny 优先于一切 allow。 */
49
+ effect?: 'allow' | 'deny';
50
+ /** 树路径 glob:"**" | "docs/**" | "device/build-01/**"。 */
51
+ pattern: string;
52
+ }
53
+
54
+ /**
55
+ * StateStore:宿主注入的状态存储接口。
56
+ *
57
+ * CF = KV / Docker = SQLite / SDK 内嵌 = 内存。core 只依赖此接口;
58
+ * 一切树配置、SK 哈希表、加密 secret 都经它读写。异步签名以兼容 KV。
59
+ *
60
+ * key 布局:
61
+ * sk:h:<sha256hex> → SecretKey(认证热路径)
62
+ * sk:i:<id> → sha256hex(管理面二级索引,指向 sk:h:*)
63
+ * node:<path> → TreeNode
64
+ * secret:<name> → { iv, ciphertext, updatedAt }
65
+ * plugin:<id> → PluginManifest
66
+ * sys:bootstrapped → true(Admin SK 引导幂等标志)
67
+ * annotation:<path> → { text, updatedAt, updatedBy }(管理员 Path 补充说明)
68
+ * feedback:<path> → FeedbackEntry[](Agent 使用反馈,单 key 整存)
69
+ */
70
+ interface StateStore {
71
+ delete(key: string): Promise<void>;
72
+ get(key: string): Promise<unknown | null>;
73
+ /** 批量读取,返回值只包含当前存在的 key;单次最多 100 keys。 */
74
+ getMany(keys: readonly string[]): Promise<Map<string, unknown>>;
75
+ list(prefix: string, opts?: {
76
+ cursor?: string;
77
+ limit?: number;
78
+ }): Promise<{
79
+ cursor?: string;
80
+ items: Array<{
81
+ key: string;
82
+ value: unknown;
83
+ }>;
84
+ }>;
85
+ put(key: string, value: unknown): Promise<void>;
86
+ }
87
+
88
+ /**
89
+ * SecretStore:上游凭证的"只进不出"加密保管。
90
+ *
91
+ * 值经 AES-256-GCM 加密后写入注入的 StateStore(key 布局 `secret:<name>`,store.ts)。
92
+ * 主密钥 `TB_SECRET_ENCRYPTION_KEY` 是部署期 env-only 的 base64url(32 字节)——
93
+ * 信任根不自举存储(spec-digest)。主密钥缺失/格式非法时能力禁用:Set 抛 unavailable。
94
+ *
95
+ * 纯逻辑,仅依赖 WebCrypto(core 无宿主依赖)。`crypto` / `TextEncoder` / `TextDecoder`
96
+ * 在 Workers 与 Node 20+ 均为全局;此处以模块作用域最小声明补齐类型(不改 tsconfig、不污染全局)。
97
+ */
98
+
99
+ /**
100
+ * SecretStore 的纯逻辑实现。以注入的 StateStore 为后端。
101
+ *
102
+ * 主密钥缺失或格式非法(非 base64url / 非 32 字节)→ 实例处于 **unavailable 态**:
103
+ * Set 抛 unavailable,resolve 返回 undefined(见方法注释)。
104
+ */
105
+ declare class SecretStoreImpl {
106
+ private readonly store;
107
+ /** 32 字节主密钥;undefined 表示 unavailable 态。 */
108
+ private readonly keyBytes;
109
+ /** 惰性导入的 CryptoKey(仅可用时);首次加解密时创建并缓存。 */
110
+ private importedKey;
111
+ /**
112
+ * @param masterKey base64url 编码的 32 字节(TB_SECRET_ENCRYPTION_KEY);
113
+ * undefined / 解码失败 / 长度非 32 → 实例处于 unavailable 态。
114
+ */
115
+ constructor(store: StateStore, masterKey: string | undefined);
116
+ private static decodeMasterKey;
117
+ /** secret 能力是否可用(主密钥有效)。 */
118
+ get available(): boolean;
119
+ private key;
120
+ /**
121
+ * 写入 / 替换 secret;明文仅在此请求中出现。
122
+ * unavailable 态 → 抛 unavailable(retryable:false):主密钥缺失时 Set 不可用。
123
+ */
124
+ set(name: string, value: string, now: Timestamp): Promise<void>;
125
+ /**
126
+ * 枚举 secret 元数据。**绝不返回明文/密文**——只出 name + updatedAt(只进不出)。
127
+ * limit 默认 50、上限 200 钳制。
128
+ */
129
+ list(opts?: ListOptions): Promise<Page<{
130
+ name: string;
131
+ updatedAt: Timestamp;
132
+ }>>;
133
+ /** 删除 secret;不存在 → not_found。 */
134
+ delete(name: string): Promise<void>;
135
+ /**
136
+ * 解密并返回明文(不存在 → undefined)。
137
+ *
138
+ * **仅供网关内部 Provider 解析引用名(authRef/skRef/secretRef);不暴露为节点 cmd**
139
+ * (节点面只有 Set/List/Delete,resolve 不是 cmd)。
140
+ * unavailable 态(主密钥缺失)同样返回 undefined——本层不区分"无从解密"与"引用名不存在"。
141
+ * **消费侧契约:配置声明了引用却拿到 undefined 必须 fail closed**(抛 unavailable),
142
+ * 不得降级为无凭证/匿名出站——上游可能据此当匿名放行或返回误导性结果。
143
+ * 各 Provider(remote/mcp/http/pluginClient)均按此实现。
144
+ */
145
+ resolve(name: string): Promise<string | undefined>;
146
+ }
147
+
148
+ /**
149
+ * Context Layer 类型(原样转写;方法签名异步化以兼容对象存储后端)。
150
+ *
151
+ * 全部动词可选:能力由 handler 存在性推导(见 context/capabilities.ts)。
152
+ * 可选能力(Search/Delete)另在 ~describe 的 capabilities 中声明,调用方先探测再用。
153
+ */
154
+
155
+ interface ContextEntryMeta {
156
+ /** "text/markdown" | "application/json" | ... */
157
+ contentType: string;
158
+ metadata: Record<string, string>;
159
+ size?: number;
160
+ updatedAt: Timestamp;
161
+ /** node://<namespace-path>/<entry-path>;目录条目以尾 '/' 表示。 */
162
+ uri: URI;
163
+ /** 乐观并发:Update/Write 可携带 ifVersion;对象存储后端 = etag。 */
164
+ version: string;
165
+ }
166
+ interface ContextEntry extends ContextEntryMeta {
167
+ /** 文本或 JSON;大对象返回 { $ref: <预签名或中转 URL> }。 */
168
+ content: string | unknown;
169
+ }
170
+ interface ContextEntryInput {
171
+ content: string | unknown;
172
+ /** 字符串 content 必填(缺失 → invalid_argument);非字符串 content 缺省 application/json。 */
173
+ contentType?: string;
174
+ /** 不匹配 → conflict。 */
175
+ ifVersion?: string;
176
+ metadata?: Record<string, string>;
177
+ }
178
+ interface ContextPatch {
179
+ content?: string | unknown;
180
+ ifVersion?: string;
181
+ /** 浅合并。 */
182
+ metadata?: Record<string, string>;
183
+ }
184
+ interface SearchOptions extends ListOptions {
185
+ /** 缺省 keyword;semantic 需 capabilities 声明 "search:semantic",未声明 → invalid_argument。 */
186
+ mode?: 'keyword' | 'semantic';
187
+ }
188
+ /**
189
+ * Context provider。**全部动词可选**:能力由 handler 存在性推导(context/capabilities.ts),
190
+ * `~help` 只列真实存在的操作,没有任何写动词即自动只读。
191
+ *
192
+ * 这样只读资源、纯搜索服务、append-only 存储都能如实表达自己,不必为满足接口而伪造
193
+ * 方法或抛 unimplemented。未实现的动词在数据面按 unknown cmd 拒绝(invalid_argument)。
194
+ */
195
+ interface ContextProvider {
196
+ Delete?(path: string): Promise<void>;
197
+ /** 读取单个条目(含内容);不存在 → not_found。 */
198
+ Get?(path: string): Promise<ContextEntry>;
199
+ /** 枚举条目(浅层列表 + 分页);path 为 namespace 内相对路径前缀。 */
200
+ List?(path: string, opts?: ListOptions): Promise<Page<ContextEntryMeta>>;
201
+ Search?(query: string, opts?: SearchOptions): Promise<Page<ContextEntryMeta>>;
202
+ /** 部分更新已存在条目的内容或 metadata;不存在 → not_found。 */
203
+ Update?(path: string, patch: ContextPatch): Promise<ContextEntryMeta>;
204
+ /** 创建或整体替换条目(幂等 upsert)。 */
205
+ Write?(path: string, entry: ContextEntryInput): Promise<ContextEntryMeta>;
206
+ }
207
+
208
+ /**
209
+ * ObjectStore:对象存储抽象。
210
+ *
211
+ * r2(R2 binding)、s3(aws4fetch)与单测内存实现共用此接口;ContextProvider 的
212
+ * 四动词语义全部落在 objectProvider.ts,后端只做本接口适配。core 无 DOM lib:
213
+ * 流与 body 用最小结构类型声明,与 Workers / Node 的全局 ReadableStream 结构兼容。
214
+ */
215
+ /** 最小读流(结构兼容全局 ReadableStream<Uint8Array>)。 */
216
+ interface ObjectBodyStream {
217
+ cancel?(reason?: unknown): Promise<void>;
218
+ getReader(): {
219
+ /** 消费方(如 Node undici 的 Response 收尾)可能调用;实现方建议提供。 */
220
+ cancel?(reason?: unknown): Promise<void>;
221
+ read(): Promise<{
222
+ done: boolean;
223
+ value?: Uint8Array;
224
+ }>;
225
+ releaseLock(): void;
226
+ };
227
+ }
228
+ /** put 可接受的 body 形态(BodyInit 子集;core 无 DOM lib 故自声明)。 */
229
+ type ObjectBody = string | Uint8Array | ArrayBuffer | ObjectBodyStream;
230
+ interface ObjectMeta {
231
+ contentType?: string;
232
+ etag: string;
233
+ key: string;
234
+ /**
235
+ * 用户 metadata。undefined 表示后端 list 未返回(如 S3 ListObjectsV2),
236
+ * 区别于空对象 {}(确认无 metadata);Search 对 undefined 按需 head 补取。
237
+ */
238
+ metadata?: Record<string, string>;
239
+ size: number;
240
+ updatedAt: string;
241
+ }
242
+ interface ObjectPutOptions {
243
+ contentType?: string;
244
+ /** 与现存对象 etag 不符(含对象不存在)→ TBError conflict。 */
245
+ ifMatchEtag?: string;
246
+ metadata?: Record<string, string>;
247
+ }
248
+ interface ObjectListOptions {
249
+ cursor?: string;
250
+ /** 提供时浅层列举:共同子前缀折叠为 { prefix }(含 delimiter 本身)。 */
251
+ delimiter?: string;
252
+ limit?: number;
253
+ }
254
+ interface ObjectListResult {
255
+ cursor?: string;
256
+ /** 文件与折叠前缀按字典序混排(与 R2/S3 行为一致)。 */
257
+ items: Array<ObjectMeta | {
258
+ prefix: string;
259
+ }>;
260
+ }
261
+ interface ObjectStore {
262
+ /** 幂等:不存在静默。 */
263
+ delete(key: string): Promise<void>;
264
+ get(key: string): Promise<{
265
+ body: ObjectBodyStream;
266
+ meta: ObjectMeta;
267
+ } | null>;
268
+ head(key: string): Promise<ObjectMeta | null>;
269
+ list(prefix: string, opts?: ObjectListOptions): Promise<ObjectListResult>;
270
+ /** 生成限时直连 URL;后端不支持则缺省(provider 退化到 relayRefUrl)。 */
271
+ presign?(key: string, ttlSec: number): Promise<string>;
272
+ put(key: string, body: ObjectBody, opts?: ObjectPutOptions): Promise<ObjectMeta>;
273
+ }
274
+
275
+ /**
276
+ * Tool Layer 的中立类型。
277
+ *
278
+ * `ToolSpec` 是**上游工具的中立形状**:mcp(`tools/list` 的 `Tool`)与 http
279
+ * (`HttpToolDef`)都归一到它,虚拟化(virtualize.ts)与 `~help` 派生(mcpSchema.ts)
280
+ * 只认 `ToolSpec`,不感知上游是 mcp 还是 http。它把 `ToolMeta`+`ToolDef`
281
+ * 合并为一个形状:`description` 可缺省(上游可能不带),另携 `confirm`(危险工具二次确认)。
282
+ */
283
+ /** 上游工具的中立形状(mcp/http 归一目标)。 */
284
+ interface ToolSpec {
285
+ /** 危险操作二次确认;进 `~help` 的 confirm 行。 */
286
+ confirm?: boolean;
287
+ /** 一句话描述;进 `~help` 的 `h` 行。上游可能不带 → 可缺省。 */
288
+ description?: string;
289
+ /** 副作用标记(read/write/destructive);进 `~help` 的 effect 行。 */
290
+ effect?: string;
291
+ /** JSON Schema;`~help` 的 body 数据源。 */
292
+ inputSchema?: unknown;
293
+ /** 工具名(虚拟化前为上游原名,虚拟化后为对外虚拟名)。 */
294
+ name: string;
295
+ }
296
+ /**
297
+ * 工具调用结果。`isError:true` 是**工具业务级错误**(上游 HTTP 200
298
+ * 正常返回、内容为错),按内容协商渲染——**不是** TBError(传输/协议错误才归一为
299
+ * TBError,见 upstreamError.ts)。
300
+ */
301
+ interface ToolResult {
302
+ /** markdown 文本或结构化 JSON(按内容协商输出)。 */
303
+ content: string | unknown;
304
+ /** MCP 等上游的原生多模态 content blocks;HTBP 渲染仍使用归一后的 content。 */
305
+ contentBlocks?: unknown[];
306
+ isError?: boolean;
307
+ /** MCP 上游返回的结构化结果;consumer endpoint 转发时保留。 */
308
+ structuredContent?: Record<string, unknown>;
309
+ }
310
+
311
+ type SearchCapability = 'search' | 'search:semantic';
312
+ /** 全局工具搜索的完整结果;只在权限和可见性候选筛选后 hydrate。 */
313
+ interface ToolSearchHit {
314
+ path: TreePath;
315
+ tool: ToolSpec;
316
+ }
317
+ /** adapter 返回的轻量候选;resumeOffset 仅在当前请求内传递,不暴露给协议调用方。 */
318
+ interface ToolSearchCandidate {
319
+ name: string;
320
+ path: TreePath;
321
+ ref: string;
322
+ resumeOffset: number;
323
+ revision: number;
324
+ }
325
+ interface ToolSearchOptions {
326
+ cursor?: string;
327
+ limit?: number;
328
+ mode?: 'keyword' | 'semantic';
329
+ }
330
+ /** 宿主提供的全局工具索引;权限和虚拟化仍由 gateway 处理。 */
331
+ interface SearchIndex {
332
+ readonly capabilities: readonly SearchCapability[];
333
+ cursorFor(query: string, candidate: ToolSearchCandidate, mode?: 'keyword' | 'semantic'): Promise<string>;
334
+ search(query: string, opts?: ToolSearchOptions): Promise<Page<ToolSearchCandidate>>;
335
+ }
336
+ /** 索引持久层使用的轻量记录;完整 ToolSpec 只保留摘要,不进入派生数据库。 */
337
+ interface SerializedToolSearchRecord {
338
+ description: string;
339
+ feedback: string;
340
+ name: string;
341
+ path: TreePath;
342
+ toolDigest: string;
343
+ }
344
+ /** rebuild 使用的物化文档;feedback 只来自 owning node 的可见反馈投影。 */
345
+ interface ToolSearchDocument extends ToolSearchHit {
346
+ feedback?: string;
347
+ }
348
+ /**
349
+ * 可变索引的宿主契约。写入单位是节点快照,避免逐条 upsert 遗留已删除工具;
350
+ * rebuild 用于首次 seed 与运维修复,removePrefix 用于设备子树回收。
351
+ */
352
+ interface MutableSearchIndex extends SearchIndex {
353
+ initialized(): Promise<boolean>;
354
+ rebuild(documents: readonly ToolSearchDocument[]): Promise<void>;
355
+ remove(path: TreePath): Promise<void>;
356
+ removePrefix(path: TreePath): Promise<void>;
357
+ replace(path: TreePath, tools: readonly ToolSpec[], opts?: {
358
+ feedback?: string;
359
+ }): Promise<void>;
360
+ }
361
+
362
+ /**
363
+ * SQLite 系 SearchIndex 的共享实现——D1(Workers)与 better-sqlite3(Node)两个
364
+ * adapter 的唯一真源。
365
+ *
366
+ * 此前两边各持一份逐行相同的 schema DDL、查询 SQL、material-change 判定与
367
+ * cursor/分页逻辑(共 ~780 行,重合度 >85%),任何一侧改动都得靠人工纪律同步到另
368
+ * 一侧;`shortTermSql.ts` 只收敛了其中最容易出事的一小段。这里把**所有**与具体
369
+ * 驱动无关的部分收进 `SqlSearchIndex`,宿主差异压缩成 `SqlSearchDriver` 的五个
370
+ * 方法:
371
+ *
372
+ * - D1 全异步、写入走 `db.batch()`,且单请求有 50 查询预算 —— 故记录插入必须
373
+ * 攒成 JSON1 块(`insertRecords` 返回少量语句)并声明 `assertInsertBudget`;
374
+ * - better-sqlite3 全同步、写入走 `db.transaction()`,无查询预算 —— 逐条插入即可。
375
+ *
376
+ * core 是宿主中立层:这里只产出 SQL 文本与参数数组,不认识 D1Database 也不认识
377
+ * better-sqlite3。
378
+ */
379
+
380
+ /** 一条待执行语句:SQL 文本 + 按序绑定的参数,不含任何驱动对象。 */
381
+ interface SqlSearchStatement {
382
+ readonly params: readonly unknown[];
383
+ readonly sql: string;
384
+ }
385
+ /**
386
+ * 宿主驱动:把 `SqlSearchStatement` 落到具体数据库上。
387
+ *
388
+ * `write` 必须原子(D1 `batch` / SQLite `transaction`)——revision bump 与数据变更
389
+ * 分裂会让 cursor 指向不存在的 offset。
390
+ */
391
+ interface SqlSearchDriver {
392
+ /** 多行只读查询。 */
393
+ all: <T>(statement: SqlSearchStatement) => Promise<T[]>;
394
+ /**
395
+ * 记录插入语句数上限(可选);只有查询预算受限的宿主(D1 50/请求)需要实现。
396
+ * `count` 是本次 mutation 的记录 + 快照插入语句总数,不含固定的删除/bump。
397
+ */
398
+ assertInsertBudget?: (count: number) => void;
399
+ /** 建表(幂等);构造时已建好的宿主实现成 no-op。 */
400
+ ensureSchema: () => Promise<void>;
401
+ /** 单行只读查询;无行返回 null。 */
402
+ first: <T>(statement: SqlSearchStatement) => Promise<T | null>;
403
+ /** 把记录变成插入语句(JSON1 攒块 or 逐条绑定)。 */
404
+ insertRecords: (records: readonly SerializedToolSearchRecord[]) => SqlSearchStatement[];
405
+ /** 把 path→digest 快照变成插入语句。 */
406
+ insertSnapshots: (digests: ReadonlyMap<TreePath, string>) => SqlSearchStatement[];
407
+ /** 原子执行一组写语句。 */
408
+ write: (statements: readonly SqlSearchStatement[]) => Promise<void>;
409
+ }
410
+ declare class SqlSearchIndex implements MutableSearchIndex {
411
+ protected readonly driver: SqlSearchDriver;
412
+ readonly capabilities: readonly SearchCapability[];
413
+ constructor(driver: SqlSearchDriver);
414
+ /** 容量 trigger 的 ABORT 在各驱动里错误形状不同,统一按标记串归一。 */
415
+ private writeOrCapacity;
416
+ private meta;
417
+ private snapshotDigests;
418
+ initialized(): Promise<boolean>;
419
+ replace(path: TreePath, tools: readonly ToolSpec[], opts?: {
420
+ feedback?: string;
421
+ }): Promise<void>;
422
+ remove(path: TreePath): Promise<void>;
423
+ removePrefix(path: TreePath): Promise<void>;
424
+ rebuild(documents: readonly ToolSearchDocument[]): Promise<void>;
425
+ search(query: string, opts?: ToolSearchOptions): Promise<Page<ToolSearchCandidate>>;
426
+ cursorFor(query: string, candidate: ToolSearchCandidate, mode?: 'keyword' | 'semantic'): Promise<string>;
427
+ }
428
+
429
+ /**
430
+ * Plugin 传输客户端:探活、契约抓取、envelope 调用。
431
+ *
432
+ * - envelope 与节点调用同形:POST {endpoint} body `{"tool":"<Method>","arguments":{...}}`,
433
+ * `X-TB-Context` 承载 CallContext(base64url,唯一载体)、`X-TB-Request-Id` 每次逻辑调用
434
+ * 唯一;编解码复用 core plugin/envelope(体积守卫 ≤ 1 MiB)。
435
+ * - Authorization 按 manifest.auth 解析:platform-token → SecretStore 保留名
436
+ * `plugin-token:<id>`;bearer → secretRef。
437
+ * - 重试:仅对 retryable TBError 与网络失败重试 1 次,Request-Id 不变。
438
+ * - 超时 30s;响应 4xx/5xx 按 TBError body 归一;`$ref` 不解引用(原样透传调用方)。
439
+ */
440
+
441
+ /**
442
+ * 进程内插件装配表:binding 名 → fetch handler(宿主注入,见 TbAppDeps.pluginBindings)。
443
+ * handler 通常闭包持有插件模块与其 env(如 `req => plugin.fetch(req, env)`);
444
+ * 懒加载放进闭包即可(首调时 import()),未挂载/未调用的插件零运行成本。
445
+ */
446
+ type PluginBindingHandler = (request: Request) => Response | Promise<Response>;
447
+ type PluginBindings = ReadonlyMap<string, PluginBindingHandler>;
448
+
449
+ /**
450
+ * gateway 侧的**异步**工具源。
451
+ *
452
+ * 这是工具源在平台上**唯一**的契约:`list` + `call` 两个动词,方法返回 Promise
453
+ * (mcp/http/plugin 实现都要发网络请求)。`list` 产出**虚拟化前**的上游原始 `ToolSpec[]`
454
+ * (名字是上游真名);虚拟化与反查在调用点用 core 的 `virtualizeTools`/`resolveUpstreamTool`。
455
+ * 没有 `Get`:`~help` 的数据源是 `list` 的产物,平台从不按名单取单个 spec。
456
+ */
457
+ interface UpstreamProvider {
458
+ /** 用**上游真名**调用(调用点已把虚拟名反查为真名)。 */
459
+ call(name: string, args: Record<string, unknown>): Promise<ToolResult>;
460
+ /** 枚举上游全部工具(虚拟化前的原名)。 */
461
+ list(): Promise<ToolSpec[]>;
462
+ }
463
+
464
+ /**
465
+ * remote 节点透传:把对 `<path>` 及其后代的 `~help`/`~skill`/`~tree`/`POST`
466
+ * 请求,改写为对 `baseUrl` 下相对路径的**同形**请求。
467
+ *
468
+ * - `baseUrl` 白名单(空 = 拒一切)——注册时与调用时双重校验。
469
+ * - `skRef` 解析出的凭证作为出站 `Authorization: Bearer`;**本地调用者的 SK 不外传**。
470
+ * - `X-TB-Via`:入站链经 `checkVia` 判环/跳数(在追加自身之前);出站 `appendVia` 追加自身标识。
471
+ * - 传输失败经 `normalizeUpstreamError` 归一;远端返回的响应(含其自身 TBError)原样透传。
472
+ *
473
+ * 宿主中立(核心零分叉):部署配置以解析后的 {@link RemoteSettings} 注入,
474
+ * env 解析(TB_REMOTE_ALLOWLIST 等)在宿主适配层(gateway app.ts / SDK config)。
475
+ */
476
+
477
+ /** remote 透传的部署配置(宿主解析后注入)。 */
478
+ interface RemoteSettings {
479
+ /** 放行 http:// 上游(仅本地开发)。 */
480
+ allowInsecure: boolean;
481
+ /** baseUrl 的 host 后缀白名单;空数组 = 拒一切 remote。 */
482
+ allowlist: string[];
483
+ /** 本实例 X-TB-Via 标识;缺省用**入站请求 host** 派生(跨实例联邦须显式配置才能可靠去环)。 */
484
+ instanceId?: string;
485
+ /** X-TB-Via 跳数上限(缺省 4,由宿主适配层落默认)。 */
486
+ maxHops: number;
487
+ }
488
+
489
+ /**
490
+ * 宿主注入面与请求期公共类型。
491
+ *
492
+ * 这里只放形状:五个注入点(state / objects / secrets / device / search)、进程内
493
+ * Provider 钩子与解析后的部署配置。行为实现分散在 paths/federation/deviceNodes/
494
+ * toolNodes/contextNodes/helpModel 与 routes/*,装配在 tbApp.ts。
495
+ */
496
+
497
+ /** 帧协议 call 转发的入参(id 由调用点生成,幂等键)。 */
498
+ interface DeviceInvokeRequest {
499
+ arguments: Record<string, unknown>;
500
+ id: string;
501
+ path: string;
502
+ tool: string;
503
+ }
504
+ /** 设备通道宿主(CF = DeviceSession DO / Docker = ws;deviceTransport 的消费面)。 */
505
+ interface DeviceChannel {
506
+ /** HTTP→WS 调用转发:结果为 DeviceCallResult 形状(设备侧 result 帧)。 */
507
+ invoke(deviceId: string, req: DeviceInvokeRequest): Promise<unknown>;
508
+ /** WS 升级请求转交(/system/device/ws)。 */
509
+ ws(deviceId: string, request: Request): Promise<Response>;
510
+ }
511
+ /** 进程内本地 Provider 钩子(SDK registerTool/registerContext 的装配面)。 */
512
+ interface LocalProviderHooks {
513
+ /** kind:'context' 节点按路径取进程内 ContextProvider;undefined → 走 plugin 解析。 */
514
+ context?(nodePath: TreePath): ContextProvider | undefined;
515
+ /** kind:'tool' 节点按路径取进程内工具源;undefined → 走 plugin 解析。 */
516
+ tool?(nodePath: TreePath): UpstreamProvider | undefined;
517
+ }
518
+ /**
519
+ * tb app 的宿主注入面(五注入点 + 解析后的部署配置)。
520
+ * 核心业务逻辑零分叉:Workers 适配层(app.ts)与 SDK(packages/sdk)都注入此形状。
521
+ */
522
+ interface TbAppDeps {
523
+ /** 放行 http:// 上游(仅本地开发)。 */
524
+ allowInsecureHttp: boolean;
525
+ /** Dashboard 静态资源(Workers Static Assets);缺省 → /ui 404。 */
526
+ assets?: (request: Request) => Promise<Response>;
527
+ /**
528
+ * 规范网关 origin(如 `https://tool-bridge.example.com`)。配置后,OAuth 的
529
+ * redirect_uri 钉在此规范值上,而非每请求动态取 origin——防止实例经多域名
530
+ * (自定义域 + *.workers.dev 等)访问时,授权 code 在不同域名间被互换。
531
+ * 缺省 → 回退到请求期 origin(单域名部署行为不变)。
532
+ */
533
+ canonicalOrigin?: string;
534
+ /** 设备通道;缺省 → device 能力禁用。 */
535
+ device?: DeviceChannel;
536
+ /** $ref 中转 token 签名密钥(TB_SECRET_ENCRYPTION_KEY);缺省 → /~ref 404、大对象走 presign 或 unavailable。 */
537
+ encryptionKey?: string;
538
+ /** 认证前的实例就绪钩子(引导/延迟注册 flush);每请求调用,幂等由宿主保证。 */
539
+ ensureReady?: () => Promise<void>;
540
+ /** SDK 进程内 Provider 表(缺省无)。 */
541
+ locals?: LocalProviderHooks;
542
+ /** context 平台对象存储('r2' provider 的落点);缺省 → 该 provider unavailable。 */
543
+ objects?: () => Promise<ObjectStore> | ObjectStore;
544
+ /**
545
+ * 进程内插件装配表(binding 名 → fetch handler)。manifest.endpoint 为
546
+ * `binding:<name>` 的插件经此直调,零网络跳;未装配的 binding 注册/调用报 unavailable。
547
+ */
548
+ pluginBindings?: PluginBindings;
549
+ /** context Get 的 $ref 内联阈值(字节,缺省 1 MiB)。 */
550
+ refThresholdBytes?: number;
551
+ /** $ref URL(presign 与 /~ref 中转)有效期秒(缺省 900)。 */
552
+ refTtlSec?: number;
553
+ /** remote 联邦透传配置。 */
554
+ remote: RemoteSettings;
555
+ /** 追加保留根路径(在内置保留根之外额外声明)。 */
556
+ reservedRoots?: string[];
557
+ /** 全局工具搜索索引;缺省或未声明 search capability 时 /~search 不存在。 */
558
+ search?: SearchIndex;
559
+ secrets: SecretStoreImpl;
560
+ state: StateStore;
561
+ /** mcp/tool 工具缓存 TTL 秒(缺省 300)。 */
562
+ toolCacheTtlSec?: number;
563
+ /** healthz 与 system/status 回显的版本号(单一真源:宿主 package.json)。 */
564
+ version: string;
565
+ }
566
+ /** 请求期变量:认证中间件写入,handler 只读。 */
567
+ type Vars = {
568
+ ctx: CallContext;
569
+ store: StateStore;
570
+ };
571
+
572
+ /**
573
+ * 构造 tool-bridge 的 Hono app(宿主中立;Workers 适配见 app.ts,SDK 装配见 packages/sdk)。
574
+ */
575
+ declare function createTbApp(deps: TbAppDeps): Hono<{
576
+ Variables: Vars;
577
+ }>;
578
+
579
+ /**
580
+ * Node 宿主的 env 配置面。变量名与语义对齐 CF 宿主(gateway/src/app.ts 的 Env),
581
+ * 仅新增宿主形态相关的 TB_PORT / TB_HOST / TB_DATA_DIR / TB_UI_DIR。
582
+ * 解析函数镜像 app.ts 的 allowInsecure / remoteSettingsFromEnv / positiveIntEnv。
583
+ */
584
+
585
+ interface ServerConfig {
586
+ /** 首次引导的 Admin SK 明文(须经 TB_BOOTSTRAP_ADMIN_SK 预置;缺省且未开 insecure bootstrap 则 fail closed)。 */
587
+ adminSk?: string;
588
+ /**
589
+ * 逃生阀:显式放行"缺 Admin SK 时随机生成并打印明文"的旧行为(仅本地/一次性开发)。
590
+ * 默认 false → 生产/Docker 缺 TB_BOOTSTRAP_ADMIN_SK 时拒绝启动,不把最高权限凭证写日志。
591
+ */
592
+ allowInsecureBootstrap: boolean;
593
+ allowInsecureHttp: boolean;
594
+ /**
595
+ * 规范网关 origin(TB_CANONICAL_ORIGIN):多域名访问时钉死 OAuth redirect_uri。
596
+ * 与 Workers 宿主同一解析真源(core normalizeCanonicalOrigin);配置了但非法 →
597
+ * configFromEnv 抛错,进程拒绝启动(fail closed,不静默回退到请求期 origin)。
598
+ */
599
+ canonicalOrigin?: string;
600
+ /** SQLite 库与 fs 对象根所在目录(state.sqlite3 + objects/)。 */
601
+ dataDir: string;
602
+ /** 设备断线后未重连的回收秒数(缺省 24h)。 */
603
+ deviceReclaimSec: number;
604
+ /** SecretStore 主密钥 + $ref 中转 token 签名密钥(base64url 32B)。 */
605
+ encryptionKey?: string;
606
+ host: string;
607
+ /**
608
+ * 进程内插件装配表(binding 名 → fetch handler),供程序化嵌入方注入;
609
+ * `binding:<name>` 的插件经此直调,零网络跳。bin 入口暂不从 env 装配。
610
+ */
611
+ pluginBindings?: PluginBindings;
612
+ port: number;
613
+ refThresholdBytes?: number;
614
+ refTtlSec?: number;
615
+ remote: {
616
+ allowInsecure: boolean;
617
+ allowlist: string[];
618
+ instanceId?: string;
619
+ maxHops: number;
620
+ };
621
+ toolCacheTtlSec?: number;
622
+ /** Dashboard 静态资源目录覆盖(缺省经 @tool-bridge/dashboard 包解析)。 */
623
+ uiDir?: string;
624
+ }
625
+ declare function configFromEnv(env?: NodeJS.ProcessEnv): ServerConfig;
626
+
627
+ /**
628
+ * DeviceHub:Node 宿主的设备通道(DeviceChannel 实现,对位 CF 的 DeviceSession DO)。
629
+ *
630
+ * WS 升级走 http.Server 'upgrade' 事件 + ws handleUpgrade(不经 fetch handler,
631
+ * tbApp 的认证中间件天然旁路),认证双点补齐:升级前 identify(401 早失败)+
632
+ * 共享 processDeviceHello 内的权威判定(与 DO 同一模块,树形态/权限判定序不漂移)。
633
+ *
634
+ * 与 DO 的有意分叉:
635
+ * - requestId 幂等表仅内存(session 内):进程重启 WS 必断、待决调用随进程消亡,
636
+ * 无 DO hibernation 的跨休眠回放需求;tbApp 每次 invoke 生成新 UUID,跨连接去重无收益。
637
+ * - 断线回收用进程内 setTimeout + StateStore 持久 meta(devicemeta:<id>),启动时
638
+ * sweepOrphans 扫描孤儿排程(对位 DO 的 storage+alarm)。
639
+ * - 心跳:应用层 ping→pong 已内置于 DeviceGatewaySession.handleFrame;另加 ws 协议层
640
+ * 探活(isAlive + 周期 ping + terminate)踢半开死连接,避免调用一律吃 60s 超时。
641
+ */
642
+
643
+ declare const DEVICE_WS_PATH = "/system/device/ws";
644
+ declare class DeviceHub {
645
+ private readonly store;
646
+ private readonly search;
647
+ private readonly reclaimSec;
648
+ private readonly wss;
649
+ private readonly activeByDevice;
650
+ private readonly connections;
651
+ private readonly reclaimTimers;
652
+ private heartbeat;
653
+ constructor(opts: {
654
+ heartbeatMs?: number;
655
+ reclaimSec: number;
656
+ search?: MutableSearchIndex;
657
+ store: StateStore;
658
+ });
659
+ /** 挂到 http.Server 的 'upgrade' 事件(仅处理 DEVICE_WS_PATH,其余 404)。 */
660
+ attach(server: http.Server): void;
661
+ /** DeviceChannel.invoke:HTTP→WS 调用转发(无活连接 → deviceOffline)。 */
662
+ invoke(deviceId: string, req: DeviceInvokeRequest): Promise<unknown>;
663
+ /** DeviceChannel.ws:Node 宿主的升级在 http 层处理,永不应命中此路由。 */
664
+ ws(): Promise<Response>;
665
+ /**
666
+ * 启动孤儿扫描:devicemeta: 里无活连接的设备补排回收 timer。
667
+ * 崩溃时仍在线(无 disconnectedAt)按"此刻断线"起算;已过期立即回收。
668
+ */
669
+ sweepOrphans(): Promise<void>;
670
+ close(): Promise<void>;
671
+ private handleUpgrade;
672
+ private acceptConnection;
673
+ private acceptHello;
674
+ /** 连接失效收尾(对位 DO markDisconnected):registry 下线 + meta 记断线 + 排回收。 */
675
+ private onClose;
676
+ private scheduleReclaim;
677
+ private cancelReclaim;
678
+ /** 回收执行(对位 DO alarm):仍无活连接才删子树 + meta。 */
679
+ private reclaim;
680
+ /** ws 协议层探活:上一轮未回 pong → terminate(触发 close → markDisconnected)。 */
681
+ private pingConnections;
682
+ }
683
+
684
+ /**
685
+ * createDataObjectStore:平台对象存储('r2' provider 落点)的 Node 实现。
686
+ *
687
+ * core FsObjectStore 是多根语义(key 首段必须等于某 root 的 basename),而平台
688
+ * 对象存储的 key 是平坦任意前缀(默认 ctx/<nodePath>/...)——本模块做薄前缀适配:
689
+ * 出入口统一加/剥内部根段 'objects/',穿越防护复用 FsObjectStore 的两层防护
690
+ * (normalizeEntryPath + realpath-in-root),不重写。cursor 是内部形态原样透传
691
+ * (消费方视 cursor 为不透明串)。无 presign → $ref 走 /~ref 网关中转(现有降级)。
692
+ */
693
+
694
+ declare function createDataObjectStore(dataDir: string): ObjectStore;
695
+
696
+ /** Node 宿主的 better-sqlite3 FTS5/trigram SearchIndex。 */
697
+ declare class SqliteSearchIndex extends SqlSearchIndex {
698
+ private readonly db;
699
+ constructor(dbPath: string);
700
+ close(): void;
701
+ }
702
+
703
+ /**
704
+ * SqliteStateStore:better-sqlite3 实现的 StateStore(Docker/Node 宿主)。
705
+ *
706
+ * 单表 kv(key TEXT PRIMARY KEY, value TEXT)——StateStore 本身就是 kv 语义,
707
+ * 拆表只会复制 key 布局知识。值 JSON 序列化存取(与 KvStateStore 同形)。
708
+ * 强一致:吊销/写入即时可见,无 KV 的最终一致窗口(kvStateStore.ts 的跳 null
709
+ * 与逐 key get 负担在此宿主不存在)。
710
+ *
711
+ * list 用 key 范围扫描(>= prefix AND < successor(prefix)),不用 LIKE/GLOB——
712
+ * key 里的路径段可含 '_'/'%'/'[',通配符转义是坑。cursor/排序语义与
713
+ * core MemoryStateStore 对拍(cursor = 上页末 key,仅在还有更多时返回)。
714
+ * 注意:SQLite TEXT 按 UTF-8 字节序比较,JS 按 UTF-16 code unit 比较,
715
+ * 二者在 ASCII 与 BMP 码点上一致;key 由本项目生成(ASCII 前缀 + 树路径),
716
+ * 防御性地对返回行再做 startsWith 过滤。
717
+ */
718
+
719
+ declare class SqliteStateStore implements StateStore {
720
+ private readonly db;
721
+ private readonly stmtGet;
722
+ private readonly stmtPut;
723
+ private readonly stmtDelete;
724
+ constructor(dbPath: string);
725
+ get(key: string): Promise<unknown | null>;
726
+ getMany(keys: readonly string[]): Promise<Map<string, unknown>>;
727
+ put(key: string, value: unknown): Promise<void>;
728
+ delete(key: string): Promise<void>;
729
+ list(prefix: string, opts?: {
730
+ cursor?: string;
731
+ limit?: number;
732
+ }): Promise<{
733
+ cursor?: string;
734
+ items: Array<{
735
+ key: string;
736
+ value: unknown;
737
+ }>;
738
+ }>;
739
+ close(): void;
740
+ }
741
+
742
+ /**
743
+ * createTbServer:Node 宿主装配(对位 gateway/src/app.ts 的 depsFromEnv)。
744
+ *
745
+ * SQLite StateStore + fs ObjectStore + @hono/node-server;引导在 start() 时
746
+ * 直调宿主中立 runBootstrap(Node 有真实启动点,不需要 Workers 的 per-request once,
747
+ * 故不注入 deps.ensureReady)。设备通道(DeviceHub)与 /ui 静态托管由后续
748
+ * 装配点注入(deps.device / deps.assets)。
749
+ */
750
+
751
+ interface TbServer {
752
+ app: ReturnType<typeof createTbApp>;
753
+ close(): Promise<void>;
754
+ deviceHub: DeviceHub;
755
+ search: SqliteSearchIndex;
756
+ /** 引导(幂等)+ 孤儿设备回收排程 + 监听;返回实际端口(config.port=0 时由系统分配)。 */
757
+ start(): Promise<{
758
+ port: number;
759
+ }>;
760
+ state: SqliteStateStore;
761
+ }
762
+ declare function createTbServer(config: ServerConfig): TbServer;
763
+
764
+ export { DEVICE_WS_PATH, DeviceHub, type ServerConfig, SqliteSearchIndex, SqliteStateStore, type TbServer, configFromEnv, createDataObjectStore, createTbServer };