@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,124 @@
|
|
|
1
|
+
import { runDiagnosis, formatDiagReportText } from "./diagnose";
|
|
2
|
+
import { runFeishuDoctor } from "./doctor";
|
|
3
|
+
import { runFeishuAuth } from "./auth";
|
|
4
|
+
import { getPluginVersion } from "../core/version";
|
|
5
|
+
function registerCommands(api) {
|
|
6
|
+
api.registerCommand({
|
|
7
|
+
name: "feishu_diagnose",
|
|
8
|
+
description: "\u8FD0\u884C\u98DE\u4E66\u63D2\u4EF6\u8BCA\u65AD\uFF0C\u68C0\u67E5\u914D\u7F6E\u3001\u8FDE\u901A\u6027\u548C\u6743\u9650\u72B6\u6001",
|
|
9
|
+
acceptsArgs: false,
|
|
10
|
+
requireAuth: true,
|
|
11
|
+
async handler(ctx) {
|
|
12
|
+
try {
|
|
13
|
+
const report = await runDiagnosis({ config: ctx.config });
|
|
14
|
+
return { text: formatDiagReportText(report) };
|
|
15
|
+
} catch (err) {
|
|
16
|
+
return {
|
|
17
|
+
text: `\u8BCA\u65AD\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
api.registerCommand({
|
|
23
|
+
name: "feishu_doctor",
|
|
24
|
+
description: "\u8FD0\u884C\u98DE\u4E66\u63D2\u4EF6\u8BCA\u65AD",
|
|
25
|
+
acceptsArgs: false,
|
|
26
|
+
requireAuth: true,
|
|
27
|
+
async handler(ctx) {
|
|
28
|
+
try {
|
|
29
|
+
const markdown = await runFeishuDoctor(ctx.config, ctx.accountId);
|
|
30
|
+
return { text: markdown };
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
text: `\u8BCA\u65AD\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
api.registerCommand({
|
|
39
|
+
name: "feishu_auth",
|
|
40
|
+
description: "\u98DE\u4E66\u7528\u6237\u6743\u9650\u6279\u91CF\u6388\u6743",
|
|
41
|
+
acceptsArgs: false,
|
|
42
|
+
requireAuth: true,
|
|
43
|
+
async handler(ctx) {
|
|
44
|
+
try {
|
|
45
|
+
const result = await runFeishuAuth(ctx.config);
|
|
46
|
+
return { text: result };
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return {
|
|
49
|
+
text: `\u6388\u6743\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
api.registerCommand({
|
|
55
|
+
name: "feishu",
|
|
56
|
+
description: "\u98DE\u4E66\u63D2\u4EF6\u547D\u4EE4\uFF08\u652F\u6301\u5B50\u547D\u4EE4: auth, doctor, start\uFF09",
|
|
57
|
+
acceptsArgs: true,
|
|
58
|
+
requireAuth: true,
|
|
59
|
+
async handler(ctx) {
|
|
60
|
+
const args = ctx.args?.trim().split(/\s+/) || [];
|
|
61
|
+
const subcommand = args[0]?.toLowerCase();
|
|
62
|
+
try {
|
|
63
|
+
if (subcommand === "auth" || subcommand === "onboarding") {
|
|
64
|
+
const result = await runFeishuAuth(ctx.config);
|
|
65
|
+
return { text: result };
|
|
66
|
+
}
|
|
67
|
+
if (subcommand === "doctor") {
|
|
68
|
+
const markdown = await runFeishuDoctor(ctx.config, ctx.accountId);
|
|
69
|
+
return { text: markdown };
|
|
70
|
+
}
|
|
71
|
+
if (subcommand === "start") {
|
|
72
|
+
const cfg = ctx.config;
|
|
73
|
+
const errors = [];
|
|
74
|
+
const warnings = [];
|
|
75
|
+
const feishuEntry = cfg.plugins?.entries?.feishu;
|
|
76
|
+
if (feishuEntry && feishuEntry.enabled !== false) {
|
|
77
|
+
errors.push(
|
|
78
|
+
"\u274C \u68C0\u6D4B\u5230\u65E7\u7248\u63D2\u4EF6\u672A\u7981\u7528\u3002\n\u{1F449} \u8BF7\u4F9D\u6B21\u8FD0\u884C\u547D\u4EE4\uFF1A\n```\nopenclaw config set plugins.entries.feishu.enabled false --json\nopenclaw gateway restart\n```"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const profile = cfg.tools?.profile;
|
|
82
|
+
const incompleteProfiles = /* @__PURE__ */ new Set(["minimal", "coding", "messaging"]);
|
|
83
|
+
if (profile && incompleteProfiles.has(profile)) {
|
|
84
|
+
warnings.push(`\u26A0\uFE0F \u5DE5\u5177 Profile \u5F53\u524D\u4E3A \`${profile}\`\uFF0C\u98DE\u4E66\u5DE5\u5177\u53EF\u80FD\u65E0\u6CD5\u52A0\u8F7D\u3002\u8BF7\u68C0\u67E5\u914D\u7F6E\u662F\u5426\u6B63\u786E\u3002
|
|
85
|
+
`);
|
|
86
|
+
}
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
const all = [...errors, ...warnings];
|
|
89
|
+
return {
|
|
90
|
+
text: `\u274C \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u542F\u52A8\u5931\u8D25\uFF1A
|
|
91
|
+
|
|
92
|
+
${all.join("\n\n")}`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (warnings.length > 0) {
|
|
96
|
+
return {
|
|
97
|
+
text: `\u26A0\uFE0F \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u5DF2\u542F\u52A8 v${getPluginVersion()}\uFF08\u5B58\u5728\u8B66\u544A\uFF09
|
|
98
|
+
|
|
99
|
+
${warnings.join("\n\n")}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { text: `\u2705 \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u5DF2\u542F\u52A8 v${getPluginVersion()}` };
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
text: `\u98DE\u4E66OpenClaw\u63D2\u4EF6 v${getPluginVersion()}
|
|
106
|
+
|
|
107
|
+
\u7528\u6CD5\uFF1A
|
|
108
|
+
/feishu start - \u6821\u9A8C\u63D2\u4EF6\u914D\u7F6E
|
|
109
|
+
/feishu auth - \u6279\u91CF\u6388\u6743\u7528\u6237\u6743\u9650
|
|
110
|
+
/feishu doctor - \u8FD0\u884C\u8BCA\u65AD
|
|
111
|
+
/feishu help - \u663E\u793A\u6B64\u5E2E\u52A9`
|
|
112
|
+
};
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
text: `\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
registerCommands
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/commands/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Register all chat commands (/feishu_diagnose, /feishu_doctor, /feishu_auth, /feishu).\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { runDiagnosis, formatDiagReportText } from './diagnose';\nimport { runFeishuDoctor } from './doctor';\nimport { runFeishuAuth } from './auth';\nimport { getPluginVersion } from '../core/version';\n\n\nexport function registerCommands(api: OpenClawPluginApi): void {\n // /feishu_diagnose\n api.registerCommand({\n name: 'feishu_diagnose',\n description: '\u8FD0\u884C\u98DE\u4E66\u63D2\u4EF6\u8BCA\u65AD\uFF0C\u68C0\u67E5\u914D\u7F6E\u3001\u8FDE\u901A\u6027\u548C\u6743\u9650\u72B6\u6001',\n acceptsArgs: false,\n requireAuth: true,\n async handler(ctx) {\n try {\n const report = await runDiagnosis({ config: ctx.config });\n return { text: formatDiagReportText(report) };\n } catch (err) {\n return {\n text: `\u8BCA\u65AD\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n\n // /feishu_doctor\n api.registerCommand({\n name: 'feishu_doctor',\n description: '\u8FD0\u884C\u98DE\u4E66\u63D2\u4EF6\u8BCA\u65AD',\n acceptsArgs: false,\n requireAuth: true,\n async handler(ctx) {\n try {\n const markdown = await runFeishuDoctor(ctx.config, ctx.accountId);\n return { text: markdown };\n } catch (err) {\n return {\n text: `\u8BCA\u65AD\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n\n // /feishu_auth\n api.registerCommand({\n name: 'feishu_auth',\n description: '\u98DE\u4E66\u7528\u6237\u6743\u9650\u6279\u91CF\u6388\u6743',\n acceptsArgs: false,\n requireAuth: true,\n async handler(ctx) {\n try {\n const result = await runFeishuAuth(ctx.config);\n return { text: result };\n } catch (err) {\n return {\n text: `\u6388\u6743\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n\n // /feishu (\u7EDF\u4E00\u5165\u53E3\uFF0C\u652F\u6301\u5B50\u547D\u4EE4)\n api.registerCommand({\n name: 'feishu',\n description: '\u98DE\u4E66\u63D2\u4EF6\u547D\u4EE4\uFF08\u652F\u6301\u5B50\u547D\u4EE4: auth, doctor, start\uFF09',\n acceptsArgs: true,\n requireAuth: true,\n async handler(ctx) {\n const args = ctx.args?.trim().split(/\\s+/) || [];\n const subcommand = args[0]?.toLowerCase();\n\n try {\n // /feishu auth \u6216 /feishu onboarding\n if (subcommand === 'auth' || subcommand === 'onboarding') {\n const result = await runFeishuAuth(ctx.config);\n return { text: result };\n }\n\n // /feishu doctor\n if (subcommand === 'doctor') {\n const markdown = await runFeishuDoctor(ctx.config, ctx.accountId);\n return { text: markdown };\n }\n\n // /feishu start\n if (subcommand === 'start') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const cfg = ctx.config as any;\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // \u68C0\u67E5\u65E7\u7248\u63D2\u4EF6\u662F\u5426\u5DF2\u7981\u7528 (error)\n const feishuEntry = cfg.plugins?.entries?.feishu;\n if (feishuEntry && feishuEntry.enabled !== false) {\n errors.push(\n '\u274C \u68C0\u6D4B\u5230\u65E7\u7248\u63D2\u4EF6\u672A\u7981\u7528\u3002\\n' +\n '\uD83D\uDC49 \u8BF7\u4F9D\u6B21\u8FD0\u884C\u547D\u4EE4\uFF1A\\n' +\n '```\\n' +\n 'openclaw config set plugins.entries.feishu.enabled false --json\\n' +\n 'openclaw gateway restart\\n' +\n '```',\n );\n }\n\n // \u68C0\u67E5 tools.profile (warning)\n const profile: string | undefined = cfg.tools?.profile;\n const incompleteProfiles = new Set(['minimal', 'coding', 'messaging']);\n if (profile && incompleteProfiles.has(profile)) {\n warnings.push(`\u26A0\uFE0F \u5DE5\u5177 Profile \u5F53\u524D\u4E3A \\`${profile}\\`\uFF0C\u98DE\u4E66\u5DE5\u5177\u53EF\u80FD\u65E0\u6CD5\u52A0\u8F7D\u3002\u8BF7\u68C0\u67E5\u914D\u7F6E\u662F\u5426\u6B63\u786E\u3002\\n`);\n }\n\n if (errors.length > 0) {\n const all = [...errors, ...warnings];\n return {\n text: `\u274C \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u542F\u52A8\u5931\u8D25\uFF1A\\n\\n${all.join('\\n\\n')}`,\n };\n }\n\n if (warnings.length > 0) {\n return {\n text: `\u26A0\uFE0F \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u5DF2\u542F\u52A8 v${getPluginVersion()}\uFF08\u5B58\u5728\u8B66\u544A\uFF09\\n\\n${warnings.join('\\n\\n')}`,\n };\n }\n\n return { text: `\u2705 \u98DE\u4E66 OpenClaw \u63D2\u4EF6\u5DF2\u542F\u52A8 v${getPluginVersion()}` };\n }\n\n // /feishu help \u6216\u65E0\u6548\u5B50\u547D\u4EE4\u6216\u65E0\u53C2\u6570\n return {\n text:\n `\u98DE\u4E66OpenClaw\u63D2\u4EF6 v${getPluginVersion()}\\n\\n` +\n '\u7528\u6CD5\uFF1A\\n' +\n ' /feishu start - \u6821\u9A8C\u63D2\u4EF6\u914D\u7F6E\\n' +\n ' /feishu auth - \u6279\u91CF\u6388\u6743\u7528\u6237\u6743\u9650\\n' +\n ' /feishu doctor - \u8FD0\u884C\u8BCA\u65AD\\n' +\n ' /feishu help - \u663E\u793A\u6B64\u5E2E\u52A9',\n };\n } catch (err) {\n return {\n text: `\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
|
|
2
|
+
function getLarkConfig(cfg) {
|
|
3
|
+
return cfg?.channels?.feishu;
|
|
4
|
+
}
|
|
5
|
+
function getAccountMap(section) {
|
|
6
|
+
return section.accounts;
|
|
7
|
+
}
|
|
8
|
+
function baseConfig(section) {
|
|
9
|
+
const { accounts: _ignored, ...rest } = section;
|
|
10
|
+
return rest;
|
|
11
|
+
}
|
|
12
|
+
function mergeAccountConfig(base, override) {
|
|
13
|
+
const merged = { ...base, ...override };
|
|
14
|
+
if (override.groupPolicy === "open") {
|
|
15
|
+
if (!("groups" in override)) merged.groups = void 0;
|
|
16
|
+
if (!("groupAllowFrom" in override)) merged.groupAllowFrom = void 0;
|
|
17
|
+
}
|
|
18
|
+
if (override.dmPolicy === "open" && !("allowFrom" in override)) {
|
|
19
|
+
merged.allowFrom = ["*"];
|
|
20
|
+
}
|
|
21
|
+
return merged;
|
|
22
|
+
}
|
|
23
|
+
function toBrand(domain) {
|
|
24
|
+
return domain ?? "feishu";
|
|
25
|
+
}
|
|
26
|
+
function getLarkAccountIds(cfg) {
|
|
27
|
+
const section = getLarkConfig(cfg);
|
|
28
|
+
if (!section) return [DEFAULT_ACCOUNT_ID];
|
|
29
|
+
const accountMap = getAccountMap(section);
|
|
30
|
+
if (!accountMap || Object.keys(accountMap).length === 0) {
|
|
31
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
32
|
+
}
|
|
33
|
+
const accountIds = Object.keys(accountMap);
|
|
34
|
+
const hasDefault = accountIds.some((id) => id.trim().toLowerCase() === DEFAULT_ACCOUNT_ID);
|
|
35
|
+
if (!hasDefault) {
|
|
36
|
+
const base = baseConfig(section);
|
|
37
|
+
if (base.appId && base.appSecret) {
|
|
38
|
+
return [DEFAULT_ACCOUNT_ID, ...accountIds];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return accountIds;
|
|
42
|
+
}
|
|
43
|
+
function getDefaultLarkAccountId(cfg) {
|
|
44
|
+
return getLarkAccountIds(cfg)[0];
|
|
45
|
+
}
|
|
46
|
+
function getLarkAccount(cfg, accountId) {
|
|
47
|
+
const requestedId = accountId ? normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID : DEFAULT_ACCOUNT_ID;
|
|
48
|
+
const section = getLarkConfig(cfg);
|
|
49
|
+
if (!section) {
|
|
50
|
+
return {
|
|
51
|
+
accountId: requestedId,
|
|
52
|
+
enabled: false,
|
|
53
|
+
configured: false,
|
|
54
|
+
brand: "feishu",
|
|
55
|
+
config: {}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const base = baseConfig(section);
|
|
59
|
+
const accountMap = getAccountMap(section);
|
|
60
|
+
const accountOverride = accountMap && requestedId !== DEFAULT_ACCOUNT_ID ? accountMap[requestedId] : void 0;
|
|
61
|
+
const merged = accountOverride ? mergeAccountConfig(base, accountOverride) : { ...base };
|
|
62
|
+
const appId = merged.appId;
|
|
63
|
+
const appSecret = merged.appSecret;
|
|
64
|
+
const configured = !!(appId && appSecret);
|
|
65
|
+
const enabled = !!(merged.enabled ?? configured);
|
|
66
|
+
const brand = toBrand(merged.domain);
|
|
67
|
+
if (configured) {
|
|
68
|
+
return {
|
|
69
|
+
accountId: requestedId,
|
|
70
|
+
enabled,
|
|
71
|
+
configured: true,
|
|
72
|
+
name: merged.name ?? void 0,
|
|
73
|
+
appId,
|
|
74
|
+
appSecret,
|
|
75
|
+
encryptKey: merged.encryptKey ?? void 0,
|
|
76
|
+
verificationToken: merged.verificationToken ?? void 0,
|
|
77
|
+
brand,
|
|
78
|
+
config: merged
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
accountId: requestedId,
|
|
83
|
+
enabled,
|
|
84
|
+
configured: false,
|
|
85
|
+
name: merged.name ?? void 0,
|
|
86
|
+
appId: appId ?? void 0,
|
|
87
|
+
appSecret: appSecret ?? void 0,
|
|
88
|
+
encryptKey: merged.encryptKey ?? void 0,
|
|
89
|
+
verificationToken: merged.verificationToken ?? void 0,
|
|
90
|
+
brand,
|
|
91
|
+
config: merged
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function getEnabledLarkAccounts(cfg) {
|
|
95
|
+
const ids = getLarkAccountIds(cfg);
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const id of ids) {
|
|
98
|
+
const account = getLarkAccount(cfg, id);
|
|
99
|
+
if (account.enabled && account.configured) {
|
|
100
|
+
results.push(account);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
function getLarkCredentials(feishuCfg) {
|
|
106
|
+
if (!feishuCfg) return null;
|
|
107
|
+
const appId = feishuCfg.appId;
|
|
108
|
+
const appSecret = feishuCfg.appSecret;
|
|
109
|
+
if (!appId || !appSecret) return null;
|
|
110
|
+
return {
|
|
111
|
+
appId,
|
|
112
|
+
appSecret,
|
|
113
|
+
encryptKey: feishuCfg.encryptKey ?? void 0,
|
|
114
|
+
verificationToken: feishuCfg.verificationToken ?? void 0,
|
|
115
|
+
brand: toBrand(feishuCfg.domain)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function isConfigured(account) {
|
|
119
|
+
return account.configured;
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
getDefaultLarkAccountId,
|
|
123
|
+
getEnabledLarkAccounts,
|
|
124
|
+
getLarkAccount,
|
|
125
|
+
getLarkAccountIds,
|
|
126
|
+
getLarkCredentials,
|
|
127
|
+
isConfigured
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/accounts.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Lark multi-account management.\n *\n * Account overrides live under `cfg.channels.feishu.accounts`.\n * Each account may override any top-level Feishu config field;\n * unset fields fall back to the top-level defaults.\n */\n\nimport { DEFAULT_ACCOUNT_ID, normalizeAccountId } from 'openclaw/plugin-sdk';\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\n\nimport type { FeishuConfig, LarkBrand, LarkAccount, LarkCredentials, ConfiguredLarkAccount } from './types';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the `channels.feishu` section from the top-level config. */\nfunction getLarkConfig(cfg: ClawdbotConfig): FeishuConfig | undefined {\n return cfg?.channels?.feishu as FeishuConfig | undefined;\n}\n\n/** Return the per-account override map, if present. */\nfunction getAccountMap(section: FeishuConfig): Record<string, Partial<FeishuConfig>> | undefined {\n return (section as FeishuConfig & { accounts?: Record<string, Partial<FeishuConfig>> }).accounts;\n}\n\n/** Strip the `accounts` key and return the remaining top-level config. */\nfunction baseConfig(section: FeishuConfig): Omit<FeishuConfig, 'accounts'> {\n const { accounts: _ignored, ...rest } = section as FeishuConfig & {\n accounts?: Record<string, unknown>;\n };\n return rest;\n}\n\n/**\n * \u5408\u5E76 base config \u4E0E account override\u3002\n *\n * \u5F53 account \u5C06\u7B56\u7565\u8BBE\u4E3A \"open\" \u65F6\uFF0C\u5254\u9664\u4ECE base \u7EE7\u627F\u7684\u9650\u5236\u6027\u5B57\u6BB5\uFF0C\n * \u907F\u514D\u4E0E \"open\" \u8BED\u4E49\u51B2\u7A81\u3002\n */\nfunction mergeAccountConfig(base: Omit<FeishuConfig, 'accounts'>, override: Partial<FeishuConfig>): FeishuConfig {\n const merged = { ...base, ...override } as FeishuConfig;\n\n if (override.groupPolicy === 'open') {\n if (!('groups' in override)) merged.groups = undefined;\n if (!('groupAllowFrom' in override)) merged.groupAllowFrom = undefined;\n }\n\n if (override.dmPolicy === 'open' && !('allowFrom' in override)) {\n merged.allowFrom = ['*'];\n }\n\n return merged;\n}\n\n/** Coerce a domain string to `LarkBrand`, defaulting to `\"feishu\"`. */\nfunction toBrand(domain: string | undefined): LarkBrand {\n return (domain as LarkBrand) ?? 'feishu';\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * List all account IDs defined in the Lark config.\n *\n * Returns `[DEFAULT_ACCOUNT_ID]` when no explicit accounts exist.\n */\nexport function getLarkAccountIds(cfg: ClawdbotConfig): string[] {\n const section = getLarkConfig(cfg);\n if (!section) return [DEFAULT_ACCOUNT_ID];\n\n const accountMap = getAccountMap(section);\n if (!accountMap || Object.keys(accountMap).length === 0) {\n return [DEFAULT_ACCOUNT_ID];\n }\n\n const accountIds = Object.keys(accountMap);\n\n // \u5F53 accounts \u5B58\u5728\u65F6\uFF0C\u5982\u679C\u9876\u5C42\u4E5F\u914D\u7F6E\u4E86 appId/appSecret\uFF08\u5373\u9ED8\u8BA4\u673A\u5668\u4EBA\uFF09\uFF0C\n // \u5C06 DEFAULT_ACCOUNT_ID \u52A0\u5165\u5217\u8868\uFF0C\u786E\u4FDD\u9876\u5C42\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u5FFD\u7565\u3002\n // \u4F46\u5982\u679C accountMap \u5DF2\u7ECF\u5305\u542B default\uFF0C\u5219\u4E0D\u91CD\u590D\u6DFB\u52A0\u3002\n const hasDefault = accountIds.some((id) => id.trim().toLowerCase() === DEFAULT_ACCOUNT_ID);\n if (!hasDefault) {\n const base = baseConfig(section);\n if (base.appId && base.appSecret) {\n return [DEFAULT_ACCOUNT_ID, ...accountIds];\n }\n }\n\n return accountIds;\n}\n\n/** Return the first (default) account ID. */\nexport function getDefaultLarkAccountId(cfg: ClawdbotConfig): string {\n return getLarkAccountIds(cfg)[0];\n}\n\n/**\n * Resolve a single account by merging the top-level config with\n * account-level overrides. Account fields take precedence.\n *\n * Falls back to the default account when `accountId` is omitted or `null`.\n */\nexport function getLarkAccount(cfg: ClawdbotConfig, accountId?: string | null): LarkAccount {\n const requestedId = accountId ? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID) : DEFAULT_ACCOUNT_ID;\n\n const section = getLarkConfig(cfg);\n\n if (!section) {\n return {\n accountId: requestedId,\n enabled: false,\n configured: false,\n brand: 'feishu',\n config: {} as FeishuConfig,\n };\n }\n\n const base = baseConfig(section);\n const accountMap = getAccountMap(section);\n const accountOverride =\n accountMap && requestedId !== DEFAULT_ACCOUNT_ID\n ? (accountMap[requestedId] as Partial<FeishuConfig> | undefined)\n : undefined;\n\n const merged: FeishuConfig = accountOverride\n ? mergeAccountConfig(base, accountOverride)\n : ({ ...base } as FeishuConfig);\n\n const appId = merged.appId;\n const appSecret = merged.appSecret;\n const configured = !!(appId && appSecret);\n\n // Respect explicit `enabled` when set; otherwise derive from `configured`.\n const enabled = !!(merged.enabled ?? configured);\n\n const brand: LarkBrand = toBrand(merged.domain);\n\n if (configured) {\n return {\n accountId: requestedId,\n enabled,\n configured: true,\n name: merged.name ?? undefined,\n appId: appId!,\n appSecret: appSecret!,\n encryptKey: merged.encryptKey ?? undefined,\n verificationToken: merged.verificationToken ?? undefined,\n brand,\n config: merged,\n };\n }\n\n return {\n accountId: requestedId,\n enabled,\n configured: false,\n name: merged.name ?? undefined,\n appId: appId ?? undefined,\n appSecret: appSecret ?? undefined,\n encryptKey: merged.encryptKey ?? undefined,\n verificationToken: merged.verificationToken ?? undefined,\n brand,\n config: merged,\n };\n}\n\n/** Return all accounts that are both configured and enabled. */\nexport function getEnabledLarkAccounts(cfg: ClawdbotConfig): LarkAccount[] {\n const ids = getLarkAccountIds(cfg);\n const results: LarkAccount[] = [];\n\n for (const id of ids) {\n const account = getLarkAccount(cfg, id);\n if (account.enabled && account.configured) {\n results.push(account);\n }\n }\n\n return results;\n}\n\n/**\n * Extract API credentials from a Feishu config fragment.\n *\n * Returns `null` when `appId` or `appSecret` is missing.\n */\nexport function getLarkCredentials(feishuCfg?: FeishuConfig): LarkCredentials | null {\n if (!feishuCfg) return null;\n\n const appId = feishuCfg.appId;\n const appSecret = feishuCfg.appSecret;\n\n if (!appId || !appSecret) return null;\n\n return {\n appId,\n appSecret,\n encryptKey: feishuCfg.encryptKey ?? undefined,\n verificationToken: feishuCfg.verificationToken ?? undefined,\n brand: toBrand(feishuCfg.domain),\n };\n}\n\n/** Type guard: narrow `LarkAccount` to `ConfiguredLarkAccount`. */\nexport function isConfigured(account: LarkAccount): account is ConfiguredLarkAccount {\n return account.configured;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function listConfiguredAgents(cfg) {
|
|
2
|
+
const agents = cfg.agents;
|
|
3
|
+
return agents?.list ?? [];
|
|
4
|
+
}
|
|
5
|
+
function resolveAgentEntry(cfg, agentId) {
|
|
6
|
+
return listConfiguredAgents(cfg).find((a) => a.id === agentId);
|
|
7
|
+
}
|
|
8
|
+
function getAgentDisplayName(cfg, agentId) {
|
|
9
|
+
const entry = resolveAgentEntry(cfg, agentId);
|
|
10
|
+
if (!entry) return void 0;
|
|
11
|
+
return entry.identity?.name ?? entry.name;
|
|
12
|
+
}
|
|
13
|
+
function getAgentSkillsFilter(cfg, agentId) {
|
|
14
|
+
return resolveAgentEntry(cfg, agentId)?.skills;
|
|
15
|
+
}
|
|
16
|
+
function getAgentToolsPolicy(cfg, agentId) {
|
|
17
|
+
const entry = resolveAgentEntry(cfg, agentId);
|
|
18
|
+
if (!entry?.tools) return void 0;
|
|
19
|
+
const { allow, deny } = entry.tools;
|
|
20
|
+
if (!allow && !deny) return void 0;
|
|
21
|
+
return { allow, deny };
|
|
22
|
+
}
|
|
23
|
+
function mergeSkillFilters(agentSkills, groupSkills) {
|
|
24
|
+
if (!agentSkills && !groupSkills) return void 0;
|
|
25
|
+
if (!agentSkills) return groupSkills;
|
|
26
|
+
if (!groupSkills) return agentSkills;
|
|
27
|
+
const agentSet = new Set(agentSkills);
|
|
28
|
+
return groupSkills.filter((s) => agentSet.has(s));
|
|
29
|
+
}
|
|
30
|
+
function isToolAllowedByPolicy(toolName, policy) {
|
|
31
|
+
if (!policy) return true;
|
|
32
|
+
if (policy.deny && policy.deny.length > 0) {
|
|
33
|
+
if (matchesAnyPattern(toolName, policy.deny)) return false;
|
|
34
|
+
}
|
|
35
|
+
if (policy.allow && policy.allow.length > 0) {
|
|
36
|
+
return matchesAnyPattern(toolName, policy.allow);
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function matchesAnyPattern(value, patterns) {
|
|
41
|
+
for (const pattern of patterns) {
|
|
42
|
+
if (pattern === "*") return true;
|
|
43
|
+
if (pattern.endsWith("*")) {
|
|
44
|
+
if (value.startsWith(pattern.slice(0, -1))) return true;
|
|
45
|
+
} else if (value === pattern) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
getAgentDisplayName,
|
|
53
|
+
getAgentSkillsFilter,
|
|
54
|
+
getAgentToolsPolicy,
|
|
55
|
+
isToolAllowedByPolicy,
|
|
56
|
+
listConfiguredAgents,
|
|
57
|
+
mergeSkillFilters,
|
|
58
|
+
resolveAgentEntry
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=agent-config.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/agent-config.ts"],
|
|
4
|
+
"sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Agent configuration helpers for the Lark/Feishu channel plugin.\n *\n * Reads agent-level configuration (identity, skills, tools, subagents)\n * from the top-level `agents.list` in OpenClawConfig. These helpers\n * bridge the gap between the SDK's agent infrastructure and the Feishu\n * plugin's dispatch/reply layers.\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\n\n// ---------------------------------------------------------------------------\n// Internal types (mirroring SDK agent config shape without importing internals)\n// ---------------------------------------------------------------------------\n\n/** Minimal agent identity fields used by the Feishu plugin. */\ninterface AgentIdentity {\n name?: string;\n emoji?: string;\n avatar?: string;\n}\n\n/** Minimal agent tools policy fields. */\ninterface AgentToolsPolicy {\n allow?: string[];\n deny?: string[];\n}\n\n/** Shape of an agent entry in `config.agents.list`. */\ninterface AgentEntry {\n id: string;\n name?: string;\n skills?: string[];\n identity?: AgentIdentity;\n tools?: AgentToolsPolicy & Record<string, unknown>;\n subagents?: {\n allowAgents?: string[];\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Retrieve the full list of configured agents from config.\n *\n * @param cfg - The top-level application config.\n * @returns Array of agent entries, or empty array if none configured.\n */\nexport function listConfiguredAgents(cfg: ClawdbotConfig): AgentEntry[] {\n const agents = (cfg as Record<string, unknown>).agents as { list?: AgentEntry[] } | undefined;\n return agents?.list ?? [];\n}\n\n/**\n * Look up a specific agent's configuration by its ID.\n *\n * @param cfg - The top-level application config.\n * @param agentId - The agent ID to search for.\n * @returns The matching agent entry, or `undefined` if not found.\n */\nexport function resolveAgentEntry(cfg: ClawdbotConfig, agentId: string): AgentEntry | undefined {\n return listConfiguredAgents(cfg).find((a) => a.id === agentId);\n}\n\n/**\n * Resolve a human-readable display name for an agent.\n *\n * Priority: `identity.name` > `name` > `undefined`.\n *\n * @param cfg - The top-level application config.\n * @param agentId - The agent ID.\n * @returns The display name, or `undefined` if none configured.\n */\nexport function getAgentDisplayName(cfg: ClawdbotConfig, agentId: string): string | undefined {\n const entry = resolveAgentEntry(cfg, agentId);\n if (!entry) return undefined;\n return entry.identity?.name ?? entry.name;\n}\n\n/**\n * Resolve the per-agent skills filter.\n *\n * @param cfg - The top-level application config.\n * @param agentId - The agent ID.\n * @returns Skill allowlist, or `undefined` if no agent-level filter.\n */\nexport function getAgentSkillsFilter(cfg: ClawdbotConfig, agentId: string): string[] | undefined {\n return resolveAgentEntry(cfg, agentId)?.skills;\n}\n\n/**\n * Resolve the per-agent tools policy (allow/deny lists).\n *\n * @param cfg - The top-level application config.\n * @param agentId - The agent ID.\n * @returns Tools policy object, or `undefined` if none configured.\n */\nexport function getAgentToolsPolicy(cfg: ClawdbotConfig, agentId: string): AgentToolsPolicy | undefined {\n const entry = resolveAgentEntry(cfg, agentId);\n if (!entry?.tools) return undefined;\n const { allow, deny } = entry.tools;\n if (!allow && !deny) return undefined;\n return { allow, deny };\n}\n\n/**\n * Merge agent-level and group-level skill filters.\n *\n * When both are present, the effective filter is the intersection:\n * a skill must appear in both lists to be included. When only one\n * is present, that list is used as-is.\n *\n * @param agentSkills - Per-agent skill allowlist (from AgentConfig.skills).\n * @param groupSkills - Per-group skill allowlist (from FeishuGroupConfig.skills).\n * @returns Merged skill filter, or `undefined` if neither is set.\n */\nexport function mergeSkillFilters(\n agentSkills: string[] | undefined,\n groupSkills: string[] | undefined,\n): string[] | undefined {\n if (!agentSkills && !groupSkills) return undefined;\n if (!agentSkills) return groupSkills;\n if (!groupSkills) return agentSkills;\n\n // Intersection: group filter narrows the agent filter.\n const agentSet = new Set(agentSkills);\n return groupSkills.filter((s) => agentSet.has(s));\n}\n\n/**\n * Check whether a tool name is permitted by an agent's tool policy.\n *\n * Evaluation order:\n * 1. If `deny` list exists and tool matches \u2192 denied.\n * 2. If `allow` list exists and tool does NOT match \u2192 denied.\n * 3. Otherwise \u2192 allowed.\n *\n * Supports glob-like patterns with trailing `*` (e.g. `feishu_calendar_*`).\n *\n * @param toolName - The tool name being invoked.\n * @param policy - The agent's tool policy.\n * @returns `true` if the tool is allowed, `false` if denied.\n */\nexport function isToolAllowedByPolicy(toolName: string, policy: AgentToolsPolicy | undefined): boolean {\n if (!policy) return true;\n\n if (policy.deny && policy.deny.length > 0) {\n if (matchesAnyPattern(toolName, policy.deny)) return false;\n }\n\n if (policy.allow && policy.allow.length > 0) {\n return matchesAnyPattern(toolName, policy.allow);\n }\n\n return true;\n}\n\n/**\n * Check whether a string matches any of the given patterns.\n * Supports trailing `*` as a simple wildcard.\n */\nfunction matchesAnyPattern(value: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === '*') return true;\n if (pattern.endsWith('*')) {\n if (value.startsWith(pattern.slice(0, -1))) return true;\n } else if (value === pattern) {\n return true;\n }\n }\n return false;\n}\n"],
|
|
5
|
+
"mappings": "AAmDO,SAAS,qBAAqB,KAAmC;AACtE,QAAM,SAAU,IAAgC;AAChD,SAAO,QAAQ,QAAQ,CAAC;AAC1B;AASO,SAAS,kBAAkB,KAAqB,SAAyC;AAC9F,SAAO,qBAAqB,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC/D;AAWO,SAAS,oBAAoB,KAAqB,SAAqC;AAC5F,QAAM,QAAQ,kBAAkB,KAAK,OAAO;AAC5C,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,UAAU,QAAQ,MAAM;AACvC;AASO,SAAS,qBAAqB,KAAqB,SAAuC;AAC/F,SAAO,kBAAkB,KAAK,OAAO,GAAG;AAC1C;AASO,SAAS,oBAAoB,KAAqB,SAA+C;AACtG,QAAM,QAAQ,kBAAkB,KAAK,OAAO;AAC5C,MAAI,CAAC,OAAO,MAAO,QAAO;AAC1B,QAAM,EAAE,OAAO,KAAK,IAAI,MAAM;AAC9B,MAAI,CAAC,SAAS,CAAC,KAAM,QAAO;AAC5B,SAAO,EAAE,OAAO,KAAK;AACvB;AAaO,SAAS,kBACd,aACA,aACsB;AACtB,MAAI,CAAC,eAAe,CAAC,YAAa,QAAO;AACzC,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,CAAC,YAAa,QAAO;AAGzB,QAAM,WAAW,IAAI,IAAI,WAAW;AACpC,SAAO,YAAY,OAAO,CAAC,MAAM,SAAS,IAAI,CAAC,CAAC;AAClD;AAgBO,SAAS,sBAAsB,UAAkB,QAA+C;AACrG,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,OAAO,QAAQ,OAAO,KAAK,SAAS,GAAG;AACzC,QAAI,kBAAkB,UAAU,OAAO,IAAI,EAAG,QAAO;AAAA,EACvD;AAEA,MAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GAAG;AAC3C,WAAO,kBAAkB,UAAU,OAAO,KAAK;AAAA,EACjD;AAEA,SAAO;AACT;AAMA,SAAS,kBAAkB,OAAe,UAA6B;AACrE,aAAW,WAAW,UAAU;AAC9B,QAAI,YAAY,IAAK,QAAO;AAC5B,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,UAAI,MAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,CAAC,EAAG,QAAO;AAAA,IACrD,WAAW,UAAU,SAAS;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { extractPermissionGrantUrl, extractPermissionScopes } from "./permission-url";
|
|
2
|
+
import { LARK_ERROR } from "./auth-errors";
|
|
3
|
+
function formatPermissionError(code, msg) {
|
|
4
|
+
if (code !== LARK_ERROR.APP_SCOPE_MISSING) return null;
|
|
5
|
+
const authUrl = extractPermissionGrantUrl(msg);
|
|
6
|
+
const scopes = extractPermissionScopes(msg);
|
|
7
|
+
return `\u6743\u9650\u4E0D\u8DB3\uFF1A\u5E94\u7528\u7F3A\u5C11 [${scopes}] \u6743\u9650\u3002
|
|
8
|
+
\u8BF7\u7BA1\u7406\u5458\u70B9\u51FB\u4EE5\u4E0B\u94FE\u63A5\u7533\u8BF7\u5E76\u5F00\u901A\u6743\u9650\uFF1A
|
|
9
|
+
${authUrl}`;
|
|
10
|
+
}
|
|
11
|
+
function coerceCode(value) {
|
|
12
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
const parsed = Number(value);
|
|
17
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
function extractLarkApiCode(err) {
|
|
22
|
+
if (!err || typeof err !== "object") return void 0;
|
|
23
|
+
const e = err;
|
|
24
|
+
return coerceCode(e.code) ?? coerceCode(e.data?.code) ?? coerceCode(e.response?.data?.code);
|
|
25
|
+
}
|
|
26
|
+
function assertLarkOk(res) {
|
|
27
|
+
if (!res.code || res.code === 0) return;
|
|
28
|
+
const permMsg = formatPermissionError(res.code, res.msg ?? "");
|
|
29
|
+
if (permMsg) throw new Error(permMsg);
|
|
30
|
+
throw new Error(res.msg ?? `Feishu API error (code: ${res.code})`);
|
|
31
|
+
}
|
|
32
|
+
function formatLarkError(err) {
|
|
33
|
+
if (!err || typeof err !== "object") {
|
|
34
|
+
return String(err);
|
|
35
|
+
}
|
|
36
|
+
const e = err;
|
|
37
|
+
if (typeof e.code === "number" && e.msg) {
|
|
38
|
+
const permMsg = formatPermissionError(e.code, e.msg);
|
|
39
|
+
if (permMsg) return permMsg;
|
|
40
|
+
return e.msg;
|
|
41
|
+
}
|
|
42
|
+
const data = e.response?.data;
|
|
43
|
+
if (data && typeof data.code === "number" && data.msg) {
|
|
44
|
+
const permMsg = formatPermissionError(data.code, data.msg);
|
|
45
|
+
if (permMsg) return permMsg;
|
|
46
|
+
return data.msg;
|
|
47
|
+
}
|
|
48
|
+
return e.message ?? String(err);
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
assertLarkOk,
|
|
52
|
+
extractLarkApiCode,
|
|
53
|
+
formatLarkError
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=api-error.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/api-error.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Shared Lark API error handling utilities.\n *\n * Provides unified error handling for two distinct error paths:\n *\n * 1. **Response-level errors** \u2014 The SDK returns a response object with a\n * non-zero `code`. Handled by {@link assertLarkOk}.\n *\n * 2. **Thrown exceptions** \u2014 The SDK throws an Axios-style error (HTTP 4xx)\n * whose properties include the Feishu error `code` and `msg`.\n * Handled by {@link formatLarkError}.\n *\n * Both paths intercept well-known codes (e.g. LARK_ERROR.APP_SCOPE_MISSING (99991672) \u2014 missing API scopes)\n * and produce user-friendly messages with actionable authorization links.\n */\n\nimport { extractPermissionGrantUrl, extractPermissionScopes } from './permission-url';\nimport { LARK_ERROR } from './auth-errors';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Given a Feishu error code and msg, format a user-friendly permission\n * error string if the code is LARK_ERROR.APP_SCOPE_MISSING (99991672). Returns `null` for other codes.\n */\nfunction formatPermissionError(code: number, msg: string): string | null {\n if (code !== LARK_ERROR.APP_SCOPE_MISSING) return null;\n\n const authUrl = extractPermissionGrantUrl(msg);\n const scopes = extractPermissionScopes(msg);\n\n return `\u6743\u9650\u4E0D\u8DB3\uFF1A\u5E94\u7528\u7F3A\u5C11 [${scopes}] \u6743\u9650\u3002\\n` + `\u8BF7\u7BA1\u7406\u5458\u70B9\u51FB\u4EE5\u4E0B\u94FE\u63A5\u7533\u8BF7\u5E76\u5F00\u901A\u6743\u9650\uFF1A\\n${authUrl}`;\n}\n\n// ---------------------------------------------------------------------------\n// Code extraction\n// ---------------------------------------------------------------------------\n\nfunction coerceCode(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === 'string') {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return undefined;\n}\n\n/**\n * \u4ECE Lark SDK \u629B\u9519\u5BF9\u8C61\u4E2D\u63D0\u53D6\u98DE\u4E66 API code\u3002\n *\n * \u652F\u6301\u4E09\u79CD\u5E38\u89C1\u7ED3\u6784\uFF1A\n * - `{ code }` \u2014 SDK \u76F4\u63A5\u6302\u8F7D\n * - `{ data: { code } }` \u2014 \u54CD\u5E94\u4F53\u5D4C\u5957\n * - `{ response: { data: { code } } }` \u2014 Axios \u98CE\u683C\n */\nexport function extractLarkApiCode(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n\n const e = err as {\n code?: unknown;\n data?: { code?: unknown };\n response?: { data?: { code?: unknown } };\n };\n\n return coerceCode(e.code) ?? coerceCode(e.data?.code) ?? coerceCode(e.response?.data?.code);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Assert that a Lark SDK response is successful (code === 0).\n *\n * For permission errors (code LARK_ERROR.APP_SCOPE_MISSING (99991672)), the thrown error includes the\n * required scope names and a direct authorization URL so the AI can\n * present it to the end user.\n */\nexport function assertLarkOk(res: { code?: number; msg?: string }): void {\n if (!res.code || res.code === 0) return;\n\n const permMsg = formatPermissionError(res.code, res.msg ?? '');\n if (permMsg) throw new Error(permMsg);\n\n throw new Error(res.msg ?? `Feishu API error (code: ${res.code})`);\n}\n\n/**\n * Extract a meaningful error message from a thrown Lark SDK / Axios error.\n *\n * The Lark SDK throws Axios errors whose object carries Feishu-specific\n * fields (`code`, `msg`) alongside the standard `message`. For permission\n * errors (LARK_ERROR.APP_SCOPE_MISSING (99991672)) we format a user-friendly string with scopes + auth URL.\n * For all other errors we try `err.msg` first (the Feishu detail) and fall\n * back to `err.message` (the generic Axios text).\n */\nexport function formatLarkError(err: unknown): string {\n if (!err || typeof err !== 'object') {\n return String(err);\n }\n const e = err as {\n code?: number;\n msg?: string;\n message?: string;\n response?: { data?: { code?: number; msg?: string } };\n };\n\n // Path 1: Lark SDK merges Feishu fields onto the thrown error object.\n if (typeof e.code === 'number' && e.msg) {\n const permMsg = formatPermissionError(e.code, e.msg);\n if (permMsg) return permMsg;\n return e.msg;\n }\n\n // Path 2: Standard Axios error \u2014 dig into response.data.\n const data = e.response?.data;\n if (data && typeof data.code === 'number' && data.msg) {\n const permMsg = formatPermissionError(data.code, data.msg);\n if (permMsg) return permMsg;\n return data.msg;\n }\n\n // Fallback.\n return e.message ?? String(err);\n}\n"],
|
|
5
|
+
"mappings": "AAmBA,SAAS,2BAA2B,+BAA+B;AACnE,SAAS,kBAAkB;AAU3B,SAAS,sBAAsB,MAAc,KAA4B;AACvE,MAAI,SAAS,WAAW,kBAAmB,QAAO;AAElD,QAAM,UAAU,0BAA0B,GAAG;AAC7C,QAAM,SAAS,wBAAwB,GAAG;AAE1C,SAAO,2DAAc,MAAM;AAAA;AAAA,EAAmC,OAAO;AACvE;AAMA,SAAS,WAAW,OAAoC;AACtD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,KAAkC;AACnE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAE5C,QAAM,IAAI;AAMV,SAAO,WAAW,EAAE,IAAI,KAAK,WAAW,EAAE,MAAM,IAAI,KAAK,WAAW,EAAE,UAAU,MAAM,IAAI;AAC5F;AAaO,SAAS,aAAa,KAA4C;AACvE,MAAI,CAAC,IAAI,QAAQ,IAAI,SAAS,EAAG;AAEjC,QAAM,UAAU,sBAAsB,IAAI,MAAM,IAAI,OAAO,EAAE;AAC7D,MAAI,QAAS,OAAM,IAAI,MAAM,OAAO;AAEpC,QAAM,IAAI,MAAM,IAAI,OAAO,2BAA2B,IAAI,IAAI,GAAG;AACnE;AAWO,SAAS,gBAAgB,KAAsB;AACpD,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,WAAO,OAAO,GAAG;AAAA,EACnB;AACA,QAAM,IAAI;AAQV,MAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK;AACvC,UAAM,UAAU,sBAAsB,EAAE,MAAM,EAAE,GAAG;AACnD,QAAI,QAAS,QAAO;AACpB,WAAO,EAAE;AAAA,EACX;AAGA,QAAM,OAAO,EAAE,UAAU;AACzB,MAAI,QAAQ,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK;AACrD,UAAM,UAAU,sBAAsB,KAAK,MAAM,KAAK,GAAG;AACzD,QAAI,QAAS,QAAO;AACpB,WAAO,KAAK;AAAA,EACd;AAGA,SAAO,EAAE,WAAW,OAAO,GAAG;AAChC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAppInfo } from "./app-scope-checker";
|
|
2
|
+
import { larkLogger } from "./lark-logger";
|
|
3
|
+
const log = larkLogger("core/app-owner-fallback");
|
|
4
|
+
async function getAppOwnerFallback(account, sdk) {
|
|
5
|
+
const { appId } = account;
|
|
6
|
+
try {
|
|
7
|
+
const appInfo = await getAppInfo(sdk, appId);
|
|
8
|
+
return appInfo.effectiveOwnerOpenId;
|
|
9
|
+
} catch (err) {
|
|
10
|
+
log.warn(`failed to get owner for ${appId}: ${err instanceof Error ? err.message : err}`);
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
getAppOwnerFallback
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=app-owner-fallback.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/app-owner-fallback.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u5E94\u7528\u6240\u6709\u8005\u67E5\u8BE2 \u2014 \u590D\u7528 app-scope-checker \u7684 API \u8C03\u7528\u548C\u7EDF\u4E00 owner \u5B9A\u4E49\u3002\n *\n * \u6240\u6709 owner \u5224\u5B9A\u7EDF\u4E00\u4F7F\u7528 {@link getAppInfo} \u8FD4\u56DE\u7684 `effectiveOwnerOpenId`\u3002\n * \u4E0D\u7EF4\u62A4\u72EC\u7ACB\u7F13\u5B58\uFF0C\u5B8C\u5168\u4F9D\u8D56 app-scope-checker \u7684 30s \u7F13\u5B58\u3002\n */\n\nimport type { ConfiguredLarkAccount } from './types';\nimport { getAppInfo } from './app-scope-checker';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/app-owner-fallback');\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * \u83B7\u53D6\u5E94\u7528\u7684 effectiveOwnerOpenId\u3002\n *\n * \u590D\u7528 app-scope-checker \u7684 API \u8C03\u7528\u3001\u7F13\u5B58\u548C\u7EDF\u4E00 owner \u5B9A\u4E49\uFF08effectiveOwnerOpenId\uFF09\u3002\n * \u67E5\u8BE2\u5931\u8D25\u65F6\u8FD4\u56DE undefined\uFF08fail-open\uFF09\u3002\n *\n * @param account - \u5DF2\u914D\u7F6E\u7684\u98DE\u4E66\u8D26\u53F7\u4FE1\u606F\n * @param sdk - \u98DE\u4E66 SDK \u5B9E\u4F8B\uFF08\u5FC5\u987B\u5DF2\u521D\u59CB\u5316 TAT\uFF09\n * @returns \u5E94\u7528\u6240\u6709\u8005\u7684 open_id\uFF0C\u5982\u679C\u67E5\u8BE2\u5931\u8D25\u5219\u8FD4\u56DE undefined\n */\nexport async function getAppOwnerFallback(\n account: ConfiguredLarkAccount,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n sdk: any,\n): Promise<string | undefined> {\n const { appId } = account;\n\n try {\n const appInfo = await getAppInfo(sdk, appId);\n return appInfo.effectiveOwnerOpenId;\n } catch (err) {\n log.warn(`failed to get owner for ${appId}: ${err instanceof Error ? err.message : err}`);\n return undefined; // fail-open: \u83B7\u53D6\u5931\u8D25\u4E0D\u963B\u585E\u4E1A\u52A1\n }\n}\n"],
|
|
5
|
+
"mappings": "AAWA,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAE3B,MAAM,MAAM,WAAW,yBAAyB;AAgBhD,eAAsB,oBACpB,SAEA,KAC6B;AAC7B,QAAM,EAAE,MAAM,IAAI;AAElB,MAAI;AACF,UAAM,UAAU,MAAM,WAAW,KAAK,KAAK;AAC3C,WAAO,QAAQ;AAAA,EACjB,SAAS,KAAK;AACZ,QAAI,KAAK,2BAA2B,KAAK,KAAK,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACxF,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { larkLogger } from "./lark-logger";
|
|
2
|
+
const log = larkLogger("core/app-scope-checker");
|
|
3
|
+
import { AppScopeCheckFailedError } from "./auth-errors";
|
|
4
|
+
const cache = /* @__PURE__ */ new Map();
|
|
5
|
+
const CACHE_TTL_MS = 30 * 1e3;
|
|
6
|
+
function invalidateAppScopeCache(appId) {
|
|
7
|
+
cache.delete(appId);
|
|
8
|
+
}
|
|
9
|
+
async function getAppGrantedScopes(sdk, appId, tokenType) {
|
|
10
|
+
const cached = cache.get(appId);
|
|
11
|
+
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
|
|
12
|
+
return cached.rawScopes.filter((s) => {
|
|
13
|
+
if (tokenType && s.token_types && Array.isArray(s.token_types)) {
|
|
14
|
+
return s.token_types.includes(tokenType);
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}).map((s) => s.scope);
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const res = await sdk.request({
|
|
21
|
+
method: "GET",
|
|
22
|
+
url: `/open-apis/application/v6/applications/${appId}`,
|
|
23
|
+
params: { lang: "zh_cn" }
|
|
24
|
+
});
|
|
25
|
+
if (res.code !== 0) {
|
|
26
|
+
throw new AppScopeCheckFailedError(appId);
|
|
27
|
+
}
|
|
28
|
+
const app = res.data?.app ?? res.app ?? res.data;
|
|
29
|
+
const rawScopes = app?.scopes ?? app?.online_version?.scopes ?? [];
|
|
30
|
+
const validScopes = rawScopes.filter((s) => typeof s.scope === "string" && s.scope.length > 0).map((s) => ({ scope: s.scope, token_types: s.token_types }));
|
|
31
|
+
cache.set(appId, { rawScopes: validScopes, rawApp: app, fetchedAt: Date.now() });
|
|
32
|
+
log.info(`fetched ${validScopes.length} scopes for app ${appId}`);
|
|
33
|
+
const scopes = validScopes.filter((s) => {
|
|
34
|
+
if (tokenType && s.token_types && Array.isArray(s.token_types)) {
|
|
35
|
+
return s.token_types.includes(tokenType);
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}).map((s) => s.scope);
|
|
39
|
+
log.info(`returning ${scopes.length} scopes${tokenType ? ` for ${tokenType} token` : ""}`);
|
|
40
|
+
return scopes;
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (err instanceof AppScopeCheckFailedError) {
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
const statusCode = err?.response?.status || err?.status || err?.statusCode;
|
|
46
|
+
const isPermissionError = statusCode === 400 || statusCode === 403 || err instanceof Error && (err.message.includes("status code 400") || err.message.includes("status code 403"));
|
|
47
|
+
if (isPermissionError) {
|
|
48
|
+
throw new AppScopeCheckFailedError(appId);
|
|
49
|
+
}
|
|
50
|
+
log.warn(`failed to fetch scopes for ${appId}: ${err instanceof Error ? err.message : err}`);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function getAppInfo(sdk, appId) {
|
|
55
|
+
await getAppGrantedScopes(sdk, appId);
|
|
56
|
+
const cached = cache.get(appId);
|
|
57
|
+
const rawApp = cached?.rawApp;
|
|
58
|
+
const owner = rawApp?.owner;
|
|
59
|
+
const creatorId = rawApp?.creator_id;
|
|
60
|
+
const ownerTypeValue = owner?.owner_type ?? owner?.type;
|
|
61
|
+
const effectiveOwnerOpenId = ownerTypeValue === 2 && owner?.owner_id ? owner.owner_id : creatorId ?? owner?.owner_id;
|
|
62
|
+
return {
|
|
63
|
+
appId,
|
|
64
|
+
creatorId,
|
|
65
|
+
ownerOpenId: owner?.owner_id,
|
|
66
|
+
ownerType: owner?.owner_type,
|
|
67
|
+
effectiveOwnerOpenId,
|
|
68
|
+
scopes: cached?.rawScopes ?? []
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function intersectScopes(appGranted, apiRequired) {
|
|
72
|
+
const grantedSet = new Set(appGranted);
|
|
73
|
+
return apiRequired.filter((s) => grantedSet.has(s));
|
|
74
|
+
}
|
|
75
|
+
function missingScopes(appGranted, apiRequired) {
|
|
76
|
+
const grantedSet = new Set(appGranted);
|
|
77
|
+
return apiRequired.filter((s) => !grantedSet.has(s));
|
|
78
|
+
}
|
|
79
|
+
function isAppScopeSatisfied(appScopes, requiredScopes, scopeNeedType) {
|
|
80
|
+
if (appScopes.length === 0) return true;
|
|
81
|
+
if (requiredScopes.length === 0) return true;
|
|
82
|
+
if (scopeNeedType === "all") {
|
|
83
|
+
return missingScopes(appScopes, requiredScopes).length === 0;
|
|
84
|
+
}
|
|
85
|
+
return intersectScopes(appScopes, requiredScopes).length > 0;
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
getAppGrantedScopes,
|
|
89
|
+
getAppInfo,
|
|
90
|
+
intersectScopes,
|
|
91
|
+
invalidateAppScopeCache,
|
|
92
|
+
isAppScopeSatisfied,
|
|
93
|
+
missingScopes
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=app-scope-checker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/app-scope-checker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * App Scope Checker \u2014 \u67E5\u8BE2\u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u5217\u8868\u3002\n *\n * \u901A\u8FC7 `GET /open-apis/application/v6/applications/:app_id` (TAT) \u83B7\u53D6\n * \u5E94\u7528\u4FE1\u606F\uFF0C\u4ECE `app.scopes` \u4E2D\u63D0\u53D6\u5DF2\u5F00\u901A\u7684 scope \u5B57\u7B26\u4E32\u5217\u8868\u3002\n *\n * \u7ED3\u679C\u5E26 30 \u79D2\u5185\u5B58\u7F13\u5B58\uFF0C\u907F\u514D\u6BCF\u6B21 invoke() \u90FD\u8C03\u8FDC\u7A0B API\u3002\n * scope \u68C0\u67E5\u5931\u8D25\u540E\u53EF\u8C03 {@link invalidateAppScopeCache} \u6E05\u7F13\u5B58\u91CD\u67E5\u3002\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type * as Lark from '@larksuiteoapi/node-sdk';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/app-scope-checker');\nimport { AppScopeCheckFailedError } from './auth-errors';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface AppInfo {\n appId: string;\n creatorId?: string; // ou_xxx\n ownerOpenId?: string; // owner.owner_id (ou_xxx)\uFF08\u539F\u6837\u4FDD\u7559\uFF0C\u517C\u5BB9\u6027\uFF09\n ownerType?: number; // 0=\u98DE\u4E66\u79D1\u6280, 1=\u5408\u4F5C\u4F19\u4F34, 2=\u4F01\u4E1A\u5185\u6210\u5458\uFF08\u539F\u6837\u4FDD\u7559\uFF0C\u517C\u5BB9\u6027\uFF09\n /**\n * \u7EDF\u4E00\u7684 owner \u5224\u5B9A\u7ED3\u679C\u3002\u6240\u6709\u9700\u8981\u5224\u5B9A\"\u8C01\u662F\u5E94\u7528 owner\"\u7684\u573A\u666F\u90FD\u5E94\u4F7F\u7528\u6B64\u5B57\u6BB5\u3002\n *\n * \u89C4\u5219\uFF1Aowner_type=2\uFF08\u4F01\u4E1A\u5185\u6210\u5458\uFF09\u65F6\u53D6 owner_id\uFF0C\u5426\u5219\u56DE\u9000 creator_id\u3002\n * \u517C\u5BB9 owner.owner_type \u548C owner.type \u4E24\u79CD\u5B57\u6BB5\u540D\u3002\n */\n effectiveOwnerOpenId?: string;\n scopes: Array<{ scope: string; token_types?: string[] }>;\n}\n\ninterface CacheEntry {\n rawScopes: Array<{ scope: string; token_types?: string[] }>;\n rawApp?: Record<string, unknown>;\n fetchedAt: number;\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\nconst cache = new Map<string, CacheEntry>();\nconst CACHE_TTL_MS = 30 * 1000; // 30 \u79D2\n\n/** \u6E05\u9664\u6307\u5B9A appId \u7684\u7F13\u5B58\u3002 */\nexport function invalidateAppScopeCache(appId: string): void {\n cache.delete(appId);\n}\n\n// ---------------------------------------------------------------------------\n// Fetch\n// ---------------------------------------------------------------------------\n\n/**\n * \u83B7\u53D6\u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u5217\u8868\u3002\n *\n * \u9700\u8981\u5E94\u7528\u81EA\u8EAB\u6709 `application:application:self_manage` \u6743\u9650\u3002\n * `appId` \u53EF\u4F20 `\"me\"` \u67E5\u81EA\u5DF1\u3002\n *\n * @param sdk - Lark SDK \u5B9E\u4F8B\n * @param appId - \u5E94\u7528 ID\n * @param tokenType - token \u7C7B\u578B\uFF0C\u7528\u4E8E\u8FC7\u6EE4\u53EA\u652F\u6301\u7279\u5B9A token \u7C7B\u578B\u7684 scope\n * @returns scope \u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u5982 `[\"calendar:calendar\", \"task:task:write\"]`\n */\nexport async function getAppGrantedScopes(\n sdk: Lark.Client,\n appId: string,\n tokenType?: 'user' | 'tenant',\n): Promise<string[]> {\n // 1. \u68C0\u67E5\u7F13\u5B58\n const cached = cache.get(appId);\n if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {\n // \u4ECE\u7F13\u5B58\u4E2D\u8FC7\u6EE4\u51FA\u652F\u6301\u5F53\u524D token \u7C7B\u578B\u7684 scope\n return cached.rawScopes\n .filter((s) => {\n if (tokenType && s.token_types && Array.isArray(s.token_types)) {\n return s.token_types.includes(tokenType);\n }\n return true;\n })\n .map((s) => s.scope);\n }\n\n // 2. \u8C03\u7528 API\n try {\n const res = await (sdk as any).request({\n method: 'GET',\n url: `/open-apis/application/v6/applications/${appId}`,\n params: { lang: 'zh_cn' },\n });\n\n if (res.code !== 0) {\n // \u4EFB\u4F55 API \u9519\u8BEF\u90FD\u8BA4\u4E3A\u662F\u5E94\u7528\u7F3A\u5C11 application:application:self_manage \u6743\u9650\n throw new AppScopeCheckFailedError(appId);\n }\n\n // \u54CD\u5E94\u7ED3\u6784: res.data.app.scopes \u2192 [{ scope: \"xxx\", description, level, token_types?: string[] }]\n // \u6216\u8005\u4ECE app_version \u4E2D\u83B7\u53D6 scopes\n const app = res.data?.app ?? res.app ?? res.data;\n const rawScopes: Array<{ scope?: string; token_types?: string[] }> =\n app?.scopes ?? app?.online_version?.scopes ?? [];\n\n // \u63D0\u53D6\u5E76\u9A8C\u8BC1 scope \u5B57\u7B26\u4E32\n const validScopes = rawScopes\n .filter((s) => typeof s.scope === 'string' && s.scope.length > 0)\n .map((s) => ({ scope: s.scope!, token_types: s.token_types }));\n\n // 3. \u5199\u7F13\u5B58\uFF08\u7F13\u5B58\u5B8C\u6574\u6570\u636E\uFF0C\u5305\u542B token_types \u548C\u539F\u59CB app \u5BF9\u8C61\uFF09\n cache.set(appId, { rawScopes: validScopes, rawApp: app, fetchedAt: Date.now() });\n log.info(`fetched ${validScopes.length} scopes for app ${appId}`);\n\n // 4. \u6839\u636E tokenType \u8FC7\u6EE4\n const scopes = validScopes\n .filter((s) => {\n if (tokenType && s.token_types && Array.isArray(s.token_types)) {\n return s.token_types.includes(tokenType);\n }\n return true;\n })\n .map((s) => s.scope);\n\n log.info(`returning ${scopes.length} scopes${tokenType ? ` for ${tokenType} token` : ''}`);\n\n return scopes;\n } catch (err) {\n // \u5982\u679C\u662F AppScopeCheckFailedError\uFF0C\u91CD\u65B0\u629B\u51FA\uFF08\u4E0D\u541E\u6389\uFF09\n if (err instanceof AppScopeCheckFailedError) {\n throw err;\n }\n\n // \u68C0\u67E5\u662F\u5426\u662F\u6743\u9650\u76F8\u5173\u7684 HTTP \u9519\u8BEF\uFF08400/403\uFF09\n // axios/SDK \u5F02\u5E38\u5BF9\u8C61\u901A\u5E38\u5305\u542B response.status \u6216 status \u5B57\u6BB5\n const statusCode = (err as any)?.response?.status || (err as any)?.status || (err as any)?.statusCode;\n\n const isPermissionError =\n statusCode === 400 ||\n statusCode === 403 ||\n (err instanceof Error && (err.message.includes('status code 400') || err.message.includes('status code 403')));\n\n if (isPermissionError) {\n throw new AppScopeCheckFailedError(appId);\n }\n\n log.warn(`failed to fetch scopes for ${appId}: ${err instanceof Error ? err.message : err}`);\n // \u5176\u4ED6\u67E5\u8BE2\u5931\u8D25\u4E0D\u963B\u585E\u8C03\u7528\uFF0C\u8FD4\u56DE\u7A7A\u6570\u7EC4\uFF08\u540E\u7EED API \u8C03\u7528\u5982\u679C\u7F3A scope \u4F1A\u88AB\u670D\u52A1\u7AEF\u62D2\u7EDD\uFF09\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// App info\n// ---------------------------------------------------------------------------\n\n/**\n * \u83B7\u53D6\u5E94\u7528\u4FE1\u606F\uFF0C\u5305\u62EC owner \u4FE1\u606F\u3002\n *\n * \u590D\u7528 getAppGrantedScopes \u7684 API \u8C03\u7528\u548C\u7F13\u5B58\u3002\n * \u5982\u679C\u7F13\u5B58\u4E2D\u5DF2\u6709\u6570\u636E\u4E14\u672A\u8FC7\u671F\uFF0C\u76F4\u63A5\u4ECE\u7F13\u5B58\u63D0\u53D6\u3002\n *\n * @param sdk - Lark SDK \u5B9E\u4F8B\n * @param appId - \u5E94\u7528 ID\uFF08\u53EF\u4F20 \"me\"\uFF09\n */\nexport async function getAppInfo(sdk: Lark.Client, appId: string): Promise<AppInfo> {\n // \u5148\u786E\u4FDD\u7F13\u5B58\u5DF2\u586B\u5145\uFF08\u8C03\u4E00\u6B21 getAppGrantedScopes \u6765\u89E6\u53D1 API + \u7F13\u5B58\uFF09\n await getAppGrantedScopes(sdk, appId);\n\n const cached = cache.get(appId);\n const rawApp = cached?.rawApp as Record<string, unknown> | undefined;\n\n // \u63D0\u53D6 owner \u4FE1\u606F\n const owner = rawApp?.owner as\n | { owner_id?: string; owner_type?: number; type?: number; owner_id_type?: string }\n | undefined;\n const creatorId = rawApp?.creator_id as string | undefined;\n\n // \u7EDF\u4E00 owner \u5B9A\u4E49\uFF1Atype=2\uFF08\u4F01\u4E1A\u5185\u6210\u5458\uFF09\u7528 owner_id\uFF0C\u5426\u5219\u56DE\u9000 creator_id\n // \u517C\u5BB9\u4E24\u79CD\u5B57\u6BB5\u540D\uFF08owner_type \u548C type\uFF09\n const ownerTypeValue = owner?.owner_type ?? (owner as any)?.type;\n const effectiveOwnerOpenId =\n ownerTypeValue === 2 && owner?.owner_id ? owner.owner_id : (creatorId ?? owner?.owner_id);\n\n return {\n appId,\n creatorId,\n ownerOpenId: owner?.owner_id,\n ownerType: owner?.owner_type,\n effectiveOwnerOpenId,\n scopes: cached?.rawScopes ?? [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Scope intersection\n// ---------------------------------------------------------------------------\n\n/**\n * \u8BA1\u7B97 APP \u5DF2\u6709 \u2229 OAPI \u9700\u8981 \u7684\u4EA4\u96C6\u3002\n *\n * \u7528\u4E8E\u4F20\u7ED9 OAuth \u7684 scope \u53C2\u6570 \u2014 \u53EA\u8BF7\u6C42 APP \u5DF2\u5F00\u901A\u4E14 API \u9700\u8981\u7684 scope\u3002\n *\n * @param appGranted - \u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u5217\u8868\n * @param apiRequired - OAPI \u8981\u6C42\u7684 scope \u5217\u8868\n * @returns \u4EA4\u96C6 scope \u5217\u8868\n */\nexport function intersectScopes(appGranted: string[], apiRequired: string[]): string[] {\n const grantedSet = new Set(appGranted);\n return apiRequired.filter((s) => grantedSet.has(s));\n}\n\n/**\n * \u8BA1\u7B97 OAPI \u9700\u8981\u4F46 APP \u672A\u5F00\u901A\u7684 scope\uFF08\u5DEE\u96C6\uFF09\u3002\n *\n * \u7528\u4E8E AppScopeMissingError \u7684 missingScopes\u3002\n *\n * @param appGranted - \u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u5217\u8868\n * @param apiRequired - OAPI \u8981\u6C42\u7684 scope \u5217\u8868\n * @returns \u7F3A\u5931\u7684 scope \u5217\u8868\n */\nexport function missingScopes(appGranted: string[], apiRequired: string[]): string[] {\n const grantedSet = new Set(appGranted);\n return apiRequired.filter((s) => !grantedSet.has(s));\n}\n\n/**\n * \u6821\u9A8C\u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u662F\u5426\u6EE1\u8DB3\u8981\u6C42\u3002\n *\n * \u4E0E tool-client.ts invoke() \u7684 scope \u6821\u9A8C\u903B\u8F91\u5B8C\u5168\u4E00\u81F4\uFF0C\u4F5C\u4E3A\u552F\u4E00\u771F\u503C\u6765\u6E90\uFF1A\n * - `scopeNeedType === \"all\"`: appScopes \u5FC5\u987B\u5305\u542B requiredScopes \u7684\u5168\u90E8\u9879\n * - \u5176\u4ED6\uFF08\u9ED8\u8BA4 \"one\"\uFF09: appScopes \u4E0E requiredScopes \u7684\u4EA4\u96C6\u975E\u7A7A\u5373\u53EF\n * - appScopes \u4E3A\u7A7A: \u89C6\u4E3A\u6EE1\u8DB3\uFF08API \u67E5\u8BE2\u5931\u8D25\uFF0C\u9000\u56DE\u670D\u52A1\u7AEF\u5224\u65AD\uFF09\n *\n * @param appScopes - \u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u5217\u8868\uFF08\u7531 getAppGrantedScopes \u8FD4\u56DE\uFF09\n * @param requiredScopes - \u9700\u8981\u7684 scope \u5217\u8868\n * @param scopeNeedType - \"all\" \u8868\u793A\u5168\u90E8\u5FC5\u987B\uFF0Cundefined/\"one\" \u8868\u793A\u4EFB\u4E00\u5373\u53EF\n */\nexport function isAppScopeSatisfied(\n appScopes: string[],\n requiredScopes: string[],\n scopeNeedType?: 'one' | 'all',\n): boolean {\n if (appScopes.length === 0) return true; // API \u67E5\u8BE2\u5931\u8D25 \u2192 \u9000\u56DE\u670D\u52A1\u7AEF\u5224\u65AD\n if (requiredScopes.length === 0) return true;\n if (scopeNeedType === 'all') {\n return missingScopes(appScopes, requiredScopes).length === 0;\n }\n return intersectScopes(appScopes, requiredScopes).length > 0;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|