十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linear 集成 Claude Managed Agent:基于 Bun 的无状态 Webhook 桥接实战指南

Linear 集成 Claude Managed Agent:基于 Bun 的无状态 Webhook 桥接实战指南 Linear 集成 Claude Managed Agent基于 Bun 的无状态 Webhook 桥接实战指南【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks导读本指南以开源仓库managed_agents/linear/目录下的完整示例为核心讲解如何在 Linear Issue 中以mention唤醒一个 Claude Managed Agent并把 Claude 的运行结果以评论形式回写到对应 Linear 会话。通过阅读本文你将掌握信号型 Webhook 轮询式数据拉取 metadata 无状态路由这套可复用的双向桥接架构并学会从 OAuth 授权、Agent 预置到双 Webhook 联调的完整落地流程可直接照搬到其他工单/协作系统与 Claude Managed Agents 的集成场景。1. 场景与整体架构该示例解决一个现实问题工程师常驻在 Linear而 Claude Agent 跑在 Anthropic 平台上。示例通过一个自托管的Linear × CMAClaude Managed Agents桥接服务让两边不用切换工具即可协作。核心数据流见 README.md如下Linear mention ──▶ /linear-webhook ──▶ sessions.create ( metadata) ──▶ 200 │ Claude runs to idle on Anthropic infra │ /cma-webhook ◀── session.status_idled ◀──────────┘ │ └──▶ sessions.retrieve → read metadata → createAgentActivity两条链路的要点入口链路Linear 收到 Issue 中的mention后把AgentSessionEventPOST 到桥接服务的/linear-webhook桥接随即调用 CMA 的sessions.create创建会话并写入路由 metadata然后发送user.message事件作为 prompt。回程链路Claude 在 Anthropic 基础设施上运行直到 idleAnthropic 把session.status_idled事件 POST 到桥接的/cma-webhook桥接通过sessions.retrieve取回会话并读取 metadata据此调用createAgentActivity把回复写回 Linear。项目作者在 skill.md 中反复强调一个关键设计metadatalinear_session_id、linear_org_id就是桥接的全部路由状态——Claude 完成后的 idle 事件里只有一个会话 ID桥接不保存任何业务状态全靠从 CMA 会话元数据读回 该回给哪个 Linear 会话、哪个组织。2. 为什么需要一座桥两种协议之间的翻译理解这个项目必须先建立三个心智模型均来自 skill.mdWebhook 本质是门铃而非快递Anthropic 的session.status_idled载荷被刻意做得很薄只有{type, id}。你永远要主动跟进用sessions.retrieve(id)拿 metadata用sessions.events.list(id)拿输出。推信号、拉数据——这样重试成本低、数据永不过期。项目源码中handleCmaWebhook正是先 retrieve 再 list印证了这一模式。两个 Webhook、一座桥Linear → 桥的事件携带agentSession.id、organizationId与 Issue 上下文Anthropic → 桥的事件只携带 CMA 会话 ID。两边都不携带回调 URL也不携带 Agent 输出两者只是带 ID 的信号。桥目前是必要的Linear 的 Agent Platform 与 CMA 没有共享的线上格式或凭据必须有人拿着两边的密钥在入口把 Linear mention 翻译成 CMA user.message在出口把 CMA idle 翻译成 Linear comment。这就是src/main.ts、src/agent.ts、src/cma-webhook.ts、src/oauth.ts四个模块组成的桥接服务。3. 目录结构与快速开始managed_agents/linear/ ├── CLAUDE.md # 给 Claude 的上下文说明setup 顺序、扩展方向 ├── README.md # 总览、架构、快速开始 ├── skill.md # 设置指南心智模型、坑、调试表 ├── package.json # Bun 工程SDK 依赖声明 ├── tsconfig.json ├── setup/ │ └── create-agent.ts # 一次性agents.create environments.create └── src/ ├── main.ts # Bun server路由分发启动时校验环境变量 ├── oauth.ts # Linear OAuthactorapp 本地 token 存储 ├── agent.ts # sessions.create user.message带路由 metadata └── cma-webhook.ts # webhooks.unwrap → 过滤 → 回帖回复快速开始按 README.mdcd managed_agents/linear bun install claudeclaude会话内直接提问walk me through setting this up.。Claude 会读取同目录的 skill.md并按真实可行的顺序驱动配置——Linear OAuth 应用、Anthropic Agent Webhook、env vars、bun run dev。CLAUDE.md中还要求 Claude 先调用/claude-apiskill 加载完整的 Managed Agents API 参考agents、sessions、environments、events、webhooks 等作为编写 SDK 调用的唯一事实来源避免凭感觉猜字段名。依赖方面该示例基于 Bun 运行type: module依赖anthropic-ai/sdk与linear/sdk。README 明确要求anthropic-ai/sdk≥ 0.95.1其中才包含beta.sessions、beta.webhooks.unwrap等 Managed Agents API 能力package.json 中实际锁定为anthropic-ai/sdk: ^0.95.1、linear/sdk: ^81.0.0。4. 一次性预置setup/create-agent.ts首次部署时运行bun run setup对应 npm scriptsetup: bun run setup/create-agent.ts。该脚本做两件一次性的事见 setup/create-agent.ts创建云端环境environmentconst env await anthropic.beta.environments.create({ name: linear-bridge-${Date.now()}, config: { type: cloud, networking: { type: unrestricted } }, });使用带时间戳的命名以便区分多套环境网络配置为 unrestricted。创建 Agentconst agent await anthropic.beta.agents.create({ name: Linear Assistant, model: claude-opus-4-7, system: You are a helpful assistant embedded in Linear. Keep replies concise and actionable — they are posted as comments. Do not invent issue IDs, users, or project names., tools: [{ type: agent_toolset_20260401, default_config: { enabled: true } }], });注意 system prompt 的定位回复会被作为评论张贴到 Linear因此要求简洁、可执行且不得编造 Issue ID、用户或项目名。agent_toolset_20260401是默认启用的 Agent 工具集。脚本结束后会打印两行 ID把它们抄进.env.localCLAUDE_ENVIRONMENT_IDenv.id CLAUDE_AGENT_IDagent.id5. 桥接服务主入口路由与环境变量校验src/main.ts 启动时首先强制校验四个必需环境变量缺失任何一个即打印FATAL: var is required并以退出码 1 终止LINEAR_WEBHOOK_SIGNING_SECRETLinear 侧 Webhook 签名密钥ANTHROPIC_WEBHOOK_SIGNING_KEYAnthropic 侧 Webhook HMAC 签名密钥即 Console 里的whsec_...CLAUDE_AGENT_ID/CLAUDE_ENVIRONMENT_ID上一步bun run setup的输出。服务用Bun.serve起 HTTP server默认端口 3000可用PORT覆盖BASE_URL默认http://localhost:${PORT}。路由表路径方法处理函数职责/GET内联健康检查返回{status:ok}/oauth/authorizeGEThandleOAuthAuthorize重定向到 Linear 授权页/oauth/callbackGEThandleOAuthCallback收 code、换 token、记录组织/linear-webhookPOSTLinearWebhookClienthandler接收 Linear Agent 事件/cma-webhookPOSThandleCmaWebhook接收 Anthropic 会话状态事件启动日志会打印三条便于联调的 URL。Linear 侧的事件通过new LinearWebhookClient(secret).createHandler()注册源码中监听AgentSessionEvent命中后打印日志并调用kickoffAgentSession(event)。6. 入口链路Linear mention → CMA 会话6.1 事件类型与 10 秒 ack 规则linear/sdk/webhooks的 handler 反序列化出的AgentSessionEvent见 src/agent.ts 的接口定义携带agentSession.idLinear 侧会话 IDagentSession.issueIssue 的identifier、title、可选descriptionagentSession.comment/agentActivitymention 附带的用户消息previousComments历史评论数组organizationId组织 IDpromptContext可选若存在则直接作为完整 prompt。kickoffAgentSession的第一步并不是创建 CMA 会话而是立即回写一条 ackconst accessToken await getAccessToken(organizationId); const linear new LinearClient({ accessToken }); // Linear requires a first activity within 10s. await linear.createAgentActivity({ agentSessionId: agentSession.id, content: { type: thought, body: Thinking... }, });这正是 skill.md 强调的Linear 10 秒 ack 规则桥接必须在收到AgentSessionEvent后 10 秒内 post 任意agentActivity一个{type: thought}就够否则 Linear 会将会话标记为失败。因此这条 ack 必须在创建 CMA 会话之前发出。6.2 创建会话并写入路由 metadataconst session await anthropic.beta.sessions.create({ agent: CLAUDE_AGENT_ID, environment_id: CLAUDE_ENVIRONMENT_ID, metadata: { linear_session_id: agentSession.id, linear_org_id: organizationId, }, }); await anthropic.beta.sessions.events.send(session.id, { events: [{ type: user.message, content: [{ type: text, text: buildPrompt(event) }] }], });代码注释点明了设计意图Stash the Linear routing info on the CMA session. The idle webhook later delivers only a session ID; we read this metadata back to know where to post the reply.把 Linear 路由信息暂存在 CMA 会话上。之后的 idle webhook 只给会话 ID我们读回 metadata 就知道该把回复发到哪。6.3 组 prompt若事件自带promptContext则直接采用否则buildPrompt按优先级拼接Issue: identifier - title附Description: descriptionPrevious comments:逐条列出历史评论User message:取agentActivity.content.body或agentSession.comment.body。各段用空行分隔若全为空则回退到兜底文案Hello! How can I help?。7. 回程链路cma-webhook 处理 idle 事件Anthropic 在会话结束idle 或 terminated后回调/cma-webhook核心逻辑在 src/cma-webhook.ts分五步验签与解析读原始 body 文本调用anthropic.beta.webhooks.unwrap(rawBody, { headers })完成 HMAC 时间戳校验与事件解析。注意注释里的关键细节unwrap()需要普通 header map 而不是 fetch 的Headers对象所以先做了Object.fromEntries(req.headers)。验签失败返回 401。幂等去重seenEventIds集合按event.id去重。Anthropic 重试失败投递时会复用同一个顶层event.id因此去重后返回 204其余情况一律触发重试——大约连续 20 次失败会自动停用端点。事件类型分发session.status_terminated走postTerminationErrorretrieve 会话、读 metadata、以{type: error, body: Agent session terminated unexpectedly.}回帖非session.status_idled一律静默返回 204。retrieve 并按 metadata 过滤这是工作区级 Webhook 必须做的防护——Anthropic 工作区 Webhook 会收到该工作区内每一个会话的事件不只你自己的。因此对任何session.status_idled都要sessions.retrieve读session.metadata?.linear_session_id/linear_org_id两个 key 缺失包括同工作区其他 API key 创建的、当前 key 读不到的会话——retrieve 抛 404/403就直接返回 204。代码用 try/catch 包裹 retrieve失败即静默忽略。拉取输出并回帖用sessions.events.list(id)遍历事件历史收集所有agent.message中的type text块拼接成回复文本。源码用for await迭代注释明确提示页对象会自动翻页若 Agent 产出的事件很多直接裸取第一页可能得到空回复。若最终文本为空则 204否则用 Linear 侧 token 调linear.createAgentActivity({ agentSessionId: linearSessionId, content: { type: response, body: responseText } })把结果作为评论回复写回原 Issue。if (seenEventIds.has(event.id)) return new Response(null, { status: 204 }); seenEventIds.add(event.id); // ... retrieve → filter metadata → events.list 收集文本 → createAgentActivity8. Linear OAuthactorapp 与本地 token 存储桥接要以应用身份回帖而不是用个人 key这依赖 src/oauth.ts 实现的 OAuth 流程。8.1 授权与回调/oauth/authorize用 URLSearchParams 构造授权跳转response_typecode、scoperead,write,app:assignable,app:mentionable、actorapp然后 302 到https://linear.app/oauth/authorize?…。/oauth/callback收code后向https://api.linear.app/oauth/token换 token再查询{ organization { id name } }拿到组织信息以org id 为 key把{accessToken, refreshToken, expiresAt}存入本地.linear-tokens.json最后返回一个 Agent installed in 的 HTML 页面。actorapp是整个 OAuth 里承重的参数skill.md 原话scopeapp:assignable,app:mentionable加上actorapp才会在 Linear 工作区里创建出一个出现在 -picker 中的 app user。个人LINEAR_API_KEY做不到这一点——桥接必须以 app 身份、通过 OAuth token 回帖。8.2 Token 刷新getAccessToken(orgId)在 token 剩余有效期不足 5 分钟时用grant_typerefresh_token主动换取新 token 并持久化否则直接返回现有 access token。整个模块以MaporgId, TokenEntry为内存态落盘到项目根目录的.linear-tokens.jsonimport.meta.dir/..这是演示级存储生产环境应替换为真正的密钥存储见第 10 节。9. 关键坑位清单与故障排查skill.md 把文档里看不出来、最耗调试时间的坑集中列出建议直接对照坑位说明与对策Anthropic Webhook 是工作区级作用域Console 注册的端点只收同一工作区内会话的事件。若ANTHROPIC_API_KEY属于工作区 A却在工作区 B 注册端点会静默零投递。核对你 API key 的 Workspace 列与 Console Webhooks 页的工作区选择器一致。工作区 Webhook 会收到工作区内所有会话的事件必须 retrieve → 检查自己的 metadata key → 非己事件返回 204同时兜住 retrieve 的 404/403同工作区其他 key 的会话读不到。只订阅需要的事件类型session.status_idled、session.status_terminated别选 All events生产建议使用专用 Anthropic 工作区。Linear OAuth 应用仅工作区管理员可建入口在linear.app/workspace/settings/api侧边栏 Administration → API → OAuth Applications。非管理员可用免费个人工作区测试Developer URL 字段必填但纯展示性任意真实https://URL 即可。actorapp是承重参数只有scopeapp:assignable,app:mentionableactorapp才会创建可被 的 app user。Linear 10 秒 ack 规则收到AgentSessionEvent后 10 秒内必须先回任意agentActivity且要在创建 CMA 会话之前。event.id就是幂等键对同一event.id去重处理或决定忽略后都返回 2xx否则触发重试约连续 20 次失败自动停用端点。签名头名称不一致文档写X-Webhook-Signature线上实际是Webhook-Signature/Webhook-Id/Webhook-TimestampStandard Webhooks 规范。SDK 的webhooks.unwrap()已处理仅手写验签时需要留意。静默失败的排查路径Thinking… 从未出现→ Linear Webhook 没送达检查 Linear 应用里的 webhook URL、ngrok 是否在跑。出现 Thinking… 但无回复→curl localhost:4040/api/requests/httpngrok 请求日志确认是否收到 POST若没有/cma-webhook请求多为 Anthropic 侧工作区不匹配或端点未保存若收到但 401则是签名密钥不匹配。回复为空→sessions.events.list可能分页Agent 产出事件多时需迭代翻页源码已用for await自动翻页处理。10. 本地联调检查清单综合 skill.md 的 checklist一套完整的本地联调顺序为起隧道ngrok http 3000或cloudflared tunnel记下公网 URL后续所有配置都用它。bun run setup把CLAUDE_AGENT_ID/CLAUDE_ENVIRONMENT_ID抄入.env.local。建 Linear OAuth 应用Administration → API → OAuth Applications → Create newDeveloper URL 填任意真实https://URL展示用callback 填url/oauth/callbackwebhook 填url/linear-webhook事件选 Agent session events复制 client ID/secret 与 webhook secret。Anthropic Console → Webhooks填url/cma-webhook事件选session.status_idledsession.status_terminated复制whsec_...——务必与你的 API key 处于同一工作区。填好.env.localbun run dev。浏览器访问url/oauth/authorize→ 同意授权 → 看到 Agent installed.。在某个 Issue 里 mention 它。main.ts中四个必需环境变量与.env.local的对应关系可对照第 5 节的校验清单逐项核对。11. 生产化建议与扩展方向生产化skill.md Production notes用真实部署替换 ngrokCloudflare Workers、Fly 等其余逻辑不变把内存里的seenEventIdsSet 换成 Redis 或数据库实现多实例幂等把.linear-tokens.json文件替换为真正的密钥存储把 Anthropic 端点的订阅收窄到恰好你要处理的事件。功能扩展CLAUDE.md 给出的官方扩展路线在基础桥接通之后可让 Claude 继续编辑setup/create-agent.ts和/或src/agent.ts以扩展能力接入 GitHub 仓库在sessions.create的resources: [{type: github_repository, ...}]挂载仓库让 Agent 能读代码MCP 工具给 Agent 配mcp_serversmcp_toolset如 Linear 或 GitHub MCP使其从只回复升级为能行动凭据通过 vaultvault_ids注入Outcomes 迭代循环把user.message换成user.define_outcome事件让 Agent 按 rubric 评分自迭代Multiagent 多智能体在 Agent 上配置multiagent: {type: coordinator, agents: [...]}协调者 子 Agent 分工Memory store 跨会话记忆resources: [{type: memory_store, ...}]实现跨会话持久化自定义工具通过agent.custom_tool_use/user.custom_tool_result把执行放到宿主侧。这些扩展的精确 API 形状以/claude-apiskill 提供的shared/managed-agents-*.md文档为准。12. 关键文件速查文件一句话定位managed_agents/linear/README.md架构总览、快速开始、文件清单与 SDK 版本下限managed_agents/linear/skill.md心智模型、全部 gotcha、调试表、本地 checklist、生产 notesmanaged_agents/linear/CLAUDE.md面向 Claude 的设置执行上下文与扩展方向managed_agents/linear/src/main.ts环境变量校验 路由表Bun.servemanaged_agents/linear/src/agent.ts10s ack sessions.createmetadata prompt 组装managed_agents/linear/src/cma-webhook.tsunwrap 验签、幂等去重、metadata 过滤、回帖、termination 错误处理managed_agents/linear/src/oauth.tsactorapp授权流与 token 刷新/落盘managed_agents/linear/setup/create-agent.ts一次性预置 Agent Environment整个示例的核心方法论可以一句话总结用两个信号型 Webhook 串联两个平台用 CMA 会话 metadata 承担全部路由状态让桥接服务保持无状态、可水平扩展。这套模式与仓库中managed_agents/linear/之外的其他 Managed Agents 示例如managed_agents/slack/、managed_agents/sentry/采用同一套官方 SDK 接口族理解这一个即可快速迁移到其他消息平台。【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表