diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index ea43f0fb..cf9db18c 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -290,6 +290,11 @@ lanes: apiKeySecretKey: api-key runnerNamespace: agentrun-v02 secretNamespace: agentrun-v02 + providerSecretNames: + codex: agentrun-v01-provider-codex + gpt-pika: agentrun-v02-provider-gpt-pika + grok: agentrun-v02-provider-grok + dsflash-go: agentrun-v01-provider-dsflash-go toolSecretNames: githubPr: agentrun-v01-tool-github-pr unideskSsh: agentrun-v01-tool-unidesk-ssh diff --git a/docs/MDTODO/details/sub2api-upstream-reliability/R2.7.12_Task_Report.md b/docs/MDTODO/details/sub2api-upstream-reliability/R2.7.12_Task_Report.md new file mode 100644 index 00000000..e9b3187f --- /dev/null +++ b/docs/MDTODO/details/sub2api-upstream-reliability/R2.7.12_Task_Report.md @@ -0,0 +1,17 @@ +# R2.7.12 任务报告 + +## 结论 + +第二轮 `$post-task` 复核发现首版 `capture validate` 内部依次执行 `status` 和两次 `list`,会对捕获目录做三次扫描。该问题已在 SuperAPI 提交 `bec2457 perf: avoid repeated capture validation scans` 中修复,并快进到 `/root/superapi` master。 + +## 调整 + +- 保留 `status` 完成的一次目录 scan,用其内存文件索引定位最新请求和响应 JSONL。 +- 每类只读取最新一行,同时完成 JSON 解析、摘要字段、Secret 脱敏和权限校验。 +- 删除验证路径中的两次 `list` 调用,不改变普通 `capture list` 行为。 +- `capture validate` 的字段、默认不输出 body 的合同和退出码保持不变。 +- 使用 MDTODO CLI 修正 `R2.7.11` 中重复的报告尾句。 + +## 验证 + +在实验分支和快进后的 `/root/superapi` master 运行 `bun scripts/superapi-cli.ts capture validate` 均为 PASS;配额、双向 JSONL、UTC 路径、权限、API Key 脱敏、双向摘要均为 true,`BODY_PRINTED=false`。运行服务未重启。 diff --git a/docs/MDTODO/hwlab-web-product-experience.md b/docs/MDTODO/hwlab-web-product-experience.md index c0869c9d..af991cc5 100644 --- a/docs/MDTODO/hwlab-web-product-experience.md +++ b/docs/MDTODO/hwlab-web-product-experience.md @@ -115,3 +115,7 @@ ### R6.1 [in_progress] 执行 [UniDesk #2529](https://github.com/pikasTech/unidesk/issues/2529):将 Workbench L1 用户入口通过 owning YAML 与 shared public-edge 固定暴露为 `https://lab-dev-workbench.hwpod.com`,CLI 与 `web-probe native-readiness` 只消费固定 HTTPS origin;内部 IP/port 仅用于 bind/probe,端口冲突时修改 YAML 且域名不变;在开发等级 SKILL 中规定所有 L1 一律使用 YAML 固定 HTTPS 域名,并要求执行 L1 时发现历史未暴露实例就逐例登记、治理和验收后再继续,完成任务后将详细报告写入[任务报告](./details/hwlab-web-product-experience/R6.1_Task_Report.md)。 + +#### R6.1.1 [in_progress] + +修复 [UniDesk #2531](https://github.com/pikasTech/unidesk/issues/2531):复用 AgentRun nc01-v02 owning YAML 的现有 provider/tool SecretRef,补齐 Workbench L1 native environment 的 namespace、provider Secret name 和 tool Secret name 映射,禁止创建、复制或反解密钥,并以固定 HTTPS typed CLI 和纯 Kafka SSE 完成 `gpt.pika` smoke,完成任务后将详细报告写入[任务报告](./details/hwlab-web-product-experience/R6.1.1_Task_Report.md)。 diff --git a/docs/MDTODO/sub2api-upstream-reliability.md b/docs/MDTODO/sub2api-upstream-reliability.md index 2abee58c..362c90bd 100644 --- a/docs/MDTODO/sub2api-upstream-reliability.md +++ b/docs/MDTODO/sub2api-upstream-reliability.md @@ -224,7 +224,14 @@ #### R2.7.11 [completed] -复盘 SuperAPI 全量捕获交付并修复已暴露的流程缺口:让 L0 实际加载代理模块以提前发现语法错误;新增 `capture validate` 聚合校验配额、JSONL、UTC 分区、权限、敏感值脱敏和请求/响应摘要;将最短验收流程沉淀到 Sub2API skill,不修改运行网关行为,完成任务后将详细报告写入任务报告,完成任务后将详细报告写入[任务报告](./details/sub2api-upstream-reliability/R2.7.11_Task_Report.md)。 +复盘 SuperAPI 全量捕获交付并修复已暴露的流程缺口:让 L0 实际加载代理模块以提前发现语法错误;新增 `capture validate` 聚合校验配额、JSONL、UTC 分区、权限、敏感值脱敏和请求/响应摘要;将最短验收流程沉淀到 Sub2API skill,不修改运行网关行为,完成任务后将详细报告写入[任务报告](./details/sub2api-upstream-reliability/R2.7.11_Task_Report.md)。 + +#### R2.7.12 [completed] + +复核 `capture validate` 聚合验收自身的调用成本,将三次捕获目录扫描收敛为一次扫描和每类最新一行读取,并验证输出合同不变,完成任务后将详细报告写入[任务报告](./details/sub2api-upstream-reliability/R2.7.12_Task_Report.md)。 +#### R2.7.13 [in_progress] + +为 SuperAPI 增加 skill description 管理面:先完成 L0,再完成 L1,不进入 L2;使用 native PostgreSQL 持久化 skill、脱敏 API Key 注册及别名、按 Key 绑定与启用开关;提供请求历史筛选及脱敏 request/response 详情;后端按 Temporal 规范拆分 API 与 worker,ai-gateway 保持独立进程;Web 使用左侧根导航和多页面工作区,并通过同一 `superapi.hwpod.com` L1 入口验收,完成任务后将详细报告写入[任务报告](./details/sub2api-upstream-reliability/R2.7.13_Task_Report.md)。 ### R2.8 [completed] 整理本 MDTODO FILE 的职责层级和编号,并把同一 SUBITEM 下的直接 SUBSUBITEM 一般不超过 20 固化到 mdtodo-edit skill,完成任务后将详细报告写入[任务报告](./details/sub2api-upstream-reliability/R2.8_Task_Report.md)。 diff --git a/project-management/PJ2026-01/specs/PJ2026-010502-api-key.md b/project-management/PJ2026-01/specs/PJ2026-010502-api-key.md index 8c346a60..67676127 100644 --- a/project-management/PJ2026-01/specs/PJ2026-010502-api-key.md +++ b/project-management/PJ2026-01/specs/PJ2026-010502-api-key.md @@ -78,7 +78,7 @@ D601 v0.3 当前已具备用户自服务 API key 创建、列表、重命名、 | PJ2026-01050202 | Key策略 | 本规格 6.2 | scope、expiry、quota、IP 和 rate limit 绑定 | 权限配额、平台配置 | 受保护 API | | PJ2026-01050203 | Key校验 | 本规格 6.3 | introspection 和 principal 输出 | 账号状态、key 策略 | Agent编排、Cloud API | | PJ2026-01050204 | Key审计 | 本规格 6.4 | last used、拒绝原因和脱敏审计 | key 校验、计量账本 | admin、运营分析 | -| PJ2026-01050205 | Key Skill Catalog | [APIKey Skill Catalog](PJ2026-01050205-api-key-skill-catalog.md) | 按 Key 绑定虚拟 skill 摘要并注入 Agent 请求 | Key校验、后端Profile | Codex-compatible Agent | +| PJ2026-01050205 | SuperAPI | [SuperAPI](PJ2026-01050205-superapi.md) | 按 Key 绑定虚拟 skill 摘要并注入 Agent 请求 | Key校验、后端Profile | Codex-compatible Agent | ## 6. 原子需求 diff --git a/project-management/PJ2026-01/specs/PJ2026-01050205-api-key-skill-catalog.md b/project-management/PJ2026-01/specs/PJ2026-01050205-superapi.md similarity index 94% rename from project-management/PJ2026-01/specs/PJ2026-01050205-api-key-skill-catalog.md rename to project-management/PJ2026-01/specs/PJ2026-01050205-superapi.md index 1ca0c947..9407bee3 100644 --- a/project-management/PJ2026-01/specs/PJ2026-01050205-api-key-skill-catalog.md +++ b/project-management/PJ2026-01/specs/PJ2026-01050205-superapi.md @@ -1,4 +1,4 @@ -# PJ2026-01050205 APIKey Skill Catalog +# PJ2026-01050205 SuperAPI ## 修改历史 @@ -9,14 +9,14 @@ ## 正文 -## PJ2026-01050205 APIKey Skill Catalog 需求规格 +## PJ2026-01050205 SuperAPI 需求规格 ## 1. 文档控制 | 字段 | 内容 | | --- | --- | | 编号 | PJ2026-01050205 | -| 短名 | APIKey Skill Catalog | +| 短名 | SuperAPI | | 层级 | L3 子课题 | | 状态 | 已生效 | | 上级规格 | [PJ2026-010502 APIKey](PJ2026-010502-api-key.md) | @@ -27,11 +27,14 @@ ### 2.1 目的 -APIKey Skill Catalog 负责把企业虚拟 skill 的 `name` 和 `description` +SuperAPI 负责把企业虚拟 skill 的 `name` 和 `description` 绑定到指定下游 API Key,并在请求进入 Codex-compatible 上游前追加到 Agent instructions,使企业可以通过 description 引导 Agent 从公司内网读取 最新文件和脚本。 +本规格中的 SuperAPI 是面向 LLM 请求的上游加载与管理服务,不是 +[PJ2026-010104 AI网关](PJ2026-010104-ai-gateway.md) 定义的硬件节点执行网关。 + ### 2.2 范围内 - 每个 API Key 独立绑定零个或多个虚拟 skill 摘要。 diff --git a/scripts/src/hwlab-native-development.test.ts b/scripts/src/hwlab-native-development.test.ts index a0aa3495..bc1728c6 100644 --- a/scripts/src/hwlab-native-development.test.ts +++ b/scripts/src/hwlab-native-development.test.ts @@ -17,6 +17,11 @@ test("L1 Workbench exposure uses YAML HTTPS origin and separate bind/probe addre expect(env.AGENTRUN_MGR_URL).toBe(native!.agentRun.managerUrl); expect(env.AGENTRUN_API_KEY).toMatch(/^hwl_live_/u); expect(env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE).toBe("gpt.pika"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE).toBe("agentrun-v02"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_GPT_PIKA_SECRET_NAME).toBe("agentrun-v02-provider-gpt-pika"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_CODEX_SECRET_NAME).toBe("agentrun-v01-provider-codex"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME).toBe("agentrun-v01-tool-github-pr"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME).toBe("agentrun-v01-tool-unidesk-ssh"); expect(env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT).toBe("0123456789abcdef0123456789abcdef01234567"); expect(env.HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED).toBe("false"); expect(env.HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED).toBe("true"); diff --git a/scripts/src/hwlab-native-development.ts b/scripts/src/hwlab-native-development.ts index 1f25a1f1..1ee22b42 100644 --- a/scripts/src/hwlab-native-development.ts +++ b/scripts/src/hwlab-native-development.ts @@ -68,6 +68,10 @@ export function hwlabNativeDevelopmentEnvironment(native: HwlabRuntimeNativeDeve if (kafkaBridge === undefined) throw new Error(`${hwlabRuntimeLaneConfigPath()}#lanes.${spec.lane}.targets.${spec.nodeId}.${native.kafka.configRef} is required`); if (native.kafka.hwlabEventConsumerGroupId === kafkaBridge.hwlabEventConsumerGroupId) throw new Error("native Workbench Kafka consumer group must be distinct from the Cloud API live SSE consumer group"); if (!/^[0-9a-f]{40}$/iu.test(sourceCommit)) throw new Error("sourceCommit must be the current HWLAB workspace 40-hex git SHA"); + const providerSecretNames = codeAgent.providerSecretNames; + if (providerSecretNames === undefined) throw new Error(`${hwlabRuntimeLaneConfigPath()}#lanes.${spec.lane}.targets.${spec.nodeId}.codeAgentRuntime.providerSecretNames is required`); + const defaultProviderKey = codeAgent.defaultProviderProfile.toLowerCase().replace(/[^a-z0-9]+/gu, "-"); + if (!providerSecretNames[defaultProviderKey]) throw new Error(`codeAgentRuntime.providerSecretNames.${defaultProviderKey} is required for defaultProviderProfile=${codeAgent.defaultProviderProfile}`); const authorizationSource = readEnvSourceFile({ root: "/root/.unidesk/.state/secrets", sourceRef: codeAgent.apiKeySourceRef, @@ -89,6 +93,10 @@ export function hwlabNativeDevelopmentEnvironment(native: HwlabRuntimeNativeDeve HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: spec.nodeId, HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: codeAgent.defaultProviderProfile, HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: codeAgent.runnerNamespace, + HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE: codeAgent.secretNamespace, + ...providerSecretNameEnvironment(providerSecretNames), + HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME: codeAgent.toolSecretNames.githubPr, + HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME: codeAgent.toolSecretNames.unideskSsh, HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: spec.gitReadUrl, HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: sourceCommit, HWLAB_RUNTIME_API_URL: native.agentRun.runtimeApiUrl, @@ -127,6 +135,13 @@ export function hwlabNativeDevelopmentEnvironment(native: HwlabRuntimeNativeDeve }; } +function providerSecretNameEnvironment(providerSecretNames: Readonly>): NodeJS.ProcessEnv { + return Object.fromEntries(Object.entries(providerSecretNames).map(([profile, secretName]) => { + const profileKey = profile.toUpperCase().replace(/[^A-Z0-9]+/gu, "_"); + return [`HWLAB_CODE_AGENT_AGENTRUN_${profileKey}_SECRET_NAME`, secretName]; + })); +} + function parsePayload(stdout: string): Record { const parsed = JSON.parse(stdout) as unknown; if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("HWLAB Workbench CLI returned invalid JSON");