@xopcai/xopc 0.0.104 → 0.0.106

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.
Files changed (184) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/gateway-setup.js +2 -2
  3. package/dist/extensions/feishu/src/adapters/gateway-setup.js.map +1 -1
  4. package/dist/extensions/feishu/src/directory/directory-adapter.js +1 -1
  5. package/dist/extensions/feishu/src/directory/directory-adapter.js.map +1 -1
  6. package/dist/extensions/feishu/src/outbound/outbound-adapter.js +4 -3
  7. package/dist/extensions/feishu/src/outbound/outbound-adapter.js.map +1 -1
  8. package/dist/extensions/feishu/src/plugin.d.ts +6 -3
  9. package/dist/extensions/feishu/src/plugin.js +59 -40
  10. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  11. package/dist/extensions/feishu/src/streaming/streaming-adapter.js +20 -16
  12. package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -1
  13. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js +1 -1
  14. package/dist/extensions/feishu/src/transport/webhook/monitor.js +1 -1
  15. package/dist/extensions/feishu/src/workflow-progress.d.ts +1 -1
  16. package/dist/extensions/feishu/src/workflow-progress.js +2 -2
  17. package/dist/extensions/feishu/src/workflow-progress.js.map +1 -1
  18. package/dist/extensions/telegram/src/adapters/gateway-setup.js +8 -6
  19. package/dist/extensions/telegram/src/adapters/gateway-setup.js.map +1 -1
  20. package/dist/extensions/telegram/src/command-handler.d.ts +1 -1
  21. package/dist/extensions/telegram/src/command-handler.js +11 -9
  22. package/dist/extensions/telegram/src/command-handler.js.map +1 -1
  23. package/dist/extensions/telegram/src/inbound-processor.js +1 -2
  24. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  25. package/dist/extensions/telegram/src/plugin.d.ts +1 -0
  26. package/dist/extensions/telegram/src/plugin.js +6 -6
  27. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  28. package/dist/extensions/telegram/src/workflow-progress.d.ts +1 -1
  29. package/dist/extensions/telegram/src/workflow-progress.js.map +1 -1
  30. package/dist/extensions/telegram/xopc.extension.json +1 -1
  31. package/dist/extensions/weixin/src/cli/gateway-qr-login.d.ts +4 -0
  32. package/dist/extensions/weixin/src/cli/gateway-qr-login.js +34 -5
  33. package/dist/extensions/weixin/src/cli/gateway-qr-login.js.map +1 -1
  34. package/dist/extensions/weixin/src/cli/qr-login.js +4 -3
  35. package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
  36. package/dist/extensions/weixin/src/plugin.d.ts +1 -0
  37. package/dist/extensions/weixin/src/plugin.js +7 -6
  38. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  39. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  40. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  41. package/dist/gateway/static/root/assets/agents-3eP0vqFO.js +396 -0
  42. package/dist/gateway/static/root/assets/{apps-page-L-IwQTe0.js → apps-page-CZ2ZKrko.js} +1 -1
  43. package/dist/gateway/static/root/assets/channels-settings-BbETa93K.js +1 -0
  44. package/dist/gateway/static/root/assets/cron-page-DCtUkHMu.js +1 -0
  45. package/dist/gateway/static/root/assets/{dist-C6vclgEL.js → dist-OM9qGfm5.js} +1 -1
  46. package/dist/gateway/static/root/assets/{extension-debug-page-D4JYAbsX.js → extension-debug-page-D-WHrr_w.js} +1 -1
  47. package/dist/gateway/static/root/assets/{extension-page-_T62gi6e.js → extension-page-CCgjSoKn.js} +1 -1
  48. package/dist/gateway/static/root/assets/{extension-settings-page-CkJbjyM-.js → extension-settings-page-BJH5a-Gn.js} +1 -1
  49. package/dist/gateway/static/root/assets/{fetch-B5aiIguv.js → fetch-CkvRO30i.js} +1 -1
  50. package/dist/gateway/static/root/assets/{gateway-startup-retry-DqmLXhs6.js → gateway-startup-retry-BkKyRDrF.js} +1 -1
  51. package/dist/gateway/static/root/assets/{heartbeat-config-api-HwJV1K_Y.js → heartbeat-config-api-CVyP6pFj.js} +1 -1
  52. package/dist/gateway/static/root/assets/index-CEZxETzw.js +4845 -0
  53. package/dist/gateway/static/root/assets/{logs-page-DsjtTjBy.js → logs-page-R6Vv_Ja6.js} +2 -2
  54. package/dist/gateway/static/root/assets/{note-detail-page-B2V7RhgD.js → note-detail-page-BUDarLhh.js} +54 -54
  55. package/dist/gateway/static/root/assets/{note-time-BG8GCTr4.js → note-time-Cc2-T21g.js} +1 -1
  56. package/dist/gateway/static/root/assets/{notes-page-xrcVBQ6Z.js → notes-page-EAHl16bq.js} +1 -1
  57. package/dist/gateway/static/root/assets/{schema-form-S-QUvhul.js → schema-form-CrlOmEZt.js} +1 -1
  58. package/dist/gateway/static/root/assets/sessions-page-Ce_xuuSB.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-advanced-gate-DujQl9c7.js → settings-advanced-gate-BBAQxxew.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-form-section-r1V7dRV6.js → settings-form-section-DjPD-ErC.js} +1 -1
  61. package/dist/gateway/static/root/assets/settings-page-KhQ9QgH0.js +3 -0
  62. package/dist/gateway/static/root/assets/{share-preview-page-DOFy0FNt.js → share-preview-page-BjvPht3V.js} +1 -1
  63. package/dist/gateway/static/root/assets/skills-page-C0laq7l1.js +2 -0
  64. package/dist/gateway/static/root/assets/{theme-store-Bo3j5Ovv.js → theme-store-DYgVdRB4.js} +1 -1
  65. package/dist/gateway/static/root/assets/url-DPEStU4M.js +3 -0
  66. package/dist/gateway/static/root/assets/{voice-api-key-field-AjmUAlEQ.js → voice-api-key-field-DeGckWkl.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflow-page.utils-DLEJ92gK.js → workflow-page.utils-D4sqR0yX.js} +1 -1
  68. package/dist/gateway/static/root/assets/workflows-page-DFjM95g5.js +27 -0
  69. package/dist/gateway/static/root/index.html +5 -5
  70. package/dist/package.js +1 -1
  71. package/dist/src/agent/context/workspace-seed.js +1 -1
  72. package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -0
  73. package/dist/src/agent/inbound/turn-dispatcher.js +1 -0
  74. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  75. package/dist/src/agent/mcp/bundle-mcp-config.js +16 -8
  76. package/dist/src/agent/mcp/bundle-mcp-config.js.map +1 -1
  77. package/dist/src/agent/mcp/bundle-mcp-policy.js +2 -2
  78. package/dist/src/agent/mcp/bundle-mcp-policy.js.map +1 -1
  79. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  80. package/dist/src/agent/mcp/mcp-http.js +1 -1
  81. package/dist/src/agent/mcp/mcp-http.js.map +1 -1
  82. package/dist/src/agent/mcp/mcp-transport-config.js +2 -6
  83. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  84. package/dist/src/agent/prompt/memory/index.js +30 -2
  85. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  86. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  87. package/dist/src/agent/service/direct-turn-helpers.js +5 -2
  88. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  89. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -0
  90. package/dist/src/agent/service/process-direct-streaming.js +15 -1
  91. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  92. package/dist/src/agent/service.js +2 -1
  93. package/dist/src/agent/service.js.map +1 -1
  94. package/dist/src/agent/service.types.d.ts +3 -0
  95. package/dist/src/agent/skills/hub-pull.js +1 -1
  96. package/dist/src/agent/source-context/index.d.ts +2 -0
  97. package/dist/src/agent/source-context/index.js +3 -0
  98. package/dist/src/agent/source-context/injector.d.ts +3 -0
  99. package/dist/src/agent/source-context/injector.js +36 -0
  100. package/dist/src/agent/source-context/injector.js.map +1 -0
  101. package/dist/src/agent/source-context/types.d.ts +19 -0
  102. package/dist/src/agent/source-context/types.js +10 -0
  103. package/dist/src/agent/source-context/types.js.map +1 -0
  104. package/dist/src/agent/tools/factory.js +5 -5
  105. package/dist/src/agent/tools/index.js +3 -3
  106. package/dist/src/agent/tools/session-search-tool.js +1 -1
  107. package/dist/src/agent/tools/shell.js +1 -1
  108. package/dist/src/agent/tools/web-extract.js +2 -2
  109. package/dist/src/agent/tools/web.js +1 -1
  110. package/dist/src/agent/workflow/index.js +2 -1
  111. package/dist/src/agent/workflow/runtime.d.ts +2 -2
  112. package/dist/src/agent/workflow/runtime.js +1 -14
  113. package/dist/src/agent/workflow/runtime.js.map +1 -1
  114. package/dist/src/agent/workflow/snapshot-empty.d.ts +2 -0
  115. package/dist/src/agent/workflow/snapshot-empty.js +19 -0
  116. package/dist/src/agent/workflow/snapshot-empty.js.map +1 -0
  117. package/dist/src/agent/workflow/snapshot.js +1 -1
  118. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  119. package/dist/src/browser/actions/extension-registry-bridge.js +1 -1
  120. package/dist/src/browser/actions/registry.js +2 -2
  121. package/dist/src/browser/cdp-local-launcher.js +1 -1
  122. package/dist/src/browser/index.js +3 -3
  123. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  124. package/dist/src/browser/providers/cloakbrowser.js +1 -1
  125. package/dist/src/cli/commands/browser-cli-helpers.js +1 -1
  126. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  127. package/dist/src/config/paths.d.ts +1 -0
  128. package/dist/src/config/paths.js +5 -1
  129. package/dist/src/config/paths.js.map +1 -1
  130. package/dist/src/connectors/health.js +1 -1
  131. package/dist/src/connectors/install.js +1 -1
  132. package/dist/src/document-understanding/extract.d.ts +13 -0
  133. package/dist/src/document-understanding/extract.js +63 -0
  134. package/dist/src/document-understanding/extract.js.map +1 -0
  135. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  136. package/dist/src/gateway/hono/routes/notes.js +46 -27
  137. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  138. package/dist/src/gateway/service.js +11 -0
  139. package/dist/src/gateway/service.js.map +1 -1
  140. package/dist/src/mcp/channel-bridge.js +1 -1
  141. package/dist/src/notes/agent-context.d.ts +40 -0
  142. package/dist/src/notes/agent-context.js +383 -0
  143. package/dist/src/notes/agent-context.js.map +1 -0
  144. package/dist/src/notes/index.d.ts +2 -0
  145. package/dist/src/notes/index.js +2 -1
  146. package/dist/src/notes/service.d.ts +5 -0
  147. package/dist/src/notes/service.js +22 -0
  148. package/dist/src/notes/service.js.map +1 -1
  149. package/dist/src/notes/store.js +2 -0
  150. package/dist/src/notes/store.js.map +1 -1
  151. package/dist/src/share/share-thumbnail.js +1 -1
  152. package/dist/src/storage/sqlite/index.d.ts +1 -0
  153. package/dist/src/storage/sqlite/index.js +2 -1
  154. package/dist/src/storage/sqlite/migrations/005_add_note_agent_contexts.sql +10 -0
  155. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  156. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  157. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  158. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  159. package/dist/src/storage/sqlite/note-agent-context-repository.d.ts +10 -0
  160. package/dist/src/storage/sqlite/note-agent-context-repository.js +35 -0
  161. package/dist/src/storage/sqlite/note-agent-context-repository.js.map +1 -0
  162. package/dist/src/storage/sqlite/schema.js +1 -1
  163. package/dist/src/tui/tui-startup-resources.js +1 -1
  164. package/dist/src/voice/stt/availability.d.ts +6 -0
  165. package/dist/src/voice/stt/availability.js +14 -6
  166. package/dist/src/voice/stt/availability.js.map +1 -1
  167. package/dist/src/voice/stt/index.js +2 -1
  168. package/dist/src/voice/stt/index.js.map +1 -1
  169. package/dist/src/voice/stt/mention.d.ts +2 -0
  170. package/dist/src/voice/stt/mention.js +20 -0
  171. package/dist/src/voice/stt/mention.js.map +1 -0
  172. package/dist/src/voice/stt/preflight.d.ts +1 -4
  173. package/dist/src/voice/stt/preflight.js +1 -18
  174. package/dist/src/voice/stt/preflight.js.map +1 -1
  175. package/package.json +4 -4
  176. package/dist/gateway/static/root/assets/agents-CvsBrBfi.js +0 -396
  177. package/dist/gateway/static/root/assets/channels-settings-D95KuPeP.js +0 -1
  178. package/dist/gateway/static/root/assets/cron-page-CfkWXDAI.js +0 -1
  179. package/dist/gateway/static/root/assets/index-DvTb6wNR.js +0 -4845
  180. package/dist/gateway/static/root/assets/sessions-page-CrQer6Au.js +0 -1
  181. package/dist/gateway/static/root/assets/settings-page-C9nrONQ2.js +0 -3
  182. package/dist/gateway/static/root/assets/skills-page-DmTsrs_a.js +0 -2
  183. package/dist/gateway/static/root/assets/url-WgQIjFZu.js +0 -3
  184. package/dist/gateway/static/root/assets/workflows-page-BnjjKrM1.js +0 -27
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "xopc Browser Bridge",
4
- "version": "0.0.104",
4
+ "version": "0.0.106",
5
5
  "description": "Browser automation bridge for xopc — executes commands in Chrome via the local xopc daemon.",
