IronClaw GitHub 扩展:用 `github.get_authenticated_user` 准确识别已认证的 GitHub 用户

原创2026-09-22 21:15:471,102 阅读
文章标签:人工智能AI 应用交互助手AI Agent

IronClaw GitHub 扩展:用 github.get_authenticated_user 准确识别已认证的 GitHub 用户

本文围绕 IronClaw 开源仓库中 GitHub 扩展包(github)提供的 github.get_authenticated_user 能力展开,讲解它是什么、在何种场景下必须调用、为什么不能从 github.list_repos 的返回结果推断认证用户身份,以及该能力在 WASM 扩展中的底层实现、认证注入与测试验证。读完本文,你将掌握在 IronClaw 中正确回答"我是谁 / 当前连接的是哪个 GitHub 账号"类问题的完整方法,并理解其安全设计依据。

github.get_authenticated_user 是 IronClaw 内置 GitHub 工具集中专门用于识别当前配置 token 所认证的 GitHub 用户的只读能力,其 prompt 文档位于 crates/extensions/packages/github/prompts/github/get_authenticated_user.md,属于 GitHub 扩展包(crates/extensions/packages/github)49 个工具面(49 tools)之一。

能力定位:何时必须调用它

根据关联文档,github.get_authenticated_user 的唯一职责是:

使用 github.get_authenticated_user 来识别由已配置 token 完成认证的 GitHub 用户。

它面向的问题非常明确,包括:

  • "who am I on GitHub?"(我在 GitHub 上是谁?)
  • "which GitHub account is connected?"(当前连接的是哪个 GitHub 账号?)
  • 在 Agent 准备对外陈述"当前认证的 GitHub 登录名"这一事实之前,必须先调用本能力取得确凿依据。

也就是说,这是一条"事实获取"工具而非"操作"工具:它不修改任何资源,只负责把"这个 token 背后到底是哪个账号"这一信息从 GitHub 侧取回来。从扩展清单 manifest.toml 中可以看到该工具的定义:

[[tools]]
origin_gate_matrix = { loop_run = "gated_unless_granted", product = "forbidden", automation = "forbidden" }
id = "github.get_authenticated_user"
description = "Get the authenticated GitHub user for the configured token."
effects = ["network", "use_secret"]
default_permission = "allow"
visibility = "model"
input_schema_ref = "schemas/github/get_authenticated_user.input.v1.json"
prompt_doc_ref = "prompts/github/get_authenticated_user.md"

其中两个值得注意的配置点:

  • effects = ["network", "use_secret"]:调用该工具会产生一次网络请求,并使用机密凭据(GitHub token)。IronClaw 的能力系统据此进行效应声明与门控管理。
  • default_permission = "allow":该工具默认放行,无需用户逐次确认(与 create_issue、create_pull_request 等写操作默认 ask 形成对比),因为它是一个只读的、低风险的身份查询能力。

关键陷阱:不要从 list_repos 的结果推断登录名

关联文档给出了本能力存在的最重要理由,也是 Agent 最容易犯的错误:

不要从 github.list_repos 返回的仓库所有者推断已认证的登录名;/user/repos 可能包含认证用户能够访问的组织所拥有的仓库。

这条警告的底层依据可以从源码得到印证。github.list_repos 的实现在 crates/extensions/packages/github/wasm-src/src/api/repos.rs 中:

pub(crate) fn list_repos(
    repo_type: Option<RepoListType>,
    page: Option<u32>,
    limit: Option<u32>,
) -> Result<String, String> {
    validate_page(page)?;
    validate_limit(limit)?;
    let limit = limit.unwrap_or(30).min(100); // Cap at 100
    let mut path = format!("/user/repos?per_page={}", limit);
    ...
    github_request("GET", &path, None)
}

它请求的端点是 /user/repos——这是"当前用户可访问的仓库"列表,而非"我拥有的仓库"列表。其返回结果中的 owner 字段可能是个人账号,也可能是该用户有权限访问的组织账号。因此,如果 Agent 看到列表里某个仓库的 owner 是 nearai,就断言"当前登录用户是 nearai",就会产生事实性错误:真实情况可能只是"该 token 拥有访问 nearai 组织仓库的权限"。

