@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,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/card/streaming-card-controller.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Streaming card controller for the Lark/Feishu channel plugin.\n *\n * Manages the full lifecycle of a streaming CardKit card:\n * idle \u2192 creating \u2192 streaming \u2192 completed / aborted / terminated.\n *\n * Delegates throttling to FlushController and message-unavailable\n * detection to UnavailableGuard.\n */\n\nimport { SILENT_REPLY_TOKEN, type ReplyPayload } from 'openclaw/plugin-sdk';\nimport { extractLarkApiCode } from '../core/api-error';\nimport { larkLogger } from '../core/lark-logger';\nimport { sendCardFeishu, updateCardFeishu } from '../messaging/outbound/send';\nimport {\n createCardEntity,\n sendCardByCardId,\n streamCardContent,\n updateCardKitCard,\n setCardStreamingMode,\n} from './cardkit';\nimport { buildCardContent, splitReasoningText, stripReasoningTags, STREAMING_ELEMENT_ID, toCardKit2 } from './builder';\nimport { optimizeMarkdownStyle } from './markdown-style';\nimport { registerShutdownHook } from '../core/shutdown-hooks';\nimport { FlushController } from './flush-controller';\nimport { UnavailableGuard } from './unavailable-guard';\nimport type {\n CardPhase,\n TerminalReason,\n ReasoningState,\n StreamingTextState,\n CardKitState,\n StreamingCardDeps,\n} from './reply-dispatcher-types';\nimport {\n TERMINAL_PHASES,\n PHASE_TRANSITIONS,\n THROTTLE_CONSTANTS,\n EMPTY_REPLY_FALLBACK_TEXT,\n} from './reply-dispatcher-types';\n\nconst log = larkLogger('card/streaming');\n// ---------------------------------------------------------------------------\n// CardKit 2.0 initial streaming payload\n// ---------------------------------------------------------------------------\n\nconst STREAMING_THINKING_CARD = {\n schema: '2.0',\n config: {\n streaming_mode: true,\n summary: { content: '\u601D\u8003\u4E2D...' },\n },\n body: {\n elements: [\n {\n tag: 'markdown',\n content: '',\n text_align: 'left',\n text_size: 'normal_v2',\n margin: '0px 0px 0px 0px',\n element_id: STREAMING_ELEMENT_ID,\n },\n {\n tag: 'markdown',\n content: ' ',\n icon: {\n tag: 'custom_icon',\n img_key: 'img_v3_02vb_496bec09-4b43-4773-ad6b-0cdd103cd2bg',\n size: '16px 16px',\n },\n element_id: 'loading_icon',\n },\n ],\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// StreamingCardController\n// ---------------------------------------------------------------------------\n\nexport class StreamingCardController {\n // ---- Explicit state machine ----\n private phase: CardPhase = 'idle';\n\n // ---- Structured state ----\n private cardKit: CardKitState = {\n cardKitCardId: null,\n originalCardKitCardId: null,\n cardKitSequence: 0,\n cardMessageId: null,\n };\n private text: StreamingTextState = {\n accumulatedText: '',\n completedText: '',\n streamingPrefix: '',\n lastPartialText: '',\n };\n private reasoning: ReasoningState = {\n accumulatedReasoningText: '',\n reasoningStartTime: null,\n reasoningElapsedMs: 0,\n isReasoningPhase: false,\n };\n\n // ---- Sub-controllers ----\n private readonly flush: FlushController;\n private readonly guard: UnavailableGuard;\n\n // ---- Lifecycle ----\n private createEpoch = 0;\n private _terminalReason: TerminalReason | null = null;\n private dispatchFullyComplete = false;\n private cardCreationPromise: Promise<void> | null = null;\n private disposeShutdownHook: (() => void) | null = null;\n private readonly dispatchStartTime = Date.now();\n\n // ---- Injected dependencies ----\n private readonly deps: StreamingCardDeps;\n\n private elapsed(): number {\n return Date.now() - this.dispatchStartTime;\n }\n\n constructor(deps: StreamingCardDeps) {\n this.deps = deps;\n\n this.guard = new UnavailableGuard({\n replyToMessageId: deps.replyToMessageId,\n getCardMessageId: () => this.cardKit.cardMessageId,\n onTerminate: () => {\n this.transition('terminated', 'UnavailableGuard', 'unavailable');\n },\n });\n\n this.flush = new FlushController(() => this.performFlush());\n }\n\n // ------------------------------------------------------------------\n // Public accessors\n // ------------------------------------------------------------------\n\n get cardMessageId(): string | null {\n return this.cardKit.cardMessageId;\n }\n\n get isTerminalPhase(): boolean {\n return TERMINAL_PHASES.has(this.phase);\n }\n\n /**\n * Whether the card has been explicitly aborted (via abortCard()).\n *\n * Distinct from isTerminalPhase \u2014 creation_failed is NOT an abort;\n * it should allow fallthrough to static delivery in the factory.\n */\n get isAborted(): boolean {\n return this.phase === 'aborted';\n }\n\n /** Whether the reply pipeline was terminated due to an unavailable message. */\n get isTerminated(): boolean {\n return this.guard.isTerminated;\n }\n\n /** Check if the pipeline should skip further operations for this source. */\n shouldSkipForUnavailable(source: string): boolean {\n return this.guard.shouldSkip(source);\n }\n\n /** Attempt to terminate the pipeline due to an unavailable message error. */\n terminateIfUnavailable(source: string, err?: unknown): boolean {\n return this.guard.terminate(source, err);\n }\n\n /** Why the controller entered a terminal phase, or null if still active. */\n get terminalReason(): TerminalReason | null {\n return this._terminalReason;\n }\n\n /** @internal \u2014 exposed for test assertions only. */\n get currentPhase(): CardPhase {\n return this.phase;\n }\n\n // ------------------------------------------------------------------\n // Unified callback guard\n // ------------------------------------------------------------------\n\n /**\n * Unified callback guard \u2014 returns true if the pipeline is active\n * and the callback should proceed.\n *\n * Combines three checks:\n * 1. guard.isTerminated \u2014 message recalled/deleted\n * 2. guard.shouldSkip(source) \u2014 eagerly detect unavailable messages\n * 3. isTerminalPhase \u2014 completed/aborted/terminated/creation_failed\n */\n private shouldProceed(source: string): boolean {\n if (this.guard.isTerminated || this.guard.shouldSkip(source)) return false;\n return !this.isTerminalPhase;\n }\n\n // ------------------------------------------------------------------\n // State machine\n // ------------------------------------------------------------------\n\n private isStaleCreate(epoch: number): boolean {\n return epoch !== this.createEpoch;\n }\n\n private transition(to: CardPhase, source: string, reason?: TerminalReason): boolean {\n const from = this.phase;\n if (from === to) return false;\n if (!PHASE_TRANSITIONS[from].has(to)) {\n log.warn('phase transition rejected', { from, to, source });\n return false;\n }\n this.phase = to;\n log.info('phase transition', { from, to, source, reason });\n if (TERMINAL_PHASES.has(to)) {\n this._terminalReason = reason ?? null;\n this.onEnterTerminalPhase();\n }\n return true;\n }\n\n private onEnterTerminalPhase(): void {\n this.createEpoch += 1;\n this.flush.cancelPendingFlush();\n this.flush.complete();\n this.disposeShutdownHook?.();\n this.disposeShutdownHook = null;\n }\n\n // ------------------------------------------------------------------\n // SDK callback bindings\n // ------------------------------------------------------------------\n\n /**\n * Handle a deliver() call in streaming card mode.\n *\n * Accumulates text from the SDK's deliver callbacks to build the\n * authoritative \"completedText\" for the final card.\n */\n async onDeliver(payload: ReplyPayload): Promise<void> {\n if (!this.shouldProceed('onDeliver')) return;\n\n const text = payload.text ?? '';\n if (!text.trim()) return;\n\n await this.ensureCardCreated();\n if (!this.shouldProceed('onDeliver.postCreate')) return;\n\n if (!this.cardKit.cardMessageId) return;\n\n const split = splitReasoningText(text);\n\n if (split.reasoningText && !split.answerText) {\n // Pure reasoning payload\n this.reasoning.reasoningElapsedMs = this.reasoning.reasoningStartTime\n ? Date.now() - this.reasoning.reasoningStartTime\n : 0;\n this.reasoning.accumulatedReasoningText = split.reasoningText;\n this.reasoning.isReasoningPhase = true;\n await this.throttledCardUpdate();\n return;\n }\n\n // Answer payload (may also contain inline reasoning from tags)\n this.reasoning.isReasoningPhase = false;\n if (split.reasoningText) {\n this.reasoning.accumulatedReasoningText = split.reasoningText;\n }\n const answerText = split.answerText ?? text;\n\n // \u7D2F\u79EF deliver \u6587\u672C\u7528\u4E8E\u6700\u7EC8\u5361\u7247\n this.text.completedText += (this.text.completedText ? '\\n\\n' : '') + answerText;\n\n // \u6CA1\u6709\u6D41\u5F0F\u6570\u636E\u65F6\uFF0C\u7528 deliver \u6587\u672C\u663E\u793A\u5728\u5361\u7247\u4E0A\n if (!this.text.lastPartialText && !this.text.streamingPrefix) {\n this.text.accumulatedText += (this.text.accumulatedText ? '\\n\\n' : '') + answerText;\n this.text.streamingPrefix = this.text.accumulatedText;\n await this.throttledCardUpdate();\n }\n }\n\n async onReasoningStream(payload: ReplyPayload): Promise<void> {\n if (!this.shouldProceed('onReasoningStream')) return;\n\n await this.ensureCardCreated();\n if (!this.shouldProceed('onReasoningStream.postCreate')) return;\n if (!this.cardKit.cardMessageId) return;\n\n const rawText = payload.text ?? '';\n if (!rawText) return;\n\n if (!this.reasoning.reasoningStartTime) {\n this.reasoning.reasoningStartTime = Date.now();\n }\n this.reasoning.isReasoningPhase = true;\n const split = splitReasoningText(rawText);\n this.reasoning.accumulatedReasoningText = split.reasoningText ?? rawText;\n await this.throttledCardUpdate();\n }\n\n async onPartialReply(payload: ReplyPayload): Promise<void> {\n if (!this.shouldProceed('onPartialReply')) return;\n\n const text = stripReasoningTags(payload.text ?? '');\n log.debug('onPartialReply', { len: text.length });\n if (!text) return;\n\n if (!this.reasoning.reasoningStartTime) {\n this.reasoning.reasoningStartTime = Date.now();\n }\n if (this.reasoning.isReasoningPhase) {\n this.reasoning.isReasoningPhase = false;\n this.reasoning.reasoningElapsedMs = this.reasoning.reasoningStartTime\n ? Date.now() - this.reasoning.reasoningStartTime\n : 0;\n }\n\n // \u68C0\u6D4B\u56DE\u590D\u8FB9\u754C\uFF1A\u6587\u672C\u957F\u5EA6\u7F29\u77ED \u2192 \u65B0\u56DE\u590D\u5F00\u59CB\n if (this.text.lastPartialText && text.length < this.text.lastPartialText.length) {\n this.text.streamingPrefix += (this.text.streamingPrefix ? '\\n\\n' : '') + this.text.lastPartialText;\n }\n this.text.lastPartialText = text;\n this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + '\\n\\n' + text : text;\n\n // NO_REPLY \u7F13\u51B2\n if (!this.text.streamingPrefix && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {\n log.debug('onPartialReply: buffering NO_REPLY prefix');\n return;\n }\n\n await this.ensureCardCreated();\n if (!this.shouldProceed('onPartialReply.postCreate')) return;\n if (!this.cardKit.cardMessageId) return;\n await this.throttledCardUpdate();\n }\n\n async onError(err: unknown, info: { kind: string }): Promise<void> {\n if (this.guard.terminate('onError', err)) return;\n\n log.error(`${info.kind} reply failed`, { error: String(err) });\n\n this.finalizeCard('onError', 'error');\n\n await this.flush.waitForFlush();\n\n if (this.cardCreationPromise) await this.cardCreationPromise;\n\n const errorEffectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;\n if (this.cardKit.cardMessageId) {\n try {\n const errorText = this.text.accumulatedText\n ? `${this.text.accumulatedText}\\n\\n---\\n**Error**: An error occurred while generating the response.`\n : '**Error**: An error occurred while generating the response.';\n const errorCard = buildCardContent('complete', {\n text: errorText,\n reasoningText: this.reasoning.accumulatedReasoningText || undefined,\n reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,\n elapsedMs: this.elapsed(),\n isError: true,\n footer: this.deps.resolvedFooter,\n });\n if (errorEffectiveCardId) {\n await this.closeStreamingAndUpdate(errorEffectiveCardId, errorCard, 'onError');\n } else {\n await updateCardFeishu({\n cfg: this.deps.cfg,\n messageId: this.cardKit.cardMessageId,\n card: errorCard as unknown as Record<string, unknown>,\n accountId: this.deps.accountId,\n });\n }\n } catch {\n // Ignore update failures during error handling\n }\n }\n }\n\n async onIdle(): Promise<void> {\n if (this.guard.isTerminated || this.guard.shouldSkip('onIdle')) return;\n\n if (!this.dispatchFullyComplete) return;\n\n if (this.isTerminalPhase) return;\n this.finalizeCard('onIdle', 'normal');\n\n await this.flush.waitForFlush();\n\n if (this.cardCreationPromise) {\n await this.cardCreationPromise;\n await new Promise((resolve) => setTimeout(resolve, 0));\n await this.flush.waitForFlush();\n }\n\n const idleEffectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;\n if (this.cardKit.cardMessageId) {\n try {\n if (idleEffectiveCardId) {\n const seqBeforeClose = this.cardKit.cardKitSequence;\n this.cardKit.cardKitSequence += 1;\n log.info('onIdle: closing streaming mode', {\n seqBefore: seqBeforeClose,\n seqAfter: this.cardKit.cardKitSequence,\n });\n await setCardStreamingMode({\n cfg: this.deps.cfg,\n cardId: idleEffectiveCardId,\n streamingMode: false,\n sequence: this.cardKit.cardKitSequence,\n accountId: this.deps.accountId,\n });\n }\n\n const isNoReplyLeak =\n !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());\n const displayText =\n this.text.completedText || (isNoReplyLeak ? '' : this.text.accumulatedText) || EMPTY_REPLY_FALLBACK_TEXT;\n if (!this.text.completedText && !this.text.accumulatedText) {\n log.warn('reply completed without visible text, using empty-reply fallback');\n }\n\n const completeCard = buildCardContent('complete', {\n text: displayText,\n reasoningText: this.reasoning.accumulatedReasoningText || undefined,\n reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,\n elapsedMs: this.elapsed(),\n footer: this.deps.resolvedFooter,\n });\n\n if (idleEffectiveCardId) {\n const seqBeforeUpdate = this.cardKit.cardKitSequence;\n this.cardKit.cardKitSequence += 1;\n log.info('onIdle: updating final card', {\n seqBefore: seqBeforeUpdate,\n seqAfter: this.cardKit.cardKitSequence,\n });\n await updateCardKitCard({\n cfg: this.deps.cfg,\n cardId: idleEffectiveCardId,\n card: toCardKit2(completeCard),\n sequence: this.cardKit.cardKitSequence,\n accountId: this.deps.accountId,\n });\n } else {\n await updateCardFeishu({\n cfg: this.deps.cfg,\n messageId: this.cardKit.cardMessageId,\n card: completeCard as unknown as Record<string, unknown>,\n accountId: this.deps.accountId,\n });\n }\n log.info('reply completed, card finalized', {\n elapsedMs: this.elapsed(),\n isCardKit: !!idleEffectiveCardId,\n });\n } catch (err) {\n log.warn('final card update failed', { error: String(err) });\n }\n }\n }\n\n // ------------------------------------------------------------------\n // External control\n // ------------------------------------------------------------------\n\n markFullyComplete(): void {\n log.debug('markFullyComplete', {\n completedTextLen: this.text.completedText.length,\n accumulatedTextLen: this.text.accumulatedText.length,\n });\n this.dispatchFullyComplete = true;\n }\n\n async abortCard(): Promise<void> {\n try {\n if (!this.transition('aborted', 'abortCard', 'abort')) return;\n\n // transition() already executed onEnterTerminalPhase (cancel + complete + dispose hook)\n // Only need to wait for any in-flight flush to finish\n await this.flush.waitForFlush();\n\n if (this.cardCreationPromise) await this.cardCreationPromise;\n\n const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;\n if (effectiveCardId) {\n const elapsedMs = Date.now() - this.dispatchStartTime;\n const abortText = this.text.accumulatedText || 'Aborted.';\n const abortCardContent = buildCardContent('complete', {\n text: abortText,\n reasoningText: this.reasoning.accumulatedReasoningText || undefined,\n reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,\n elapsedMs,\n isAborted: true,\n footer: this.deps.resolvedFooter,\n });\n await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, 'abortCard');\n log.info('abortCard completed', { effectiveCardId });\n } else if (this.cardKit.cardMessageId) {\n // IM fallback: \u5361\u7247\u4E0D\u662F\u901A\u8FC7 CardKit \u53D1\u7684\uFF0C\u7528 im.message.patch \u66F4\u65B0\n const elapsedMs = Date.now() - this.dispatchStartTime;\n const abortText = this.text.accumulatedText || 'Aborted.';\n const abortCard = buildCardContent('complete', {\n text: abortText,\n reasoningText: this.reasoning.accumulatedReasoningText || undefined,\n reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,\n elapsedMs,\n isAborted: true,\n footer: this.deps.resolvedFooter,\n });\n await updateCardFeishu({\n cfg: this.deps.cfg,\n messageId: this.cardKit.cardMessageId,\n card: abortCard as unknown as Record<string, unknown>,\n accountId: this.deps.accountId,\n });\n log.info('abortCard completed (IM fallback)', { messageId: this.cardKit.cardMessageId });\n }\n } catch (err) {\n log.warn('abortCard failed', { error: String(err) });\n }\n }\n\n // ------------------------------------------------------------------\n // Internal: card creation\n // ------------------------------------------------------------------\n\n async ensureCardCreated(): Promise<void> {\n if (this.guard.shouldSkip('ensureCardCreated.precheck')) return;\n if (this.cardKit.cardMessageId || this.phase === 'creation_failed' || this.isTerminalPhase) {\n return;\n }\n if (this.cardCreationPromise) {\n await this.cardCreationPromise;\n return;\n }\n if (!this.transition('creating', 'ensureCardCreated')) return;\n this.createEpoch += 1;\n const epoch = this.createEpoch;\n this.cardCreationPromise = (async () => {\n try {\n try {\n // Step 1: Create card entity\n const cId = await createCardEntity({\n cfg: this.deps.cfg,\n card: STREAMING_THINKING_CARD,\n accountId: this.deps.accountId,\n });\n\n if (this.isStaleCreate(epoch)) {\n log.info('ensureCardCreated: stale epoch after createCardEntity, bailing out', {\n epoch,\n phase: this.phase,\n });\n return;\n }\n\n if (cId) {\n this.cardKit.cardKitCardId = cId;\n this.cardKit.originalCardKitCardId = cId;\n this.cardKit.cardKitSequence = 1;\n this.disposeShutdownHook = registerShutdownHook(`streaming-card:${cId}`, () => this.abortCard());\n log.info('created CardKit entity', {\n cardId: cId,\n initialSequence: this.cardKit.cardKitSequence,\n });\n\n // Step 2: Send IM message referencing card_id\n const result = await sendCardByCardId({\n cfg: this.deps.cfg,\n to: this.deps.chatId,\n cardId: cId,\n replyToMessageId: this.deps.replyToMessageId,\n replyInThread: this.deps.replyInThread,\n accountId: this.deps.accountId,\n });\n\n if (this.isStaleCreate(epoch)) {\n log.info('ensureCardCreated: stale epoch after sendCardByCardId, bailing out', {\n epoch,\n phase: this.phase,\n });\n this.disposeShutdownHook?.();\n this.disposeShutdownHook = null;\n return;\n }\n\n this.cardKit.cardMessageId = result.messageId;\n this.flush.setCardMessageReady(true);\n if (!this.transition('streaming', 'ensureCardCreated.cardkit')) {\n this.disposeShutdownHook?.();\n this.disposeShutdownHook = null;\n return;\n }\n log.info('sent CardKit card', { messageId: result.messageId });\n } else {\n throw new Error('card.create returned empty card_id');\n }\n } catch (cardKitErr: unknown) {\n if (this.isStaleCreate(epoch)) return;\n if (this.guard.terminate('ensureCardCreated.cardkitFlow', cardKitErr)) {\n return;\n }\n // CardKit flow failed \u2014 fall back to regular IM card\n const apiDetail = extractApiDetail(cardKitErr);\n log.warn('CardKit flow failed, falling back to IM', { apiDetail });\n this.cardKit.cardKitCardId = null;\n this.cardKit.originalCardKitCardId = null;\n\n const fallbackCard = buildCardContent('thinking');\n const result = await sendCardFeishu({\n cfg: this.deps.cfg,\n to: this.deps.chatId,\n card: fallbackCard as unknown as Record<string, unknown>,\n replyToMessageId: this.deps.replyToMessageId,\n replyInThread: this.deps.replyInThread,\n accountId: this.deps.accountId,\n });\n\n if (this.isStaleCreate(epoch)) {\n log.info('ensureCardCreated: stale epoch after IM fallback send, bailing out', {\n epoch,\n phase: this.phase,\n });\n return;\n }\n\n this.cardKit.cardMessageId = result.messageId;\n this.flush.setCardMessageReady(true);\n if (!this.transition('streaming', 'ensureCardCreated.imFallback')) {\n return;\n }\n log.info('sent fallback IM card', { messageId: result.messageId });\n }\n } catch (err) {\n if (this.isStaleCreate(epoch)) return;\n if (this.guard.terminate('ensureCardCreated.outer', err)) {\n return;\n }\n log.warn('thinking card failed, falling back to static', { error: String(err) });\n this.transition('creation_failed', 'ensureCardCreated.outer', 'creation_failed');\n }\n })();\n await this.cardCreationPromise;\n }\n\n // ------------------------------------------------------------------\n // Internal: flush\n // ------------------------------------------------------------------\n\n private async performFlush(): Promise<void> {\n if (!this.cardKit.cardMessageId || this.isTerminalPhase) return;\n\n // v2 CardKit \u5361\u7247\u4E0D\u80FD\u8D70 IM patch\uFF0C\u5982\u679C\u6D41\u5F0F CardKit \u5DF2\u7981\u7528\u4F46 originalCardKitCardId\n // \u4ECD\u5728\uFF0C\u8BF4\u660E\u5361\u7247\u662F\u901A\u8FC7 CardKit \u53D1\u7684\u2014\u2014\u8DF3\u8FC7\u4E2D\u95F4\u6001\u66F4\u65B0\uFF0C\u7B49\u7EC8\u6001\u7528 originalCardKitCardId \u6536\u5C3E\n if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {\n log.debug('performFlush: skipping (CardKit streaming disabled, awaiting final update)');\n return;\n }\n\n log.debug('flushCardUpdate: enter', {\n seq: this.cardKit.cardKitSequence,\n isCardKit: !!this.cardKit.cardKitCardId,\n });\n\n try {\n const displayText = this.buildDisplayText();\n\n if (this.cardKit.cardKitCardId) {\n // CardKit streaming \u2014 typewriter effect\n const prevSeq = this.cardKit.cardKitSequence;\n this.cardKit.cardKitSequence += 1;\n log.debug('flushCardUpdate: seq bump', {\n seqBefore: prevSeq,\n seqAfter: this.cardKit.cardKitSequence,\n });\n await streamCardContent({\n cfg: this.deps.cfg,\n cardId: this.cardKit.cardKitCardId,\n elementId: STREAMING_ELEMENT_ID,\n content: optimizeMarkdownStyle(displayText),\n sequence: this.cardKit.cardKitSequence,\n accountId: this.deps.accountId,\n });\n } else {\n log.debug('flushCardUpdate: IM patch fallback');\n const card = buildCardContent('streaming', {\n text: this.reasoning.isReasoningPhase ? '' : displayText,\n reasoningText: this.reasoning.isReasoningPhase ? this.reasoning.accumulatedReasoningText : undefined,\n });\n await updateCardFeishu({\n cfg: this.deps.cfg,\n messageId: this.cardKit.cardMessageId,\n card: card as unknown as Record<string, unknown>,\n accountId: this.deps.accountId,\n });\n }\n } catch (err: unknown) {\n if (this.guard.terminate('flushCardUpdate', err)) return;\n\n const apiCode = extractLarkApiCode(err);\n\n if (apiCode === 230020) {\n log.info('flushCardUpdate: rate limited (230020), skipping', {\n seq: this.cardKit.cardKitSequence,\n });\n return;\n }\n\n const apiDetail = extractApiDetail(err);\n log.error('card stream update failed', {\n apiCode,\n seq: this.cardKit.cardKitSequence,\n apiDetail,\n });\n if (this.cardKit.cardKitCardId) {\n log.warn('disabling CardKit streaming, falling back to im.message.patch');\n this.cardKit.cardKitCardId = null;\n }\n }\n }\n\n private buildDisplayText(): string {\n if (this.reasoning.isReasoningPhase && this.reasoning.accumulatedReasoningText) {\n const reasoningDisplay = `\uD83D\uDCAD **Thinking...**\\n\\n${this.reasoning.accumulatedReasoningText}`;\n return this.text.accumulatedText ? this.text.accumulatedText + '\\n\\n' + reasoningDisplay : reasoningDisplay;\n }\n return this.text.accumulatedText;\n }\n\n private async throttledCardUpdate(): Promise<void> {\n if (this.guard.shouldSkip('throttledCardUpdate')) return;\n const throttleMs = this.cardKit.cardKitCardId ? THROTTLE_CONSTANTS.CARDKIT_MS : THROTTLE_CONSTANTS.PATCH_MS;\n await this.flush.throttledUpdate(throttleMs);\n }\n\n // ------------------------------------------------------------------\n // Internal: lifecycle helpers\n // ------------------------------------------------------------------\n\n private finalizeCard(source: string, reason: TerminalReason): void {\n this.transition('completed', source, reason);\n }\n\n /**\n * Close streaming mode then update card content (shared by onError and abortCard).\n */\n private async closeStreamingAndUpdate(\n cardId: string,\n card: ReturnType<typeof buildCardContent>,\n label: string,\n ): Promise<void> {\n const seqBeforeClose = this.cardKit.cardKitSequence;\n this.cardKit.cardKitSequence += 1;\n log.info(`${label}: closing streaming mode`, {\n seqBefore: seqBeforeClose,\n seqAfter: this.cardKit.cardKitSequence,\n });\n await setCardStreamingMode({\n cfg: this.deps.cfg,\n cardId,\n streamingMode: false,\n sequence: this.cardKit.cardKitSequence,\n accountId: this.deps.accountId,\n });\n const seqBeforeUpdate = this.cardKit.cardKitSequence;\n this.cardKit.cardKitSequence += 1;\n log.info(`${label}: updating card`, {\n seqBefore: seqBeforeUpdate,\n seqAfter: this.cardKit.cardKitSequence,\n });\n await updateCardKitCard({\n cfg: this.deps.cfg,\n cardId,\n card: toCardKit2(card),\n sequence: this.cardKit.cardKitSequence,\n accountId: this.deps.accountId,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error detail extraction helpers (replacing `any` casts)\n// ---------------------------------------------------------------------------\n\nfunction extractApiDetail(err: unknown): string {\n if (!err || typeof err !== 'object') return String(err);\n const e = err as { response?: { data?: unknown } };\n return e.response?.data ? JSON.stringify(e.response.data) : String(err);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { larkLogger } from "../core/lark-logger";
|
|
2
|
+
import { extractLarkApiCode } from "../core/api-error";
|
|
3
|
+
import {
|
|
4
|
+
getMessageUnavailableState,
|
|
5
|
+
isMessageUnavailable,
|
|
6
|
+
isMessageUnavailableError,
|
|
7
|
+
isTerminalMessageApiCode,
|
|
8
|
+
markMessageUnavailable
|
|
9
|
+
} from "../core/message-unavailable";
|
|
10
|
+
const log = larkLogger("card/unavailable-guard");
|
|
11
|
+
class UnavailableGuard {
|
|
12
|
+
terminated = false;
|
|
13
|
+
replyToMessageId;
|
|
14
|
+
getCardMessageId;
|
|
15
|
+
onTerminate;
|
|
16
|
+
constructor(params) {
|
|
17
|
+
this.replyToMessageId = params.replyToMessageId;
|
|
18
|
+
this.getCardMessageId = params.getCardMessageId;
|
|
19
|
+
this.onTerminate = params.onTerminate;
|
|
20
|
+
}
|
|
21
|
+
get isTerminated() {
|
|
22
|
+
return this.terminated;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check whether the reply pipeline should skip further operations.
|
|
26
|
+
* Returns true if the message is already known to be unavailable.
|
|
27
|
+
*/
|
|
28
|
+
shouldSkip(source) {
|
|
29
|
+
if (this.terminated) return true;
|
|
30
|
+
if (!this.replyToMessageId) return false;
|
|
31
|
+
if (!isMessageUnavailable(this.replyToMessageId)) return false;
|
|
32
|
+
return this.terminate(source);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Attempt to terminate the reply pipeline due to an unavailable message.
|
|
36
|
+
*
|
|
37
|
+
* @param source - Descriptive label for the caller (for logging).
|
|
38
|
+
* @param err - Optional error that triggered the check.
|
|
39
|
+
* @returns true if the pipeline was (or already had been) terminated.
|
|
40
|
+
*/
|
|
41
|
+
terminate(source, err) {
|
|
42
|
+
if (this.terminated) return true;
|
|
43
|
+
const fromError = isMessageUnavailableError(err) ? err : void 0;
|
|
44
|
+
const cardMessageId = this.getCardMessageId();
|
|
45
|
+
const state = getMessageUnavailableState(this.replyToMessageId) ?? getMessageUnavailableState(cardMessageId ?? void 0);
|
|
46
|
+
let apiCode = fromError?.apiCode ?? state?.apiCode;
|
|
47
|
+
if (!apiCode && err) {
|
|
48
|
+
const detectedCode = extractLarkApiCode(err);
|
|
49
|
+
if (isTerminalMessageApiCode(detectedCode)) {
|
|
50
|
+
const fallbackMessageId = this.replyToMessageId ?? cardMessageId ?? void 0;
|
|
51
|
+
if (fallbackMessageId) {
|
|
52
|
+
markMessageUnavailable({
|
|
53
|
+
messageId: fallbackMessageId,
|
|
54
|
+
apiCode: detectedCode,
|
|
55
|
+
operation: source
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
apiCode = detectedCode;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!apiCode) return false;
|
|
62
|
+
this.terminated = true;
|
|
63
|
+
this.onTerminate();
|
|
64
|
+
const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? "unknown";
|
|
65
|
+
log.warn("reply pipeline terminated by unavailable message", {
|
|
66
|
+
source,
|
|
67
|
+
apiCode,
|
|
68
|
+
messageId: affectedMessageId
|
|
69
|
+
});
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
UnavailableGuard
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=unavailable-guard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/card/unavailable-guard.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Guard against operating on unavailable (deleted/recalled) messages.\n *\n * Encapsulates the terminateDueToUnavailable / shouldSkipForUnavailable\n * logic previously scattered as closures in reply-dispatcher.ts.\n */\n\nimport { larkLogger } from '../core/lark-logger';\nimport { extractLarkApiCode } from '../core/api-error';\nimport {\n getMessageUnavailableState,\n isMessageUnavailable,\n isMessageUnavailableError,\n isTerminalMessageApiCode,\n markMessageUnavailable,\n} from '../core/message-unavailable';\n\nconst log = larkLogger('card/unavailable-guard');\n\n// ---------------------------------------------------------------------------\n// Constructor params\n// ---------------------------------------------------------------------------\n\nexport interface UnavailableGuardParams {\n replyToMessageId: string | undefined;\n getCardMessageId: () => string | null;\n onTerminate: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// UnavailableGuard\n// ---------------------------------------------------------------------------\n\nexport class UnavailableGuard {\n private terminated = false;\n\n private readonly replyToMessageId: string | undefined;\n private readonly getCardMessageId: () => string | null;\n private readonly onTerminate: () => void;\n\n constructor(params: UnavailableGuardParams) {\n this.replyToMessageId = params.replyToMessageId;\n this.getCardMessageId = params.getCardMessageId;\n this.onTerminate = params.onTerminate;\n }\n\n get isTerminated(): boolean {\n return this.terminated;\n }\n\n /**\n * Check whether the reply pipeline should skip further operations.\n * Returns true if the message is already known to be unavailable.\n */\n shouldSkip(source: string): boolean {\n if (this.terminated) return true;\n if (!this.replyToMessageId) return false;\n if (!isMessageUnavailable(this.replyToMessageId)) return false;\n return this.terminate(source);\n }\n\n /**\n * Attempt to terminate the reply pipeline due to an unavailable message.\n *\n * @param source - Descriptive label for the caller (for logging).\n * @param err - Optional error that triggered the check.\n * @returns true if the pipeline was (or already had been) terminated.\n */\n terminate(source: string, err?: unknown): boolean {\n if (this.terminated) return true;\n\n const fromError = isMessageUnavailableError(err) ? err : undefined;\n const cardMessageId = this.getCardMessageId();\n const state =\n getMessageUnavailableState(this.replyToMessageId) ?? getMessageUnavailableState(cardMessageId ?? undefined);\n let apiCode = fromError?.apiCode ?? state?.apiCode;\n\n if (!apiCode && err) {\n const detectedCode = extractLarkApiCode(err);\n if (isTerminalMessageApiCode(detectedCode)) {\n const fallbackMessageId = this.replyToMessageId ?? cardMessageId ?? undefined;\n if (fallbackMessageId) {\n markMessageUnavailable({\n messageId: fallbackMessageId,\n apiCode: detectedCode,\n operation: source,\n });\n }\n apiCode = detectedCode;\n }\n }\n if (!apiCode) return false;\n\n this.terminated = true;\n this.onTerminate();\n\n const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? 'unknown';\n log.warn('reply pipeline terminated by unavailable message', {\n source,\n apiCode,\n messageId: affectedMessageId,\n });\n return true;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,MAAM,WAAW,wBAAwB;AAgBxC,MAAM,iBAAiB;AAAA,EACpB,aAAa;AAAA,EAEJ;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgC;AAC1C,SAAK,mBAAmB,OAAO;AAC/B,SAAK,mBAAmB,OAAO;AAC/B,SAAK,cAAc,OAAO;AAAA,EAC5B;AAAA,EAEA,IAAI,eAAwB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,QAAyB;AAClC,QAAI,KAAK,WAAY,QAAO;AAC5B,QAAI,CAAC,KAAK,iBAAkB,QAAO;AACnC,QAAI,CAAC,qBAAqB,KAAK,gBAAgB,EAAG,QAAO;AACzD,WAAO,KAAK,UAAU,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAU,QAAgB,KAAwB;AAChD,QAAI,KAAK,WAAY,QAAO;AAE5B,UAAM,YAAY,0BAA0B,GAAG,IAAI,MAAM;AACzD,UAAM,gBAAgB,KAAK,iBAAiB;AAC5C,UAAM,QACJ,2BAA2B,KAAK,gBAAgB,KAAK,2BAA2B,iBAAiB,MAAS;AAC5G,QAAI,UAAU,WAAW,WAAW,OAAO;AAE3C,QAAI,CAAC,WAAW,KAAK;AACnB,YAAM,eAAe,mBAAmB,GAAG;AAC3C,UAAI,yBAAyB,YAAY,GAAG;AAC1C,cAAM,oBAAoB,KAAK,oBAAoB,iBAAiB;AACpE,YAAI,mBAAmB;AACrB,iCAAuB;AAAA,YACrB,WAAW;AAAA,YACX,SAAS;AAAA,YACT,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AACA,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,QAAI,CAAC,QAAS,QAAO;AAErB,SAAK,aAAa;AAClB,SAAK,YAAY;AAEjB,UAAM,oBAAoB,WAAW,aAAa,KAAK,oBAAoB,iBAAiB;AAC5F,QAAI,KAAK,oDAAoD;AAAA,MAC3D;AAAA,MACA;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const ABORT_TRIGGERS = /* @__PURE__ */ new Set([
|
|
2
|
+
"stop",
|
|
3
|
+
"esc",
|
|
4
|
+
"abort",
|
|
5
|
+
"wait",
|
|
6
|
+
"exit",
|
|
7
|
+
"interrupt",
|
|
8
|
+
"detente",
|
|
9
|
+
"deten",
|
|
10
|
+
"det\xE9n",
|
|
11
|
+
"arrete",
|
|
12
|
+
"arr\xEAte",
|
|
13
|
+
"\u505C\u6B62",
|
|
14
|
+
"\u3084\u3081\u3066",
|
|
15
|
+
"\u6B62\u3081\u3066",
|
|
16
|
+
"\u0930\u0941\u0915\u094B",
|
|
17
|
+
"\u062A\u0648\u0642\u0641",
|
|
18
|
+
"\u0441\u0442\u043E\u043F",
|
|
19
|
+
"\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C",
|
|
20
|
+
"\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438",
|
|
21
|
+
"\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C",
|
|
22
|
+
"\u043F\u0440\u0435\u043A\u0440\u0430\u0442\u0438",
|
|
23
|
+
"halt",
|
|
24
|
+
"anhalten",
|
|
25
|
+
"aufh\xF6ren",
|
|
26
|
+
"hoer auf",
|
|
27
|
+
"stopp",
|
|
28
|
+
"pare",
|
|
29
|
+
"stop openclaw",
|
|
30
|
+
"openclaw stop",
|
|
31
|
+
"stop action",
|
|
32
|
+
"stop current action",
|
|
33
|
+
"stop run",
|
|
34
|
+
"stop current run",
|
|
35
|
+
"stop agent",
|
|
36
|
+
"stop the agent",
|
|
37
|
+
"stop don't do anything",
|
|
38
|
+
"stop dont do anything",
|
|
39
|
+
"stop do not do anything",
|
|
40
|
+
"stop doing anything",
|
|
41
|
+
"do not do that",
|
|
42
|
+
"please stop",
|
|
43
|
+
"stop please"
|
|
44
|
+
]);
|
|
45
|
+
const TRAILING_ABORT_PUNCTUATION_RE = /[.!?…,,。;;::'"'")\]}]+$/u;
|
|
46
|
+
function normalizeAbortTriggerText(text) {
|
|
47
|
+
return text.trim().toLowerCase().replace(/['`]/g, "'").replace(/\s+/g, " ").replace(TRAILING_ABORT_PUNCTUATION_RE, "").trim();
|
|
48
|
+
}
|
|
49
|
+
function isAbortTrigger(text) {
|
|
50
|
+
if (!text) return false;
|
|
51
|
+
const normalized = normalizeAbortTriggerText(text);
|
|
52
|
+
return ABORT_TRIGGERS.has(normalized);
|
|
53
|
+
}
|
|
54
|
+
function isLikelyAbortText(text) {
|
|
55
|
+
if (!text) return false;
|
|
56
|
+
const trimmed = text.trim().toLowerCase();
|
|
57
|
+
if (trimmed === "/stop") return true;
|
|
58
|
+
return isAbortTrigger(trimmed);
|
|
59
|
+
}
|
|
60
|
+
function extractRawTextFromEvent(event) {
|
|
61
|
+
if (!event.message || event.message.message_type !== "text") {
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(event.message.content);
|
|
66
|
+
let text = parsed?.text;
|
|
67
|
+
if (typeof text !== "string") return void 0;
|
|
68
|
+
text = text.replace(/@_user_\d+/g, "").trim();
|
|
69
|
+
return text || void 0;
|
|
70
|
+
} catch {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
extractRawTextFromEvent,
|
|
76
|
+
isAbortTrigger,
|
|
77
|
+
isLikelyAbortText
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=abort-detect.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/channel/abort-detect.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Abort trigger detection for the Lark/Feishu channel plugin.\n *\n * Provides a fast-path check to determine whether an inbound message is\n * an abort/stop command *before* it enters the per-chat serial queue.\n *\n * The trigger word list and normalisation logic are copied from the\n * OpenClaw core (`src/auto-reply/reply/abort.ts`) so the plugin can\n * make a lightweight decision without importing the full reply pipeline.\n * The message still flows through `tryFastAbortFromMessage()` for\n * authoritative handling.\n */\n\nimport type { FeishuMessageEvent } from '../messaging/types';\n\n// ---------------------------------------------------------------------------\n// Trigger word list (synced with OpenClaw core abort.ts)\n// ---------------------------------------------------------------------------\n\nconst ABORT_TRIGGERS = new Set([\n 'stop',\n 'esc',\n 'abort',\n 'wait',\n 'exit',\n 'interrupt',\n 'detente',\n 'deten',\n 'det\u00E9n',\n 'arrete',\n 'arr\u00EAte',\n '\u505C\u6B62',\n '\u3084\u3081\u3066',\n '\u6B62\u3081\u3066',\n '\u0930\u0941\u0915\u094B',\n '\u062A\u0648\u0642\u0641',\n '\u0441\u0442\u043E\u043F',\n '\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C',\n '\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438',\n '\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C',\n '\u043F\u0440\u0435\u043A\u0440\u0430\u0442\u0438',\n 'halt',\n 'anhalten',\n 'aufh\u00F6ren',\n 'hoer auf',\n 'stopp',\n 'pare',\n 'stop openclaw',\n 'openclaw stop',\n 'stop action',\n 'stop current action',\n 'stop run',\n 'stop current run',\n 'stop agent',\n 'stop the agent',\n \"stop don't do anything\",\n 'stop dont do anything',\n 'stop do not do anything',\n 'stop doing anything',\n 'do not do that',\n 'please stop',\n 'stop please',\n]);\n\n// ---------------------------------------------------------------------------\n// Normalisation helpers\n// ---------------------------------------------------------------------------\n\nconst TRAILING_ABORT_PUNCTUATION_RE = /[.!?\u2026,\uFF0C\u3002;\uFF1B:\uFF1A'\"'\")\\]}]+$/u;\n\nfunction normalizeAbortTriggerText(text: string): string {\n return text\n .trim()\n .toLowerCase()\n .replace(/['`]/g, \"'\")\n .replace(/\\s+/g, ' ')\n .replace(TRAILING_ABORT_PUNCTUATION_RE, '')\n .trim();\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** Exact trigger-word match (same logic as OpenClaw core `isAbortTrigger`). */\nexport function isAbortTrigger(text: string): boolean {\n if (!text) return false;\n const normalized = normalizeAbortTriggerText(text);\n return ABORT_TRIGGERS.has(normalized);\n}\n\n/**\n * Extended abort detection: matches both bare trigger words and the\n * `/stop` command form. Used by the monitor fast-path.\n */\nexport function isLikelyAbortText(text: string): boolean {\n if (!text) return false;\n const trimmed = text.trim().toLowerCase();\n if (trimmed === '/stop') return true;\n return isAbortTrigger(trimmed);\n}\n\n/**\n * Extract the raw text payload from a Feishu message event.\n *\n * Only handles `text` type messages. The `message.content` field is a\n * JSON string like `{\"text\":\"hello\"}`. Returns `undefined` for\n * non-text messages or parse failures.\n *\n * In group chats, bot mention placeholders (`@_user_N`) are stripped so\n * a message like `@Bot stop` is detected as `stop`.\n */\nexport function extractRawTextFromEvent(event: FeishuMessageEvent): string | undefined {\n if (!event.message || event.message.message_type !== 'text') {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(event.message.content);\n let text: string | undefined = parsed?.text;\n if (typeof text !== 'string') return undefined;\n\n // Strip bot mention placeholders (@_user_1, @_user_2, etc.)\n text = text.replace(/@_user_\\d+/g, '').trim();\n\n return text || undefined;\n } catch {\n return undefined;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAsBA,MAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,MAAM,gCAAgC;AAEtC,SAAS,0BAA0B,MAAsB;AACvD,SAAO,KACJ,KAAK,EACL,YAAY,EACZ,QAAQ,SAAS,GAAG,EACpB,QAAQ,QAAQ,GAAG,EACnB,QAAQ,+BAA+B,EAAE,EACzC,KAAK;AACV;AAOO,SAAS,eAAe,MAAuB;AACpD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,aAAa,0BAA0B,IAAI;AACjD,SAAO,eAAe,IAAI,UAAU;AACtC;AAMO,SAAS,kBAAkB,MAAuB;AACvD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,UAAU,KAAK,KAAK,EAAE,YAAY;AACxC,MAAI,YAAY,QAAS,QAAO;AAChC,SAAO,eAAe,OAAO;AAC/B;AAYO,SAAS,wBAAwB,OAA+C;AACrF,MAAI,CAAC,MAAM,WAAW,MAAM,QAAQ,iBAAiB,QAAQ;AAC3D,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,MAAM,QAAQ,OAAO;AAC/C,QAAI,OAA2B,QAAQ;AACvC,QAAI,OAAO,SAAS,SAAU,QAAO;AAGrC,WAAO,KAAK,QAAQ,eAAe,EAAE,EAAE,KAAK;AAE5C,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const chatQueues = /* @__PURE__ */ new Map();
|
|
2
|
+
const activeDispatchers = /* @__PURE__ */ new Map();
|
|
3
|
+
function threadScopedKey(base, threadId) {
|
|
4
|
+
return threadId ? `${base}:thread:${threadId}` : base;
|
|
5
|
+
}
|
|
6
|
+
function buildQueueKey(accountId, chatId, threadId) {
|
|
7
|
+
return threadScopedKey(`${accountId}:${chatId}`, threadId);
|
|
8
|
+
}
|
|
9
|
+
function registerActiveDispatcher(key, entry) {
|
|
10
|
+
activeDispatchers.set(key, entry);
|
|
11
|
+
}
|
|
12
|
+
function unregisterActiveDispatcher(key) {
|
|
13
|
+
activeDispatchers.delete(key);
|
|
14
|
+
}
|
|
15
|
+
function getActiveDispatcher(key) {
|
|
16
|
+
return activeDispatchers.get(key);
|
|
17
|
+
}
|
|
18
|
+
function hasActiveTask(key) {
|
|
19
|
+
return chatQueues.has(key);
|
|
20
|
+
}
|
|
21
|
+
function enqueueFeishuChatTask(params) {
|
|
22
|
+
const { accountId, chatId, threadId, task } = params;
|
|
23
|
+
const key = buildQueueKey(accountId, chatId, threadId);
|
|
24
|
+
const prev = chatQueues.get(key) ?? Promise.resolve();
|
|
25
|
+
const status = chatQueues.has(key) ? "queued" : "immediate";
|
|
26
|
+
const next = prev.then(task, task);
|
|
27
|
+
chatQueues.set(key, next);
|
|
28
|
+
const cleanup = () => {
|
|
29
|
+
if (chatQueues.get(key) === next) {
|
|
30
|
+
chatQueues.delete(key);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
next.then(cleanup, cleanup);
|
|
34
|
+
return { status, promise: next };
|
|
35
|
+
}
|
|
36
|
+
function _resetChatQueueState() {
|
|
37
|
+
chatQueues.clear();
|
|
38
|
+
activeDispatchers.clear();
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
_resetChatQueueState,
|
|
42
|
+
buildQueueKey,
|
|
43
|
+
enqueueFeishuChatTask,
|
|
44
|
+
getActiveDispatcher,
|
|
45
|
+
hasActiveTask,
|
|
46
|
+
registerActiveDispatcher,
|
|
47
|
+
threadScopedKey,
|
|
48
|
+
unregisterActiveDispatcher
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=chat-queue.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/channel/chat-queue.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Process-level chat task queue.\n *\n * Although located in channel/, this module is intentionally shared\n * across channel, messaging, tools, and card layers as a process-level\n * singleton. Consumers: monitor.ts, dispatch.ts, oauth.ts, auto-auth.ts.\n *\n * Ensures tasks targeting the same account+chat are executed serially.\n * Used by both websocket inbound messages and synthetic message paths.\n */\n\ntype QueueStatus = 'queued' | 'immediate';\n\nexport interface ActiveDispatcherEntry {\n abortCard: () => Promise<void>;\n abortController?: AbortController;\n}\n\nconst chatQueues = new Map<string, Promise<void>>();\nconst activeDispatchers = new Map<string, ActiveDispatcherEntry>();\n\n/**\n * Append `:thread:{threadId}` suffix when threadId is present.\n * Consistent with the SDK's `:thread:` separator convention.\n */\nexport function threadScopedKey(base: string, threadId?: string): string {\n return threadId ? `${base}:thread:${threadId}` : base;\n}\n\nexport function buildQueueKey(accountId: string, chatId: string, threadId?: string): string {\n return threadScopedKey(`${accountId}:${chatId}`, threadId);\n}\n\nexport function registerActiveDispatcher(key: string, entry: ActiveDispatcherEntry): void {\n activeDispatchers.set(key, entry);\n}\n\nexport function unregisterActiveDispatcher(key: string): void {\n activeDispatchers.delete(key);\n}\n\nexport function getActiveDispatcher(key: string): ActiveDispatcherEntry | undefined {\n return activeDispatchers.get(key);\n}\n\n/** Check whether the queue has an active task for the given key. */\nexport function hasActiveTask(key: string): boolean {\n return chatQueues.has(key);\n}\n\nexport function enqueueFeishuChatTask(params: {\n accountId: string;\n chatId: string;\n threadId?: string;\n task: () => Promise<void>;\n}): { status: QueueStatus; promise: Promise<void> } {\n const { accountId, chatId, threadId, task } = params;\n const key = buildQueueKey(accountId, chatId, threadId);\n const prev = chatQueues.get(key) ?? Promise.resolve();\n const status: QueueStatus = chatQueues.has(key) ? 'queued' : 'immediate';\n const next = prev.then(task, task); // continue queue even if previous task failed\n chatQueues.set(key, next);\n\n const cleanup = (): void => {\n if (chatQueues.get(key) === next) {\n chatQueues.delete(key);\n }\n };\n\n next.then(cleanup, cleanup);\n\n return { status, promise: next };\n}\n\n/** @internal Test-only: reset all queue and dispatcher state. */\nexport function _resetChatQueueState(): void {\n chatQueues.clear();\n activeDispatchers.clear();\n}\n"],
|
|
5
|
+
"mappings": "AAqBA,MAAM,aAAa,oBAAI,IAA2B;AAClD,MAAM,oBAAoB,oBAAI,IAAmC;AAM1D,SAAS,gBAAgB,MAAc,UAA2B;AACvE,SAAO,WAAW,GAAG,IAAI,WAAW,QAAQ,KAAK;AACnD;AAEO,SAAS,cAAc,WAAmB,QAAgB,UAA2B;AAC1F,SAAO,gBAAgB,GAAG,SAAS,IAAI,MAAM,IAAI,QAAQ;AAC3D;AAEO,SAAS,yBAAyB,KAAa,OAAoC;AACxF,oBAAkB,IAAI,KAAK,KAAK;AAClC;AAEO,SAAS,2BAA2B,KAAmB;AAC5D,oBAAkB,OAAO,GAAG;AAC9B;AAEO,SAAS,oBAAoB,KAAgD;AAClF,SAAO,kBAAkB,IAAI,GAAG;AAClC;AAGO,SAAS,cAAc,KAAsB;AAClD,SAAO,WAAW,IAAI,GAAG;AAC3B;AAEO,SAAS,sBAAsB,QAKc;AAClD,QAAM,EAAE,WAAW,QAAQ,UAAU,KAAK,IAAI;AAC9C,QAAM,MAAM,cAAc,WAAW,QAAQ,QAAQ;AACrD,QAAM,OAAO,WAAW,IAAI,GAAG,KAAK,QAAQ,QAAQ;AACpD,QAAM,SAAsB,WAAW,IAAI,GAAG,IAAI,WAAW;AAC7D,QAAM,OAAO,KAAK,KAAK,MAAM,IAAI;AACjC,aAAW,IAAI,KAAK,IAAI;AAExB,QAAM,UAAU,MAAY;AAC1B,QAAI,WAAW,IAAI,GAAG,MAAM,MAAM;AAChC,iBAAW,OAAO,GAAG;AAAA,IACvB;AAAA,EACF;AAEA,OAAK,KAAK,SAAS,OAAO;AAE1B,SAAO,EAAE,QAAQ,SAAS,KAAK;AACjC;AAGO,SAAS,uBAA6B;AAC3C,aAAW,MAAM;AACjB,oBAAkB,MAAM;AAC1B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
|
|
2
|
+
import { getLarkAccount, getLarkAccountIds } from "../core/accounts";
|
|
3
|
+
import { collectIsolationWarnings } from "../core/security-check";
|
|
4
|
+
function mergeFeishuAccountConfig(cfg, accountId, patch) {
|
|
5
|
+
const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
|
|
6
|
+
if (isDefault) {
|
|
7
|
+
return {
|
|
8
|
+
...cfg,
|
|
9
|
+
channels: {
|
|
10
|
+
...cfg.channels,
|
|
11
|
+
feishu: { ...cfg.channels?.feishu, ...patch }
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const feishuCfg = cfg.channels?.feishu;
|
|
16
|
+
return {
|
|
17
|
+
...cfg,
|
|
18
|
+
channels: {
|
|
19
|
+
...cfg.channels,
|
|
20
|
+
feishu: {
|
|
21
|
+
...feishuCfg,
|
|
22
|
+
accounts: {
|
|
23
|
+
...feishuCfg?.accounts,
|
|
24
|
+
[accountId]: { ...feishuCfg?.accounts?.[accountId], ...patch }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function setAccountEnabled(cfg, accountId, enabled) {
|
|
31
|
+
return mergeFeishuAccountConfig(cfg, accountId, { enabled });
|
|
32
|
+
}
|
|
33
|
+
function applyAccountConfig(cfg, accountId, patch) {
|
|
34
|
+
return mergeFeishuAccountConfig(cfg, accountId, patch);
|
|
35
|
+
}
|
|
36
|
+
function deleteAccount(cfg, accountId) {
|
|
37
|
+
const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
|
|
38
|
+
if (isDefault) {
|
|
39
|
+
const next = { ...cfg };
|
|
40
|
+
const nextChannels = { ...cfg.channels };
|
|
41
|
+
delete nextChannels.feishu;
|
|
42
|
+
if (Object.keys(nextChannels).length > 0) {
|
|
43
|
+
next.channels = nextChannels;
|
|
44
|
+
} else {
|
|
45
|
+
delete next.channels;
|
|
46
|
+
}
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
const feishuCfg = cfg.channels?.feishu;
|
|
50
|
+
const accounts = { ...feishuCfg?.accounts };
|
|
51
|
+
delete accounts[accountId];
|
|
52
|
+
return {
|
|
53
|
+
...cfg,
|
|
54
|
+
channels: {
|
|
55
|
+
...cfg.channels,
|
|
56
|
+
feishu: {
|
|
57
|
+
...feishuCfg,
|
|
58
|
+
accounts: Object.keys(accounts).length > 0 ? accounts : void 0
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function collectFeishuSecurityWarnings(params) {
|
|
64
|
+
const { cfg, accountId } = params;
|
|
65
|
+
const warnings = [];
|
|
66
|
+
const account = getLarkAccount(cfg, accountId);
|
|
67
|
+
const feishuCfg = account.config;
|
|
68
|
+
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
69
|
+
const groupPolicy = feishuCfg?.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
|
70
|
+
if (groupPolicy === "open") {
|
|
71
|
+
warnings.push(
|
|
72
|
+
`- Feishu[${account.accountId}] groups: groupPolicy="open" allows any group to interact (mention-gated). To restrict which groups are allowed, set groupPolicy="allowlist" and list group IDs in channels.feishu.groups. To restrict which senders can trigger the bot, set channels.feishu.groupAllowFrom with user open_ids (ou_xxx).`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const allIds = getLarkAccountIds(cfg);
|
|
76
|
+
if (allIds.length === 0 || accountId === allIds[0]) {
|
|
77
|
+
for (const w of collectIsolationWarnings(cfg)) {
|
|
78
|
+
warnings.push(w);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return warnings;
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
applyAccountConfig,
|
|
85
|
+
collectFeishuSecurityWarnings,
|
|
86
|
+
deleteAccount,
|
|
87
|
+
setAccountEnabled
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=config-adapter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/channel/config-adapter.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Configuration merge helpers for Feishu account management.\n *\n * Centralises the pattern of merging a partial configuration patch\n * into the Feishu section of the top-level ClawdbotConfig, handling\n * both the default account (top-level fields) and named accounts\n * (nested under `accounts`).\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk';\nimport type { FeishuConfig } from '../core/types';\nimport { getLarkAccount, getLarkAccountIds } from '../core/accounts';\nimport { collectIsolationWarnings } from '../core/security-check';\n\n/** Generic Feishu account config merge. */\nfunction mergeFeishuAccountConfig(\n cfg: ClawdbotConfig,\n accountId: string,\n patch: Record<string, unknown>,\n): ClawdbotConfig {\n const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;\n if (isDefault) {\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n feishu: { ...cfg.channels?.feishu, ...patch },\n },\n };\n }\n const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n feishu: {\n ...feishuCfg,\n accounts: {\n ...feishuCfg?.accounts,\n [accountId]: { ...feishuCfg?.accounts?.[accountId], ...patch },\n },\n },\n },\n };\n}\n\n/** Set the `enabled` flag on a Feishu account. */\nexport function setAccountEnabled(cfg: ClawdbotConfig, accountId: string, enabled: boolean): ClawdbotConfig {\n return mergeFeishuAccountConfig(cfg, accountId, { enabled });\n}\n\n/** Apply an arbitrary config patch to a Feishu account. */\nexport function applyAccountConfig(\n cfg: ClawdbotConfig,\n accountId: string,\n patch: Record<string, unknown>,\n): ClawdbotConfig {\n return mergeFeishuAccountConfig(cfg, accountId, patch);\n}\n\n/** Delete a Feishu account entry from the config. */\nexport function deleteAccount(cfg: ClawdbotConfig, accountId: string): ClawdbotConfig {\n const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;\n\n if (isDefault) {\n // Delete entire feishu config\n const next = { ...cfg } as ClawdbotConfig;\n const nextChannels = { ...cfg.channels };\n delete (nextChannels as Record<string, unknown>).feishu;\n if (Object.keys(nextChannels).length > 0) {\n next.channels = nextChannels;\n } else {\n delete next.channels;\n }\n return next;\n }\n\n // Delete specific account from accounts\n const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;\n const accounts = { ...feishuCfg?.accounts };\n delete accounts[accountId];\n\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n feishu: {\n ...feishuCfg,\n accounts: Object.keys(accounts).length > 0 ? accounts : undefined,\n },\n },\n };\n}\n\n/** Collect security warnings for a Feishu account. */\nexport function collectFeishuSecurityWarnings(params: { cfg: ClawdbotConfig; accountId: string }): string[] {\n const { cfg, accountId } = params;\n const warnings: string[] = [];\n\n const account = getLarkAccount(cfg, accountId);\n const feishuCfg = account.config;\n // cfg.channels.defaults is a cross-channel defaults object (not formally typed)\n const defaultGroupPolicy = (\n (cfg.channels as Record<string, unknown> | undefined)?.defaults as { groupPolicy?: string } | undefined\n )?.groupPolicy;\n const groupPolicy = feishuCfg?.groupPolicy ?? defaultGroupPolicy ?? 'allowlist';\n if (groupPolicy === 'open') {\n warnings.push(\n `- Feishu[${account.accountId}] groups: groupPolicy=\"open\" allows any group to interact (mention-gated). To restrict which groups are allowed, set groupPolicy=\"allowlist\" and list group IDs in channels.feishu.groups. To restrict which senders can trigger the bot, set channels.feishu.groupAllowFrom with user open_ids (ou_xxx).`,\n );\n }\n\n // Multi-account cross-tenant isolation check (only on first account to avoid duplicates)\n const allIds = getLarkAccountIds(cfg);\n if (allIds.length === 0 || accountId === allIds[0]) {\n for (const w of collectIsolationWarnings(cfg)) {\n warnings.push(w);\n }\n }\n\n return warnings;\n}\n"],
|
|
5
|
+
"mappings": "AAaA,SAAS,0BAA0B;AAEnC,SAAS,gBAAgB,yBAAyB;AAClD,SAAS,gCAAgC;AAGzC,SAAS,yBACP,KACA,WACA,OACgB;AAChB,QAAM,YAAY,CAAC,aAAa,cAAc;AAC9C,MAAI,WAAW;AACb,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU;AAAA,QACR,GAAG,IAAI;AAAA,QACP,QAAQ,EAAE,GAAG,IAAI,UAAU,QAAQ,GAAG,MAAM;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,IAAI,UAAU;AAChC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU;AAAA,MACR,GAAG,IAAI;AAAA,MACP,QAAQ;AAAA,QACN,GAAG;AAAA,QACH,UAAU;AAAA,UACR,GAAG,WAAW;AAAA,UACd,CAAC,SAAS,GAAG,EAAE,GAAG,WAAW,WAAW,SAAS,GAAG,GAAG,MAAM;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGO,SAAS,kBAAkB,KAAqB,WAAmB,SAAkC;AAC1G,SAAO,yBAAyB,KAAK,WAAW,EAAE,QAAQ,CAAC;AAC7D;AAGO,SAAS,mBACd,KACA,WACA,OACgB;AAChB,SAAO,yBAAyB,KAAK,WAAW,KAAK;AACvD;AAGO,SAAS,cAAc,KAAqB,WAAmC;AACpF,QAAM,YAAY,CAAC,aAAa,cAAc;AAE9C,MAAI,WAAW;AAEb,UAAM,OAAO,EAAE,GAAG,IAAI;AACtB,UAAM,eAAe,EAAE,GAAG,IAAI,SAAS;AACvC,WAAQ,aAAyC;AACjD,QAAI,OAAO,KAAK,YAAY,EAAE,SAAS,GAAG;AACxC,WAAK,WAAW;AAAA,IAClB,OAAO;AACL,aAAO,KAAK;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,IAAI,UAAU;AAChC,QAAM,WAAW,EAAE,GAAG,WAAW,SAAS;AAC1C,SAAO,SAAS,SAAS;AAEzB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU;AAAA,MACR,GAAG,IAAI;AAAA,MACP,QAAQ;AAAA,QACN,GAAG;AAAA,QACH,UAAU,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;AAGO,SAAS,8BAA8B,QAA8D;AAC1G,QAAM,EAAE,KAAK,UAAU,IAAI;AAC3B,QAAM,WAAqB,CAAC;AAE5B,QAAM,UAAU,eAAe,KAAK,SAAS;AAC7C,QAAM,YAAY,QAAQ;AAE1B,QAAM,qBACH,IAAI,UAAkD,UACtD;AACH,QAAM,cAAc,WAAW,eAAe,sBAAsB;AACpE,MAAI,gBAAgB,QAAQ;AAC1B,aAAS;AAAA,MACP,YAAY,QAAQ,SAAS;AAAA,IAC/B;AAAA,EACF;AAGA,QAAM,SAAS,kBAAkB,GAAG;AACpC,MAAI,OAAO,WAAW,KAAK,cAAc,OAAO,CAAC,GAAG;AAClD,eAAW,KAAK,yBAAyB,GAAG,GAAG;AAC7C,eAAS,KAAK,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { getLarkAccount } from "../core/accounts";
|
|
2
|
+
import { LarkClient } from "../core/lark-client";
|
|
3
|
+
import { normalizeFeishuTarget } from "../core/targets";
|
|
4
|
+
function matchesQuery(id, name, query) {
|
|
5
|
+
if (!query) return true;
|
|
6
|
+
return id.toLowerCase().includes(query) || (name?.toLowerCase().includes(query) ?? false);
|
|
7
|
+
}
|
|
8
|
+
function applyLimitSlice(items, limit) {
|
|
9
|
+
return limit && limit > 0 ? items.slice(0, limit) : items;
|
|
10
|
+
}
|
|
11
|
+
async function listFeishuDirectoryPeers(params) {
|
|
12
|
+
const account = getLarkAccount(params.cfg, params.accountId);
|
|
13
|
+
const feishuCfg = account.config;
|
|
14
|
+
const q = params.query?.trim().toLowerCase() || "";
|
|
15
|
+
const ids = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const entry of feishuCfg?.allowFrom ?? []) {
|
|
17
|
+
const trimmed = String(entry).trim();
|
|
18
|
+
if (trimmed && trimmed !== "*") {
|
|
19
|
+
ids.add(trimmed);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (const userId of Object.keys(feishuCfg?.dms ?? {})) {
|
|
23
|
+
const trimmed = userId.trim();
|
|
24
|
+
if (trimmed) {
|
|
25
|
+
ids.add(trimmed);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const peers = Array.from(ids).map((raw) => raw.trim()).filter(Boolean).map((raw) => normalizeFeishuTarget(raw) ?? raw).filter((id) => matchesQuery(id, void 0, q)).map((id) => ({ kind: "user", id }));
|
|
29
|
+
return applyLimitSlice(peers, params.limit);
|
|
30
|
+
}
|
|
31
|
+
async function listFeishuDirectoryGroups(params) {
|
|
32
|
+
const account = getLarkAccount(params.cfg, params.accountId);
|
|
33
|
+
const feishuCfg = account.config;
|
|
34
|
+
const q = params.query?.trim().toLowerCase() || "";
|
|
35
|
+
const ids = /* @__PURE__ */ new Set();
|
|
36
|
+
for (const groupId of Object.keys(feishuCfg?.groups ?? {})) {
|
|
37
|
+
const trimmed = groupId.trim();
|
|
38
|
+
if (trimmed && trimmed !== "*") {
|
|
39
|
+
ids.add(trimmed);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const entry of feishuCfg?.groupAllowFrom ?? []) {
|
|
43
|
+
const trimmed = String(entry).trim();
|
|
44
|
+
if (trimmed && trimmed !== "*") {
|
|
45
|
+
ids.add(trimmed);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const groups = Array.from(ids).map((raw) => raw.trim()).filter(Boolean).filter((id) => matchesQuery(id, void 0, q)).map((id) => ({ kind: "group", id }));
|
|
49
|
+
return applyLimitSlice(groups, params.limit);
|
|
50
|
+
}
|
|
51
|
+
async function listFeishuDirectoryPeersLive(params) {
|
|
52
|
+
const account = getLarkAccount(params.cfg, params.accountId);
|
|
53
|
+
if (!account.configured) {
|
|
54
|
+
return listFeishuDirectoryPeers(params);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const client = LarkClient.fromAccount(account).sdk;
|
|
58
|
+
const peers = [];
|
|
59
|
+
const limit = params.limit ?? 50;
|
|
60
|
+
if (limit <= 0) return [];
|
|
61
|
+
const q = params.query?.trim().toLowerCase() || "";
|
|
62
|
+
let pageToken;
|
|
63
|
+
do {
|
|
64
|
+
const remaining = limit - peers.length;
|
|
65
|
+
const response = await client.contact.user.list({
|
|
66
|
+
params: {
|
|
67
|
+
page_size: Math.min(remaining, 50),
|
|
68
|
+
page_token: pageToken
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (response.code !== 0 || !response.data?.items) break;
|
|
72
|
+
for (const user of response.data.items) {
|
|
73
|
+
if (user.open_id && matchesQuery(user.open_id, user.name, q)) {
|
|
74
|
+
peers.push({
|
|
75
|
+
kind: "user",
|
|
76
|
+
id: user.open_id,
|
|
77
|
+
name: user.name || void 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (peers.length >= limit) break;
|
|
81
|
+
}
|
|
82
|
+
pageToken = response.data?.page_token;
|
|
83
|
+
} while (pageToken && peers.length < limit);
|
|
84
|
+
return peers;
|
|
85
|
+
} catch {
|
|
86
|
+
return listFeishuDirectoryPeers(params);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function listFeishuDirectoryGroupsLive(params) {
|
|
90
|
+
const account = getLarkAccount(params.cfg, params.accountId);
|
|
91
|
+
if (!account.configured) {
|
|
92
|
+
return listFeishuDirectoryGroups(params);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const client = LarkClient.fromAccount(account).sdk;
|
|
96
|
+
const groups = [];
|
|
97
|
+
const limit = params.limit ?? 50;
|
|
98
|
+
if (limit <= 0) return [];
|
|
99
|
+
const q = params.query?.trim().toLowerCase() || "";
|
|
100
|
+
let pageToken;
|
|
101
|
+
do {
|
|
102
|
+
const remaining = limit - groups.length;
|
|
103
|
+
const response = await client.im.chat.list({
|
|
104
|
+
params: {
|
|
105
|
+
page_size: Math.min(remaining, 100),
|
|
106
|
+
page_token: pageToken
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
if (response.code !== 0 || !response.data?.items) break;
|
|
110
|
+
for (const chat of response.data.items) {
|
|
111
|
+
if (chat.chat_id && matchesQuery(chat.chat_id, chat.name, q)) {
|
|
112
|
+
groups.push({
|
|
113
|
+
kind: "group",
|
|
114
|
+
id: chat.chat_id,
|
|
115
|
+
name: chat.name || void 0
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (groups.length >= limit) break;
|
|
119
|
+
}
|
|
120
|
+
pageToken = response.data?.page_token;
|
|
121
|
+
} while (pageToken && groups.length < limit);
|
|
122
|
+
return groups;
|
|
123
|
+
} catch {
|
|
124
|
+
return listFeishuDirectoryGroups(params);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
listFeishuDirectoryGroups,
|
|
129
|
+
listFeishuDirectoryGroupsLive,
|
|
130
|
+
listFeishuDirectoryPeers,
|
|
131
|
+
listFeishuDirectoryPeersLive
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=directory.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/channel/directory.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Directory listing for Feishu peers (users) and groups.\n *\n * Provides both config-based (offline) and live API directory\n * lookups so the outbound subsystem and UI can resolve targets.\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { getLarkAccount } from '../core/accounts';\nimport { LarkClient } from '../core/lark-client';\nimport { normalizeFeishuTarget } from '../core/targets';\nimport type { FeishuDirectoryPeer, FeishuDirectoryGroup } from './types';\n\n// Re-export types for backward compatibility\nexport type { FeishuDirectoryPeer, FeishuDirectoryGroup } from './types';\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n/** Case-insensitive substring match on id and optional name. */\nfunction matchesQuery(id: string, name: string | undefined, query: string): boolean {\n if (!query) return true;\n return id.toLowerCase().includes(query) || (name?.toLowerCase().includes(query) ?? false);\n}\n\n/** Filter items and apply optional limit. */\nfunction applyLimitSlice<T>(items: T[], limit?: number): T[] {\n return limit && limit > 0 ? items.slice(0, limit) : items;\n}\n\n// ---------------------------------------------------------------------------\n// Config-based (offline) directory\n// ---------------------------------------------------------------------------\n\n/**\n * List users known from the channel config (allowFrom + dms fields).\n *\n * Does not make any API calls -- useful when the bot is not yet\n * connected or when credentials are unavailable.\n */\nexport async function listFeishuDirectoryPeers(params: {\n cfg: ClawdbotConfig;\n query?: string;\n limit?: number;\n accountId?: string;\n}): Promise<FeishuDirectoryPeer[]> {\n const account = getLarkAccount(params.cfg, params.accountId);\n const feishuCfg = account.config;\n const q = params.query?.trim().toLowerCase() || '';\n const ids = new Set<string>();\n\n // Collect from allowFrom entries.\n for (const entry of feishuCfg?.allowFrom ?? []) {\n const trimmed = String(entry).trim();\n if (trimmed && trimmed !== '*') {\n ids.add(trimmed);\n }\n }\n\n // Collect from per-user DM config keys.\n for (const userId of Object.keys(feishuCfg?.dms ?? {})) {\n const trimmed = userId.trim();\n if (trimmed) {\n ids.add(trimmed);\n }\n }\n\n const peers = Array.from(ids)\n .map((raw) => raw.trim())\n .filter(Boolean)\n .map((raw) => normalizeFeishuTarget(raw) ?? raw)\n .filter((id) => matchesQuery(id, undefined, q))\n .map((id) => ({ kind: 'user' as const, id }));\n\n return applyLimitSlice(peers, params.limit);\n}\n\n/**\n * List groups known from the channel config (groups + groupAllowFrom).\n */\nexport async function listFeishuDirectoryGroups(params: {\n cfg: ClawdbotConfig;\n query?: string;\n limit?: number;\n accountId?: string;\n}): Promise<FeishuDirectoryGroup[]> {\n const account = getLarkAccount(params.cfg, params.accountId);\n const feishuCfg = account.config;\n const q = params.query?.trim().toLowerCase() || '';\n const ids = new Set<string>();\n\n // Collect from per-group config keys.\n for (const groupId of Object.keys(feishuCfg?.groups ?? {})) {\n const trimmed = groupId.trim();\n if (trimmed && trimmed !== '*') {\n ids.add(trimmed);\n }\n }\n\n // Collect from groupAllowFrom entries.\n for (const entry of feishuCfg?.groupAllowFrom ?? []) {\n const trimmed = String(entry).trim();\n if (trimmed && trimmed !== '*') {\n ids.add(trimmed);\n }\n }\n\n const groups = Array.from(ids)\n .map((raw) => raw.trim())\n .filter(Boolean)\n .filter((id) => matchesQuery(id, undefined, q))\n .map((id) => ({ kind: 'group' as const, id }));\n\n return applyLimitSlice(groups, params.limit);\n}\n\n// ---------------------------------------------------------------------------\n// Live API directory\n// ---------------------------------------------------------------------------\n\n/**\n * List users via the Feishu contact/v3/users API.\n *\n * Falls back to config-based listing when credentials are missing or\n * the API call fails.\n */\nexport async function listFeishuDirectoryPeersLive(params: {\n cfg: ClawdbotConfig;\n query?: string;\n limit?: number;\n accountId?: string;\n}): Promise<FeishuDirectoryPeer[]> {\n const account = getLarkAccount(params.cfg, params.accountId);\n if (!account.configured) {\n return listFeishuDirectoryPeers(params);\n }\n\n try {\n const client = LarkClient.fromAccount(account).sdk;\n const peers: FeishuDirectoryPeer[] = [];\n const limit = params.limit ?? 50;\n if (limit <= 0) return [];\n const q = params.query?.trim().toLowerCase() || '';\n let pageToken: string | undefined;\n\n do {\n const remaining = limit - peers.length;\n const response = await client.contact.user.list({\n params: {\n page_size: Math.min(remaining, 50),\n page_token: pageToken,\n },\n });\n\n if (response.code !== 0 || !response.data?.items) break;\n\n for (const user of response.data.items) {\n if (user.open_id && matchesQuery(user.open_id, user.name, q)) {\n peers.push({\n kind: 'user',\n id: user.open_id,\n name: user.name || undefined,\n });\n }\n if (peers.length >= limit) break;\n }\n\n pageToken = response.data?.page_token;\n } while (pageToken && peers.length < limit);\n\n return peers;\n } catch {\n // Fallback to config-based listing on API failure.\n return listFeishuDirectoryPeers(params);\n }\n}\n\n/**\n * List groups via the Feishu im/v1/chats API.\n *\n * Falls back to config-based listing when credentials are missing or\n * the API call fails.\n */\nexport async function listFeishuDirectoryGroupsLive(params: {\n cfg: ClawdbotConfig;\n query?: string;\n limit?: number;\n accountId?: string;\n}): Promise<FeishuDirectoryGroup[]> {\n const account = getLarkAccount(params.cfg, params.accountId);\n if (!account.configured) {\n return listFeishuDirectoryGroups(params);\n }\n\n try {\n const client = LarkClient.fromAccount(account).sdk;\n const groups: FeishuDirectoryGroup[] = [];\n const limit = params.limit ?? 50;\n if (limit <= 0) return [];\n const q = params.query?.trim().toLowerCase() || '';\n let pageToken: string | undefined;\n\n do {\n const remaining = limit - groups.length;\n const response = await client.im.chat.list({\n params: {\n page_size: Math.min(remaining, 100),\n page_token: pageToken,\n },\n });\n\n if (response.code !== 0 || !response.data?.items) break;\n\n for (const chat of response.data.items) {\n if (chat.chat_id && matchesQuery(chat.chat_id, chat.name, q)) {\n groups.push({\n kind: 'group',\n id: chat.chat_id,\n name: chat.name || undefined,\n });\n }\n if (groups.length >= limit) break;\n }\n\n pageToken = response.data?.page_token;\n } while (pageToken && groups.length < limit);\n\n return groups;\n } catch {\n // Fallback to config-based listing on API failure.\n return listFeishuDirectoryGroups(params);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|