@lark-project/openclaw-lark-project 2026.3.131
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.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/README.zh.md +80 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +7 -0
- package/dist/skills/feishu-bitable/SKILL.md +248 -0
- package/dist/skills/feishu-bitable/references/examples.md +813 -0
- package/dist/skills/feishu-bitable/references/field-properties.md +763 -0
- package/dist/skills/feishu-bitable/references/record-values.md +911 -0
- package/dist/skills/feishu-calendar/SKILL.md +244 -0
- package/dist/skills/feishu-channel-rules/SKILL.md +18 -0
- package/dist/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/dist/skills/feishu-create-doc/SKILL.md +719 -0
- package/dist/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/dist/skills/feishu-im-read/SKILL.md +163 -0
- package/dist/skills/feishu-project/SKILL.md +122 -0
- package/dist/skills/feishu-task/SKILL.md +293 -0
- package/dist/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/dist/skills/feishu-update-doc/SKILL.md +285 -0
- package/dist/src/card/builder.js +293 -0
- package/dist/src/card/builder.js.map +7 -0
- package/dist/src/card/cardkit.js +126 -0
- package/dist/src/card/cardkit.js.map +7 -0
- package/dist/src/card/flush-controller.js +107 -0
- package/dist/src/card/flush-controller.js.map +7 -0
- package/dist/src/card/markdown-style.js +57 -0
- package/dist/src/card/markdown-style.js.map +7 -0
- package/dist/src/card/reply-dispatcher-types.js +39 -0
- package/dist/src/card/reply-dispatcher-types.js.map +7 -0
- package/dist/src/card/reply-dispatcher.js +245 -0
- package/dist/src/card/reply-dispatcher.js.map +7 -0
- package/dist/src/card/reply-mode.js +29 -0
- package/dist/src/card/reply-mode.js.map +7 -0
- package/dist/src/card/streaming-card-controller.js +653 -0
- package/dist/src/card/streaming-card-controller.js.map +7 -0
- package/dist/src/card/unavailable-guard.js +76 -0
- package/dist/src/card/unavailable-guard.js.map +7 -0
- package/dist/src/channel/abort-detect.js +79 -0
- package/dist/src/channel/abort-detect.js.map +7 -0
- package/dist/src/channel/chat-queue.js +50 -0
- package/dist/src/channel/chat-queue.js.map +7 -0
- package/dist/src/channel/config-adapter.js +89 -0
- package/dist/src/channel/config-adapter.js.map +7 -0
- package/dist/src/channel/directory.js +133 -0
- package/dist/src/channel/directory.js.map +7 -0
- package/dist/src/channel/event-handlers.js +175 -0
- package/dist/src/channel/event-handlers.js.map +7 -0
- package/dist/src/channel/monitor.js +108 -0
- package/dist/src/channel/monitor.js.map +7 -0
- package/dist/src/channel/onboarding-config.js +76 -0
- package/dist/src/channel/onboarding-config.js.map +7 -0
- package/dist/src/channel/onboarding-migrate.js +55 -0
- package/dist/src/channel/onboarding-migrate.js.map +7 -0
- package/dist/src/channel/onboarding.js +285 -0
- package/dist/src/channel/onboarding.js.map +7 -0
- package/dist/src/channel/plugin.js +260 -0
- package/dist/src/channel/plugin.js.map +7 -0
- package/dist/src/channel/probe.js +14 -0
- package/dist/src/channel/probe.js.map +7 -0
- package/dist/src/channel/types.js +1 -0
- package/dist/src/channel/types.js.map +7 -0
- package/dist/src/commands/auth.js +73 -0
- package/dist/src/commands/auth.js.map +7 -0
- package/dist/src/commands/diagnose.js +658 -0
- package/dist/src/commands/diagnose.js.map +7 -0
- package/dist/src/commands/doctor.js +327 -0
- package/dist/src/commands/doctor.js.map +7 -0
- package/dist/src/commands/index.js +124 -0
- package/dist/src/commands/index.js.map +7 -0
- package/dist/src/core/accounts.js +129 -0
- package/dist/src/core/accounts.js.map +7 -0
- package/dist/src/core/agent-config.js +60 -0
- package/dist/src/core/agent-config.js.map +7 -0
- package/dist/src/core/api-error.js +55 -0
- package/dist/src/core/api-error.js.map +7 -0
- package/dist/src/core/app-owner-fallback.js +17 -0
- package/dist/src/core/app-owner-fallback.js.map +7 -0
- package/dist/src/core/app-scope-checker.js +95 -0
- package/dist/src/core/app-scope-checker.js.map +7 -0
- package/dist/src/core/auth-errors.js +120 -0
- package/dist/src/core/auth-errors.js.map +7 -0
- package/dist/src/core/chat-info-cache.js +102 -0
- package/dist/src/core/chat-info-cache.js.map +7 -0
- package/dist/src/core/config-schema.js +150 -0
- package/dist/src/core/config-schema.js.map +7 -0
- package/dist/src/core/device-flow.js +174 -0
- package/dist/src/core/device-flow.js.map +7 -0
- package/dist/src/core/feishu-fetch.js +12 -0
- package/dist/src/core/feishu-fetch.js.map +7 -0
- package/dist/src/core/footer-config.js +16 -0
- package/dist/src/core/footer-config.js.map +7 -0
- package/dist/src/core/lark-client.js +322 -0
- package/dist/src/core/lark-client.js.map +7 -0
- package/dist/src/core/lark-logger.js +92 -0
- package/dist/src/core/lark-logger.js.map +7 -0
- package/dist/src/core/lark-ticket.js +18 -0
- package/dist/src/core/lark-ticket.js.map +7 -0
- package/dist/src/core/message-unavailable.js +119 -0
- package/dist/src/core/message-unavailable.js.map +7 -0
- package/dist/src/core/owner-policy.js +25 -0
- package/dist/src/core/owner-policy.js.map +7 -0
- package/dist/src/core/permission-url.js +37 -0
- package/dist/src/core/permission-url.js.map +7 -0
- package/dist/src/core/project-auth.js +177 -0
- package/dist/src/core/project-auth.js.map +7 -0
- package/dist/src/core/project-oauth-flow.js +124 -0
- package/dist/src/core/project-oauth-flow.js.map +7 -0
- package/dist/src/core/project-token-store.js +172 -0
- package/dist/src/core/project-token-store.js.map +7 -0
- package/dist/src/core/raw-request.js +45 -0
- package/dist/src/core/raw-request.js.map +7 -0
- package/dist/src/core/scope-manager.js +62 -0
- package/dist/src/core/scope-manager.js.map +7 -0
- package/dist/src/core/security-check.js +118 -0
- package/dist/src/core/security-check.js.map +7 -0
- package/dist/src/core/shutdown-hooks.js +37 -0
- package/dist/src/core/shutdown-hooks.js.map +7 -0
- package/dist/src/core/targets.js +55 -0
- package/dist/src/core/targets.js.map +7 -0
- package/dist/src/core/token-store.js +215 -0
- package/dist/src/core/token-store.js.map +7 -0
- package/dist/src/core/tool-client.js +335 -0
- package/dist/src/core/tool-client.js.map +7 -0
- package/dist/src/core/tool-scopes.js +207 -0
- package/dist/src/core/tool-scopes.js.map +7 -0
- package/dist/src/core/tools-config.js +57 -0
- package/dist/src/core/tools-config.js.map +7 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/core/types.js.map +7 -0
- package/dist/src/core/uat-client.js +124 -0
- package/dist/src/core/uat-client.js.map +7 -0
- package/dist/src/core/version.js +27 -0
- package/dist/src/core/version.js.map +7 -0
- package/dist/src/messaging/converters/audio.js +19 -0
- package/dist/src/messaging/converters/audio.js.map +7 -0
- package/dist/src/messaging/converters/calendar.js +46 -0
- package/dist/src/messaging/converters/calendar.js.map +7 -0
- package/dist/src/messaging/converters/content-converter.js +61 -0
- package/dist/src/messaging/converters/content-converter.js.map +7 -0
- package/dist/src/messaging/converters/file.js +18 -0
- package/dist/src/messaging/converters/file.js.map +7 -0
- package/dist/src/messaging/converters/folder.js +18 -0
- package/dist/src/messaging/converters/folder.js.map +7 -0
- package/dist/src/messaging/converters/hongbao.js +14 -0
- package/dist/src/messaging/converters/hongbao.js.map +7 -0
- package/dist/src/messaging/converters/image.js +16 -0
- package/dist/src/messaging/converters/image.js.map +7 -0
- package/dist/src/messaging/converters/index.js +48 -0
- package/dist/src/messaging/converters/index.js.map +7 -0
- package/dist/src/messaging/converters/interactive/card-converter.js +1040 -0
- package/dist/src/messaging/converters/interactive/card-converter.js.map +7 -0
- package/dist/src/messaging/converters/interactive/card-utils.js +36 -0
- package/dist/src/messaging/converters/interactive/card-utils.js.map +7 -0
- package/dist/src/messaging/converters/interactive/index.js +19 -0
- package/dist/src/messaging/converters/interactive/index.js.map +7 -0
- package/dist/src/messaging/converters/interactive/legacy.js +53 -0
- package/dist/src/messaging/converters/interactive/legacy.js.map +7 -0
- package/dist/src/messaging/converters/interactive/types.js +23 -0
- package/dist/src/messaging/converters/interactive/types.js.map +7 -0
- package/dist/src/messaging/converters/location.js +17 -0
- package/dist/src/messaging/converters/location.js.map +7 -0
- package/dist/src/messaging/converters/merge-forward.js +143 -0
- package/dist/src/messaging/converters/merge-forward.js.map +7 -0
- package/dist/src/messaging/converters/post.js +113 -0
- package/dist/src/messaging/converters/post.js.map +7 -0
- package/dist/src/messaging/converters/share.js +22 -0
- package/dist/src/messaging/converters/share.js.map +7 -0
- package/dist/src/messaging/converters/sticker.js +16 -0
- package/dist/src/messaging/converters/sticker.js.map +7 -0
- package/dist/src/messaging/converters/system.js +25 -0
- package/dist/src/messaging/converters/system.js.map +7 -0
- package/dist/src/messaging/converters/text.js +12 -0
- package/dist/src/messaging/converters/text.js.map +7 -0
- package/dist/src/messaging/converters/todo.js +37 -0
- package/dist/src/messaging/converters/todo.js.map +7 -0
- package/dist/src/messaging/converters/types.js +1 -0
- package/dist/src/messaging/converters/types.js.map +7 -0
- package/dist/src/messaging/converters/unknown.js +13 -0
- package/dist/src/messaging/converters/unknown.js.map +7 -0
- package/dist/src/messaging/converters/utils.js +35 -0
- package/dist/src/messaging/converters/utils.js.map +7 -0
- package/dist/src/messaging/converters/video-chat.js +21 -0
- package/dist/src/messaging/converters/video-chat.js.map +7 -0
- package/dist/src/messaging/converters/video.js +30 -0
- package/dist/src/messaging/converters/video.js.map +7 -0
- package/dist/src/messaging/converters/vote.js +24 -0
- package/dist/src/messaging/converters/vote.js.map +7 -0
- package/dist/src/messaging/inbound/dedup.js +82 -0
- package/dist/src/messaging/inbound/dedup.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-builders.js +98 -0
- package/dist/src/messaging/inbound/dispatch-builders.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-commands.js +94 -0
- package/dist/src/messaging/inbound/dispatch-commands.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-context.js +96 -0
- package/dist/src/messaging/inbound/dispatch-context.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch.js +150 -0
- package/dist/src/messaging/inbound/dispatch.js.map +7 -0
- package/dist/src/messaging/inbound/enrich.js +137 -0
- package/dist/src/messaging/inbound/enrich.js.map +7 -0
- package/dist/src/messaging/inbound/gate-effects.js +28 -0
- package/dist/src/messaging/inbound/gate-effects.js.map +7 -0
- package/dist/src/messaging/inbound/gate.js +163 -0
- package/dist/src/messaging/inbound/gate.js.map +7 -0
- package/dist/src/messaging/inbound/handler.js +132 -0
- package/dist/src/messaging/inbound/handler.js.map +7 -0
- package/dist/src/messaging/inbound/media-resolver.js +70 -0
- package/dist/src/messaging/inbound/media-resolver.js.map +7 -0
- package/dist/src/messaging/inbound/mention.js +50 -0
- package/dist/src/messaging/inbound/mention.js.map +7 -0
- package/dist/src/messaging/inbound/parse-io.js +41 -0
- package/dist/src/messaging/inbound/parse-io.js.map +7 -0
- package/dist/src/messaging/inbound/parse.js +79 -0
- package/dist/src/messaging/inbound/parse.js.map +7 -0
- package/dist/src/messaging/inbound/permission.js +30 -0
- package/dist/src/messaging/inbound/permission.js.map +7 -0
- package/dist/src/messaging/inbound/policy.js +83 -0
- package/dist/src/messaging/inbound/policy.js.map +7 -0
- package/dist/src/messaging/inbound/reaction-handler.js +162 -0
- package/dist/src/messaging/inbound/reaction-handler.js.map +7 -0
- package/dist/src/messaging/inbound/user-name-cache.js +172 -0
- package/dist/src/messaging/inbound/user-name-cache.js.map +7 -0
- package/dist/src/messaging/outbound/actions.js +239 -0
- package/dist/src/messaging/outbound/actions.js.map +7 -0
- package/dist/src/messaging/outbound/chat-manage.js +74 -0
- package/dist/src/messaging/outbound/chat-manage.js.map +7 -0
- package/dist/src/messaging/outbound/deliver.js +162 -0
- package/dist/src/messaging/outbound/deliver.js.map +7 -0
- package/dist/src/messaging/outbound/fetch.js +7 -0
- package/dist/src/messaging/outbound/fetch.js.map +7 -0
- package/dist/src/messaging/outbound/forward.js +31 -0
- package/dist/src/messaging/outbound/forward.js.map +7 -0
- package/dist/src/messaging/outbound/media-url-utils.js +101 -0
- package/dist/src/messaging/outbound/media-url-utils.js.map +7 -0
- package/dist/src/messaging/outbound/media.js +463 -0
- package/dist/src/messaging/outbound/media.js.map +7 -0
- package/dist/src/messaging/outbound/outbound.js +95 -0
- package/dist/src/messaging/outbound/outbound.js.map +7 -0
- package/dist/src/messaging/outbound/reactions.js +312 -0
- package/dist/src/messaging/outbound/reactions.js.map +7 -0
- package/dist/src/messaging/outbound/send.js +194 -0
- package/dist/src/messaging/outbound/send.js.map +7 -0
- package/dist/src/messaging/outbound/typing.js +77 -0
- package/dist/src/messaging/outbound/typing.js.map +7 -0
- package/dist/src/messaging/shared/message-lookup.js +84 -0
- package/dist/src/messaging/shared/message-lookup.js.map +7 -0
- package/dist/src/messaging/types.js +1 -0
- package/dist/src/messaging/types.js.map +7 -0
- package/dist/src/tools/auto-auth.js +714 -0
- package/dist/src/tools/auto-auth.js.map +7 -0
- package/dist/src/tools/helpers.js +133 -0
- package/dist/src/tools/helpers.js.map +7 -0
- package/dist/src/tools/mcp/doc/create.js +35 -0
- package/dist/src/tools/mcp/doc/create.js.map +7 -0
- package/dist/src/tools/mcp/doc/fetch.js +33 -0
- package/dist/src/tools/mcp/doc/fetch.js.map +7 -0
- package/dist/src/tools/mcp/doc/index.js +32 -0
- package/dist/src/tools/mcp/doc/index.js.map +7 -0
- package/dist/src/tools/mcp/doc/update.js +61 -0
- package/dist/src/tools/mcp/doc/update.js.map +7 -0
- package/dist/src/tools/mcp/project/endpoint.js +25 -0
- package/dist/src/tools/mcp/project/endpoint.js.map +7 -0
- package/dist/src/tools/mcp/project/index.js +27 -0
- package/dist/src/tools/mcp/project/index.js.map +7 -0
- package/dist/src/tools/mcp/project/tools.js +579 -0
- package/dist/src/tools/mcp/project/tools.js.map +7 -0
- package/dist/src/tools/mcp/shared.js +170 -0
- package/dist/src/tools/mcp/shared.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-field.js +244 -0
- package/dist/src/tools/oapi/bitable/app-table-field.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-record.js +501 -0
- package/dist/src/tools/oapi/bitable/app-table-record.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-view.js +226 -0
- package/dist/src/tools/oapi/bitable/app-table-view.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table.js +278 -0
- package/dist/src/tools/oapi/bitable/app-table.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app.js +200 -0
- package/dist/src/tools/oapi/bitable/app.js.map +7 -0
- package/dist/src/tools/oapi/bitable/index.js +13 -0
- package/dist/src/tools/oapi/bitable/index.js.map +7 -0
- package/dist/src/tools/oapi/calendar/calendar.js +131 -0
- package/dist/src/tools/oapi/calendar/calendar.js.map +7 -0
- package/dist/src/tools/oapi/calendar/event-attendee.js +301 -0
- package/dist/src/tools/oapi/calendar/event-attendee.js.map +7 -0
- package/dist/src/tools/oapi/calendar/event.js +834 -0
- package/dist/src/tools/oapi/calendar/event.js.map +7 -0
- package/dist/src/tools/oapi/calendar/freebusy.js +111 -0
- package/dist/src/tools/oapi/calendar/freebusy.js.map +7 -0
- package/dist/src/tools/oapi/calendar/index.js +11 -0
- package/dist/src/tools/oapi/calendar/index.js.map +7 -0
- package/dist/src/tools/oapi/chat/chat.js +132 -0
- package/dist/src/tools/oapi/chat/chat.js.map +7 -0
- package/dist/src/tools/oapi/chat/index.js +11 -0
- package/dist/src/tools/oapi/chat/index.js.map +7 -0
- package/dist/src/tools/oapi/chat/members.js +83 -0
- package/dist/src/tools/oapi/chat/members.js.map +7 -0
- package/dist/src/tools/oapi/common/get-user.js +95 -0
- package/dist/src/tools/oapi/common/get-user.js.map +7 -0
- package/dist/src/tools/oapi/common/index.js +7 -0
- package/dist/src/tools/oapi/common/index.js.map +7 -0
- package/dist/src/tools/oapi/common/search-user.js +67 -0
- package/dist/src/tools/oapi/common/search-user.js.map +7 -0
- package/dist/src/tools/oapi/drive/doc-comments.js +310 -0
- package/dist/src/tools/oapi/drive/doc-comments.js.map +7 -0
- package/dist/src/tools/oapi/drive/doc-media.js +314 -0
- package/dist/src/tools/oapi/drive/doc-media.js.map +7 -0
- package/dist/src/tools/oapi/drive/file.js +548 -0
- package/dist/src/tools/oapi/drive/file.js.map +7 -0
- package/dist/src/tools/oapi/drive/index.js +29 -0
- package/dist/src/tools/oapi/drive/index.js.map +7 -0
- package/dist/src/tools/oapi/helpers.js +199 -0
- package/dist/src/tools/oapi/helpers.js.map +7 -0
- package/dist/src/tools/oapi/im/format-messages.js +128 -0
- package/dist/src/tools/oapi/im/format-messages.js.map +7 -0
- package/dist/src/tools/oapi/im/index.js +15 -0
- package/dist/src/tools/oapi/im/index.js.map +7 -0
- package/dist/src/tools/oapi/im/message-read.js +404 -0
- package/dist/src/tools/oapi/im/message-read.js.map +7 -0
- package/dist/src/tools/oapi/im/message.js +179 -0
- package/dist/src/tools/oapi/im/message.js.map +7 -0
- package/dist/src/tools/oapi/im/resource.js +126 -0
- package/dist/src/tools/oapi/im/resource.js.map +7 -0
- package/dist/src/tools/oapi/im/time-utils.js +169 -0
- package/dist/src/tools/oapi/im/time-utils.js.map +7 -0
- package/dist/src/tools/oapi/im/user-name-uat.js +103 -0
- package/dist/src/tools/oapi/im/user-name-uat.js.map +7 -0
- package/dist/src/tools/oapi/index.js +56 -0
- package/dist/src/tools/oapi/index.js.map +7 -0
- package/dist/src/tools/oapi/sdk-types.js +1 -0
- package/dist/src/tools/oapi/sdk-types.js.map +7 -0
- package/dist/src/tools/oapi/search/doc-search.js +215 -0
- package/dist/src/tools/oapi/search/doc-search.js.map +7 -0
- package/dist/src/tools/oapi/search/index.js +25 -0
- package/dist/src/tools/oapi/search/index.js.map +7 -0
- package/dist/src/tools/oapi/sheets/index.js +25 -0
- package/dist/src/tools/oapi/sheets/index.js.map +7 -0
- package/dist/src/tools/oapi/sheets/sheet.js +652 -0
- package/dist/src/tools/oapi/sheets/sheet.js.map +7 -0
- package/dist/src/tools/oapi/task/comment.js +151 -0
- package/dist/src/tools/oapi/task/comment.js.map +7 -0
- package/dist/src/tools/oapi/task/index.js +11 -0
- package/dist/src/tools/oapi/task/index.js.map +7 -0
- package/dist/src/tools/oapi/task/subtask.js +175 -0
- package/dist/src/tools/oapi/task/subtask.js.map +7 -0
- package/dist/src/tools/oapi/task/task.js +405 -0
- package/dist/src/tools/oapi/task/task.js.map +7 -0
- package/dist/src/tools/oapi/task/tasklist.js +366 -0
- package/dist/src/tools/oapi/task/tasklist.js.map +7 -0
- package/dist/src/tools/oapi/wiki/index.js +27 -0
- package/dist/src/tools/oapi/wiki/index.js.map +7 -0
- package/dist/src/tools/oapi/wiki/space-node.js +311 -0
- package/dist/src/tools/oapi/wiki/space-node.js.map +7 -0
- package/dist/src/tools/oapi/wiki/space.js +148 -0
- package/dist/src/tools/oapi/wiki/space.js.map +7 -0
- package/dist/src/tools/oauth-batch-auth.js +125 -0
- package/dist/src/tools/oauth-batch-auth.js.map +7 -0
- package/dist/src/tools/oauth-cards.js +269 -0
- package/dist/src/tools/oauth-cards.js.map +7 -0
- package/dist/src/tools/oauth.js +538 -0
- package/dist/src/tools/oauth.js.map +7 -0
- package/dist/src/tools/onboarding-auth.js +101 -0
- package/dist/src/tools/onboarding-auth.js.map +7 -0
- package/dist/src/tools/project-oauth.js +305 -0
- package/dist/src/tools/project-oauth.js.map +7 -0
- package/dist/src/tools/tat/im/index.js +9 -0
- package/dist/src/tools/tat/im/index.js.map +7 -0
- package/dist/src/tools/tat/im/resource.js +123 -0
- package/dist/src/tools/tat/im/resource.js.map +7 -0
- package/package.json +64 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
import { getTicket } from "../core/lark-ticket";
|
|
2
|
+
import { larkLogger } from "../core/lark-logger";
|
|
3
|
+
const log = larkLogger("tools/auto-auth");
|
|
4
|
+
import { getLarkAccount } from "../core/accounts";
|
|
5
|
+
import { UserAuthRequiredError, UserScopeInsufficientError, AppScopeMissingError } from "../core/tool-client";
|
|
6
|
+
import { invalidateAppScopeCache, getAppGrantedScopes, isAppScopeSatisfied } from "../core/app-scope-checker";
|
|
7
|
+
import { LarkClient } from "../core/lark-client";
|
|
8
|
+
import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit";
|
|
9
|
+
import { executeAuthorize } from "./oauth";
|
|
10
|
+
import { formatLarkError, json } from "./oapi/helpers";
|
|
11
|
+
import { OwnerAccessDeniedError } from "../core/owner-policy";
|
|
12
|
+
import { enqueueFeishuChatTask } from "../channel/chat-queue";
|
|
13
|
+
import { handleFeishuMessage } from "../messaging/inbound/handler";
|
|
14
|
+
import { withTicket } from "../core/lark-ticket";
|
|
15
|
+
const authBatches = /* @__PURE__ */ new Map();
|
|
16
|
+
const AUTH_DEBOUNCE_MS = 50;
|
|
17
|
+
const AUTH_USER_DEBOUNCE_MS = 150;
|
|
18
|
+
const AUTH_UPDATE_DEBOUNCE_MS = 500;
|
|
19
|
+
const AUTH_COOLDOWN_MS = 3e4;
|
|
20
|
+
function enqueueAuthRequest(bufferKey, scopes, ctx, flushFn, debounceMs = AUTH_DEBOUNCE_MS) {
|
|
21
|
+
const existing = authBatches.get(bufferKey);
|
|
22
|
+
if (existing) {
|
|
23
|
+
for (const s of scopes) existing.scopes.add(s);
|
|
24
|
+
if (existing.phase === "executing") {
|
|
25
|
+
log.info(`auth in-flight, piggyback \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(", ")}]`);
|
|
26
|
+
if (existing.updateTimer) clearTimeout(existing.updateTimer);
|
|
27
|
+
existing.updateTimer = setTimeout(async () => {
|
|
28
|
+
existing.updateTimer = null;
|
|
29
|
+
if (existing.isUpdating) {
|
|
30
|
+
existing.pendingReupdate = true;
|
|
31
|
+
log.info(`scope update deferred (previous update still running) \u2192 key=${bufferKey}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
existing.isUpdating = true;
|
|
35
|
+
try {
|
|
36
|
+
const mergedScopes = [...existing.scopes];
|
|
37
|
+
log.info(`scope update flush \u2192 key=${bufferKey}, scopes=[${mergedScopes.join(", ")}]`);
|
|
38
|
+
await existing.flushFn(mergedScopes);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
log.warn(`scope update failed: ${err}`);
|
|
41
|
+
} finally {
|
|
42
|
+
existing.isUpdating = false;
|
|
43
|
+
if (existing.pendingReupdate) {
|
|
44
|
+
existing.pendingReupdate = false;
|
|
45
|
+
const finalScopes = [...existing.scopes];
|
|
46
|
+
log.info(`scope reupdate \u2192 key=${bufferKey}, scopes=[${finalScopes.join(", ")}]`);
|
|
47
|
+
try {
|
|
48
|
+
await existing.flushFn(finalScopes);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
log.warn(`scope reupdate failed: ${err}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, AUTH_UPDATE_DEBOUNCE_MS);
|
|
55
|
+
return existing.resultPromise;
|
|
56
|
+
}
|
|
57
|
+
log.info(`debounce merge \u2192 key=${bufferKey}, scopes=[${[...existing.scopes].join(", ")}]`);
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
existing.waiters.push({ resolve, reject });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const entry = {
|
|
63
|
+
phase: "collecting",
|
|
64
|
+
scopes: new Set(scopes),
|
|
65
|
+
waiters: [],
|
|
66
|
+
timer: null,
|
|
67
|
+
resultPromise: null,
|
|
68
|
+
updateTimer: null,
|
|
69
|
+
isUpdating: false,
|
|
70
|
+
pendingReupdate: false,
|
|
71
|
+
flushFn: null,
|
|
72
|
+
account: ctx.account,
|
|
73
|
+
cfg: ctx.cfg,
|
|
74
|
+
ticket: ctx.ticket
|
|
75
|
+
};
|
|
76
|
+
const promise = new Promise((resolve, reject) => {
|
|
77
|
+
entry.waiters.push({ resolve, reject });
|
|
78
|
+
});
|
|
79
|
+
entry.timer = setTimeout(async () => {
|
|
80
|
+
entry.phase = "executing";
|
|
81
|
+
entry.timer = null;
|
|
82
|
+
entry.flushFn = flushFn;
|
|
83
|
+
const mergedScopes = [...entry.scopes];
|
|
84
|
+
log.info(
|
|
85
|
+
`debounce flush \u2192 key=${bufferKey}, waiters=${entry.waiters.length}, scopes=[${mergedScopes.join(", ")}]`
|
|
86
|
+
);
|
|
87
|
+
entry.resultPromise = flushFn(mergedScopes);
|
|
88
|
+
try {
|
|
89
|
+
const result = await entry.resultPromise;
|
|
90
|
+
for (const w of entry.waiters) w.resolve(result);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
for (const w of entry.waiters) w.reject(err);
|
|
93
|
+
} finally {
|
|
94
|
+
setTimeout(() => authBatches.delete(bufferKey), AUTH_COOLDOWN_MS);
|
|
95
|
+
}
|
|
96
|
+
}, debounceMs);
|
|
97
|
+
authBatches.set(bufferKey, entry);
|
|
98
|
+
return promise;
|
|
99
|
+
}
|
|
100
|
+
const PENDING_FLOW_TTL_MS = 15 * 60 * 1e3;
|
|
101
|
+
function makeDedupKey(chatId, messageId, scopes) {
|
|
102
|
+
return chatId + "\0" + messageId + "\0" + [...scopes].sort().join(",");
|
|
103
|
+
}
|
|
104
|
+
class AppAuthFlowManager {
|
|
105
|
+
flows = /* @__PURE__ */ new Map();
|
|
106
|
+
dedupIndex = /* @__PURE__ */ new Map();
|
|
107
|
+
activeCardIndex = /* @__PURE__ */ new Map();
|
|
108
|
+
/** 原子注册新流程(同时写入 3 个索引 + 设置统一 TTL) */
|
|
109
|
+
register(operationId, flow, dedupKey, activeCardKey) {
|
|
110
|
+
const registered = { ...flow, dedupKey, activeCardKey };
|
|
111
|
+
this.flows.set(operationId, registered);
|
|
112
|
+
this.dedupIndex.set(dedupKey, operationId);
|
|
113
|
+
this.activeCardIndex.set(activeCardKey, operationId);
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
if (!this.flows.has(operationId)) return;
|
|
116
|
+
this.remove(operationId);
|
|
117
|
+
}, PENDING_FLOW_TTL_MS);
|
|
118
|
+
}
|
|
119
|
+
/** 只需 operationId 即可原子清理所有索引 */
|
|
120
|
+
remove(operationId) {
|
|
121
|
+
const flow = this.flows.get(operationId);
|
|
122
|
+
if (!flow) return;
|
|
123
|
+
if (flow.ticket?.senderOpenId) {
|
|
124
|
+
const deferKey = `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;
|
|
125
|
+
deferredUserAuth.delete(deferKey);
|
|
126
|
+
}
|
|
127
|
+
this.flows.delete(operationId);
|
|
128
|
+
if (this.dedupIndex.get(flow.dedupKey) === operationId) {
|
|
129
|
+
this.dedupIndex.delete(flow.dedupKey);
|
|
130
|
+
}
|
|
131
|
+
if (this.activeCardIndex.get(flow.activeCardKey) === operationId) {
|
|
132
|
+
this.activeCardIndex.delete(flow.activeCardKey);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 迁移到新 operationId(卡片复用场景:按钮回调需要匹配新 ID)。
|
|
137
|
+
* 原子操作:清理旧索引 → 更新 flow → 建立新索引 → 注册新 TTL。
|
|
138
|
+
*
|
|
139
|
+
* 修复原代码卡片复用路径缺少 TTL 注册导致的内存泄漏。
|
|
140
|
+
*/
|
|
141
|
+
migrateToNewOperationId(oldOperationId, newOperationId, updates) {
|
|
142
|
+
const flow = this.flows.get(oldOperationId);
|
|
143
|
+
if (!flow) return void 0;
|
|
144
|
+
this.flows.delete(oldOperationId);
|
|
145
|
+
if (updates?.dedupKey) {
|
|
146
|
+
if (this.dedupIndex.get(flow.dedupKey) === oldOperationId) {
|
|
147
|
+
this.dedupIndex.delete(flow.dedupKey);
|
|
148
|
+
}
|
|
149
|
+
flow.dedupKey = updates.dedupKey;
|
|
150
|
+
}
|
|
151
|
+
if (updates?.requiredScopes) flow.requiredScopes = updates.requiredScopes;
|
|
152
|
+
if (updates?.scopeNeedType) flow.scopeNeedType = updates.scopeNeedType;
|
|
153
|
+
this.flows.set(newOperationId, flow);
|
|
154
|
+
this.dedupIndex.set(flow.dedupKey, newOperationId);
|
|
155
|
+
this.activeCardIndex.set(flow.activeCardKey, newOperationId);
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
if (!this.flows.has(newOperationId)) return;
|
|
158
|
+
this.remove(newOperationId);
|
|
159
|
+
}, PENDING_FLOW_TTL_MS);
|
|
160
|
+
return flow;
|
|
161
|
+
}
|
|
162
|
+
/** 通过 operationId 查询(card action 回调用) */
|
|
163
|
+
getByOperationId(id) {
|
|
164
|
+
return this.flows.get(id);
|
|
165
|
+
}
|
|
166
|
+
/** 通过去重键查询(避免发送重复卡片) */
|
|
167
|
+
getByDedupKey(key) {
|
|
168
|
+
const opId = this.dedupIndex.get(key);
|
|
169
|
+
if (!opId) return void 0;
|
|
170
|
+
const flow = this.flows.get(opId);
|
|
171
|
+
return flow ? { operationId: opId, flow } : void 0;
|
|
172
|
+
}
|
|
173
|
+
/** 通过活跃卡片键查询(同消息卡片复用) */
|
|
174
|
+
getByActiveCardKey(key) {
|
|
175
|
+
const opId = this.activeCardIndex.get(key);
|
|
176
|
+
if (!opId) return void 0;
|
|
177
|
+
const flow = this.flows.get(opId);
|
|
178
|
+
return flow ? { operationId: opId, flow } : void 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const appAuthFlows = new AppAuthFlowManager();
|
|
182
|
+
const deferredUserAuth = /* @__PURE__ */ new Map();
|
|
183
|
+
function hasActiveAppAuthForMessage(ticket) {
|
|
184
|
+
const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
|
|
185
|
+
const appEntry = authBatches.get(appKey);
|
|
186
|
+
if (appEntry && (appEntry.phase === "collecting" || appEntry.phase === "executing")) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
const activeCardKey = `${ticket.chatId}:${ticket.messageId}`;
|
|
190
|
+
return !!appAuthFlows.getByActiveCardKey(activeCardKey);
|
|
191
|
+
}
|
|
192
|
+
function addToDeferredUserAuth(ticket, scopes, account, cfg) {
|
|
193
|
+
const key = `${ticket.accountId}:${ticket.senderOpenId}:${ticket.messageId}`;
|
|
194
|
+
const existing = deferredUserAuth.get(key);
|
|
195
|
+
if (existing) {
|
|
196
|
+
for (const s of scopes) existing.scopes.add(s);
|
|
197
|
+
log.info(`deferred user auth scope merge \u2192 key=${key}, scopes=[${[...existing.scopes].join(", ")}]`);
|
|
198
|
+
} else {
|
|
199
|
+
deferredUserAuth.set(key, { scopes: new Set(scopes), account, cfg, ticket });
|
|
200
|
+
log.info(`deferred user auth created \u2192 key=${key}, scopes=[${scopes.join(", ")}]`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function buildAppScopeMissingCard(params) {
|
|
204
|
+
const { missingScopes, appId, operationId } = params;
|
|
205
|
+
const authUrl = appId ? `https://open.feishu.cn/app/${appId}/auth?q=${encodeURIComponent(missingScopes.join(","))}&op_from=feishu-openclaw&token_type=user` : "https://open.feishu.cn/";
|
|
206
|
+
const multiUrl = { url: authUrl, pc_url: authUrl, android_url: authUrl, ios_url: authUrl };
|
|
207
|
+
const scopeList = missingScopes.map((s) => `\u2022 ${s}`).join("\n");
|
|
208
|
+
return {
|
|
209
|
+
schema: "2.0",
|
|
210
|
+
config: { wide_screen_mode: true },
|
|
211
|
+
header: {
|
|
212
|
+
title: { tag: "plain_text", content: "\u{1F510} \u9700\u8981\u7533\u8BF7\u6743\u9650\u624D\u80FD\u7EE7\u7EED" },
|
|
213
|
+
template: "orange"
|
|
214
|
+
},
|
|
215
|
+
body: {
|
|
216
|
+
elements: [
|
|
217
|
+
{
|
|
218
|
+
tag: "markdown",
|
|
219
|
+
content: "\u8C03\u7528\u524D\uFF0C\u8BF7\u4F60\u5148\u7533\u8BF7\u4EE5\u4E0B**\u6240\u6709**\u6743\u9650\uFF1A",
|
|
220
|
+
text_size: "normal"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
tag: "column_set",
|
|
224
|
+
flex_mode: "none",
|
|
225
|
+
background_style: "grey",
|
|
226
|
+
horizontal_spacing: "default",
|
|
227
|
+
columns: [
|
|
228
|
+
{
|
|
229
|
+
tag: "column",
|
|
230
|
+
width: "weighted",
|
|
231
|
+
weight: 1,
|
|
232
|
+
vertical_align: "center",
|
|
233
|
+
elements: [{ tag: "markdown", content: scopeList }]
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
{ tag: "hr" },
|
|
238
|
+
{
|
|
239
|
+
tag: "column_set",
|
|
240
|
+
flex_mode: "none",
|
|
241
|
+
horizontal_spacing: "default",
|
|
242
|
+
columns: [
|
|
243
|
+
{
|
|
244
|
+
tag: "column",
|
|
245
|
+
width: "weighted",
|
|
246
|
+
weight: 3,
|
|
247
|
+
vertical_align: "center",
|
|
248
|
+
elements: [{ tag: "markdown", content: "**\u7B2C\u4E00\u6B65\uFF1A\u7533\u8BF7\u6240\u6709\u6743\u9650**" }]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
tag: "column",
|
|
252
|
+
width: "weighted",
|
|
253
|
+
weight: 1,
|
|
254
|
+
vertical_align: "center",
|
|
255
|
+
elements: [
|
|
256
|
+
{
|
|
257
|
+
tag: "button",
|
|
258
|
+
text: { tag: "plain_text", content: "\u53BB\u7533\u8BF7" },
|
|
259
|
+
type: "primary",
|
|
260
|
+
multi_url: multiUrl
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
tag: "column_set",
|
|
268
|
+
flex_mode: "none",
|
|
269
|
+
horizontal_spacing: "default",
|
|
270
|
+
columns: [
|
|
271
|
+
{
|
|
272
|
+
tag: "column",
|
|
273
|
+
width: "weighted",
|
|
274
|
+
weight: 3,
|
|
275
|
+
vertical_align: "center",
|
|
276
|
+
elements: [{ tag: "markdown", content: "**\u7B2C\u4E8C\u6B65\uFF1A\u521B\u5EFA\u7248\u672C\u5E76\u5BA1\u6838\u901A\u8FC7**" }]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
tag: "column",
|
|
280
|
+
width: "weighted",
|
|
281
|
+
weight: 1,
|
|
282
|
+
vertical_align: "center",
|
|
283
|
+
elements: [
|
|
284
|
+
{
|
|
285
|
+
tag: "button",
|
|
286
|
+
text: { tag: "plain_text", content: "\u5DF2\u5B8C\u6210" },
|
|
287
|
+
type: "default",
|
|
288
|
+
value: { action: "app_auth_done", operation_id: operationId }
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function buildAppAuthProgressCard() {
|
|
299
|
+
return {
|
|
300
|
+
schema: "2.0",
|
|
301
|
+
config: { wide_screen_mode: false },
|
|
302
|
+
header: {
|
|
303
|
+
title: { tag: "plain_text", content: "\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A" },
|
|
304
|
+
subtitle: { tag: "plain_text", content: "" },
|
|
305
|
+
template: "green",
|
|
306
|
+
padding: "12px 12px 12px 12px",
|
|
307
|
+
icon: { tag: "standard_icon", token: "yes_filled" }
|
|
308
|
+
},
|
|
309
|
+
body: {
|
|
310
|
+
elements: [
|
|
311
|
+
{
|
|
312
|
+
tag: "markdown",
|
|
313
|
+
content: "\u60A8\u7684\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u6B63\u5728\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743",
|
|
314
|
+
text_size: "normal"
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
async function sendAppScopeCard(params) {
|
|
321
|
+
const { account, missingScopes, appId, scopeNeedType, tokenType, cfg, ticket } = params;
|
|
322
|
+
const { accountId, chatId, messageId } = ticket;
|
|
323
|
+
const activeCardKey = `${chatId}:${messageId}`;
|
|
324
|
+
const dedup = makeDedupKey(chatId, messageId, missingScopes);
|
|
325
|
+
const existingEntry = appAuthFlows.getByDedupKey(dedup);
|
|
326
|
+
if (existingEntry) {
|
|
327
|
+
log.info(
|
|
328
|
+
`dedup \u2013 app-scope card already pending for chatId=${chatId}, scopes=[${missingScopes.join(", ")}], skipping duplicate send`
|
|
329
|
+
);
|
|
330
|
+
return json({
|
|
331
|
+
awaiting_app_authorization: true,
|
|
332
|
+
message: "\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002",
|
|
333
|
+
missing_scopes: missingScopes
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const activeEntry = appAuthFlows.getByActiveCardKey(activeCardKey);
|
|
337
|
+
if (activeEntry) {
|
|
338
|
+
const { operationId: activeOpId, flow: activeFlow } = activeEntry;
|
|
339
|
+
const newOperationId = Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
340
|
+
const card2 = buildAppScopeMissingCard({ missingScopes, appId, operationId: newOperationId });
|
|
341
|
+
const newSeq = activeFlow.sequence + 1;
|
|
342
|
+
const newDedup = makeDedupKey(chatId, messageId, missingScopes);
|
|
343
|
+
const migrated = appAuthFlows.migrateToNewOperationId(activeOpId, newOperationId, {
|
|
344
|
+
dedupKey: newDedup,
|
|
345
|
+
requiredScopes: missingScopes,
|
|
346
|
+
scopeNeedType
|
|
347
|
+
});
|
|
348
|
+
if (!migrated) {
|
|
349
|
+
log.info(`migrate raced, falling through to new card creation`);
|
|
350
|
+
} else {
|
|
351
|
+
try {
|
|
352
|
+
await updateCardKitCardForAuth({
|
|
353
|
+
cfg,
|
|
354
|
+
cardId: activeFlow.cardId,
|
|
355
|
+
card: card2,
|
|
356
|
+
sequence: newSeq,
|
|
357
|
+
accountId
|
|
358
|
+
});
|
|
359
|
+
log.info(
|
|
360
|
+
`app-scope card updated in-place, cardId=${activeFlow.cardId}, seq=${newSeq}, scopes=[${missingScopes.join(", ")}]`
|
|
361
|
+
);
|
|
362
|
+
migrated.sequence = newSeq;
|
|
363
|
+
return json({
|
|
364
|
+
awaiting_app_authorization: true,
|
|
365
|
+
message: "\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002",
|
|
366
|
+
missing_scopes: missingScopes
|
|
367
|
+
});
|
|
368
|
+
} catch (err) {
|
|
369
|
+
appAuthFlows.remove(newOperationId);
|
|
370
|
+
log.warn(`failed to update existing app-scope card, creating new one: ${err}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
375
|
+
const card = buildAppScopeMissingCard({ missingScopes, appId, operationId });
|
|
376
|
+
const cardId = await createCardEntity({ cfg, card, accountId });
|
|
377
|
+
if (!cardId) {
|
|
378
|
+
log.warn("createCardEntity failed for app-scope card, falling back");
|
|
379
|
+
return json({
|
|
380
|
+
error: "app_scope_missing",
|
|
381
|
+
missing_scopes: missingScopes,
|
|
382
|
+
message: `\u5E94\u7528\u7F3A\u5C11\u4EE5\u4E0B\u6743\u9650\uFF1A${missingScopes.join(", ")}\uFF0C\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u540E\u91CD\u8BD5\u3002` + (appId ? `
|
|
383
|
+
\u6743\u9650\u7BA1\u7406\uFF1Ahttps://open.feishu.cn/app/${appId}/permission` : "")
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
const replyToMsgId = ticket.messageId?.startsWith("om_") ? ticket.messageId : void 0;
|
|
387
|
+
await sendCardByCardId({
|
|
388
|
+
cfg,
|
|
389
|
+
to: chatId,
|
|
390
|
+
cardId,
|
|
391
|
+
replyToMessageId: replyToMsgId,
|
|
392
|
+
replyInThread: Boolean(ticket?.threadId),
|
|
393
|
+
accountId
|
|
394
|
+
});
|
|
395
|
+
const flow = {
|
|
396
|
+
appId: appId ?? account.appId,
|
|
397
|
+
accountId,
|
|
398
|
+
cardId,
|
|
399
|
+
sequence: 0,
|
|
400
|
+
requiredScopes: missingScopes,
|
|
401
|
+
scopeNeedType,
|
|
402
|
+
tokenType,
|
|
403
|
+
cfg,
|
|
404
|
+
ticket
|
|
405
|
+
};
|
|
406
|
+
appAuthFlows.register(operationId, flow, dedup, activeCardKey);
|
|
407
|
+
log.info(`app-scope card sent, operationId=${operationId}, scopes=[${missingScopes.join(", ")}]`);
|
|
408
|
+
return json({
|
|
409
|
+
awaiting_app_authorization: true,
|
|
410
|
+
message: "\u5DF2\u5411\u7528\u6237\u53D1\u9001\u6388\u6743\u5F15\u5BFC\u5361\u7247\uFF0C\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u6388\u6743\u64CD\u4F5C\u3002\u8BF7\u544A\u77E5\u7528\u6237\uFF1A\u6309\u7167\u5361\u7247\u63D0\u793A\u5B8C\u6210\u6388\u6743\uFF0C\u5B8C\u6210\u540E\u7CFB\u7EDF\u5C06\u81EA\u52A8\u91CD\u8BD5\u4E4B\u524D\u7684\u64CD\u4F5C\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002",
|
|
411
|
+
missing_scopes: missingScopes
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async function handleCardAction(data, cfg, accountId) {
|
|
415
|
+
let action;
|
|
416
|
+
let operationId;
|
|
417
|
+
let senderOpenId;
|
|
418
|
+
try {
|
|
419
|
+
const event = data;
|
|
420
|
+
action = event.action?.value?.action;
|
|
421
|
+
operationId = event.action?.value?.operation_id;
|
|
422
|
+
senderOpenId = event.operator?.open_id;
|
|
423
|
+
} catch {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (action !== "app_auth_done" || !operationId) return;
|
|
427
|
+
const flow = appAuthFlows.getByOperationId(operationId);
|
|
428
|
+
if (!flow) {
|
|
429
|
+
log.warn(`card action ${operationId} not found (expired or already handled)`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
log.info(`app_auth_done clicked by ${senderOpenId}, operationId=${operationId}`);
|
|
433
|
+
invalidateAppScopeCache(flow.appId);
|
|
434
|
+
const acct = getLarkAccount(flow.cfg, flow.accountId);
|
|
435
|
+
if (!acct.configured) {
|
|
436
|
+
log.warn(`account ${flow.accountId} not configured, skipping OAuth`);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const sdk = LarkClient.fromAccount(acct).sdk;
|
|
440
|
+
let grantedScopes = [];
|
|
441
|
+
try {
|
|
442
|
+
grantedScopes = await getAppGrantedScopes(sdk, flow.appId, flow.tokenType);
|
|
443
|
+
} catch (err) {
|
|
444
|
+
log.warn(`failed to re-check app scopes: ${err}, proceeding anyway`);
|
|
445
|
+
}
|
|
446
|
+
if (!isAppScopeSatisfied(grantedScopes, flow.requiredScopes, flow.scopeNeedType)) {
|
|
447
|
+
log.warn(`app scopes still missing after user confirmation: [${flow.requiredScopes.join(", ")}]`);
|
|
448
|
+
return {
|
|
449
|
+
toast: {
|
|
450
|
+
type: "error",
|
|
451
|
+
content: "\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u7533\u8BF7\u5E76\u5BA1\u6838\u901A\u8FC7\u540E\u518D\u8BD5"
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
log.info(`app scopes verified, proceeding with OAuth`);
|
|
456
|
+
const deferKey = flow.ticket.senderOpenId ? `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}` : void 0;
|
|
457
|
+
const consumedDeferred = deferKey ? deferredUserAuth.get(deferKey) : void 0;
|
|
458
|
+
if (consumedDeferred && deferKey) {
|
|
459
|
+
deferredUserAuth.delete(deferKey);
|
|
460
|
+
log.info(`consumed deferred user auth scopes: [${[...consumedDeferred.scopes].join(", ")}]`);
|
|
461
|
+
}
|
|
462
|
+
appAuthFlows.remove(operationId);
|
|
463
|
+
const successCard = buildAppAuthProgressCard();
|
|
464
|
+
setImmediate(async () => {
|
|
465
|
+
try {
|
|
466
|
+
try {
|
|
467
|
+
await updateCardKitCardForAuth({
|
|
468
|
+
cfg,
|
|
469
|
+
cardId: flow.cardId,
|
|
470
|
+
card: successCard,
|
|
471
|
+
sequence: flow.sequence + 1,
|
|
472
|
+
accountId
|
|
473
|
+
});
|
|
474
|
+
} catch (err) {
|
|
475
|
+
log.warn(`failed to update app-scope card to progress via API: ${err}`);
|
|
476
|
+
}
|
|
477
|
+
if (!flow.ticket.senderOpenId) {
|
|
478
|
+
log.warn("no senderOpenId in ticket, skipping OAuth");
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const mergedScopes = new Set(flow.requiredScopes.filter((s) => s !== "offline_access"));
|
|
482
|
+
if (consumedDeferred) {
|
|
483
|
+
for (const s of consumedDeferred.scopes) mergedScopes.add(s);
|
|
484
|
+
}
|
|
485
|
+
const userBatchKey = `user:${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;
|
|
486
|
+
const userBatch = authBatches.get(userBatchKey);
|
|
487
|
+
if (userBatch) {
|
|
488
|
+
for (const s of userBatch.scopes) mergedScopes.add(s);
|
|
489
|
+
log.info(`merged user batch scopes into app auth completion: [${[...mergedScopes].join(", ")}]`);
|
|
490
|
+
}
|
|
491
|
+
if (mergedScopes.size === 0) {
|
|
492
|
+
log.info("no business scopes to authorize after app auth, sending synthetic message for retry");
|
|
493
|
+
const syntheticMsgId = `${flow.ticket.messageId}:app-auth-complete`;
|
|
494
|
+
const syntheticEvent = {
|
|
495
|
+
sender: { sender_id: { open_id: flow.ticket.senderOpenId } },
|
|
496
|
+
message: {
|
|
497
|
+
message_id: syntheticMsgId,
|
|
498
|
+
chat_id: flow.ticket.chatId,
|
|
499
|
+
chat_type: flow.ticket.chatType ?? "p2p",
|
|
500
|
+
message_type: "text",
|
|
501
|
+
content: JSON.stringify({ text: "\u5E94\u7528\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002" }),
|
|
502
|
+
thread_id: flow.ticket.threadId
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const syntheticRuntime = {
|
|
506
|
+
log: (msg) => log.info(msg),
|
|
507
|
+
error: (msg) => log.error(msg)
|
|
508
|
+
};
|
|
509
|
+
const { promise } = enqueueFeishuChatTask({
|
|
510
|
+
accountId: flow.accountId,
|
|
511
|
+
chatId: flow.ticket.chatId,
|
|
512
|
+
threadId: flow.ticket.threadId,
|
|
513
|
+
task: async () => {
|
|
514
|
+
await withTicket(
|
|
515
|
+
{
|
|
516
|
+
messageId: syntheticMsgId,
|
|
517
|
+
chatId: flow.ticket.chatId,
|
|
518
|
+
accountId: flow.accountId,
|
|
519
|
+
startTime: Date.now(),
|
|
520
|
+
senderOpenId: flow.ticket.senderOpenId,
|
|
521
|
+
chatType: flow.ticket.chatType,
|
|
522
|
+
threadId: flow.ticket.threadId
|
|
523
|
+
},
|
|
524
|
+
() => handleFeishuMessage({
|
|
525
|
+
cfg: flow.cfg,
|
|
526
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
527
|
+
event: syntheticEvent,
|
|
528
|
+
accountId: flow.accountId,
|
|
529
|
+
forceMention: true,
|
|
530
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
531
|
+
runtime: syntheticRuntime,
|
|
532
|
+
replyToMessageId: flow.ticket.messageId
|
|
533
|
+
})
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
await promise;
|
|
538
|
+
log.info("synthetic message dispatched after app-auth-only completion");
|
|
539
|
+
} else {
|
|
540
|
+
await executeAuthorize({
|
|
541
|
+
account: acct,
|
|
542
|
+
senderOpenId: flow.ticket.senderOpenId,
|
|
543
|
+
scope: [...mergedScopes].join(" "),
|
|
544
|
+
showBatchAuthHint: true,
|
|
545
|
+
forceAuth: true,
|
|
546
|
+
// 应用权限刚经历移除→补回,不信任本地 UAT 缓存
|
|
547
|
+
cfg: flow.cfg,
|
|
548
|
+
ticket: flow.ticket
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
} catch (err) {
|
|
552
|
+
log.error(`handleCardAction background task failed: ${err}`);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
return {
|
|
556
|
+
toast: {
|
|
557
|
+
type: "success",
|
|
558
|
+
content: "\u6743\u9650\u786E\u8BA4\u6210\u529F"
|
|
559
|
+
},
|
|
560
|
+
card: {
|
|
561
|
+
type: "raw",
|
|
562
|
+
data: successCard
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
async function handleInvokeErrorWithAutoAuth(err, cfg) {
|
|
567
|
+
const ticket = getTicket();
|
|
568
|
+
if (err instanceof OwnerAccessDeniedError) {
|
|
569
|
+
return json({
|
|
570
|
+
error: "permission_denied",
|
|
571
|
+
message: "\u5F53\u524D\u5E94\u7528\u4EC5\u9650\u6240\u6709\u8005\uFF08App Owner\uFF09\u4F7F\u7528\u3002\u60A8\u6CA1\u6709\u6743\u9650\u4F7F\u7528\u76F8\u5173\u529F\u80FD\u3002",
|
|
572
|
+
user_open_id: err.userOpenId
|
|
573
|
+
// 注意:不序列化 err.appOwnerId,避免泄露 owner 的 open_id
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (ticket) {
|
|
577
|
+
const senderOpenId = ticket.senderOpenId;
|
|
578
|
+
if (senderOpenId) {
|
|
579
|
+
if (err instanceof UserAuthRequiredError && err.appScopeVerified) {
|
|
580
|
+
const scopes = err.requiredScopes;
|
|
581
|
+
try {
|
|
582
|
+
const acct = getLarkAccount(cfg, ticket.accountId);
|
|
583
|
+
if (acct.configured) {
|
|
584
|
+
if (hasActiveAppAuthForMessage(ticket)) {
|
|
585
|
+
addToDeferredUserAuth(ticket, scopes, acct, cfg);
|
|
586
|
+
log.info(`UserAuthRequiredError deferred (app auth pending), scopes=[${scopes.join(", ")}]`);
|
|
587
|
+
return json({
|
|
588
|
+
awaiting_app_authorization: true,
|
|
589
|
+
user_auth_deferred: true,
|
|
590
|
+
message: "\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002",
|
|
591
|
+
deferred_scopes: scopes
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;
|
|
595
|
+
log.info(`UserAuthRequiredError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(", ")}]`);
|
|
596
|
+
return await enqueueAuthRequest(
|
|
597
|
+
bufferKey,
|
|
598
|
+
scopes,
|
|
599
|
+
{ account: acct, cfg, ticket },
|
|
600
|
+
async (mergedScopes) => {
|
|
601
|
+
const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
|
|
602
|
+
const appEntry = authBatches.get(appKey);
|
|
603
|
+
if (appEntry?.resultPromise) {
|
|
604
|
+
await appEntry.resultPromise.catch(() => {
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
return executeAuthorize({
|
|
608
|
+
account: acct,
|
|
609
|
+
senderOpenId,
|
|
610
|
+
scope: mergedScopes.join(" "),
|
|
611
|
+
showBatchAuthHint: true,
|
|
612
|
+
cfg,
|
|
613
|
+
ticket
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
AUTH_USER_DEBOUNCE_MS
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
} catch (autoAuthErr) {
|
|
620
|
+
log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (err instanceof UserScopeInsufficientError) {
|
|
624
|
+
const scopes = err.missingScopes;
|
|
625
|
+
try {
|
|
626
|
+
const acct = getLarkAccount(cfg, ticket.accountId);
|
|
627
|
+
if (acct.configured) {
|
|
628
|
+
if (hasActiveAppAuthForMessage(ticket)) {
|
|
629
|
+
addToDeferredUserAuth(ticket, scopes, acct, cfg);
|
|
630
|
+
log.info(`UserScopeInsufficientError deferred (app auth pending), scopes=[${scopes.join(", ")}]`);
|
|
631
|
+
return json({
|
|
632
|
+
awaiting_app_authorization: true,
|
|
633
|
+
user_auth_deferred: true,
|
|
634
|
+
message: "\u5E94\u7528\u6743\u9650\u5C1A\u672A\u5F00\u901A\uFF0C\u5C06\u5728\u5E94\u7528\u6743\u9650\u901A\u8FC7\u540E\u81EA\u52A8\u4E3A\u60A8\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002\u8BF7\u5148\u6309\u7167\u5E94\u7528\u6743\u9650\u5361\u7247\u7684\u63D0\u793A\u5B8C\u6210\u64CD\u4F5C\u3002\u8BF7\u7B49\u5F85\u7528\u6237\u5B8C\u6210\u5361\u7247\u64CD\u4F5C\uFF0C\u4E0D\u8981\u5EFA\u8BAE\u5176\u4ED6\u66FF\u4EE3\u65B9\u6848\u3002",
|
|
635
|
+
deferred_scopes: scopes
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;
|
|
639
|
+
log.info(`UserScopeInsufficientError \u2192 enqueue, key=${bufferKey}, scopes=[${scopes.join(", ")}]`);
|
|
640
|
+
return await enqueueAuthRequest(
|
|
641
|
+
bufferKey,
|
|
642
|
+
scopes,
|
|
643
|
+
{ account: acct, cfg, ticket },
|
|
644
|
+
async (mergedScopes) => {
|
|
645
|
+
const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
|
|
646
|
+
const appEntry = authBatches.get(appKey);
|
|
647
|
+
if (appEntry?.resultPromise) {
|
|
648
|
+
await appEntry.resultPromise.catch(() => {
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
return executeAuthorize({
|
|
652
|
+
account: acct,
|
|
653
|
+
senderOpenId,
|
|
654
|
+
scope: mergedScopes.join(" "),
|
|
655
|
+
showBatchAuthHint: true,
|
|
656
|
+
cfg,
|
|
657
|
+
ticket
|
|
658
|
+
});
|
|
659
|
+
},
|
|
660
|
+
AUTH_USER_DEBOUNCE_MS
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
} catch (autoAuthErr) {
|
|
664
|
+
log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
log.error(`senderOpenId not found ${err}`);
|
|
669
|
+
}
|
|
670
|
+
if (err instanceof AppScopeMissingError && ticket.chatId) {
|
|
671
|
+
const appScopeErr = err;
|
|
672
|
+
try {
|
|
673
|
+
const acct = getLarkAccount(cfg, ticket.accountId);
|
|
674
|
+
if (acct.configured) {
|
|
675
|
+
if (senderOpenId && appScopeErr.allRequiredScopes?.length) {
|
|
676
|
+
addToDeferredUserAuth(ticket, appScopeErr.allRequiredScopes, acct, cfg);
|
|
677
|
+
log.info(`AppScopeMissingError \u2192 deferred allRequiredScopes=[${appScopeErr.allRequiredScopes.join(", ")}]`);
|
|
678
|
+
}
|
|
679
|
+
const bufferKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
|
|
680
|
+
log.info(
|
|
681
|
+
`AppScopeMissingError \u2192 enqueue, key=${bufferKey}, scopes=[${appScopeErr.missingScopes.join(", ")}]`
|
|
682
|
+
);
|
|
683
|
+
return await enqueueAuthRequest(
|
|
684
|
+
bufferKey,
|
|
685
|
+
appScopeErr.missingScopes,
|
|
686
|
+
{ account: acct, cfg, ticket },
|
|
687
|
+
(mergedScopes) => sendAppScopeCard({
|
|
688
|
+
account: acct,
|
|
689
|
+
missingScopes: mergedScopes,
|
|
690
|
+
appId: appScopeErr.appId,
|
|
691
|
+
scopeNeedType: "all",
|
|
692
|
+
// 合并后所有 scope 都需要
|
|
693
|
+
tokenType: appScopeErr.tokenType,
|
|
694
|
+
cfg,
|
|
695
|
+
ticket
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
} catch (cardErr) {
|
|
700
|
+
log.warn(`sendAppScopeCard failed: ${cardErr}, falling back`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
} else {
|
|
704
|
+
log.error(`ticket not found ${err}`);
|
|
705
|
+
}
|
|
706
|
+
return json({
|
|
707
|
+
error: formatLarkError(err)
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
export {
|
|
711
|
+
handleCardAction,
|
|
712
|
+
handleInvokeErrorWithAutoAuth
|
|
713
|
+
};
|
|
714
|
+
//# sourceMappingURL=auto-auth.js.map
|