仓库内的单元测试也直接验证了 /user/repos 端点会携带 type 过滤参数(见 crates/extensions/packages/github/wasm-src/src/lib.rs 中 list_repos_appends_type_for_authenticated_user、list_repos_appends_supported_types_for_authenticated_user 等测试,断言路径形如 /user/repos?per_page=2&type=member、/user/repos?per_page=3&type=all 等),进一步说明该端点天然是"认证用户视角的可访问仓库集合"。

结论:唯一可靠的身份来源是 GitHub 的 /user 端点(即本工具),而不是任何仓库列表的 owner 字段。

底层实现:从能力 ID 到 HTTP 请求的完整调用链

github.get_authenticated_user 并非独立编写的业务逻辑,而是 GitHub 扩展 WASM 模块中的一个标准动作。整个调用链如下:

1. 能力 ID 映射为动作(dispatch)

调用时,宿主上下文会携带 {"capability_id":"github.get_authenticated_user"},分发层在 crates/extensions/packages/github/wasm-src/src/dispatch.rs 中将其映射到具体实现:

GitHubAction::GetAuthenticatedUser {} => get_authenticated_user(),

动作类型 GitHubAction::GetAuthenticatedUser 定义于 crates/extensions/packages/github/wasm-src/src/types.rs:

#[serde(rename = "get_authenticated_user")]
GetAuthenticatedUser {},

2. 动作实现:请求 /user 端点

核心实现位于 crates/extensions/packages/github/wasm-src/src/api/repos.rs:

pub(crate) fn get_authenticated_user() -> Result<String, String> {
    github_request("GET", "/user", None)
}

可以看到它不发请求体、不做参数校验(因为该能力本就不需要任何输入参数),直接以 GET 方式请求 GitHub REST API 的 /user 端点。

3. 请求封装:经宿主 HTTP 出口出网

github_request 的实现在 crates/extensions/packages/github/wasm-src/src/request.rs 中,它定义了请求的完整形态:

const GITHUB_API_ROOT: &str = "https://api.github.com";
const GITHUB_API_VERSION: &str = "2026-03-10";
const HTTP_TIMEOUT_MS: u32 = 10_000;

pub(crate) fn github_request(
    method: &str,
    path: &str,
    body: Option<String>,
) -> Result<String, String> {
    let url = format!("{GITHUB_API_ROOT}{path}");
    let headers = serde_json::json!({
        "Accept": "application/vnd.github+json",
        "Content-Type": "application/json",
        "X-GitHub-Api-Version": GITHUB_API_VERSION,
        "User-Agent": "IronClaw-GitHub-Reborn-WASM"
    });
    ...
    let response = crate::near::agent::host::http_request(
        method,
        &url,
        &headers.to_string(),
        body_bytes.as_deref(),
        Some(HTTP_TIMEOUT_MS),
    )
    ...
}

这里的关键点在于:网络请求并不是在 WASM guest 内部直接发起的,而是通过 near::agent::host::http_request 委托给宿主(host)的 HTTP 出口(HTTP egress)执行。这正对应关联文档中"该能力通过宿主 HTTP 出口读取 GitHub API"的描述。好处是:出网行为受宿主网络策略约束,凭据注入也由宿主管控,而不是把 token 交给不受控的 guest 代码。

响应处理逻辑还包括:2xx 直接透传响应体;422 且响应体符合 GitHub "Validation Failed" 结构时返回稳定错误码;401 时会捕获 provider 返回的 message 字段(截断至 512 字符)供宿主用于认证门控诊断。

4. 输入契约:空参数工具

github.get_authenticated_user 的输入 schema 位于 crates/extensions/packages/github/schemas/github/get_authenticated_user.input.v1.json,是一个不含任何属性的空对象:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "GitHub get_authenticated_user input",
  "type": "object",
  "additionalProperties": false,
  "properties": {},
  "required": []
}

additionalProperties: false 意味着:如果调用方(模型)试图传入任何多余参数,校验会直接失败。该 schema 在 WASM 中通过 crates/extensions/packages/github/wasm-src/src/schema.rs 的 include_str! 嵌入,作为工具输入校验的依据。

5. 输出:GitHub 原始 JSON 透传

