@lark-project/openclaw-lark-project 2026.3.134 → 2026.3.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@ import { LarkClient } from "../core/lark-client.js";
8
8
  import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit.js";
9
9
  import { executeAuthorize } from "./oauth.js";
10
10
  import { formatLarkError, json } from "./oapi/helpers.js";
11
+ import { handleProjectAuthCardAction } from "./project-oauth.js";
11
12
  import { OwnerAccessDeniedError } from "../core/owner-policy.js";
12
13
  import { enqueueFeishuChatTask } from "../channel/chat-queue.js";
13
14
  import { handleFeishuMessage } from "../messaging/inbound/handler.js";
@@ -423,6 +424,9 @@ async function handleCardAction(data, cfg, accountId) {
423
424
  } catch {
424
425
  return;
425
426
  }
427
+ if (action === "project_auth_complete") {
428
+ return handleProjectAuthCardAction(data, cfg, accountId);
429
+ }
426
430
  if (action !== "app_auth_done" || !operationId) return;
427
431
  const flow = appAuthFlows.getByOperationId(operationId);
428
432
  if (!flow) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/auto-auth.ts"],
4
- "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * auto-auth.ts \u2014 \u5DE5\u5177\u5C42\u81EA\u52A8\u6388\u6743\u5904\u7406\u3002\n *\n * \u5F53 OAPI \u5DE5\u5177\u9047\u5230\u6388\u6743\u95EE\u9898\u65F6\uFF0C\u76F4\u63A5\u5728\u5DE5\u5177\u5C42\u5904\u7406\uFF0C\u4E0D\u518D\u8BA9 AI \u5224\u65AD\uFF1A\n *\n * - UserAuthRequiredError (appScopeVerified=true)\n * \u2192 \u76F4\u63A5\u8C03\u7528 executeAuthorize \u53D1\u8D77 OAuth Device Flow \u5361\u7247\n *\n * - UserScopeInsufficientError\n * \u2192 \u76F4\u63A5\u8C03\u7528 executeAuthorize\uFF08\u4F7F\u7528 missingScopes\uFF09\n *\n * - AppScopeMissingError\n * \u2192 \u53D1\u9001\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\uFF1B\u7528\u6237\u70B9\u51FB\"\u6211\u5DF2\u5B8C\u6210\"\u540E\uFF1A\n * 1. \u66F4\u65B0\u5361\u7247\u4E3A\u5904\u7406\u4E2D\u72B6\u6001\n * 2. invalidateAppScopeCache\n * 3. \u53D1\u9001\u4E2D\u95F4\u5408\u6210\u6D88\u606F\u544A\u77E5 AI\uFF08\"\u5E94\u7528\u6743\u9650\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u53D1\u8D77\u7528\u6237\u6388\u6743...\"\uFF09\n * 4. \u8C03\u7528 executeAuthorize \u53D1\u8D77 OAuth Device Flow\n *\n * - \u5176\u4ED6\u60C5\u51B5\uFF08AppScopeCheckFailedError\u3001appScopeVerified=false \u7B49\uFF09\n * \u2192 \u56DE\u9000\u5230\u539F handleInvokeError\uFF08\u4E0D\u89E6\u53D1\u81EA\u52A8\u6388\u6743\uFF09\n *\n * \u964D\u7EA7\u7B56\u7565\uFF08\u4FDD\u5B88\uFF09\uFF1A\u4EE5\u4E0B\u60C5\u51B5\u5747\u56DE\u9000\u5230 handleInvokeError\uFF1A\n * - \u65E0 LarkTicket\uFF08\u975E\u6D88\u606F\u573A\u666F\uFF09\n * - \u65E0 senderOpenId\uFF08\u65E0\u6CD5\u786E\u5B9A\u6388\u6743\u5BF9\u8C61\uFF09\n * - \u8D26\u53F7\u672A\u914D\u7F6E\uFF08!acct.configured\uFF09\n * - \u4EFB\u4F55\u6B65\u9AA4\u629B\u51FA\u5F02\u5E38\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport type { ConfiguredLarkAccount } from '../core/types';\nimport type { LarkTicket } from '../core/lark-ticket';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\n\nconst log = larkLogger('tools/auto-auth');\nimport { getLarkAccount } from '../core/accounts';\nimport { UserAuthRequiredError, UserScopeInsufficientError, AppScopeMissingError } from '../core/tool-client';\nimport { invalidateAppScopeCache, getAppGrantedScopes, isAppScopeSatisfied } from '../core/app-scope-checker';\nimport { LarkClient } from '../core/lark-client';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { executeAuthorize } from './oauth';\nimport { formatLarkError, json } from './oapi/helpers';\nimport { OwnerAccessDeniedError } from '../core/owner-policy';\nimport { enqueueFeishuChatTask } from '../channel/chat-queue';\nimport { handleFeishuMessage } from '../messaging/inbound/handler';\nimport { withTicket } from '../core/lark-ticket';\n\n// ---------------------------------------------------------------------------\n// Debounce + scope merge \u2014 \u9632\u6296\u7F13\u51B2\u533A\uFF08\u4E24\u9636\u6BB5\uFF09\n//\n// \u5DE5\u5177\u8C03\u7528\u53EF\u80FD\u662F\u771F\u6B63\u5E76\u53D1\uFF0850ms \u5185\u5230\u8FBE\uFF09\u6216\u88AB\u6846\u67B6\u5E8F\u5217\u5316\uFF08\u95F4\u9694\u6570\u79D2\u5230\u8FBE\uFF09\u3002\n// \u4E3A\u540C\u65F6\u8986\u76D6\u4E24\u79CD\u573A\u666F\uFF0C\u91C7\u7528\u4E24\u9636\u6BB5\u8BBE\u8BA1\uFF1A\n//\n// collecting\uFF08\u6536\u96C6\u9636\u6BB5\uFF09\uFF1A50ms \u9632\u6296\u7A97\u53E3\uFF0C\u5408\u5E76 scope\n// executing\uFF08\u6267\u884C\u9636\u6BB5\uFF09\uFF1AflushFn \u6B63\u5728\u8FD0\u884C\uFF0C\u540E\u7EED\u8BF7\u6C42\u590D\u7528\u540C\u4E00\u7ED3\u679C\n//\n// \u4ECE collecting \u2192 executing \u8F6C\u6362\u65F6\u4E0D\u4ECE Map \u4E2D\u5220\u9664 entry\uFF0C\n// \u76F4\u5230 flushFn \u5B8C\u6210\uFF08resolve / reject\uFF09\u624D\u79FB\u9664\u3002\n// ---------------------------------------------------------------------------\n\ntype JsonResult = ReturnType<typeof json>;\n\n/** \u7F13\u51B2\u4E2D\u7684\u6388\u6743\u8BF7\u6C42 */\ninterface AuthBatchEntry {\n phase: 'collecting' | 'executing';\n scopes: Set<string>;\n waiters: Array<{ resolve: (v: JsonResult) => void; reject: (e: unknown) => void }>;\n timer: ReturnType<typeof setTimeout> | null;\n /** flushFn \u6267\u884C\u4E2D\u7684 Promise\uFF08executing \u9636\u6BB5\u6709\u503C\uFF09 */\n resultPromise: Promise<JsonResult> | null;\n /** executing \u9636\u6BB5\uFF1A\u65B0 scope \u5230\u8FBE\u65F6\u7684\u5EF6\u8FDF\u5237\u65B0\u5B9A\u65F6\u5668 */\n updateTimer: ReturnType<typeof setTimeout> | null;\n /** scope \u66F4\u65B0\u7684 executeAuthorize \u662F\u5426\u6B63\u5728\u6267\u884C\uFF08\u4E92\u65A5\u9501\uFF09 */\n isUpdating: boolean;\n /** isUpdating \u671F\u95F4\u53C8\u6709\u65B0 scope \u5230\u8FBE\uFF0C\u9700\u8981\u518D\u66F4\u65B0\u4E00\u8F6E */\n pendingReupdate: boolean;\n /** flushFn \u5F15\u7528\uFF0Cexecuting \u9636\u6BB5\u7528\u4E8E scope \u66F4\u65B0\u65F6\u91CD\u65B0\u8C03\u7528 */\n flushFn: ((mergedScopes: string[]) => Promise<JsonResult>) | null;\n /** \u4EE5\u4E0B\u5B57\u6BB5\u6765\u81EA\u7B2C\u4E00\u4E2A\u5165\u961F\u7684\u8BF7\u6C42\uFF0C\u540E\u7EED\u8BF7\u6C42\u590D\u7528 */\n account: ConfiguredLarkAccount;\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/**\n * \u9632\u6296\u7F13\u51B2\u533A Map\u3002\n *\n * Key \u89C4\u5219\uFF1A\n * \u7528\u6237\u6388\u6743\uFF1A`user:${accountId}:${senderOpenId}:${messageId}`\n * \u5E94\u7528\u6388\u6743\uFF1A`app:${accountId}:${chatId}:${messageId}`\n */\nconst authBatches = new Map<string, AuthBatchEntry>();\n\n/** \u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09 */\nconst AUTH_DEBOUNCE_MS = 50;\n\n/** \u7528\u6237\u6388\u6743\u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09\u3002\u6BD4 app auth \u7684 50ms \u66F4\u957F\uFF0C\u4FDD\u8BC1\u5E94\u7528\u6743\u9650\u5361\u7247\u5148\u53D1\u51FA\u3002 */\nconst AUTH_USER_DEBOUNCE_MS = 150;\n\n/**\n * Scope \u66F4\u65B0\u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09\u3002\n * \u6BD4\u521D\u59CB\u9632\u6296\u66F4\u957F\uFF0C\u56E0\u4E3A\u5DE5\u5177\u8C03\u7528\u53EF\u80FD\u95F4\u9694\u6570\u5341\u5230\u6570\u767E\u6BEB\u79D2\u987A\u5E8F\u5230\u8FBE\u3002\n * \u9700\u8981\u7B49\u8DB3\u591F\u4E45\u4EE5\u6536\u96C6\u6240\u6709\u540E\u7EED\u5230\u8FBE\u7684 scope \u540E\u518D\u4E00\u6B21\u6027\u66F4\u65B0\u5361\u7247\u3002\n */\nconst AUTH_UPDATE_DEBOUNCE_MS = 500;\n\n/**\n * \u51B7\u5374\u671F\uFF08\u6BEB\u79D2\uFF09\u3002\n * flushFn \u6267\u884C\u5B8C\u6BD5\u540E\uFF0Centry \u7EE7\u7EED\u4FDD\u7559\u5728 Map \u4E2D\u8FD9\u4E48\u957F\u65F6\u95F4\uFF0C\n * \u9632\u6B62\u540E\u7EED\u987A\u5E8F\u5230\u8FBE\u7684\u5DE5\u5177\u8C03\u7528\u521B\u5EFA\u91CD\u590D\u5361\u7247\u3002\n */\nconst AUTH_COOLDOWN_MS = 30_000;\n\n/**\n * \u5C06\u6388\u6743\u8BF7\u6C42\u5165\u961F\u5230\u9632\u6296\u7F13\u51B2\u533A\u3002\n *\n * \u540C\u4E00 bufferKey \u7684\u8BF7\u6C42\u4F1A\u88AB\u5408\u5E76\uFF1A\n * - collecting \u9636\u6BB5\uFF1Ascope \u96C6\u5408\u53D6\u5E76\u96C6\uFF0C\u5171\u4EAB\u540C\u4E00\u4E2A flushFn \u6267\u884C\u7ED3\u679C\n * - executing \u9636\u6BB5\uFF1AflushFn \u5DF2\u5728\u8FD0\u884C\uFF0C\u540E\u7EED\u8BF7\u6C42\u76F4\u63A5\u590D\u7528\u5DF2\u6709\u7ED3\u679C\uFF08\u4E0D\u91CD\u590D\u53D1\u5361\u7247\uFF09\n *\n * @param bufferKey - \u7F13\u51B2\u533A key\uFF08\u533A\u5206\u4E0D\u540C\u7528\u6237/\u4F1A\u8BDD\uFF09\n * @param scopes - \u672C\u6B21\u8BF7\u6C42\u9700\u8981\u7684 scope \u5217\u8868\n * @param ctx - \u4E0A\u4E0B\u6587\u4FE1\u606F\uFF08\u4EC5\u7B2C\u4E00\u4E2A\u8BF7\u6C42\u7684\u88AB\u91C7\u7528\uFF09\n * @param flushFn - \u5B9A\u65F6\u5668\u5230\u671F\u540E\u6267\u884C\u7684\u5B9E\u9645\u6388\u6743\u51FD\u6570\uFF0C\u63A5\u6536\u5408\u5E76\u540E\u7684 scope \u6570\u7EC4\n */\nfunction enqueueAuthRequest(\n bufferKey: string,\n scopes: string[],\n ctx: { account: ConfiguredLarkAccount; cfg: ClawdbotConfig; ticket: LarkTicket },\n flushFn: (mergedScopes: string[]) => Promise<JsonResult>,\n debounceMs: number = AUTH_DEBOUNCE_MS,\n): Promise<JsonResult> {\n const existing = authBatches.get(bufferKey);\n\n if (existing) {\n // \u4E0D\u8BBA\u54EA\u4E2A\u9636\u6BB5\uFF0C\u90FD\u8FFD\u52A0 scope\n for (const s of scopes) existing.scopes.add(s);\n\n if (existing.phase === 'executing') {\n // flushFn \u5DF2\u5728\u6267\u884C\u6216\u5DF2\u5B8C\u6210\uFF08\u5361\u7247\u5DF2\u53D1\u51FA\uFF09\uFF0C\u590D\u7528\u7ED3\u679C\n // \u540C\u65F6\u89E6\u53D1\u5EF6\u8FDF\u5237\u65B0\uFF1A\u7528\u5408\u5E76\u540E\u7684 scope \u91CD\u65B0\u8C03\u7528 flushFn \u66F4\u65B0\u5361\u7247\n log.info(`auth in-flight, piggyback \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);\n\n // \u9632\u6296 + \u4E92\u65A5\uFF1A\u591A\u4E2A\u5FEB\u901F\u5230\u8FBE\u7684\u8BF7\u6C42\u53EA\u89E6\u53D1\u4E00\u6B21\u5361\u7247\u66F4\u65B0\n if (existing.updateTimer) clearTimeout(existing.updateTimer);\n existing.updateTimer = setTimeout(async () => {\n existing.updateTimer = null;\n\n // \u4E92\u65A5\uFF1A\u5982\u679C\u4E0A\u4E00\u8F6E\u66F4\u65B0\u8FD8\u5728\u6267\u884C\uFF0C\u6807\u8BB0 pendingReupdate \u7B49\u5B83\u7ED3\u675F\u540E\u91CD\u8DD1\n if (existing.isUpdating) {\n existing.pendingReupdate = true;\n log.info(`scope update deferred (previous update still running) \u2192 key=${bufferKey}`);\n return;\n }\n\n existing.isUpdating = true;\n try {\n const mergedScopes = [...existing.scopes];\n log.info(`scope update flush \u2192 key=${bufferKey}, scopes=[${mergedScopes.join(', ')}]`);\n // \u91CD\u65B0\u8C03\u7528 flushFn\uFF08executeAuthorize \u4F1A\u68C0\u6D4B\u5230 pendingFlow\uFF0C\n // \u539F\u5730\u66F4\u65B0\u65E7\u5361\u7247\u5185\u5BB9 + \u91CD\u542F Device Flow\uFF09\n await existing.flushFn!(mergedScopes);\n } catch (err) {\n log.warn(`scope update failed: ${err}`);\n } finally {\n existing.isUpdating = false;\n // \u5982\u679C\u9501\u5B9A\u671F\u95F4\u6709\u65B0 scope \u5230\u8FBE\uFF0C\u518D\u8DD1\u4E00\u8F6E\n if (existing.pendingReupdate) {\n existing.pendingReupdate = false;\n const finalScopes = [...existing.scopes];\n log.info(`scope reupdate \u2192 key=${bufferKey}, scopes=[${finalScopes.join(', ')}]`);\n try {\n await existing.flushFn!(finalScopes);\n } catch (err) {\n log.warn(`scope reupdate failed: ${err}`);\n }\n }\n }\n }, AUTH_UPDATE_DEBOUNCE_MS);\n\n return existing.resultPromise!;\n }\n\n // collecting \u9636\u6BB5\uFF1A\u6B63\u5E38\u5408\u5E76\n log.info(`debounce merge \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);\n return new Promise<JsonResult>((resolve, reject) => {\n existing.waiters.push({ resolve, reject });\n });\n }\n\n // \u521B\u5EFA\u65B0\u7F13\u51B2\u533A\uFF08collecting \u9636\u6BB5\uFF09\n const entry: AuthBatchEntry = {\n phase: 'collecting',\n scopes: new Set(scopes),\n waiters: [],\n timer: null,\n resultPromise: null,\n updateTimer: null,\n isUpdating: false,\n pendingReupdate: false,\n flushFn: null,\n account: ctx.account,\n cfg: ctx.cfg,\n ticket: ctx.ticket,\n };\n\n const promise = new Promise<JsonResult>((resolve, reject) => {\n entry.waiters.push({ resolve, reject });\n });\n\n entry.timer = setTimeout(async () => {\n // \u8F6C\u5165 executing \u9636\u6BB5\uFF08\u4E0D\u4ECE Map \u4E2D\u5220\u9664\uFF0C\u963B\u6B62\u540E\u7EED\u8BF7\u6C42\u521B\u5EFA\u65B0\u5361\u7247\uFF09\n entry.phase = 'executing';\n entry.timer = null;\n entry.flushFn = flushFn; // \u4FDD\u5B58\u5F15\u7528\uFF0C\u4F9B executing \u9636\u6BB5 scope \u66F4\u65B0\u65F6\u91CD\u65B0\u8C03\u7528\n const mergedScopes = [...entry.scopes];\n\n log.info(\n `debounce flush \u2192 key=${bufferKey}, ` + `waiters=${entry.waiters.length}, scopes=[${mergedScopes.join(', ')}]`,\n );\n\n // \u5C06 flushFn \u7684 Promise \u5B58\u5165 entry\uFF0C\u4F9B executing \u9636\u6BB5\u7684\u540E\u6765\u8005\u590D\u7528\n entry.resultPromise = flushFn(mergedScopes);\n\n try {\n const result = await entry.resultPromise;\n for (const w of entry.waiters) w.resolve(result);\n } catch (err) {\n for (const w of entry.waiters) w.reject(err);\n } finally {\n // \u8FDB\u5165\u51B7\u5374\u671F\uFF1Aentry \u7EE7\u7EED\u7559\u5728 Map \u4E2D\uFF0C\u540E\u7EED\u5230\u8FBE\u7684\u5DE5\u5177\u8C03\u7528\n // \u4F1A\u547D\u4E2D executing \u5206\u652F\u5E76\u590D\u7528 resultPromise\uFF0C\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5361\u7247\u3002\n // \u51B7\u5374\u671F\u7ED3\u675F\u540E\u6E05\u7406\u3002\n setTimeout(() => authBatches.delete(bufferKey), AUTH_COOLDOWN_MS);\n }\n }, debounceMs);\n\n authBatches.set(bufferKey, entry);\n return promise;\n}\n\n// ---------------------------------------------------------------------------\n// PendingAppAuthFlow \u2014 \u7B49\u5F85\u7528\u6237\u786E\u8BA4\u7684\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u6D41\u7A0B\n// ---------------------------------------------------------------------------\n\ninterface PendingAppAuthFlow {\n appId: string;\n accountId: string;\n cardId: string;\n sequence: number;\n requiredScopes: string[];\n /** \u4E0E\u89E6\u53D1 AppScopeMissingError \u65F6\u7684 scopeNeedType \u4E00\u81F4\u3002 */\n scopeNeedType?: 'one' | 'all';\n /** \u4E0E\u89E6\u53D1 AppScopeMissingError \u65F6\u7684 tokenType \u4E00\u81F4\u3002 */\n tokenType?: 'user' | 'tenant';\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/** TTL\uFF1A15 \u5206\u949F\u540E\u81EA\u52A8\u6E05\u7406\uFF0C\u9632\u6B62\u5185\u5B58\u6CC4\u6F0F\u3002 */\nconst PENDING_FLOW_TTL_MS = 15 * 60 * 1000;\n\n/** \u8BA1\u7B97\u53BB\u91CD key\uFF08chatId + messageId + \u6709\u5E8F scopes\uFF09\u3002 */\nfunction makeDedupKey(chatId: string, messageId: string, scopes: string[]): string {\n return chatId + '\\0' + messageId + '\\0' + [...scopes].sort().join(',');\n}\n\n/** \u6CE8\u518C\u540E\u7684 flow\uFF0C\u9644\u52A0\u7D22\u5F15\u952E\u4FE1\u606F */\ntype RegisteredFlow = PendingAppAuthFlow & {\n dedupKey: string;\n activeCardKey: string;\n};\n\n/**\n * \u5E94\u7528\u6743\u9650\u6388\u6743\u6D41\u7BA1\u7406\u5668 \u2014 \u7EDF\u4E00\u7BA1\u7406\u4E09\u4E2A\u5173\u8054\u7D22\u5F15\u7684\u4E00\u81F4\u6027\u3002\n *\n * \u66FF\u4EE3\u539F\u6765\u6563\u5E03\u7684 pendingAppAuthFlows / dedupIndex / activeAppCardIndex \u4E09\u4E2A Map\uFF0C\n * \u786E\u4FDD\u6CE8\u518C\u3001\u5220\u9664\u3001\u8FC1\u79FB\u64CD\u4F5C\u7684\u539F\u5B50\u6027\u3002\n */\nclass AppAuthFlowManager {\n private readonly flows = new Map<string, RegisteredFlow>();\n private readonly dedupIndex = new Map<string, string>();\n private readonly activeCardIndex = new Map<string, string>();\n\n /** \u539F\u5B50\u6CE8\u518C\u65B0\u6D41\u7A0B\uFF08\u540C\u65F6\u5199\u5165 3 \u4E2A\u7D22\u5F15 + \u8BBE\u7F6E\u7EDF\u4E00 TTL\uFF09 */\n register(operationId: string, flow: PendingAppAuthFlow, dedupKey: string, activeCardKey: string): void {\n const registered: RegisteredFlow = { ...flow, dedupKey, activeCardKey };\n this.flows.set(operationId, registered);\n this.dedupIndex.set(dedupKey, operationId);\n this.activeCardIndex.set(activeCardKey, operationId);\n\n // \u7EDF\u4E00 TTL \u6E05\u7406\n setTimeout(() => {\n if (!this.flows.has(operationId)) return; // \u5DF2\u88AB\u624B\u52A8\u6E05\u7406\uFF0C\u8DF3\u8FC7\n this.remove(operationId);\n }, PENDING_FLOW_TTL_MS);\n }\n\n /** \u53EA\u9700 operationId \u5373\u53EF\u539F\u5B50\u6E05\u7406\u6240\u6709\u7D22\u5F15 */\n remove(operationId: string): void {\n const flow = this.flows.get(operationId);\n if (!flow) return;\n\n // \u8054\u52A8\u6E05\u7406\u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\uFF08\u9632\u6B62\u5185\u5B58\u6CC4\u6F0F\uFF09\n if (flow.ticket?.senderOpenId) {\n const deferKey = `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;\n deferredUserAuth.delete(deferKey);\n }\n\n this.flows.delete(operationId);\n // \u6761\u4EF6\u5220\u9664\uFF1A\u9632\u6B62\u8BEF\u5220\u5DF2\u88AB\u65B0 flow \u8986\u76D6\u7684\u7D22\u5F15\n if (this.dedupIndex.get(flow.dedupKey) === operationId) {\n this.dedupIndex.delete(flow.dedupKey);\n }\n if (this.activeCardIndex.get(flow.activeCardKey) === operationId) {\n this.activeCardIndex.delete(flow.activeCardKey);\n }\n }\n\n /**\n * \u8FC1\u79FB\u5230\u65B0 operationId\uFF08\u5361\u7247\u590D\u7528\u573A\u666F\uFF1A\u6309\u94AE\u56DE\u8C03\u9700\u8981\u5339\u914D\u65B0 ID\uFF09\u3002\n * \u539F\u5B50\u64CD\u4F5C\uFF1A\u6E05\u7406\u65E7\u7D22\u5F15 \u2192 \u66F4\u65B0 flow \u2192 \u5EFA\u7ACB\u65B0\u7D22\u5F15 \u2192 \u6CE8\u518C\u65B0 TTL\u3002\n *\n * \u4FEE\u590D\u539F\u4EE3\u7801\u5361\u7247\u590D\u7528\u8DEF\u5F84\u7F3A\u5C11 TTL \u6CE8\u518C\u5BFC\u81F4\u7684\u5185\u5B58\u6CC4\u6F0F\u3002\n */\n migrateToNewOperationId(\n oldOperationId: string,\n newOperationId: string,\n updates?: { dedupKey?: string; requiredScopes?: string[]; scopeNeedType?: 'one' | 'all' },\n ): RegisteredFlow | undefined {\n const flow = this.flows.get(oldOperationId);\n if (!flow) return undefined;\n\n // \u6E05\u7406\u65E7\u7D22\u5F15\n this.flows.delete(oldOperationId);\n if (updates?.dedupKey) {\n if (this.dedupIndex.get(flow.dedupKey) === oldOperationId) {\n this.dedupIndex.delete(flow.dedupKey);\n }\n flow.dedupKey = updates.dedupKey;\n }\n if (updates?.requiredScopes) flow.requiredScopes = updates.requiredScopes;\n if (updates?.scopeNeedType) flow.scopeNeedType = updates.scopeNeedType;\n\n // \u5EFA\u7ACB\u65B0\u7D22\u5F15\n this.flows.set(newOperationId, flow);\n this.dedupIndex.set(flow.dedupKey, newOperationId);\n this.activeCardIndex.set(flow.activeCardKey, newOperationId);\n\n // \u4E3A\u65B0 operationId \u6CE8\u518C TTL\uFF08\u4FEE\u590D\u539F\u4EE3\u7801\u7684\u5185\u5B58\u6CC4\u6F0F\uFF09\n setTimeout(() => {\n if (!this.flows.has(newOperationId)) return;\n this.remove(newOperationId);\n }, PENDING_FLOW_TTL_MS);\n\n return flow;\n }\n\n /** \u901A\u8FC7 operationId \u67E5\u8BE2\uFF08card action \u56DE\u8C03\u7528\uFF09 */\n getByOperationId(id: string): PendingAppAuthFlow | undefined {\n return this.flows.get(id);\n }\n\n /** \u901A\u8FC7\u53BB\u91CD\u952E\u67E5\u8BE2\uFF08\u907F\u514D\u53D1\u9001\u91CD\u590D\u5361\u7247\uFF09 */\n getByDedupKey(key: string): { operationId: string; flow: PendingAppAuthFlow } | undefined {\n const opId = this.dedupIndex.get(key);\n if (!opId) return undefined;\n const flow = this.flows.get(opId);\n return flow ? { operationId: opId, flow } : undefined;\n }\n\n /** \u901A\u8FC7\u6D3B\u8DC3\u5361\u7247\u952E\u67E5\u8BE2\uFF08\u540C\u6D88\u606F\u5361\u7247\u590D\u7528\uFF09 */\n getByActiveCardKey(key: string): { operationId: string; flow: RegisteredFlow } | undefined {\n const opId = this.activeCardIndex.get(key);\n if (!opId) return undefined;\n const flow = this.flows.get(opId);\n return flow ? { operationId: opId, flow } : undefined;\n }\n}\n\nconst appAuthFlows = new AppAuthFlowManager();\n\n// ---------------------------------------------------------------------------\n// Deferred User Auth Queue \u2014 \u7528\u6237\u6388\u6743\u5EF6\u8FDF\u961F\u5217\n//\n// \u5F53\u7528\u6237\u6388\u6743\u8BF7\u6C42\u5230\u8FBE\u65F6\uFF0C\u5982\u679C\u540C\u4E00\u6D88\u606F\u4E0A\u4E0B\u6587\u5B58\u5728\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6D41\u7A0B\uFF0C\n// \u5C06 scope \u6536\u96C6\u5230\u5EF6\u8FDF\u961F\u5217\uFF0C\u7B49\u5E94\u7528\u6388\u6743\u5B8C\u6210\u540E\u7EDF\u4E00\u53D1\u8D77 OAuth\u3002\n// ---------------------------------------------------------------------------\n\ninterface DeferredUserAuthEntry {\n scopes: Set<string>;\n account: ConfiguredLarkAccount;\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/** \u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\u3002Key: `${accountId}:${senderOpenId}:${messageId}` */\nconst deferredUserAuth = new Map<string, DeferredUserAuthEntry>();\n\n/**\n * \u68C0\u67E5\u6307\u5B9A\u6D88\u606F\u4E0A\u4E0B\u6587\u662F\u5426\u6709\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6388\u6743\u6D41\u7A0B\u3002\n * \u68C0\u67E5\u4E24\u4E2A\u6765\u6E90\uFF1A\n * 1. authBatches \u4E2D\u7684 app auth entry\uFF08collecting/executing \u9636\u6BB5\uFF09\n * 2. appAuthFlows \u4E2D\u7684\u6D3B\u8DC3\u6D41\uFF08\u5361\u7247\u5DF2\u53D1\u9001\uFF0C\u7B49\u5F85\u7528\u6237\u70B9\u51FB\"\u5DF2\u5B8C\u6210\"\uFF09\n */\nfunction hasActiveAppAuthForMessage(ticket: LarkTicket): boolean {\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry && (appEntry.phase === 'collecting' || appEntry.phase === 'executing')) {\n return true;\n }\n const activeCardKey = `${ticket.chatId}:${ticket.messageId}`;\n return !!appAuthFlows.getByActiveCardKey(activeCardKey);\n}\n\n/**\n * \u5C06\u7528\u6237\u6388\u6743 scope \u6DFB\u52A0\u5230\u5EF6\u8FDF\u961F\u5217\u3002\n * \u591A\u4E2A\u5DE5\u5177\u8C03\u7528\u7684 scope \u4F1A\u88AB\u5408\u5E76\u5230\u540C\u4E00\u4E2A entry\u3002\n */\nfunction addToDeferredUserAuth(\n ticket: LarkTicket,\n scopes: string[],\n account: ConfiguredLarkAccount,\n cfg: ClawdbotConfig,\n): void {\n const key = `${ticket.accountId}:${ticket.senderOpenId}:${ticket.messageId}`;\n const existing = deferredUserAuth.get(key);\n if (existing) {\n for (const s of scopes) existing.scopes.add(s);\n log.info(`deferred user auth scope merge \u2192 key=${key}, scopes=[${[...existing.scopes].join(', ')}]`);\n } else {\n deferredUserAuth.set(key, { scopes: new Set(scopes), account, cfg, ticket });\n log.info(`deferred user auth created \u2192 key=${key}, scopes=[${scopes.join(', ')}]`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Card builders \u2014 CardKit v2 \u683C\u5F0F\n// ---------------------------------------------------------------------------\n\n/**\n * \u6784\u5EFA\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u3002\n *\n * \u84DD\u8272 header\uFF0C\u5217\u51FA\u7F3A\u5931\u7684 scope\uFF0C\u63D0\u4F9B\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u548C\"\u6211\u5DF2\u5B8C\u6210\uFF0C\u7EE7\u7EED\u6388\u6743\"\u6309\u94AE\u3002\n */\nfunction buildAppScopeMissingCard(params: {\n missingScopes: string[];\n appId?: string;\n operationId: string;\n}): Record<string, unknown> {\n const { missingScopes, appId, operationId } = params;\n const authUrl = appId\n ? `https://open.feishu.cn/app/${appId}/auth?q=${encodeURIComponent(missingScopes.join(','))}&op_from=feishu-openclaw&token_type=user`\n : 'https://open.feishu.cn/';\n const multiUrl = { url: authUrl, pc_url: authUrl, android_url: authUrl, ios_url: authUrl };\n\n const scopeList = missingScopes.map((s) => `\u2022 ${s}`).join('\\n');\n\n return {\n schema: '2.0',\n config: { wide_screen_mode: true },\n header: {\n title: { tag: 'plain_text', content: '\uD83D\uDD10 \u9700\u8981\u7533\u8BF7\u6743\u9650\u624D\u80FD\u7EE7\u7EED' },\n template: 'orange',\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u8C03\u7528\u524D\uFF0C\u8BF7\u4F60\u5148\u7533\u8BF7\u4EE5\u4E0B**\u6240\u6709**\u6743\u9650\uFF1A',\n text_size: 'normal',\n },\n {\n tag: 'column_set',\n flex_mode: 'none',\n background_style: 'grey',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: scopeList }],\n },\n ],\n },\n { tag: 'hr' },\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 3,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: '**\u7B2C\u4E00\u6B65\uFF1A\u7533\u8BF7\u6240\u6709\u6743\u9650**' }],\n },\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u53BB\u7533\u8BF7' },\n type: 'primary',\n multi_url: multiUrl,\n },\n ],\n },\n ],\n },\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 3,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: '**\u7B2C\u4E8C\u6B65\uFF1A\u521B\u5EFA\u7248\u672C\u5E76\u5BA1\u6838\u901A\u8FC7**' }],\n },\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u5DF2\u5B8C\u6210' },\n type: 'default',\n value: { action: 'app_auth_done', operation_id: operationId },\n },\n ],\n },\n ],\n },\n ],\n },\n };\n}\n\n/**\n * \u6784\u5EFA\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u7684\"\u5904\u7406\u4E2D\"\u72B6\u6001\uFF08\u7528\u6237\u70B9\u51FB\u6309\u94AE\u540E\u66F4\u65B0\uFF09\u3002\n */\nfunction buildAppAuthProgressCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: { wide_screen_mode: false },\n header: {\n title: { tag: 'plain_text', content: '\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A' },\n subtitle: { tag: 'plain_text', content: '' },\n template: 'green',\n padding: '12px 12px 12px 12px',\n icon: { tag: 'standard_icon', token: 'yes_filled' },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u60A8\u7684\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u6B63\u5728\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743',\n text_size: 'normal',\n },\n ],\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * \u53D1\u9001\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\uFF0C\u5E76\u5C06 flow \u5B58\u5165 pendingAppAuthFlows\u3002\n * \u8FD4\u56DE\u5DE5\u5177\u7ED3\u679C\uFF08\u544A\u77E5 AI \u7B49\u5F85\u7528\u6237\u64CD\u4F5C\uFF09\u3002\n */\nasync function sendAppScopeCard(params: {\n account: ConfiguredLarkAccount;\n missingScopes: string[];\n appId?: string;\n scopeNeedType?: 'one' | 'all';\n tokenType?: 'user' | 'tenant';\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}): Promise<ReturnType<typeof json>> {\n const { account, missingScopes, appId, scopeNeedType, tokenType, cfg, ticket } = params;\n const { accountId, chatId, messageId } = ticket;\n const activeCardKey = `${chatId}:${messageId}`;\n\n // ---- \u53BB\u91CD\uFF1A\u907F\u514D\u5E76\u53D1\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u51FA\u591A\u5F20\u5185\u5BB9\u76F8\u540C\u7684\u5361\u7247 ----\n const dedup = makeDedupKey(chatId, messageId, missingScopes);\n const existingEntry = appAuthFlows.getByDedupKey(dedup);\n if (existingEntry) {\n log.info(\n `dedup \u2013 app-scope card already pending for chatId=${chatId}, ` +\n `scopes=[${missingScopes.join(', ')}], skipping duplicate send`,\n );\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n }\n\n // ---- \u5361\u7247\u590D\u7528\uFF1A\u540C\u4E00 chatId+messageId \u5DF2\u6709\u6D3B\u8DC3\u5361\u7247\u65F6\uFF0C\u539F\u5730\u66F4\u65B0\u800C\u975E\u521B\u5EFA\u65B0\u5361\u7247 ----\n const activeEntry = appAuthFlows.getByActiveCardKey(activeCardKey);\n\n if (activeEntry) {\n const { operationId: activeOpId, flow: activeFlow } = activeEntry;\n // \u66F4\u65B0\u5DF2\u6709\u5361\u7247\u7684\u5185\u5BB9\uFF08\u5408\u5E76\u540E\u7684 scope\uFF09\n const newOperationId = Date.now().toString(36) + Math.random().toString(36).slice(2);\n const card = buildAppScopeMissingCard({ missingScopes, appId, operationId: newOperationId });\n const newSeq = activeFlow.sequence + 1;\n\n // TOCTOU \u4FEE\u590D\uFF1A\u5148\u539F\u5B50\u8FC1\u79FB\uFF08\u540C\u6B65\u64CD\u4F5C\uFF09\uFF0C\u518D await \u66F4\u65B0\u5361\u7247\n const newDedup = makeDedupKey(chatId, messageId, missingScopes);\n const migrated = appAuthFlows.migrateToNewOperationId(activeOpId, newOperationId, {\n dedupKey: newDedup,\n requiredScopes: missingScopes,\n scopeNeedType,\n });\n if (!migrated) {\n // \u88AB\u5176\u4ED6\u5E76\u53D1\u8BF7\u6C42\u62A2\u5148\u8FC1\u79FB\u4E86\uFF0C\u964D\u7EA7\u5230\u65B0\u5EFA\u5361\u7247\n log.info(`migrate raced, falling through to new card creation`);\n } else {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: activeFlow.cardId,\n card,\n sequence: newSeq,\n accountId,\n });\n log.info(\n `app-scope card updated in-place, cardId=${activeFlow.cardId}, ` +\n `seq=${newSeq}, scopes=[${missingScopes.join(', ')}]`,\n );\n\n // \u66F4\u65B0 sequence\uFF08migrate \u4E0D\u5904\u7406 sequence\uFF09\n migrated.sequence = newSeq;\n\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n } catch (err) {\n // \u56DE\u6EDA\uFF1A\u5220\u9664\u5DF2\u8FC1\u79FB\u7684 flow\n appAuthFlows.remove(newOperationId);\n log.warn(`failed to update existing app-scope card, creating new one: ${err}`);\n // \u964D\u7EA7\uFF1A\u8D70\u4E0B\u9762\u7684\u65B0\u5EFA\u5361\u7247\u8DEF\u5F84\n }\n }\n }\n\n const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);\n\n const card = buildAppScopeMissingCard({ missingScopes, appId, operationId });\n\n // \u521B\u5EFA CardKit \u5361\u7247\u5B9E\u4F53\n const cardId = await createCardEntity({ cfg, card, accountId });\n if (!cardId) {\n log.warn('createCardEntity failed for app-scope card, falling back');\n return json({\n error: 'app_scope_missing',\n missing_scopes: missingScopes,\n message:\n `\u5E94\u7528\u7F3A\u5C11\u4EE5\u4E0B\u6743\u9650\uFF1A${missingScopes.join(', ')}\uFF0C` +\n `\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u540E\u91CD\u8BD5\u3002` +\n (appId ? `\\n\u6743\u9650\u7BA1\u7406\uFF1Ahttps://open.feishu.cn/app/${appId}/permission` : ''),\n });\n }\n\n // \u53D1\u9001\u5230\u5F53\u524D\u4F1A\u8BDD\n const replyToMsgId = ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined;\n\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: replyToMsgId,\n replyInThread: Boolean(ticket?.threadId),\n accountId,\n });\n\n // \u539F\u5B50\u6CE8\u518C\u5230\u7BA1\u7406\u5668\uFF08\u7EDF\u4E00 TTL \u6E05\u7406\uFF09\n const flow: PendingAppAuthFlow = {\n appId: appId ?? account.appId,\n accountId,\n cardId,\n sequence: 0,\n requiredScopes: missingScopes,\n scopeNeedType,\n tokenType,\n cfg,\n ticket,\n };\n appAuthFlows.register(operationId, flow, dedup, activeCardKey);\n\n log.info(`app-scope card sent, operationId=${operationId}, scopes=[${missingScopes.join(', ')}]`);\n\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Card action handler (exported for monitor.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * \u5904\u7406 card.action.trigger \u56DE\u8C03\u4E8B\u4EF6\uFF08\u7531 monitor.ts \u8C03\u7528\uFF09\u3002\n *\n * \u5F53\u7528\u6237\u70B9\u51FB\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u7684\"\u6211\u5DF2\u5B8C\u6210\uFF0C\u7EE7\u7EED\u6388\u6743\"\u6309\u94AE\u65F6\uFF1A\n * 1. \u66F4\u65B0\u5361\u7247\u4E3A\"\u5904\u7406\u4E2D\"\u72B6\u6001\n * 2. \u6E05\u9664\u5E94\u7528 scope \u7F13\u5B58\n * 3. \u53D1\u9001\u4E2D\u95F4\u5408\u6210\u6D88\u606F\u544A\u77E5 AI\n * 4. \u53D1\u8D77 OAuth Device Flow\n *\n * \u6CE8\u610F\uFF1A\u51FD\u6570\u4F53\u5185\u7684\u4E3B\u8981\u903B\u8F91\u901A\u8FC7 setImmediate + fire-and-forget \u5F02\u6B65\u6267\u884C\uFF0C\n * \u786E\u4FDD Feishu card.action.trigger \u56DE\u8C03\u5728 3 \u79D2\u5185\u8FD4\u56DE\u3002\n */\nexport async function handleCardAction(data: unknown, cfg: ClawdbotConfig, accountId: string): Promise<unknown> {\n let action: string | undefined;\n let operationId: string | undefined;\n let senderOpenId: string | undefined;\n\n try {\n const event = data as {\n operator?: { open_id?: string };\n action?: { value?: { action?: string; operation_id?: string } };\n };\n action = event.action?.value?.action;\n operationId = event.action?.value?.operation_id;\n senderOpenId = event.operator?.open_id;\n } catch {\n return;\n }\n\n if (action !== 'app_auth_done' || !operationId) return;\n\n const flow = appAuthFlows.getByOperationId(operationId);\n if (!flow) {\n log.warn(`card action ${operationId} not found (expired or already handled)`);\n return;\n }\n\n log.info(`app_auth_done clicked by ${senderOpenId}, operationId=${operationId}`);\n\n // scope \u6821\u9A8C\u5728\u540C\u6B65\u8DEF\u5F84\u5B8C\u6210\uFF083 \u79D2\u5185\u8FD4\u56DE toast response\uFF09\n invalidateAppScopeCache(flow.appId);\n\n const acct = getLarkAccount(flow.cfg, flow.accountId);\n if (!acct.configured) {\n log.warn(`account ${flow.accountId} not configured, skipping OAuth`);\n return;\n }\n\n const sdk = LarkClient.fromAccount(acct).sdk;\n let grantedScopes: string[] = [];\n try {\n // \u4F7F\u7528\u4E0E\u539F\u59CB AppScopeMissingError \u76F8\u540C\u7684 tokenType\uFF0C\u4FDD\u8BC1\u6821\u9A8C\u903B\u8F91\u5B8C\u5168\u4E00\u81F4\n grantedScopes = await getAppGrantedScopes(sdk, flow.appId, flow.tokenType);\n } catch (err) {\n log.warn(`failed to re-check app scopes: ${err}, proceeding anyway`);\n }\n\n // \u4F7F\u7528\u5171\u4EAB\u51FD\u6570 isAppScopeSatisfied\uFF0C\u4E0E tool-client invoke() \u903B\u8F91\u5B8C\u5168\u4E00\u81F4\uFF1A\n // - scopeNeedType \"all\" \u2192 \u5168\u90E8\u5FC5\u987B\u6709\n // - \u9ED8\u8BA4\"one\" \u2192 \u4EA4\u96C6\u975E\u7A7A\u5373\u53EF\n // - grantedScopes \u4E3A\u7A7A \u2192 \u89C6\u4E3A\u6EE1\u8DB3\uFF08API \u5931\u8D25\u9000\u56DE\u670D\u52A1\u7AEF\u5224\u65AD\uFF09\n if (!isAppScopeSatisfied(grantedScopes, flow.requiredScopes, flow.scopeNeedType)) {\n log.warn(`app scopes still missing after user confirmation: [${flow.requiredScopes.join(', ')}]`);\n return {\n toast: {\n type: 'error',\n content: '\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u7533\u8BF7\u5E76\u5BA1\u6838\u901A\u8FC7\u540E\u518D\u8BD5',\n },\n };\n }\n\n log.info(`app scopes verified, proceeding with OAuth`);\n\n // \u2605 \u5728 remove() \u4E4B\u524D\u5148\u53D6\u51FA\u5EF6\u8FDF\u961F\u5217\u6570\u636E\uFF0C\u907F\u514D remove() \u7684\u8054\u52A8\u6E05\u7406\u63D0\u524D\u5220\u6389\u5B83\n const deferKey = flow.ticket.senderOpenId\n ? `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`\n : undefined;\n const consumedDeferred = deferKey ? deferredUserAuth.get(deferKey) : undefined;\n if (consumedDeferred && deferKey) {\n deferredUserAuth.delete(deferKey);\n log.info(`consumed deferred user auth scopes: [${[...consumedDeferred.scopes].join(', ')}]`);\n }\n\n // \u6821\u9A8C\u901A\u8FC7\u624D\u5220\u9664\uFF0C\u9632\u6B62\u7528\u6237\u5728\u6743\u9650\u901A\u8FC7\u524D\u591A\u6B21\u70B9\u51FB\u65E0\u6CD5\u91CD\u8BD5\n appAuthFlows.remove(operationId);\n\n // \u901A\u8FC7\u56DE\u8C03\u8FD4\u56DE\u503C\u76F4\u63A5\u66F4\u65B0\u5361\u7247\uFF08\u65B9\u5F0F\u4E00\uFF1A3 \u79D2\u5185\u7ACB\u5373\u66F4\u65B0\uFF09\u3002\n // \u98DE\u4E66\u6587\u6863\u8981\u6C42 card \u5B57\u6BB5\u5FC5\u987B\u5305\u542B type + data \u5305\u88C5\uFF1A\n // { card: { type: \"raw\", data: { schema: \"2.0\", ... } } }\n // \u6CE8\u610F\uFF1A\u4E0D\u80FD\u5728\u56DE\u8C03\u8FD4\u56DE\u524D\u8C03\u7528 card.update API\uFF0C\u98DE\u4E66\u6587\u6863\u660E\u786E\u8BF4\u660E\n // \"\u5EF6\u65F6\u66F4\u65B0\u5FC5\u987B\u5728\u54CD\u5E94\u56DE\u8C03\u8BF7\u6C42\u4E4B\u540E\u6267\u884C\uFF0C\u5E76\u884C\u6267\u884C\u6216\u63D0\u524D\u6267\u884C\u4F1A\u51FA\u73B0\u66F4\u65B0\u5931\u8D25\"\u3002\n const successCard = buildAppAuthProgressCard();\n\n // \u540E\u53F0\u5F02\u6B65\uFF1A\u56DE\u8C03\u54CD\u5E94\u4E4B\u540E\u518D\u6267\u884C API \u66F4\u65B0 + OAuth\n setImmediate(async () => {\n try {\n // \u901A\u8FC7 API \u518D\u6B21\u66F4\u65B0\u5361\u7247\uFF08\u786E\u4FDD\u6240\u6709\u67E5\u770B\u8005\u90FD\u770B\u5230\u66F4\u65B0\uFF0C\u4E0D\u53EA\u662F\u70B9\u51FB\u8005\uFF09\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: flow.cardId,\n card: successCard,\n sequence: flow.sequence + 1,\n accountId,\n });\n } catch (err) {\n log.warn(`failed to update app-scope card to progress via API: ${err}`);\n }\n\n // \u53D1\u8D77 OAuth Device Flow\uFF08\u5B8C\u6210\u540E executeAuthorize \u4F1A\u81EA\u52A8\u53D1\u5408\u6210\u6D88\u606F\u89E6\u53D1 AI \u91CD\u8BD5\uFF09\n if (!flow.ticket.senderOpenId) {\n log.warn('no senderOpenId in ticket, skipping OAuth');\n return;\n }\n\n // \u6536\u96C6\u6240\u6709\u6765\u6E90\u7684 scope\uFF08\u8FC7\u6EE4 offline_access\uFF1A\u4EC5 app \u7EA7\u9700\u8981\uFF0Cdevice-flow \u81EA\u52A8\u8FFD\u52A0\uFF09\n const mergedScopes = new Set(flow.requiredScopes.filter((s) => s !== 'offline_access'));\n\n // \u6765\u6E90 1: \u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\uFF08\u5DF2\u5728\u540C\u6B65\u8DEF\u5F84\u4E2D\u63D0\u524D\u53D6\u51FA\uFF0C\u89C1 consumedDeferred\uFF09\n if (consumedDeferred) {\n for (const s of consumedDeferred.scopes) mergedScopes.add(s);\n }\n\n // \u6765\u6E90 2: \u73B0\u6709 user auth batch\uFF08\u5411\u540E\u517C\u5BB9\uFF0C\u5904\u7406\u672A\u88AB\u5EF6\u8FDF\u62E6\u622A\u7684 user auth\uFF09\n const userBatchKey = `user:${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;\n const userBatch = authBatches.get(userBatchKey);\n if (userBatch) {\n for (const s of userBatch.scopes) mergedScopes.add(s);\n log.info(`merged user batch scopes into app auth completion: [${[...mergedScopes].join(', ')}]`);\n }\n\n if (mergedScopes.size === 0) {\n // \u65E0\u4E1A\u52A1 scope \u9700\u8981\u7528\u6237\u6388\u6743\uFF08\u4F8B\u5982 offline_access \u662F\u552F\u4E00\u7F3A\u5931\u7684\u5E94\u7528\u6743\u9650\uFF0C\n // \u4E14\u6CA1\u6709\u5176\u4ED6\u5DE5\u5177\u4EA7\u751F\u7528\u6237\u6388\u6743\u9700\u6C42\uFF09\u3002\u8DF3\u8FC7 OAuth\uFF0C\u76F4\u63A5\u53D1\u5408\u6210\u6D88\u606F\u89E6\u53D1 AI \u91CD\u8BD5\uFF0C\n // \u91CD\u8BD5\u65F6\u5DE5\u5177\u4F1A\u81EA\u7136\u53D1\u73B0\u9700\u8981\u7528\u6237\u6388\u6743\u5E76\u53D1\u8D77\u6B63\u786E\u7684 OAuth \u6D41\u7A0B\u3002\n log.info('no business scopes to authorize after app auth, sending synthetic message for retry');\n const syntheticMsgId = `${flow.ticket.messageId}:app-auth-complete`;\n const syntheticEvent = {\n sender: { sender_id: { open_id: flow.ticket.senderOpenId } },\n message: {\n message_id: syntheticMsgId,\n chat_id: flow.ticket.chatId,\n chat_type: flow.ticket.chatType ?? ('p2p' as const),\n message_type: 'text',\n content: JSON.stringify({ text: '\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' }),\n thread_id: flow.ticket.threadId,\n },\n };\n const syntheticRuntime = {\n log: (msg: string) => log.info(msg),\n error: (msg: string) => log.error(msg),\n };\n const { promise } = enqueueFeishuChatTask({\n accountId: flow.accountId,\n chatId: flow.ticket.chatId,\n threadId: flow.ticket.threadId,\n task: async () => {\n await withTicket(\n {\n messageId: syntheticMsgId,\n chatId: flow.ticket.chatId,\n accountId: flow.accountId,\n startTime: Date.now(),\n senderOpenId: flow.ticket.senderOpenId!,\n chatType: flow.ticket.chatType,\n threadId: flow.ticket.threadId,\n },\n () =>\n handleFeishuMessage({\n cfg: flow.cfg,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n event: syntheticEvent as any,\n accountId: flow.accountId,\n forceMention: true,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n runtime: syntheticRuntime as any,\n replyToMessageId: flow.ticket.messageId,\n }),\n );\n },\n });\n await promise;\n log.info('synthetic message dispatched after app-auth-only completion');\n } else {\n await executeAuthorize({\n account: acct,\n senderOpenId: flow.ticket.senderOpenId,\n scope: [...mergedScopes].join(' '),\n showBatchAuthHint: true,\n forceAuth: true, // \u5E94\u7528\u6743\u9650\u521A\u7ECF\u5386\u79FB\u9664\u2192\u8865\u56DE\uFF0C\u4E0D\u4FE1\u4EFB\u672C\u5730 UAT \u7F13\u5B58\n cfg: flow.cfg,\n ticket: flow.ticket,\n });\n }\n } catch (err) {\n log.error(`handleCardAction background task failed: ${err}`);\n }\n });\n\n // \u56DE\u8C03\u8FD4\u56DE\u503C\uFF1A\u901A\u8FC7 card \u5B57\u6BB5\u7ACB\u5373\u66F4\u65B0\u5361\u7247 + toast \u63D0\u793A\n return {\n toast: {\n type: 'success' as const,\n content: '\u6743\u9650\u786E\u8BA4\u6210\u529F',\n },\n card: {\n type: 'raw' as const,\n data: successCard,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * \u7EDF\u4E00\u5904\u7406 `client.invoke()` \u629B\u51FA\u7684\u9519\u8BEF\uFF0C\u652F\u6301\u81EA\u52A8\u53D1\u8D77 OAuth \u6388\u6743\u3002\n *\n * \u66FF\u4EE3 `handleInvokeError`\uFF0C\u5728\u5DE5\u5177\u5C42\u76F4\u63A5\u5904\u7406\u6388\u6743\u95EE\u9898\uFF1A\n * - \u7528\u6237\u6388\u6743\u7C7B\u9519\u8BEF \u2192 \u76F4\u63A5 executeAuthorize\uFF08\u53D1 Device Flow \u5361\u7247\uFF09\n * - \u5E94\u7528\u6743\u9650\u7F3A\u5931 \u2192 \u53D1\u9001\u5F15\u5BFC\u5361\u7247\uFF0C\u7528\u6237\u786E\u8BA4\u540E\u81EA\u52A8\u63A5\u529B OAuth\n * - \u5176\u4ED6\u9519\u8BEF \u2192 \u56DE\u9000\u5230 handleInvokeError \u7684\u6807\u51C6\u5904\u7406\n *\n * @param err - invoke() \u6216\u5176\u4ED6\u903B\u8F91\u629B\u51FA\u7684\u9519\u8BEF\n * @param cfg - OpenClaw \u914D\u7F6E\u5BF9\u8C61\uFF08\u4ECE\u5DE5\u5177\u6CE8\u518C\u51FD\u6570\u7684\u95ED\u5305\u4E2D\u83B7\u53D6\uFF09\n */\nexport async function handleInvokeErrorWithAutoAuth(err: unknown, cfg: ClawdbotConfig) {\n const ticket = getTicket();\n\n // --- Path 0\uFF1AOwner \u8BBF\u95EE\u62D2\u7EDD \u2192 \u76F4\u63A5\u8FD4\u56DE\u53CB\u597D\u63D0\u793A ---\n if (err instanceof OwnerAccessDeniedError) {\n return json({\n error: 'permission_denied',\n message: '\u5F53\u524D\u5E94\u7528\u4EC5\u9650\u6240\u6709\u8005\uFF08App Owner\uFF09\u4F7F\u7528\u3002\u60A8\u6CA1\u6709\u6743\u9650\u4F7F\u7528\u76F8\u5173\u529F\u80FD\u3002',\n user_open_id: err.userOpenId,\n // \u6CE8\u610F\uFF1A\u4E0D\u5E8F\u5217\u5316 err.appOwnerId\uFF0C\u907F\u514D\u6CC4\u9732 owner \u7684 open_id\n });\n }\n\n if (ticket) {\n const senderOpenId = ticket.senderOpenId;\n\n // --- Path 1\uFF1A\u7528\u6237\u6388\u6743\u7C7B\u9519\u8BEF \u2192 \u9632\u6296\u5408\u5E76\u540E\u53D1\u8D77 OAuth ---\n\n if (senderOpenId) {\n // 1a. \u7528\u6237\u672A\u6388\u6743\u6216 token scope \u4E0D\u8DB3\uFF08\u4E14 app scope \u5DF2\u9A8C\u8BC1\uFF09\n if (err instanceof UserAuthRequiredError && err.appScopeVerified) {\n const scopes = err.requiredScopes;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5EF6\u8FDF\u68C0\u67E5\uFF1A\u5982\u679C\u540C\u4E00\u6D88\u606F\u6709\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6D41\u7A0B\uFF0C\n // \u5C06\u7528\u6237\u6388\u6743 scope \u6536\u96C6\u5230\u5EF6\u8FDF\u961F\u5217\uFF0C\u7B49\u5E94\u7528\u6388\u6743\u5B8C\u6210\u540E\u7EDF\u4E00\u53D1\u8D77 OAuth\n if (hasActiveAppAuthForMessage(ticket)) {\n addToDeferredUserAuth(ticket, scopes, acct, cfg);\n log.info(`UserAuthRequiredError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);\n return json({\n awaiting_app_authorization: true,\n user_auth_deferred: true,\n message:\n '\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002' +\n '\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n deferred_scopes: scopes,\n });\n }\n\n const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;\n log.info(`UserAuthRequiredError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);\n return await enqueueAuthRequest(\n bufferKey,\n scopes,\n { account: acct, cfg, ticket },\n async (mergedScopes) => {\n // \u7B49\u5F85\u540C\u4E00\u6D88\u606F\u7684 app auth \u5361\u7247\u5148\u53D1\u51FA\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry?.resultPromise) {\n await appEntry.resultPromise.catch(() => {});\n }\n return executeAuthorize({\n account: acct,\n senderOpenId,\n scope: mergedScopes.join(' '),\n showBatchAuthHint: true,\n cfg,\n ticket,\n });\n },\n AUTH_USER_DEBOUNCE_MS,\n );\n }\n } catch (autoAuthErr) {\n log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);\n }\n }\n\n // 1b. \u7528\u6237 token \u5B58\u5728\u4F46 scope \u4E0D\u8DB3\uFF08\u670D\u52A1\u7AEF LARK_ERROR.USER_SCOPE_INSUFFICIENT / 99991679\uFF09\n if (err instanceof UserScopeInsufficientError) {\n const scopes = err.missingScopes;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5EF6\u8FDF\u68C0\u67E5\uFF1A\u540C Path 1a\n if (hasActiveAppAuthForMessage(ticket)) {\n addToDeferredUserAuth(ticket, scopes, acct, cfg);\n log.info(`UserScopeInsufficientError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);\n return json({\n awaiting_app_authorization: true,\n user_auth_deferred: true,\n message:\n '\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002' +\n '\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n deferred_scopes: scopes,\n });\n }\n\n const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;\n log.info(`UserScopeInsufficientError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);\n return await enqueueAuthRequest(\n bufferKey,\n scopes,\n { account: acct, cfg, ticket },\n async (mergedScopes) => {\n // \u7B49\u5F85\u540C\u4E00\u6D88\u606F\u7684 app auth \u5361\u7247\u5148\u53D1\u51FA\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry?.resultPromise) {\n await appEntry.resultPromise.catch(() => {});\n }\n return executeAuthorize({\n account: acct,\n senderOpenId,\n scope: mergedScopes.join(' '),\n showBatchAuthHint: true,\n cfg,\n ticket,\n });\n },\n AUTH_USER_DEBOUNCE_MS,\n );\n }\n } catch (autoAuthErr) {\n log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);\n }\n }\n } else {\n log.error(`senderOpenId not found ${err}`);\n }\n\n // --- Path 2\uFF1A\u5E94\u7528\u6743\u9650\u7F3A\u5931 \u2192 \u9632\u6296\u5408\u5E76\u540E\u53D1\u9001\u5F15\u5BFC\u5361\u7247 ---\n\n if (err instanceof AppScopeMissingError && ticket.chatId) {\n // \u6355\u83B7\u5F53\u524D\u9519\u8BEF\u7684\u9644\u52A0\u4FE1\u606F\uFF0C\u4F9B flushFn \u4F7F\u7528\n const appScopeErr = err;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5C06\u5DE5\u5177\u7684\u5168\u90E8\u6240\u9700 scope \u52A0\u5165\u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\u3002\n // \u5E94\u7528\u6743\u9650\u5B8C\u6210\u540E handleCardAction \u4F1A\u6D88\u8D39\u8FD9\u4E9B scope\uFF0C\n // \u4E0E flow.requiredScopes\uFF08\u4EC5 app \u7F3A\u5931\u7684\uFF09\u5408\u5E76\uFF0C\u4E00\u6B21\u6027\u53D1\u8D77 OAuth\u3002\n if (senderOpenId && appScopeErr.allRequiredScopes?.length) {\n addToDeferredUserAuth(ticket, appScopeErr.allRequiredScopes, acct, cfg);\n log.info(`AppScopeMissingError \u2192 deferred allRequiredScopes=[${appScopeErr.allRequiredScopes.join(', ')}]`);\n }\n\n const bufferKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n log.info(\n `AppScopeMissingError \u2192 enqueue, key=${bufferKey}, ` + `scopes=[${appScopeErr.missingScopes.join(', ')}]`,\n );\n return await enqueueAuthRequest(\n bufferKey,\n appScopeErr.missingScopes,\n { account: acct, cfg, ticket },\n (mergedScopes) =>\n sendAppScopeCard({\n account: acct,\n missingScopes: mergedScopes,\n appId: appScopeErr.appId,\n scopeNeedType: 'all', // \u5408\u5E76\u540E\u6240\u6709 scope \u90FD\u9700\u8981\n tokenType: appScopeErr.tokenType,\n cfg,\n ticket,\n }),\n );\n }\n } catch (cardErr) {\n log.warn(`sendAppScopeCard failed: ${cardErr}, falling back`);\n }\n }\n } else {\n log.error(`ticket not found ${err}`);\n }\n return json({\n error: formatLarkError(err),\n });\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * auto-auth.ts \u2014 \u5DE5\u5177\u5C42\u81EA\u52A8\u6388\u6743\u5904\u7406\u3002\n *\n * \u5F53 OAPI \u5DE5\u5177\u9047\u5230\u6388\u6743\u95EE\u9898\u65F6\uFF0C\u76F4\u63A5\u5728\u5DE5\u5177\u5C42\u5904\u7406\uFF0C\u4E0D\u518D\u8BA9 AI \u5224\u65AD\uFF1A\n *\n * - UserAuthRequiredError (appScopeVerified=true)\n * \u2192 \u76F4\u63A5\u8C03\u7528 executeAuthorize \u53D1\u8D77 OAuth Device Flow \u5361\u7247\n *\n * - UserScopeInsufficientError\n * \u2192 \u76F4\u63A5\u8C03\u7528 executeAuthorize\uFF08\u4F7F\u7528 missingScopes\uFF09\n *\n * - AppScopeMissingError\n * \u2192 \u53D1\u9001\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\uFF1B\u7528\u6237\u70B9\u51FB\"\u6211\u5DF2\u5B8C\u6210\"\u540E\uFF1A\n * 1. \u66F4\u65B0\u5361\u7247\u4E3A\u5904\u7406\u4E2D\u72B6\u6001\n * 2. invalidateAppScopeCache\n * 3. \u53D1\u9001\u4E2D\u95F4\u5408\u6210\u6D88\u606F\u544A\u77E5 AI\uFF08\"\u5E94\u7528\u6743\u9650\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u53D1\u8D77\u7528\u6237\u6388\u6743...\"\uFF09\n * 4. \u8C03\u7528 executeAuthorize \u53D1\u8D77 OAuth Device Flow\n *\n * - \u5176\u4ED6\u60C5\u51B5\uFF08AppScopeCheckFailedError\u3001appScopeVerified=false \u7B49\uFF09\n * \u2192 \u56DE\u9000\u5230\u539F handleInvokeError\uFF08\u4E0D\u89E6\u53D1\u81EA\u52A8\u6388\u6743\uFF09\n *\n * \u964D\u7EA7\u7B56\u7565\uFF08\u4FDD\u5B88\uFF09\uFF1A\u4EE5\u4E0B\u60C5\u51B5\u5747\u56DE\u9000\u5230 handleInvokeError\uFF1A\n * - \u65E0 LarkTicket\uFF08\u975E\u6D88\u606F\u573A\u666F\uFF09\n * - \u65E0 senderOpenId\uFF08\u65E0\u6CD5\u786E\u5B9A\u6388\u6743\u5BF9\u8C61\uFF09\n * - \u8D26\u53F7\u672A\u914D\u7F6E\uFF08!acct.configured\uFF09\n * - \u4EFB\u4F55\u6B65\u9AA4\u629B\u51FA\u5F02\u5E38\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport type { ConfiguredLarkAccount } from '../core/types';\nimport type { LarkTicket } from '../core/lark-ticket';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\n\nconst log = larkLogger('tools/auto-auth');\nimport { getLarkAccount } from '../core/accounts';\nimport { UserAuthRequiredError, UserScopeInsufficientError, AppScopeMissingError } from '../core/tool-client';\nimport { invalidateAppScopeCache, getAppGrantedScopes, isAppScopeSatisfied } from '../core/app-scope-checker';\nimport { LarkClient } from '../core/lark-client';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { executeAuthorize } from './oauth';\nimport { formatLarkError, json } from './oapi/helpers';\nimport { handleProjectAuthCardAction } from './project-oauth';\nimport { OwnerAccessDeniedError } from '../core/owner-policy';\nimport { enqueueFeishuChatTask } from '../channel/chat-queue';\nimport { handleFeishuMessage } from '../messaging/inbound/handler';\nimport { withTicket } from '../core/lark-ticket';\n\n// ---------------------------------------------------------------------------\n// Debounce + scope merge \u2014 \u9632\u6296\u7F13\u51B2\u533A\uFF08\u4E24\u9636\u6BB5\uFF09\n//\n// \u5DE5\u5177\u8C03\u7528\u53EF\u80FD\u662F\u771F\u6B63\u5E76\u53D1\uFF0850ms \u5185\u5230\u8FBE\uFF09\u6216\u88AB\u6846\u67B6\u5E8F\u5217\u5316\uFF08\u95F4\u9694\u6570\u79D2\u5230\u8FBE\uFF09\u3002\n// \u4E3A\u540C\u65F6\u8986\u76D6\u4E24\u79CD\u573A\u666F\uFF0C\u91C7\u7528\u4E24\u9636\u6BB5\u8BBE\u8BA1\uFF1A\n//\n// collecting\uFF08\u6536\u96C6\u9636\u6BB5\uFF09\uFF1A50ms \u9632\u6296\u7A97\u53E3\uFF0C\u5408\u5E76 scope\n// executing\uFF08\u6267\u884C\u9636\u6BB5\uFF09\uFF1AflushFn \u6B63\u5728\u8FD0\u884C\uFF0C\u540E\u7EED\u8BF7\u6C42\u590D\u7528\u540C\u4E00\u7ED3\u679C\n//\n// \u4ECE collecting \u2192 executing \u8F6C\u6362\u65F6\u4E0D\u4ECE Map \u4E2D\u5220\u9664 entry\uFF0C\n// \u76F4\u5230 flushFn \u5B8C\u6210\uFF08resolve / reject\uFF09\u624D\u79FB\u9664\u3002\n// ---------------------------------------------------------------------------\n\ntype JsonResult = ReturnType<typeof json>;\n\n/** \u7F13\u51B2\u4E2D\u7684\u6388\u6743\u8BF7\u6C42 */\ninterface AuthBatchEntry {\n phase: 'collecting' | 'executing';\n scopes: Set<string>;\n waiters: Array<{ resolve: (v: JsonResult) => void; reject: (e: unknown) => void }>;\n timer: ReturnType<typeof setTimeout> | null;\n /** flushFn \u6267\u884C\u4E2D\u7684 Promise\uFF08executing \u9636\u6BB5\u6709\u503C\uFF09 */\n resultPromise: Promise<JsonResult> | null;\n /** executing \u9636\u6BB5\uFF1A\u65B0 scope \u5230\u8FBE\u65F6\u7684\u5EF6\u8FDF\u5237\u65B0\u5B9A\u65F6\u5668 */\n updateTimer: ReturnType<typeof setTimeout> | null;\n /** scope \u66F4\u65B0\u7684 executeAuthorize \u662F\u5426\u6B63\u5728\u6267\u884C\uFF08\u4E92\u65A5\u9501\uFF09 */\n isUpdating: boolean;\n /** isUpdating \u671F\u95F4\u53C8\u6709\u65B0 scope \u5230\u8FBE\uFF0C\u9700\u8981\u518D\u66F4\u65B0\u4E00\u8F6E */\n pendingReupdate: boolean;\n /** flushFn \u5F15\u7528\uFF0Cexecuting \u9636\u6BB5\u7528\u4E8E scope \u66F4\u65B0\u65F6\u91CD\u65B0\u8C03\u7528 */\n flushFn: ((mergedScopes: string[]) => Promise<JsonResult>) | null;\n /** \u4EE5\u4E0B\u5B57\u6BB5\u6765\u81EA\u7B2C\u4E00\u4E2A\u5165\u961F\u7684\u8BF7\u6C42\uFF0C\u540E\u7EED\u8BF7\u6C42\u590D\u7528 */\n account: ConfiguredLarkAccount;\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/**\n * \u9632\u6296\u7F13\u51B2\u533A Map\u3002\n *\n * Key \u89C4\u5219\uFF1A\n * \u7528\u6237\u6388\u6743\uFF1A`user:${accountId}:${senderOpenId}:${messageId}`\n * \u5E94\u7528\u6388\u6743\uFF1A`app:${accountId}:${chatId}:${messageId}`\n */\nconst authBatches = new Map<string, AuthBatchEntry>();\n\n/** \u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09 */\nconst AUTH_DEBOUNCE_MS = 50;\n\n/** \u7528\u6237\u6388\u6743\u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09\u3002\u6BD4 app auth \u7684 50ms \u66F4\u957F\uFF0C\u4FDD\u8BC1\u5E94\u7528\u6743\u9650\u5361\u7247\u5148\u53D1\u51FA\u3002 */\nconst AUTH_USER_DEBOUNCE_MS = 150;\n\n/**\n * Scope \u66F4\u65B0\u9632\u6296\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09\u3002\n * \u6BD4\u521D\u59CB\u9632\u6296\u66F4\u957F\uFF0C\u56E0\u4E3A\u5DE5\u5177\u8C03\u7528\u53EF\u80FD\u95F4\u9694\u6570\u5341\u5230\u6570\u767E\u6BEB\u79D2\u987A\u5E8F\u5230\u8FBE\u3002\n * \u9700\u8981\u7B49\u8DB3\u591F\u4E45\u4EE5\u6536\u96C6\u6240\u6709\u540E\u7EED\u5230\u8FBE\u7684 scope \u540E\u518D\u4E00\u6B21\u6027\u66F4\u65B0\u5361\u7247\u3002\n */\nconst AUTH_UPDATE_DEBOUNCE_MS = 500;\n\n/**\n * \u51B7\u5374\u671F\uFF08\u6BEB\u79D2\uFF09\u3002\n * flushFn \u6267\u884C\u5B8C\u6BD5\u540E\uFF0Centry \u7EE7\u7EED\u4FDD\u7559\u5728 Map \u4E2D\u8FD9\u4E48\u957F\u65F6\u95F4\uFF0C\n * \u9632\u6B62\u540E\u7EED\u987A\u5E8F\u5230\u8FBE\u7684\u5DE5\u5177\u8C03\u7528\u521B\u5EFA\u91CD\u590D\u5361\u7247\u3002\n */\nconst AUTH_COOLDOWN_MS = 30_000;\n\n/**\n * \u5C06\u6388\u6743\u8BF7\u6C42\u5165\u961F\u5230\u9632\u6296\u7F13\u51B2\u533A\u3002\n *\n * \u540C\u4E00 bufferKey \u7684\u8BF7\u6C42\u4F1A\u88AB\u5408\u5E76\uFF1A\n * - collecting \u9636\u6BB5\uFF1Ascope \u96C6\u5408\u53D6\u5E76\u96C6\uFF0C\u5171\u4EAB\u540C\u4E00\u4E2A flushFn \u6267\u884C\u7ED3\u679C\n * - executing \u9636\u6BB5\uFF1AflushFn \u5DF2\u5728\u8FD0\u884C\uFF0C\u540E\u7EED\u8BF7\u6C42\u76F4\u63A5\u590D\u7528\u5DF2\u6709\u7ED3\u679C\uFF08\u4E0D\u91CD\u590D\u53D1\u5361\u7247\uFF09\n *\n * @param bufferKey - \u7F13\u51B2\u533A key\uFF08\u533A\u5206\u4E0D\u540C\u7528\u6237/\u4F1A\u8BDD\uFF09\n * @param scopes - \u672C\u6B21\u8BF7\u6C42\u9700\u8981\u7684 scope \u5217\u8868\n * @param ctx - \u4E0A\u4E0B\u6587\u4FE1\u606F\uFF08\u4EC5\u7B2C\u4E00\u4E2A\u8BF7\u6C42\u7684\u88AB\u91C7\u7528\uFF09\n * @param flushFn - \u5B9A\u65F6\u5668\u5230\u671F\u540E\u6267\u884C\u7684\u5B9E\u9645\u6388\u6743\u51FD\u6570\uFF0C\u63A5\u6536\u5408\u5E76\u540E\u7684 scope \u6570\u7EC4\n */\nfunction enqueueAuthRequest(\n bufferKey: string,\n scopes: string[],\n ctx: { account: ConfiguredLarkAccount; cfg: ClawdbotConfig; ticket: LarkTicket },\n flushFn: (mergedScopes: string[]) => Promise<JsonResult>,\n debounceMs: number = AUTH_DEBOUNCE_MS,\n): Promise<JsonResult> {\n const existing = authBatches.get(bufferKey);\n\n if (existing) {\n // \u4E0D\u8BBA\u54EA\u4E2A\u9636\u6BB5\uFF0C\u90FD\u8FFD\u52A0 scope\n for (const s of scopes) existing.scopes.add(s);\n\n if (existing.phase === 'executing') {\n // flushFn \u5DF2\u5728\u6267\u884C\u6216\u5DF2\u5B8C\u6210\uFF08\u5361\u7247\u5DF2\u53D1\u51FA\uFF09\uFF0C\u590D\u7528\u7ED3\u679C\n // \u540C\u65F6\u89E6\u53D1\u5EF6\u8FDF\u5237\u65B0\uFF1A\u7528\u5408\u5E76\u540E\u7684 scope \u91CD\u65B0\u8C03\u7528 flushFn \u66F4\u65B0\u5361\u7247\n log.info(`auth in-flight, piggyback \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);\n\n // \u9632\u6296 + \u4E92\u65A5\uFF1A\u591A\u4E2A\u5FEB\u901F\u5230\u8FBE\u7684\u8BF7\u6C42\u53EA\u89E6\u53D1\u4E00\u6B21\u5361\u7247\u66F4\u65B0\n if (existing.updateTimer) clearTimeout(existing.updateTimer);\n existing.updateTimer = setTimeout(async () => {\n existing.updateTimer = null;\n\n // \u4E92\u65A5\uFF1A\u5982\u679C\u4E0A\u4E00\u8F6E\u66F4\u65B0\u8FD8\u5728\u6267\u884C\uFF0C\u6807\u8BB0 pendingReupdate \u7B49\u5B83\u7ED3\u675F\u540E\u91CD\u8DD1\n if (existing.isUpdating) {\n existing.pendingReupdate = true;\n log.info(`scope update deferred (previous update still running) \u2192 key=${bufferKey}`);\n return;\n }\n\n existing.isUpdating = true;\n try {\n const mergedScopes = [...existing.scopes];\n log.info(`scope update flush \u2192 key=${bufferKey}, scopes=[${mergedScopes.join(', ')}]`);\n // \u91CD\u65B0\u8C03\u7528 flushFn\uFF08executeAuthorize \u4F1A\u68C0\u6D4B\u5230 pendingFlow\uFF0C\n // \u539F\u5730\u66F4\u65B0\u65E7\u5361\u7247\u5185\u5BB9 + \u91CD\u542F Device Flow\uFF09\n await existing.flushFn!(mergedScopes);\n } catch (err) {\n log.warn(`scope update failed: ${err}`);\n } finally {\n existing.isUpdating = false;\n // \u5982\u679C\u9501\u5B9A\u671F\u95F4\u6709\u65B0 scope \u5230\u8FBE\uFF0C\u518D\u8DD1\u4E00\u8F6E\n if (existing.pendingReupdate) {\n existing.pendingReupdate = false;\n const finalScopes = [...existing.scopes];\n log.info(`scope reupdate \u2192 key=${bufferKey}, scopes=[${finalScopes.join(', ')}]`);\n try {\n await existing.flushFn!(finalScopes);\n } catch (err) {\n log.warn(`scope reupdate failed: ${err}`);\n }\n }\n }\n }, AUTH_UPDATE_DEBOUNCE_MS);\n\n return existing.resultPromise!;\n }\n\n // collecting \u9636\u6BB5\uFF1A\u6B63\u5E38\u5408\u5E76\n log.info(`debounce merge \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);\n return new Promise<JsonResult>((resolve, reject) => {\n existing.waiters.push({ resolve, reject });\n });\n }\n\n // \u521B\u5EFA\u65B0\u7F13\u51B2\u533A\uFF08collecting \u9636\u6BB5\uFF09\n const entry: AuthBatchEntry = {\n phase: 'collecting',\n scopes: new Set(scopes),\n waiters: [],\n timer: null,\n resultPromise: null,\n updateTimer: null,\n isUpdating: false,\n pendingReupdate: false,\n flushFn: null,\n account: ctx.account,\n cfg: ctx.cfg,\n ticket: ctx.ticket,\n };\n\n const promise = new Promise<JsonResult>((resolve, reject) => {\n entry.waiters.push({ resolve, reject });\n });\n\n entry.timer = setTimeout(async () => {\n // \u8F6C\u5165 executing \u9636\u6BB5\uFF08\u4E0D\u4ECE Map \u4E2D\u5220\u9664\uFF0C\u963B\u6B62\u540E\u7EED\u8BF7\u6C42\u521B\u5EFA\u65B0\u5361\u7247\uFF09\n entry.phase = 'executing';\n entry.timer = null;\n entry.flushFn = flushFn; // \u4FDD\u5B58\u5F15\u7528\uFF0C\u4F9B executing \u9636\u6BB5 scope \u66F4\u65B0\u65F6\u91CD\u65B0\u8C03\u7528\n const mergedScopes = [...entry.scopes];\n\n log.info(\n `debounce flush \u2192 key=${bufferKey}, ` + `waiters=${entry.waiters.length}, scopes=[${mergedScopes.join(', ')}]`,\n );\n\n // \u5C06 flushFn \u7684 Promise \u5B58\u5165 entry\uFF0C\u4F9B executing \u9636\u6BB5\u7684\u540E\u6765\u8005\u590D\u7528\n entry.resultPromise = flushFn(mergedScopes);\n\n try {\n const result = await entry.resultPromise;\n for (const w of entry.waiters) w.resolve(result);\n } catch (err) {\n for (const w of entry.waiters) w.reject(err);\n } finally {\n // \u8FDB\u5165\u51B7\u5374\u671F\uFF1Aentry \u7EE7\u7EED\u7559\u5728 Map \u4E2D\uFF0C\u540E\u7EED\u5230\u8FBE\u7684\u5DE5\u5177\u8C03\u7528\n // \u4F1A\u547D\u4E2D executing \u5206\u652F\u5E76\u590D\u7528 resultPromise\uFF0C\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5361\u7247\u3002\n // \u51B7\u5374\u671F\u7ED3\u675F\u540E\u6E05\u7406\u3002\n setTimeout(() => authBatches.delete(bufferKey), AUTH_COOLDOWN_MS);\n }\n }, debounceMs);\n\n authBatches.set(bufferKey, entry);\n return promise;\n}\n\n// ---------------------------------------------------------------------------\n// PendingAppAuthFlow \u2014 \u7B49\u5F85\u7528\u6237\u786E\u8BA4\u7684\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u6D41\u7A0B\n// ---------------------------------------------------------------------------\n\ninterface PendingAppAuthFlow {\n appId: string;\n accountId: string;\n cardId: string;\n sequence: number;\n requiredScopes: string[];\n /** \u4E0E\u89E6\u53D1 AppScopeMissingError \u65F6\u7684 scopeNeedType \u4E00\u81F4\u3002 */\n scopeNeedType?: 'one' | 'all';\n /** \u4E0E\u89E6\u53D1 AppScopeMissingError \u65F6\u7684 tokenType \u4E00\u81F4\u3002 */\n tokenType?: 'user' | 'tenant';\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/** TTL\uFF1A15 \u5206\u949F\u540E\u81EA\u52A8\u6E05\u7406\uFF0C\u9632\u6B62\u5185\u5B58\u6CC4\u6F0F\u3002 */\nconst PENDING_FLOW_TTL_MS = 15 * 60 * 1000;\n\n/** \u8BA1\u7B97\u53BB\u91CD key\uFF08chatId + messageId + \u6709\u5E8F scopes\uFF09\u3002 */\nfunction makeDedupKey(chatId: string, messageId: string, scopes: string[]): string {\n return chatId + '\\0' + messageId + '\\0' + [...scopes].sort().join(',');\n}\n\n/** \u6CE8\u518C\u540E\u7684 flow\uFF0C\u9644\u52A0\u7D22\u5F15\u952E\u4FE1\u606F */\ntype RegisteredFlow = PendingAppAuthFlow & {\n dedupKey: string;\n activeCardKey: string;\n};\n\n/**\n * \u5E94\u7528\u6743\u9650\u6388\u6743\u6D41\u7BA1\u7406\u5668 \u2014 \u7EDF\u4E00\u7BA1\u7406\u4E09\u4E2A\u5173\u8054\u7D22\u5F15\u7684\u4E00\u81F4\u6027\u3002\n *\n * \u66FF\u4EE3\u539F\u6765\u6563\u5E03\u7684 pendingAppAuthFlows / dedupIndex / activeAppCardIndex \u4E09\u4E2A Map\uFF0C\n * \u786E\u4FDD\u6CE8\u518C\u3001\u5220\u9664\u3001\u8FC1\u79FB\u64CD\u4F5C\u7684\u539F\u5B50\u6027\u3002\n */\nclass AppAuthFlowManager {\n private readonly flows = new Map<string, RegisteredFlow>();\n private readonly dedupIndex = new Map<string, string>();\n private readonly activeCardIndex = new Map<string, string>();\n\n /** \u539F\u5B50\u6CE8\u518C\u65B0\u6D41\u7A0B\uFF08\u540C\u65F6\u5199\u5165 3 \u4E2A\u7D22\u5F15 + \u8BBE\u7F6E\u7EDF\u4E00 TTL\uFF09 */\n register(operationId: string, flow: PendingAppAuthFlow, dedupKey: string, activeCardKey: string): void {\n const registered: RegisteredFlow = { ...flow, dedupKey, activeCardKey };\n this.flows.set(operationId, registered);\n this.dedupIndex.set(dedupKey, operationId);\n this.activeCardIndex.set(activeCardKey, operationId);\n\n // \u7EDF\u4E00 TTL \u6E05\u7406\n setTimeout(() => {\n if (!this.flows.has(operationId)) return; // \u5DF2\u88AB\u624B\u52A8\u6E05\u7406\uFF0C\u8DF3\u8FC7\n this.remove(operationId);\n }, PENDING_FLOW_TTL_MS);\n }\n\n /** \u53EA\u9700 operationId \u5373\u53EF\u539F\u5B50\u6E05\u7406\u6240\u6709\u7D22\u5F15 */\n remove(operationId: string): void {\n const flow = this.flows.get(operationId);\n if (!flow) return;\n\n // \u8054\u52A8\u6E05\u7406\u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\uFF08\u9632\u6B62\u5185\u5B58\u6CC4\u6F0F\uFF09\n if (flow.ticket?.senderOpenId) {\n const deferKey = `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;\n deferredUserAuth.delete(deferKey);\n }\n\n this.flows.delete(operationId);\n // \u6761\u4EF6\u5220\u9664\uFF1A\u9632\u6B62\u8BEF\u5220\u5DF2\u88AB\u65B0 flow \u8986\u76D6\u7684\u7D22\u5F15\n if (this.dedupIndex.get(flow.dedupKey) === operationId) {\n this.dedupIndex.delete(flow.dedupKey);\n }\n if (this.activeCardIndex.get(flow.activeCardKey) === operationId) {\n this.activeCardIndex.delete(flow.activeCardKey);\n }\n }\n\n /**\n * \u8FC1\u79FB\u5230\u65B0 operationId\uFF08\u5361\u7247\u590D\u7528\u573A\u666F\uFF1A\u6309\u94AE\u56DE\u8C03\u9700\u8981\u5339\u914D\u65B0 ID\uFF09\u3002\n * \u539F\u5B50\u64CD\u4F5C\uFF1A\u6E05\u7406\u65E7\u7D22\u5F15 \u2192 \u66F4\u65B0 flow \u2192 \u5EFA\u7ACB\u65B0\u7D22\u5F15 \u2192 \u6CE8\u518C\u65B0 TTL\u3002\n *\n * \u4FEE\u590D\u539F\u4EE3\u7801\u5361\u7247\u590D\u7528\u8DEF\u5F84\u7F3A\u5C11 TTL \u6CE8\u518C\u5BFC\u81F4\u7684\u5185\u5B58\u6CC4\u6F0F\u3002\n */\n migrateToNewOperationId(\n oldOperationId: string,\n newOperationId: string,\n updates?: { dedupKey?: string; requiredScopes?: string[]; scopeNeedType?: 'one' | 'all' },\n ): RegisteredFlow | undefined {\n const flow = this.flows.get(oldOperationId);\n if (!flow) return undefined;\n\n // \u6E05\u7406\u65E7\u7D22\u5F15\n this.flows.delete(oldOperationId);\n if (updates?.dedupKey) {\n if (this.dedupIndex.get(flow.dedupKey) === oldOperationId) {\n this.dedupIndex.delete(flow.dedupKey);\n }\n flow.dedupKey = updates.dedupKey;\n }\n if (updates?.requiredScopes) flow.requiredScopes = updates.requiredScopes;\n if (updates?.scopeNeedType) flow.scopeNeedType = updates.scopeNeedType;\n\n // \u5EFA\u7ACB\u65B0\u7D22\u5F15\n this.flows.set(newOperationId, flow);\n this.dedupIndex.set(flow.dedupKey, newOperationId);\n this.activeCardIndex.set(flow.activeCardKey, newOperationId);\n\n // \u4E3A\u65B0 operationId \u6CE8\u518C TTL\uFF08\u4FEE\u590D\u539F\u4EE3\u7801\u7684\u5185\u5B58\u6CC4\u6F0F\uFF09\n setTimeout(() => {\n if (!this.flows.has(newOperationId)) return;\n this.remove(newOperationId);\n }, PENDING_FLOW_TTL_MS);\n\n return flow;\n }\n\n /** \u901A\u8FC7 operationId \u67E5\u8BE2\uFF08card action \u56DE\u8C03\u7528\uFF09 */\n getByOperationId(id: string): PendingAppAuthFlow | undefined {\n return this.flows.get(id);\n }\n\n /** \u901A\u8FC7\u53BB\u91CD\u952E\u67E5\u8BE2\uFF08\u907F\u514D\u53D1\u9001\u91CD\u590D\u5361\u7247\uFF09 */\n getByDedupKey(key: string): { operationId: string; flow: PendingAppAuthFlow } | undefined {\n const opId = this.dedupIndex.get(key);\n if (!opId) return undefined;\n const flow = this.flows.get(opId);\n return flow ? { operationId: opId, flow } : undefined;\n }\n\n /** \u901A\u8FC7\u6D3B\u8DC3\u5361\u7247\u952E\u67E5\u8BE2\uFF08\u540C\u6D88\u606F\u5361\u7247\u590D\u7528\uFF09 */\n getByActiveCardKey(key: string): { operationId: string; flow: RegisteredFlow } | undefined {\n const opId = this.activeCardIndex.get(key);\n if (!opId) return undefined;\n const flow = this.flows.get(opId);\n return flow ? { operationId: opId, flow } : undefined;\n }\n}\n\nconst appAuthFlows = new AppAuthFlowManager();\n\n// ---------------------------------------------------------------------------\n// Deferred User Auth Queue \u2014 \u7528\u6237\u6388\u6743\u5EF6\u8FDF\u961F\u5217\n//\n// \u5F53\u7528\u6237\u6388\u6743\u8BF7\u6C42\u5230\u8FBE\u65F6\uFF0C\u5982\u679C\u540C\u4E00\u6D88\u606F\u4E0A\u4E0B\u6587\u5B58\u5728\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6D41\u7A0B\uFF0C\n// \u5C06 scope \u6536\u96C6\u5230\u5EF6\u8FDF\u961F\u5217\uFF0C\u7B49\u5E94\u7528\u6388\u6743\u5B8C\u6210\u540E\u7EDF\u4E00\u53D1\u8D77 OAuth\u3002\n// ---------------------------------------------------------------------------\n\ninterface DeferredUserAuthEntry {\n scopes: Set<string>;\n account: ConfiguredLarkAccount;\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}\n\n/** \u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\u3002Key: `${accountId}:${senderOpenId}:${messageId}` */\nconst deferredUserAuth = new Map<string, DeferredUserAuthEntry>();\n\n/**\n * \u68C0\u67E5\u6307\u5B9A\u6D88\u606F\u4E0A\u4E0B\u6587\u662F\u5426\u6709\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6388\u6743\u6D41\u7A0B\u3002\n * \u68C0\u67E5\u4E24\u4E2A\u6765\u6E90\uFF1A\n * 1. authBatches \u4E2D\u7684 app auth entry\uFF08collecting/executing \u9636\u6BB5\uFF09\n * 2. appAuthFlows \u4E2D\u7684\u6D3B\u8DC3\u6D41\uFF08\u5361\u7247\u5DF2\u53D1\u9001\uFF0C\u7B49\u5F85\u7528\u6237\u70B9\u51FB\"\u5DF2\u5B8C\u6210\"\uFF09\n */\nfunction hasActiveAppAuthForMessage(ticket: LarkTicket): boolean {\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry && (appEntry.phase === 'collecting' || appEntry.phase === 'executing')) {\n return true;\n }\n const activeCardKey = `${ticket.chatId}:${ticket.messageId}`;\n return !!appAuthFlows.getByActiveCardKey(activeCardKey);\n}\n\n/**\n * \u5C06\u7528\u6237\u6388\u6743 scope \u6DFB\u52A0\u5230\u5EF6\u8FDF\u961F\u5217\u3002\n * \u591A\u4E2A\u5DE5\u5177\u8C03\u7528\u7684 scope \u4F1A\u88AB\u5408\u5E76\u5230\u540C\u4E00\u4E2A entry\u3002\n */\nfunction addToDeferredUserAuth(\n ticket: LarkTicket,\n scopes: string[],\n account: ConfiguredLarkAccount,\n cfg: ClawdbotConfig,\n): void {\n const key = `${ticket.accountId}:${ticket.senderOpenId}:${ticket.messageId}`;\n const existing = deferredUserAuth.get(key);\n if (existing) {\n for (const s of scopes) existing.scopes.add(s);\n log.info(`deferred user auth scope merge \u2192 key=${key}, scopes=[${[...existing.scopes].join(', ')}]`);\n } else {\n deferredUserAuth.set(key, { scopes: new Set(scopes), account, cfg, ticket });\n log.info(`deferred user auth created \u2192 key=${key}, scopes=[${scopes.join(', ')}]`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Card builders \u2014 CardKit v2 \u683C\u5F0F\n// ---------------------------------------------------------------------------\n\n/**\n * \u6784\u5EFA\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u3002\n *\n * \u84DD\u8272 header\uFF0C\u5217\u51FA\u7F3A\u5931\u7684 scope\uFF0C\u63D0\u4F9B\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u548C\"\u6211\u5DF2\u5B8C\u6210\uFF0C\u7EE7\u7EED\u6388\u6743\"\u6309\u94AE\u3002\n */\nfunction buildAppScopeMissingCard(params: {\n missingScopes: string[];\n appId?: string;\n operationId: string;\n}): Record<string, unknown> {\n const { missingScopes, appId, operationId } = params;\n const authUrl = appId\n ? `https://open.feishu.cn/app/${appId}/auth?q=${encodeURIComponent(missingScopes.join(','))}&op_from=feishu-openclaw&token_type=user`\n : 'https://open.feishu.cn/';\n const multiUrl = { url: authUrl, pc_url: authUrl, android_url: authUrl, ios_url: authUrl };\n\n const scopeList = missingScopes.map((s) => `\u2022 ${s}`).join('\\n');\n\n return {\n schema: '2.0',\n config: { wide_screen_mode: true },\n header: {\n title: { tag: 'plain_text', content: '\uD83D\uDD10 \u9700\u8981\u7533\u8BF7\u6743\u9650\u624D\u80FD\u7EE7\u7EED' },\n template: 'orange',\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u8C03\u7528\u524D\uFF0C\u8BF7\u4F60\u5148\u7533\u8BF7\u4EE5\u4E0B**\u6240\u6709**\u6743\u9650\uFF1A',\n text_size: 'normal',\n },\n {\n tag: 'column_set',\n flex_mode: 'none',\n background_style: 'grey',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: scopeList }],\n },\n ],\n },\n { tag: 'hr' },\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 3,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: '**\u7B2C\u4E00\u6B65\uFF1A\u7533\u8BF7\u6240\u6709\u6743\u9650**' }],\n },\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u53BB\u7533\u8BF7' },\n type: 'primary',\n multi_url: multiUrl,\n },\n ],\n },\n ],\n },\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 3,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: '**\u7B2C\u4E8C\u6B65\uFF1A\u521B\u5EFA\u7248\u672C\u5E76\u5BA1\u6838\u901A\u8FC7**' }],\n },\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u5DF2\u5B8C\u6210' },\n type: 'default',\n value: { action: 'app_auth_done', operation_id: operationId },\n },\n ],\n },\n ],\n },\n ],\n },\n };\n}\n\n/**\n * \u6784\u5EFA\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u7684\"\u5904\u7406\u4E2D\"\u72B6\u6001\uFF08\u7528\u6237\u70B9\u51FB\u6309\u94AE\u540E\u66F4\u65B0\uFF09\u3002\n */\nfunction buildAppAuthProgressCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: { wide_screen_mode: false },\n header: {\n title: { tag: 'plain_text', content: '\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A' },\n subtitle: { tag: 'plain_text', content: '' },\n template: 'green',\n padding: '12px 12px 12px 12px',\n icon: { tag: 'standard_icon', token: 'yes_filled' },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u60A8\u7684\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u6B63\u5728\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743',\n text_size: 'normal',\n },\n ],\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * \u53D1\u9001\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\uFF0C\u5E76\u5C06 flow \u5B58\u5165 pendingAppAuthFlows\u3002\n * \u8FD4\u56DE\u5DE5\u5177\u7ED3\u679C\uFF08\u544A\u77E5 AI \u7B49\u5F85\u7528\u6237\u64CD\u4F5C\uFF09\u3002\n */\nasync function sendAppScopeCard(params: {\n account: ConfiguredLarkAccount;\n missingScopes: string[];\n appId?: string;\n scopeNeedType?: 'one' | 'all';\n tokenType?: 'user' | 'tenant';\n cfg: ClawdbotConfig;\n ticket: LarkTicket;\n}): Promise<ReturnType<typeof json>> {\n const { account, missingScopes, appId, scopeNeedType, tokenType, cfg, ticket } = params;\n const { accountId, chatId, messageId } = ticket;\n const activeCardKey = `${chatId}:${messageId}`;\n\n // ---- \u53BB\u91CD\uFF1A\u907F\u514D\u5E76\u53D1\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u51FA\u591A\u5F20\u5185\u5BB9\u76F8\u540C\u7684\u5361\u7247 ----\n const dedup = makeDedupKey(chatId, messageId, missingScopes);\n const existingEntry = appAuthFlows.getByDedupKey(dedup);\n if (existingEntry) {\n log.info(\n `dedup \u2013 app-scope card already pending for chatId=${chatId}, ` +\n `scopes=[${missingScopes.join(', ')}], skipping duplicate send`,\n );\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n }\n\n // ---- \u5361\u7247\u590D\u7528\uFF1A\u540C\u4E00 chatId+messageId \u5DF2\u6709\u6D3B\u8DC3\u5361\u7247\u65F6\uFF0C\u539F\u5730\u66F4\u65B0\u800C\u975E\u521B\u5EFA\u65B0\u5361\u7247 ----\n const activeEntry = appAuthFlows.getByActiveCardKey(activeCardKey);\n\n if (activeEntry) {\n const { operationId: activeOpId, flow: activeFlow } = activeEntry;\n // \u66F4\u65B0\u5DF2\u6709\u5361\u7247\u7684\u5185\u5BB9\uFF08\u5408\u5E76\u540E\u7684 scope\uFF09\n const newOperationId = Date.now().toString(36) + Math.random().toString(36).slice(2);\n const card = buildAppScopeMissingCard({ missingScopes, appId, operationId: newOperationId });\n const newSeq = activeFlow.sequence + 1;\n\n // TOCTOU \u4FEE\u590D\uFF1A\u5148\u539F\u5B50\u8FC1\u79FB\uFF08\u540C\u6B65\u64CD\u4F5C\uFF09\uFF0C\u518D await \u66F4\u65B0\u5361\u7247\n const newDedup = makeDedupKey(chatId, messageId, missingScopes);\n const migrated = appAuthFlows.migrateToNewOperationId(activeOpId, newOperationId, {\n dedupKey: newDedup,\n requiredScopes: missingScopes,\n scopeNeedType,\n });\n if (!migrated) {\n // \u88AB\u5176\u4ED6\u5E76\u53D1\u8BF7\u6C42\u62A2\u5148\u8FC1\u79FB\u4E86\uFF0C\u964D\u7EA7\u5230\u65B0\u5EFA\u5361\u7247\n log.info(`migrate raced, falling through to new card creation`);\n } else {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: activeFlow.cardId,\n card,\n sequence: newSeq,\n accountId,\n });\n log.info(\n `app-scope card updated in-place, cardId=${activeFlow.cardId}, ` +\n `seq=${newSeq}, scopes=[${missingScopes.join(', ')}]`,\n );\n\n // \u66F4\u65B0 sequence\uFF08migrate \u4E0D\u5904\u7406 sequence\uFF09\n migrated.sequence = newSeq;\n\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n } catch (err) {\n // \u56DE\u6EDA\uFF1A\u5220\u9664\u5DF2\u8FC1\u79FB\u7684 flow\n appAuthFlows.remove(newOperationId);\n log.warn(`failed to update existing app-scope card, creating new one: ${err}`);\n // \u964D\u7EA7\uFF1A\u8D70\u4E0B\u9762\u7684\u65B0\u5EFA\u5361\u7247\u8DEF\u5F84\n }\n }\n }\n\n const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);\n\n const card = buildAppScopeMissingCard({ missingScopes, appId, operationId });\n\n // \u521B\u5EFA CardKit \u5361\u7247\u5B9E\u4F53\n const cardId = await createCardEntity({ cfg, card, accountId });\n if (!cardId) {\n log.warn('createCardEntity failed for app-scope card, falling back');\n return json({\n error: 'app_scope_missing',\n missing_scopes: missingScopes,\n message:\n `\u5E94\u7528\u7F3A\u5C11\u4EE5\u4E0B\u6743\u9650\uFF1A${missingScopes.join(', ')}\uFF0C` +\n `\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u540E\u91CD\u8BD5\u3002` +\n (appId ? `\\n\u6743\u9650\u7BA1\u7406\uFF1Ahttps://open.feishu.cn/app/${appId}/permission` : ''),\n });\n }\n\n // \u53D1\u9001\u5230\u5F53\u524D\u4F1A\u8BDD\n const replyToMsgId = ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined;\n\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: replyToMsgId,\n replyInThread: Boolean(ticket?.threadId),\n accountId,\n });\n\n // \u539F\u5B50\u6CE8\u518C\u5230\u7BA1\u7406\u5668\uFF08\u7EDF\u4E00 TTL \u6E05\u7406\uFF09\n const flow: PendingAppAuthFlow = {\n appId: appId ?? account.appId,\n accountId,\n cardId,\n sequence: 0,\n requiredScopes: missingScopes,\n scopeNeedType,\n tokenType,\n cfg,\n ticket,\n };\n appAuthFlows.register(operationId, flow, dedup, activeCardKey);\n\n log.info(`app-scope card sent, operationId=${operationId}, scopes=[${missingScopes.join(', ')}]`);\n\n return json({\n awaiting_app_authorization: true,\n message:\n '\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002' +\n '\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n missing_scopes: missingScopes,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Card action handler (exported for monitor.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * \u5904\u7406 card.action.trigger \u56DE\u8C03\u4E8B\u4EF6\uFF08\u7531 monitor.ts \u8C03\u7528\uFF09\u3002\n *\n * \u5F53\u7528\u6237\u70B9\u51FB\u5E94\u7528\u6743\u9650\u5F15\u5BFC\u5361\u7247\u7684\"\u6211\u5DF2\u5B8C\u6210\uFF0C\u7EE7\u7EED\u6388\u6743\"\u6309\u94AE\u65F6\uFF1A\n * 1. \u66F4\u65B0\u5361\u7247\u4E3A\"\u5904\u7406\u4E2D\"\u72B6\u6001\n * 2. \u6E05\u9664\u5E94\u7528 scope \u7F13\u5B58\n * 3. \u53D1\u9001\u4E2D\u95F4\u5408\u6210\u6D88\u606F\u544A\u77E5 AI\n * 4. \u53D1\u8D77 OAuth Device Flow\n *\n * \u6CE8\u610F\uFF1A\u51FD\u6570\u4F53\u5185\u7684\u4E3B\u8981\u903B\u8F91\u901A\u8FC7 setImmediate + fire-and-forget \u5F02\u6B65\u6267\u884C\uFF0C\n * \u786E\u4FDD Feishu card.action.trigger \u56DE\u8C03\u5728 3 \u79D2\u5185\u8FD4\u56DE\u3002\n */\nexport async function handleCardAction(data: unknown, cfg: ClawdbotConfig, accountId: string): Promise<unknown> {\n let action: string | undefined;\n let operationId: string | undefined;\n let senderOpenId: string | undefined;\n\n try {\n const event = data as {\n operator?: { open_id?: string };\n action?: { value?: { action?: string; operation_id?: string } };\n };\n action = event.action?.value?.action;\n operationId = event.action?.value?.operation_id;\n senderOpenId = event.operator?.open_id;\n } catch {\n return;\n }\n\n if (action === 'project_auth_complete') {\n return handleProjectAuthCardAction(data, cfg, accountId);\n }\n\n if (action !== 'app_auth_done' || !operationId) return;\n\n const flow = appAuthFlows.getByOperationId(operationId);\n if (!flow) {\n log.warn(`card action ${operationId} not found (expired or already handled)`);\n return;\n }\n\n log.info(`app_auth_done clicked by ${senderOpenId}, operationId=${operationId}`);\n\n // scope \u6821\u9A8C\u5728\u540C\u6B65\u8DEF\u5F84\u5B8C\u6210\uFF083 \u79D2\u5185\u8FD4\u56DE toast response\uFF09\n invalidateAppScopeCache(flow.appId);\n\n const acct = getLarkAccount(flow.cfg, flow.accountId);\n if (!acct.configured) {\n log.warn(`account ${flow.accountId} not configured, skipping OAuth`);\n return;\n }\n\n const sdk = LarkClient.fromAccount(acct).sdk;\n let grantedScopes: string[] = [];\n try {\n // \u4F7F\u7528\u4E0E\u539F\u59CB AppScopeMissingError \u76F8\u540C\u7684 tokenType\uFF0C\u4FDD\u8BC1\u6821\u9A8C\u903B\u8F91\u5B8C\u5168\u4E00\u81F4\n grantedScopes = await getAppGrantedScopes(sdk, flow.appId, flow.tokenType);\n } catch (err) {\n log.warn(`failed to re-check app scopes: ${err}, proceeding anyway`);\n }\n\n // \u4F7F\u7528\u5171\u4EAB\u51FD\u6570 isAppScopeSatisfied\uFF0C\u4E0E tool-client invoke() \u903B\u8F91\u5B8C\u5168\u4E00\u81F4\uFF1A\n // - scopeNeedType \"all\" \u2192 \u5168\u90E8\u5FC5\u987B\u6709\n // - \u9ED8\u8BA4\"one\" \u2192 \u4EA4\u96C6\u975E\u7A7A\u5373\u53EF\n // - grantedScopes \u4E3A\u7A7A \u2192 \u89C6\u4E3A\u6EE1\u8DB3\uFF08API \u5931\u8D25\u9000\u56DE\u670D\u52A1\u7AEF\u5224\u65AD\uFF09\n if (!isAppScopeSatisfied(grantedScopes, flow.requiredScopes, flow.scopeNeedType)) {\n log.warn(`app scopes still missing after user confirmation: [${flow.requiredScopes.join(', ')}]`);\n return {\n toast: {\n type: 'error',\n content: '\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u7533\u8BF7\u5E76\u5BA1\u6838\u901A\u8FC7\u540E\u518D\u8BD5',\n },\n };\n }\n\n log.info(`app scopes verified, proceeding with OAuth`);\n\n // \u2605 \u5728 remove() \u4E4B\u524D\u5148\u53D6\u51FA\u5EF6\u8FDF\u961F\u5217\u6570\u636E\uFF0C\u907F\u514D remove() \u7684\u8054\u52A8\u6E05\u7406\u63D0\u524D\u5220\u6389\u5B83\n const deferKey = flow.ticket.senderOpenId\n ? `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`\n : undefined;\n const consumedDeferred = deferKey ? deferredUserAuth.get(deferKey) : undefined;\n if (consumedDeferred && deferKey) {\n deferredUserAuth.delete(deferKey);\n log.info(`consumed deferred user auth scopes: [${[...consumedDeferred.scopes].join(', ')}]`);\n }\n\n // \u6821\u9A8C\u901A\u8FC7\u624D\u5220\u9664\uFF0C\u9632\u6B62\u7528\u6237\u5728\u6743\u9650\u901A\u8FC7\u524D\u591A\u6B21\u70B9\u51FB\u65E0\u6CD5\u91CD\u8BD5\n appAuthFlows.remove(operationId);\n\n // \u901A\u8FC7\u56DE\u8C03\u8FD4\u56DE\u503C\u76F4\u63A5\u66F4\u65B0\u5361\u7247\uFF08\u65B9\u5F0F\u4E00\uFF1A3 \u79D2\u5185\u7ACB\u5373\u66F4\u65B0\uFF09\u3002\n // \u98DE\u4E66\u6587\u6863\u8981\u6C42 card \u5B57\u6BB5\u5FC5\u987B\u5305\u542B type + data \u5305\u88C5\uFF1A\n // { card: { type: \"raw\", data: { schema: \"2.0\", ... } } }\n // \u6CE8\u610F\uFF1A\u4E0D\u80FD\u5728\u56DE\u8C03\u8FD4\u56DE\u524D\u8C03\u7528 card.update API\uFF0C\u98DE\u4E66\u6587\u6863\u660E\u786E\u8BF4\u660E\n // \"\u5EF6\u65F6\u66F4\u65B0\u5FC5\u987B\u5728\u54CD\u5E94\u56DE\u8C03\u8BF7\u6C42\u4E4B\u540E\u6267\u884C\uFF0C\u5E76\u884C\u6267\u884C\u6216\u63D0\u524D\u6267\u884C\u4F1A\u51FA\u73B0\u66F4\u65B0\u5931\u8D25\"\u3002\n const successCard = buildAppAuthProgressCard();\n\n // \u540E\u53F0\u5F02\u6B65\uFF1A\u56DE\u8C03\u54CD\u5E94\u4E4B\u540E\u518D\u6267\u884C API \u66F4\u65B0 + OAuth\n setImmediate(async () => {\n try {\n // \u901A\u8FC7 API \u518D\u6B21\u66F4\u65B0\u5361\u7247\uFF08\u786E\u4FDD\u6240\u6709\u67E5\u770B\u8005\u90FD\u770B\u5230\u66F4\u65B0\uFF0C\u4E0D\u53EA\u662F\u70B9\u51FB\u8005\uFF09\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: flow.cardId,\n card: successCard,\n sequence: flow.sequence + 1,\n accountId,\n });\n } catch (err) {\n log.warn(`failed to update app-scope card to progress via API: ${err}`);\n }\n\n // \u53D1\u8D77 OAuth Device Flow\uFF08\u5B8C\u6210\u540E executeAuthorize \u4F1A\u81EA\u52A8\u53D1\u5408\u6210\u6D88\u606F\u89E6\u53D1 AI \u91CD\u8BD5\uFF09\n if (!flow.ticket.senderOpenId) {\n log.warn('no senderOpenId in ticket, skipping OAuth');\n return;\n }\n\n // \u6536\u96C6\u6240\u6709\u6765\u6E90\u7684 scope\uFF08\u8FC7\u6EE4 offline_access\uFF1A\u4EC5 app \u7EA7\u9700\u8981\uFF0Cdevice-flow \u81EA\u52A8\u8FFD\u52A0\uFF09\n const mergedScopes = new Set(flow.requiredScopes.filter((s) => s !== 'offline_access'));\n\n // \u6765\u6E90 1: \u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\uFF08\u5DF2\u5728\u540C\u6B65\u8DEF\u5F84\u4E2D\u63D0\u524D\u53D6\u51FA\uFF0C\u89C1 consumedDeferred\uFF09\n if (consumedDeferred) {\n for (const s of consumedDeferred.scopes) mergedScopes.add(s);\n }\n\n // \u6765\u6E90 2: \u73B0\u6709 user auth batch\uFF08\u5411\u540E\u517C\u5BB9\uFF0C\u5904\u7406\u672A\u88AB\u5EF6\u8FDF\u62E6\u622A\u7684 user auth\uFF09\n const userBatchKey = `user:${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;\n const userBatch = authBatches.get(userBatchKey);\n if (userBatch) {\n for (const s of userBatch.scopes) mergedScopes.add(s);\n log.info(`merged user batch scopes into app auth completion: [${[...mergedScopes].join(', ')}]`);\n }\n\n if (mergedScopes.size === 0) {\n // \u65E0\u4E1A\u52A1 scope \u9700\u8981\u7528\u6237\u6388\u6743\uFF08\u4F8B\u5982 offline_access \u662F\u552F\u4E00\u7F3A\u5931\u7684\u5E94\u7528\u6743\u9650\uFF0C\n // \u4E14\u6CA1\u6709\u5176\u4ED6\u5DE5\u5177\u4EA7\u751F\u7528\u6237\u6388\u6743\u9700\u6C42\uFF09\u3002\u8DF3\u8FC7 OAuth\uFF0C\u76F4\u63A5\u53D1\u5408\u6210\u6D88\u606F\u89E6\u53D1 AI \u91CD\u8BD5\uFF0C\n // \u91CD\u8BD5\u65F6\u5DE5\u5177\u4F1A\u81EA\u7136\u53D1\u73B0\u9700\u8981\u7528\u6237\u6388\u6743\u5E76\u53D1\u8D77\u6B63\u786E\u7684 OAuth \u6D41\u7A0B\u3002\n log.info('no business scopes to authorize after app auth, sending synthetic message for retry');\n const syntheticMsgId = `${flow.ticket.messageId}:app-auth-complete`;\n const syntheticEvent = {\n sender: { sender_id: { open_id: flow.ticket.senderOpenId } },\n message: {\n message_id: syntheticMsgId,\n chat_id: flow.ticket.chatId,\n chat_type: flow.ticket.chatType ?? ('p2p' as const),\n message_type: 'text',\n content: JSON.stringify({ text: '\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' }),\n thread_id: flow.ticket.threadId,\n },\n };\n const syntheticRuntime = {\n log: (msg: string) => log.info(msg),\n error: (msg: string) => log.error(msg),\n };\n const { promise } = enqueueFeishuChatTask({\n accountId: flow.accountId,\n chatId: flow.ticket.chatId,\n threadId: flow.ticket.threadId,\n task: async () => {\n await withTicket(\n {\n messageId: syntheticMsgId,\n chatId: flow.ticket.chatId,\n accountId: flow.accountId,\n startTime: Date.now(),\n senderOpenId: flow.ticket.senderOpenId!,\n chatType: flow.ticket.chatType,\n threadId: flow.ticket.threadId,\n },\n () =>\n handleFeishuMessage({\n cfg: flow.cfg,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n event: syntheticEvent as any,\n accountId: flow.accountId,\n forceMention: true,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n runtime: syntheticRuntime as any,\n replyToMessageId: flow.ticket.messageId,\n }),\n );\n },\n });\n await promise;\n log.info('synthetic message dispatched after app-auth-only completion');\n } else {\n await executeAuthorize({\n account: acct,\n senderOpenId: flow.ticket.senderOpenId,\n scope: [...mergedScopes].join(' '),\n showBatchAuthHint: true,\n forceAuth: true, // \u5E94\u7528\u6743\u9650\u521A\u7ECF\u5386\u79FB\u9664\u2192\u8865\u56DE\uFF0C\u4E0D\u4FE1\u4EFB\u672C\u5730 UAT \u7F13\u5B58\n cfg: flow.cfg,\n ticket: flow.ticket,\n });\n }\n } catch (err) {\n log.error(`handleCardAction background task failed: ${err}`);\n }\n });\n\n // \u56DE\u8C03\u8FD4\u56DE\u503C\uFF1A\u901A\u8FC7 card \u5B57\u6BB5\u7ACB\u5373\u66F4\u65B0\u5361\u7247 + toast \u63D0\u793A\n return {\n toast: {\n type: 'success' as const,\n content: '\u6743\u9650\u786E\u8BA4\u6210\u529F',\n },\n card: {\n type: 'raw' as const,\n data: successCard,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * \u7EDF\u4E00\u5904\u7406 `client.invoke()` \u629B\u51FA\u7684\u9519\u8BEF\uFF0C\u652F\u6301\u81EA\u52A8\u53D1\u8D77 OAuth \u6388\u6743\u3002\n *\n * \u66FF\u4EE3 `handleInvokeError`\uFF0C\u5728\u5DE5\u5177\u5C42\u76F4\u63A5\u5904\u7406\u6388\u6743\u95EE\u9898\uFF1A\n * - \u7528\u6237\u6388\u6743\u7C7B\u9519\u8BEF \u2192 \u76F4\u63A5 executeAuthorize\uFF08\u53D1 Device Flow \u5361\u7247\uFF09\n * - \u5E94\u7528\u6743\u9650\u7F3A\u5931 \u2192 \u53D1\u9001\u5F15\u5BFC\u5361\u7247\uFF0C\u7528\u6237\u786E\u8BA4\u540E\u81EA\u52A8\u63A5\u529B OAuth\n * - \u5176\u4ED6\u9519\u8BEF \u2192 \u56DE\u9000\u5230 handleInvokeError \u7684\u6807\u51C6\u5904\u7406\n *\n * @param err - invoke() \u6216\u5176\u4ED6\u903B\u8F91\u629B\u51FA\u7684\u9519\u8BEF\n * @param cfg - OpenClaw \u914D\u7F6E\u5BF9\u8C61\uFF08\u4ECE\u5DE5\u5177\u6CE8\u518C\u51FD\u6570\u7684\u95ED\u5305\u4E2D\u83B7\u53D6\uFF09\n */\nexport async function handleInvokeErrorWithAutoAuth(err: unknown, cfg: ClawdbotConfig) {\n const ticket = getTicket();\n\n // --- Path 0\uFF1AOwner \u8BBF\u95EE\u62D2\u7EDD \u2192 \u76F4\u63A5\u8FD4\u56DE\u53CB\u597D\u63D0\u793A ---\n if (err instanceof OwnerAccessDeniedError) {\n return json({\n error: 'permission_denied',\n message: '\u5F53\u524D\u5E94\u7528\u4EC5\u9650\u6240\u6709\u8005\uFF08App Owner\uFF09\u4F7F\u7528\u3002\u60A8\u6CA1\u6709\u6743\u9650\u4F7F\u7528\u76F8\u5173\u529F\u80FD\u3002',\n user_open_id: err.userOpenId,\n // \u6CE8\u610F\uFF1A\u4E0D\u5E8F\u5217\u5316 err.appOwnerId\uFF0C\u907F\u514D\u6CC4\u9732 owner \u7684 open_id\n });\n }\n\n if (ticket) {\n const senderOpenId = ticket.senderOpenId;\n\n // --- Path 1\uFF1A\u7528\u6237\u6388\u6743\u7C7B\u9519\u8BEF \u2192 \u9632\u6296\u5408\u5E76\u540E\u53D1\u8D77 OAuth ---\n\n if (senderOpenId) {\n // 1a. \u7528\u6237\u672A\u6388\u6743\u6216 token scope \u4E0D\u8DB3\uFF08\u4E14 app scope \u5DF2\u9A8C\u8BC1\uFF09\n if (err instanceof UserAuthRequiredError && err.appScopeVerified) {\n const scopes = err.requiredScopes;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5EF6\u8FDF\u68C0\u67E5\uFF1A\u5982\u679C\u540C\u4E00\u6D88\u606F\u6709\u672A\u5B8C\u6210\u7684\u5E94\u7528\u6743\u9650\u6D41\u7A0B\uFF0C\n // \u5C06\u7528\u6237\u6388\u6743 scope \u6536\u96C6\u5230\u5EF6\u8FDF\u961F\u5217\uFF0C\u7B49\u5E94\u7528\u6388\u6743\u5B8C\u6210\u540E\u7EDF\u4E00\u53D1\u8D77 OAuth\n if (hasActiveAppAuthForMessage(ticket)) {\n addToDeferredUserAuth(ticket, scopes, acct, cfg);\n log.info(`UserAuthRequiredError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);\n return json({\n awaiting_app_authorization: true,\n user_auth_deferred: true,\n message:\n '\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002' +\n '\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n deferred_scopes: scopes,\n });\n }\n\n const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;\n log.info(`UserAuthRequiredError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);\n return await enqueueAuthRequest(\n bufferKey,\n scopes,\n { account: acct, cfg, ticket },\n async (mergedScopes) => {\n // \u7B49\u5F85\u540C\u4E00\u6D88\u606F\u7684 app auth \u5361\u7247\u5148\u53D1\u51FA\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry?.resultPromise) {\n await appEntry.resultPromise.catch(() => {});\n }\n return executeAuthorize({\n account: acct,\n senderOpenId,\n scope: mergedScopes.join(' '),\n showBatchAuthHint: true,\n cfg,\n ticket,\n });\n },\n AUTH_USER_DEBOUNCE_MS,\n );\n }\n } catch (autoAuthErr) {\n log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);\n }\n }\n\n // 1b. \u7528\u6237 token \u5B58\u5728\u4F46 scope \u4E0D\u8DB3\uFF08\u670D\u52A1\u7AEF LARK_ERROR.USER_SCOPE_INSUFFICIENT / 99991679\uFF09\n if (err instanceof UserScopeInsufficientError) {\n const scopes = err.missingScopes;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5EF6\u8FDF\u68C0\u67E5\uFF1A\u540C Path 1a\n if (hasActiveAppAuthForMessage(ticket)) {\n addToDeferredUserAuth(ticket, scopes, acct, cfg);\n log.info(`UserScopeInsufficientError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);\n return json({\n awaiting_app_authorization: true,\n user_auth_deferred: true,\n message:\n '\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002' +\n '\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002' +\n '\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002',\n deferred_scopes: scopes,\n });\n }\n\n const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;\n log.info(`UserScopeInsufficientError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);\n return await enqueueAuthRequest(\n bufferKey,\n scopes,\n { account: acct, cfg, ticket },\n async (mergedScopes) => {\n // \u7B49\u5F85\u540C\u4E00\u6D88\u606F\u7684 app auth \u5361\u7247\u5148\u53D1\u51FA\n const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n const appEntry = authBatches.get(appKey);\n if (appEntry?.resultPromise) {\n await appEntry.resultPromise.catch(() => {});\n }\n return executeAuthorize({\n account: acct,\n senderOpenId,\n scope: mergedScopes.join(' '),\n showBatchAuthHint: true,\n cfg,\n ticket,\n });\n },\n AUTH_USER_DEBOUNCE_MS,\n );\n }\n } catch (autoAuthErr) {\n log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);\n }\n }\n } else {\n log.error(`senderOpenId not found ${err}`);\n }\n\n // --- Path 2\uFF1A\u5E94\u7528\u6743\u9650\u7F3A\u5931 \u2192 \u9632\u6296\u5408\u5E76\u540E\u53D1\u9001\u5F15\u5BFC\u5361\u7247 ---\n\n if (err instanceof AppScopeMissingError && ticket.chatId) {\n // \u6355\u83B7\u5F53\u524D\u9519\u8BEF\u7684\u9644\u52A0\u4FE1\u606F\uFF0C\u4F9B flushFn \u4F7F\u7528\n const appScopeErr = err;\n try {\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (acct.configured) {\n // \u2605 \u5C06\u5DE5\u5177\u7684\u5168\u90E8\u6240\u9700 scope \u52A0\u5165\u5EF6\u8FDF\u7528\u6237\u6388\u6743\u961F\u5217\u3002\n // \u5E94\u7528\u6743\u9650\u5B8C\u6210\u540E handleCardAction \u4F1A\u6D88\u8D39\u8FD9\u4E9B scope\uFF0C\n // \u4E0E flow.requiredScopes\uFF08\u4EC5 app \u7F3A\u5931\u7684\uFF09\u5408\u5E76\uFF0C\u4E00\u6B21\u6027\u53D1\u8D77 OAuth\u3002\n if (senderOpenId && appScopeErr.allRequiredScopes?.length) {\n addToDeferredUserAuth(ticket, appScopeErr.allRequiredScopes, acct, cfg);\n log.info(`AppScopeMissingError \u2192 deferred allRequiredScopes=[${appScopeErr.allRequiredScopes.join(', ')}]`);\n }\n\n const bufferKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;\n log.info(\n `AppScopeMissingError \u2192 enqueue, key=${bufferKey}, ` + `scopes=[${appScopeErr.missingScopes.join(', ')}]`,\n );\n return await enqueueAuthRequest(\n bufferKey,\n appScopeErr.missingScopes,\n { account: acct, cfg, ticket },\n (mergedScopes) =>\n sendAppScopeCard({\n account: acct,\n missingScopes: mergedScopes,\n appId: appScopeErr.appId,\n scopeNeedType: 'all', // \u5408\u5E76\u540E\u6240\u6709 scope \u90FD\u9700\u8981\n tokenType: appScopeErr.tokenType,\n cfg,\n ticket,\n }),\n );\n }\n } catch (cardErr) {\n log.warn(`sendAppScopeCard failed: ${cardErr}, falling back`);\n }\n }\n } else {\n log.error(`ticket not found ${err}`);\n }\n return json({\n error: formatLarkError(err),\n });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["card"]
7
7
  }
