@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,101 @@
|
|
|
1
|
+
import { getLarkAccount } from "../core/accounts";
|
|
2
|
+
import { LarkClient } from "../core/lark-client";
|
|
3
|
+
import { getAppGrantedScopes } from "../core/app-scope-checker";
|
|
4
|
+
import { getAppOwnerFallback } from "../core/app-owner-fallback";
|
|
5
|
+
import { executeAuthorize } from "./oauth";
|
|
6
|
+
import { larkLogger } from "../core/lark-logger";
|
|
7
|
+
import { filterSensitiveScopes } from "../core/tool-scopes";
|
|
8
|
+
const log = larkLogger("tools/onboarding-auth");
|
|
9
|
+
const MAX_SCOPES_PER_BATCH = 100;
|
|
10
|
+
async function triggerOnboarding(params) {
|
|
11
|
+
const { cfg, userOpenId, accountId } = params;
|
|
12
|
+
const acct = getLarkAccount(cfg, accountId);
|
|
13
|
+
if (!acct.configured) {
|
|
14
|
+
log.warn(`account ${accountId} not configured, skipping`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const sdk = LarkClient.fromAccount(acct).sdk;
|
|
18
|
+
const { appId } = acct;
|
|
19
|
+
const ownerOpenId = await getAppOwnerFallback(acct, sdk);
|
|
20
|
+
if (!ownerOpenId) {
|
|
21
|
+
log.info(`app ${appId} has no owner info, skipping`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (userOpenId !== ownerOpenId) {
|
|
25
|
+
log.info(`user ${userOpenId} is not app owner (${ownerOpenId}), skipping`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
log.info(`user ${userOpenId} is app owner, starting OAuth`);
|
|
29
|
+
let allUserScopes;
|
|
30
|
+
try {
|
|
31
|
+
allUserScopes = await getAppGrantedScopes(sdk, appId, "user");
|
|
32
|
+
} catch (err) {
|
|
33
|
+
log.warn(`failed to get app granted scopes: ${err}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
allUserScopes = filterSensitiveScopes(allUserScopes);
|
|
37
|
+
if (allUserScopes.length === 0) {
|
|
38
|
+
log.info("no user scopes configured, skipping");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const batches = [];
|
|
42
|
+
for (let i = 0; i < allUserScopes.length; i += MAX_SCOPES_PER_BATCH) {
|
|
43
|
+
batches.push(allUserScopes.slice(i, i + MAX_SCOPES_PER_BATCH));
|
|
44
|
+
}
|
|
45
|
+
log.info(`${allUserScopes.length} user scopes, ${batches.length} batch(es)`);
|
|
46
|
+
const startBatch = async (batchIndex) => {
|
|
47
|
+
if (batchIndex >= batches.length) {
|
|
48
|
+
log.info("all batches completed");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const batch = batches[batchIndex];
|
|
52
|
+
const scope = batch.join(" ");
|
|
53
|
+
let batchInfo = "";
|
|
54
|
+
if (batches.length > 1) {
|
|
55
|
+
batchInfo = `
|
|
56
|
+
|
|
57
|
+
\u{1F4CB} \u6388\u6743\u8FDB\u5EA6\uFF1A\u7B2C ${batchIndex + 1}/${batches.length} \u6279\uFF08\u672C\u6279 ${batch.length} \u4E2A\u6743\u9650\uFF0C\u5171 ${allUserScopes.length} \u4E2A\uFF09`;
|
|
58
|
+
if (batchIndex < batches.length - 1) {
|
|
59
|
+
batchInfo += `
|
|
60
|
+
\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\u3002`;
|
|
61
|
+
} else {
|
|
62
|
+
batchInfo += `
|
|
63
|
+
\u8FD9\u662F\u6700\u540E\u4E00\u6279\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528\u6240\u6709\u529F\u80FD\u3002`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const ticket = {
|
|
67
|
+
messageId: `onboarding:${Date.now()}`,
|
|
68
|
+
chatId: userOpenId,
|
|
69
|
+
accountId,
|
|
70
|
+
startTime: Date.now(),
|
|
71
|
+
senderOpenId: userOpenId,
|
|
72
|
+
chatType: "p2p"
|
|
73
|
+
};
|
|
74
|
+
log.info(`starting batch ${batchIndex + 1}/${batches.length}, scopes=${batch.length}`);
|
|
75
|
+
try {
|
|
76
|
+
await executeAuthorize({
|
|
77
|
+
account: acct,
|
|
78
|
+
senderOpenId: userOpenId,
|
|
79
|
+
scope,
|
|
80
|
+
isBatchAuth: true,
|
|
81
|
+
totalAppScopes: allUserScopes.length,
|
|
82
|
+
alreadyGranted: batchIndex * MAX_SCOPES_PER_BATCH,
|
|
83
|
+
batchInfo,
|
|
84
|
+
skipSyntheticMessage: true,
|
|
85
|
+
cfg,
|
|
86
|
+
ticket,
|
|
87
|
+
onAuthComplete: async () => {
|
|
88
|
+
log.info(`batch ${batchIndex + 1}/${batches.length} auth completed`);
|
|
89
|
+
await startBatch(batchIndex + 1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
log.error(`batch ${batchIndex + 1} failed: ${err}`);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
await startBatch(0);
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
triggerOnboarding
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=onboarding-auth.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/tools/onboarding-auth.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Onboarding \u9884\u6388\u6743\u6A21\u5757\u3002\n *\n * \u914D\u5BF9\u540E\u81EA\u52A8\u53D1\u8D77 OAuth Device Flow\uFF0C\u5F15\u5BFC\u5E94\u7528 owner \u5B8C\u6210\u7528\u6237\u6388\u6743\u3002\n * \u4EC5\u5F53\u914D\u5BF9\u7528\u6237 === \u5E94\u7528 owner \u65F6\u89E6\u53D1\u3002\n *\n * \u98DE\u4E66\u9650\u5236\uFF1A\u5355\u6B21 OAuth \u6700\u591A 50 \u4E2A scope\u3002\n * \u8D85\u8FC7 50 \u4E2A\u65F6\u81EA\u52A8\u5206\u6279\u5904\u7406\uFF0C\u6BCF\u6279\u6388\u6743\u5B8C\u6210\u540E\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\uFF08\u94FE\u5F0F\u89E6\u53D1\uFF09\u3002\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { getLarkAccount } from '../core/accounts';\nimport { LarkClient } from '../core/lark-client';\nimport { getAppGrantedScopes } from '../core/app-scope-checker';\nimport { getAppOwnerFallback } from '../core/app-owner-fallback';\nimport { executeAuthorize } from './oauth';\nimport { larkLogger } from '../core/lark-logger';\nimport { filterSensitiveScopes } from '../core/tool-scopes';\n\nconst log = larkLogger('tools/onboarding-auth');\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_SCOPES_PER_BATCH = 100;\n\n// ---------------------------------------------------------------------------\n// Trigger onboarding\n// ---------------------------------------------------------------------------\n\n/**\n * \u914D\u5BF9\u540E\u89E6\u53D1 onboarding OAuth \u6388\u6743\u3002\n *\n * \u6D41\u7A0B\uFF1A\n * 1. \u68C0\u67E5 userOpenId === \u5E94\u7528 owner\uFF0C\u4E0D\u5339\u914D\u5219\u9759\u9ED8\u8DF3\u8FC7\n * 2. \u8BFB\u53D6 onboarding-scopes.json \u4E2D\u7684 user scope \u5217\u8868\n * 3. \u5206\u6279\u5904\u7406\uFF08\u6BCF\u6279\u6700\u591A 50 \u4E2A\uFF09\uFF0C\u7B2C\u4E00\u6279\u76F4\u63A5\u53D1\u8D77 OAuth Device Flow\n * 4. \u6BCF\u6279\u6388\u6743\u5B8C\u6210\u540E\u901A\u8FC7 onAuthComplete \u56DE\u8C03\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\n */\nexport async function triggerOnboarding(params: {\n cfg: ClawdbotConfig;\n userOpenId: string;\n accountId: string;\n}): Promise<void> {\n const { cfg, userOpenId, accountId } = params;\n\n const acct = getLarkAccount(cfg, accountId);\n if (!acct.configured) {\n log.warn(`account ${accountId} not configured, skipping`);\n return;\n }\n\n const sdk = LarkClient.fromAccount(acct).sdk;\n const { appId } = acct;\n\n // 1. \u68C0\u67E5 userOpenId === \u5E94\u7528 owner\uFF08\u7EDF\u4E00\u8D70 getAppOwnerFallback\uFF09\n const ownerOpenId = await getAppOwnerFallback(acct, sdk);\n if (!ownerOpenId) {\n log.info(`app ${appId} has no owner info, skipping`);\n return;\n }\n if (userOpenId !== ownerOpenId) {\n log.info(`user ${userOpenId} is not app owner (${ownerOpenId}), skipping`);\n return;\n }\n\n log.info(`user ${userOpenId} is app owner, starting OAuth`);\n\n // 3. \u52A8\u6001\u83B7\u53D6\u5E94\u7528\u5DF2\u5F00\u901A\u7684 user scope \u5217\u8868\n let allUserScopes: string[];\n try {\n allUserScopes = await getAppGrantedScopes(sdk, appId, 'user');\n } catch (err) {\n log.warn(`failed to get app granted scopes: ${err}`);\n return;\n }\n\n // \u8FC7\u6EE4\u6389\u654F\u611F scope\n allUserScopes = filterSensitiveScopes(allUserScopes);\n\n if (allUserScopes.length === 0) {\n log.info('no user scopes configured, skipping');\n return;\n }\n\n // 4. \u5206\u6279\n const batches: string[][] = [];\n for (let i = 0; i < allUserScopes.length; i += MAX_SCOPES_PER_BATCH) {\n batches.push(allUserScopes.slice(i, i + MAX_SCOPES_PER_BATCH));\n }\n\n log.info(`${allUserScopes.length} user scopes, ${batches.length} batch(es)`);\n\n // 5. \u94FE\u5F0F\u53D1\u8D77\u6388\u6743\uFF08\u7B2C\u4E00\u6279\u540C\u6B65\u53D1\u8D77\uFF0C\u540E\u7EED\u6279\u6B21\u7531 onAuthComplete \u56DE\u8C03\u89E6\u53D1\uFF09\n const startBatch = async (batchIndex: number): Promise<void> => {\n if (batchIndex >= batches.length) {\n log.info('all batches completed');\n return;\n }\n\n const batch = batches[batchIndex];\n const scope = batch.join(' ');\n\n let batchInfo = '';\n if (batches.length > 1) {\n batchInfo =\n `\\n\\n\uD83D\uDCCB \u6388\u6743\u8FDB\u5EA6\uFF1A\u7B2C ${batchIndex + 1}/${batches.length} \u6279` +\n `\uFF08\u672C\u6279 ${batch.length} \u4E2A\u6743\u9650\uFF0C\u5171 ${allUserScopes.length} \u4E2A\uFF09`;\n if (batchIndex < batches.length - 1) {\n batchInfo += `\\n\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\u3002`;\n } else {\n batchInfo += `\\n\u8FD9\u662F\u6700\u540E\u4E00\u6279\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528\u6240\u6709\u529F\u80FD\u3002`;\n }\n }\n\n const ticket = {\n messageId: `onboarding:${Date.now()}`,\n chatId: userOpenId,\n accountId,\n startTime: Date.now(),\n senderOpenId: userOpenId,\n chatType: 'p2p' as const,\n };\n\n log.info(`starting batch ${batchIndex + 1}/${batches.length}, scopes=${batch.length}`);\n\n try {\n await executeAuthorize({\n account: acct,\n senderOpenId: userOpenId,\n scope,\n isBatchAuth: true,\n totalAppScopes: allUserScopes.length,\n alreadyGranted: batchIndex * MAX_SCOPES_PER_BATCH,\n batchInfo,\n skipSyntheticMessage: true,\n cfg,\n ticket,\n onAuthComplete: async () => {\n log.info(`batch ${batchIndex + 1}/${batches.length} auth completed`);\n await startBatch(batchIndex + 1);\n },\n });\n } catch (err) {\n log.error(`batch ${batchIndex + 1} failed: ${err}`);\n }\n };\n\n await startBatch(0);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { getTicket } from "../core/lark-ticket";
|
|
3
|
+
import { larkLogger } from "../core/lark-logger";
|
|
4
|
+
import { formatLarkError } from "../core/api-error";
|
|
5
|
+
import {
|
|
6
|
+
startLocalAuthFlow,
|
|
7
|
+
prepareRemoteAuth,
|
|
8
|
+
completeRemoteAuth
|
|
9
|
+
} from "../core/project-auth";
|
|
10
|
+
import {
|
|
11
|
+
getProjectStoredToken,
|
|
12
|
+
setProjectStoredToken,
|
|
13
|
+
removeProjectStoredToken,
|
|
14
|
+
projectTokenStatus,
|
|
15
|
+
getProjectClientId
|
|
16
|
+
} from "../core/project-token-store";
|
|
17
|
+
import { getProjectMcpEndpoint } from "../tools/mcp/project/endpoint";
|
|
18
|
+
import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit";
|
|
19
|
+
import { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from "./oauth-cards";
|
|
20
|
+
import { json } from "./oapi/helpers";
|
|
21
|
+
const log = larkLogger("tools/project-oauth");
|
|
22
|
+
const FeishuProjectOAuthSchema = Type.Object(
|
|
23
|
+
{
|
|
24
|
+
action: Type.Union(
|
|
25
|
+
[
|
|
26
|
+
Type.Literal("authorize"),
|
|
27
|
+
Type.Literal("complete_auth"),
|
|
28
|
+
Type.Literal("status"),
|
|
29
|
+
Type.Literal("revoke")
|
|
30
|
+
],
|
|
31
|
+
{
|
|
32
|
+
description: "authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743"
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
callback_url: Type.Optional(
|
|
36
|
+
Type.String({
|
|
37
|
+
description: "\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL"
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
description: "\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002"
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
const pendingLocalFlows = /* @__PURE__ */ new Map();
|
|
46
|
+
const pendingRemoteSessions = /* @__PURE__ */ new Map();
|
|
47
|
+
function fk(userOpenId) {
|
|
48
|
+
return `project:${userOpenId}`;
|
|
49
|
+
}
|
|
50
|
+
function buildTokenFromOAuth(userOpenId, clientId, tokens) {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
const expiresIn = tokens.expires_in ?? 7200;
|
|
53
|
+
const refreshExpiresIn = 30 * 24 * 3600;
|
|
54
|
+
return {
|
|
55
|
+
userOpenId,
|
|
56
|
+
appId: clientId,
|
|
57
|
+
accessToken: tokens.access_token,
|
|
58
|
+
refreshToken: tokens.refresh_token ?? "",
|
|
59
|
+
expiresAt: now + expiresIn * 1e3,
|
|
60
|
+
refreshExpiresAt: now + refreshExpiresIn * 1e3,
|
|
61
|
+
scope: tokens.scope ?? "",
|
|
62
|
+
grantedAt: now
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function isAlreadyAuthorized(senderOpenId) {
|
|
66
|
+
const clientId = await getProjectClientId(senderOpenId);
|
|
67
|
+
if (!clientId) return false;
|
|
68
|
+
const existing = await getProjectStoredToken(clientId, senderOpenId);
|
|
69
|
+
return !!existing && projectTokenStatus(existing) !== "expired";
|
|
70
|
+
}
|
|
71
|
+
async function tryLocalAuth(mcpEndpoint) {
|
|
72
|
+
try {
|
|
73
|
+
return await startLocalAuthFlow(mcpEndpoint);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function registerFeishuProjectOAuthTool(api) {
|
|
80
|
+
if (!api.config) return;
|
|
81
|
+
const cfg = api.config;
|
|
82
|
+
api.registerTool(
|
|
83
|
+
{
|
|
84
|
+
name: "feishu_project_oauth",
|
|
85
|
+
label: "Feishu Project OAuth",
|
|
86
|
+
description: "\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002",
|
|
87
|
+
parameters: FeishuProjectOAuthSchema,
|
|
88
|
+
async execute(_toolCallId, params) {
|
|
89
|
+
const p = params;
|
|
90
|
+
const ticket = getTicket();
|
|
91
|
+
const senderOpenId = ticket?.senderOpenId;
|
|
92
|
+
if (!senderOpenId) {
|
|
93
|
+
return json({
|
|
94
|
+
error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002"
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const accountId = ticket.accountId;
|
|
98
|
+
const mcpEndpoint = getProjectMcpEndpoint(cfg);
|
|
99
|
+
try {
|
|
100
|
+
switch (p.action) {
|
|
101
|
+
// ---------------------------------------------------------------
|
|
102
|
+
// STATUS
|
|
103
|
+
// ---------------------------------------------------------------
|
|
104
|
+
case "status": {
|
|
105
|
+
const clientId = await getProjectClientId(senderOpenId);
|
|
106
|
+
if (!clientId) {
|
|
107
|
+
return json({
|
|
108
|
+
authorized: false,
|
|
109
|
+
message: "\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const existing = await getProjectStoredToken(clientId, senderOpenId);
|
|
113
|
+
if (!existing) {
|
|
114
|
+
return json({
|
|
115
|
+
authorized: false,
|
|
116
|
+
message: "\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const status = projectTokenStatus(existing);
|
|
120
|
+
return json({
|
|
121
|
+
authorized: status !== "expired",
|
|
122
|
+
token_status: status,
|
|
123
|
+
scope: existing.scope,
|
|
124
|
+
granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : void 0,
|
|
125
|
+
expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : void 0
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------
|
|
129
|
+
// AUTHORIZE(自动选择本地/远程)
|
|
130
|
+
// ---------------------------------------------------------------
|
|
131
|
+
case "authorize": {
|
|
132
|
+
if (await isAlreadyAuthorized(senderOpenId)) {
|
|
133
|
+
return json({
|
|
134
|
+
success: true,
|
|
135
|
+
message: "\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002",
|
|
136
|
+
authorized: true
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const chatId = ticket.chatId;
|
|
140
|
+
if (!chatId) {
|
|
141
|
+
return json({ error: "\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807" });
|
|
142
|
+
}
|
|
143
|
+
const key = fk(senderOpenId);
|
|
144
|
+
const oldLocal = pendingLocalFlows.get(key);
|
|
145
|
+
if (oldLocal) {
|
|
146
|
+
oldLocal.superseded = true;
|
|
147
|
+
oldLocal.controller.abort();
|
|
148
|
+
await oldLocal.close().catch(() => {
|
|
149
|
+
});
|
|
150
|
+
pendingLocalFlows.delete(key);
|
|
151
|
+
}
|
|
152
|
+
pendingRemoteSessions.delete(key);
|
|
153
|
+
const local = await tryLocalAuth(mcpEndpoint);
|
|
154
|
+
if (local) {
|
|
155
|
+
const { session: session2, waitForCode, port, close } = local;
|
|
156
|
+
const authCard2 = buildAuthCard({
|
|
157
|
+
verificationUriComplete: session2.authorizationUrl,
|
|
158
|
+
expiresMin: 5
|
|
159
|
+
});
|
|
160
|
+
const cardId2 = await createCardEntity({ cfg, card: authCard2, accountId });
|
|
161
|
+
if (!cardId2) {
|
|
162
|
+
await close();
|
|
163
|
+
return json({ error: "\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25" });
|
|
164
|
+
}
|
|
165
|
+
await sendCardByCardId({
|
|
166
|
+
cfg,
|
|
167
|
+
to: chatId,
|
|
168
|
+
cardId: cardId2,
|
|
169
|
+
replyToMessageId: ticket.messageId?.startsWith("om_") ? ticket.messageId : void 0,
|
|
170
|
+
replyInThread: Boolean(ticket.threadId),
|
|
171
|
+
accountId
|
|
172
|
+
});
|
|
173
|
+
const abortController = new AbortController();
|
|
174
|
+
let seq = 1;
|
|
175
|
+
const currentFlow = {
|
|
176
|
+
controller: abortController,
|
|
177
|
+
cardId: cardId2,
|
|
178
|
+
sequence: seq,
|
|
179
|
+
superseded: false,
|
|
180
|
+
close
|
|
181
|
+
};
|
|
182
|
+
pendingLocalFlows.set(key, currentFlow);
|
|
183
|
+
waitForCode().then(async (result) => {
|
|
184
|
+
if (currentFlow.superseded) return;
|
|
185
|
+
const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);
|
|
186
|
+
await setProjectStoredToken(storedToken);
|
|
187
|
+
try {
|
|
188
|
+
await updateCardKitCardForAuth({
|
|
189
|
+
cfg,
|
|
190
|
+
cardId: cardId2,
|
|
191
|
+
card: buildAuthSuccessCard(),
|
|
192
|
+
sequence: ++seq,
|
|
193
|
+
accountId
|
|
194
|
+
});
|
|
195
|
+
} catch (e) {
|
|
196
|
+
log.warn(`failed to update project auth card to success: ${e}`);
|
|
197
|
+
}
|
|
198
|
+
}).catch(async (err) => {
|
|
199
|
+
if (currentFlow.superseded) return;
|
|
200
|
+
log.error(`project local auth failed: ${err}`);
|
|
201
|
+
try {
|
|
202
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
203
|
+
await updateCardKitCardForAuth({
|
|
204
|
+
cfg,
|
|
205
|
+
cardId: cardId2,
|
|
206
|
+
card: buildAuthFailedCard(msg),
|
|
207
|
+
sequence: ++seq,
|
|
208
|
+
accountId
|
|
209
|
+
});
|
|
210
|
+
} catch (e) {
|
|
211
|
+
log.warn(`failed to update project auth card to failure: ${e}`);
|
|
212
|
+
}
|
|
213
|
+
}).finally(async () => {
|
|
214
|
+
await close().catch(() => {
|
|
215
|
+
});
|
|
216
|
+
if (pendingLocalFlows.get(key) === currentFlow) {
|
|
217
|
+
pendingLocalFlows.delete(key);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return json({
|
|
221
|
+
success: true,
|
|
222
|
+
mode: "local",
|
|
223
|
+
message: "\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002",
|
|
224
|
+
awaiting_authorization: true,
|
|
225
|
+
callback_port: port
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const session = await prepareRemoteAuth(mcpEndpoint);
|
|
229
|
+
pendingRemoteSessions.set(key, session);
|
|
230
|
+
const authCard = buildAuthCard({
|
|
231
|
+
verificationUriComplete: session.authorizationUrl,
|
|
232
|
+
expiresMin: 10
|
|
233
|
+
});
|
|
234
|
+
const cardId = await createCardEntity({ cfg, card: authCard, accountId });
|
|
235
|
+
if (cardId) {
|
|
236
|
+
await sendCardByCardId({
|
|
237
|
+
cfg,
|
|
238
|
+
to: chatId,
|
|
239
|
+
cardId,
|
|
240
|
+
replyToMessageId: ticket.messageId?.startsWith("om_") ? ticket.messageId : void 0,
|
|
241
|
+
replyInThread: Boolean(ticket.threadId),
|
|
242
|
+
accountId
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return json({
|
|
246
|
+
success: true,
|
|
247
|
+
mode: "remote",
|
|
248
|
+
message: "\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002",
|
|
249
|
+
authorization_url: session.authorizationUrl,
|
|
250
|
+
awaiting_callback_url: true
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// ---------------------------------------------------------------
|
|
254
|
+
// COMPLETE_AUTH(远程模式下用户回传 callback URL)
|
|
255
|
+
// ---------------------------------------------------------------
|
|
256
|
+
case "complete_auth": {
|
|
257
|
+
if (!p.callback_url) {
|
|
258
|
+
return json({
|
|
259
|
+
error: "\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const key = fk(senderOpenId);
|
|
263
|
+
const session = pendingRemoteSessions.get(key);
|
|
264
|
+
if (!session) {
|
|
265
|
+
return json({
|
|
266
|
+
error: "\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const result = await completeRemoteAuth(session, p.callback_url);
|
|
270
|
+
pendingRemoteSessions.delete(key);
|
|
271
|
+
const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);
|
|
272
|
+
await setProjectStoredToken(storedToken);
|
|
273
|
+
return json({
|
|
274
|
+
success: true,
|
|
275
|
+
message: "\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01",
|
|
276
|
+
authorized: true
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
// ---------------------------------------------------------------
|
|
280
|
+
// REVOKE
|
|
281
|
+
// ---------------------------------------------------------------
|
|
282
|
+
case "revoke": {
|
|
283
|
+
const clientId = await getProjectClientId(senderOpenId);
|
|
284
|
+
if (clientId) {
|
|
285
|
+
await removeProjectStoredToken(clientId, senderOpenId);
|
|
286
|
+
}
|
|
287
|
+
return json({ success: true, message: "\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002" });
|
|
288
|
+
}
|
|
289
|
+
default:
|
|
290
|
+
return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${p.action}` });
|
|
291
|
+
}
|
|
292
|
+
} catch (err) {
|
|
293
|
+
log.error(`project oauth ${p.action} failed: ${err}`);
|
|
294
|
+
return json({ error: formatLarkError(err) });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
{ name: "feishu_project_oauth" }
|
|
299
|
+
);
|
|
300
|
+
api.logger.info?.("feishu_project_oauth: Registered feishu_project_oauth tool");
|
|
301
|
+
}
|
|
302
|
+
export {
|
|
303
|
+
registerFeishuProjectOAuthTool
|
|
304
|
+
};
|
|
305
|
+
//# sourceMappingURL=project-oauth.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/tools/project-oauth.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * feishu_project_oauth \u2014 \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB OAuth \u6388\u6743\u5DE5\u5177\u3002\n *\n * \u57FA\u4E8E MCP \u6807\u51C6 OAuth\uFF08Authorization Code + PKCE + \u52A8\u6001\u5BA2\u6237\u7AEF\u6CE8\u518C\uFF09\uFF0C\n * \u4E0D\u9700\u8981 appId/appSecret\u3002\n *\n * \u5355\u4E00 authorize action \u81EA\u52A8\u5224\u65AD\u8FD0\u884C\u73AF\u5883\uFF1A\n * - \u672C\u5730\uFF08\u80FD\u7ED1\u7AEF\u53E3\uFF09\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\uFF0C\u6D4F\u89C8\u5668\u91CD\u5B9A\u5411\u81EA\u52A8\u5B8C\u6210\n * - \u8FDC\u7A0B\uFF08\u7ED1\u7AEF\u53E3\u5931\u8D25 / headless\uFF09\uFF1A\u964D\u7EA7\u4E3A\u53D1\u9001\u6388\u6743\u94FE\u63A5\uFF0C\u7B49\u7528\u6237\u56DE\u4F20 callback URL\n *\n * Actions:\n * - authorize : \u53D1\u8D77\u6388\u6743\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\u6A21\u5F0F\uFF09\n * - complete_auth : \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\uFF0C\u7528\u6237\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743\n * - status : \u68C0\u67E5\u98DE\u4E66\u9879\u76EE\u6388\u6743\u72B6\u6001\n * - revoke : \u64A4\u9500\u98DE\u4E66\u9879\u76EE\u6388\u6743\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { Type } from '@sinclair/typebox';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\nimport { formatLarkError } from '../core/api-error';\nimport {\n startLocalAuthFlow,\n prepareRemoteAuth,\n completeRemoteAuth,\n type ProjectAuthSession,\n} from '../core/project-auth';\nimport {\n getProjectStoredToken,\n setProjectStoredToken,\n removeProjectStoredToken,\n projectTokenStatus,\n getProjectClientId,\n} from '../core/project-token-store';\nimport type { StoredUAToken } from '../core/token-store';\nimport { getProjectMcpEndpoint } from '../tools/mcp/project/endpoint';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from './oauth-cards';\nimport { json } from './oapi/helpers';\n\nconst log = larkLogger('tools/project-oauth');\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst FeishuProjectOAuthSchema = Type.Object(\n {\n action: Type.Union(\n [\n Type.Literal('authorize'),\n Type.Literal('complete_auth'),\n Type.Literal('status'),\n Type.Literal('revoke'),\n ],\n {\n description:\n 'authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; ' +\n 'complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; ' +\n 'status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743',\n },\n ),\n callback_url: Type.Optional(\n Type.String({\n description: '\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL',\n }),\n ),\n },\n {\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002',\n },\n);\n\ninterface FeishuProjectOAuthParams {\n action: 'authorize' | 'complete_auth' | 'status' | 'revoke';\n callback_url?: string;\n}\n\n// ---------------------------------------------------------------------------\n// In-flight state\n// ---------------------------------------------------------------------------\n\ninterface PendingLocalFlow {\n controller: AbortController;\n cardId: string;\n sequence: number;\n superseded: boolean;\n close: () => Promise<void>;\n}\n\nconst pendingLocalFlows = new Map<string, PendingLocalFlow>();\nconst pendingRemoteSessions = new Map<string, ProjectAuthSession>();\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fk(userOpenId: string): string {\n return `project:${userOpenId}`;\n}\n\nfunction buildTokenFromOAuth(\n userOpenId: string,\n clientId: string,\n tokens: { access_token: string; refresh_token?: string; expires_in?: number; scope?: string },\n): StoredUAToken {\n const now = Date.now();\n const expiresIn = tokens.expires_in ?? 7200;\n const refreshExpiresIn = 30 * 24 * 3600;\n return {\n userOpenId,\n appId: clientId,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? '',\n expiresAt: now + expiresIn * 1000,\n refreshExpiresAt: now + refreshExpiresIn * 1000,\n scope: tokens.scope ?? '',\n grantedAt: now,\n };\n}\n\nasync function isAlreadyAuthorized(senderOpenId: string): Promise<boolean> {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) return false;\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n return !!existing && projectTokenStatus(existing) !== 'expired';\n}\n\n/**\n * \u5C1D\u8BD5\u542F\u52A8\u672C\u5730\u56DE\u8C03\u670D\u52A1\u5668\u3002\n * \u6210\u529F\u8FD4\u56DE flow \u5BF9\u8C61\uFF1B\u5931\u8D25\uFF08\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\u7B49\uFF09\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F\u3002\n */\nasync function tryLocalAuth(mcpEndpoint: string) {\n try {\n return await startLocalAuthFlow(mcpEndpoint);\n } catch (err) {\n log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerFeishuProjectOAuthTool(api: OpenClawPluginApi) {\n if (!api.config) return;\n\n const cfg = api.config;\n\n api.registerTool(\n {\n name: 'feishu_project_oauth',\n label: 'Feishu Project OAuth',\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002' +\n '\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002' +\n '\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002',\n parameters: FeishuProjectOAuthSchema,\n\n async execute(_toolCallId: string, params: unknown) {\n const p = params as FeishuProjectOAuthParams;\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return json({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const accountId = ticket.accountId;\n const mcpEndpoint = getProjectMcpEndpoint(cfg);\n\n try {\n switch (p.action) {\n // ---------------------------------------------------------------\n // STATUS\n // ---------------------------------------------------------------\n case 'status': {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n if (!existing) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const status = projectTokenStatus(existing);\n return json({\n authorized: status !== 'expired',\n token_status: status,\n scope: existing.scope,\n granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : undefined,\n expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : undefined,\n });\n }\n\n // ---------------------------------------------------------------\n // AUTHORIZE\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\uFF09\n // ---------------------------------------------------------------\n case 'authorize': {\n if (await isAlreadyAuthorized(senderOpenId)) {\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002',\n authorized: true,\n });\n }\n\n const chatId = ticket.chatId;\n if (!chatId) {\n return json({ error: '\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807' });\n }\n\n // Cancel any existing local flow\n const key = fk(senderOpenId);\n const oldLocal = pendingLocalFlows.get(key);\n if (oldLocal) {\n oldLocal.superseded = true;\n oldLocal.controller.abort();\n await oldLocal.close().catch(() => {});\n pendingLocalFlows.delete(key);\n }\n pendingRemoteSessions.delete(key);\n\n // --- \u5C1D\u8BD5\u672C\u5730\u6A21\u5F0F ---\n const local = await tryLocalAuth(mcpEndpoint);\n\n if (local) {\n // \u672C\u5730\u6A21\u5F0F\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\u6210\u529F\n const { session, waitForCode, port, close } = local;\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 5,\n });\n const cardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!cardId) {\n await close();\n return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n }\n\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n\n const abortController = new AbortController();\n let seq = 1;\n const currentFlow: PendingLocalFlow = {\n controller: abortController,\n cardId,\n sequence: seq,\n superseded: false,\n close,\n };\n pendingLocalFlows.set(key, currentFlow);\n\n waitForCode()\n .then(async (result) => {\n if (currentFlow.superseded) return;\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n try {\n await updateCardKitCardForAuth({\n cfg, cardId, card: buildAuthSuccessCard(), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n })\n .catch(async (err) => {\n if (currentFlow.superseded) return;\n log.error(`project local auth failed: ${err}`);\n try {\n const msg = err instanceof Error ? err.message : String(err);\n await updateCardKitCardForAuth({\n cfg, cardId, card: buildAuthFailedCard(msg), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to failure: ${e}`);\n }\n })\n .finally(async () => {\n await close().catch(() => {});\n if (pendingLocalFlows.get(key) === currentFlow) {\n pendingLocalFlows.delete(key);\n }\n });\n\n return json({\n success: true,\n mode: 'local',\n message: '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002',\n awaiting_authorization: true,\n callback_port: port,\n });\n }\n\n // --- \u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F ---\n const session = await prepareRemoteAuth(mcpEndpoint);\n pendingRemoteSessions.set(key, session);\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 10,\n });\n const cardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (cardId) {\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n }\n\n return json({\n success: true,\n mode: 'remote',\n message:\n '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002' +\n '\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C' +\n '\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002',\n authorization_url: session.authorizationUrl,\n awaiting_callback_url: true,\n });\n }\n\n // ---------------------------------------------------------------\n // COMPLETE_AUTH\uFF08\u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u7528\u6237\u56DE\u4F20 callback URL\uFF09\n // ---------------------------------------------------------------\n case 'complete_auth': {\n if (!p.callback_url) {\n return json({\n error: '\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002',\n });\n }\n\n const key = fk(senderOpenId);\n const session = pendingRemoteSessions.get(key);\n if (!session) {\n return json({\n error: '\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n\n const result = await completeRemoteAuth(session, p.callback_url);\n pendingRemoteSessions.delete(key);\n\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01',\n authorized: true,\n });\n }\n\n // ---------------------------------------------------------------\n // REVOKE\n // ---------------------------------------------------------------\n case 'revoke': {\n const clientId = await getProjectClientId(senderOpenId);\n if (clientId) {\n await removeProjectStoredToken(clientId, senderOpenId);\n }\n return json({ success: true, message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002' });\n }\n\n default:\n return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${(p as { action: string }).action}` });\n }\n } catch (err) {\n log.error(`project oauth ${p.action} failed: ${err}`);\n return json({ error: formatLarkError(err) });\n }\n },\n },\n { name: 'feishu_project_oauth' },\n );\n\n api.logger.info?.('feishu_project_oauth: Registered feishu_project_oauth tool');\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["session", "authCard", "cardId"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { registerFeishuImBotImageTool } from "./resource";
|
|
2
|
+
function registerFeishuImTools(api) {
|
|
3
|
+
registerFeishuImBotImageTool(api);
|
|
4
|
+
api.logger.info?.("feishu_im: Registered feishu_im_bot_image");
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
registerFeishuImTools
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/tools/tat/im/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * IM \u5DE5\u5177\u96C6\n * \u7EDF\u4E00\u5BFC\u51FA\u6240\u6709\u5373\u65F6\u901A\u8BAF\u76F8\u5173\u5DE5\u5177\u7684\u6CE8\u518C\u51FD\u6570\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { registerFeishuImBotImageTool } from './resource';\n\n/**\n * \u6CE8\u518C\u6240\u6709 IM \u5DE5\u5177\n *\n * Note: feishu_im_message_reaction \u548C feishu_im_message_recall \u5DF2\u79FB\u9664\uFF0C\n * \u5176\u529F\u80FD\u7531 ChannelMessageActionAdapter (actions.ts) \u7684 react/delete action \u7EDF\u4E00\u8986\u76D6\u3002\n */\nexport function registerFeishuImTools(api: OpenClawPluginApi) {\n registerFeishuImBotImageTool(api);\n api.logger.info?.('feishu_im: Registered feishu_im_bot_image');\n}\n"],
|
|
5
|
+
"mappings": "AASA,SAAS,oCAAoC;AAQtC,SAAS,sBAAsB,KAAwB;AAC5D,+BAA6B,GAAG;AAChC,MAAI,OAAO,OAAO,2CAA2C;AAC/D;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { buildRandomTempFilePath } from "openclaw/plugin-sdk";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { json, createToolContext, formatLarkError } from "../../oapi/helpers";
|
|
4
|
+
import * as fsPromises from "node:fs/promises";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
const MIME_TO_EXT = {
|
|
7
|
+
"image/png": ".png",
|
|
8
|
+
"image/jpeg": ".jpg",
|
|
9
|
+
"image/jpg": ".jpg",
|
|
10
|
+
"image/gif": ".gif",
|
|
11
|
+
"image/webp": ".webp",
|
|
12
|
+
"image/svg+xml": ".svg",
|
|
13
|
+
"image/bmp": ".bmp",
|
|
14
|
+
"image/tiff": ".tiff",
|
|
15
|
+
"video/mp4": ".mp4",
|
|
16
|
+
"video/mpeg": ".mpeg",
|
|
17
|
+
"video/quicktime": ".mov",
|
|
18
|
+
"video/x-msvideo": ".avi",
|
|
19
|
+
"video/webm": ".webm",
|
|
20
|
+
"audio/mpeg": ".mp3",
|
|
21
|
+
"audio/wav": ".wav",
|
|
22
|
+
"audio/ogg": ".ogg",
|
|
23
|
+
"audio/mp4": ".m4a",
|
|
24
|
+
"application/pdf": ".pdf",
|
|
25
|
+
"application/msword": ".doc",
|
|
26
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
|
27
|
+
"application/vnd.ms-excel": ".xls",
|
|
28
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
29
|
+
"application/vnd.ms-powerpoint": ".ppt",
|
|
30
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
|
31
|
+
"application/zip": ".zip",
|
|
32
|
+
"application/x-rar-compressed": ".rar",
|
|
33
|
+
"text/plain": ".txt",
|
|
34
|
+
"application/json": ".json"
|
|
35
|
+
};
|
|
36
|
+
async function extractBuffer(res) {
|
|
37
|
+
let chunks;
|
|
38
|
+
if (typeof res.getReadableStream === "function") {
|
|
39
|
+
const stream = res.getReadableStream();
|
|
40
|
+
chunks = [];
|
|
41
|
+
for await (const chunk of stream) {
|
|
42
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
43
|
+
}
|
|
44
|
+
} else if (Buffer.isBuffer(res)) {
|
|
45
|
+
chunks = [res];
|
|
46
|
+
} else if (Buffer.isBuffer(res?.data)) {
|
|
47
|
+
chunks = [res.data];
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error("\u65E0\u6CD5\u4ECE\u54CD\u5E94\u4E2D\u63D0\u53D6\u4E8C\u8FDB\u5236\u6570\u636E");
|
|
50
|
+
}
|
|
51
|
+
const buffer = Buffer.concat(chunks);
|
|
52
|
+
const contentType = res.headers?.["content-type"] ?? "";
|
|
53
|
+
return { buffer, contentType };
|
|
54
|
+
}
|
|
55
|
+
async function saveToTempFile(buffer, contentType, prefix) {
|
|
56
|
+
const mimeType = contentType ? contentType.split(";")[0].trim() : "";
|
|
57
|
+
const mimeExt = mimeType ? MIME_TO_EXT[mimeType] : void 0;
|
|
58
|
+
const filePath = buildRandomTempFilePath({
|
|
59
|
+
prefix,
|
|
60
|
+
extension: mimeExt
|
|
61
|
+
});
|
|
62
|
+
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
63
|
+
await fsPromises.writeFile(filePath, buffer);
|
|
64
|
+
return filePath;
|
|
65
|
+
}
|
|
66
|
+
const FeishuImBotImageSchema = Type.Object({
|
|
67
|
+
message_id: Type.String({
|
|
68
|
+
description: "\u6D88\u606F ID\uFF08om_xxx \u683C\u5F0F\uFF09\uFF0C\u5F15\u7528\u6D88\u606F\u53EF\u4ECE\u4E0A\u4E0B\u6587\u4E2D\u7684 [message_id=om_xxx] \u63D0\u53D6"
|
|
69
|
+
}),
|
|
70
|
+
file_key: Type.String({
|
|
71
|
+
description: "\u8D44\u6E90 Key\uFF0C\u56FE\u7247\u6D88\u606F\u7684 image_key\uFF08img_xxx\uFF09\u6216\u6587\u4EF6\u6D88\u606F\u7684 file_key\uFF08file_xxx\uFF09"
|
|
72
|
+
}),
|
|
73
|
+
type: Type.Union([Type.Literal("image"), Type.Literal("file")], {
|
|
74
|
+
description: "\u8D44\u6E90\u7C7B\u578B\uFF1Aimage\uFF08\u56FE\u7247\u6D88\u606F\u4E2D\u7684\u56FE\u7247\uFF09\u3001file\uFF08\u6587\u4EF6/\u97F3\u9891/\u89C6\u9891\u6D88\u606F\u4E2D\u7684\u6587\u4EF6\uFF09"
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
function registerFeishuImBotImageTool(api) {
|
|
78
|
+
if (!api.config) return;
|
|
79
|
+
const { getClient, log } = createToolContext(api, "feishu_im_bot_image");
|
|
80
|
+
api.registerTool(
|
|
81
|
+
{
|
|
82
|
+
name: "feishu_im_bot_image",
|
|
83
|
+
label: "Feishu: IM Bot Image Download",
|
|
84
|
+
description: "\u3010\u4EE5\u673A\u5668\u4EBA\u8EAB\u4EFD\u3011\u4E0B\u8F7D\u98DE\u4E66 IM \u6D88\u606F\u4E2D\u7684\u56FE\u7247\u6216\u6587\u4EF6\u8D44\u6E90\u5230\u672C\u5730\u3002\n\n\u9002\u7528\u573A\u666F\uFF1A\u7528\u6237\u76F4\u63A5\u53D1\u9001\u7ED9\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3001\u7528\u6237\u5F15\u7528\u7684\u6D88\u606F\u3001\u673A\u5668\u4EBA\u6536\u5230\u7684\u7FA4\u804A\u6D88\u606F\u4E2D\u7684\u56FE\u7247/\u6587\u4EF6\u3002\u5373\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u4E2D\u51FA\u73B0\u7684 message_id \u548C image_key/file_key\uFF0C\u5E94\u4F7F\u7528\u672C\u5DE5\u5177\u4E0B\u8F7D\u3002\n\u5F15\u7528\u6D88\u606F\u7684 message_id \u53EF\u4ECE\u4E0A\u4E0B\u6587\u4E2D\u7684 [message_id=om_xxx] \u63D0\u53D6\uFF0C\u65E0\u9700\u5411\u7528\u6237\u8BE2\u95EE\u3002\n\n\u6587\u4EF6\u81EA\u52A8\u4FDD\u5B58\u5230 /tmp/openclaw/ \u4E0B\uFF0C\u8FD4\u56DE\u503C\u4E2D\u7684 saved_path \u4E3A\u5B9E\u9645\u4FDD\u5B58\u8DEF\u5F84\u3002",
|
|
85
|
+
parameters: FeishuImBotImageSchema,
|
|
86
|
+
async execute(_toolCallId, params) {
|
|
87
|
+
const p = params;
|
|
88
|
+
try {
|
|
89
|
+
const client = getClient();
|
|
90
|
+
log.info(`download: message_id="${p.message_id}", file_key="${p.file_key}", type="${p.type}"`);
|
|
91
|
+
const res = await client.im.messageResource.get({
|
|
92
|
+
path: {
|
|
93
|
+
message_id: p.message_id,
|
|
94
|
+
file_key: p.file_key
|
|
95
|
+
},
|
|
96
|
+
params: { type: p.type }
|
|
97
|
+
});
|
|
98
|
+
const { buffer, contentType } = await extractBuffer(res);
|
|
99
|
+
log.info(`download: ${buffer.length} bytes, content-type=${contentType}`);
|
|
100
|
+
const savedPath = await saveToTempFile(buffer, contentType, "bot-resource");
|
|
101
|
+
log.info(`download: saved to ${savedPath}`);
|
|
102
|
+
return json({
|
|
103
|
+
message_id: p.message_id,
|
|
104
|
+
file_key: p.file_key,
|
|
105
|
+
type: p.type,
|
|
106
|
+
size_bytes: buffer.length,
|
|
107
|
+
content_type: contentType,
|
|
108
|
+
saved_path: savedPath
|
|
109
|
+
});
|
|
110
|
+
} catch (err) {
|
|
111
|
+
log.error(`Error: ${formatLarkError(err)}`);
|
|
112
|
+
return json({ error: formatLarkError(err) });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{ name: "feishu_im_bot_image" }
|
|
117
|
+
);
|
|
118
|
+
api.logger.info?.("feishu_im_bot_image: Registered feishu_im_bot_image tool");
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
registerFeishuImBotImageTool
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=resource.js.map
|