响应体为 GitHub /user 端点返回的原始 JSON(不做字段裁剪),模型可直接从中读取 login、type 等字段。仓库测试 crates/extensions/packages/github/wasm-src/src/lib.rs 的 get_authenticated_user_uses_user_endpoint 用例给出了典型返回形态:

test_support::set_response(Ok(json!({
    "login": "serrrfirat",
    "type": "User"
})
.to_string()));

let output = execute_inner(
    r#"{}"#,
    Some(r#"{"capability_id":"github.get_authenticated_user"}"#),
)
.expect("github.get_authenticated_user should return authenticated user");
let output: serde_json::Value =
    serde_json::from_str(&output).expect("mock output should be JSON");
assert_eq!(output["login"], "serrrfirat");
assert_eq!(output["type"], "User");

let requests = test_support::requests();
assert_eq!(requests.len(), 1);
assert_eq!(requests[0].method, "GET");
assert_eq!(requests[0].body, None);
assert_eq!(requests[0].path, "/user");

该测试同时验证了三件事:调用会精确发出 GET /user(无请求体)、返回的 JSON 中 login 与 type 字段可被正确解析、且请求数恰好为 1(不存在额外请求)。

认证与凭据注入:token 从哪里来、如何被使用

github.get_authenticated_user 的 effects 包含 use_secret,意味着它必须依赖一个已配置的 GitHub 凭据才能工作。凭据的完整链路定义在 manifest.toml 中。

凭据句柄与注入方式

[[tools.credentials]]
handle = "github_runtime_token"
vendor = "github"
audience = { scheme = "https", host = "api.github.com" }
injection = { type = "header", name = "authorization", prefix = "token " }
placeholder_env = "GH_TOKEN"

也就是说,每个 GitHub 工具(包括本能力)都声明使用句柄 github_runtime_token,其受众(audience)是 https://api.github.com,注入方式为向 authorization 请求头写入 token <GH_TOKEN>。placeholder_env = "GH_TOKEN" 则提示该凭据对应的占位环境变量为 GH_TOKEN。

product-auth 账户配置

在扩展清单底部,[auth.github] 定义了该厂商的认证配置:

[auth.github]
method = "api_key"
display_name = "GitHub personal access token"
fields = [ { handle = "github_runtime_token", label = "Personal access token", secret = true } ]
validation = { method = "GET", url = "https://api.github.com/user", success_status = [200], inject = { handle = "github_runtime_token", type = "header", name = "authorization", prefix = "Bearer " } }

这印证了关联文档中的最后一句"该能力需要配置 GitHub product-auth 账户":用户需要在 IronClaw 中配置一个 GitHub Personal Access Token,作为该 product-auth 账户的凭据。校验方式本身也很有启发性——宿主会以 GET https://api.github.com/user(同样的 /user 端点)并注入 Authorization: Bearer <token> 来验证 token 是否有效,成功状态码为 200。这与 github.get_authenticated_user 的实现端点完全一致,二者共享"查询 /user 即验证身份"的同一事实基础。

在 IronClaw 中的实际使用场景与最佳实践

综合关联文档与源码,推荐的使用姿势如下:

  1. 在需要陈述账号身份时优先调用:当用户询问"我在 GitHub 上是谁""当前连接了哪个账号",或 Agent 需要在对话/报告/审计日志中声称"以账号 X 的身份执行了操作"时,先调用 github.get_authenticated_user 取得 login 字段作为事实来源。
  2. 把身份查询与仓库操作解耦:不要因为 github.list_repos、github.get_repo 等返回结果中的 owner 恰好等于某个登录名,就推断"当前认证用户就是该 owner"。/user/repos 的视角是"token 可访问的仓库集合",包含组织仓库,owner 字段不能作为身份证据。唯一的身份证据是 /user 端点。
  3. 善用其零参数与低权限设计:由于输入为空对象、默认权限为 allow、仅产生只读网络请求,可以在流程开始时作为"身份探针"安全调用,无需额外授权确认;其失败(如 401 或 egress 被拒)也能快速暴露 token 配置问题——例如请求失败时的错误码体系(github_api_error_status_401、github_api_egress_denied 等)都定义在 crates/extensions/packages/github/wasm-src/src/request.rs 中,便于排查。

延伸阅读

登录后查看全文
ironclaw