6
6
  "permissions": [
7
7
  "debugger",
@@ -1,8 +1,6 @@
1
- import { init_loader, loadConfig, saveConfig } from "../../../../src/config/loader.js";
2
1
  import { mergeDistinctSenderIds } from "../../../../src/channels/pairing/preseed-allow-from.js";
3
2
  import { beginAppRegistration, initAppRegistration, pollAppRegistration } from "../auth/app-registration.js";
4
3
  //#region extensions/feishu/src/adapters/gateway-setup.ts
5
- init_loader();
6
4
  const activeSessions = /* @__PURE__ */ new Map();
7
5
  const completedSessions = /* @__PURE__ */ new Map();
8
6
  function rememberCompleted(sessionKey, state) {
@@ -150,6 +148,7 @@ const feishuGatewaySetupActions = { async runAction({ actionId, input, locale })
150
148
  return;
151
149
  }
152
150
  try {
151
+ const { loadConfig, saveConfig } = await import("../../../../src/config/loader.js");
153
152
  await saveConfig(buildFeishuConfig(loadConfig(), outcome.result));
154
153
  rememberCompleted(sessionKey, {
155
154
  phase: "done",
@@ -200,6 +199,7 @@ const feishuGatewaySetupActions = { async runAction({ actionId, input, locale })
200
199
  ok: false,
201
200
  message: zh ? "App ID 和 App Secret 为必填项。" : "App ID and App Secret are required."
202
201
  };
202
+ const { loadConfig, saveConfig } = await import("../../../../src/config/loader.js");
203
203
  await saveConfig(buildFeishuConfig(loadConfig(), {
204
204
  appId,
205
205
  appSecret,
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-setup.js","names":[],"sources":["../../../../../extensions/feishu/src/adapters/gateway-setup.ts"],"sourcesContent":["import { loadConfig, saveConfig } from '@xopcai/xopc/config/loader.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type {\n ChannelRuntimeActionAdapter,\n ChannelRuntimeActionPayload,\n} from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { mergeDistinctSenderIds } from '@xopcai/xopc/channels/pairing/index.js';\n\nimport {\n beginAppRegistration,\n initAppRegistration,\n pollAppRegistration,\n type FeishuDomain,\n type AppRegistrationResult,\n} from '../auth/app-registration.js';\n\ntype FeishuGatewaySetupDone =\n | { phase: 'done'; ok: true; accountId: string; appId: string; domain: FeishuDomain }\n | { phase: 'done'; ok: false; message: string };\n\ntype FeishuGatewaySetupActive = {\n startedAt: number;\n qrPayload: string;\n};\n\nconst activeSessions = new Map<string, FeishuGatewaySetupActive>();\nconst completedSessions = new Map<string, FeishuGatewaySetupDone>();\n\nfunction rememberCompleted(sessionKey: string, state: FeishuGatewaySetupDone): void {\n activeSessions.delete(sessionKey);\n completedSessions.set(sessionKey, state);\n setTimeout(() => completedSessions.delete(sessionKey), 10 * 60_000);\n}\n\nfunction readInput(input: unknown): Record<string, unknown> {\n return input && typeof input === 'object' && !Array.isArray(input)\n ? (input as Record<string, unknown>)\n : {};\n}\n\nfunction normalizeDomain(input: unknown): FeishuDomain {\n return input === 'lark' ? 'lark' : 'feishu';\n}\n\nfunction buildFeishuConfig(config: Config, result: AppRegistrationResult): Config {\n const existing = config.channels?.feishu as Record<string, unknown> | undefined;\n const ownerOpenId = result.openId?.trim();\n const previousAllowFrom = Array.isArray(existing?.allowFrom)\n ? (existing.allowFrom as Array<string | number>)\n : [];\n const allowFrom = ownerOpenId\n ? mergeDistinctSenderIds(previousAllowFrom, [ownerOpenId])\n : previousAllowFrom;\n\n return {\n ...config,\n channels: {\n ...config.channels,\n feishu: {\n ...(existing ?? {}),\n enabled: true,\n appId: result.appId,\n appSecret: result.appSecret,\n domain: result.domain,\n connectionMode: 'websocket',\n dmPolicy: existing?.dmPolicy ?? 'open',\n groupPolicy: existing?.groupPolicy ?? 'allowlist',\n allowFrom,\n groupAllowFrom: Array.isArray(existing?.groupAllowFrom) ? existing.groupAllowFrom : [],\n requireMention: typeof existing?.requireMention === 'boolean' ? existing.requireMention : true,\n renderMode: existing?.renderMode ?? 'auto',\n streaming: typeof existing?.streaming === 'boolean' ? existing.streaming : false,\n reactionNotifications: existing?.reactionNotifications ?? 'own',\n actions: { ...(existing?.actions as Record<string, unknown> | undefined), reactions: true },\n tools: {\n doc: true,\n wiki: true,\n drive: true,\n perm: false,\n bitable: true,\n scopes: true,\n ...(existing?.tools as Record<string, unknown> | undefined),\n },\n historyLimit: typeof existing?.historyLimit === 'number' ? existing.historyLimit : 50,\n textChunkLimit: typeof existing?.textChunkLimit === 'number' ? existing.textChunkLimit : 4000,\n },\n },\n };\n}\n\nfunction isZh(locale: string | undefined): boolean {\n return locale?.toLowerCase().startsWith('zh') === true;\n}\n\nfunction statusPayload(\n state: FeishuGatewaySetupDone | undefined,\n active: FeishuGatewaySetupActive | undefined,\n locale?: string,\n): ChannelRuntimeActionPayload {\n const zh = isZh(locale);\n if (!state && active) {\n return {\n type: 'poll',\n phase: 'pending',\n ok: undefined,\n qrPayload: active.qrPayload,\n message: zh ? '等待飞书 / Lark 确认…' : 'Waiting for Feishu/Lark confirmation...',\n };\n }\n if (!state) {\n return {\n type: 'poll',\n phase: 'unknown',\n ok: false,\n message: zh ? '没有活动的飞书配置会话。请重新开始,二维码可能已过期。' : 'No active Feishu setup session. Start again or the QR may have expired.',\n };\n }\n if (state.ok === false) {\n return {\n type: 'poll',\n phase: 'done',\n ok: false,\n message: state.message,\n };\n }\n return {\n type: 'poll',\n phase: 'done',\n ok: true,\n accountId: state.accountId,\n appId: state.appId,\n domain: state.domain,\n message: zh ? '飞书配置完成。' : 'Feishu setup complete.',\n configChanged: true,\n };\n}\n\nexport const feishuGatewaySetupActions: ChannelRuntimeActionAdapter = {\n async runAction({ actionId, input, locale }) {\n const zh = isZh(locale);\n if (actionId === 'setup.start') {\n const raw = readInput(input);\n const domain = normalizeDomain(raw.domain);\n const supported = await initAppRegistration(domain);\n if (!supported) {\n return {\n ok: true,\n payload: {\n type: 'form',\n submitAction: 'setup.manual',\n message: zh ? '飞书扫码创建不可用。请在高级配置中手动填写应用凭据。' : 'Feishu scan-to-create is unavailable. Enter app credentials manually in Advanced configuration.',\n schema: {\n type: 'object',\n properties: {\n appId: { type: 'string', title: 'App ID' },\n appSecret: { type: 'string', title: 'App Secret', format: 'password' },\n domain: { type: 'string', enum: ['feishu', 'lark'], default: domain, title: zh ? '域名' : 'Domain' },\n },\n required: ['appId', 'appSecret'],\n },\n values: { domain },\n },\n };\n }\n\n const begin = await beginAppRegistration(domain);\n const sessionKey = begin.deviceCode;\n activeSessions.set(sessionKey, {\n startedAt: Date.now(),\n qrPayload: begin.qrUrl,\n });\n\n void (async () => {\n const outcome = await pollAppRegistration({\n deviceCode: begin.deviceCode,\n intervalSec: begin.intervalSec,\n expireInSec: begin.expireInSec,\n initialDomain: domain,\n });\n\n if (outcome.status !== 'success') {\n const message =\n outcome.status === 'access_denied'\n ? (zh ? '用户拒绝授权。' : 'User denied authorization.')\n : outcome.status === 'expired'\n ? (zh ? '配置会话已过期。' : 'Setup session expired.')\n : outcome.status === 'timeout'\n ? (zh ? '配置超时。' : 'Setup timed out.')\n : outcome.message ?? (zh ? '飞书配置失败。' : 'Feishu setup failed.');\n rememberCompleted(sessionKey, { phase: 'done', ok: false, message });\n return;\n }\n\n try {\n const current = loadConfig();\n await saveConfig(buildFeishuConfig(current, outcome.result));\n rememberCompleted(sessionKey, {\n phase: 'done',\n ok: true,\n accountId: 'default',\n appId: outcome.result.appId,\n domain: outcome.result.domain,\n });\n } catch (err) {\n rememberCompleted(sessionKey, {\n phase: 'done',\n ok: false,\n message: `${zh ? '配置保存失败' : 'Config save failed'}: ${String(err)}`,\n });\n }\n })();\n\n return {\n ok: true,\n payload: {\n type: 'qr',\n sessionKey,\n qrPayload: begin.qrUrl,\n statusAction: 'setup.status',\n pollIntervalMs: Math.max(1000, begin.intervalSec * 1000),\n expiresInSec: begin.expireInSec,\n message: zh ? '请使用飞书 / Lark 扫码创建并授权应用。' : 'Scan with Feishu/Lark to create and authorize an app.',\n },\n };\n }\n\n if (actionId === 'setup.status') {\n const raw = readInput(input);\n const sessionKey = typeof raw.sessionKey === 'string' ? raw.sessionKey : '';\n if (!sessionKey) return { ok: false, message: zh ? '缺少配置 sessionKey' : 'Missing setup sessionKey' };\n return { ok: true, payload: statusPayload(completedSessions.get(sessionKey), activeSessions.get(sessionKey), locale) };\n }\n\n if (actionId === 'setup.manual') {\n const raw = readInput(input);\n const appId = typeof raw.appId === 'string' ? raw.appId.trim() : '';\n const appSecret = typeof raw.appSecret === 'string' ? raw.appSecret.trim() : '';\n const domain = normalizeDomain(raw.domain);\n if (!appId || !appSecret) {\n return { ok: false, message: zh ? 'App ID 和 App Secret 为必填项。' : 'App ID and App Secret are required.' };\n }\n const current = loadConfig();\n await saveConfig(buildFeishuConfig(current, { appId, appSecret, domain }));\n return {\n ok: true,\n payload: {\n type: 'ok',\n message: zh ? '飞书配置已保存。' : 'Feishu configuration saved.',\n configChanged: true,\n },\n };\n }\n\n return { ok: false, message: `${zh ? '不支持的飞书操作' : 'Unsupported Feishu action'}: ${actionId}` };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway-setup.js","names":[],"sources":["../../../../../extensions/feishu/src/adapters/gateway-setup.ts"],"sourcesContent":["import type { Config } from '@xopcai/xopc/config/schema.js';\nimport type {\n ChannelRuntimeActionAdapter,\n ChannelRuntimeActionPayload,\n} from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { mergeDistinctSenderIds } from '@xopcai/xopc/channels/pairing/index.js';\n\nimport {\n beginAppRegistration,\n initAppRegistration,\n pollAppRegistration,\n type FeishuDomain,\n type AppRegistrationResult,\n} from '../auth/app-registration.js';\n\ntype FeishuGatewaySetupDone =\n | { phase: 'done'; ok: true; accountId: string; appId: string; domain: FeishuDomain }\n | { phase: 'done'; ok: false; message: string };\n\ntype FeishuGatewaySetupActive = {\n startedAt: number;\n qrPayload: string;\n};\n\nconst activeSessions = new Map<string, FeishuGatewaySetupActive>();\nconst completedSessions = new Map<string, FeishuGatewaySetupDone>();\n\nfunction rememberCompleted(sessionKey: string, state: FeishuGatewaySetupDone): void {\n activeSessions.delete(sessionKey);\n completedSessions.set(sessionKey, state);\n setTimeout(() => completedSessions.delete(sessionKey), 10 * 60_000);\n}\n\nfunction readInput(input: unknown): Record<string, unknown> {\n return input && typeof input === 'object' && !Array.isArray(input)\n ? (input as Record<string, unknown>)\n : {};\n}\n\nfunction normalizeDomain(input: unknown): FeishuDomain {\n return input === 'lark' ? 'lark' : 'feishu';\n}\n\nfunction buildFeishuConfig(config: Config, result: AppRegistrationResult): Config {\n const existing = config.channels?.feishu as Record<string, unknown> | undefined;\n const ownerOpenId = result.openId?.trim();\n const previousAllowFrom = Array.isArray(existing?.allowFrom)\n ? (existing.allowFrom as Array<string | number>)\n : [];\n const allowFrom = ownerOpenId\n ? mergeDistinctSenderIds(previousAllowFrom, [ownerOpenId])\n : previousAllowFrom;\n\n return {\n ...config,\n channels: {\n ...config.channels,\n feishu: {\n ...(existing ?? {}),\n enabled: true,\n appId: result.appId,\n appSecret: result.appSecret,\n domain: result.domain,\n connectionMode: 'websocket',\n dmPolicy: existing?.dmPolicy ?? 'open',\n groupPolicy: existing?.groupPolicy ?? 'allowlist',\n allowFrom,\n groupAllowFrom: Array.isArray(existing?.groupAllowFrom) ? existing.groupAllowFrom : [],\n requireMention: typeof existing?.requireMention === 'boolean' ? existing.requireMention : true,\n renderMode: existing?.renderMode ?? 'auto',\n streaming: typeof existing?.streaming === 'boolean' ? existing.streaming : false,\n reactionNotifications: existing?.reactionNotifications ?? 'own',\n actions: { ...(existing?.actions as Record<string, unknown> | undefined), reactions: true },\n tools: {\n doc: true,\n wiki: true,\n drive: true,\n perm: false,\n bitable: true,\n scopes: true,\n ...(existing?.tools as Record<string, unknown> | undefined),\n },\n historyLimit: typeof existing?.historyLimit === 'number' ? existing.historyLimit : 50,\n textChunkLimit: typeof existing?.textChunkLimit === 'number' ? existing.textChunkLimit : 4000,\n },\n },\n };\n}\n\nfunction isZh(locale: string | undefined): boolean {\n return locale?.toLowerCase().startsWith('zh') === true;\n}\n\nfunction statusPayload(\n state: FeishuGatewaySetupDone | undefined,\n active: FeishuGatewaySetupActive | undefined,\n locale?: string,\n): ChannelRuntimeActionPayload {\n const zh = isZh(locale);\n if (!state && active) {\n return {\n type: 'poll',\n phase: 'pending',\n ok: undefined,\n qrPayload: active.qrPayload,\n message: zh ? '等待飞书 / Lark 确认…' : 'Waiting for Feishu/Lark confirmation...',\n };\n }\n if (!state) {\n return {\n type: 'poll',\n phase: 'unknown',\n ok: false,\n message: zh ? '没有活动的飞书配置会话。请重新开始,二维码可能已过期。' : 'No active Feishu setup session. Start again or the QR may have expired.',\n };\n }\n if (state.ok === false) {\n return {\n type: 'poll',\n phase: 'done',\n ok: false,\n message: state.message,\n };\n }\n return {\n type: 'poll',\n phase: 'done',\n ok: true,\n accountId: state.accountId,\n appId: state.appId,\n domain: state.domain,\n message: zh ? '飞书配置完成。' : 'Feishu setup complete.',\n configChanged: true,\n };\n}\n\nexport const feishuGatewaySetupActions: ChannelRuntimeActionAdapter = {\n async runAction({ actionId, input, locale }) {\n const zh = isZh(locale);\n if (actionId === 'setup.start') {\n const raw = readInput(input);\n const domain = normalizeDomain(raw.domain);\n const supported = await initAppRegistration(domain);\n if (!supported) {\n return {\n ok: true,\n payload: {\n type: 'form',\n submitAction: 'setup.manual',\n message: zh ? '飞书扫码创建不可用。请在高级配置中手动填写应用凭据。' : 'Feishu scan-to-create is unavailable. Enter app credentials manually in Advanced configuration.',\n schema: {\n type: 'object',\n properties: {\n appId: { type: 'string', title: 'App ID' },\n appSecret: { type: 'string', title: 'App Secret', format: 'password' },\n domain: { type: 'string', enum: ['feishu', 'lark'], default: domain, title: zh ? '域名' : 'Domain' },\n },\n required: ['appId', 'appSecret'],\n },\n values: { domain },\n },\n };\n }\n\n const begin = await beginAppRegistration(domain);\n const sessionKey = begin.deviceCode;\n activeSessions.set(sessionKey, {\n startedAt: Date.now(),\n qrPayload: begin.qrUrl,\n });\n\n void (async () => {\n const outcome = await pollAppRegistration({\n deviceCode: begin.deviceCode,\n intervalSec: begin.intervalSec,\n expireInSec: begin.expireInSec,\n initialDomain: domain,\n });\n\n if (outcome.status !== 'success') {\n const message =\n outcome.status === 'access_denied'\n ? (zh ? '用户拒绝授权。' : 'User denied authorization.')\n : outcome.status === 'expired'\n ? (zh ? '配置会话已过期。' : 'Setup session expired.')\n : outcome.status === 'timeout'\n ? (zh ? '配置超时。' : 'Setup timed out.')\n : outcome.message ?? (zh ? '飞书配置失败。' : 'Feishu setup failed.');\n rememberCompleted(sessionKey, { phase: 'done', ok: false, message });\n return;\n }\n\n try {\n const { loadConfig, saveConfig } = await import('@xopcai/xopc/config/loader.js');\n const current = loadConfig();\n await saveConfig(buildFeishuConfig(current, outcome.result));\n rememberCompleted(sessionKey, {\n phase: 'done',\n ok: true,\n accountId: 'default',\n appId: outcome.result.appId,\n domain: outcome.result.domain,\n });\n } catch (err) {\n rememberCompleted(sessionKey, {\n phase: 'done',\n ok: false,\n message: `${zh ? '配置保存失败' : 'Config save failed'}: ${String(err)}`,\n });\n }\n })();\n\n return {\n ok: true,\n payload: {\n type: 'qr',\n sessionKey,\n qrPayload: begin.qrUrl,\n statusAction: 'setup.status',\n pollIntervalMs: Math.max(1000, begin.intervalSec * 1000),\n expiresInSec: begin.expireInSec,\n message: zh ? '请使用飞书 / Lark 扫码创建并授权应用。' : 'Scan with Feishu/Lark to create and authorize an app.',\n },\n };\n }\n\n if (actionId === 'setup.status') {\n const raw = readInput(input);\n const sessionKey = typeof raw.sessionKey === 'string' ? raw.sessionKey : '';\n if (!sessionKey) return { ok: false, message: zh ? '缺少配置 sessionKey' : 'Missing setup sessionKey' };\n return { ok: true, payload: statusPayload(completedSessions.get(sessionKey), activeSessions.get(sessionKey), locale) };\n }\n\n if (actionId === 'setup.manual') {\n const raw = readInput(input);\n const appId = typeof raw.appId === 'string' ? raw.appId.trim() : '';\n const appSecret = typeof raw.appSecret === 'string' ? raw.appSecret.trim() : '';\n const domain = normalizeDomain(raw.domain);\n if (!appId || !appSecret) {\n return { ok: false, message: zh ? 'App ID 和 App Secret 为必填项。' : 'App ID and App Secret are required.' };\n }\n const { loadConfig, saveConfig } = await import('@xopcai/xopc/config/loader.js');\n const current = loadConfig();\n await saveConfig(buildFeishuConfig(current, { appId, appSecret, domain }));\n return {\n ok: true,\n payload: {\n type: 'ok',\n message: zh ? '飞书配置已保存。' : 'Feishu configuration saved.',\n configChanged: true,\n },\n };\n }\n\n return { ok: false, message: `${zh ? '不支持的飞书操作' : 'Unsupported Feishu action'}: ${actionId}` };\n },\n};\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { resolveFeishuAccount } from "../state/accounts.js";
2
- import { createFeishuClient } from "../transport/client/client.js";
3
2
  //#region extensions/feishu/src/directory/directory-adapter.ts
4
3
  function createFeishuDirectoryAdapter() {
5
4
  return { resolveDisplayName: async (params) => {
@@ -8,6 +7,7 @@ function createFeishuDirectoryAdapter() {
8
7
  if (!id.startsWith("ou_") && !id.startsWith("on_")) return;
9
8
  const account = resolveFeishuAccount(params.cfg, "default");
10
9
  if (!account.configured) return void 0;
10
+ const { createFeishuClient } = await import("../transport/client/client.js");
11
11
  const { api } = createFeishuClient(account);
12
12
  try {
13
13
  const res = await api.contact.user.get({
@@ -1 +1 @@
1
- {"version":3,"file":"directory-adapter.js","names":[],"sources":["../../../../../extensions/feishu/src/directory/directory-adapter.ts"],"sourcesContent":["import type { ChannelDirectoryAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\n\nimport { resolveFeishuAccount } from '../state/accounts.js';\nimport { createFeishuClient } from '../transport/client/client.js';\n\nexport function createFeishuDirectoryAdapter(): ChannelDirectoryAdapter {\n return {\n resolveDisplayName: async (params: { cfg: Config; id: string }) => {\n const id = params.id.trim();\n if (!id) return undefined;\n\n // Best-effort: if this looks like an open_id, try resolve user name.\n if (!id.startsWith('ou_') && !id.startsWith('on_')) {\n return undefined;\n }\n\n // Use default account for directory lookups for now.\n const account = resolveFeishuAccount(params.cfg, 'default');\n if (!account.configured) return undefined;\n\n const { api } = createFeishuClient(account);\n try {\n const res = await (api as any).contact.user.get({\n path: { user_id: id },\n params: { user_id_type: 'open_id' },\n });\n const name = res?.data?.user?.name ?? res?.data?.name;\n return typeof name === 'string' && name.trim() ? name.trim() : undefined;\n } catch {\n return undefined;\n }\n },\n };\n}\n\n"],"mappings":";;;AAMA,SAAgB,+BAAwD;AACtE,QAAO,EACL,oBAAoB,OAAO,WAAwC;EACjE,MAAM,KAAK,OAAO,GAAG,MAAM;AAC3B,MAAI,CAAC,GAAI,QAAO,KAAA;AAGhB,MAAI,CAAC,GAAG,WAAW,MAAM,IAAI,CAAC,GAAG,WAAW,MAAM,CAChD;EAIF,MAAM,UAAU,qBAAqB,OAAO,KAAK,UAAU;AAC3D,MAAI,CAAC,QAAQ,WAAY,QAAO,KAAA;EAEhC,MAAM,EAAE,QAAQ,mBAAmB,QAAQ;AAC3C,MAAI;GACF,MAAM,MAAM,MAAO,IAAY,QAAQ,KAAK,IAAI;IAC9C,MAAM,EAAE,SAAS,IAAI;IACrB,QAAQ,EAAE,cAAc,WAAW;IACpC,CAAC;GACF,MAAM,OAAO,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM;AACjD,UAAO,OAAO,SAAS,YAAY,KAAK,MAAM,GAAG,KAAK,MAAM,GAAG,KAAA;UACzD;AACN;;IAGL"}
1
+ {"version":3,"file":"directory-adapter.js","names":[],"sources":["../../../../../extensions/feishu/src/directory/directory-adapter.ts"],"sourcesContent":["import type { ChannelDirectoryAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\n\nimport { resolveFeishuAccount } from '../state/accounts.js';\n\nexport function createFeishuDirectoryAdapter(): ChannelDirectoryAdapter {\n return {\n resolveDisplayName: async (params: { cfg: Config; id: string }) => {\n const id = params.id.trim();\n if (!id) return undefined;\n\n // Best-effort: if this looks like an open_id, try resolve user name.\n if (!id.startsWith('ou_') && !id.startsWith('on_')) {\n return undefined;\n }\n\n // Use default account for directory lookups for now.\n const account = resolveFeishuAccount(params.cfg, 'default');\n if (!account.configured) return undefined;\n\n const { createFeishuClient } = await import('../transport/client/client.js');\n const { api } = createFeishuClient(account);\n try {\n const res = await (api as any).contact.user.get({\n path: { user_id: id },\n params: { user_id_type: 'open_id' },\n });\n const name = res?.data?.user?.name ?? res?.data?.name;\n return typeof name === 'string' && name.trim() ? name.trim() : undefined;\n } catch {\n return undefined;\n }\n },\n };\n}\n\n"],"mappings":";;AAKA,SAAgB,+BAAwD;AACtE,QAAO,EACL,oBAAoB,OAAO,WAAwC;EACjE,MAAM,KAAK,OAAO,GAAG,MAAM;AAC3B,MAAI,CAAC,GAAI,QAAO,KAAA;AAGhB,MAAI,CAAC,GAAG,WAAW,MAAM,IAAI,CAAC,GAAG,WAAW,MAAM,CAChD;EAIF,MAAM,UAAU,qBAAqB,OAAO,KAAK,UAAU;AAC3D,MAAI,CAAC,QAAQ,WAAY,QAAO,KAAA;EAEhC,MAAM,EAAE,uBAAuB,MAAM,OAAO;EAC5C,MAAM,EAAE,QAAQ,mBAAmB,QAAQ;AAC3C,MAAI;GACF,MAAM,MAAM,MAAO,IAAY,QAAQ,KAAK,IAAI;IAC9C,MAAM,EAAE,SAAS,IAAI;IACrB,QAAQ,EAAE,cAAc,WAAW;IACpC,CAAC;GACF,MAAM,OAAO,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM;AACjD,UAAO,OAAO,SAAS,YAAY,KAAK,MAAM,GAAG,KAAK,MAAM,GAAG,KAAA;UACzD;AACN;;IAGL"}
@@ -1,8 +1,6 @@
1
- import { formatFeishuOutboundText } from "../format.js";
2
1
  import { resolveFeishuAccount } from "../state/accounts.js";
3
- import { createFeishuClient } from "../transport/client/client.js";
4
- import { getFeishuBindingByMessageId, recordFeishuMessageBinding } from "../state/message-bindings.js";
5
2
  import { loadMediaForFeishu } from "./media-load.js";
3
+ import { getFeishuBindingByMessageId, recordFeishuMessageBinding } from "../state/message-bindings.js";
6
4
  //#region extensions/feishu/src/outbound/outbound-adapter.ts
7
5
  function createFeishuOutboundAdapter() {
8
6
  return {
@@ -18,11 +16,13 @@ function createFeishuOutboundAdapter() {
18
16
  chatId: ctx.to,
19
17
  error: "Feishu account is not configured"
20
18
  };
19
+ const { createFeishuClient } = await import("../transport/client/client.js");
21
20
  const { api } = createFeishuClient(account);
22
21
  const to = ctx.to;
23
22
  const raw = ctx.text ?? "";
24
23
  const renderMode = account.renderMode ?? "auto";
25
24
  const preferCard = renderMode === "card" || renderMode === "auto";
25
+ const { formatFeishuOutboundText } = await import("../format.js");
26
26
  const cardBody = formatFeishuOutboundText({
27
27
  text: raw,
28
28
  renderMode,
@@ -100,6 +100,7 @@ function createFeishuOutboundAdapter() {
100
100
  maxBytes: 20 * 1024 * 1024,
101
101
  localRoots: ctx.mediaLocalRoots
102
102
  });
103
+ const { createFeishuClient } = await import("../transport/client/client.js");
103
104
  const { api } = createFeishuClient(account);
104
105
  if (loaded.mimeType.startsWith("image/")) {
105
106
  const up = await api.im.v1.image.create({ data: {
@@ -1 +1 @@
1
- {"version":3,"file":"outbound-adapter.js","names":[],"sources":["../../../../../extensions/feishu/src/outbound/outbound-adapter.ts"],"sourcesContent":["import type {\n ChannelOutboundAdapter,\n ChannelOutboundContext,\n OutboundDeliveryResult,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\n\nimport { resolveFeishuAccount } from '../state/accounts.js';\nimport { createFeishuClient } from '../transport/client/client.js';\nimport { formatFeishuOutboundText } from '../format.js';\nimport { loadMediaForFeishu } from './media-load.js';\nimport { getFeishuBindingByMessageId, recordFeishuMessageBinding } from '../state/message-bindings.js';\n\nexport function createFeishuOutboundAdapter(): ChannelOutboundAdapter {\n return {\n deliveryMode: 'direct',\n chunkerMode: 'text',\n textChunkLimit: 4000,\n\n async sendText(ctx: ChannelOutboundContext): Promise<OutboundDeliveryResult> {\n const cfg = ctx.cfg as Config;\n const account = resolveFeishuAccount(cfg, ctx.accountId ?? 'default');\n if (!account.configured) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu account is not configured' };\n }\n\n const { api } = createFeishuClient(account);\n const to = ctx.to;\n const raw = ctx.text ?? '';\n const renderMode = account.renderMode ?? 'auto';\n const preferCard = renderMode === 'card' || renderMode === 'auto';\n\n const cardBody = formatFeishuOutboundText({\n text: raw,\n renderMode,\n forCardMarkdown: true,\n });\n const plainBody = formatFeishuOutboundText({\n text: raw,\n renderMode,\n forCardMarkdown: false,\n });\n\n const receive_id_type = isProbablyOpenId(to) ? 'open_id' : 'chat_id';\n const payloadText = {\n msg_type: 'text',\n content: JSON.stringify({ text: plainBody }),\n };\n const payloadCard = {\n msg_type: 'interactive',\n content: JSON.stringify({\n schema: '2.0',\n config: { update_multi: true },\n body: {\n elements: [\n {\n tag: 'markdown',\n element_id: 'md_1',\n content: cardBody,\n },\n ],\n },\n }),\n };\n\n const send = async (useCard: boolean) => {\n const p = useCard ? payloadCard : payloadText;\n return ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n ...p,\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type },\n data: {\n receive_id: to,\n ...p,\n },\n });\n };\n\n const res = preferCard\n ? await send(true).catch(async () => await send(false))\n : await send(false);\n\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({\n ...parent,\n messageId,\n });\n }\n }\n return { success: true, messageId, chatId: to };\n },\n\n async sendMedia(ctx: ChannelOutboundContext): Promise<OutboundDeliveryResult> {\n const cfg = ctx.cfg as Config;\n const account = resolveFeishuAccount(cfg, ctx.accountId ?? 'default');\n if (!account.configured) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu account is not configured' };\n }\n if (!ctx.mediaUrl?.trim()) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu sendMedia requires mediaUrl' };\n }\n\n const loaded = await loadMediaForFeishu(cfg, ctx.mediaUrl, {\n maxBytes: 20 * 1024 * 1024,\n localRoots: ctx.mediaLocalRoots,\n });\n\n const { api } = createFeishuClient(account);\n\n const isImage = loaded.mimeType.startsWith('image/');\n if (isImage) {\n const up = await (api as any).im.v1.image.create({\n data: {\n image_type: 'message',\n image: loaded.stream,\n },\n });\n const imageKey = up?.data?.image_key ?? up?.image_key;\n if (!imageKey) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu image upload failed' };\n }\n\n const res = ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n msg_type: 'image',\n content: JSON.stringify({ image_key: imageKey }),\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type: isProbablyOpenId(ctx.to) ? 'open_id' : 'chat_id' },\n data: {\n receive_id: ctx.to,\n msg_type: 'image',\n content: JSON.stringify({ image_key: imageKey }),\n },\n });\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({ ...parent, messageId });\n }\n }\n return { success: true, messageId, chatId: ctx.to };\n }\n\n const fileType = guessFileType(loaded.filename);\n const up = await (api as any).im.v1.file.create({\n data: {\n file_type: fileType,\n file_name: loaded.filename,\n file: loaded.stream,\n },\n });\n const fileKey = up?.data?.file_key ?? up?.file_key;\n if (!fileKey) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu file upload failed' };\n }\n\n const res = ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n msg_type: 'file',\n content: JSON.stringify({ file_key: fileKey }),\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type: isProbablyOpenId(ctx.to) ? 'open_id' : 'chat_id' },\n data: {\n receive_id: ctx.to,\n msg_type: 'file',\n content: JSON.stringify({ file_key: fileKey }),\n },\n });\n\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({ ...parent, messageId });\n }\n }\n return { success: true, messageId, chatId: ctx.to };\n },\n };\n}\n\nfunction isProbablyOpenId(to: string): boolean {\n const t = to.trim();\n return t.startsWith('ou_') || t.startsWith('on_') || t.startsWith('open_id:') || t.startsWith('user:');\n}\n\nfunction guessFileType(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase() ?? '';\n if (!ext) return 'stream';\n return ext;\n}\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"outbound-adapter.js","names":[],"sources":["../../../../../extensions/feishu/src/outbound/outbound-adapter.ts"],"sourcesContent":["import type {\n ChannelOutboundAdapter,\n ChannelOutboundContext,\n OutboundDeliveryResult,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\n\nimport { resolveFeishuAccount } from '../state/accounts.js';\nimport { loadMediaForFeishu } from './media-load.js';\nimport { getFeishuBindingByMessageId, recordFeishuMessageBinding } from '../state/message-bindings.js';\n\nexport function createFeishuOutboundAdapter(): ChannelOutboundAdapter {\n return {\n deliveryMode: 'direct',\n chunkerMode: 'text',\n textChunkLimit: 4000,\n\n async sendText(ctx: ChannelOutboundContext): Promise<OutboundDeliveryResult> {\n const cfg = ctx.cfg as Config;\n const account = resolveFeishuAccount(cfg, ctx.accountId ?? 'default');\n if (!account.configured) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu account is not configured' };\n }\n\n const { createFeishuClient } = await import('../transport/client/client.js');\n const { api } = createFeishuClient(account);\n const to = ctx.to;\n const raw = ctx.text ?? '';\n const renderMode = account.renderMode ?? 'auto';\n const preferCard = renderMode === 'card' || renderMode === 'auto';\n\n const { formatFeishuOutboundText } = await import('../format.js');\n const cardBody = formatFeishuOutboundText({\n text: raw,\n renderMode,\n forCardMarkdown: true,\n });\n const plainBody = formatFeishuOutboundText({\n text: raw,\n renderMode,\n forCardMarkdown: false,\n });\n\n const receive_id_type = isProbablyOpenId(to) ? 'open_id' : 'chat_id';\n const payloadText = {\n msg_type: 'text',\n content: JSON.stringify({ text: plainBody }),\n };\n const payloadCard = {\n msg_type: 'interactive',\n content: JSON.stringify({\n schema: '2.0',\n config: { update_multi: true },\n body: {\n elements: [\n {\n tag: 'markdown',\n element_id: 'md_1',\n content: cardBody,\n },\n ],\n },\n }),\n };\n\n const send = async (useCard: boolean) => {\n const p = useCard ? payloadCard : payloadText;\n return ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n ...p,\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type },\n data: {\n receive_id: to,\n ...p,\n },\n });\n };\n\n const res = preferCard\n ? await send(true).catch(async () => await send(false))\n : await send(false);\n\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({\n ...parent,\n messageId,\n });\n }\n }\n return { success: true, messageId, chatId: to };\n },\n\n async sendMedia(ctx: ChannelOutboundContext): Promise<OutboundDeliveryResult> {\n const cfg = ctx.cfg as Config;\n const account = resolveFeishuAccount(cfg, ctx.accountId ?? 'default');\n if (!account.configured) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu account is not configured' };\n }\n if (!ctx.mediaUrl?.trim()) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu sendMedia requires mediaUrl' };\n }\n\n const loaded = await loadMediaForFeishu(cfg, ctx.mediaUrl, {\n maxBytes: 20 * 1024 * 1024,\n localRoots: ctx.mediaLocalRoots,\n });\n\n const { createFeishuClient } = await import('../transport/client/client.js');\n const { api } = createFeishuClient(account);\n\n const isImage = loaded.mimeType.startsWith('image/');\n if (isImage) {\n const up = await (api as any).im.v1.image.create({\n data: {\n image_type: 'message',\n image: loaded.stream,\n },\n });\n const imageKey = up?.data?.image_key ?? up?.image_key;\n if (!imageKey) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu image upload failed' };\n }\n\n const res = ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n msg_type: 'image',\n content: JSON.stringify({ image_key: imageKey }),\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type: isProbablyOpenId(ctx.to) ? 'open_id' : 'chat_id' },\n data: {\n receive_id: ctx.to,\n msg_type: 'image',\n content: JSON.stringify({ image_key: imageKey }),\n },\n });\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({ ...parent, messageId });\n }\n }\n return { success: true, messageId, chatId: ctx.to };\n }\n\n const fileType = guessFileType(loaded.filename);\n const up = await (api as any).im.v1.file.create({\n data: {\n file_type: fileType,\n file_name: loaded.filename,\n file: loaded.stream,\n },\n });\n const fileKey = up?.data?.file_key ?? up?.file_key;\n if (!fileKey) {\n return { success: false, messageId: '', chatId: ctx.to, error: 'Feishu file upload failed' };\n }\n\n const res = ctx.replyToId\n ? await (api as any).im.message.reply({\n path: { message_id: ctx.replyToId },\n data: {\n msg_type: 'file',\n content: JSON.stringify({ file_key: fileKey }),\n ...(ctx.threadId ? { reply_in_thread: true } : {}),\n },\n })\n : await (api as any).im.message.create({\n params: { receive_id_type: isProbablyOpenId(ctx.to) ? 'open_id' : 'chat_id' },\n data: {\n receive_id: ctx.to,\n msg_type: 'file',\n content: JSON.stringify({ file_key: fileKey }),\n },\n });\n\n const messageId = res?.data?.message_id ?? res?.message_id ?? '';\n if (messageId && ctx.replyToId) {\n const parent = getFeishuBindingByMessageId(ctx.replyToId);\n if (parent) {\n recordFeishuMessageBinding({ ...parent, messageId });\n }\n }\n return { success: true, messageId, chatId: ctx.to };\n },\n };\n}\n\nfunction isProbablyOpenId(to: string): boolean {\n const t = to.trim();\n return t.startsWith('ou_') || t.startsWith('on_') || t.startsWith('open_id:') || t.startsWith('user:');\n}\n\nfunction guessFileType(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase() ?? '';\n if (!ext) return 'stream';\n return ext;\n}\n\n"],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { Config } from '@xopcai/xopc/config/schema.js';
8
8
  import type { ChannelCapabilities, ChannelDoctorAdapter, ChannelMessageActionAdapter, ChannelOutboundAdapter, ChannelPlugin, ChannelPluginDefaults, ChannelPluginInitOptions, ChannelPluginReloadMeta, ChannelPluginStartOptions, ChannelSecurityAdapter, ChannelStatusAdapter } from '@xopcai/xopc/channels/plugin-types.js';
9
- import type { ChannelCliLoginAdapter, ChannelRuntimeActionAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';
9
+ import type { ChannelCliLoginAdapter, ChannelRuntimeActionAdapter, ChannelOnboardAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';
10
10
  import { type ResolvedFeishuAccount } from './state/accounts.js';
11
11
  export declare class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishuAccount> {
12
12
  readonly id: "feishu";
@@ -34,7 +34,7 @@ export declare class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishu
34
34
  };
35
35
  readonly configSurface: import("@xopcai/xopc/channels/plugins/types.adapters.js").ChannelConfigSurfaceAdapter;
36
36
  readonly pairing: import("@xopcai/xopc/channels/plugins/types.adapters.js").ChannelPairingAdapter;
37
- onboard: import("@xopcai/xopc/channels/plugins/types.adapters.js").ChannelOnboardAdapter;
37
+ onboard: ChannelOnboardAdapter;
38
38
  readonly cliLogin: ChannelCliLoginAdapter;
39
39
  readonly runtimeActions: ChannelRuntimeActionAdapter;
40
40
  private bus;
@@ -60,7 +60,9 @@ export declare class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishu
60
60
  streaming: import("@xopcai/xopc/channels/plugin-types.js").ChannelStreamingAdapter;
61
61
  status: ChannelStatusAdapter<ResolvedFeishuAccount>;
62
62
  doctor: ChannelDoctorAdapter;
63
- directory: import("@xopcai/xopc/channels/plugins/types.adapters.js").ChannelDirectoryAdapter;
63
+ directory: {
64
+ resolveDisplayName: (params: any) => Promise<string>;
65
+ };
64
66
  actions: ChannelMessageActionAdapter;
65
67
  agentTools: {
66
68
  name: string;
@@ -68,6 +70,7 @@ export declare class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishu
68
70
  execute: (toolCtx: any, args: any) => Promise<any>;
69
71
  }[];
70
72
  init(options: ChannelPluginInitOptions): Promise<void>;
73
+ private registerWorkflowProgressCapability;
71
74
  start(options?: ChannelPluginStartOptions): Promise<void>;
72
75
  stop(accountId?: string): Promise<void>;
73
76
  channelIsRunning(cfg: Config): boolean;
@@ -1,28 +1,18 @@
1
1
  import { createLogger } from "../../../src/utils/logger/index.js";
2
2
  import { init_logger } from "../../../src/utils/logger.js";
3
- import { getWorkflowProgressBroker } from "../../../src/agent/workflow/progress-broker.js";
4
- import "../../../src/agent/workflow/index.js";
5
3
  import { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from "../../../src/channels/security.js";
6
4
  import { createStandardPairingAdapter } from "../../../src/channels/pairing/pairing-store-adapter.js";
7
5
  import { FeishuConfigSchema } from "./schema/config-schema.js";
8
6
  import { listFeishuAccountIds, resolveFeishuAccount } from "./state/accounts.js";
9
- import { createFeishuSocketModeMonitor } from "./transport/socket-mode/monitor.js";
10
- import { createFeishuWebhookMonitor } from "./transport/webhook/monitor.js";
11
7
  import { createFeishuOutboundAdapter } from "./outbound/outbound-adapter.js";
12
8
  import { createFeishuStatusAdapter } from "./status/status-adapter.js";
13
9
  import { createFeishuDoctorAdapter } from "./status/doctor.js";
14
10
  import { feishuConfigSurface } from "./ui/config-surface.js";
15
11
  import { createFeishuStreamingAdapter } from "./streaming/streaming-adapter.js";
16
12
  import { readFrameworkAllowFromList } from "./auth/pairing.js";
17
- import { addReactionFeishu, editMessageFeishu, getMessageFeishu, listPinsFeishu, listReactionsFeishu, pinMessageFeishu, removeReactionFeishu, unpinMessageFeishu } from "./outbound/actions.js";
18
- import { createFeishuDirectoryAdapter } from "./directory/directory-adapter.js";
19
- import { feishuWhoAmI } from "./tools/tools.js";
20
- import { feishuCliLoginAdapter } from "./adapters/cli-login.js";
21
- import { feishuOnboardAdapter } from "./adapters/onboard-cli.js";
22
13
  import { feishuGatewaySetupActions } from "./adapters/gateway-setup.js";
23
14
  import { handleFeishuChannelMessageAction } from "./actions/message-action-handler.js";
24
15
  import { createFeishuInboundPipeline } from "./transport/reliability/inbound-pipeline.js";
25
- import { createFeishuWorkflowProgressCapability } from "./workflow-progress.js";
26
16
  import { isDeepStrictEqual } from "node:util";
27
17
  //#region extensions/feishu/src/plugin.ts
28
18
  /**
@@ -76,8 +66,22 @@ var FeishuChannelPlugin = class {
76
66
  };
77
67
  configSurface = feishuConfigSurface;
78
68
  pairing = createStandardPairingAdapter("feishu");
79
- onboard = feishuOnboardAdapter;
80
- cliLogin = feishuCliLoginAdapter;
69
+ onboard = {
70
+ isConfigured: (config) => {
71
+ const feishu = config.channels?.feishu;
72
+ const appId = typeof feishu?.appId === "string" ? feishu.appId.trim() : "";
73
+ const appSecret = typeof feishu?.appSecret === "string" ? feishu.appSecret.trim() : "";
74
+ return feishu?.enabled === true && Boolean(appId && appSecret);
75
+ },
76
+ configure: async (config) => {
77
+ const { feishuOnboardAdapter } = await import("./adapters/onboard-cli.js");
78
+ return feishuOnboardAdapter.configure(config);
79
+ }
80
+ };
81
+ cliLogin = { runLogin: async (params) => {
82
+ const { feishuCliLoginAdapter } = await import("./adapters/cli-login.js");
83
+ return feishuCliLoginAdapter.runLogin(params);
84
+ } };
81
85
  runtimeActions = feishuGatewaySetupActions;
82
86
  bus;
83
87
  cfg;
@@ -136,7 +140,10 @@ var FeishuChannelPlugin = class {
136
140
  streaming = createFeishuStreamingAdapter(() => this.cfg);
137
141
  status = createFeishuStatusAdapter();
138
142
  doctor = createFeishuDoctorAdapter();
139
- directory = createFeishuDirectoryAdapter();
143
+ directory = { resolveDisplayName: async (params) => {
144
+ const { createFeishuDirectoryAdapter } = await import("./directory/directory-adapter.js");
145
+ return createFeishuDirectoryAdapter().resolveDisplayName?.(params);
146
+ } };
140
147
  actions = { handleAction: handleFeishuChannelMessageAction };
141
148
  agentTools = [
142
149
  {
@@ -145,6 +152,7 @@ var FeishuChannelPlugin = class {
145
152
  execute: async (toolCtx, args) => {
146
153
  const messageId = typeof args?.messageId === "string" ? args.messageId : toolCtx.messageId;
147
154
  if (!messageId) throw new Error("feishu_read requires messageId");
155
+ const { getMessageFeishu } = await import("./outbound/actions.js");
148
156
  return await getMessageFeishu({
149
157
  cfg: this.cfg,
150
158
  accountId: toolCtx.accountId,
@@ -160,6 +168,7 @@ var FeishuChannelPlugin = class {
160
168
  const text = typeof args?.text === "string" ? args.text : "";
161
169
  if (!messageId) throw new Error("feishu_edit requires messageId");
162
170
  if (!text.trim()) throw new Error("feishu_edit requires text");
171
+ const { editMessageFeishu } = await import("./outbound/actions.js");
163
172
  return await editMessageFeishu({
164
173
  cfg: this.cfg,
165
174
  accountId: toolCtx.accountId,
@@ -172,6 +181,7 @@ var FeishuChannelPlugin = class {
172
181
  name: "feishu_scopes_probe",
173
182
  description: "Probe Feishu credentials/scopes (placeholder for full docs/wiki/drive tools).",
174
183
  execute: async (toolCtx) => {
184
+ const { feishuWhoAmI } = await import("./tools/tools.js");
175
185
  return await feishuWhoAmI({
176
186
  cfg: this.cfg,
177
187
  accountId: toolCtx.accountId
@@ -186,15 +196,19 @@ var FeishuChannelPlugin = class {
186
196
  const messageId = typeof a?.messageId === "string" ? a.messageId : toolCtx.messageId;
187
197
  if (!messageId) throw new Error("feishu_react requires messageId");
188
198
  if (!(resolveFeishuAccount(this.cfg, toolCtx.accountId ?? "default").actions?.reactions !== false)) throw new Error("Feishu reactions are disabled via channels.feishu.actions.reactions");
189
- if (a?.list === true) return await listReactionsFeishu({
190
- cfg: this.cfg,
191
- accountId: toolCtx.accountId,
192
- messageId,
193
- emojiType: typeof a?.emojiType === "string" ? a.emojiType : void 0
194
- });
199
+ if (a?.list === true) {
200
+ const { listReactionsFeishu } = await import("./outbound/actions.js");
201
+ return await listReactionsFeishu({
202
+ cfg: this.cfg,
203
+ accountId: toolCtx.accountId,
204
+ messageId,
205
+ emojiType: typeof a?.emojiType === "string" ? a.emojiType : void 0
206
+ });
207
+ }
195
208
  if (a?.remove === true) {
196
209
  const reactionId = typeof a?.reactionId === "string" ? a.reactionId : "";
197
210
  if (!reactionId) throw new Error("feishu_react remove requires reactionId");
211
+ const { removeReactionFeishu } = await import("./outbound/actions.js");
198
212
  return await removeReactionFeishu({
199
213
  cfg: this.cfg,
200
214
  accountId: toolCtx.accountId,
@@ -204,6 +218,7 @@ var FeishuChannelPlugin = class {
204
218
  }
205
219
  const emojiType = typeof a?.emojiType === "string" ? a.emojiType : "";
206
220
  if (!emojiType) throw new Error("feishu_react requires emojiType");
221
+ const { addReactionFeishu } = await import("./outbound/actions.js");
207
222
  return await addReactionFeishu({
208
223
  cfg: this.cfg,
209
224
  accountId: toolCtx.accountId,
@@ -221,6 +236,7 @@ var FeishuChannelPlugin = class {
221
236
  if (action === "list") {
222
237
  const chatId = typeof a?.chatId === "string" ? a.chatId : toolCtx.chatId;
223
238
  if (!chatId) throw new Error("feishu_pins list requires chatId");
239
+ const { listPinsFeishu } = await import("./outbound/actions.js");
224
240
  return await listPinsFeishu({
225
241
  cfg: this.cfg,
226
242
  accountId: toolCtx.accountId,
@@ -233,16 +249,22 @@ var FeishuChannelPlugin = class {
233
249
  }
234
250
  const messageId = typeof a?.messageId === "string" ? a.messageId : toolCtx.messageId;
235
251
  if (!messageId) throw new Error("feishu_pins requires messageId");
236
- if (action === "pin") return await pinMessageFeishu({
237
- cfg: this.cfg,
238
- accountId: toolCtx.accountId,
239
- messageId
240
- });
241
- if (action === "unpin") return await unpinMessageFeishu({
242
- cfg: this.cfg,
243
- accountId: toolCtx.accountId,
244
- messageId
245
- });
252
+ if (action === "pin") {
253
+ const { pinMessageFeishu } = await import("./outbound/actions.js");
254
+ return await pinMessageFeishu({
255
+ cfg: this.cfg,
256
+ accountId: toolCtx.accountId,
257
+ messageId
258
+ });
259
+ }
260
+ if (action === "unpin") {
261
+ const { unpinMessageFeishu } = await import("./outbound/actions.js");
262
+ return await unpinMessageFeishu({
263
+ cfg: this.cfg,
264
+ accountId: toolCtx.accountId,
265
+ messageId
266
+ });
267
+ }
246
268
  throw new Error("feishu_pins requires action: pin | unpin | list");
247
269
  }
248
270
  }
@@ -261,9 +283,13 @@ var FeishuChannelPlugin = class {
261
283
  }, "Feishu inbound pipeline flush failed");
262
284
  }
263
285
  });
264
- this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }));
286
+ await this.registerWorkflowProgressCapability();
265
287
  log.debug("Feishu plugin initialized");
266
288
  }
289
+ async registerWorkflowProgressCapability() {
290
+ const [{ getWorkflowProgressBroker }, { createFeishuWorkflowProgressCapability }] = await Promise.all([import("../../../src/agent/workflow/progress-broker.js"), import("./workflow-progress.js")]);
291
+ this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }));
292
+ }
267
293
  async start(options) {
268
294
  const section = this.cfg.channels?.feishu;
269
295
  if (!section || section.enabled !== true) return;
@@ -280,22 +306,15 @@ var FeishuChannelPlugin = class {
280
306
  if (!p) return this.bus.publishInbound(work.inbound);
281
307
  return p.enqueue(work);
282
308
  };
283
- const monitor = createFeishuSocketModeMonitor({
309
+ const monitorDeps = {
284
310
  account,
285
311
  config: this.cfg,
286
312
  enqueueInbound,
287
313
  inboundDebounceDefaultMs: defaultDebounce,
288
314
  abortSignal: ac.signal,
289
315
  security: { checkAccess: (ctx) => this.security.checkAccess?.(ctx, account, this.cfg) }
290
- });
291
- (account.connectionMode === "webhook" ? createFeishuWebhookMonitor({
292
- account,
293
- config: this.cfg,
294
- enqueueInbound,
295
- inboundDebounceDefaultMs: defaultDebounce,
296
- abortSignal: ac.signal,
297
- security: { checkAccess: (ctx) => this.security.checkAccess?.(ctx, account, this.cfg) }
298
- }) : monitor).run().catch((err) => {
316
+ };
317
+ (account.connectionMode === "webhook" ? (await import("./transport/webhook/monitor.js")).createFeishuWebhookMonitor(monitorDeps) : (await import("./transport/socket-mode/monitor.js")).createFeishuSocketModeMonitor(monitorDeps)).run().catch((err) => {
299
318
  if (err?.name === "AbortError") {
300
319
  log.debug({ accountId }, "Feishu monitor stopped");
301
320
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../../../extensions/feishu/src/plugin.ts"],"sourcesContent":["/**\n * Feishu/Lark channel plugin (Socket Mode first).\n *\n * This is intentionally decomposed into small modules so we can grow toward\n * openclaw `extensions/feishu` parity without turning `plugin.ts` into a monolith.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';\nimport type {\n ChannelCapabilities,\n ChannelDoctorAdapter,\n ChannelMessageActionAdapter,\n ChannelOutboundAdapter,\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginStartOptions,\n ChannelSecurityAdapter,\n ChannelSecurityContext,\n ChannelStatusAdapter,\n ChatType,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type {\n ChannelCliLoginAdapter,\n ChannelRuntimeActionAdapter,\n} from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from '@xopcai/xopc/channels/security.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\n\nimport { FeishuConfigSchema, type FeishuConfig } from './schema/config-schema.js';\nimport { listFeishuAccountIds, resolveFeishuAccount, type ResolvedFeishuAccount } from './state/accounts.js';\nimport { createFeishuSocketModeMonitor } from './transport/socket-mode/monitor.js';\nimport { createFeishuWebhookMonitor } from './transport/webhook/monitor.js';\nimport { createFeishuOutboundAdapter } from './outbound/outbound-adapter.js';\nimport { createFeishuStatusAdapter } from './status/status-adapter.js';\nimport { createFeishuDoctorAdapter } from './status/doctor.js';\nimport { feishuConfigSurface } from './ui/config-surface.js';\nimport { createFeishuStreamingAdapter } from './streaming/streaming-adapter.js';\nimport { readFrameworkAllowFromList } from './auth/pairing.js';\nimport {\n addReactionFeishu,\n editMessageFeishu,\n getMessageFeishu,\n listPinsFeishu,\n listReactionsFeishu,\n pinMessageFeishu,\n removeReactionFeishu,\n unpinMessageFeishu,\n} from './outbound/actions.js';\nimport { createFeishuDirectoryAdapter } from './directory/directory-adapter.js';\nimport { feishuWhoAmI } from './tools/tools.js';\nimport { feishuCliLoginAdapter } from './adapters/cli-login.js';\nimport { feishuOnboardAdapter } from './adapters/onboard-cli.js';\nimport { feishuGatewaySetupActions } from './adapters/gateway-setup.js';\nimport { handleFeishuChannelMessageAction } from './actions/message-action-handler.js';\nimport { createFeishuInboundPipeline, type FeishuInboundWork } from './transport/reliability/inbound-pipeline.js';\nimport { getWorkflowProgressBroker } from '@xopcai/xopc/agent/workflow/index.js';\nimport { createFeishuWorkflowProgressCapability } from './workflow-progress.js';\n\nconst log = createLogger('FeishuPlugin');\n\nexport class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishuAccount> {\n readonly id = 'feishu' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.feishu'],\n };\n\n readonly meta = {\n id: 'feishu',\n label: 'Feishu',\n selectionLabel: 'Feishu/Lark (飞书)',\n docsPath: '/channels/feishu',\n blurb: 'Feishu/Lark enterprise messaging (Socket Mode).',\n order: 4,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities: ChannelCapabilities = {\n chatTypes: ['direct', 'channel'] as ChatType[],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: false,\n edit: true,\n reply: true,\n } as any;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: 350 },\n outbound: { textChunkLimit: 4000 },\n streaming: {\n blockStreamingCoalesce: {\n minChars: 200,\n idleMs: 2500,\n },\n },\n };\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = FeishuConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly configSurface = feishuConfigSurface;\n\n readonly pairing = createStandardPairingAdapter('feishu');\n\n onboard = feishuOnboardAdapter;\n\n readonly cliLogin: ChannelCliLoginAdapter = feishuCliLoginAdapter;\n\n readonly runtimeActions: ChannelRuntimeActionAdapter = feishuGatewaySetupActions;\n\n private bus!: MessageBus;\n private cfg!: Config;\n private abortControllers = new Map<string, AbortController>();\n private inboundPipeline?: ReturnType<typeof createFeishuInboundPipeline>;\n /** Unregister fn for the workflow-progress capability registered against the global broker. */\n private workflowProgressUnregister: (() => void) | null = null;\n\n config = {\n listAccountIds: (cfg: Config) => listFeishuAccountIds(cfg),\n resolveAccount: (cfg: Config, accountId?: string | null) => resolveFeishuAccount(cfg, accountId),\n isConfigured: async (account: ResolvedFeishuAccount) => account.configured,\n describeAccount: (account: ResolvedFeishuAccount) => ({\n accountId: account.accountId,\n channelId: 'feishu',\n enabled: account.enabled,\n configured: account.configured,\n status: account.configured ? undefined : 'unconfigured',\n }),\n };\n\n security: ChannelSecurityAdapter<ResolvedFeishuAccount> = {\n resolveDmPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveDmPolicy(account.dmPolicy, 'open'),\n resolveGroupPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveGroupPolicy(account.groupPolicy, 'allowlist'),\n checkAccess: (ctx: ChannelSecurityContext, account: ResolvedFeishuAccount, _cfg: Config) => {\n const isDm = !ctx.isGroup;\n const frameworkAllowFrom = readFrameworkAllowFromList(account.accountId);\n const baseAllowFrom = isDm ? account.allowFrom : account.groupAllowFrom ?? account.allowFrom;\n const allowFrom = [...(baseAllowFrom ?? []), ...frameworkAllowFrom];\n if (isDm) {\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: false,\n isDm: true,\n },\n dmPolicy: account.dmPolicy,\n allowFrom,\n });\n }\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: true,\n isDm: false,\n },\n groupPolicy: account.groupPolicy,\n allowFrom,\n });\n },\n };\n\n outbound: ChannelOutboundAdapter = createFeishuOutboundAdapter();\n\n streaming = createFeishuStreamingAdapter(() => this.cfg);\n\n status: ChannelStatusAdapter<ResolvedFeishuAccount> = createFeishuStatusAdapter();\n\n doctor: ChannelDoctorAdapter = createFeishuDoctorAdapter();\n\n directory = createFeishuDirectoryAdapter();\n\n actions: ChannelMessageActionAdapter = {\n handleAction: handleFeishuChannelMessageAction,\n };\n\n agentTools = [\n {\n name: 'feishu_read',\n description: 'Read a Feishu message by messageId (debug/utility).',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_read requires messageId');\n return await getMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n },\n },\n {\n name: 'feishu_edit',\n description: 'Edit a Feishu message by messageId.',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n const text = typeof (args as any)?.text === 'string' ? (args as any).text : '';\n if (!messageId) throw new Error('feishu_edit requires messageId');\n if (!text.trim()) throw new Error('feishu_edit requires text');\n return await editMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, text });\n },\n },\n {\n name: 'feishu_scopes_probe',\n description: 'Probe Feishu credentials/scopes (placeholder for full docs/wiki/drive tools).',\n execute: async (toolCtx) => {\n return await feishuWhoAmI({ cfg: this.cfg, accountId: toolCtx.accountId });\n },\n },\n {\n name: 'feishu_react',\n description: 'Add/remove/list reactions for a message.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_react requires messageId');\n\n const account = resolveFeishuAccount(this.cfg, toolCtx.accountId ?? 'default');\n const enabled = (account.actions as any)?.reactions !== false;\n if (!enabled) {\n throw new Error('Feishu reactions are disabled via channels.feishu.actions.reactions');\n }\n\n if (a?.list === true) {\n return await listReactionsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n messageId,\n emojiType: typeof a?.emojiType === 'string' ? a.emojiType : undefined,\n });\n }\n\n if (a?.remove === true) {\n const reactionId = typeof a?.reactionId === 'string' ? a.reactionId : '';\n if (!reactionId) throw new Error('feishu_react remove requires reactionId');\n return await removeReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, reactionId });\n }\n\n const emojiType = typeof a?.emojiType === 'string' ? a.emojiType : '';\n if (!emojiType) throw new Error('feishu_react requires emojiType');\n return await addReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, emojiType });\n },\n },\n {\n name: 'feishu_pins',\n description: 'Pin/unpin/list pins for a chat.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const action = typeof a?.action === 'string' ? a.action : '';\n if (action === 'list') {\n const chatId = typeof a?.chatId === 'string' ? a.chatId : toolCtx.chatId;\n if (!chatId) throw new Error('feishu_pins list requires chatId');\n return await listPinsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n chatId,\n startTime: typeof a?.startTime === 'string' ? a.startTime : undefined,\n endTime: typeof a?.endTime === 'string' ? a.endTime : undefined,\n pageSize: typeof a?.pageSize === 'number' ? a.pageSize : undefined,\n pageToken: typeof a?.pageToken === 'string' ? a.pageToken : undefined,\n });\n }\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_pins requires messageId');\n if (action === 'pin') {\n return await pinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n if (action === 'unpin') {\n return await unpinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n throw new Error('feishu_pins requires action: pin | unpin | list');\n },\n },\n ];\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n this.inboundPipeline = createFeishuInboundPipeline({\n bus: this.bus,\n defaultDebounceMs: defaultDebounce,\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Feishu inbound pipeline flush failed');\n },\n });\n\n // Workflow progress broker capability — turns mid-run snapshots into\n // edit-in-place Feishu messages. Broker is the agent-side subscriber for\n // `tool_execution_update` on the `workflow` tool. Registration is\n // idempotent (replaces a prior cap with the same channelId).\n this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(\n createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }),\n );\n\n log.debug('Feishu plugin initialized');\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.feishu as FeishuConfig | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const ids = options?.accountId ? [options.accountId] : listFeishuAccountIds(this.cfg);\n for (const accountId of ids) {\n const account = resolveFeishuAccount(this.cfg, accountId);\n if (!account.enabled || !account.configured) continue;\n if (this.abortControllers.has(accountId)) continue;\n\n const ac = new AbortController();\n this.abortControllers.set(accountId, ac);\n\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n const enqueueInbound = (work: FeishuInboundWork) => {\n const p = this.inboundPipeline;\n if (!p) {\n return this.bus.publishInbound(work.inbound);\n }\n return p.enqueue(work);\n };\n\n const monitor = createFeishuSocketModeMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) => this.security.checkAccess?.(ctx, account, this.cfg),\n },\n });\n\n const runner =\n account.connectionMode === 'webhook'\n ? createFeishuWebhookMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) =>\n this.security.checkAccess?.(ctx, account, this.cfg),\n },\n })\n : monitor;\n\n void runner.run().catch((err) => {\n if ((err as { name?: string } | undefined)?.name === 'AbortError') {\n log.debug({ accountId }, 'Feishu monitor stopped');\n return;\n }\n log.error({ err, accountId }, 'Feishu monitor exited with error');\n });\n\n log.info({ accountId, mode: account.connectionMode }, 'Feishu monitor started');\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n try {\n await this.inboundPipeline?.flushAll();\n } catch (err) {\n log.warn({ err }, 'Feishu inbound pipeline flushAll failed');\n }\n const ids = accountId ? [accountId] : [...this.abortControllers.keys()];\n for (const id of ids) {\n const ac = this.abortControllers.get(id);\n if (ac) {\n ac.abort();\n this.abortControllers.delete(id);\n }\n }\n // Only unregister the broker capability on a full stop. Per-account stops\n // still leave other accounts capable of delivering progress through the\n // same registered cap.\n if (!accountId) {\n this.workflowProgressUnregister?.();\n this.workflowProgressUnregister = null;\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n const ids = listFeishuAccountIds(cfg);\n return ids.some((id) => {\n const a = resolveFeishuAccount(cfg, id);\n return a.enabled !== false && a.configured && this.abortControllers.has(id);\n });\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prev = this.cfg.channels?.feishu as unknown;\n const next = cfg.channels?.feishu as { enabled?: boolean } | undefined;\n const channelOff = !next || next.enabled !== true;\n\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n return;\n }\n\n this.cfg = cfg;\n\n if (isDeepStrictEqual(prev, next) && this.channelIsRunning(cfg)) {\n return;\n }\n\n await this.stop();\n await this.start();\n }\n}\n\nexport const feishuPlugin = new FeishuChannelPlugin();\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../../../extensions/feishu/src/plugin.ts"],"sourcesContent":["/**\n * Feishu/Lark channel plugin (Socket Mode first).\n *\n * This is intentionally decomposed into small modules so we can grow toward\n * openclaw `extensions/feishu` parity without turning `plugin.ts` into a monolith.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';\nimport type {\n ChannelCapabilities,\n ChannelDoctorAdapter,\n ChannelMessageActionAdapter,\n ChannelOutboundAdapter,\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginStartOptions,\n ChannelSecurityAdapter,\n ChannelSecurityContext,\n ChannelStatusAdapter,\n ChatType,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type {\n ChannelCliLoginAdapter,\n ChannelRuntimeActionAdapter,\n ChannelOnboardAdapter,\n} from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from '@xopcai/xopc/channels/security.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\n\nimport { FeishuConfigSchema, type FeishuConfig } from './schema/config-schema.js';\nimport { listFeishuAccountIds, resolveFeishuAccount, type ResolvedFeishuAccount } from './state/accounts.js';\nimport { createFeishuOutboundAdapter } from './outbound/outbound-adapter.js';\nimport { createFeishuStatusAdapter } from './status/status-adapter.js';\nimport { createFeishuDoctorAdapter } from './status/doctor.js';\nimport { feishuConfigSurface } from './ui/config-surface.js';\nimport { createFeishuStreamingAdapter } from './streaming/streaming-adapter.js';\nimport { readFrameworkAllowFromList } from './auth/pairing.js';\nimport { feishuGatewaySetupActions } from './adapters/gateway-setup.js';\nimport { handleFeishuChannelMessageAction } from './actions/message-action-handler.js';\nimport { createFeishuInboundPipeline, type FeishuInboundWork } from './transport/reliability/inbound-pipeline.js';\n\nconst log = createLogger('FeishuPlugin');\n\nexport class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishuAccount> {\n readonly id = 'feishu' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.feishu'],\n };\n\n readonly meta = {\n id: 'feishu',\n label: 'Feishu',\n selectionLabel: 'Feishu/Lark (飞书)',\n docsPath: '/channels/feishu',\n blurb: 'Feishu/Lark enterprise messaging (Socket Mode).',\n order: 4,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities: ChannelCapabilities = {\n chatTypes: ['direct', 'channel'] as ChatType[],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: false,\n edit: true,\n reply: true,\n } as any;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: 350 },\n outbound: { textChunkLimit: 4000 },\n streaming: {\n blockStreamingCoalesce: {\n minChars: 200,\n idleMs: 2500,\n },\n },\n };\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = FeishuConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly configSurface = feishuConfigSurface;\n\n readonly pairing = createStandardPairingAdapter('feishu');\n\n onboard: ChannelOnboardAdapter = {\n isConfigured: (config) => {\n const feishu = config.channels?.feishu as Record<string, unknown> | undefined;\n const appId = typeof feishu?.appId === 'string' ? feishu.appId.trim() : '';\n const appSecret = typeof feishu?.appSecret === 'string' ? feishu.appSecret.trim() : '';\n return feishu?.enabled === true && Boolean(appId && appSecret);\n },\n configure: async (config) => {\n const { feishuOnboardAdapter } = await import('./adapters/onboard-cli.js');\n return feishuOnboardAdapter.configure(config);\n },\n };\n\n readonly cliLogin: ChannelCliLoginAdapter = {\n runLogin: async (params) => {\n const { feishuCliLoginAdapter } = await import('./adapters/cli-login.js');\n return feishuCliLoginAdapter.runLogin(params);\n },\n };\n\n readonly runtimeActions: ChannelRuntimeActionAdapter = feishuGatewaySetupActions;\n\n private bus!: MessageBus;\n private cfg!: Config;\n private abortControllers = new Map<string, AbortController>();\n private inboundPipeline?: ReturnType<typeof createFeishuInboundPipeline>;\n /** Unregister fn for the workflow-progress capability registered against the global broker. */\n private workflowProgressUnregister: (() => void) | null = null;\n\n config = {\n listAccountIds: (cfg: Config) => listFeishuAccountIds(cfg),\n resolveAccount: (cfg: Config, accountId?: string | null) => resolveFeishuAccount(cfg, accountId),\n isConfigured: async (account: ResolvedFeishuAccount) => account.configured,\n describeAccount: (account: ResolvedFeishuAccount) => ({\n accountId: account.accountId,\n channelId: 'feishu',\n enabled: account.enabled,\n configured: account.configured,\n status: account.configured ? undefined : 'unconfigured',\n }),\n };\n\n security: ChannelSecurityAdapter<ResolvedFeishuAccount> = {\n resolveDmPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveDmPolicy(account.dmPolicy, 'open'),\n resolveGroupPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveGroupPolicy(account.groupPolicy, 'allowlist'),\n checkAccess: (ctx: ChannelSecurityContext, account: ResolvedFeishuAccount, _cfg: Config) => {\n const isDm = !ctx.isGroup;\n const frameworkAllowFrom = readFrameworkAllowFromList(account.accountId);\n const baseAllowFrom = isDm ? account.allowFrom : account.groupAllowFrom ?? account.allowFrom;\n const allowFrom = [...(baseAllowFrom ?? []), ...frameworkAllowFrom];\n if (isDm) {\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: false,\n isDm: true,\n },\n dmPolicy: account.dmPolicy,\n allowFrom,\n });\n }\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: true,\n isDm: false,\n },\n groupPolicy: account.groupPolicy,\n allowFrom,\n });\n },\n };\n\n outbound: ChannelOutboundAdapter = createFeishuOutboundAdapter();\n\n streaming = createFeishuStreamingAdapter(() => this.cfg);\n\n status: ChannelStatusAdapter<ResolvedFeishuAccount> = createFeishuStatusAdapter();\n\n doctor: ChannelDoctorAdapter = createFeishuDoctorAdapter();\n\n directory = {\n resolveDisplayName: async (params) => {\n const { createFeishuDirectoryAdapter } = await import('./directory/directory-adapter.js');\n return createFeishuDirectoryAdapter().resolveDisplayName?.(params);\n },\n };\n\n actions: ChannelMessageActionAdapter = {\n handleAction: handleFeishuChannelMessageAction,\n };\n\n agentTools = [\n {\n name: 'feishu_read',\n description: 'Read a Feishu message by messageId (debug/utility).',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_read requires messageId');\n const { getMessageFeishu } = await import('./outbound/actions.js');\n return await getMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n },\n },\n {\n name: 'feishu_edit',\n description: 'Edit a Feishu message by messageId.',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n const text = typeof (args as any)?.text === 'string' ? (args as any).text : '';\n if (!messageId) throw new Error('feishu_edit requires messageId');\n if (!text.trim()) throw new Error('feishu_edit requires text');\n const { editMessageFeishu } = await import('./outbound/actions.js');\n return await editMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, text });\n },\n },\n {\n name: 'feishu_scopes_probe',\n description: 'Probe Feishu credentials/scopes (placeholder for full docs/wiki/drive tools).',\n execute: async (toolCtx) => {\n const { feishuWhoAmI } = await import('./tools/tools.js');\n return await feishuWhoAmI({ cfg: this.cfg, accountId: toolCtx.accountId });\n },\n },\n {\n name: 'feishu_react',\n description: 'Add/remove/list reactions for a message.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_react requires messageId');\n\n const account = resolveFeishuAccount(this.cfg, toolCtx.accountId ?? 'default');\n const enabled = (account.actions as any)?.reactions !== false;\n if (!enabled) {\n throw new Error('Feishu reactions are disabled via channels.feishu.actions.reactions');\n }\n\n if (a?.list === true) {\n const { listReactionsFeishu } = await import('./outbound/actions.js');\n return await listReactionsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n messageId,\n emojiType: typeof a?.emojiType === 'string' ? a.emojiType : undefined,\n });\n }\n\n if (a?.remove === true) {\n const reactionId = typeof a?.reactionId === 'string' ? a.reactionId : '';\n if (!reactionId) throw new Error('feishu_react remove requires reactionId');\n const { removeReactionFeishu } = await import('./outbound/actions.js');\n return await removeReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, reactionId });\n }\n\n const emojiType = typeof a?.emojiType === 'string' ? a.emojiType : '';\n if (!emojiType) throw new Error('feishu_react requires emojiType');\n const { addReactionFeishu } = await import('./outbound/actions.js');\n return await addReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, emojiType });\n },\n },\n {\n name: 'feishu_pins',\n description: 'Pin/unpin/list pins for a chat.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const action = typeof a?.action === 'string' ? a.action : '';\n if (action === 'list') {\n const chatId = typeof a?.chatId === 'string' ? a.chatId : toolCtx.chatId;\n if (!chatId) throw new Error('feishu_pins list requires chatId');\n const { listPinsFeishu } = await import('./outbound/actions.js');\n return await listPinsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n chatId,\n startTime: typeof a?.startTime === 'string' ? a.startTime : undefined,\n endTime: typeof a?.endTime === 'string' ? a.endTime : undefined,\n pageSize: typeof a?.pageSize === 'number' ? a.pageSize : undefined,\n pageToken: typeof a?.pageToken === 'string' ? a.pageToken : undefined,\n });\n }\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_pins requires messageId');\n if (action === 'pin') {\n const { pinMessageFeishu } = await import('./outbound/actions.js');\n return await pinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n if (action === 'unpin') {\n const { unpinMessageFeishu } = await import('./outbound/actions.js');\n return await unpinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n throw new Error('feishu_pins requires action: pin | unpin | list');\n },\n },\n ];\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n this.inboundPipeline = createFeishuInboundPipeline({\n bus: this.bus,\n defaultDebounceMs: defaultDebounce,\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Feishu inbound pipeline flush failed');\n },\n });\n\n await this.registerWorkflowProgressCapability();\n\n log.debug('Feishu plugin initialized');\n }\n\n private async registerWorkflowProgressCapability(): Promise<void> {\n // Lazy-load workflow progress so Feishu runtime startup does not pull the\n // agent/workflow/provider graph into Electron's dynamic extension path.\n const [{ getWorkflowProgressBroker }, { createFeishuWorkflowProgressCapability }] = await Promise.all([\n import('@xopcai/xopc/agent/workflow/progress-broker.js'),\n import('./workflow-progress.js'),\n ]);\n this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(\n createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }),\n );\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.feishu as FeishuConfig | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const ids = options?.accountId ? [options.accountId] : listFeishuAccountIds(this.cfg);\n for (const accountId of ids) {\n const account = resolveFeishuAccount(this.cfg, accountId);\n if (!account.enabled || !account.configured) continue;\n if (this.abortControllers.has(accountId)) continue;\n\n const ac = new AbortController();\n this.abortControllers.set(accountId, ac);\n\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n const enqueueInbound = (work: FeishuInboundWork) => {\n const p = this.inboundPipeline;\n if (!p) {\n return this.bus.publishInbound(work.inbound);\n }\n return p.enqueue(work);\n };\n\n const monitorDeps = {\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) => this.security.checkAccess?.(ctx, account, this.cfg),\n },\n };\n const runner =\n account.connectionMode === 'webhook'\n ? (await import('./transport/webhook/monitor.js')).createFeishuWebhookMonitor(monitorDeps)\n : (await import('./transport/socket-mode/monitor.js')).createFeishuSocketModeMonitor(monitorDeps);\n\n void runner.run().catch((err) => {\n if ((err as { name?: string } | undefined)?.name === 'AbortError') {\n log.debug({ accountId }, 'Feishu monitor stopped');\n return;\n }\n log.error({ err, accountId }, 'Feishu monitor exited with error');\n });\n\n log.info({ accountId, mode: account.connectionMode }, 'Feishu monitor started');\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n try {\n await this.inboundPipeline?.flushAll();\n } catch (err) {\n log.warn({ err }, 'Feishu inbound pipeline flushAll failed');\n }\n const ids = accountId ? [accountId] : [...this.abortControllers.keys()];\n for (const id of ids) {\n const ac = this.abortControllers.get(id);\n if (ac) {\n ac.abort();\n this.abortControllers.delete(id);\n }\n }\n // Only unregister the broker capability on a full stop. Per-account stops\n // still leave other accounts capable of delivering progress through the\n // same registered cap.\n if (!accountId) {\n this.workflowProgressUnregister?.();\n this.workflowProgressUnregister = null;\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n const ids = listFeishuAccountIds(cfg);\n return ids.some((id) => {\n const a = resolveFeishuAccount(cfg, id);\n return a.enabled !== false && a.configured && this.abortControllers.has(id);\n });\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prev = this.cfg.channels?.feishu as unknown;\n const next = cfg.channels?.feishu as { enabled?: boolean } | undefined;\n const channelOff = !next || next.enabled !== true;\n\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n return;\n }\n\n this.cfg = cfg;\n\n if (isDeepStrictEqual(prev, next) && this.channelIsRunning(cfg)) {\n return;\n }\n\n await this.stop();\n await this.start();\n }\n}\n\nexport const feishuPlugin = new FeishuChannelPlugin();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;aA+B4D;AAgB5D,MAAM,MAAM,aAAa,eAAe;AAExC,IAAa,sBAAb,MAAiF;CAC/E,KAAc;CAEd,SAA2C,EACzC,gBAAgB,CAAC,kBAAkB,EACpC;CAED,OAAgB;EACd,IAAI;EACJ,OAAO;EACP,gBAAgB;EAChB,UAAU;EACV,OAAO;EACP,OAAO;EACP,8BAA8B;EAC/B;CAED,eAA6C;EAC3C,WAAW,CAAC,UAAU,UAAU;EAChC,WAAW;EACX,SAAS;EACT,OAAO;EACP,OAAO;EACP,gBAAgB;EAChB,gBAAgB;EAChB,MAAM;EACN,OAAO;EACR;CAED,WAA2C;EACzC,OAAO,EAAE,YAAY,KAAK;EAC1B,UAAU,EAAE,gBAAgB,KAAM;EAClC,WAAW,EACT,wBAAwB;GACtB,UAAU;GACV,QAAQ;GACT,EACF;EACF;CAED,eAAwB;EACtB,QAAQ,EAAE;EACV,WAAW,QAAiB;GAC1B,MAAM,IAAI,mBAAmB,UAAU,IAAI;AAC3C,UAAO,EAAE,UAAU,EAAE,IAAI,MAAe,GAAG;IAAE,IAAI;IAAgB,QAAQ,CAAC,EAAE,MAAM,QAAQ;IAAE;;EAE/F;CAED,gBAAyB;CAEzB,UAAmB,6BAA6B,SAAS;CAEzD,UAAiC;EAC/B,eAAe,WAAW;GACxB,MAAM,SAAS,OAAO,UAAU;GAChC,MAAM,QAAQ,OAAO,QAAQ,UAAU,WAAW,OAAO,MAAM,MAAM,GAAG;GACxE,MAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,OAAO,UAAU,MAAM,GAAG;AACpF,UAAO,QAAQ,YAAY,QAAQ,QAAQ,SAAS,UAAU;;EAEhE,WAAW,OAAO,WAAW;GAC3B,MAAM,EAAE,yBAAyB,MAAM,OAAO;AAC9C,UAAO,qBAAqB,UAAU,OAAO;;EAEhD;CAED,WAA4C,EAC1C,UAAU,OAAO,WAAW;EAC1B,MAAM,EAAE,0BAA0B,MAAM,OAAO;AAC/C,SAAO,sBAAsB,SAAS,OAAO;IAEhD;CAED,iBAAuD;CAEvD;CACA;CACA,mCAA2B,IAAI,KAA8B;CAC7D;;CAEA,6BAA0D;CAE1D,SAAS;EACP,iBAAiB,QAAgB,qBAAqB,IAAI;EAC1D,iBAAiB,KAAa,cAA8B,qBAAqB,KAAK,UAAU;EAChG,cAAc,OAAO,YAAmC,QAAQ;EAChE,kBAAkB,aAAoC;GACpD,WAAW,QAAQ;GACnB,WAAW;GACX,SAAS,QAAQ;GACjB,YAAY,QAAQ;GACpB,QAAQ,QAAQ,aAAa,KAAA,IAAY;GAC1C;EACF;CAED,WAA0D;EACxD,kBAAkB,EAAE,cAClB,gBAAgB,QAAQ,UAAU,OAAO;EAC3C,qBAAqB,EAAE,cACrB,mBAAmB,QAAQ,aAAa,YAAY;EACtD,cAAc,KAA6B,SAAgC,SAAiB;GAC1F,MAAM,OAAO,CAAC,IAAI;GAClB,MAAM,qBAAqB,2BAA2B,QAAQ,UAAU;GAExE,MAAM,YAAY,CAAC,IADG,OAAO,QAAQ,YAAY,QAAQ,kBAAkB,QAAQ,cAC3C,EAAE,EAAG,GAAG,mBAAmB;AACnE,OAAI,KACF,QAAO,eAAe;IACpB,SAAS;KACP,SAAS;KACT,WAAW,QAAQ;KACnB,QAAQ,IAAI;KACZ,UAAU,IAAI;KACd,YAAY,IAAI;KAChB,SAAS;KACT,MAAM;KACP;IACD,UAAU,QAAQ;IAClB;IACD,CAAC;AAEJ,UAAO,eAAe;IACpB,SAAS;KACP,SAAS;KACT,WAAW,QAAQ;KACnB,QAAQ,IAAI;KACZ,UAAU,IAAI;KACd,YAAY,IAAI;KAChB,SAAS;KACT,MAAM;KACP;IACD,aAAa,QAAQ;IACrB;IACD,CAAC;;EAEL;CAED,WAAmC,6BAA6B;CAEhE,YAAY,mCAAmC,KAAK,IAAI;CAExD,SAAsD,2BAA2B;CAEjF,SAA+B,2BAA2B;CAE1D,YAAY,EACV,oBAAoB,OAAO,WAAW;EACpC,MAAM,EAAE,iCAAiC,MAAM,OAAO;AACtD,SAAO,8BAA8B,CAAC,qBAAqB,OAAO;IAErE;CAED,UAAuC,EACrC,cAAc,kCACf;CAED,aAAa;EACX;GACE,MAAM;GACN,aAAa;GACb,SAAS,OAAO,SAAS,SAAS;IAChC,MAAM,YAAY,OAAQ,MAAc,cAAc,WAAY,KAAa,YAAY,QAAQ;AACnG,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,iCAAiC;IACjE,MAAM,EAAE,qBAAqB,MAAM,OAAO;AAC1C,WAAO,MAAM,iBAAiB;KAAE,KAAK,KAAK;KAAK,WAAW,QAAQ;KAAW;KAAW,CAAC;;GAE5F;EACD;GACE,MAAM;GACN,aAAa;GACb,SAAS,OAAO,SAAS,SAAS;IAChC,MAAM,YAAY,OAAQ,MAAc,cAAc,WAAY,KAAa,YAAY,QAAQ;IACnG,MAAM,OAAO,OAAQ,MAAc,SAAS,WAAY,KAAa,OAAO;AAC5E,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACjE,QAAI,CAAC,KAAK,MAAM,CAAE,OAAM,IAAI,MAAM,4BAA4B;IAC9D,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,WAAO,MAAM,kBAAkB;KAAE,KAAK,KAAK;KAAK,WAAW,QAAQ;KAAW;KAAW;KAAM,CAAC;;GAEnG;EACD;GACE,MAAM;GACN,aAAa;GACb,SAAS,OAAO,YAAY;IAC1B,MAAM,EAAE,iBAAiB,MAAM,OAAO;AACtC,WAAO,MAAM,aAAa;KAAE,KAAK,KAAK;KAAK,WAAW,QAAQ;KAAW,CAAC;;GAE7E;EACD;GACE,MAAM;GACN,aAAa;GACb,SAAS,OAAO,SAAS,SAAS;IAChC,MAAM,IAAI;IACV,MAAM,YAAY,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY,QAAQ;AAC3E,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,kCAAkC;AAIlE,QAAI,EAFY,qBAAqB,KAAK,KAAK,QAAQ,aAAa,UAC5C,CAAC,SAAiB,cAAc,OAEtD,OAAM,IAAI,MAAM,sEAAsE;AAGxF,QAAI,GAAG,SAAS,MAAM;KACpB,MAAM,EAAE,wBAAwB,MAAM,OAAO;AAC7C,YAAO,MAAM,oBAAoB;MAC/B,KAAK,KAAK;MACV,WAAW,QAAQ;MACnB;MACA,WAAW,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY,KAAA;MAC7D,CAAC;;AAGJ,QAAI,GAAG,WAAW,MAAM;KACtB,MAAM,aAAa,OAAO,GAAG,eAAe,WAAW,EAAE,aAAa;AACtE,SAAI,CAAC,WAAY,OAAM,IAAI,MAAM,0CAA0C;KAC3E,MAAM,EAAE,yBAAyB,MAAM,OAAO;AAC9C,YAAO,MAAM,qBAAqB;MAAE,KAAK,KAAK;MAAK,WAAW,QAAQ;MAAW;MAAW;MAAY,CAAC;;IAG3G,MAAM,YAAY,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY;AACnE,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,kCAAkC;IAClE,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,WAAO,MAAM,kBAAkB;KAAE,KAAK,KAAK;KAAK,WAAW,QAAQ;KAAW;KAAW;KAAW,CAAC;;GAExG;EACD;GACE,MAAM;GACN,aAAa;GACb,SAAS,OAAO,SAAS,SAAS;IAChC,MAAM,IAAI;IACV,MAAM,SAAS,OAAO,GAAG,WAAW,WAAW,EAAE,SAAS;AAC1D,QAAI,WAAW,QAAQ;KACrB,MAAM,SAAS,OAAO,GAAG,WAAW,WAAW,EAAE,SAAS,QAAQ;AAClE,SAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,mCAAmC;KAChE,MAAM,EAAE,mBAAmB,MAAM,OAAO;AACxC,YAAO,MAAM,eAAe;MAC1B,KAAK,KAAK;MACV,WAAW,QAAQ;MACnB;MACA,WAAW,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY,KAAA;MAC5D,SAAS,OAAO,GAAG,YAAY,WAAW,EAAE,UAAU,KAAA;MACtD,UAAU,OAAO,GAAG,aAAa,WAAW,EAAE,WAAW,KAAA;MACzD,WAAW,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY,KAAA;MAC7D,CAAC;;IAEJ,MAAM,YAAY,OAAO,GAAG,cAAc,WAAW,EAAE,YAAY,QAAQ;AAC3E,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACjE,QAAI,WAAW,OAAO;KACpB,MAAM,EAAE,qBAAqB,MAAM,OAAO;AAC1C,YAAO,MAAM,iBAAiB;MAAE,KAAK,KAAK;MAAK,WAAW,QAAQ;MAAW;MAAW,CAAC;;AAE3F,QAAI,WAAW,SAAS;KACtB,MAAM,EAAE,uBAAuB,MAAM,OAAO;AAC5C,YAAO,MAAM,mBAAmB;MAAE,KAAK,KAAK;MAAK,WAAW,QAAQ;MAAW;MAAW,CAAC;;AAE7F,UAAM,IAAI,MAAM,kDAAkD;;GAErE;EACF;CAED,MAAM,KAAK,SAAkD;AAC3D,OAAK,MAAM,QAAQ;AACnB,OAAK,MAAM,QAAQ;EACnB,MAAM,kBAAkB,KAAK,SAAS,OAAO,cAAc;AAC3D,OAAK,kBAAkB,4BAA4B;GACjD,KAAK,KAAK;GACV,mBAAmB;GACnB,UAAU,KAAK,UAAU;AACvB,QAAI,MAAM;KAAE;KAAK,OAAO,MAAM;KAAQ,EAAE,uCAAuC;;GAElF,CAAC;AAEF,QAAM,KAAK,oCAAoC;AAE/C,MAAI,MAAM,4BAA4B;;CAGxC,MAAc,qCAAoD;EAGhE,MAAM,CAAC,EAAE,6BAA6B,EAAE,4CAA4C,MAAM,QAAQ,IAAI,CACpG,OAAO,mDACP,OAAO,0BACR,CAAC;AACF,OAAK,6BAA6B,2BAA2B,CAAC,gBAC5D,uCAAuC,EAAE,iBAAiB,KAAK,KAAK,CAAC,CACtE;;CAGH,MAAM,MAAM,SAAoD;EAC9D,MAAM,UAAU,KAAK,IAAI,UAAU;AACnC,MAAI,CAAC,WAAW,QAAQ,YAAY,KAClC;EAGF,MAAM,MAAM,SAAS,YAAY,CAAC,QAAQ,UAAU,GAAG,qBAAqB,KAAK,IAAI;AACrF,OAAK,MAAM,aAAa,KAAK;GAC3B,MAAM,UAAU,qBAAqB,KAAK,KAAK,UAAU;AACzD,OAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,WAAY;AAC7C,OAAI,KAAK,iBAAiB,IAAI,UAAU,CAAE;GAE1C,MAAM,KAAK,IAAI,iBAAiB;AAChC,QAAK,iBAAiB,IAAI,WAAW,GAAG;GAExC,MAAM,kBAAkB,KAAK,SAAS,OAAO,cAAc;GAC3D,MAAM,kBAAkB,SAA4B;IAClD,MAAM,IAAI,KAAK;AACf,QAAI,CAAC,EACH,QAAO,KAAK,IAAI,eAAe,KAAK,QAAQ;AAE9C,WAAO,EAAE,QAAQ,KAAK;;GAGxB,MAAM,cAAc;IAClB;IACA,QAAQ,KAAK;IACb;IACA,0BAA0B;IAC1B,aAAa,GAAG;IAChB,UAAU,EACR,cAAc,QAAgC,KAAK,SAAS,cAAc,KAAK,SAAS,KAAK,IAAI,EAClG;IACF;AAMD,IAJE,QAAQ,mBAAmB,aACtB,MAAM,OAAO,mCAAmC,2BAA2B,YAAY,IACvF,MAAM,OAAO,uCAAuC,8BAA8B,YAAY,EAEzF,KAAK,CAAC,OAAO,QAAQ;AAC/B,QAAK,KAAuC,SAAS,cAAc;AACjE,SAAI,MAAM,EAAE,WAAW,EAAE,yBAAyB;AAClD;;AAEF,QAAI,MAAM;KAAE;KAAK;KAAW,EAAE,mCAAmC;KACjE;AAEF,OAAI,KAAK;IAAE;IAAW,MAAM,QAAQ;IAAgB,EAAE,yBAAyB;;;CAInF,MAAM,KAAK,WAAmC;AAC5C,MAAI;AACF,SAAM,KAAK,iBAAiB,UAAU;WAC/B,KAAK;AACZ,OAAI,KAAK,EAAE,KAAK,EAAE,0CAA0C;;EAE9D,MAAM,MAAM,YAAY,CAAC,UAAU,GAAG,CAAC,GAAG,KAAK,iBAAiB,MAAM,CAAC;AACvE,OAAK,MAAM,MAAM,KAAK;GACpB,MAAM,KAAK,KAAK,iBAAiB,IAAI,GAAG;AACxC,OAAI,IAAI;AACN,OAAG,OAAO;AACV,SAAK,iBAAiB,OAAO,GAAG;;;AAMpC,MAAI,CAAC,WAAW;AACd,QAAK,8BAA8B;AACnC,QAAK,6BAA6B;;;CAItC,iBAAiB,KAAsB;AAErC,SADY,qBAAqB,IACvB,CAAC,MAAM,OAAO;GACtB,MAAM,IAAI,qBAAqB,KAAK,GAAG;AACvC,UAAO,EAAE,YAAY,SAAS,EAAE,cAAc,KAAK,iBAAiB,IAAI,GAAG;IAC3E;;CAGJ,MAAM,gBAAgB,KAA4B;EAChD,MAAM,OAAO,KAAK,IAAI,UAAU;EAChC,MAAM,OAAO,IAAI,UAAU;AAG3B,MAFmB,CAAC,QAAQ,KAAK,YAAY,MAE7B;AACd,QAAK,MAAM;AACX,SAAM,KAAK,MAAM;AACjB;;AAGF,OAAK,MAAM;AAEX,MAAI,kBAAkB,MAAM,KAAK,IAAI,KAAK,iBAAiB,IAAI,CAC7D;AAGF,QAAM,KAAK,MAAM;AACjB,QAAM,KAAK,OAAO;;;AAItB,MAAa,eAAe,IAAI,qBAAqB"}