@tbox.cn/app-template-agent 0.10.20 → 0.10.22

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.
@@ -1,185 +0,0 @@
1
- # provider-custom —— 自定义 API 实施落点(真源)
2
-
3
- 本包是**自定义 API(内联声明)的 adapter 代码落点**:某服务槽在 `config/integrations.json`
4
- 保存内联声明(provider 为对象 + implementation 字面量)而 adapter 未实施时,服务调用报
5
- `PROVIDER_NOT_IMPLEMENTED`——按本指引在本包实施,注册命中后即恢复。
6
-
7
- 接入材料清单规范与任务路由(哪些服务、要向用户收集什么)见应用根 `INTEGRATION.md` 第 1 步
8
- (本文件管实施侧);声明写法与维度选择见应用根 `INTEGRATION.md`「声明落点」。
9
-
10
- ## 实施三步
11
-
12
- 1. **读声明**:`config/integrations.json` 中目标服务(如 `parking.query`)的 `provider`
13
- 为对象(`{ provider, credentialType, configSchema? }`)+ `implementation` 字面量——这是
14
- 厂商身份与凭据约定。
15
- 2. **实现 adapter**:在 `src/server/` 新建实现文件,实现该服务对应模块的 Port 接口(接口
16
- 定义见消费模块 `src/server/ports.ts`(或 `ports/` 目录)或 `@app/contracts-mall`
17
- 契约包——本包按需加 `@app/contracts-mall` 依赖并 `import type`)。HTTP 调用一律经
18
- `input.transport`(baseUrl 取自绑定 config;熔断/HTTPS 强制(localhost 的 http 豁免)/
19
- 响应体 1MB 上限内建):
20
- `await input.transport.request({ path, method, body, query })`。
21
- 3. **注册**:在 `src/server/index.ts` 的 `register(ctx)` 中调用
22
- `ctx.integrations?.registerAdapter('<provider 名>', 'inline', factory)`——**注册键
23
- implementation 段恒为 `'inline'`**(内联声明运行时按合成 `'inline'` 寻址,与声明中的
24
- implementation 字面量无关)。
25
-
26
- **多服务单键**:同一 provider 名的全部服务共享唯一注册键——重复注册后者覆盖前者。同一
27
- 后端接多个服务 = 只注册一次,factory 内按 `input.service` 分支返回各 Port 实现;或每服务
28
- 用不同 provider 名分键。adapter 内取凭据值(如自签名):`input.credential()` 惰性 getter
29
- (返回 `{ scope, type, value }`;未配 credentialRef → undefined)。
30
-
31
- ## 声明形态 ⇔ 实现落点(先选对形态再动手)
32
-
33
- | 声明形态(services.<slot>.provider) | adapter 落点 | 失配信号 |
34
- |---|---|---|
35
- | 内联对象 `{ provider, credentialType }` | **本包**(provider-custom) | 未注册 → `PROVIDER_NOT_IMPLEMENTED` |
36
- | 字符串厂商名(如 `"mall-demo"`) | 对应正式 provider 包(catalog 来源;`packages/provider-mock` 为样例) | catalog 无此厂商 → **`PROVIDER_NOT_FOUND`** |
37
-
38
- **v8 起:字符串声明 `"provider-custom"` = 唯一推荐声明形态**——本包经
39
- `declare.providers.id` 自声明入 provider catalog(零槽起步,AI 实施即补槽声明:
40
- `{ service, provider: 'provider-custom', implementation, credentialType, configSchema? }`)。
41
- 历史条款「禁止配字符串声明」随零槽形态退役。存量内联对象声明运行时按
42
- catalog 查表,必报 `PROVIDER_NOT_FOUND`(错误文案「provider 未命中
43
- catalog」≠ 名字拼错,是**形态落错**)。
44
-
45
- ## 内联声明 JSON 片段(可复制形态)
46
-
47
- `config/integrations.json` 槽级写法(`provider` 对象与 `implementation`
48
- 是**兄弟键**——implementation 不在对象内;省略 `instances` = 通配激活):
49
-
50
- ```json
51
- {
52
- "services": {
53
- "parking.query": {
54
- "provider": { "provider": "my-api", "credentialType": "static-headers-v1" },
55
- "implementation": "my-api@1",
56
- "config": { "baseUrl": "https://api.example.com" },
57
- "credentialRef": "secret://my-api-parking.query"
58
- }
59
- }
60
- }
61
- ```
62
-
63
- **错误形态对照**:
64
-
65
- | ❌ 错误 | 原因 |
66
- |---|---|
67
- | `"provider": { "provider": "my-api", "implementation": "my-api@1" }` | `implementation` 不是内联对象字段(zod 拒)——v8 弃用形态,存量迁移 `tbox-app provider migrate --write` |
68
- | `"provider": "my-api"` 而 adapter 在本包 | catalog 查表失配 → `PROVIDER_NOT_FOUND`(见上节) |
69
- | `"instances": {}` | 零激活(省略 = 通配激活) |
70
-
71
- 实参(config 值 / 凭据 stem / 槽名)按用户 API 文档填;凭据真值只进
72
- `config/credentials/<stem>.json` 嵌套形(格式见「认证与凭据」)。
73
-
74
- ## 最小代码骨架
75
-
76
- 类型 import 双源(全部 `import type`——typecheck 首祸排除):
77
-
78
- - `ServiceAdapterFactory` / `ServerContext` ← `@tbox.cn/app-agent-sdk-server`
79
- - Port 接口 ← 服务域契约包(mall 域 = `@app/contracts-mall`;消费模块
80
- `src/server/ports.ts` 的 import 行可反查正确来源——如 `parking.query` 的
81
- 供给接口 `ParkingQueryPort`;本包 `package.json` 按需补该依赖)
82
-
83
- ```ts
84
- // packages/provider-custom/src/server/my-api.ts
85
- import type { ServiceAdapterFactory } from '@tbox.cn/app-agent-sdk-server';
86
- import type { ParkingQueryPort } from '@app/contracts-mall';
87
-
88
- export const myApiFactory: ServiceAdapterFactory = (input) => {
89
- const impl: ParkingQueryPort = {
90
- async queryFee(q, req) {
91
- // input.transport:HTTP 通道(baseUrl/认证/熔断/HTTPS 强制内建)
92
- const res = await input.transport.request({
93
- path: '/v1/parking/fee',
94
- method: 'POST',
95
- body: { plateNo: req.plateNo },
96
- });
97
- return mapFee(res);
98
- },
99
- // ...Port 其余方法签名照抄接口定义
100
- };
101
- return impl;
102
- };
103
- ```
104
-
105
- **Port 形状检索工作法(零猜测)**:
106
-
107
- ```sh
108
- rg "export (interface|type) .*Port" packages/<消费模块>/src/server/ports.ts
109
- rg "<接口名>" packages/contracts-mall/src/
110
- ```
111
-
112
- - Port 方法签名 = 实现清单——逐个照抄(参数/返回值与工具语义一一对应);
113
- - 厂商响应 → contracts 结构转换后返回,不透传厂商字段名。
114
-
115
- 注册(`src/server/index.ts` 的 `register(ctx)` 内):
116
-
117
- ```ts
118
- import type { ServerContext } from '@tbox.cn/app-agent-sdk-server';
119
- import { myApiFactory } from './my-api';
120
-
121
- // register(ctx) 内:
122
- ctx.integrations?.registerAdapter('provider-custom', '<implementation>', myApiFactory);
123
- ```
124
-
125
- 单 factory 多服务:按 `input.service` 分支返回不同 Port 实现(服务清单见
126
- 消费模块 `tbox.module.json` `declare.owns`);或每服务独立 provider 名
127
- 分键注册。`input` 字段全集见 `src/server/index.ts` 签名示例注释。
128
-
129
- ## provider 名三处一致
130
-
131
- 取的 provider 名必须下三处同名(任一处失配则运行时报 `PROVIDER_NOT_IMPLEMENTED`——
132
- resolveService 惰性查表查不到对应 adapter):
133
-
134
- 1. **声明侧**:`config/integrations.json` 中 `services.<slot>.provider.provider` 字段
135
- (内联声明对象内的 `provider` 键;若声明是字符串厂商则该键名直接是名字)
136
- 2. **注册侧**:`packages/provider-custom/src/server/index.ts` 中
137
- `ctx.integrations?.registerAdapter('<同名>', 'inline', factory)` 第一参
138
- 3. **凭据侧**:`config/credentials/${provider名}-${service}.json` 的 stem 前缀
139
- (服务级);共享 stem = `${provider名}-global`;实例差异化另见
140
- `${provider名}-${instanceId}-${domain}` 形(凭据文件嵌套形格式见下节)
141
-
142
- > 多服务单键只是注册侧共享,三处同名规则不放宽——同一 provider 名的全部服务 stem 仍各自
143
- > 含 `-<service>` 段;只有注册键(registerAdapter 第一参)一次("单键")。
144
-
145
- ## 认证与凭据
146
-
147
- | 认证形态 | credentialType(声明与凭据文件一致) | 机制 |
148
- |---|---|---|
149
- | 静态头(x-api-key 等白名单头名) | `static-headers-v1`(SDK 内置) | 头自动注入,adapter 全部经 `input.transport` |
150
- | 其他(Bearer / 自定义签名…) | 自定(如 `bearer-v1`) | 本包 `register(ctx)` 先 `ctx.authStrategies.register({ credentialType, apply(input) {...} })` 注册策略(SDK `ProviderAuthStrategy`),adapter 同走 `input.transport` |
151
-
152
- - **勿用 `'-'`**:非 local 供给在 transport 构造期抛「未知认证类型」。
153
- - 静态头白名单(本应用 `apps/server/src/app.ts`):`content-type` / `sign` / `timestamp` /
154
- `publickey` / `x-api-key`;白名单外头名走自定义策略。
155
- - 凭据文件(**嵌套形**,运行时读取格式)`config/credentials/<stem>.json`:
156
- `{ "type": "<credentialType>", "value": { "<头名/参数名>": "<真值>" } }`(省略 `scope` =
157
- 全域通配)。声明/实例持 `credentialRef: "secret://<stem>"`;服务级 stem =
158
- `${provider}-${service}`,共享 stem = `${provider}-global`(按实例差异化凭据另有
159
- `${provider}-${instanceId}-${domain}` 形)。
160
- - 槽 `instances` 省略 = 通配激活(**勿写空对象 `{}`** = 零激活);已绑字符串厂商的槽整组
161
- 替换前先向用户确认。
162
-
163
- ### 数据可见性边界(config / configSchema / 凭据)
164
-
165
- | 数据 | 落点 | 类型/形态 | 能否进 prompt / 代码 / 入库文档 |
166
- |---|---|---|---|
167
- | `config` 字段(baseUrl / 超时 / 路径前缀等连接参数;`services[slot].config`、`modules[].config`、root `config` 等所有 `config*`) | `config/integrations.json` | 明文真实值——直接写,可见即设计意图 | ⚠ 可见,但禁作"密钥代用品" |
168
- | `configSchema`(内联声明中的可选字段) | `integrations.json` 写路径字符串,文件本体在 `config/<路径>.json` | 可选 schema 制品——相对应用 `config/` 的路径;无强制格式;写则 doctor 谓词校验,不写则跳过 | ✅ 可见(非真值)。**AI 接自定义集成任务不要因为没 schema 而拒绝落声明** |
169
- | 凭据真值(API key / token / 签名 secret / OAuth secret 等) | `config/credentials/<stem>.json` 嵌套形 或 平台 env | `{ "type": "<credentialType>", "value": { "<头名>": "<真值>" } }` | ❌ **禁**入 prompt / 代码 / integrations.json 的 `config.*` / 入库文档 |
170
- | `credentialRef: "secret://<stem>"` 引用本身 | `config/integrations.json` 明文 | 仅 stem 引用,非真值 | ✅ 可见 |
171
- | AGENTS.md / INTEGRATION.md / 模板出厂文档 | 仓库 | — | ❌ 不含 `secret://` 真值(仅描述引用形态) |
172
-
173
- ## 完成判据
174
-
175
- - `pnpm typecheck && pnpm build` 通过;
176
- - 对话或 curl 触发一次该服务的工具调用,确认正常出结果(typecheck/build 与 doctor
177
- 均不触运行时——resolveService 惰性,调用时才判 `PROVIDER_NOT_IMPLEMENTED`);
178
- - 通配槽下 doctor 不检查凭据,运行时为最终裁判。
179
-
180
- ## 真实厂商接入
181
-
182
- 对外提供正式 API 的厂商(支付/会员等大规模集成)走官方 provider 包形态:manifest
183
- `declare.providers.slots` 声明供给 + 独立包发布 + `tbox-app module add` 装配 +
184
- `tbox-app provider bind` 绑定。样例:`tbox-app module add @tbox.cn/app-provider-mock --mode
185
- codegen --source @tbox.cn/app-provider-mock` 后参照 `packages/provider-mock` 结构。