@@ -146,6 +146,98 @@ function toInAppWebUrl(targetUrl) {
146
146
  );
147
147
  return `https://applink.feishu.cn/client/web_url/open?mode=sidebar-semi&max_width=800&reload=false&url=${encoded}&lk_meta=${lkMeta}`;
148
148
  }
149
+ function buildProjectAuthCard(params) {
150
+ const { authorizationUrl, operationId, expiresMin } = params;
151
+ const multiUrl = {
152
+ url: authorizationUrl,
153
+ pc_url: authorizationUrl,
154
+ android_url: authorizationUrl,
155
+ ios_url: authorizationUrl
156
+ };
157
+ return {
158
+ schema: "2.0",
159
+ config: { wide_screen_mode: false },
160
+ header: {
161
+ title: { tag: "plain_text", content: "\u98DE\u4E66\u9879\u76EE\u9700\u8981\u60A8\u7684\u6388\u6743" },
162
+ subtitle: { tag: "plain_text", content: "" },
163
+ template: "blue",
164
+ padding: "12px 12px 12px 12px",
165
+ icon: { tag: "standard_icon", token: "lock-chat_filled" }
166
+ },
167
+ body: {
168
+ elements: [
169
+ {
170
+ tag: "markdown",
171
+ content: "\u6388\u6743\u540E\uFF0C\u5E94\u7528\u5C06\u80FD\u591F\u4EE5\u60A8\u7684\u8EAB\u4EFD\u6267\u884C\u98DE\u4E66\u9879\u76EE\u76F8\u5173\u64CD\u4F5C\u3002",
172
+ text_size: "normal"
173
+ },
174
+ // Step 1: navigate to auth page
175
+ {
176
+ tag: "column_set",
177
+ flex_mode: "none",
178
+ horizontal_spacing: "default",
179
+ columns: [
180
+ {
181
+ tag: "column",
182
+ width: "weighted",
183
+ weight: 3,
184
+ vertical_align: "center",
185
+ elements: [{ tag: "markdown", content: "**\u7B2C\u4E00\u6B65\uFF1A\u70B9\u51FB\u6309\u94AE\uFF0C\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743**" }]
186
+ },
187
+ {
188
+ tag: "column",
189
+ width: "weighted",
190
+ weight: 1,
191
+ vertical_align: "center",
192
+ elements: [{
193
+ tag: "button",
194
+ text: { tag: "plain_text", content: "\u524D\u5F80\u6388\u6743" },
195
+ type: "primary",
196
+ size: "medium",
197
+ multi_url: multiUrl
198
+ }]
199
+ }
200
+ ]
201
+ },
202
+ { tag: "hr" },
203
+ // Step 2: paste callback URL
204
+ {
205
+ tag: "markdown",
206
+ content: "**\u7B2C\u4E8C\u6B65\uFF1A\u7C98\u8D34\u6D4F\u89C8\u5668\u5730\u5740\u680F URL**\n<font color='grey'>\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u6253\u5F00\u7684\u9875\u9762\uFF0C\u8FD9\u662F\u6B63\u5E38\u7684\u2014\u2014\u8BF7\u590D\u5236\u5730\u5740\u680F\u4E2D\u7684\u5B8C\u6574 URL \u7C98\u8D34\u5230\u4E0B\u65B9</font>",
207
+ text_size: "normal"
208
+ },
209
+ {
210
+ tag: "form",
211
+ name: `project_auth_form`,
212
+ elements: [
213
+ {
214
+ tag: "input",
215
+ name: "callback_url",
216
+ placeholder: {
217
+ tag: "plain_text",
218
+ content: "\u7C98\u8D34 URL\uFF0C\u5982 http://127.0.0.1:3456/callback?code=..."
219
+ },
220
+ max_length: 2e3
221
+ },
222
+ {
223
+ tag: "button",
224
+ text: { tag: "plain_text", content: "\u5B8C\u6210\u6388\u6743" },
225
+ type: "primary",
226
+ form_action_type: "submit",
227
+ name: "submit_project_auth",
228
+ value: { action: "project_auth_complete", operation_id: operationId }
229
+ }
230
+ ]
231
+ },
232
+ {
233
+ tag: "markdown",
234
+ content: `<font color='grey'>\u6388\u6743\u94FE\u63A5\u5C06\u5728 ${expiresMin} \u5206\u949F\u540E\u5931\u6548\uFF0C\u5C4A\u65F6\u9700\u91CD\u65B0\u53D1\u8D77</font>`,
235
+ text_size: "notation"
236
+ }
237
+ ]
238
+ }
239
+ };
240
+ }
149
241
  function buildAuthSuccessCard() {
150
242
  return {
151
243
  schema: "2.0",
@@ -263,6 +355,7 @@ export {
263
355
  buildAuthFailedCard,
264
356
  buildAuthIdentityMismatchCard,
265
357
  buildAuthSuccessCard,
358
+ buildProjectAuthCard,
266
359
  formatScopeDescription,
267
360
  toInAppWebUrl
268
361
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/oauth-cards.ts"],
4
- "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * oauth-cards.ts \u2014 OAuth \u6388\u6743\u5361\u7247\u6784\u5EFA\u51FD\u6570\u3002\n *\n * \u4ECE oauth.ts \u63D0\u53D6\u7684\u7EAF UI \u51FD\u6570\uFF0C\u4E0E OAuth \u4E1A\u52A1\u6D41\u7A0B\u89E3\u8026\u3002\n */\n\n// ---------------------------------------------------------------------------\n// Card builders\n// ---------------------------------------------------------------------------\n\nexport function buildAuthCard(params: {\n verificationUriComplete: string;\n expiresMin: number;\n scope?: string;\n isBatchAuth?: boolean;\n totalAppScopes?: number;\n alreadyGranted?: number;\n batchInfo?: string;\n filteredScopes?: string[]; // \u88AB\u8FC7\u6EE4\u7684 scope\uFF08\u5E94\u7528\u672A\u5F00\u901A\uFF09\n appId?: string; // \u7528\u4E8E\u751F\u6210\u6743\u9650\u7BA1\u7406\u94FE\u63A5\n showBatchAuthHint?: boolean; // \u4EC5 auto-auth \u6D41\u7A0B\u5C55\u793A\u6279\u91CF\u6388\u6743\u63D0\u793A\n}): Record<string, unknown> {\n const {\n verificationUriComplete,\n expiresMin,\n scope,\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n filteredScopes,\n appId,\n showBatchAuthHint,\n } = params;\n const inAppUrl = toInAppWebUrl(verificationUriComplete);\n const multiUrl = {\n url: inAppUrl,\n pc_url: inAppUrl,\n android_url: inAppUrl,\n ios_url: inAppUrl,\n };\n\n // \u5C06 scope \u8F6C\u6210\u53EF\u8BFB\u8BF4\u660E\n const scopeDesc = formatScopeDescription(\n scope,\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n filteredScopes,\n appId,\n );\n\n const elements: Record<string, unknown>[] = [\n // \u6388\u6743\u8BF4\u660E\n {\n tag: 'markdown',\n content: scopeDesc,\n text_size: 'normal',\n },\n // \u6388\u6743\u6309\u94AE\uFF08small\uFF0C\u9760\u53F3\uFF09\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_align: 'right',\n columns: [\n {\n tag: 'column',\n width: 'auto',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u524D\u5F80\u6388\u6743' },\n type: 'primary',\n size: 'medium',\n multi_url: multiUrl,\n },\n ],\n },\n ],\n },\n // \u5931\u6548\u65F6\u95F4\u63D0\u9192\n {\n tag: 'markdown',\n content: `<font color='grey'>\u6388\u6743\u94FE\u63A5\u5C06\u5728 ${expiresMin} \u5206\u949F\u540E\u5931\u6548\uFF0C\u5C4A\u65F6\u9700\u91CD\u65B0\u53D1\u8D77</font>`,\n text_size: 'notation',\n },\n // \u6279\u91CF\u6388\u6743\u63D0\u793A\uFF08\u4EC5 auto-auth \u6D41\u7A0B\u5C55\u793A\uFF09\n ...(showBatchAuthHint\n ? [\n {\n tag: 'markdown',\n content:\n \"<font color='grey'>\uD83D\uDCA1\u5982\u679C\u4F60\u5E0C\u671B\u4E00\u6B21\u6027\u6388\u4E88\u6240\u6709\u63D2\u4EF6\u6240\u9700\u8981\u7684\u6743\u9650\uFF0C\u53EF\u4EE5\u544A\u8BC9\u6211\u300C\u6388\u4E88\u6240\u6709\u7528\u6237\u6743\u9650\u300D\uFF0C\u6211\u4F1A\u534F\u52A9\u4F60\u5B8C\u6210\u3002</font>\",\n text_size: 'notation',\n },\n ]\n : []),\n ];\n\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-yellow-bg': {\n light_mode: 'rgba(255, 214, 102, 0.12)',\n dark_mode: 'rgba(255, 214, 102, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u9700\u8981\u60A8\u7684\u6388\u6743\u624D\u80FD\u7EE7\u7EED',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'blue',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'lock-chat_filled',\n },\n },\n body: { elements },\n };\n}\n\n/** scope \u5B57\u7B26\u4E32 \u2192 \u53EF\u8BFB\u63CF\u8FF0 */\nexport function formatScopeDescription(\n scope?: string,\n isBatchAuth?: boolean,\n totalAppScopes?: number,\n alreadyGranted?: number,\n batchInfo?: string,\n _filteredScopes?: string[],\n _appId?: string,\n): string {\n const scopes = scope?.split(/\\s+/).filter(Boolean);\n\n if (isBatchAuth && scopes && scopes.length > 0) {\n let message = `\u5E94\u7528\u9700\u8981\u6388\u6743 **${scopes.length}** \u4E2A\u7528\u6237\u6743\u9650\uFF08\u5171 ${totalAppScopes} \u4E2A\uFF0C\u5DF2\u6388\u6743 ${alreadyGranted} \u4E2A\uFF09\u3002`;\n\n // \u5982\u679C\u8D85\u8FC7 5 \u4E2A scope\uFF0C\u53EA\u663E\u793A\u524D 3 \u4E2A\uFF0C\u7136\u540E\u7528\"...\"\u8868\u793A\n if (scopes.length > 5) {\n const previewScopes = scopes.slice(0, 3).join('\\n');\n message += `\\n\\n**\u5C06\u8981\u6388\u6743\u7684\u6743\u9650**\uFF1A\\n${previewScopes}\\n...\\n`;\n } else {\n const scopeList = scopes.map((s, idx) => `${idx + 1}. ${s}`).join('\\n');\n message += `\\n\\n**\u5C06\u8981\u6388\u6743\u7684\u6743\u9650\u5217\u8868**\uFF1A\\n${scopeList}\\n`;\n }\n\n // \u6DFB\u52A0\u5206\u6279\u63D0\u793A\u4FE1\u606F\n if (batchInfo) {\n message += `\\n\\n${batchInfo}`;\n }\n\n return message;\n }\n\n const desc = '\u6388\u6743\u540E\uFF0C\u5E94\u7528\u5C06\u80FD\u591F\u4EE5\u60A8\u7684\u8EAB\u4EFD\u6267\u884C\u76F8\u5173\u64CD\u4F5C\u3002';\n if (!scopes?.length) return desc;\n\n const message = desc + '\\n\\n\u6240\u9700\u6743\u9650\uFF1A\\n' + scopes.map((s) => `- ${s}`).join('\\n');\n\n return message;\n}\n\nexport function toInAppWebUrl(targetUrl: string): string {\n const encoded = encodeURIComponent(targetUrl);\n const lkMeta = encodeURIComponent(\n JSON.stringify({\n 'page-meta': {\n showNavBar: 'false',\n showBottomNavBar: 'false',\n },\n }),\n );\n return (\n 'https://applink.feishu.cn/client/web_url/open' +\n `?mode=sidebar-semi&max_width=800&reload=false&url=${encoded}&lk_meta=${lkMeta}`\n );\n}\n\nexport function buildAuthSuccessCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-green-bg': {\n light_mode: 'rgba(52, 199, 89, 0.12)',\n dark_mode: 'rgba(52, 199, 89, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u6210\u529F',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'green',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'yes_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content:\n '\u60A8\u7684\u98DE\u4E66\u8D26\u53F7\u5DF2\u6210\u529F\u6388\u6743\uFF0C\u6B63\u5728\u4E3A\u60A8\u7EE7\u7EED\u6267\u884C\u64CD\u4F5C\u3002\\n\\n' +\n \"<font color='grey'>\u5982\u9700\u64A4\u9500\u6388\u6743\uFF0C\u53EF\u968F\u65F6\u544A\u8BC9\u6211\u3002</font>\",\n },\n ],\n },\n };\n}\n\nexport function buildAuthFailedCard(_reason: string): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-grey-bg': {\n light_mode: 'rgba(142, 142, 147, 0.12)',\n dark_mode: 'rgba(142, 142, 147, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u672A\u5B8C\u6210',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'yellow',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'warning_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u3002',\n },\n ],\n },\n };\n}\n\nexport function buildAuthIdentityMismatchCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u5931\u8D25\uFF0C\u64CD\u4F5C\u8D26\u53F7\u4E0E\u53D1\u8D77\u8D26\u53F7\u4E0D\u4E00\u81F4',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'red',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'close_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content:\n '\u68C0\u6D4B\u5230\u5F53\u524D\u8FDB\u884C\u6388\u6743\u64CD\u4F5C\u7684\u98DE\u4E66\u8D26\u53F7\u4E0E\u53D1\u8D77\u6388\u6743\u8BF7\u6C42\u7684\u8D26\u53F7\u4E0D\u4E00\u81F4\u3002\u4E3A\u4FDD\u969C\u6570\u636E\u5B89\u5168\uFF0C\u672C\u6B21\u6388\u6743\u5DF2\u88AB\u62D2\u7EDD\u3002\\n\\n' +\n \"<font color='grey'>\u8BF7\u6388\u6743\u8BF7\u6C42\u7684\u53D1\u8D77\u4EBA\u4F7F\u7528\u5176\u8D26\u53F7\uFF0C\u70B9\u51FB\u6388\u6743\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002</font>\",\n },\n ],\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * oauth-cards.ts \u2014 OAuth \u6388\u6743\u5361\u7247\u6784\u5EFA\u51FD\u6570\u3002\n *\n * \u4ECE oauth.ts \u63D0\u53D6\u7684\u7EAF UI \u51FD\u6570\uFF0C\u4E0E OAuth \u4E1A\u52A1\u6D41\u7A0B\u89E3\u8026\u3002\n */\n\n// ---------------------------------------------------------------------------\n// Card builders\n// ---------------------------------------------------------------------------\n\nexport function buildAuthCard(params: {\n verificationUriComplete: string;\n expiresMin: number;\n scope?: string;\n isBatchAuth?: boolean;\n totalAppScopes?: number;\n alreadyGranted?: number;\n batchInfo?: string;\n filteredScopes?: string[]; // \u88AB\u8FC7\u6EE4\u7684 scope\uFF08\u5E94\u7528\u672A\u5F00\u901A\uFF09\n appId?: string; // \u7528\u4E8E\u751F\u6210\u6743\u9650\u7BA1\u7406\u94FE\u63A5\n showBatchAuthHint?: boolean; // \u4EC5 auto-auth \u6D41\u7A0B\u5C55\u793A\u6279\u91CF\u6388\u6743\u63D0\u793A\n}): Record<string, unknown> {\n const {\n verificationUriComplete,\n expiresMin,\n scope,\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n filteredScopes,\n appId,\n showBatchAuthHint,\n } = params;\n const inAppUrl = toInAppWebUrl(verificationUriComplete);\n const multiUrl = {\n url: inAppUrl,\n pc_url: inAppUrl,\n android_url: inAppUrl,\n ios_url: inAppUrl,\n };\n\n // \u5C06 scope \u8F6C\u6210\u53EF\u8BFB\u8BF4\u660E\n const scopeDesc = formatScopeDescription(\n scope,\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n filteredScopes,\n appId,\n );\n\n const elements: Record<string, unknown>[] = [\n // \u6388\u6743\u8BF4\u660E\n {\n tag: 'markdown',\n content: scopeDesc,\n text_size: 'normal',\n },\n // \u6388\u6743\u6309\u94AE\uFF08small\uFF0C\u9760\u53F3\uFF09\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_align: 'right',\n columns: [\n {\n tag: 'column',\n width: 'auto',\n elements: [\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u524D\u5F80\u6388\u6743' },\n type: 'primary',\n size: 'medium',\n multi_url: multiUrl,\n },\n ],\n },\n ],\n },\n // \u5931\u6548\u65F6\u95F4\u63D0\u9192\n {\n tag: 'markdown',\n content: `<font color='grey'>\u6388\u6743\u94FE\u63A5\u5C06\u5728 ${expiresMin} \u5206\u949F\u540E\u5931\u6548\uFF0C\u5C4A\u65F6\u9700\u91CD\u65B0\u53D1\u8D77</font>`,\n text_size: 'notation',\n },\n // \u6279\u91CF\u6388\u6743\u63D0\u793A\uFF08\u4EC5 auto-auth \u6D41\u7A0B\u5C55\u793A\uFF09\n ...(showBatchAuthHint\n ? [\n {\n tag: 'markdown',\n content:\n \"<font color='grey'>\uD83D\uDCA1\u5982\u679C\u4F60\u5E0C\u671B\u4E00\u6B21\u6027\u6388\u4E88\u6240\u6709\u63D2\u4EF6\u6240\u9700\u8981\u7684\u6743\u9650\uFF0C\u53EF\u4EE5\u544A\u8BC9\u6211\u300C\u6388\u4E88\u6240\u6709\u7528\u6237\u6743\u9650\u300D\uFF0C\u6211\u4F1A\u534F\u52A9\u4F60\u5B8C\u6210\u3002</font>\",\n text_size: 'notation',\n },\n ]\n : []),\n ];\n\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-yellow-bg': {\n light_mode: 'rgba(255, 214, 102, 0.12)',\n dark_mode: 'rgba(255, 214, 102, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u9700\u8981\u60A8\u7684\u6388\u6743\u624D\u80FD\u7EE7\u7EED',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'blue',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'lock-chat_filled',\n },\n },\n body: { elements },\n };\n}\n\n/** scope \u5B57\u7B26\u4E32 \u2192 \u53EF\u8BFB\u63CF\u8FF0 */\nexport function formatScopeDescription(\n scope?: string,\n isBatchAuth?: boolean,\n totalAppScopes?: number,\n alreadyGranted?: number,\n batchInfo?: string,\n _filteredScopes?: string[],\n _appId?: string,\n): string {\n const scopes = scope?.split(/\\s+/).filter(Boolean);\n\n if (isBatchAuth && scopes && scopes.length > 0) {\n let message = `\u5E94\u7528\u9700\u8981\u6388\u6743 **${scopes.length}** \u4E2A\u7528\u6237\u6743\u9650\uFF08\u5171 ${totalAppScopes} \u4E2A\uFF0C\u5DF2\u6388\u6743 ${alreadyGranted} \u4E2A\uFF09\u3002`;\n\n // \u5982\u679C\u8D85\u8FC7 5 \u4E2A scope\uFF0C\u53EA\u663E\u793A\u524D 3 \u4E2A\uFF0C\u7136\u540E\u7528\"...\"\u8868\u793A\n if (scopes.length > 5) {\n const previewScopes = scopes.slice(0, 3).join('\\n');\n message += `\\n\\n**\u5C06\u8981\u6388\u6743\u7684\u6743\u9650**\uFF1A\\n${previewScopes}\\n...\\n`;\n } else {\n const scopeList = scopes.map((s, idx) => `${idx + 1}. ${s}`).join('\\n');\n message += `\\n\\n**\u5C06\u8981\u6388\u6743\u7684\u6743\u9650\u5217\u8868**\uFF1A\\n${scopeList}\\n`;\n }\n\n // \u6DFB\u52A0\u5206\u6279\u63D0\u793A\u4FE1\u606F\n if (batchInfo) {\n message += `\\n\\n${batchInfo}`;\n }\n\n return message;\n }\n\n const desc = '\u6388\u6743\u540E\uFF0C\u5E94\u7528\u5C06\u80FD\u591F\u4EE5\u60A8\u7684\u8EAB\u4EFD\u6267\u884C\u76F8\u5173\u64CD\u4F5C\u3002';\n if (!scopes?.length) return desc;\n\n const message = desc + '\\n\\n\u6240\u9700\u6743\u9650\uFF1A\\n' + scopes.map((s) => `- ${s}`).join('\\n');\n\n return message;\n}\n\nexport function toInAppWebUrl(targetUrl: string): string {\n const encoded = encodeURIComponent(targetUrl);\n const lkMeta = encodeURIComponent(\n JSON.stringify({\n 'page-meta': {\n showNavBar: 'false',\n showBottomNavBar: 'false',\n },\n }),\n );\n return (\n 'https://applink.feishu.cn/client/web_url/open' +\n `?mode=sidebar-semi&max_width=800&reload=false&url=${encoded}&lk_meta=${lkMeta}`\n );\n}\n\n/**\n * \u98DE\u4E66\u9879\u76EE OAuth \u4E13\u7528\u6388\u6743\u5361\u7247\u3002\n *\n * \u4E24\u6B65\u5F0F\uFF1A\u2460 \u524D\u5F80\u6388\u6743\u6309\u94AE \u2461 URL \u7C98\u8D34\u8F93\u5165\u6846 + \u63D0\u4EA4\u6309\u94AE\uFF08\u901A\u8FC7\u5361\u7247\u56DE\u8C03\u81EA\u52A8\u5B8C\u6210\uFF09\n */\nexport function buildProjectAuthCard(params: {\n authorizationUrl: string;\n operationId: string;\n expiresMin: number;\n}): Record<string, unknown> {\n const { authorizationUrl, operationId, expiresMin } = params;\n // \u98DE\u4E66\u9879\u76EE OAuth \u7684\u56DE\u8C03\u5730\u5740\u662F 127.0.0.1\uFF0C\u5728\u98DE\u4E66\u5E94\u7528\u5185\u6D4F\u89C8\u5668\u4E2D\u4E0D\u53EF\u8FBE\uFF0C\n // \u56E0\u6B64\u4E0D\u4F7F\u7528 toInAppWebUrl \u5305\u88C5\uFF08\u907F\u514D URL \u4E8C\u6B21\u7F16\u7801\u81A8\u80C0\u5BFC\u81F4\u5361\u7247\u5185\u5BB9\u8D85\u9650\uFF09\u3002\n const multiUrl = {\n url: authorizationUrl,\n pc_url: authorizationUrl,\n android_url: authorizationUrl,\n ios_url: authorizationUrl,\n };\n\n return {\n schema: '2.0',\n config: { wide_screen_mode: false },\n header: {\n title: { tag: 'plain_text', content: '\u98DE\u4E66\u9879\u76EE\u9700\u8981\u60A8\u7684\u6388\u6743' },\n subtitle: { tag: 'plain_text', content: '' },\n template: 'blue',\n padding: '12px 12px 12px 12px',\n icon: { tag: 'standard_icon', token: 'lock-chat_filled' },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u6388\u6743\u540E\uFF0C\u5E94\u7528\u5C06\u80FD\u591F\u4EE5\u60A8\u7684\u8EAB\u4EFD\u6267\u884C\u98DE\u4E66\u9879\u76EE\u76F8\u5173\u64CD\u4F5C\u3002',\n text_size: 'normal',\n },\n // Step 1: navigate to auth page\n {\n tag: 'column_set',\n flex_mode: 'none',\n horizontal_spacing: 'default',\n columns: [\n {\n tag: 'column',\n width: 'weighted',\n weight: 3,\n vertical_align: 'center',\n elements: [{ tag: 'markdown', content: '**\u7B2C\u4E00\u6B65\uFF1A\u70B9\u51FB\u6309\u94AE\uFF0C\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743**' }],\n },\n {\n tag: 'column',\n width: 'weighted',\n weight: 1,\n vertical_align: 'center',\n elements: [{\n tag: 'button',\n text: { tag: 'plain_text', content: '\u524D\u5F80\u6388\u6743' },\n type: 'primary',\n size: 'medium',\n multi_url: multiUrl,\n }],\n },\n ],\n },\n { tag: 'hr' },\n // Step 2: paste callback URL\n {\n tag: 'markdown',\n content:\n '**\u7B2C\u4E8C\u6B65\uFF1A\u7C98\u8D34\u6D4F\u89C8\u5668\u5730\u5740\u680F URL**\\n' +\n \"<font color='grey'>\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u6253\u5F00\u7684\u9875\u9762\uFF0C\u8FD9\u662F\u6B63\u5E38\u7684\u2014\u2014\u8BF7\u590D\u5236\u5730\u5740\u680F\u4E2D\u7684\u5B8C\u6574 URL \u7C98\u8D34\u5230\u4E0B\u65B9</font>\",\n text_size: 'normal',\n },\n {\n tag: 'form',\n name: `project_auth_form`,\n elements: [\n {\n tag: 'input',\n name: 'callback_url',\n placeholder: {\n tag: 'plain_text',\n content: '\u7C98\u8D34 URL\uFF0C\u5982 http://127.0.0.1:3456/callback?code=...',\n },\n max_length: 2000,\n },\n {\n tag: 'button',\n text: { tag: 'plain_text', content: '\u5B8C\u6210\u6388\u6743' },\n type: 'primary',\n form_action_type: 'submit',\n name: 'submit_project_auth',\n value: { action: 'project_auth_complete', operation_id: operationId },\n },\n ],\n },\n {\n tag: 'markdown',\n content: `<font color='grey'>\u6388\u6743\u94FE\u63A5\u5C06\u5728 ${expiresMin} \u5206\u949F\u540E\u5931\u6548\uFF0C\u5C4A\u65F6\u9700\u91CD\u65B0\u53D1\u8D77</font>`,\n text_size: 'notation',\n },\n ],\n },\n };\n}\n\nexport function buildAuthSuccessCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-green-bg': {\n light_mode: 'rgba(52, 199, 89, 0.12)',\n dark_mode: 'rgba(52, 199, 89, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u6210\u529F',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'green',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'yes_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content:\n '\u60A8\u7684\u98DE\u4E66\u8D26\u53F7\u5DF2\u6210\u529F\u6388\u6743\uFF0C\u6B63\u5728\u4E3A\u60A8\u7EE7\u7EED\u6267\u884C\u64CD\u4F5C\u3002\\n\\n' +\n \"<font color='grey'>\u5982\u9700\u64A4\u9500\u6388\u6743\uFF0C\u53EF\u968F\u65F6\u544A\u8BC9\u6211\u3002</font>\",\n },\n ],\n },\n };\n}\n\nexport function buildAuthFailedCard(_reason: string): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n style: {\n color: {\n 'light-grey-bg': {\n light_mode: 'rgba(142, 142, 147, 0.12)',\n dark_mode: 'rgba(142, 142, 147, 0.08)',\n },\n },\n },\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u672A\u5B8C\u6210',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'yellow',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'warning_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u3002',\n },\n ],\n },\n };\n}\n\nexport function buildAuthIdentityMismatchCard(): Record<string, unknown> {\n return {\n schema: '2.0',\n config: {\n wide_screen_mode: false,\n },\n header: {\n title: {\n tag: 'plain_text',\n content: '\u6388\u6743\u5931\u8D25\uFF0C\u64CD\u4F5C\u8D26\u53F7\u4E0E\u53D1\u8D77\u8D26\u53F7\u4E0D\u4E00\u81F4',\n },\n subtitle: {\n tag: 'plain_text',\n content: '',\n },\n template: 'red',\n padding: '12px 12px 12px 12px',\n icon: {\n tag: 'standard_icon',\n token: 'close_filled',\n },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content:\n '\u68C0\u6D4B\u5230\u5F53\u524D\u8FDB\u884C\u6388\u6743\u64CD\u4F5C\u7684\u98DE\u4E66\u8D26\u53F7\u4E0E\u53D1\u8D77\u6388\u6743\u8BF7\u6C42\u7684\u8D26\u53F7\u4E0D\u4E00\u81F4\u3002\u4E3A\u4FDD\u969C\u6570\u636E\u5B89\u5168\uFF0C\u672C\u6B21\u6388\u6743\u5DF2\u88AB\u62D2\u7EDD\u3002\\n\\n' +\n \"<font color='grey'>\u8BF7\u6388\u6743\u8BF7\u6C42\u7684\u53D1\u8D77\u4EBA\u4F7F\u7528\u5176\u8D26\u53F7\uFF0C\u70B9\u51FB\u6388\u6743\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002</font>\",\n },\n ],\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["message"]
7
7
  }
@@ -16,8 +16,11 @@ import {
16
16
  } from "../core/project-token-store.js";
17
17
  import { getProjectMcpEndpoint } from "../tools/mcp/project/endpoint.js";
18
18
  import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit.js";
19
- import { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from "./oauth-cards.js";
19
+ import { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard, buildProjectAuthCard } from "./oauth-cards.js";
20
20
  import { json } from "./oapi/helpers.js";
21
+ import { handleFeishuMessage } from "../messaging/inbound/handler.js";
22
+ import { enqueueFeishuChatTask } from "../channel/chat-queue.js";
23
+ import { withTicket } from "../core/lark-ticket.js";
21
24
  const log = larkLogger("tools/project-oauth");
22
25
  const FeishuProjectOAuthSchema = Type.Object(
23
26
  {
@@ -44,6 +47,8 @@ const FeishuProjectOAuthSchema = Type.Object(
44
47
  );
45
48
  const pendingLocalFlows = /* @__PURE__ */ new Map();
46
49
  const pendingRemoteSessions = /* @__PURE__ */ new Map();
50
+ const operationIdIndex = /* @__PURE__ */ new Map();
51
+ const PENDING_SESSION_TTL_MS = 10 * 60 * 1e3;
47
52
  function fk(userOpenId) {
48
53
  return `project:${userOpenId}`;
49
54
  }
@@ -220,8 +225,10 @@ function registerFeishuProjectOAuthTool(api) {
220
225
  }
221
226
  }
222
227
  const session = await prepareRemoteAuth(mcpEndpoint);
223
- const authCard = buildAuthCard({
224
- verificationUriComplete: session.authorizationUrl,
228
+ const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);
229
+ const authCard = buildProjectAuthCard({
230
+ authorizationUrl: session.authorizationUrl,
231
+ operationId,
225
232
  expiresMin: 10
226
233
  });
227
234
  const remoteCardId = chatId ? await createCardEntity({ cfg, card: authCard, accountId }) : null;
@@ -235,16 +242,30 @@ function registerFeishuProjectOAuthTool(api) {
235
242
  accountId
236
243
  });
237
244
  }
238
- pendingRemoteSessions.set(key, {
245
+ const pendingSession = {
239
246
  session,
240
- cardId: remoteCardId ?? void 0
241
- });
247
+ cardId: remoteCardId ?? void 0,
248
+ operationId,
249
+ senderOpenId,
250
+ ticket: {
251
+ messageId: ticket.messageId,
252
+ chatId,
253
+ accountId,
254
+ chatType: ticket.chatType,
255
+ threadId: ticket.threadId
256
+ }
257
+ };
258
+ pendingRemoteSessions.set(key, pendingSession);
259
+ operationIdIndex.set(operationId, key);
260
+ setTimeout(() => {
261
+ if (operationIdIndex.get(operationId) === key) operationIdIndex.delete(operationId);
262
+ if (pendingRemoteSessions.get(key)?.operationId === operationId) pendingRemoteSessions.delete(key);
263
+ }, PENDING_SESSION_TTL_MS);
242
264
  return json({
243
265
  success: true,
244
266
  mode: "remote",
245
- message: "\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002",
246
- authorization_url: session.authorizationUrl,
247
- awaiting_callback_url: true
267
+ message: '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u7528\u6237\u6309\u7167\u5361\u7247\u4E0A\u7684\u4E24\u6B65\u64CD\u4F5C\u5B8C\u6210\u6388\u6743\uFF1A\u2460 \u70B9\u51FB"\u524D\u5F80\u6388\u6743"\u6309\u94AE\u5B8C\u6210\u98DE\u4E66\u9879\u76EE\u6388\u6743\uFF0C\u2461 \u5C06\u6D4F\u89C8\u5668\u5730\u5740\u680F URL \u7C98\u8D34\u5230\u5361\u7247\u8F93\u5165\u6846\u5E76\u70B9\u51FB"\u5B8C\u6210\u6388\u6743"\u3002\u6388\u6743\u5B8C\u6210\u540E\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u901A\u77E5\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u65B9\u6848\u3002',
268
+ awaiting_authorization: true
248
269
  });
249
270
  }
250
271
  // ---------------------------------------------------------------
@@ -264,6 +285,7 @@ function registerFeishuProjectOAuthTool(api) {
264
285
  });
265
286
  }
