From 366d0c5697132f7e5a0b14a5eb5c9739d53e8e5c Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 10 Jun 2026 21:47:20 +0800 Subject: [PATCH] fix: add Artificer GitHub PR token projection --- config/aipods/artificer.yaml | 10 ++++++++++ docs/reference/spec-v01-aipod-spec.md | 3 ++- docs/reference/spec-v01-secret-distribution.md | 1 + src/selftest/cases/76-aipod-spec.ts | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/config/aipods/artificer.yaml b/config/aipods/artificer.yaml index 85815ce..479dd43 100644 --- a/config/aipods/artificer.yaml +++ b/config/aipods/artificer.yaml @@ -36,6 +36,16 @@ spec: - auth.json - config.toml toolCredentials: + - tool: github + purpose: github-pr + secretRef: + name: agentrun-v01-tool-github-pr + keys: + - GH_TOKEN + projection: + kind: env + envName: GH_TOKEN + secretKey: GH_TOKEN - tool: unidesk-ssh purpose: ssh-passthrough secretRef: diff --git a/docs/reference/spec-v01-aipod-spec.md b/docs/reference/spec-v01-aipod-spec.md index eea5566..e411628 100644 --- a/docs/reference/spec-v01-aipod-spec.md +++ b/docs/reference/spec-v01-aipod-spec.md @@ -58,6 +58,7 @@ spec: - 使用 `backendProfile=sub2api`,模型声明为 `gpt-5.5`,reasoning effort 为 `xhigh`。 - 通过 provider SecretRef `agentrun-v01-provider-sub2api` 获取 `auth.json` 与 `config.toml`。 +- 通过 `toolCredentials` 获取 GitHub Issue/PR 写入 token:`agentrun-v01-tool-github-pr` -> env `GH_TOKEN`。 - 通过 `toolCredentials` 获取 UniDesk SSH 透传 token:`agentrun-v01-tool-unidesk-ssh` -> env `UNIDESK_SSH_CLIENT_TOKEN`。 - 通过 `toolCredentials` 获取 GitHub SSH 凭据:`agentrun-v01-tool-github-ssh` -> volume `/home/agentrun/.ssh`。 - 通过 GitHub SSH 直接 checkout 多仓库 gitbundle,装配 UniDesk repo 中的 `unidesk-*` skills、AgentRun repo 中的 `tools/trans`、`tools/tran`、`tools/apply_patch`,以及 `agent_skills` repo 中的 `dad-dev`、`cli-spec`、`docs-spec`、`git-spec`;Artificer 默认不依赖 G14 git mirror 的覆盖范围。 @@ -120,7 +121,7 @@ CLI: ## 测试规格 -- A1:`config/aipods/artificer.yaml` 能被 manager list/show/render,render 结果包含 `backendProfile=sub2api`、`model=gpt-5.5`、`reasoningEffort=xhigh`、provider SecretRef、UniDesk SSH env projection、GitHub SSH volume projection、Artificer `gitMirror.enabled=false`、AgentRun runner tools gitbundle 和 gitbundle requiredSkills。 +- A1:`config/aipods/artificer.yaml` 能被 manager list/show/render,render 结果包含 `backendProfile=sub2api`、`model=gpt-5.5`、`reasoningEffort=xhigh`、provider SecretRef、GitHub PR token env projection、UniDesk SSH env projection、GitHub SSH volume projection、Artificer `gitMirror.enabled=false`、AgentRun runner tools gitbundle 和 gitbundle requiredSkills。 - A2:`queue submit --aipod Artificer --dry-run` 输出标准 `queue-submit-plan`,且 `idempotencyKey`、prompt 与 metadata 被保留。 - A3:Artificer 默认 `gitMirror.enabled=false` 时 GitHub URL 保持 SSH fetch;显式启用 `gitMirror` 后 GitHub URL 改写到 mirror base URL;非 GitHub URL 不改写。 - A4:runner Job dry-run 支持 tool credential volume mount,并且 response/manifest/event 不泄漏 Secret 明文。 diff --git a/docs/reference/spec-v01-secret-distribution.md b/docs/reference/spec-v01-secret-distribution.md index 7b65d86..bf14f22 100644 --- a/docs/reference/spec-v01-secret-distribution.md +++ b/docs/reference/spec-v01-secret-distribution.md @@ -130,6 +130,7 @@ Run 的 `executionPolicy.secretScope` 只能包含引用,不包含值。provid - Secret projection 不能直接作为 `CODEX_HOME`。Codex app-server 会读取并可能维护默认配置、PATH 或运行态文件;把只读 Secret volume 直接挂到 `CODEX_HOME` 会造成启动期写入失败。v0.1 的固定边界是:Secret volume 只读、`/home/agentrun` 由 `emptyDir` 提供可写 runtime home、复制动作只发生在 runner/backend 容器内且不打印文件内容。 - SecretRef 不存在或 RBAC 不允许时,run 必须失败为结构化 `failureKind=secret-unavailable` 或等价错误,不得降级成无凭证重试风暴。 - `toolCredentials` 的 SecretRef/projection/redaction 规则以 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md) 为准;本文只约束 Secret value 不落库、不输出、不进入 Git source 或 GitOps Secret data。 +- GitHub Issue/PR 写入 token 与 GitHub SSH 凭据是两条独立投影:前者走 `GH_TOKEN` env,后者走 `/home/agentrun/.ssh` volume,互不替代。 ## runner-job transientEnv diff --git a/src/selftest/cases/76-aipod-spec.ts b/src/selftest/cases/76-aipod-spec.ts index 65f9342..92d04d0 100644 --- a/src/selftest/cases/76-aipod-spec.ts +++ b/src/selftest/cases/76-aipod-spec.ts @@ -39,6 +39,7 @@ const selfTest: SelfTestCase = async (context) => { const providerCredentials = secretScope.providerCredentials as JsonRecord[]; assert.equal(providerCredentials.some((item) => item.profile === "sub2api" && ((item.secretRef as JsonRecord).name) === "agentrun-v01-provider-sub2api"), true); const toolCredentials = secretScope.toolCredentials as JsonRecord[]; + assert.equal(toolCredentials.some((item) => item.tool === "github" && ((item.projection as JsonRecord).kind) === "env" && ((item.projection as JsonRecord).envName) === "GH_TOKEN" && ((item.secretRef as JsonRecord).name) === "agentrun-v01-tool-github-pr"), true); assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && ((item.projection as JsonRecord).envName) === "UNIDESK_SSH_CLIENT_TOKEN"), true); assert.equal(toolCredentials.some((item) => item.tool === "github" && ((item.projection as JsonRecord).kind) === "volume" && ((item.projection as JsonRecord).mountPath) === "/home/agentrun/.ssh"), true); const bundle = task.resourceBundleRef as JsonRecord;