Merge remote-tracking branch 'origin/master' into fix/2510-pac-release-observe

This commit is contained in:
pikastech
2026-07-18 13:59:03 +02:00
8 changed files with 62 additions and 6 deletions
+5
View File
@@ -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
@@ -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`。运行服务未重启。
@@ -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)。
+8 -1
View File
@@ -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 与 workerai-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)。
@@ -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. 原子需求
@@ -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 摘要。
@@ -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");
+15
View File
@@ -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<Record<string, string>>): 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<string, unknown> {
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");