266
287
  const result = await completeRemoteAuth(pending.session, p.callback_url);
288
+ operationIdIndex.delete(pending.operationId);
267
289
  pendingRemoteSessions.delete(completeKey);
268
290
  const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);
269
291
  await setProjectStoredToken(storedToken);
@@ -309,7 +331,143 @@ function registerFeishuProjectOAuthTool(api) {
309
331
  );
310
332
  api.logger.info?.("feishu_project_oauth: Registered feishu_project_oauth tool");
311
333
  }
334
+ async function handleProjectAuthCardAction(data, cfg, accountId) {
335
+ let operationId;
336
+ let callbackUrl;
337
+ let senderOpenId;
338
+ try {
339
+ const event = data;
340
+ operationId = event.action?.value?.operation_id;
341
+ callbackUrl = event.action?.form_value?.callback_url?.trim();
342
+ senderOpenId = event.operator?.open_id;
343
+ } catch {
344
+ return;
345
+ }
346
+ if (!operationId || !callbackUrl) {
347
+ return {
348
+ toast: { type: "error", content: "\u8BF7\u5728\u8F93\u5165\u6846\u4E2D\u7C98\u8D34\u6D4F\u89C8\u5668\u5730\u5740\u680F\u7684\u5B8C\u6574 URL" }
349
+ };
350
+ }
351
+ const userKey = operationIdIndex.get(operationId);
352
+ if (!userKey) {
353
+ log.warn(`project card action: operationId=${operationId} not found (expired)`);
354
+ return {
355
+ toast: { type: "error", content: "\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }
356
+ };
357
+ }
358
+ const pending = pendingRemoteSessions.get(userKey);
359
+ if (!pending) {
360
+ return {
361
+ toast: { type: "error", content: "\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }
362
+ };
363
+ }
364
+ if (senderOpenId && senderOpenId !== pending.senderOpenId) {
365
+ log.warn(`project card action: identity mismatch, expected=${pending.senderOpenId}, actual=${senderOpenId}`);
366
+ return {
367
+ toast: { type: "error", content: "\u8BF7\u4F7F\u7528\u53D1\u8D77\u6388\u6743\u7684\u8D26\u53F7\u5B8C\u6210\u64CD\u4F5C" }
368
+ };
369
+ }
370
+ operationIdIndex.delete(operationId);
371
+ pendingRemoteSessions.delete(userKey);
372
+ const capturedPending = pending;
373
+ const capturedCallbackUrl = callbackUrl;
374
+ setImmediate(async () => {
375
+ try {
376
+ const result = await completeRemoteAuth(capturedPending.session, capturedCallbackUrl);
377
+ const storedToken = buildTokenFromOAuth(
378
+ capturedPending.senderOpenId,
379
+ result.clientId,
380
+ result.tokens
381
+ );
382
+ await setProjectStoredToken(storedToken);
383
+ if (capturedPending.cardId) {
384
+ try {
385
+ await updateCardKitCardForAuth({
386
+ cfg,
387
+ cardId: capturedPending.cardId,
388
+ card: buildAuthSuccessCard(),
389
+ sequence: 2,
390
+ accountId
391
+ });
392
+ } catch (e) {
393
+ log.warn(`failed to update project auth card to success: ${e}`);
394
+ }
395
+ }
396
+ log.info(`project auth completed via card callback for ${capturedPending.senderOpenId}`);
397
+ const t = capturedPending.ticket;
398
+ if (t.chatId) {
399
+ try {
400
+ const syntheticMsgId = `${t.messageId}:project-auth-complete`;
401
+ const syntheticEvent = {
402
+ sender: { sender_id: { open_id: capturedPending.senderOpenId } },
403
+ message: {
404
+ message_id: syntheticMsgId,
405
+ chat_id: t.chatId,
406
+ chat_type: t.chatType ?? "p2p",
407
+ message_type: "text",
408
+ content: JSON.stringify({ text: "\u6211\u5DF2\u5B8C\u6210\u98DE\u4E66\u9879\u76EE\u6388\u6743\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002" }),
409
+ thread_id: t.threadId
410
+ }
411
+ };
412
+ const syntheticRuntime = {
413
+ log: (msg) => log.info(msg),
414
+ error: (msg) => log.error(msg)
415
+ };
416
+ const { promise } = enqueueFeishuChatTask({
417
+ accountId: t.accountId,
418
+ chatId: t.chatId,
419
+ threadId: t.threadId,
420
+ task: async () => {
421
+ await withTicket(
422
+ {
423
+ messageId: syntheticMsgId,
424
+ chatId: t.chatId,
425
+ accountId: t.accountId,
426
+ startTime: Date.now(),
427
+ senderOpenId: capturedPending.senderOpenId,
428
+ chatType: t.chatType,
429
+ threadId: t.threadId
430
+ },
431
+ () => handleFeishuMessage({
432
+ cfg,
433
+ event: syntheticEvent,
434
+ accountId: t.accountId,
435
+ forceMention: true,
436
+ runtime: syntheticRuntime,
437
+ replyToMessageId: t.messageId
438
+ })
439
+ );
440
+ }
441
+ });
442
+ await promise;
443
+ log.info("synthetic message dispatched after project auth");
444
+ } catch (e) {
445
+ log.warn(`failed to send synthetic message after project auth: ${e}`);
446
+ }
447
+ }
448
+ } catch (err) {
449
+ log.error(`project auth card callback failed: ${err}`);
450
+ if (capturedPending.cardId) {
451
+ try {
452
+ await updateCardKitCardForAuth({
453
+ cfg,
454
+ cardId: capturedPending.cardId,
455
+ card: buildAuthFailedCard(err instanceof Error ? err.message : String(err)),
456
+ sequence: 2,
457
+ accountId
458
+ });
459
+ } catch (e) {
460
+ log.warn(`failed to update project auth card to failure: ${e}`);
461
+ }
462
+ }
463
+ }
464
+ });
465
+ return {
466
+ toast: { type: "info", content: "\u6B63\u5728\u5B8C\u6210\u6388\u6743..." }
467
+ };
468
+ }
312
469
  export {
470
+ handleProjectAuthCardAction,
313
471
  registerFeishuProjectOAuthTool
314
472
  };
315
473
  //# sourceMappingURL=project-oauth.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/project-oauth.ts"],
4
- "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * feishu_project_oauth \u2014 \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB OAuth \u6388\u6743\u5DE5\u5177\u3002\n *\n * \u57FA\u4E8E MCP \u6807\u51C6 OAuth\uFF08Authorization Code + PKCE + \u52A8\u6001\u5BA2\u6237\u7AEF\u6CE8\u518C\uFF09\uFF0C\n * \u4E0D\u9700\u8981 appId/appSecret\u3002\n *\n * \u5355\u4E00 authorize action \u81EA\u52A8\u5224\u65AD\u8FD0\u884C\u73AF\u5883\uFF1A\n * - \u672C\u5730\uFF08\u80FD\u7ED1\u7AEF\u53E3\uFF09\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\uFF0C\u6D4F\u89C8\u5668\u91CD\u5B9A\u5411\u81EA\u52A8\u5B8C\u6210\n * - \u8FDC\u7A0B\uFF08\u7ED1\u7AEF\u53E3\u5931\u8D25 / headless\uFF09\uFF1A\u964D\u7EA7\u4E3A\u53D1\u9001\u6388\u6743\u94FE\u63A5\uFF0C\u7B49\u7528\u6237\u56DE\u4F20 callback URL\n *\n * Actions:\n * - authorize : \u53D1\u8D77\u6388\u6743\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\u6A21\u5F0F\uFF09\n * - complete_auth : \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\uFF0C\u7528\u6237\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743\n * - status : \u68C0\u67E5\u98DE\u4E66\u9879\u76EE\u6388\u6743\u72B6\u6001\n * - revoke : \u64A4\u9500\u98DE\u4E66\u9879\u76EE\u6388\u6743\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { Type } from '@sinclair/typebox';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\nimport { formatLarkError } from '../core/api-error';\nimport {\n startLocalAuthFlow,\n prepareRemoteAuth,\n completeRemoteAuth,\n type ProjectAuthSession,\n} from '../core/project-auth';\nimport {\n getProjectStoredToken,\n setProjectStoredToken,\n removeProjectStoredToken,\n projectTokenStatus,\n getProjectClientId,\n} from '../core/project-token-store';\nimport type { StoredUAToken } from '../core/token-store';\nimport { getProjectMcpEndpoint } from '../tools/mcp/project/endpoint';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from './oauth-cards';\nimport { json } from './oapi/helpers';\n\nconst log = larkLogger('tools/project-oauth');\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst FeishuProjectOAuthSchema = Type.Object(\n {\n action: Type.Union(\n [\n Type.Literal('authorize'),\n Type.Literal('complete_auth'),\n Type.Literal('status'),\n Type.Literal('revoke'),\n ],\n {\n description:\n 'authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; ' +\n 'complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; ' +\n 'status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743',\n },\n ),\n callback_url: Type.Optional(\n Type.String({\n description: '\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL',\n }),\n ),\n },\n {\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002',\n },\n);\n\ninterface FeishuProjectOAuthParams {\n action: 'authorize' | 'complete_auth' | 'status' | 'revoke';\n callback_url?: string;\n}\n\n// ---------------------------------------------------------------------------\n// In-flight state\n// ---------------------------------------------------------------------------\n\ninterface PendingLocalFlow {\n controller: AbortController;\n cardId: string;\n sequence: number;\n superseded: boolean;\n close: () => Promise<void>;\n}\n\nconst pendingLocalFlows = new Map<string, PendingLocalFlow>();\ninterface PendingRemoteSession {\n session: ProjectAuthSession;\n cardId?: string;\n}\n\nconst pendingRemoteSessions = new Map<string, PendingRemoteSession>();\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fk(userOpenId: string): string {\n return `project:${userOpenId}`;\n}\n\nfunction buildTokenFromOAuth(\n userOpenId: string,\n clientId: string,\n tokens: { access_token: string; refresh_token?: string; expires_in?: number; scope?: string },\n): StoredUAToken {\n const now = Date.now();\n const expiresIn = tokens.expires_in ?? 7200;\n const refreshExpiresIn = 30 * 24 * 3600;\n return {\n userOpenId,\n appId: clientId,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? '',\n expiresAt: now + expiresIn * 1000,\n refreshExpiresAt: now + refreshExpiresIn * 1000,\n scope: tokens.scope ?? '',\n grantedAt: now,\n };\n}\n\nasync function isAlreadyAuthorized(senderOpenId: string): Promise<boolean> {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) return false;\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n return !!existing && projectTokenStatus(existing) !== 'expired';\n}\n\n/**\n * \u5C1D\u8BD5\u542F\u52A8\u672C\u5730\u56DE\u8C03\u670D\u52A1\u5668\u3002\n * \u6210\u529F\u8FD4\u56DE flow \u5BF9\u8C61\uFF1B\u5931\u8D25\uFF08\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\u7B49\uFF09\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F\u3002\n */\nasync function tryLocalAuth(mcpEndpoint: string) {\n try {\n return await startLocalAuthFlow(mcpEndpoint);\n } catch (err) {\n log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerFeishuProjectOAuthTool(api: OpenClawPluginApi) {\n if (!api.config) return;\n\n const cfg = api.config;\n\n api.registerTool(\n {\n name: 'feishu_project_oauth',\n label: 'Feishu Project OAuth',\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002' +\n '\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002' +\n '\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002',\n parameters: FeishuProjectOAuthSchema,\n\n async execute(_toolCallId: string, params: unknown) {\n const p = params as FeishuProjectOAuthParams;\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return json({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const accountId = ticket.accountId;\n const mcpEndpoint = getProjectMcpEndpoint(cfg);\n\n try {\n switch (p.action) {\n // ---------------------------------------------------------------\n // STATUS\n // ---------------------------------------------------------------\n case 'status': {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n if (!existing) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const status = projectTokenStatus(existing);\n return json({\n authorized: status !== 'expired',\n token_status: status,\n scope: existing.scope,\n granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : undefined,\n expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : undefined,\n });\n }\n\n // ---------------------------------------------------------------\n // AUTHORIZE\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\uFF09\n // ---------------------------------------------------------------\n case 'authorize': {\n if (await isAlreadyAuthorized(senderOpenId)) {\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002',\n authorized: true,\n });\n }\n\n const chatId = ticket.chatId;\n if (!chatId) {\n return json({ error: '\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807' });\n }\n\n // Cancel any existing local flow\n const key = fk(senderOpenId);\n const oldLocal = pendingLocalFlows.get(key);\n if (oldLocal) {\n oldLocal.superseded = true;\n oldLocal.controller.abort();\n await oldLocal.close().catch(() => {});\n pendingLocalFlows.delete(key);\n }\n pendingRemoteSessions.delete(key);\n\n // \u98DE\u4E66 Bot \u573A\u666F\uFF1A\u56DE\u8C03\u670D\u52A1\u5668\u8FD0\u884C\u5728\u670D\u52A1\u7AEF\uFF0C127.0.0.1 \u4ECE\u7528\u6237\u6D4F\u89C8\u5668\u4E0D\u53EF\u8FBE\uFF0C\n // \u5FC5\u987B\u4F7F\u7528\u8FDC\u7A0B\u6A21\u5F0F\uFF08\u7528\u6237\u624B\u52A8\u56DE\u4F20 callback URL\uFF09\u3002\n // \u4EC5\u5F53\u6CA1\u6709 chatId\uFF08CLI \u672C\u5730\u8C03\u8BD5\u7B49\uFF09\u65F6\u624D\u5C1D\u8BD5\u672C\u5730\u56DE\u8C03\u3002\n if (!chatId) {\n const local = await tryLocalAuth(mcpEndpoint);\n if (local) {\n const { session, waitForCode, port, close } = local;\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 5,\n });\n const localCardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!localCardId) {\n await close();\n return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n }\n\n const abortController = new AbortController();\n let seq = 1;\n const currentFlow: PendingLocalFlow = {\n controller: abortController,\n cardId: localCardId,\n sequence: seq,\n superseded: false,\n close,\n };\n pendingLocalFlows.set(key, currentFlow);\n\n waitForCode()\n .then(async (result) => {\n if (currentFlow.superseded) return;\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n try {\n await updateCardKitCardForAuth({\n cfg, cardId: localCardId, card: buildAuthSuccessCard(), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n })\n .catch(async (err) => {\n if (currentFlow.superseded) return;\n log.error(`project local auth failed: ${err}`);\n try {\n const msg = err instanceof Error ? err.message : String(err);\n await updateCardKitCardForAuth({\n cfg, cardId: localCardId, card: buildAuthFailedCard(msg), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to failure: ${e}`);\n }\n })\n .finally(async () => {\n await close().catch(() => {});\n if (pendingLocalFlows.get(key) === currentFlow) {\n pendingLocalFlows.delete(key);\n }\n });\n\n return json({\n success: true,\n mode: 'local',\n message: '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002',\n awaiting_authorization: true,\n callback_port: port,\n });\n }\n }\n\n // --- \u8FDC\u7A0B\u6A21\u5F0F\uFF08\u98DE\u4E66 Bot \u573A\u666F \u6216 \u672C\u5730\u6A21\u5F0F\u4E0D\u53EF\u7528\u65F6\u7684\u964D\u7EA7\uFF09 ---\n const session = await prepareRemoteAuth(mcpEndpoint);\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 10,\n });\n const remoteCardId = chatId\n ? await createCardEntity({ cfg, card: authCard, accountId })\n : null;\n if (remoteCardId && chatId) {\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId: remoteCardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n }\n\n pendingRemoteSessions.set(key, {\n session,\n cardId: remoteCardId ?? undefined,\n });\n\n return json({\n success: true,\n mode: 'remote',\n message:\n '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002' +\n '\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C' +\n '\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002',\n authorization_url: session.authorizationUrl,\n awaiting_callback_url: true,\n });\n }\n\n // ---------------------------------------------------------------\n // COMPLETE_AUTH\uFF08\u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u7528\u6237\u56DE\u4F20 callback URL\uFF09\n // ---------------------------------------------------------------\n case 'complete_auth': {\n if (!p.callback_url) {\n return json({\n error: '\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002',\n });\n }\n\n const completeKey = fk(senderOpenId);\n const pending = pendingRemoteSessions.get(completeKey);\n if (!pending) {\n return json({\n error: '\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n\n const result = await completeRemoteAuth(pending.session, p.callback_url);\n pendingRemoteSessions.delete(completeKey);\n\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n\n // \u66F4\u65B0\u5361\u7247\u4E3A\u300C\u6388\u6743\u6210\u529F\u300D\n if (pending.cardId) {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: pending.cardId,\n card: buildAuthSuccessCard(),\n sequence: 2,\n accountId: ticket.accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n }\n\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01',\n authorized: true,\n });\n }\n\n // ---------------------------------------------------------------\n // REVOKE\n // ---------------------------------------------------------------\n case 'revoke': {\n const clientId = await getProjectClientId(senderOpenId);\n if (clientId) {\n await removeProjectStoredToken(clientId, senderOpenId);\n }\n return json({ success: true, message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002' });\n }\n\n default:\n return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${(p as { action: string }).action}` });\n }\n } catch (err) {\n log.error(`project oauth ${p.action} failed: ${err}`);\n return json({ error: formatLarkError(err) });\n }\n },\n },\n { name: 'feishu_project_oauth' },\n );\n\n api.logger.info?.('feishu_project_oauth: Registered feishu_project_oauth tool');\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * feishu_project_oauth \u2014 \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB OAuth \u6388\u6743\u5DE5\u5177\u3002\n *\n * \u57FA\u4E8E MCP \u6807\u51C6 OAuth\uFF08Authorization Code + PKCE + \u52A8\u6001\u5BA2\u6237\u7AEF\u6CE8\u518C\uFF09\uFF0C\n * \u4E0D\u9700\u8981 appId/appSecret\u3002\n *\n * \u5355\u4E00 authorize action \u81EA\u52A8\u5224\u65AD\u8FD0\u884C\u73AF\u5883\uFF1A\n * - \u672C\u5730\uFF08\u80FD\u7ED1\u7AEF\u53E3\uFF09\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\uFF0C\u6D4F\u89C8\u5668\u91CD\u5B9A\u5411\u81EA\u52A8\u5B8C\u6210\n * - \u8FDC\u7A0B\uFF08\u7ED1\u7AEF\u53E3\u5931\u8D25 / headless\uFF09\uFF1A\u964D\u7EA7\u4E3A\u53D1\u9001\u6388\u6743\u94FE\u63A5\uFF0C\u7B49\u7528\u6237\u56DE\u4F20 callback URL\n *\n * Actions:\n * - authorize : \u53D1\u8D77\u6388\u6743\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\u6A21\u5F0F\uFF09\n * - complete_auth : \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\uFF0C\u7528\u6237\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743\n * - status : \u68C0\u67E5\u98DE\u4E66\u9879\u76EE\u6388\u6743\u72B6\u6001\n * - revoke : \u64A4\u9500\u98DE\u4E66\u9879\u76EE\u6388\u6743\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { Type } from '@sinclair/typebox';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\nimport { formatLarkError } from '../core/api-error';\nimport {\n startLocalAuthFlow,\n prepareRemoteAuth,\n completeRemoteAuth,\n type ProjectAuthSession,\n} from '../core/project-auth';\nimport {\n getProjectStoredToken,\n setProjectStoredToken,\n removeProjectStoredToken,\n projectTokenStatus,\n getProjectClientId,\n} from '../core/project-token-store';\nimport type { StoredUAToken } from '../core/token-store';\nimport { getProjectMcpEndpoint } from '../tools/mcp/project/endpoint';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard, buildProjectAuthCard } from './oauth-cards';\nimport { json } from './oapi/helpers';\nimport { handleFeishuMessage } from '../messaging/inbound/handler';\nimport { enqueueFeishuChatTask } from '../channel/chat-queue';\nimport { withTicket } from '../core/lark-ticket';\n\nconst log = larkLogger('tools/project-oauth');\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst FeishuProjectOAuthSchema = Type.Object(\n {\n action: Type.Union(\n [\n Type.Literal('authorize'),\n Type.Literal('complete_auth'),\n Type.Literal('status'),\n Type.Literal('revoke'),\n ],\n {\n description:\n 'authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; ' +\n 'complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; ' +\n 'status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743',\n },\n ),\n callback_url: Type.Optional(\n Type.String({\n description: '\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL',\n }),\n ),\n },\n {\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002',\n },\n);\n\ninterface FeishuProjectOAuthParams {\n action: 'authorize' | 'complete_auth' | 'status' | 'revoke';\n callback_url?: string;\n}\n\n// ---------------------------------------------------------------------------\n// In-flight state\n// ---------------------------------------------------------------------------\n\ninterface PendingLocalFlow {\n controller: AbortController;\n cardId: string;\n sequence: number;\n superseded: boolean;\n close: () => Promise<void>;\n}\n\nconst pendingLocalFlows = new Map<string, PendingLocalFlow>();\ninterface PendingRemoteSession {\n session: ProjectAuthSession;\n cardId?: string;\n operationId: string;\n senderOpenId: string;\n ticket: {\n messageId: string;\n chatId: string;\n accountId: string;\n chatType?: 'p2p' | 'group';\n threadId?: string;\n };\n}\n\n/** userKey \u2192 PendingRemoteSession */\nconst pendingRemoteSessions = new Map<string, PendingRemoteSession>();\n/** operationId \u2192 userKey (reverse index for card callback) */\nconst operationIdIndex = new Map<string, string>();\n\nconst PENDING_SESSION_TTL_MS = 10 * 60 * 1000; // 10 min\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fk(userOpenId: string): string {\n return `project:${userOpenId}`;\n}\n\nfunction buildTokenFromOAuth(\n userOpenId: string,\n clientId: string,\n tokens: { access_token: string; refresh_token?: string; expires_in?: number; scope?: string },\n): StoredUAToken {\n const now = Date.now();\n const expiresIn = tokens.expires_in ?? 7200;\n const refreshExpiresIn = 30 * 24 * 3600;\n return {\n userOpenId,\n appId: clientId,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? '',\n expiresAt: now + expiresIn * 1000,\n refreshExpiresAt: now + refreshExpiresIn * 1000,\n scope: tokens.scope ?? '',\n grantedAt: now,\n };\n}\n\nasync function isAlreadyAuthorized(senderOpenId: string): Promise<boolean> {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) return false;\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n return !!existing && projectTokenStatus(existing) !== 'expired';\n}\n\n/**\n * \u5C1D\u8BD5\u542F\u52A8\u672C\u5730\u56DE\u8C03\u670D\u52A1\u5668\u3002\n * \u6210\u529F\u8FD4\u56DE flow \u5BF9\u8C61\uFF1B\u5931\u8D25\uFF08\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\u7B49\uFF09\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F\u3002\n */\nasync function tryLocalAuth(mcpEndpoint: string) {\n try {\n return await startLocalAuthFlow(mcpEndpoint);\n } catch (err) {\n log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerFeishuProjectOAuthTool(api: OpenClawPluginApi) {\n if (!api.config) return;\n\n const cfg = api.config;\n\n api.registerTool(\n {\n name: 'feishu_project_oauth',\n label: 'Feishu Project OAuth',\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002' +\n '\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002' +\n '\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002',\n parameters: FeishuProjectOAuthSchema,\n\n async execute(_toolCallId: string, params: unknown) {\n const p = params as FeishuProjectOAuthParams;\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return json({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const accountId = ticket.accountId;\n const mcpEndpoint = getProjectMcpEndpoint(cfg);\n\n try {\n switch (p.action) {\n // ---------------------------------------------------------------\n // STATUS\n // ---------------------------------------------------------------\n case 'status': {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n if (!existing) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const status = projectTokenStatus(existing);\n return json({\n authorized: status !== 'expired',\n token_status: status,\n scope: existing.scope,\n granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : undefined,\n expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : undefined,\n });\n }\n\n // ---------------------------------------------------------------\n // AUTHORIZE\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\uFF09\n // ---------------------------------------------------------------\n case 'authorize': {\n if (await isAlreadyAuthorized(senderOpenId)) {\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002',\n authorized: true,\n });\n }\n\n const chatId = ticket.chatId;\n if (!chatId) {\n return json({ error: '\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807' });\n }\n\n // Cancel any existing local flow\n const key = fk(senderOpenId);\n const oldLocal = pendingLocalFlows.get(key);\n if (oldLocal) {\n oldLocal.superseded = true;\n oldLocal.controller.abort();\n await oldLocal.close().catch(() => {});\n pendingLocalFlows.delete(key);\n }\n pendingRemoteSessions.delete(key);\n\n // \u98DE\u4E66 Bot \u573A\u666F\uFF1A\u56DE\u8C03\u670D\u52A1\u5668\u8FD0\u884C\u5728\u670D\u52A1\u7AEF\uFF0C127.0.0.1 \u4ECE\u7528\u6237\u6D4F\u89C8\u5668\u4E0D\u53EF\u8FBE\uFF0C\n // \u5FC5\u987B\u4F7F\u7528\u8FDC\u7A0B\u6A21\u5F0F\uFF08\u7528\u6237\u624B\u52A8\u56DE\u4F20 callback URL\uFF09\u3002\n // \u4EC5\u5F53\u6CA1\u6709 chatId\uFF08CLI \u672C\u5730\u8C03\u8BD5\u7B49\uFF09\u65F6\u624D\u5C1D\u8BD5\u672C\u5730\u56DE\u8C03\u3002\n if (!chatId) {\n const local = await tryLocalAuth(mcpEndpoint);\n if (local) {\n const { session, waitForCode, port, close } = local;\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 5,\n });\n const localCardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!localCardId) {\n await close();\n return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n }\n\n const abortController = new AbortController();\n let seq = 1;\n const currentFlow: PendingLocalFlow = {\n controller: abortController,\n cardId: localCardId,\n sequence: seq,\n superseded: false,\n close,\n };\n pendingLocalFlows.set(key, currentFlow);\n\n waitForCode()\n .then(async (result) => {\n if (currentFlow.superseded) return;\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n try {\n await updateCardKitCardForAuth({\n cfg, cardId: localCardId, card: buildAuthSuccessCard(), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n })\n .catch(async (err) => {\n if (currentFlow.superseded) return;\n log.error(`project local auth failed: ${err}`);\n try {\n const msg = err instanceof Error ? err.message : String(err);\n await updateCardKitCardForAuth({\n cfg, cardId: localCardId, card: buildAuthFailedCard(msg), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to failure: ${e}`);\n }\n })\n .finally(async () => {\n await close().catch(() => {});\n if (pendingLocalFlows.get(key) === currentFlow) {\n pendingLocalFlows.delete(key);\n }\n });\n\n return json({\n success: true,\n mode: 'local',\n message: '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002',\n awaiting_authorization: true,\n callback_port: port,\n });\n }\n }\n\n // --- \u8FDC\u7A0B\u6A21\u5F0F\uFF08\u98DE\u4E66 Bot \u573A\u666F \u6216 \u672C\u5730\u6A21\u5F0F\u4E0D\u53EF\u7528\u65F6\u7684\u964D\u7EA7\uFF09 ---\n const session = await prepareRemoteAuth(mcpEndpoint);\n const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);\n\n const authCard = buildProjectAuthCard({\n authorizationUrl: session.authorizationUrl,\n operationId,\n expiresMin: 10,\n });\n const remoteCardId = chatId\n ? await createCardEntity({ cfg, card: authCard, accountId })\n : null;\n if (remoteCardId && chatId) {\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId: remoteCardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n }\n\n const pendingSession: PendingRemoteSession = {\n session,\n cardId: remoteCardId ?? undefined,\n operationId,\n senderOpenId,\n ticket: {\n messageId: ticket.messageId,\n chatId,\n accountId,\n chatType: ticket.chatType,\n threadId: ticket.threadId,\n },\n };\n pendingRemoteSessions.set(key, pendingSession);\n operationIdIndex.set(operationId, key);\n setTimeout(() => {\n if (operationIdIndex.get(operationId) === key) operationIdIndex.delete(operationId);\n if (pendingRemoteSessions.get(key)?.operationId === operationId) pendingRemoteSessions.delete(key);\n }, PENDING_SESSION_TTL_MS);\n\n return json({\n success: true,\n mode: 'remote',\n message:\n '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u7528\u6237\u6309\u7167\u5361\u7247\u4E0A\u7684\u4E24\u6B65\u64CD\u4F5C\u5B8C\u6210\u6388\u6743\uFF1A' +\n '\u2460 \u70B9\u51FB\"\u524D\u5F80\u6388\u6743\"\u6309\u94AE\u5B8C\u6210\u98DE\u4E66\u9879\u76EE\u6388\u6743\uFF0C' +\n '\u2461 \u5C06\u6D4F\u89C8\u5668\u5730\u5740\u680F URL \u7C98\u8D34\u5230\u5361\u7247\u8F93\u5165\u6846\u5E76\u70B9\u51FB\"\u5B8C\u6210\u6388\u6743\"\u3002' +\n '\u6388\u6743\u5B8C\u6210\u540E\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u901A\u77E5\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u65B9\u6848\u3002',\n awaiting_authorization: true,\n });\n }\n\n // ---------------------------------------------------------------\n // COMPLETE_AUTH\uFF08\u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u7528\u6237\u56DE\u4F20 callback URL\uFF09\n // ---------------------------------------------------------------\n case 'complete_auth': {\n if (!p.callback_url) {\n return json({\n error: '\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002',\n });\n }\n\n const completeKey = fk(senderOpenId);\n const pending = pendingRemoteSessions.get(completeKey);\n if (!pending) {\n return json({\n error: '\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n\n const result = await completeRemoteAuth(pending.session, p.callback_url);\n operationIdIndex.delete(pending.operationId);\n pendingRemoteSessions.delete(completeKey);\n\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n\n // \u66F4\u65B0\u5361\u7247\u4E3A\u300C\u6388\u6743\u6210\u529F\u300D\n if (pending.cardId) {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: pending.cardId,\n card: buildAuthSuccessCard(),\n sequence: 2,\n accountId: ticket.accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n }\n\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01',\n authorized: true,\n });\n }\n\n // ---------------------------------------------------------------\n // REVOKE\n // ---------------------------------------------------------------\n case 'revoke': {\n const clientId = await getProjectClientId(senderOpenId);\n if (clientId) {\n await removeProjectStoredToken(clientId, senderOpenId);\n }\n return json({ success: true, message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002' });\n }\n\n default:\n return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${(p as { action: string }).action}` });\n }\n } catch (err) {\n log.error(`project oauth ${p.action} failed: ${err}`);\n return json({ error: formatLarkError(err) });\n }\n },\n },\n { name: 'feishu_project_oauth' },\n );\n\n api.logger.info?.('feishu_project_oauth: Registered feishu_project_oauth tool');\n}\n\n// ---------------------------------------------------------------------------\n// Card callback handler \u2014 \u5361\u7247\u8868\u5355\u63D0\u4EA4\u56DE\u8C03\n// ---------------------------------------------------------------------------\n\n/**\n * \u5904\u7406\u98DE\u4E66\u9879\u76EE OAuth \u5361\u7247\u7684 form submit \u56DE\u8C03\u3002\n *\n * \u7528\u6237\u5728\u5361\u7247\u8F93\u5165\u6846\u7C98\u8D34 callback URL \u5E76\u70B9\u51FB\"\u5B8C\u6210\u6388\u6743\"\u540E\u89E6\u53D1\u3002\n * \u7531 auto-auth.ts \u7684 handleCardAction \u5206\u53D1\u8C03\u7528\u3002\n */\nexport async function handleProjectAuthCardAction(\n data: unknown,\n cfg: import('openclaw/plugin-sdk').ClawdbotConfig,\n accountId: string,\n): Promise<unknown> {\n let operationId: string | undefined;\n let callbackUrl: string | undefined;\n let senderOpenId: string | undefined;\n\n try {\n const event = data as {\n operator?: { open_id?: string };\n action?: {\n value?: { action?: string; operation_id?: string };\n form_value?: Record<string, string>;\n };\n };\n operationId = event.action?.value?.operation_id;\n callbackUrl = event.action?.form_value?.callback_url?.trim();\n senderOpenId = event.operator?.open_id;\n } catch {\n return;\n }\n\n if (!operationId || !callbackUrl) {\n return {\n toast: { type: 'error' as const, content: '\u8BF7\u5728\u8F93\u5165\u6846\u4E2D\u7C98\u8D34\u6D4F\u89C8\u5668\u5730\u5740\u680F\u7684\u5B8C\u6574 URL' },\n };\n }\n\n // \u67E5\u627E pending session\n const userKey = operationIdIndex.get(operationId);\n if (!userKey) {\n log.warn(`project card action: operationId=${operationId} not found (expired)`);\n return {\n toast: { type: 'error' as const, content: '\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743' },\n };\n }\n\n const pending = pendingRemoteSessions.get(userKey);\n if (!pending) {\n return {\n toast: { type: 'error' as const, content: '\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743' },\n };\n }\n\n // \u6821\u9A8C\u64CD\u4F5C\u4EBA\u4E0E\u53D1\u8D77\u4EBA\u4E00\u81F4\n if (senderOpenId && senderOpenId !== pending.senderOpenId) {\n log.warn(`project card action: identity mismatch, expected=${pending.senderOpenId}, actual=${senderOpenId}`);\n return {\n toast: { type: 'error' as const, content: '\u8BF7\u4F7F\u7528\u53D1\u8D77\u6388\u6743\u7684\u8D26\u53F7\u5B8C\u6210\u64CD\u4F5C' },\n };\n }\n\n // \u6E05\u7406\n operationIdIndex.delete(operationId);\n pendingRemoteSessions.delete(userKey);\n\n // \u5F02\u6B65\u5B8C\u6210 token \u4EA4\u6362\uFF08\u5148\u8FD4\u56DE toast\uFF0C\u518D\u540E\u53F0\u5904\u7406\uFF09\n const capturedPending = pending;\n const capturedCallbackUrl = callbackUrl;\n\n setImmediate(async () => {\n try {\n const result = await completeRemoteAuth(capturedPending.session, capturedCallbackUrl);\n const storedToken = buildTokenFromOAuth(\n capturedPending.senderOpenId,\n result.clientId,\n result.tokens,\n );\n await setProjectStoredToken(storedToken);\n\n // \u66F4\u65B0\u5361\u7247\u4E3A\"\u6388\u6743\u6210\u529F\"\n if (capturedPending.cardId) {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: capturedPending.cardId,\n card: buildAuthSuccessCard(),\n sequence: 2,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n }\n\n log.info(`project auth completed via card callback for ${capturedPending.senderOpenId}`);\n\n // \u53D1\u9001\u5408\u6210\u6D88\u606F\uFF0C\u8BA9 AI \u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\n const t = capturedPending.ticket;\n if (t.chatId) {\n try {\n const syntheticMsgId = `${t.messageId}:project-auth-complete`;\n const syntheticEvent = {\n sender: { sender_id: { open_id: capturedPending.senderOpenId } },\n message: {\n message_id: syntheticMsgId,\n chat_id: t.chatId,\n chat_type: t.chatType ?? 'p2p',\n message_type: 'text',\n content: JSON.stringify({ text: '\u6211\u5DF2\u5B8C\u6210\u98DE\u4E66\u9879\u76EE\u6388\u6743\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002' }),\n thread_id: t.threadId,\n },\n };\n const syntheticRuntime = {\n log: (msg: string) => log.info(msg),\n error: (msg: string) => log.error(msg),\n };\n const { promise } = enqueueFeishuChatTask({\n accountId: t.accountId,\n chatId: t.chatId,\n threadId: t.threadId,\n task: async () => {\n await withTicket(\n {\n messageId: syntheticMsgId,\n chatId: t.chatId,\n accountId: t.accountId,\n startTime: Date.now(),\n senderOpenId: capturedPending.senderOpenId,\n chatType: t.chatType,\n threadId: t.threadId,\n },\n () =>\n handleFeishuMessage({\n cfg,\n event: syntheticEvent as Parameters<typeof handleFeishuMessage>[0]['event'],\n accountId: t.accountId,\n forceMention: true,\n runtime: syntheticRuntime as Parameters<typeof handleFeishuMessage>[0]['runtime'],\n replyToMessageId: t.messageId,\n }),\n );\n },\n });\n await promise;\n log.info('synthetic message dispatched after project auth');\n } catch (e) {\n log.warn(`failed to send synthetic message after project auth: ${e}`);\n }\n }\n } catch (err) {\n log.error(`project auth card callback failed: ${err}`);\n // \u5C1D\u8BD5\u66F4\u65B0\u5361\u7247\u4E3A\u5931\u8D25\u72B6\u6001\n if (capturedPending.cardId) {\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: capturedPending.cardId,\n card: buildAuthFailedCard(err instanceof Error ? err.message : String(err)),\n sequence: 2,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to failure: ${e}`);\n }\n }\n }\n });\n\n return {\n toast: { type: 'info' as const, content: '\u6B63\u5728\u5B8C\u6210\u6388\u6743...' },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["session", "authCard"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-project/openclaw-lark-project",
3
- "version": "2026.3.134",
3
+ "version": "2026.3.162",
4
4
  "description": "OpenClaw Lark/Feishu channel plugin",
5
5
  "type": "module",
6
6
  "files": [