@larksuite/openclaw-lark 2025.7.15-beta.0
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 +76 -0
- package/bin/openclaw-lark.js +39 -0
- package/index.d.ts +36 -0
- package/index.js +188 -0
- package/openclaw.plugin.json +64 -0
- package/package.json +63 -0
- package/secret-contract-api.d.ts +15 -0
- package/secret-contract-api.js +78 -0
- package/skills/feishu-bitable/SKILL.md +248 -0
- package/skills/feishu-bitable/references/examples.md +813 -0
- package/skills/feishu-bitable/references/field-properties.md +763 -0
- package/skills/feishu-bitable/references/record-values.md +911 -0
- package/skills/feishu-calendar/SKILL.md +242 -0
- package/skills/feishu-channel-rules/SKILL.md +18 -0
- package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/skills/feishu-create-doc/SKILL.md +719 -0
- package/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/skills/feishu-im-read/SKILL.md +163 -0
- package/skills/feishu-task/SKILL.md +340 -0
- package/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/skills/feishu-update-doc/SKILL.md +285 -0
- package/src/card/builder.d.ts +152 -0
- package/src/card/builder.js +794 -0
- package/src/card/card-error.d.ts +91 -0
- package/src/card/card-error.js +206 -0
- package/src/card/cardkit.d.ts +90 -0
- package/src/card/cardkit.js +203 -0
- package/src/card/flush-controller.d.ts +45 -0
- package/src/card/flush-controller.js +138 -0
- package/src/card/image-resolver.d.ts +45 -0
- package/src/card/image-resolver.js +116 -0
- package/src/card/markdown-style.d.ts +16 -0
- package/src/card/markdown-style.js +106 -0
- package/src/card/reasoning-utils.d.ts +14 -0
- package/src/card/reasoning-utils.js +64 -0
- package/src/card/reply-dispatcher-types.d.ts +132 -0
- package/src/card/reply-dispatcher-types.js +61 -0
- package/src/card/reply-dispatcher.d.ts +15 -0
- package/src/card/reply-dispatcher.js +447 -0
- package/src/card/reply-mode.d.ts +41 -0
- package/src/card/reply-mode.js +70 -0
- package/src/card/streaming-card-controller.d.ts +118 -0
- package/src/card/streaming-card-controller.js +1045 -0
- package/src/card/tool-use-config.d.ts +26 -0
- package/src/card/tool-use-config.js +76 -0
- package/src/card/tool-use-display.d.ts +37 -0
- package/src/card/tool-use-display.js +476 -0
- package/src/card/tool-use-trace-store.d.ts +51 -0
- package/src/card/tool-use-trace-store.js +271 -0
- package/src/card/unavailable-guard.d.ts +35 -0
- package/src/card/unavailable-guard.js +87 -0
- package/src/channel/abort-detect.d.ts +47 -0
- package/src/channel/abort-detect.js +219 -0
- package/src/channel/chat-queue.d.ts +41 -0
- package/src/channel/chat-queue.js +68 -0
- package/src/channel/config-adapter.d.ts +23 -0
- package/src/channel/config-adapter.js +107 -0
- package/src/channel/directory.d.ts +57 -0
- package/src/channel/directory.js +197 -0
- package/src/channel/event-handlers.d.ts +17 -0
- package/src/channel/event-handlers.js +380 -0
- package/src/channel/interactive-dispatch.d.ts +59 -0
- package/src/channel/interactive-dispatch.js +195 -0
- package/src/channel/monitor.d.ts +17 -0
- package/src/channel/monitor.js +140 -0
- package/src/channel/onboarding-config.d.ts +18 -0
- package/src/channel/onboarding-config.js +96 -0
- package/src/channel/onboarding-migrate.d.ts +25 -0
- package/src/channel/onboarding-migrate.js +70 -0
- package/src/channel/onboarding.d.ts +12 -0
- package/src/channel/onboarding.js +300 -0
- package/src/channel/plugin.d.ts +13 -0
- package/src/channel/plugin.js +310 -0
- package/src/channel/probe.d.ts +14 -0
- package/src/channel/probe.js +24 -0
- package/src/channel/types.d.ts +37 -0
- package/src/channel/types.js +8 -0
- package/src/commands/auth.d.ts +21 -0
- package/src/commands/auth.js +165 -0
- package/src/commands/diagnose.d.ts +69 -0
- package/src/commands/diagnose.js +848 -0
- package/src/commands/doctor.d.ts +27 -0
- package/src/commands/doctor.js +588 -0
- package/src/commands/index.d.ts +25 -0
- package/src/commands/index.js +219 -0
- package/src/commands/locale.d.ts +7 -0
- package/src/commands/locale.js +8 -0
- package/src/core/accounts.d.ts +51 -0
- package/src/core/accounts.js +219 -0
- package/src/core/agent-config.d.ts +100 -0
- package/src/core/agent-config.js +148 -0
- package/src/core/api-error.d.ts +48 -0
- package/src/core/api-error.js +117 -0
- package/src/core/app-owner-fallback.d.ts +22 -0
- package/src/core/app-owner-fallback.js +39 -0
- package/src/core/app-scope-checker.d.ts +87 -0
- package/src/core/app-scope-checker.js +198 -0
- package/src/core/auth-errors.d.ts +144 -0
- package/src/core/auth-errors.js +160 -0
- package/src/core/card-action-operator.d.ts +33 -0
- package/src/core/card-action-operator.js +30 -0
- package/src/core/chat-info-cache.d.ts +67 -0
- package/src/core/chat-info-cache.js +165 -0
- package/src/core/comment-target.d.ts +65 -0
- package/src/core/comment-target.js +100 -0
- package/src/core/config-schema.d.ts +490 -0
- package/src/core/config-schema.js +223 -0
- package/src/core/device-flow.d.ts +77 -0
- package/src/core/device-flow.js +217 -0
- package/src/core/domains.d.ts +18 -0
- package/src/core/domains.js +34 -0
- package/src/core/feishu-fetch.d.ts +18 -0
- package/src/core/feishu-fetch.js +28 -0
- package/src/core/footer-config.d.ts +24 -0
- package/src/core/footer-config.js +51 -0
- package/src/core/lark-client.d.ts +125 -0
- package/src/core/lark-client.js +468 -0
- package/src/core/lark-logger.d.ts +23 -0
- package/src/core/lark-logger.js +160 -0
- package/src/core/lark-ticket.d.ts +29 -0
- package/src/core/lark-ticket.js +40 -0
- package/src/core/message-unavailable.d.ts +53 -0
- package/src/core/message-unavailable.js +142 -0
- package/src/core/owner-policy.d.ts +32 -0
- package/src/core/owner-policy.js +55 -0
- package/src/core/permission-url.d.ts +22 -0
- package/src/core/permission-url.js +76 -0
- package/src/core/raw-request.d.ts +27 -0
- package/src/core/raw-request.js +90 -0
- package/src/core/runtime-store.d.ts +13 -0
- package/src/core/runtime-store.js +29 -0
- package/src/core/scope-manager.d.ts +168 -0
- package/src/core/scope-manager.js +224 -0
- package/src/core/sdk-compat.d.ts +20 -0
- package/src/core/sdk-compat.js +28 -0
- package/src/core/security-check.d.ts +72 -0
- package/src/core/security-check.js +184 -0
- package/src/core/shutdown-hooks.d.ts +22 -0
- package/src/core/shutdown-hooks.js +61 -0
- package/src/core/synthetic-target.d.ts +33 -0
- package/src/core/synthetic-target.js +40 -0
- package/src/core/targets.d.ts +60 -0
- package/src/core/targets.js +173 -0
- package/src/core/token-store.d.ts +54 -0
- package/src/core/token-store.js +326 -0
- package/src/core/tool-client.d.ts +176 -0
- package/src/core/tool-client.js +426 -0
- package/src/core/tool-scopes.d.ts +156 -0
- package/src/core/tool-scopes.js +344 -0
- package/src/core/tools-config.d.ts +55 -0
- package/src/core/tools-config.js +143 -0
- package/src/core/types.d.ts +91 -0
- package/src/core/types.js +12 -0
- package/src/core/uat-client.d.ts +46 -0
- package/src/core/uat-client.js +192 -0
- package/src/core/version.d.ts +31 -0
- package/src/core/version.js +69 -0
- package/src/messaging/converters/audio.d.ts +8 -0
- package/src/messaging/converters/audio.js +24 -0
- package/src/messaging/converters/calendar.d.ts +13 -0
- package/src/messaging/converters/calendar.js +56 -0
- package/src/messaging/converters/content-converter-helpers.d.ts +30 -0
- package/src/messaging/converters/content-converter-helpers.js +82 -0
- package/src/messaging/converters/content-converter.d.ts +24 -0
- package/src/messaging/converters/content-converter.js +40 -0
- package/src/messaging/converters/file.d.ts +8 -0
- package/src/messaging/converters/file.js +24 -0
- package/src/messaging/converters/folder.d.ts +8 -0
- package/src/messaging/converters/folder.js +24 -0
- package/src/messaging/converters/hongbao.d.ts +8 -0
- package/src/messaging/converters/hongbao.js +20 -0
- package/src/messaging/converters/image.d.ts +8 -0
- package/src/messaging/converters/image.js +22 -0
- package/src/messaging/converters/index.d.ts +8 -0
- package/src/messaging/converters/index.js +53 -0
- package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
- package/src/messaging/converters/interactive/card-converter.js +1177 -0
- package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
- package/src/messaging/converters/interactive/card-utils.js +47 -0
- package/src/messaging/converters/interactive/index.d.ts +8 -0
- package/src/messaging/converters/interactive/index.js +25 -0
- package/src/messaging/converters/interactive/legacy.d.ts +11 -0
- package/src/messaging/converters/interactive/legacy.js +60 -0
- package/src/messaging/converters/interactive/types.d.ts +23 -0
- package/src/messaging/converters/interactive/types.js +27 -0
- package/src/messaging/converters/location.d.ts +8 -0
- package/src/messaging/converters/location.js +23 -0
- package/src/messaging/converters/merge-forward.d.ts +32 -0
- package/src/messaging/converters/merge-forward.js +235 -0
- package/src/messaging/converters/post.d.ts +11 -0
- package/src/messaging/converters/post.js +235 -0
- package/src/messaging/converters/share.d.ts +9 -0
- package/src/messaging/converters/share.js +28 -0
- package/src/messaging/converters/sticker.d.ts +8 -0
- package/src/messaging/converters/sticker.js +22 -0
- package/src/messaging/converters/system.d.ts +12 -0
- package/src/messaging/converters/system.js +36 -0
- package/src/messaging/converters/text.d.ts +8 -0
- package/src/messaging/converters/text.js +18 -0
- package/src/messaging/converters/todo.d.ts +8 -0
- package/src/messaging/converters/todo.js +45 -0
- package/src/messaging/converters/types.d.ts +114 -0
- package/src/messaging/converters/types.js +8 -0
- package/src/messaging/converters/unknown.d.ts +8 -0
- package/src/messaging/converters/unknown.js +20 -0
- package/src/messaging/converters/utils.d.ts +22 -0
- package/src/messaging/converters/utils.js +57 -0
- package/src/messaging/converters/video-chat.d.ts +8 -0
- package/src/messaging/converters/video-chat.js +31 -0
- package/src/messaging/converters/video.d.ts +8 -0
- package/src/messaging/converters/video.js +35 -0
- package/src/messaging/converters/vote.d.ts +8 -0
- package/src/messaging/converters/vote.js +28 -0
- package/src/messaging/inbound/bot-content.d.ts +84 -0
- package/src/messaging/inbound/bot-content.js +117 -0
- package/src/messaging/inbound/bot-loop-guard.d.ts +48 -0
- package/src/messaging/inbound/bot-loop-guard.js +89 -0
- package/src/messaging/inbound/comment-context.d.ts +82 -0
- package/src/messaging/inbound/comment-context.js +353 -0
- package/src/messaging/inbound/comment-handler.d.ts +30 -0
- package/src/messaging/inbound/comment-handler.js +269 -0
- package/src/messaging/inbound/dedup.d.ts +59 -0
- package/src/messaging/inbound/dedup.js +121 -0
- package/src/messaging/inbound/dispatch-builders.d.ts +104 -0
- package/src/messaging/inbound/dispatch-builders.js +241 -0
- package/src/messaging/inbound/dispatch-commands.d.ts +22 -0
- package/src/messaging/inbound/dispatch-commands.js +131 -0
- package/src/messaging/inbound/dispatch-context.d.ts +67 -0
- package/src/messaging/inbound/dispatch-context.js +153 -0
- package/src/messaging/inbound/dispatch.d.ts +50 -0
- package/src/messaging/inbound/dispatch.js +477 -0
- package/src/messaging/inbound/enrich.d.ts +102 -0
- package/src/messaging/inbound/enrich.js +236 -0
- package/src/messaging/inbound/gate-effects.d.ts +23 -0
- package/src/messaging/inbound/gate-effects.js +46 -0
- package/src/messaging/inbound/gate.d.ts +91 -0
- package/src/messaging/inbound/gate.js +342 -0
- package/src/messaging/inbound/handler-registry.d.ts +25 -0
- package/src/messaging/inbound/handler-registry.js +23 -0
- package/src/messaging/inbound/handler.d.ts +37 -0
- package/src/messaging/inbound/handler.js +286 -0
- package/src/messaging/inbound/media-resolver.d.ts +32 -0
- package/src/messaging/inbound/media-resolver.js +91 -0
- package/src/messaging/inbound/mention-registry.d.ts +59 -0
- package/src/messaging/inbound/mention-registry.js +115 -0
- package/src/messaging/inbound/mention.d.ts +48 -0
- package/src/messaging/inbound/mention.js +102 -0
- package/src/messaging/inbound/parse-io.d.ts +50 -0
- package/src/messaging/inbound/parse-io.js +86 -0
- package/src/messaging/inbound/parse.d.ts +28 -0
- package/src/messaging/inbound/parse.js +128 -0
- package/src/messaging/inbound/permission.d.ts +17 -0
- package/src/messaging/inbound/permission.js +44 -0
- package/src/messaging/inbound/policy.d.ts +95 -0
- package/src/messaging/inbound/policy.js +168 -0
- package/src/messaging/inbound/reaction-handler.d.ts +62 -0
- package/src/messaging/inbound/reaction-handler.js +259 -0
- package/src/messaging/inbound/sentinel-store.d.ts +37 -0
- package/src/messaging/inbound/sentinel-store.js +98 -0
- package/src/messaging/inbound/synthetic-message.d.ts +26 -0
- package/src/messaging/inbound/synthetic-message.js +59 -0
- package/src/messaging/inbound/user-name-cache-store.d.ts +62 -0
- package/src/messaging/inbound/user-name-cache-store.js +236 -0
- package/src/messaging/inbound/user-name-cache.d.ts +78 -0
- package/src/messaging/inbound/user-name-cache.js +306 -0
- package/src/messaging/inbound/vc-meeting-invited-handler.d.ts +20 -0
- package/src/messaging/inbound/vc-meeting-invited-handler.js +236 -0
- package/src/messaging/inbound/vc-sender.d.ts +41 -0
- package/src/messaging/inbound/vc-sender.js +53 -0
- package/src/messaging/outbound/actions.d.ts +16 -0
- package/src/messaging/outbound/actions.js +329 -0
- package/src/messaging/outbound/bot-peer-context.d.ts +42 -0
- package/src/messaging/outbound/bot-peer-context.js +40 -0
- package/src/messaging/outbound/chat-manage.d.ts +64 -0
- package/src/messaging/outbound/chat-manage.js +117 -0
- package/src/messaging/outbound/deliver.d.ts +186 -0
- package/src/messaging/outbound/deliver.js +410 -0
- package/src/messaging/outbound/fetch.d.ts +12 -0
- package/src/messaging/outbound/fetch.js +17 -0
- package/src/messaging/outbound/forward.d.ts +26 -0
- package/src/messaging/outbound/forward.js +51 -0
- package/src/messaging/outbound/media-url-utils.d.ts +29 -0
- package/src/messaging/outbound/media-url-utils.js +172 -0
- package/src/messaging/outbound/media.d.ts +260 -0
- package/src/messaging/outbound/media.js +865 -0
- package/src/messaging/outbound/normalize-mentions.d.ts +50 -0
- package/src/messaging/outbound/normalize-mentions.js +166 -0
- package/src/messaging/outbound/outbound-mention.d.ts +41 -0
- package/src/messaging/outbound/outbound-mention.js +112 -0
- package/src/messaging/outbound/outbound.d.ts +79 -0
- package/src/messaging/outbound/outbound.js +195 -0
- package/src/messaging/outbound/reactions.d.ts +124 -0
- package/src/messaging/outbound/reactions.js +384 -0
- package/src/messaging/outbound/send.d.ts +153 -0
- package/src/messaging/outbound/send.js +417 -0
- package/src/messaging/outbound/typing.d.ts +60 -0
- package/src/messaging/outbound/typing.js +139 -0
- package/src/messaging/shared/message-lookup.d.ts +54 -0
- package/src/messaging/shared/message-lookup.js +120 -0
- package/src/messaging/types.d.ts +316 -0
- package/src/messaging/types.js +11 -0
- package/src/tools/ask-user-question.d.ts +32 -0
- package/src/tools/ask-user-question.js +922 -0
- package/src/tools/auto-auth.d.ts +57 -0
- package/src/tools/auto-auth.js +904 -0
- package/src/tools/helpers.d.ts +264 -0
- package/src/tools/helpers.js +389 -0
- package/src/tools/mcp/doc/create.d.ts +12 -0
- package/src/tools/mcp/doc/create.js +47 -0
- package/src/tools/mcp/doc/fetch.d.ts +12 -0
- package/src/tools/mcp/doc/fetch.js +39 -0
- package/src/tools/mcp/doc/index.d.ts +12 -0
- package/src/tools/mcp/doc/index.js +50 -0
- package/src/tools/mcp/doc/update.d.ts +12 -0
- package/src/tools/mcp/doc/update.js +64 -0
- package/src/tools/mcp/shared.d.ts +59 -0
- package/src/tools/mcp/shared.js +239 -0
- package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
- package/src/tools/oapi/bitable/app-table-field.js +225 -0
- package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
- package/src/tools/oapi/bitable/app-table-record.js +439 -0
- package/src/tools/oapi/bitable/app-table-view.d.ts +16 -0
- package/src/tools/oapi/bitable/app-table-view.js +172 -0
- package/src/tools/oapi/bitable/app-table.d.ts +17 -0
- package/src/tools/oapi/bitable/app-table.js +195 -0
- package/src/tools/oapi/bitable/app.d.ts +18 -0
- package/src/tools/oapi/bitable/app.js +189 -0
- package/src/tools/oapi/bitable/index.d.ts +9 -0
- package/src/tools/oapi/bitable/index.js +17 -0
- package/src/tools/oapi/calendar/calendar.d.ts +15 -0
- package/src/tools/oapi/calendar/calendar.js +125 -0
- package/src/tools/oapi/calendar/event-attendee.d.ts +14 -0
- package/src/tools/oapi/calendar/event-attendee.js +161 -0
- package/src/tools/oapi/calendar/event.d.ts +16 -0
- package/src/tools/oapi/calendar/event.js +712 -0
- package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
- package/src/tools/oapi/calendar/freebusy.js +114 -0
- package/src/tools/oapi/calendar/index.d.ts +8 -0
- package/src/tools/oapi/calendar/index.js +15 -0
- package/src/tools/oapi/chat/chat.d.ts +16 -0
- package/src/tools/oapi/chat/chat.js +127 -0
- package/src/tools/oapi/chat/index.d.ts +10 -0
- package/src/tools/oapi/chat/index.js +23 -0
- package/src/tools/oapi/chat/members.d.ts +11 -0
- package/src/tools/oapi/chat/members.js +84 -0
- package/src/tools/oapi/common/get-user.d.ts +12 -0
- package/src/tools/oapi/common/get-user.js +109 -0
- package/src/tools/oapi/common/index.d.ts +6 -0
- package/src/tools/oapi/common/index.js +11 -0
- package/src/tools/oapi/common/search-user.d.ts +11 -0
- package/src/tools/oapi/common/search-user.js +76 -0
- package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
- package/src/tools/oapi/drive/doc-comments.js +351 -0
- package/src/tools/oapi/drive/doc-media.d.ts +19 -0
- package/src/tools/oapi/drive/doc-media.js +371 -0
- package/src/tools/oapi/drive/file.d.ts +19 -0
- package/src/tools/oapi/drive/file.js +519 -0
- package/src/tools/oapi/drive/index.d.ts +12 -0
- package/src/tools/oapi/drive/index.js +45 -0
- package/src/tools/oapi/helpers.d.ts +183 -0
- package/src/tools/oapi/helpers.js +385 -0
- package/src/tools/oapi/im/format-messages.d.ts +50 -0
- package/src/tools/oapi/im/format-messages.js +169 -0
- package/src/tools/oapi/im/index.d.ts +10 -0
- package/src/tools/oapi/im/index.js +25 -0
- package/src/tools/oapi/im/message-read.d.ts +13 -0
- package/src/tools/oapi/im/message-read.js +419 -0
- package/src/tools/oapi/im/message.d.ts +16 -0
- package/src/tools/oapi/im/message.js +152 -0
- package/src/tools/oapi/im/resource.d.ts +13 -0
- package/src/tools/oapi/im/resource.js +186 -0
- package/src/tools/oapi/im/time-utils.d.ts +46 -0
- package/src/tools/oapi/im/time-utils.js +212 -0
- package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
- package/src/tools/oapi/im/user-name-uat.js +143 -0
- package/src/tools/oapi/index.d.ts +11 -0
- package/src/tools/oapi/index.js +64 -0
- package/src/tools/oapi/sdk-types.d.ts +96 -0
- package/src/tools/oapi/sdk-types.js +13 -0
- package/src/tools/oapi/search/doc-search.d.ts +13 -0
- package/src/tools/oapi/search/doc-search.js +194 -0
- package/src/tools/oapi/search/index.d.ts +12 -0
- package/src/tools/oapi/search/index.js +37 -0
- package/src/tools/oapi/sheets/index.d.ts +12 -0
- package/src/tools/oapi/sheets/index.js +35 -0
- package/src/tools/oapi/sheets/sheet.d.ts +16 -0
- package/src/tools/oapi/sheets/sheet.js +688 -0
- package/src/tools/oapi/task/attachment.d.ts +18 -0
- package/src/tools/oapi/task/attachment.js +107 -0
- package/src/tools/oapi/task/comment.d.ts +15 -0
- package/src/tools/oapi/task/comment.js +149 -0
- package/src/tools/oapi/task/index.d.ts +11 -0
- package/src/tools/oapi/task/index.js +21 -0
- package/src/tools/oapi/task/section.d.ts +17 -0
- package/src/tools/oapi/task/section.js +293 -0
- package/src/tools/oapi/task/subtask.d.ts +14 -0
- package/src/tools/oapi/task/subtask.js +171 -0
- package/src/tools/oapi/task/task.d.ts +19 -0
- package/src/tools/oapi/task/task.js +512 -0
- package/src/tools/oapi/task/task_agent.d.ts +14 -0
- package/src/tools/oapi/task/task_agent.js +108 -0
- package/src/tools/oapi/task/tasklist.d.ts +19 -0
- package/src/tools/oapi/task/tasklist.js +276 -0
- package/src/tools/oapi/wiki/index.d.ts +12 -0
- package/src/tools/oapi/wiki/index.js +42 -0
- package/src/tools/oapi/wiki/space-node.d.ts +17 -0
- package/src/tools/oapi/wiki/space-node.js +233 -0
- package/src/tools/oapi/wiki/space.d.ts +15 -0
- package/src/tools/oapi/wiki/space.js +133 -0
- package/src/tools/oauth-batch-auth.d.ts +11 -0
- package/src/tools/oauth-batch-auth.js +150 -0
- package/src/tools/oauth-cards.d.ts +39 -0
- package/src/tools/oauth-cards.js +324 -0
- package/src/tools/oauth.d.ts +47 -0
- package/src/tools/oauth.js +592 -0
- package/src/tools/onboarding-auth.d.ts +27 -0
- package/src/tools/onboarding-auth.js +133 -0
- package/src/tools/tat/im/index.d.ts +15 -0
- package/src/tools/tat/im/index.js +22 -0
- package/src/tools/tat/im/resource.d.ts +15 -0
- package/src/tools/tat/im/resource.js +192 -0
- package/tsdown.config.d.ts +2 -0
- package/tsdown.config.js +25 -0
- package/vitest.config.d.ts +2 -0
- package/vitest.config.js +12 -0
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* Diagnostic module for the Lark/Feishu plugin.
|
|
7
|
+
*
|
|
8
|
+
* Collects environment info, account configuration, API connectivity,
|
|
9
|
+
* app permissions, tool registration state, and recent error logs into
|
|
10
|
+
* a structured report that users can share with developers for
|
|
11
|
+
* remote troubleshooting.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.runDiagnosis = runDiagnosis;
|
|
48
|
+
exports.formatDiagReportText = formatDiagReportText;
|
|
49
|
+
exports.traceByMessageId = traceByMessageId;
|
|
50
|
+
exports.formatTraceOutput = formatTraceOutput;
|
|
51
|
+
exports.analyzeTrace = analyzeTrace;
|
|
52
|
+
exports.formatDiagReportCli = formatDiagReportCli;
|
|
53
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
54
|
+
const path = __importStar(require("node:path"));
|
|
55
|
+
const os = __importStar(require("node:os"));
|
|
56
|
+
const probe_1 = require("../channel/probe.js");
|
|
57
|
+
const accounts_1 = require("../core/accounts.js");
|
|
58
|
+
const lark_client_1 = require("../core/lark-client.js");
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the global config for cross-account operations.
|
|
61
|
+
* See doctor.ts for rationale.
|
|
62
|
+
*/
|
|
63
|
+
function resolveGlobalConfig(config) {
|
|
64
|
+
return lark_client_1.LarkClient.globalConfig ?? config;
|
|
65
|
+
}
|
|
66
|
+
const api_error_1 = require("../core/api-error.js");
|
|
67
|
+
const tools_config_1 = require("../core/tools-config.js");
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Constants
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
const PLUGIN_VERSION = '2026.2.10';
|
|
72
|
+
const LOG_READ_BYTES = 256 * 1024; // read last 256KB of log
|
|
73
|
+
const MAX_ERROR_LINES = 20;
|
|
74
|
+
/** Matches a timestamped log line: 2026-02-13T09:23:35.038Z [level]: ... */
|
|
75
|
+
const TIMESTAMPED_LINE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
76
|
+
const ERROR_LEVEL_RE = /\[error\]|\[warn\]/i;
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Helpers
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
function maskSecret(secret) {
|
|
81
|
+
if (!secret)
|
|
82
|
+
return '(未设置)';
|
|
83
|
+
if (secret.length <= 4)
|
|
84
|
+
return '****';
|
|
85
|
+
return secret.slice(0, 4) + '****';
|
|
86
|
+
}
|
|
87
|
+
async function extractRecentErrors(logPath) {
|
|
88
|
+
try {
|
|
89
|
+
await fs.access(logPath);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const stat = await fs.stat(logPath);
|
|
96
|
+
const readSize = Math.min(stat.size, LOG_READ_BYTES);
|
|
97
|
+
const fd = await fs.open(logPath, 'r');
|
|
98
|
+
try {
|
|
99
|
+
const buffer = Buffer.alloc(readSize);
|
|
100
|
+
await fd.read(buffer, 0, readSize, Math.max(0, stat.size - readSize));
|
|
101
|
+
const content = buffer.toString('utf-8');
|
|
102
|
+
const lines = content.split('\n').filter(Boolean);
|
|
103
|
+
// Only pick timestamped log entries at error/warn level,
|
|
104
|
+
// ignoring stack trace fragments and other noise.
|
|
105
|
+
const errorLines = lines.filter((line) => TIMESTAMPED_LINE_RE.test(line) && ERROR_LEVEL_RE.test(line));
|
|
106
|
+
return errorLines.slice(-MAX_ERROR_LINES);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await fd.close();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function checkAppScopes(client) {
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
+
const res = await client.application.scope.list({});
|
|
119
|
+
(0, api_error_1.assertLarkOk)(res);
|
|
120
|
+
const scopes = res.data?.scopes ?? [];
|
|
121
|
+
const granted = scopes.filter((s) => s.grant_status === 1);
|
|
122
|
+
const pending = scopes.filter((s) => s.grant_status !== 1);
|
|
123
|
+
return {
|
|
124
|
+
granted: granted.length,
|
|
125
|
+
pending: pending.length,
|
|
126
|
+
summary: `${granted.length} 已授权, ${pending.length} 待授权`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function detectRegisteredTools(config) {
|
|
130
|
+
const accounts = (0, accounts_1.getEnabledLarkAccounts)(config);
|
|
131
|
+
if (accounts.length === 0)
|
|
132
|
+
return [];
|
|
133
|
+
const toolsCfg = (0, tools_config_1.resolveAnyEnabledToolsConfig)(accounts);
|
|
134
|
+
const tools = [];
|
|
135
|
+
if (toolsCfg.doc)
|
|
136
|
+
tools.push('feishu_doc');
|
|
137
|
+
if (toolsCfg.scopes)
|
|
138
|
+
tools.push('feishu_app_scopes');
|
|
139
|
+
if (toolsCfg.wiki)
|
|
140
|
+
tools.push('feishu_wiki');
|
|
141
|
+
if (toolsCfg.drive)
|
|
142
|
+
tools.push('feishu_drive');
|
|
143
|
+
if (toolsCfg.perm)
|
|
144
|
+
tools.push('feishu_perm');
|
|
145
|
+
tools.push('feishu_bitable_get_meta', 'feishu_bitable_list_fields', 'feishu_bitable_list_records', 'feishu_bitable_get_record', 'feishu_bitable_create_record', 'feishu_bitable_update_record');
|
|
146
|
+
tools.push('feishu_task');
|
|
147
|
+
tools.push('feishu_calendar');
|
|
148
|
+
return tools;
|
|
149
|
+
}
|
|
150
|
+
async function diagnoseAccount(account) {
|
|
151
|
+
const checks = [];
|
|
152
|
+
const result = {
|
|
153
|
+
accountId: account.accountId,
|
|
154
|
+
name: account.name,
|
|
155
|
+
enabled: account.enabled,
|
|
156
|
+
configured: account.configured,
|
|
157
|
+
appId: account.appId ?? '(未设置)',
|
|
158
|
+
brand: account.brand,
|
|
159
|
+
checks,
|
|
160
|
+
};
|
|
161
|
+
// A1: Credentials
|
|
162
|
+
checks.push({
|
|
163
|
+
name: '凭证完整性',
|
|
164
|
+
status: account.configured ? 'pass' : 'fail',
|
|
165
|
+
message: account.configured
|
|
166
|
+
? `appId: ${account.appId}, appSecret: ${maskSecret(account.appSecret)}`
|
|
167
|
+
: '缺少 appId 或 appSecret',
|
|
168
|
+
});
|
|
169
|
+
// A2: Enabled
|
|
170
|
+
checks.push({
|
|
171
|
+
name: '账户启用',
|
|
172
|
+
status: account.enabled ? 'pass' : 'warn',
|
|
173
|
+
message: account.enabled ? '已启用' : '已禁用',
|
|
174
|
+
});
|
|
175
|
+
if (!account.configured || !account.appId || !account.appSecret) {
|
|
176
|
+
checks.push({
|
|
177
|
+
name: 'API 连通性',
|
|
178
|
+
status: 'skip',
|
|
179
|
+
message: '凭证未配置,跳过',
|
|
180
|
+
});
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
// A3: API connectivity via probe
|
|
184
|
+
try {
|
|
185
|
+
const probeResult = await (0, probe_1.probeFeishu)({
|
|
186
|
+
accountId: account.accountId,
|
|
187
|
+
appId: account.appId,
|
|
188
|
+
appSecret: account.appSecret,
|
|
189
|
+
brand: account.brand,
|
|
190
|
+
});
|
|
191
|
+
checks.push({
|
|
192
|
+
name: 'API 连通性',
|
|
193
|
+
status: probeResult.ok ? 'pass' : 'fail',
|
|
194
|
+
message: probeResult.ok ? `连接成功` : `连接失败: ${probeResult.error}`,
|
|
195
|
+
});
|
|
196
|
+
// A4: Bot info
|
|
197
|
+
if (probeResult.ok) {
|
|
198
|
+
checks.push({
|
|
199
|
+
name: 'Bot 信息',
|
|
200
|
+
status: probeResult.botName ? 'pass' : 'warn',
|
|
201
|
+
message: probeResult.botName ? `${probeResult.botName} (${probeResult.botOpenId})` : '未获取到 Bot 名称',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
checks.push({
|
|
207
|
+
name: 'API 连通性',
|
|
208
|
+
status: 'fail',
|
|
209
|
+
message: `探测异常: ${err instanceof Error ? err.message : String(err)}`,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// A5: App scopes
|
|
213
|
+
try {
|
|
214
|
+
const client = lark_client_1.LarkClient.fromAccount(account).sdk;
|
|
215
|
+
const scopesResult = await checkAppScopes(client);
|
|
216
|
+
checks.push({
|
|
217
|
+
name: '应用权限',
|
|
218
|
+
status: scopesResult.pending > 0 ? 'warn' : 'pass',
|
|
219
|
+
message: scopesResult.summary,
|
|
220
|
+
details: scopesResult.pending > 0 ? '存在未授权的权限,可能影响部分功能' : undefined,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
checks.push({
|
|
225
|
+
name: '应用权限',
|
|
226
|
+
status: 'warn',
|
|
227
|
+
message: `权限检查失败: ${(0, api_error_1.formatLarkError)(err)}`,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// A6: Brand
|
|
231
|
+
checks.push({
|
|
232
|
+
name: '品牌配置',
|
|
233
|
+
status: 'pass',
|
|
234
|
+
message: `brand: ${account.brand}`,
|
|
235
|
+
});
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Core
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
async function runDiagnosis(params) {
|
|
242
|
+
const { config } = params;
|
|
243
|
+
// Use the global config to enumerate all accounts — the passed-in
|
|
244
|
+
// config may be account-scoped (accounts map stripped).
|
|
245
|
+
const globalCfg = resolveGlobalConfig(config);
|
|
246
|
+
const globalChecks = [];
|
|
247
|
+
// -- Environment --
|
|
248
|
+
const nodeVer = parseInt(process.version.slice(1), 10);
|
|
249
|
+
globalChecks.push({
|
|
250
|
+
name: 'Node.js 版本',
|
|
251
|
+
status: nodeVer >= 18 ? 'pass' : 'warn',
|
|
252
|
+
message: process.version,
|
|
253
|
+
details: nodeVer < 18 ? '建议升级到 Node.js 18+' : undefined,
|
|
254
|
+
});
|
|
255
|
+
// -- Account count --
|
|
256
|
+
const accountIds = (0, accounts_1.getLarkAccountIds)(globalCfg);
|
|
257
|
+
globalChecks.push({
|
|
258
|
+
name: '飞书账户数量',
|
|
259
|
+
status: accountIds.length > 0 ? 'pass' : 'fail',
|
|
260
|
+
message: `${accountIds.length} 个账户`,
|
|
261
|
+
});
|
|
262
|
+
// -- Log file --
|
|
263
|
+
const logPath = path.join(os.homedir(), '.openclaw', 'logs', 'gateway.log');
|
|
264
|
+
let logExists = false;
|
|
265
|
+
try {
|
|
266
|
+
await fs.access(logPath);
|
|
267
|
+
logExists = true;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// noop
|
|
271
|
+
}
|
|
272
|
+
globalChecks.push({
|
|
273
|
+
name: '日志文件',
|
|
274
|
+
status: logExists ? 'pass' : 'warn',
|
|
275
|
+
message: logExists ? logPath : `未找到: ${logPath}`,
|
|
276
|
+
});
|
|
277
|
+
// -- Per-account diagnosis (sequential to avoid rate limits) --
|
|
278
|
+
const accountResults = [];
|
|
279
|
+
for (const id of accountIds) {
|
|
280
|
+
const account = (0, accounts_1.getLarkAccount)(globalCfg, id);
|
|
281
|
+
const result = await diagnoseAccount(account);
|
|
282
|
+
accountResults.push(result);
|
|
283
|
+
}
|
|
284
|
+
// -- Tools --
|
|
285
|
+
const tools = detectRegisteredTools(globalCfg);
|
|
286
|
+
// -- Recent errors --
|
|
287
|
+
const recentErrors = await extractRecentErrors(logPath);
|
|
288
|
+
globalChecks.push({
|
|
289
|
+
name: '最近错误日志',
|
|
290
|
+
status: recentErrors.length > 0 ? 'warn' : 'pass',
|
|
291
|
+
message: recentErrors.length > 0 ? `发现 ${recentErrors.length} 条错误` : '无最近错误',
|
|
292
|
+
});
|
|
293
|
+
// -- Overall status --
|
|
294
|
+
const allChecks = [...globalChecks, ...accountResults.flatMap((a) => a.checks)];
|
|
295
|
+
const hasFail = allChecks.some((c) => c.status === 'fail');
|
|
296
|
+
const hasWarn = allChecks.some((c) => c.status === 'warn');
|
|
297
|
+
return {
|
|
298
|
+
timestamp: new Date().toISOString(),
|
|
299
|
+
environment: {
|
|
300
|
+
nodeVersion: process.version,
|
|
301
|
+
platform: process.platform,
|
|
302
|
+
arch: process.arch,
|
|
303
|
+
pluginVersion: PLUGIN_VERSION,
|
|
304
|
+
},
|
|
305
|
+
accounts: accountResults,
|
|
306
|
+
toolsRegistered: tools,
|
|
307
|
+
recentErrors,
|
|
308
|
+
overallStatus: hasFail ? 'unhealthy' : hasWarn ? 'degraded' : 'healthy',
|
|
309
|
+
checks: globalChecks,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// Formatting — plain text (chat command)
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
const STATUS_LABEL = {
|
|
316
|
+
pass: '[PASS]',
|
|
317
|
+
warn: '[WARN]',
|
|
318
|
+
fail: '[FAIL]',
|
|
319
|
+
skip: '[SKIP]',
|
|
320
|
+
};
|
|
321
|
+
function formatCheck(c) {
|
|
322
|
+
let line = ` ${STATUS_LABEL[c.status]} ${c.name}: ${c.message}`;
|
|
323
|
+
if (c.details) {
|
|
324
|
+
line += `\n ${c.details}`;
|
|
325
|
+
}
|
|
326
|
+
return line;
|
|
327
|
+
}
|
|
328
|
+
function formatDiagReportText(report) {
|
|
329
|
+
const lines = [];
|
|
330
|
+
const sep = '====================================';
|
|
331
|
+
lines.push(sep);
|
|
332
|
+
lines.push(' 飞书插件诊断报告');
|
|
333
|
+
lines.push(` ${report.timestamp}`);
|
|
334
|
+
lines.push(sep);
|
|
335
|
+
lines.push('');
|
|
336
|
+
// Environment
|
|
337
|
+
lines.push('【环境信息】');
|
|
338
|
+
lines.push(` Node.js: ${report.environment.nodeVersion}`);
|
|
339
|
+
lines.push(` 插件版本: ${report.environment.pluginVersion}`);
|
|
340
|
+
lines.push(` 系统: ${report.environment.platform} ${report.environment.arch}`);
|
|
341
|
+
lines.push('');
|
|
342
|
+
// Global checks
|
|
343
|
+
lines.push('【全局检查】');
|
|
344
|
+
for (const c of report.checks) {
|
|
345
|
+
lines.push(formatCheck(c));
|
|
346
|
+
}
|
|
347
|
+
lines.push('');
|
|
348
|
+
// Per-account
|
|
349
|
+
for (const acct of report.accounts) {
|
|
350
|
+
lines.push(`【账户: ${acct.accountId}】`);
|
|
351
|
+
if (acct.name)
|
|
352
|
+
lines.push(` 名称: ${acct.name}`);
|
|
353
|
+
lines.push(` App ID: ${acct.appId}`);
|
|
354
|
+
lines.push(` 品牌: ${acct.brand}`);
|
|
355
|
+
lines.push('');
|
|
356
|
+
for (const c of acct.checks) {
|
|
357
|
+
lines.push(formatCheck(c));
|
|
358
|
+
}
|
|
359
|
+
lines.push('');
|
|
360
|
+
}
|
|
361
|
+
// Tools
|
|
362
|
+
lines.push('【工具注册】');
|
|
363
|
+
if (report.toolsRegistered.length > 0) {
|
|
364
|
+
lines.push(` ${report.toolsRegistered.join(', ')}`);
|
|
365
|
+
lines.push(` 共 ${report.toolsRegistered.length} 个`);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
lines.push(' 无工具注册(未找到已配置的账户)');
|
|
369
|
+
}
|
|
370
|
+
lines.push('');
|
|
371
|
+
// Recent errors
|
|
372
|
+
if (report.recentErrors.length > 0) {
|
|
373
|
+
lines.push(`【最近错误】(${report.recentErrors.length} 条)`);
|
|
374
|
+
for (let i = 0; i < report.recentErrors.length; i++) {
|
|
375
|
+
lines.push(` ${i + 1}. ${report.recentErrors[i]}`);
|
|
376
|
+
}
|
|
377
|
+
lines.push('');
|
|
378
|
+
}
|
|
379
|
+
// Overall
|
|
380
|
+
const statusMap = {
|
|
381
|
+
healthy: 'HEALTHY',
|
|
382
|
+
degraded: 'DEGRADED (存在警告)',
|
|
383
|
+
unhealthy: 'UNHEALTHY (存在失败项)',
|
|
384
|
+
};
|
|
385
|
+
lines.push(sep);
|
|
386
|
+
lines.push(` 总体状态: ${statusMap[report.overallStatus]}`);
|
|
387
|
+
lines.push(sep);
|
|
388
|
+
return lines.join('\n');
|
|
389
|
+
}
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// Formatting — ANSI colored (CLI)
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
const ANSI = {
|
|
394
|
+
reset: '\x1b[0m',
|
|
395
|
+
bold: '\x1b[1m',
|
|
396
|
+
green: '\x1b[32m',
|
|
397
|
+
yellow: '\x1b[33m',
|
|
398
|
+
red: '\x1b[31m',
|
|
399
|
+
gray: '\x1b[90m',
|
|
400
|
+
};
|
|
401
|
+
const STATUS_LABEL_CLI = {
|
|
402
|
+
pass: `${ANSI.green}[PASS]${ANSI.reset}`,
|
|
403
|
+
warn: `${ANSI.yellow}[WARN]${ANSI.reset}`,
|
|
404
|
+
fail: `${ANSI.red}[FAIL]${ANSI.reset}`,
|
|
405
|
+
skip: `${ANSI.gray}[SKIP]${ANSI.reset}`,
|
|
406
|
+
};
|
|
407
|
+
function formatCheckCli(c) {
|
|
408
|
+
let line = ` ${STATUS_LABEL_CLI[c.status]} ${c.name}: ${c.message}`;
|
|
409
|
+
if (c.details) {
|
|
410
|
+
line += `\n ${ANSI.gray}${c.details}${ANSI.reset}`;
|
|
411
|
+
}
|
|
412
|
+
return line;
|
|
413
|
+
}
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
// Trace by message_id
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
/**
|
|
418
|
+
* Extract all log lines tagged with a specific message_id from gateway.log.
|
|
419
|
+
*
|
|
420
|
+
* Scans the last 1MB of the log file for lines containing `[msg:{messageId}]`.
|
|
421
|
+
* Returns matching lines in chronological order.
|
|
422
|
+
*/
|
|
423
|
+
async function traceByMessageId(messageId) {
|
|
424
|
+
const logPath = path.join(os.homedir(), '.openclaw', 'logs', 'gateway.log');
|
|
425
|
+
try {
|
|
426
|
+
await fs.access(logPath);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
const TRACE_READ_BYTES = 1024 * 1024; // 1MB — more than extractRecentErrors
|
|
432
|
+
try {
|
|
433
|
+
const stat = await fs.stat(logPath);
|
|
434
|
+
const readSize = Math.min(stat.size, TRACE_READ_BYTES);
|
|
435
|
+
const fd = await fs.open(logPath, 'r');
|
|
436
|
+
try {
|
|
437
|
+
const buffer = Buffer.alloc(readSize);
|
|
438
|
+
await fd.read(buffer, 0, readSize, Math.max(0, stat.size - readSize));
|
|
439
|
+
const content = buffer.toString('utf-8');
|
|
440
|
+
const needle = `[msg:${messageId}]`;
|
|
441
|
+
return content.split('\n').filter((line) => line.includes(needle));
|
|
442
|
+
}
|
|
443
|
+
finally {
|
|
444
|
+
await fd.close();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Format trace output for CLI display.
|
|
453
|
+
*/
|
|
454
|
+
function formatTraceOutput(lines, messageId) {
|
|
455
|
+
const sep = '────────────────────────────────';
|
|
456
|
+
if (lines.length === 0) {
|
|
457
|
+
return [
|
|
458
|
+
sep,
|
|
459
|
+
` 未找到 ${messageId} 的追踪日志`,
|
|
460
|
+
'',
|
|
461
|
+
' 可能原因:',
|
|
462
|
+
' 1. 该消息尚未被处理',
|
|
463
|
+
' 2. 日志已被轮转',
|
|
464
|
+
' 3. 追踪功能未启用(需要更新插件版本)',
|
|
465
|
+
sep,
|
|
466
|
+
].join('\n');
|
|
467
|
+
}
|
|
468
|
+
const header = `追踪 ${messageId} 的处理链路 (${lines.length} 条日志):`;
|
|
469
|
+
const output = [header, sep];
|
|
470
|
+
for (const line of lines) {
|
|
471
|
+
output.push(line);
|
|
472
|
+
}
|
|
473
|
+
output.push(sep);
|
|
474
|
+
return output.join('\n');
|
|
475
|
+
}
|
|
476
|
+
function classifyEvent(body) {
|
|
477
|
+
if (body.startsWith('received from'))
|
|
478
|
+
return 'received';
|
|
479
|
+
if (body.startsWith('sender resolved'))
|
|
480
|
+
return 'sender_resolved';
|
|
481
|
+
if (body.startsWith('rejected:'))
|
|
482
|
+
return 'rejected';
|
|
483
|
+
if (body.startsWith('dispatching to agent'))
|
|
484
|
+
return 'dispatching';
|
|
485
|
+
if (body.startsWith('dispatch complete'))
|
|
486
|
+
return 'dispatch_complete';
|
|
487
|
+
if (body.startsWith('card entity created'))
|
|
488
|
+
return 'card_created';
|
|
489
|
+
if (body.startsWith('card message sent'))
|
|
490
|
+
return 'card_sent';
|
|
491
|
+
if (body.startsWith('cardkit cardElement.content:'))
|
|
492
|
+
return 'card_stream';
|
|
493
|
+
if (body.startsWith('card stream update failed'))
|
|
494
|
+
return 'card_stream_fail';
|
|
495
|
+
if (body.startsWith('cardkit card.settings:'))
|
|
496
|
+
return 'card_settings';
|
|
497
|
+
if (body.startsWith('cardkit card.update:'))
|
|
498
|
+
return 'card_update';
|
|
499
|
+
if (body.startsWith('card creation failed'))
|
|
500
|
+
return 'card_fallback';
|
|
501
|
+
if (body.startsWith('reply completed'))
|
|
502
|
+
return 'reply_completed';
|
|
503
|
+
if (body.startsWith('reply error'))
|
|
504
|
+
return 'reply_error';
|
|
505
|
+
if (body.startsWith('tool call:'))
|
|
506
|
+
return 'tool_call';
|
|
507
|
+
if (body.startsWith('tool done:'))
|
|
508
|
+
return 'tool_done';
|
|
509
|
+
if (body.startsWith('tool fail:'))
|
|
510
|
+
return 'tool_fail';
|
|
511
|
+
return 'other';
|
|
512
|
+
}
|
|
513
|
+
const EVENT_LABEL = {
|
|
514
|
+
received: '消息接收',
|
|
515
|
+
sender_resolved: 'Sender 解析',
|
|
516
|
+
rejected: '消息拒绝',
|
|
517
|
+
dispatching: '分发到 Agent',
|
|
518
|
+
dispatch_complete: 'Agent 处理完成',
|
|
519
|
+
card_created: '卡片创建',
|
|
520
|
+
card_sent: '卡片消息发送',
|
|
521
|
+
card_stream: '流式更新',
|
|
522
|
+
card_stream_fail: '流式更新失败',
|
|
523
|
+
card_settings: '卡片设置',
|
|
524
|
+
card_update: '卡片最终更新',
|
|
525
|
+
card_fallback: '卡片降级',
|
|
526
|
+
reply_completed: '回复完成',
|
|
527
|
+
reply_error: '回复错误',
|
|
528
|
+
tool_call: '工具调用',
|
|
529
|
+
tool_done: '工具完成',
|
|
530
|
+
tool_fail: '工具失败',
|
|
531
|
+
};
|
|
532
|
+
/** Expected stages in a normal message processing flow. */
|
|
533
|
+
const EXPECTED_STAGES = [
|
|
534
|
+
{ kind: 'received', label: '消息接收 (received from)' },
|
|
535
|
+
{ kind: 'dispatching', label: '分发到 Agent (dispatching to agent)' },
|
|
536
|
+
{ kind: 'card_created', label: '卡片创建 (card entity created)' },
|
|
537
|
+
{ kind: 'card_sent', label: '卡片消息发送 (card message sent)' },
|
|
538
|
+
{ kind: 'card_stream', label: '流式输出 (cardElement.content)' },
|
|
539
|
+
{ kind: 'dispatch_complete', label: '处理完成 (dispatch complete)' },
|
|
540
|
+
{ kind: 'reply_completed', label: '回复收尾 (reply completed)' },
|
|
541
|
+
];
|
|
542
|
+
/** Time gap thresholds (ms) for performance warnings. */
|
|
543
|
+
const PERF_THRESHOLDS = [
|
|
544
|
+
{ from: 'received', to: 'dispatching', warnMs: 500, label: '消息接收 → 分发' },
|
|
545
|
+
{ from: 'dispatching', to: 'card_created', warnMs: 5000, label: '分发 → 卡片创建' },
|
|
546
|
+
{ from: 'card_created', to: 'card_stream', warnMs: 30000, label: '卡片创建 → 首次流式输出' },
|
|
547
|
+
];
|
|
548
|
+
function parseTraceLines(lines) {
|
|
549
|
+
const events = [];
|
|
550
|
+
// Match: 2026-02-13T12:42:04.682Z [feishu] feishu[...][msg:...]: <body>
|
|
551
|
+
const re = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s.*?\]:\s(.+)$/;
|
|
552
|
+
for (const line of lines) {
|
|
553
|
+
const m = line.match(re);
|
|
554
|
+
if (m) {
|
|
555
|
+
events.push({ timestamp: new Date(m[1]), raw: line, body: m[2] });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return events;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Analyze trace log lines and produce a structured CLI report.
|
|
562
|
+
*/
|
|
563
|
+
function analyzeTrace(lines, _messageId) {
|
|
564
|
+
const events = parseTraceLines(lines);
|
|
565
|
+
if (events.length === 0) {
|
|
566
|
+
return `无法解析日志行,请确认日志格式正确。`;
|
|
567
|
+
}
|
|
568
|
+
const out = [];
|
|
569
|
+
const sep = '────────────────────────────────';
|
|
570
|
+
const startTime = events[0].timestamp.getTime();
|
|
571
|
+
const totalMs = events[events.length - 1].timestamp.getTime() - startTime;
|
|
572
|
+
// ── Section 1: Timeline ──
|
|
573
|
+
out.push('');
|
|
574
|
+
out.push(`${ANSI.bold}【时间线】${ANSI.reset} (${events.length} 条日志,跨度 ${(totalMs / 1000).toFixed(1)}s)`);
|
|
575
|
+
out.push(sep);
|
|
576
|
+
let prevMs = startTime;
|
|
577
|
+
// Collapse consecutive card_stream events
|
|
578
|
+
let streamCount = 0;
|
|
579
|
+
let streamFirstSeq = '';
|
|
580
|
+
let streamLastSeq = '';
|
|
581
|
+
function flushStream() {
|
|
582
|
+
if (streamCount > 0) {
|
|
583
|
+
const label = streamCount === 1
|
|
584
|
+
? ` ${ANSI.gray}...${ANSI.reset} 流式更新 seq=${streamFirstSeq}`
|
|
585
|
+
: ` ${ANSI.gray}...${ANSI.reset} 流式更新 x${streamCount} (seq=${streamFirstSeq}~${streamLastSeq})`;
|
|
586
|
+
out.push(label);
|
|
587
|
+
streamCount = 0;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
for (const ev of events) {
|
|
591
|
+
const kind = classifyEvent(ev.body);
|
|
592
|
+
const deltaMs = ev.timestamp.getTime() - prevMs;
|
|
593
|
+
prevMs = ev.timestamp.getTime();
|
|
594
|
+
const offsetMs = ev.timestamp.getTime() - startTime;
|
|
595
|
+
const offsetStr = `+${offsetMs}ms`.padStart(10);
|
|
596
|
+
// Collapse card_stream
|
|
597
|
+
if (kind === 'card_stream') {
|
|
598
|
+
const seqMatch = ev.body.match(/seq=(\d+)/);
|
|
599
|
+
const seq = seqMatch ? seqMatch[1] : '?';
|
|
600
|
+
if (streamCount === 0)
|
|
601
|
+
streamFirstSeq = seq;
|
|
602
|
+
streamLastSeq = seq;
|
|
603
|
+
streamCount++;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
flushStream();
|
|
607
|
+
const label = EVENT_LABEL[kind] ?? kind;
|
|
608
|
+
const gapWarn = deltaMs > 5000 ? ` ${ANSI.yellow}⚠ ${(deltaMs / 1000).toFixed(1)}s${ANSI.reset}` : '';
|
|
609
|
+
// Marker for errors
|
|
610
|
+
let marker = ' ';
|
|
611
|
+
if (kind === 'rejected' ||
|
|
612
|
+
kind === 'reply_error' ||
|
|
613
|
+
kind === 'tool_fail' ||
|
|
614
|
+
kind === 'card_stream_fail' ||
|
|
615
|
+
kind === 'card_fallback') {
|
|
616
|
+
marker = `${ANSI.red}✘ ${ANSI.reset}`;
|
|
617
|
+
}
|
|
618
|
+
else if (kind === 'tool_call') {
|
|
619
|
+
marker = '→ ';
|
|
620
|
+
}
|
|
621
|
+
// Extract key detail from body
|
|
622
|
+
let detail = '';
|
|
623
|
+
if (kind === 'received') {
|
|
624
|
+
const m = ev.body.match(/from (\S+) in (\S+) \((\w+)\)/);
|
|
625
|
+
if (m)
|
|
626
|
+
detail = `sender=${m[1]}, chat=${m[2]} (${m[3]})`;
|
|
627
|
+
}
|
|
628
|
+
else if (kind === 'dispatching') {
|
|
629
|
+
const m = ev.body.match(/session=(\S+)\)/);
|
|
630
|
+
if (m)
|
|
631
|
+
detail = `session=${m[1]}`;
|
|
632
|
+
}
|
|
633
|
+
else if (kind === 'dispatch_complete') {
|
|
634
|
+
const m = ev.body.match(/replies=(\d+), elapsed=(\d+)ms/);
|
|
635
|
+
if (m)
|
|
636
|
+
detail = `replies=${m[1]}, elapsed=${m[2]}ms`;
|
|
637
|
+
}
|
|
638
|
+
else if (kind === 'tool_call') {
|
|
639
|
+
const m = ev.body.match(/tool call: (\S+)/);
|
|
640
|
+
if (m)
|
|
641
|
+
detail = m[1];
|
|
642
|
+
}
|
|
643
|
+
else if (kind === 'tool_fail') {
|
|
644
|
+
detail = ev.body.replace('tool fail: ', '');
|
|
645
|
+
}
|
|
646
|
+
else if (kind === 'card_created') {
|
|
647
|
+
const m = ev.body.match(/card_id=(\S+)\)/);
|
|
648
|
+
if (m)
|
|
649
|
+
detail = `card_id=${m[1]}`;
|
|
650
|
+
}
|
|
651
|
+
else if (kind === 'reply_completed') {
|
|
652
|
+
const m = ev.body.match(/elapsed=(\d+)ms/);
|
|
653
|
+
if (m)
|
|
654
|
+
detail = `elapsed=${m[1]}ms`;
|
|
655
|
+
}
|
|
656
|
+
else if (kind === 'rejected') {
|
|
657
|
+
detail = ev.body.replace('rejected: ', '');
|
|
658
|
+
}
|
|
659
|
+
out.push(`${ANSI.gray}[${offsetStr}]${ANSI.reset} ${marker}${label}${detail ? ` — ${detail}` : ''}${gapWarn}`);
|
|
660
|
+
}
|
|
661
|
+
flushStream();
|
|
662
|
+
out.push('');
|
|
663
|
+
// ── Section 2: Anomaly detection ──
|
|
664
|
+
const issues = [];
|
|
665
|
+
const kindSet = new Set(events.map((e) => classifyEvent(e.body)));
|
|
666
|
+
// 2.1 Missing stages
|
|
667
|
+
for (const stage of EXPECTED_STAGES) {
|
|
668
|
+
if (!kindSet.has(stage.kind)) {
|
|
669
|
+
// dispatch_complete 和 reply_completed 缺失仅在有 dispatching 时才告警
|
|
670
|
+
if ((stage.kind === 'dispatch_complete' || stage.kind === 'reply_completed') && !kindSet.has('dispatching'))
|
|
671
|
+
continue;
|
|
672
|
+
// card 相关阶段在有 rejected 时不告警
|
|
673
|
+
if ((stage.kind === 'card_created' || stage.kind === 'card_sent' || stage.kind === 'card_stream') &&
|
|
674
|
+
kindSet.has('rejected'))
|
|
675
|
+
continue;
|
|
676
|
+
issues.push(`缺失阶段: ${stage.label}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// 2.2 Errors
|
|
680
|
+
for (const ev of events) {
|
|
681
|
+
const kind = classifyEvent(ev.body);
|
|
682
|
+
if (kind === 'rejected')
|
|
683
|
+
issues.push(`消息被拒绝: ${ev.body.replace('rejected: ', '')}`);
|
|
684
|
+
if (kind === 'reply_error')
|
|
685
|
+
issues.push(`回复错误: ${ev.body}`);
|
|
686
|
+
if (kind === 'tool_fail')
|
|
687
|
+
issues.push(`工具失败: ${ev.body}`);
|
|
688
|
+
if (kind === 'card_stream_fail')
|
|
689
|
+
issues.push(`流式更新失败: ${ev.body}`);
|
|
690
|
+
if (kind === 'card_fallback')
|
|
691
|
+
issues.push(`卡片降级: ${ev.body}`);
|
|
692
|
+
// CardKit non-zero code
|
|
693
|
+
if (kind === 'card_stream' || kind === 'card_update' || kind === 'card_settings' || kind === 'card_created') {
|
|
694
|
+
const codeMatch = ev.body.match(/code=(\d+)/);
|
|
695
|
+
if (codeMatch && codeMatch[1] !== '0') {
|
|
696
|
+
issues.push(`API 返回错误码: code=${codeMatch[1]} — ${ev.body}`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
// 2.3 Performance thresholds
|
|
701
|
+
const firstByKind = new Map();
|
|
702
|
+
for (const ev of events) {
|
|
703
|
+
const kind = classifyEvent(ev.body);
|
|
704
|
+
if (!firstByKind.has(kind))
|
|
705
|
+
firstByKind.set(kind, ev);
|
|
706
|
+
}
|
|
707
|
+
for (const rule of PERF_THRESHOLDS) {
|
|
708
|
+
const from = firstByKind.get(rule.from);
|
|
709
|
+
const to = firstByKind.get(rule.to);
|
|
710
|
+
if (from && to) {
|
|
711
|
+
const gap = to.timestamp.getTime() - from.timestamp.getTime();
|
|
712
|
+
if (gap > rule.warnMs) {
|
|
713
|
+
issues.push(`性能警告: ${rule.label} 耗时 ${(gap / 1000).toFixed(1)}s(阈值 ${(rule.warnMs / 1000).toFixed(0)}s)`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// 2.4 Duplicate delivery
|
|
718
|
+
const receivedCount = events.filter((e) => classifyEvent(e.body) === 'received').length;
|
|
719
|
+
if (receivedCount > 1) {
|
|
720
|
+
issues.push(`重复投递: 同一消息被接收 ${receivedCount} 次(WebSocket 重投递)`);
|
|
721
|
+
}
|
|
722
|
+
// 2.5 Card stream continuity
|
|
723
|
+
const streamSeqs = [];
|
|
724
|
+
for (const ev of events) {
|
|
725
|
+
if (classifyEvent(ev.body) === 'card_stream') {
|
|
726
|
+
const m = ev.body.match(/seq=(\d+)/);
|
|
727
|
+
if (m)
|
|
728
|
+
streamSeqs.push(parseInt(m[1], 10));
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (streamSeqs.length > 1) {
|
|
732
|
+
for (let i = 1; i < streamSeqs.length; i++) {
|
|
733
|
+
if (streamSeqs[i] !== streamSeqs[i - 1] + 1) {
|
|
734
|
+
issues.push(`流式 seq 不连续: seq=${streamSeqs[i - 1]} → seq=${streamSeqs[i]}(跳过了 ${streamSeqs[i] - streamSeqs[i - 1] - 1} 个)`);
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
out.push(`${ANSI.bold}【异常检测】${ANSI.reset}`);
|
|
740
|
+
out.push(sep);
|
|
741
|
+
if (issues.length === 0) {
|
|
742
|
+
out.push(` ${ANSI.green}未发现异常${ANSI.reset}`);
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
for (let i = 0; i < issues.length; i++) {
|
|
746
|
+
const isError = issues[i].startsWith('工具失败') ||
|
|
747
|
+
issues[i].startsWith('回复错误') ||
|
|
748
|
+
issues[i].startsWith('API 返回错误码') ||
|
|
749
|
+
issues[i].startsWith('流式更新失败');
|
|
750
|
+
const color = isError ? ANSI.red : ANSI.yellow;
|
|
751
|
+
out.push(` ${color}${i + 1}. ${issues[i]}${ANSI.reset}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
out.push('');
|
|
755
|
+
// ── Section 3: Diagnosis ──
|
|
756
|
+
out.push(`${ANSI.bold}【诊断总结】${ANSI.reset}`);
|
|
757
|
+
out.push(sep);
|
|
758
|
+
const hasError = issues.some((i) => i.startsWith('工具失败') ||
|
|
759
|
+
i.startsWith('回复错误') ||
|
|
760
|
+
i.startsWith('API 返回错误码') ||
|
|
761
|
+
i.startsWith('流式更新失败') ||
|
|
762
|
+
i.startsWith('缺失阶段'));
|
|
763
|
+
const hasWarn = issues.length > 0;
|
|
764
|
+
if (!hasWarn) {
|
|
765
|
+
out.push(` 状态: ${ANSI.green}✓ 正常${ANSI.reset}`);
|
|
766
|
+
out.push(` 消息处理链路完整,全程耗时 ${(totalMs / 1000).toFixed(1)}s。`);
|
|
767
|
+
// Break down time
|
|
768
|
+
const dispatchComplete = events.find((e) => classifyEvent(e.body) === 'dispatch_complete' && e.body.includes('replies=') && !e.body.includes('replies=0'));
|
|
769
|
+
if (dispatchComplete) {
|
|
770
|
+
const m = dispatchComplete.body.match(/elapsed=(\d+)ms/);
|
|
771
|
+
if (m) {
|
|
772
|
+
out.push(` 其中 Agent 处理耗时 ${(parseInt(m[1], 10) / 1000).toFixed(1)}s(含 AI 推理 + 工具调用)。`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
else if (hasError) {
|
|
777
|
+
out.push(` 状态: ${ANSI.red}✘ 异常${ANSI.reset}`);
|
|
778
|
+
out.push(` 发现 ${issues.length} 个问题,需要排查。`);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
out.push(` 状态: ${ANSI.yellow}⚠ 有警告${ANSI.reset}`);
|
|
782
|
+
out.push(` 发现 ${issues.length} 个警告,功能可用但需关注。`);
|
|
783
|
+
}
|
|
784
|
+
out.push('');
|
|
785
|
+
return out.join('\n');
|
|
786
|
+
}
|
|
787
|
+
function formatDiagReportCli(report) {
|
|
788
|
+
const lines = [];
|
|
789
|
+
const sep = '====================================';
|
|
790
|
+
lines.push(sep);
|
|
791
|
+
lines.push(` ${ANSI.bold}飞书插件诊断报告${ANSI.reset}`);
|
|
792
|
+
lines.push(` ${report.timestamp}`);
|
|
793
|
+
lines.push(sep);
|
|
794
|
+
lines.push('');
|
|
795
|
+
// Environment
|
|
796
|
+
lines.push(`${ANSI.bold}【环境信息】${ANSI.reset}`);
|
|
797
|
+
lines.push(` Node.js: ${report.environment.nodeVersion}`);
|
|
798
|
+
lines.push(` 插件版本: ${report.environment.pluginVersion}`);
|
|
799
|
+
lines.push(` 系统: ${report.environment.platform} ${report.environment.arch}`);
|
|
800
|
+
lines.push('');
|
|
801
|
+
// Global checks
|
|
802
|
+
lines.push(`${ANSI.bold}【全局检查】${ANSI.reset}`);
|
|
803
|
+
for (const c of report.checks) {
|
|
804
|
+
lines.push(formatCheckCli(c));
|
|
805
|
+
}
|
|
806
|
+
lines.push('');
|
|
807
|
+
// Per-account
|
|
808
|
+
for (const acct of report.accounts) {
|
|
809
|
+
lines.push(`${ANSI.bold}【账户: ${acct.accountId}】${ANSI.reset}`);
|
|
810
|
+
if (acct.name)
|
|
811
|
+
lines.push(` 名称: ${acct.name}`);
|
|
812
|
+
lines.push(` App ID: ${acct.appId}`);
|
|
813
|
+
lines.push(` 品牌: ${acct.brand}`);
|
|
814
|
+
lines.push('');
|
|
815
|
+
for (const c of acct.checks) {
|
|
816
|
+
lines.push(formatCheckCli(c));
|
|
817
|
+
}
|
|
818
|
+
lines.push('');
|
|
819
|
+
}
|
|
820
|
+
// Tools
|
|
821
|
+
lines.push(`${ANSI.bold}【工具注册】${ANSI.reset}`);
|
|
822
|
+
if (report.toolsRegistered.length > 0) {
|
|
823
|
+
lines.push(` ${report.toolsRegistered.join(', ')}`);
|
|
824
|
+
lines.push(` 共 ${report.toolsRegistered.length} 个`);
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
lines.push(' 无工具注册(未找到已配置的账户)');
|
|
828
|
+
}
|
|
829
|
+
lines.push('');
|
|
830
|
+
// Recent errors
|
|
831
|
+
if (report.recentErrors.length > 0) {
|
|
832
|
+
lines.push(`${ANSI.bold}【最近错误】${ANSI.reset}(${report.recentErrors.length} 条)`);
|
|
833
|
+
for (let i = 0; i < report.recentErrors.length; i++) {
|
|
834
|
+
lines.push(` ${ANSI.gray}${i + 1}. ${report.recentErrors[i]}${ANSI.reset}`);
|
|
835
|
+
}
|
|
836
|
+
lines.push('');
|
|
837
|
+
}
|
|
838
|
+
// Overall
|
|
839
|
+
const statusColorMap = {
|
|
840
|
+
healthy: `${ANSI.green}HEALTHY${ANSI.reset}`,
|
|
841
|
+
degraded: `${ANSI.yellow}DEGRADED (存在警告)${ANSI.reset}`,
|
|
842
|
+
unhealthy: `${ANSI.red}UNHEALTHY (存在失败项)${ANSI.reset}`,
|
|
843
|
+
};
|
|
844
|
+
lines.push(sep);
|
|
845
|
+
lines.push(` 总体状态: ${statusColorMap[report.overallStatus]}`);
|
|
846
|
+
lines.push(sep);
|
|
847
|
+
return lines.join('\n');
|
|
848
|
+
}
|