feat: define SuperAPI binding-level skill forcing
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Failed
Pipelines as Code CI / unidesk-host- Success

This commit is contained in:
pikastech
2026-07-19 12:18:37 +02:00
parent 0908df59ac
commit aa04334e68
3 changed files with 74 additions and 3 deletions
+8 -1
View File
@@ -110,7 +110,14 @@ bun scripts/cli.ts web-probe product-smoke \
--profile management
```
- `management l0-smoke` 一次覆盖 PostgreSQL、skill CRUD、API Key 单向保存、绑定启停、历史导入和快照。
- `management l0-smoke` 一次覆盖 PostgreSQL、skill CRUD、API Key 单向保存、绑定启停、绑定级强制全文、历史导入和快照。
- 绑定级强制全文必须满足:
- `forceEnabled` 默认关闭,并与普通 `enabled` 分别保存;
- 关闭时保持 `name + description + SKILL.md URL` 注入;
- 开启时把标准化后的完整 `SKILL.md` 注入 instructions
- `enabled=false` 时不进入 Gateway 快照;
- 没有完整 `SKILL.md` 的摘要 Skill 必须由服务端拒绝开启;
- 配置只影响当前 API Key 与当前 Skill 的绑定,不得变成 Skill 全局开关。
- 普通 Sub2API 用户权限变更使用独立最短链:
- 先用 `codex-pool users ensure` 创建隔离普通测试用户和 Key
- 用户、目标和 Key 名通过参数输入,凭据只使用命令输出的 owner-only
@@ -38,6 +38,7 @@ Agent instructions。SuperAPI 同时提供公开的文档型 Remote Skill regist
### 2.2 范围内
- 每个 API Key 独立绑定零个或多个虚拟 skill 摘要。
- 每条 API Key 与 skill 绑定可以独立选择是否强制注入完整 `SKILL.md`
- 在 Responses 请求中保留原 instructions 并追加匹配 Key 的 catalog。
- 支持流式与同步 Responses 请求的透明透传。
- API Key、Authorization 和上游响应的脱敏日志与健康状态。
@@ -148,6 +149,7 @@ SuperAPI 应使用 owning YAML 引用的 native PostgreSQL 保存以下实体:
- skill 绑定:
- API Key 与 skill 的唯一关系;
- 独立启用开关;
- 默认关闭的完整 `SKILL.md` 强制注入开关;
- 创建与更新时间。
- 请求历史:
- request id
@@ -174,6 +176,7 @@ SuperAPI 应提供左侧根导航和多页面管理工作区:
- 点击列表行打开绑定对话框;
- 通过下拉菜单添加 skill
- 对每条绑定独立启用或停用;
- 对每条绑定独立启用或停用完整 `SKILL.md` 强制注入;
- 删除绑定。
- 请求历史:
- 按时间、API Key、路径、状态和 request id 筛选;
@@ -377,3 +380,43 @@ Skill 绑定管理、他人资源拒绝、历史导航不可见与历史 API 拒
保留管理员登录和管理工作区回归,并确认浏览器网络、DOM、console 和 SuperAPI
日志中不存在用户密码、access token 或 API Key 明文。本需求不修改 Sub2API 源码、
版本、账号池或运行配置。
### 4.20 USER-KEY-SKILL-REQ-020 绑定级完整 Skill 强制注入
每条 API Key 与 Skill 绑定应具有独立的 `forceEnabled` 布尔字段,默认值为
`false`。该字段属于绑定关系,不属于 Skill 全局属性;同一个 Skill 绑定到不同
API Key 时,各绑定可以采用不同值。现有 `enabled` 字段继续独立控制绑定是否进入
Gateway 快照,`forceEnabled` 不得隐式启用已停用绑定。
`forceEnabled=false` 时,Gateway 应保持现有稳定行为:
- 注入 `name + description` 摘要;
- 存在公开主文档时提供精确 `SKILL.md` URL
- 由 Agent 在任务匹配时按 Remote Skill 加载合同一次性读取文档。
`forceEnabled=true` 时,Gateway 应在保留稳定 Skill 身份和边界标记的前提下,把
该绑定对应的完整标准化 `SKILL.md` 原文直接追加到请求 instructions。强制注入
不得要求客户端增加 header、环境变量或专用配置,也不得读取或注入 references、
脚本和其他资源。没有完整 `SKILL.md` 的普通摘要 Skill 必须由服务端拒绝开启
`forceEnabled`,禁止保存无法生效的配置或静默退化为摘要模式。
管理 API、默认本地 CLI、显式 `--over-api` CLI 和 API Key 绑定对话框应读写同一
绑定字段。Web 应为每条绑定分别显示普通启用开关和“强制注入完整 Skill”开关;
不支持强制注入的 Skill 应显示不可用状态,而不是隐藏服务端限制。每次变更都应
沿既有 dispatcher 和 snapshot refresh 路径原子发布,并继续执行 API Key
所有权校验。
L0 应验证:
- 新建绑定的 `forceEnabled` 默认为 `false`
- 默认模式只注入摘要和文档 URL,不包含 `SKILL.md` 正文;
- 显式开启后同一 API Key 的 instructions 包含完整正文;
- 同一 Skill 在另一个 API Key 上保持默认关闭;
- 普通摘要 Skill 开启强制注入时被拒绝;
- 关闭普通 `enabled` 后,即使保存 `forceEnabled=true` 也不进入快照;
- 快照和 CLI/API 输出不包含 API Key 明文。
L1 应通过普通 Sub2API 测试用户完成自己 API Key 上的强制注入开关读取、开启、
回读和恢复,并通过 typed Web smoke 验证两个独立开关及不可用状态。公网 Gateway
验收应使用标准 Responses 请求确认默认摘要模式与强制全文模式的实际 instructions
差异;本需求不修改 Sub2API 源码、版本、账号池或运行配置,不进入 L2/L3。
+23 -2
View File
@@ -262,13 +262,34 @@ export default async function superApiManagementSmoke({ page, goto, wait, screen
width: Math.round(rect.width),
};
});
const bindingControls = await dialog.locator(".binding-row").evaluateAll((rows) =>
rows.map((row) => {
const inputs = Array.from(row.querySelectorAll("input[type='checkbox']"));
const enabled = inputs.find((input) => input.getAttribute("aria-label")?.startsWith("启用 "));
const force = inputs.find((input) => input.getAttribute("aria-label")?.startsWith("强制注入 "));
return {
enabledControl: enabled !== undefined,
forceControl: force !== undefined,
forceDisabled: force instanceof HTMLInputElement ? force.disabled : null,
};
}));
const bindingControlsOk = bindingControls.every((row) =>
row.enabledControl && row.forceControl);
const ok = bindingEvidence.placeholder === "请选择要绑定的 Skill"
&& bindingEvidence.visible
&& bindingEvidence.width >= 240;
&& bindingEvidence.width >= 240
&& bindingControlsOk;
if (!ok) addFailure("API Key Skill 下拉框占位符或布局不完整");
const interactionShot = await screenshot("superapi-api-key-bindings.png");
screenshots.push(interactionShot);
interactions.push({ id: item.interaction, ok, ...bindingEvidence, screenshot: interactionShot });
interactions.push({
id: item.interaction,
ok,
...bindingEvidence,
bindingControls,
bindingControlsOk,
screenshot: interactionShot,
});
await dialog.getByLabel("关闭", { exact: true }).click();
if (config.actor === "admin") {
await page.getByRole("button", { name: "注册 API Key", exact: true }).click();