botmux 2.47.0 → 2.47.1
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/README.en.md +10 -5
- package/README.md +10 -5
- package/dist/adapters/adopt-route.d.ts +63 -0
- package/dist/adapters/adopt-route.d.ts.map +1 -0
- package/dist/adapters/adopt-route.js +195 -0
- package/dist/adapters/adopt-route.js.map +1 -0
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +11 -0
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +11 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +17 -1
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +36 -9
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/coco.d.ts.map +1 -1
- package/dist/adapters/cli/coco.js +26 -1
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts +4 -0
- package/dist/adapters/cli/codex-app.d.ts.map +1 -0
- package/dist/adapters/cli/codex-app.js +72 -0
- package/dist/adapters/cli/codex-app.js.map +1 -0
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +6 -1
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/cursor.d.ts.map +1 -1
- package/dist/adapters/cli/cursor.js +58 -12
- package/dist/adapters/cli/cursor.js.map +1 -1
- package/dist/adapters/cli/gemini.d.ts.map +1 -1
- package/dist/adapters/cli/gemini.js +5 -1
- package/dist/adapters/cli/gemini.js.map +1 -1
- package/dist/adapters/cli/hermes.d.ts +4 -0
- package/dist/adapters/cli/hermes.d.ts.map +1 -0
- package/dist/adapters/cli/hermes.js +40 -0
- package/dist/adapters/cli/hermes.js.map +1 -0
- package/dist/adapters/cli/mira.d.ts +4 -0
- package/dist/adapters/cli/mira.d.ts.map +1 -0
- package/dist/adapters/cli/mira.js +67 -0
- package/dist/adapters/cli/mira.js.map +1 -0
- package/dist/adapters/cli/mtr.d.ts +5 -0
- package/dist/adapters/cli/mtr.d.ts.map +1 -0
- package/dist/adapters/cli/mtr.js +62 -0
- package/dist/adapters/cli/mtr.js.map +1 -0
- package/dist/adapters/cli/opencode.d.ts.map +1 -1
- package/dist/adapters/cli/opencode.js +19 -1
- package/dist/adapters/cli/opencode.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts +5 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +22 -2
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +1 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +2 -1
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +35 -2
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/adapters/hook-command.d.ts +18 -0
- package/dist/adapters/hook-command.d.ts.map +1 -0
- package/dist/adapters/hook-command.js +38 -0
- package/dist/adapters/hook-command.js.map +1 -0
- package/dist/adapters/hook-installer.d.ts +14 -0
- package/dist/adapters/hook-installer.d.ts.map +1 -0
- package/dist/adapters/hook-installer.js +192 -0
- package/dist/adapters/hook-installer.js.map +1 -0
- package/dist/bot-registry.d.ts +59 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +67 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +8 -0
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +9 -0
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/cli.d.ts +15 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +603 -106
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.d.ts +3 -0
- package/dist/codex-app-runner.d.ts.map +1 -0
- package/dist/codex-app-runner.js +512 -0
- package/dist/codex-app-runner.js.map +1 -0
- package/dist/config.d.ts +11 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -4
- package/dist/config.js.map +1 -1
- package/dist/core/ask-api.d.ts +47 -0
- package/dist/core/ask-api.d.ts.map +1 -0
- package/dist/core/ask-api.js +139 -0
- package/dist/core/ask-api.js.map +1 -0
- package/dist/core/ask-args.d.ts +53 -0
- package/dist/core/ask-args.d.ts.map +1 -0
- package/dist/core/ask-args.js +122 -0
- package/dist/core/ask-args.js.map +1 -0
- package/dist/core/ask-broker.d.ts +98 -0
- package/dist/core/ask-broker.d.ts.map +1 -0
- package/dist/core/ask-broker.js +329 -0
- package/dist/core/ask-broker.js.map +1 -0
- package/dist/core/ask-hook/claude-code.d.ts +50 -0
- package/dist/core/ask-hook/claude-code.d.ts.map +1 -0
- package/dist/core/ask-hook/claude-code.js +145 -0
- package/dist/core/ask-hook/claude-code.js.map +1 -0
- package/dist/core/ask-hook/codex.d.ts +43 -0
- package/dist/core/ask-hook/codex.d.ts.map +1 -0
- package/dist/core/ask-hook/codex.js +69 -0
- package/dist/core/ask-hook/codex.js.map +1 -0
- package/dist/core/ask-hook/opencode.d.ts +41 -0
- package/dist/core/ask-hook/opencode.d.ts.map +1 -0
- package/dist/core/ask-hook/opencode.js +108 -0
- package/dist/core/ask-hook/opencode.js.map +1 -0
- package/dist/core/ask-hook/registry.d.ts +3 -0
- package/dist/core/ask-hook/registry.d.ts.map +1 -0
- package/dist/core/ask-hook/registry.js +12 -0
- package/dist/core/ask-hook/registry.js.map +1 -0
- package/dist/core/ask-hook/types.d.ts +26 -0
- package/dist/core/ask-hook/types.d.ts.map +1 -0
- package/dist/core/ask-hook/types.js +2 -0
- package/dist/core/ask-hook/types.js.map +1 -0
- package/dist/core/ask-types.d.ts +146 -0
- package/dist/core/ask-types.d.ts.map +1 -0
- package/dist/core/ask-types.js +18 -0
- package/dist/core/ask-types.js.map +1 -0
- package/dist/core/command-handler.d.ts +20 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +774 -311
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +2 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +222 -2
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +17 -1
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +64 -10
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +19 -5
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +37 -20
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +9 -0
- package/dist/core/trigger-session.d.ts.map +1 -0
- package/dist/core/trigger-session.js +158 -0
- package/dist/core/trigger-session.js.map +1 -0
- package/dist/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +141 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +543 -24
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +213 -58
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +6 -1
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +9 -1
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts +3 -0
- package/dist/dashboard/connector-api.d.ts.map +1 -0
- package/dist/dashboard/connector-api.js +351 -0
- package/dist/dashboard/connector-api.js.map +1 -0
- package/dist/dashboard/federated-group-core.d.ts +54 -0
- package/dist/dashboard/federated-group-core.d.ts.map +1 -0
- package/dist/dashboard/federated-group-core.js +165 -0
- package/dist/dashboard/federated-group-core.js.map +1 -0
- package/dist/dashboard/federation-api.d.ts +42 -0
- package/dist/dashboard/federation-api.d.ts.map +1 -0
- package/dist/dashboard/federation-api.js +408 -0
- package/dist/dashboard/federation-api.js.map +1 -0
- package/dist/dashboard/federation-spoke-api.d.ts +76 -0
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
- package/dist/dashboard/federation-spoke-api.js +618 -0
- package/dist/dashboard/federation-spoke-api.js.map +1 -0
- package/dist/dashboard/team-group.d.ts +18 -0
- package/dist/dashboard/team-group.d.ts.map +1 -0
- package/dist/dashboard/team-group.js +7 -0
- package/dist/dashboard/team-group.js.map +1 -0
- package/dist/dashboard/trigger-api.d.ts +13 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -0
- package/dist/dashboard/trigger-api.js +77 -0
- package/dist/dashboard/trigger-api.js.map +1 -0
- package/dist/dashboard/web/app.js +8 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +205 -21
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/connectors.d.ts +2 -0
- package/dist/dashboard/web/connectors.d.ts.map +1 -0
- package/dist/dashboard/web/connectors.js +187 -0
- package/dist/dashboard/web/connectors.js.map +1 -0
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +43 -5
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +4 -0
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/team-federation.d.ts +3 -0
- package/dist/dashboard/web/team-federation.d.ts.map +1 -0
- package/dist/dashboard/web/team-federation.js +487 -0
- package/dist/dashboard/web/team-federation.js.map +1 -0
- package/dist/dashboard/web/workflows.js +3 -3
- package/dist/dashboard/web/workflows.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts +19 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -0
- package/dist/dashboard/webhook-routes.js +321 -0
- package/dist/dashboard/webhook-routes.js.map +1 -0
- package/dist/dashboard/workflow-api.d.ts +8 -1
- package/dist/dashboard/workflow-api.d.ts.map +1 -1
- package/dist/dashboard/workflow-api.js +19 -4
- package/dist/dashboard/workflow-api.js.map +1 -1
- package/dist/dashboard-web/app.js +539 -375
- package/dist/dashboard-web/index.html +3 -1
- package/dist/dashboard-web/style.css +22 -0
- package/dist/dashboard.js +199 -2
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +104 -11
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +104 -11
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts +55 -0
- package/dist/im/lark/ask-card.d.ts.map +1 -0
- package/dist/im/lark/ask-card.js +328 -0
- package/dist/im/lark/ask-card.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +108 -3
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +480 -50
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +241 -18
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +83 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +286 -70
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +29 -4
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts +2 -1
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +3 -2
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/identity-cache.d.ts.map +1 -1
- package/dist/im/lark/identity-cache.js +3 -3
- package/dist/im/lark/identity-cache.js.map +1 -1
- package/dist/im/lark/md-card.d.ts +20 -2
- package/dist/im/lark/md-card.d.ts.map +1 -1
- package/dist/im/lark/md-card.js +49 -17
- package/dist/im/lark/md-card.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +87 -31
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/workflow-card-handler.d.ts +2 -2
- package/dist/im/lark/workflow-card-handler.d.ts.map +1 -1
- package/dist/im/lark/workflow-card-handler.js +12 -1
- package/dist/im/lark/workflow-card-handler.js.map +1 -1
- package/dist/im/lark/workflow-progress-card.d.ts.map +1 -1
- package/dist/im/lark/workflow-progress-card.js +53 -0
- package/dist/im/lark/workflow-progress-card.js.map +1 -1
- package/dist/mira-output.d.ts +3 -0
- package/dist/mira-output.d.ts.map +1 -0
- package/dist/mira-output.js +136 -0
- package/dist/mira-output.js.map +1 -0
- package/dist/mira-runner.d.ts +3 -0
- package/dist/mira-runner.d.ts.map +1 -0
- package/dist/mira-runner.js +534 -0
- package/dist/mira-runner.js.map +1 -0
- package/dist/services/bot-owner-store.d.ts +28 -0
- package/dist/services/bot-owner-store.d.ts.map +1 -0
- package/dist/services/bot-owner-store.js +82 -0
- package/dist/services/bot-owner-store.js.map +1 -0
- package/dist/services/bot-profile-store.d.ts +16 -0
- package/dist/services/bot-profile-store.d.ts.map +1 -0
- package/dist/services/bot-profile-store.js +98 -0
- package/dist/services/bot-profile-store.js.map +1 -0
- package/dist/services/brand-store.d.ts +15 -0
- package/dist/services/brand-store.d.ts.map +1 -0
- package/dist/services/brand-store.js +47 -0
- package/dist/services/brand-store.js.map +1 -0
- package/dist/services/card-prefs-store.d.ts +20 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -0
- package/dist/services/card-prefs-store.js +82 -0
- package/dist/services/card-prefs-store.js.map +1 -0
- package/dist/services/codex-bridge-queue.d.ts +1 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -1
- package/dist/services/codex-bridge-queue.js +23 -0
- package/dist/services/codex-bridge-queue.js.map +1 -1
- package/dist/services/codex-transcript.d.ts +1 -0
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/connector-store.d.ts +58 -0
- package/dist/services/connector-store.d.ts.map +1 -0
- package/dist/services/connector-store.js +79 -0
- package/dist/services/connector-store.js.map +1 -0
- package/dist/services/deployment-identity.d.ts +22 -0
- package/dist/services/deployment-identity.d.ts.map +1 -0
- package/dist/services/deployment-identity.js +67 -0
- package/dist/services/deployment-identity.js.map +1 -0
- package/dist/services/federation-membership-store.d.ts +23 -0
- package/dist/services/federation-membership-store.d.ts.map +1 -0
- package/dist/services/federation-membership-store.js +66 -0
- package/dist/services/federation-membership-store.js.map +1 -0
- package/dist/services/federation-roster.d.ts +54 -0
- package/dist/services/federation-roster.d.ts.map +1 -0
- package/dist/services/federation-roster.js +51 -0
- package/dist/services/federation-roster.js.map +1 -0
- package/dist/services/federation-store.d.ts +76 -0
- package/dist/services/federation-store.d.ts.map +1 -0
- package/dist/services/federation-store.js +133 -0
- package/dist/services/federation-store.js.map +1 -0
- package/dist/services/grant-store.d.ts +12 -2
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +51 -4
- package/dist/services/grant-store.js.map +1 -1
- package/dist/services/group-creator.d.ts +10 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +26 -1
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +30 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +85 -12
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/hermes-transcript.d.ts +7 -0
- package/dist/services/hermes-transcript.d.ts.map +1 -0
- package/dist/services/hermes-transcript.js +117 -0
- package/dist/services/hermes-transcript.js.map +1 -0
- package/dist/services/invite-store.d.ts +28 -0
- package/dist/services/invite-store.d.ts.map +1 -0
- package/dist/services/invite-store.js +85 -0
- package/dist/services/invite-store.js.map +1 -0
- package/dist/services/pairing-store.d.ts +47 -0
- package/dist/services/pairing-store.d.ts.map +1 -0
- package/dist/services/pairing-store.js +132 -0
- package/dist/services/pairing-store.js.map +1 -0
- package/dist/services/project-scanner.d.ts +10 -0
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +11 -0
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/services/relay-picker.d.ts +22 -0
- package/dist/services/relay-picker.d.ts.map +1 -0
- package/dist/services/relay-picker.js +62 -0
- package/dist/services/relay-picker.js.map +1 -0
- package/dist/services/send-policy.d.ts +55 -0
- package/dist/services/send-policy.d.ts.map +1 -0
- package/dist/services/send-policy.js +47 -0
- package/dist/services/send-policy.js.map +1 -0
- package/dist/services/session-store.js +1 -1
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/team-roster.d.ts +38 -0
- package/dist/services/team-roster.d.ts.map +1 -0
- package/dist/services/team-roster.js +82 -0
- package/dist/services/team-roster.js.map +1 -0
- package/dist/services/team-store.d.ts +54 -0
- package/dist/services/team-store.d.ts.map +1 -0
- package/dist/services/team-store.js +156 -0
- package/dist/services/team-store.js.map +1 -0
- package/dist/services/trigger-log-store.d.ts +46 -0
- package/dist/services/trigger-log-store.d.ts.map +1 -0
- package/dist/services/trigger-log-store.js +132 -0
- package/dist/services/trigger-log-store.js.map +1 -0
- package/dist/services/trigger-types.d.ts +57 -0
- package/dist/services/trigger-types.d.ts.map +1 -0
- package/dist/services/trigger-types.js +28 -0
- package/dist/services/trigger-types.js.map +1 -0
- package/dist/services/webhook-key.d.ts +16 -0
- package/dist/services/webhook-key.d.ts.map +1 -0
- package/dist/services/webhook-key.js +123 -0
- package/dist/services/webhook-key.js.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.js +59 -0
- package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
- package/dist/services/webhook-lifecycle-store.d.ts +45 -0
- package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-store.js +159 -0
- package/dist/services/webhook-lifecycle-store.js.map +1 -0
- package/dist/setup/bot-config-editor.d.ts +8 -1
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +20 -2
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/ensure-tmux.d.ts +0 -22
- package/dist/setup/ensure-tmux.d.ts.map +1 -1
- package/dist/setup/ensure-tmux.js +25 -1
- package/dist/setup/ensure-tmux.js.map +1 -1
- package/dist/setup/verify-permissions.d.ts.map +1 -1
- package/dist/setup/verify-permissions.js +15 -1
- package/dist/setup/verify-permissions.js.map +1 -1
- package/dist/skills/definitions.d.ts +2 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +178 -12
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/installer.d.ts +34 -0
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +119 -2
- package/dist/skills/installer.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +50 -0
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js +83 -0
- package/dist/utils/bot-routing.js.map +1 -1
- package/dist/utils/daemon-discovery.d.ts +11 -0
- package/dist/utils/daemon-discovery.d.ts.map +1 -0
- package/dist/utils/daemon-discovery.js +59 -0
- package/dist/utils/daemon-discovery.js.map +1 -0
- package/dist/utils/user-token.d.ts.map +1 -1
- package/dist/utils/user-token.js +0 -2
- package/dist/utils/user-token.js.map +1 -1
- package/dist/worker.js +198 -27
- package/dist/worker.js.map +1 -1
- package/dist/workflows/attempt-resume.d.ts.map +1 -1
- package/dist/workflows/attempt-resume.js +2 -2
- package/dist/workflows/attempt-resume.js.map +1 -1
- package/dist/workflows/definition.d.ts +412 -9
- package/dist/workflows/definition.d.ts.map +1 -1
- package/dist/workflows/definition.js +238 -3
- package/dist/workflows/definition.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +114 -11
- package/dist/workflows/events/payloads.d.ts.map +1 -1
- package/dist/workflows/events/payloads.js +46 -0
- package/dist/workflows/events/payloads.js.map +1 -1
- package/dist/workflows/events/replay.d.ts +21 -0
- package/dist/workflows/events/replay.d.ts.map +1 -1
- package/dist/workflows/events/replay.js +103 -0
- package/dist/workflows/events/replay.js.map +1 -1
- package/dist/workflows/events/schema.d.ts +1301 -606
- package/dist/workflows/events/schema.d.ts.map +1 -1
- package/dist/workflows/events/schema.js +37 -1
- package/dist/workflows/events/schema.js.map +1 -1
- package/dist/workflows/events/types.d.ts +5 -1
- package/dist/workflows/events/types.d.ts.map +1 -1
- package/dist/workflows/loader.d.ts +14 -0
- package/dist/workflows/loader.d.ts.map +1 -1
- package/dist/workflows/loader.js +27 -0
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/loop.js +58 -0
- package/dist/workflows/loop.js.map +1 -1
- package/dist/workflows/ops-projection.d.ts +58 -0
- package/dist/workflows/ops-projection.d.ts.map +1 -1
- package/dist/workflows/ops-projection.js +74 -0
- package/dist/workflows/ops-projection.js.map +1 -1
- package/dist/workflows/orchestrator.d.ts +65 -1
- package/dist/workflows/orchestrator.d.ts.map +1 -1
- package/dist/workflows/orchestrator.js +486 -74
- package/dist/workflows/orchestrator.js.map +1 -1
- package/dist/workflows/output-binding.d.ts +8 -1
- package/dist/workflows/output-binding.d.ts.map +1 -1
- package/dist/workflows/output-binding.js +75 -11
- package/dist/workflows/output-binding.js.map +1 -1
- package/dist/workflows/runtime.d.ts +1 -1
- package/dist/workflows/runtime.d.ts.map +1 -1
- package/dist/workflows/runtime.js +39 -4
- package/dist/workflows/runtime.js.map +1 -1
- package/dist/workflows/trigger-from-envelope.d.ts +13 -0
- package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
- package/dist/workflows/trigger-from-envelope.js +67 -0
- package/dist/workflows/trigger-from-envelope.js.map +1 -0
- package/dist/workflows/wait.d.ts +23 -2
- package/dist/workflows/wait.d.ts.map +1 -1
- package/dist/workflows/wait.js +39 -17
- package/dist/workflows/wait.js.map +1 -1
- package/package.json +1 -1
- package/dist/services/feishu-task-client.d.ts +0 -28
- package/dist/services/feishu-task-client.d.ts.map +0 -1
- package/dist/services/feishu-task-client.js +0 -123
- package/dist/services/feishu-task-client.js.map +0 -1
- package/dist/services/task-store.d.ts +0 -37
- package/dist/services/task-store.d.ts.map +0 -1
- package/dist/services/task-store.js +0 -115
- package/dist/services/task-store.js.map +0 -1
|
@@ -4,10 +4,14 @@ const cliDisplayNames = {
|
|
|
4
4
|
'aiden': 'Aiden',
|
|
5
5
|
'coco': 'CoCo',
|
|
6
6
|
'codex': 'Codex',
|
|
7
|
+
'codex-app': 'Codex App',
|
|
7
8
|
'cursor': 'Cursor',
|
|
8
9
|
'gemini': 'Gemini',
|
|
9
10
|
'opencode': 'OpenCode',
|
|
10
11
|
'antigravity': 'Antigravity',
|
|
12
|
+
'mtr': 'MTR',
|
|
13
|
+
'hermes': 'Hermes',
|
|
14
|
+
'mira': 'Mira',
|
|
11
15
|
};
|
|
12
16
|
export function getCliDisplayName(cliId) {
|
|
13
17
|
return cliDisplayNames[cliId] ?? cliId;
|
|
@@ -129,14 +133,70 @@ export function buildSessionClosedCard(sessionId, rootId, title, cliId, workingD
|
|
|
129
133
|
};
|
|
130
134
|
return JSON.stringify(card);
|
|
131
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Build a frozen-snapshot card to PATCH onto the source-chat streaming card
|
|
138
|
+
* after `/relay` moves the session elsewhere.
|
|
139
|
+
*
|
|
140
|
+
* Why this exists: a live streaming card carries action buttons (close /
|
|
141
|
+
* toggle display / get write link). Those buttons identify their session by
|
|
142
|
+
* `session_id` in the value payload, so clicking them after relay still
|
|
143
|
+
* reaches the now-relocated session — closing it, toggling its display
|
|
144
|
+
* mode, etc. — but the visible feedback all lands on the NEW card in the
|
|
145
|
+
* target chat, not this one. The source-chat card then looks like a "live
|
|
146
|
+
* console" while actually being a footgun. PATCH it to an inert snapshot
|
|
147
|
+
* so the user sees clearly it's historical.
|
|
148
|
+
*
|
|
149
|
+
* Last-frame rendering:
|
|
150
|
+
* - imageKey present (session was in 'screenshot' / expanded mode at
|
|
151
|
+
* relay time) → embed the same img element the live card had.
|
|
152
|
+
* img_key is a Lark server resource independent of the card it lived
|
|
153
|
+
* on, so the PATCHed card can still reference it.
|
|
154
|
+
* - imageKey absent (hidden / collapsed mode) → render nothing extra.
|
|
155
|
+
* The header + body notice already convey the state; raw tmux pane
|
|
156
|
+
* text as a code-block is too long and noisy (王皓 caught this in
|
|
157
|
+
* testing).
|
|
158
|
+
*
|
|
159
|
+
* No action buttons are rendered in either case.
|
|
160
|
+
*/
|
|
161
|
+
export function buildRelayedFrozenCard(title, cliId, imageKey, locale) {
|
|
162
|
+
const cliName = getCliDisplayName(cliId ?? 'claude-code');
|
|
163
|
+
const body = `**${escapeMd(title || cliName)}**\n` +
|
|
164
|
+
`${t('card.body.relay_frozen', undefined, locale)}`;
|
|
165
|
+
const elements = [
|
|
166
|
+
{ tag: 'markdown', content: body },
|
|
167
|
+
];
|
|
168
|
+
if (imageKey) {
|
|
169
|
+
elements.push({
|
|
170
|
+
tag: 'img',
|
|
171
|
+
img_key: imageKey,
|
|
172
|
+
alt: { tag: 'plain_text', content: '' },
|
|
173
|
+
mode: 'fit_horizontal',
|
|
174
|
+
preview: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const card = {
|
|
178
|
+
config: { wide_screen_mode: true },
|
|
179
|
+
header: {
|
|
180
|
+
title: { tag: 'plain_text', content: t('card.status.relay_frozen', undefined, locale) },
|
|
181
|
+
template: 'grey',
|
|
182
|
+
},
|
|
183
|
+
elements,
|
|
184
|
+
};
|
|
185
|
+
return JSON.stringify(card);
|
|
186
|
+
}
|
|
132
187
|
/**
|
|
133
188
|
* Feishu card API rejects payloads exceeding ~109 KB (error 230025).
|
|
134
189
|
* Cap markdown content byte size with headroom for card JSON overhead.
|
|
135
190
|
*/
|
|
136
191
|
const MAX_CONTENT_BYTES = 100_000;
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Truncate content to fit within `maxBytes`, keeping the tail (most recent
|
|
194
|
+
* output). Defaults to {@link MAX_CONTENT_BYTES}; callers that wrap the content
|
|
195
|
+
* in additional card JSON (e.g. the private snapshot's code fence) pass a
|
|
196
|
+
* tighter budget so the whole card stays under Feishu's ~109 KB hard limit.
|
|
197
|
+
*/
|
|
198
|
+
export function truncateContent(content, locale, maxBytes = MAX_CONTENT_BYTES) {
|
|
199
|
+
if (Buffer.byteLength(content, 'utf-8') <= maxBytes)
|
|
140
200
|
return content;
|
|
141
201
|
// Binary search for the longest suffix that fits
|
|
142
202
|
const lines = content.split('\n');
|
|
@@ -145,7 +205,7 @@ export function truncateContent(content, locale) {
|
|
|
145
205
|
while (lo < hi) {
|
|
146
206
|
const mid = Math.floor((lo + hi) / 2);
|
|
147
207
|
const candidate = lines.slice(mid).join('\n');
|
|
148
|
-
if (Buffer.byteLength(candidate, 'utf-8') <=
|
|
208
|
+
if (Buffer.byteLength(candidate, 'utf-8') <= maxBytes - 30) {
|
|
149
209
|
hi = mid;
|
|
150
210
|
}
|
|
151
211
|
else {
|
|
@@ -154,36 +214,29 @@ export function truncateContent(content, locale) {
|
|
|
154
214
|
}
|
|
155
215
|
return `${t('card.status.truncated_prefix', undefined, locale)}\n${lines.slice(lo).join('\n')}`;
|
|
156
216
|
}
|
|
157
|
-
/**
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
*
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
case 'limited': return usageLimit?.retryReady
|
|
181
|
-
? t('card.status.retry_ready', undefined, locale)
|
|
182
|
-
: t('card.status.limited', undefined, locale);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const elements = [];
|
|
186
|
-
// ── Output body ─────────────────────────────────────────────────────────
|
|
217
|
+
/** Byte budget for the private snapshot's text fallback. Well under the ~109 KB
|
|
218
|
+
* card limit, leaving room for JSON escaping + the card's structural overhead. */
|
|
219
|
+
const PRIVATE_SNAPSHOT_TEXT_MAX = 50_000;
|
|
220
|
+
const STREAM_TEMPLATE_MAP = {
|
|
221
|
+
starting: 'yellow', working: 'blue', idle: 'green', analyzing: 'purple', limited: 'red', retry_ready: 'green',
|
|
222
|
+
};
|
|
223
|
+
/** Header status label for a streaming/snapshot card. Shared by the live card
|
|
224
|
+
* and the private snapshot so the two never drift. */
|
|
225
|
+
function streamStatusLabel(status, usageLimit, locale) {
|
|
226
|
+
switch (status) {
|
|
227
|
+
case 'starting': return t('card.status.starting', undefined, locale);
|
|
228
|
+
case 'working': return t('card.status.working', undefined, locale);
|
|
229
|
+
case 'idle': return t('card.status.idle', undefined, locale);
|
|
230
|
+
case 'analyzing': return t('card.status.analyzing', undefined, locale);
|
|
231
|
+
case 'limited': return usageLimit?.retryReady
|
|
232
|
+
? t('card.status.retry_ready', undefined, locale)
|
|
233
|
+
: t('card.status.limited', undefined, locale);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** Push the shared "output body" elements (usage-limit notice + screenshot) used
|
|
237
|
+
* by both {@link buildStreamingCard} and {@link buildPrivateSnapshotCard}. */
|
|
238
|
+
function pushStreamBody(elements, opts) {
|
|
239
|
+
const { status, usageLimit, displayMode, imageKey, cliName, locale } = opts;
|
|
187
240
|
if (status === 'limited' && usageLimit) {
|
|
188
241
|
elements.push({
|
|
189
242
|
tag: 'markdown',
|
|
@@ -195,19 +248,33 @@ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screen
|
|
|
195
248
|
}
|
|
196
249
|
if (displayMode === 'screenshot') {
|
|
197
250
|
if (imageKey) {
|
|
198
|
-
elements.push({
|
|
199
|
-
tag: 'img',
|
|
200
|
-
img_key: imageKey,
|
|
201
|
-
alt: { tag: 'plain_text', content: '' },
|
|
202
|
-
mode: 'fit_horizontal',
|
|
203
|
-
preview: true,
|
|
204
|
-
});
|
|
251
|
+
elements.push({ tag: 'img', img_key: imageKey, alt: { tag: 'plain_text', content: '' }, mode: 'fit_horizontal', preview: true });
|
|
205
252
|
}
|
|
206
253
|
else {
|
|
207
254
|
elements.push({ tag: 'markdown', content: t('card.status.waiting_screenshot', undefined, locale) });
|
|
208
255
|
}
|
|
209
256
|
elements.push({ tag: 'hr' });
|
|
210
257
|
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Build a Feishu streaming card that shows live terminal output + controls.
|
|
261
|
+
* This card is PATCHed in-place as the CLI works.
|
|
262
|
+
*
|
|
263
|
+
* displayMode:
|
|
264
|
+
* - 'hidden' — body collapsed; only header + main controls visible.
|
|
265
|
+
* - 'screenshot' — img element (rendered server-side, uploaded for img_key).
|
|
266
|
+
*
|
|
267
|
+
* Quick-action buttons (Esc, ^C, Tab, Space, Enter, ←↑↓→, ½屏 ↑/↓) appear
|
|
268
|
+
* whenever displayMode !== 'hidden'.
|
|
269
|
+
*/
|
|
270
|
+
export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screenContent, status, cliId, displayMode = 'hidden', cardNonce, imageKey, adoptMode, showTakeover, locale, usageLimit, writableTerminalUrl) {
|
|
271
|
+
const effectiveCliId = cliId ?? 'claude-code';
|
|
272
|
+
const cliName = getCliDisplayName(effectiveCliId);
|
|
273
|
+
const actionBase = { root_id: rootId, session_id: sessionId, cli_id: effectiveCliId, ...(cardNonce ? { card_nonce: cardNonce } : {}) };
|
|
274
|
+
const displayStatus = status === 'limited' && usageLimit?.retryReady ? 'retry_ready' : status;
|
|
275
|
+
const elements = [];
|
|
276
|
+
// ── Output body (shared with the private snapshot card) ──────────────────
|
|
277
|
+
pushStreamBody(elements, { status, usageLimit, displayMode, imageKey, cliName, locale });
|
|
211
278
|
// ── Main control row: display toggle, mode toggle, terminal, manage ─────
|
|
212
279
|
const headerActions = [];
|
|
213
280
|
headerActions.push({
|
|
@@ -277,6 +344,16 @@ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screen
|
|
|
277
344
|
});
|
|
278
345
|
}
|
|
279
346
|
elements.push({ tag: 'action', actions: headerActions });
|
|
347
|
+
// ── Writable terminal link (opt-in) ─────────────────────────────────────
|
|
348
|
+
// When the bot enables `writableTerminalLinkInCard`, embed the token-bearing
|
|
349
|
+
// link right in the card so anyone here can open a writable terminal without
|
|
350
|
+
// the get-write-link → DM round-trip. The link is intentionally group-visible.
|
|
351
|
+
if (writableTerminalUrl) {
|
|
352
|
+
elements.push({
|
|
353
|
+
tag: 'markdown',
|
|
354
|
+
content: t('card.writable_terminal_link', { url: writableTerminalUrl }, locale),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
280
357
|
// ── Quick-action keys (only when the screenshot is visible — in text mode
|
|
281
358
|
// there's no visible cursor/input, so these keys would fire blindly) ──
|
|
282
359
|
if (displayMode === 'screenshot') {
|
|
@@ -311,8 +388,94 @@ export function buildStreamingCard(sessionId, rootId, terminalUrl, title, screen
|
|
|
311
388
|
const card = {
|
|
312
389
|
config: { wide_screen_mode: true },
|
|
313
390
|
header: {
|
|
314
|
-
title: { tag: 'plain_text', content: `🖥️ ${cliName} · ${escapeMd(title)} — ${
|
|
315
|
-
template:
|
|
391
|
+
title: { tag: 'plain_text', content: `🖥️ ${cliName} · ${escapeMd(title)} — ${streamStatusLabel(status, usageLimit, locale)}` },
|
|
392
|
+
template: STREAM_TEMPLATE_MAP[displayStatus],
|
|
393
|
+
},
|
|
394
|
+
elements,
|
|
395
|
+
};
|
|
396
|
+
return JSON.stringify(card);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Build a static "private snapshot" card for `/card` in private mode — sent via
|
|
400
|
+
* the ephemeral API to one user at a time. Unlike {@link buildStreamingCard} it
|
|
401
|
+
* is **never PATCH-updated** (ephemeral cards can't be), so it carries only a
|
|
402
|
+
* one-shot snapshot of the terminal screenshot plus three buttons:
|
|
403
|
+
* • read-only "open terminal" link (a plain URL button — no callback);
|
|
404
|
+
* • "get write link", whose callback DMs the writable link to the clicker;
|
|
405
|
+
* • "close session", whose callback kills the session and (in private mode)
|
|
406
|
+
* sends the "closed" card ephemeral to the owner audience too — so the
|
|
407
|
+
* session title / CLI name / workingDir on it don't leak to the group.
|
|
408
|
+
* The last two have callbacks but neither patches THIS card (one DMs, the other
|
|
409
|
+
* sends a fresh card), so both work fine on an ephemeral card. Both are
|
|
410
|
+
* `canOperate`-gated in the handler — talk-only viewers who tap them are denied.
|
|
411
|
+
* The patch-driven controls (toggle/refresh/export/term keys) and the inline
|
|
412
|
+
* writable link are still omitted: those need to update this card, which
|
|
413
|
+
* ephemeral cards can't do.
|
|
414
|
+
*/
|
|
415
|
+
export function buildPrivateSnapshotCard(terminalUrl, title, status, cliId, imageKey, screenContent, sessionId, rootId, locale, usageLimit) {
|
|
416
|
+
const effectiveCliId = cliId ?? 'claude-code';
|
|
417
|
+
const cliName = getCliDisplayName(effectiveCliId);
|
|
418
|
+
const displayStatus = status === 'limited' && usageLimit?.retryReady ? 'retry_ready' : status;
|
|
419
|
+
// `visibility: 'private'` pins this card's privacy intent onto the action
|
|
420
|
+
// itself, so a later callback (notably `close`) keeps sending ephemeral even
|
|
421
|
+
// if the bot's `privateCard` config is toggled off after the card was sent —
|
|
422
|
+
// otherwise the closed card (session title / workingDir / resume command)
|
|
423
|
+
// could leak to the group. See the `close` handler in card-handler.ts.
|
|
424
|
+
const actionBase = { root_id: rootId, session_id: sessionId, cli_id: effectiveCliId, visibility: 'private' };
|
|
425
|
+
const elements = [];
|
|
426
|
+
// Show the terminal once: prefer the rendered screenshot when present;
|
|
427
|
+
// otherwise fall back to a code-block of the latest screen text so the
|
|
428
|
+
// snapshot isn't empty (common when the bot has the streaming card disabled
|
|
429
|
+
// or display mode never flipped to screenshot — `lastScreenContent` is still
|
|
430
|
+
// kept up to date regardless). pushStreamBody also emits the usage-limit
|
|
431
|
+
// notice, which applies in either case.
|
|
432
|
+
pushStreamBody(elements, {
|
|
433
|
+
status, usageLimit, displayMode: imageKey ? 'screenshot' : 'hidden', imageKey, cliName, locale,
|
|
434
|
+
});
|
|
435
|
+
if (!imageKey) {
|
|
436
|
+
const text = (screenContent ?? '').replace(/[ \t\r\n]+$/, '');
|
|
437
|
+
if (text) {
|
|
438
|
+
const body = truncateContent(text, locale, PRIVATE_SNAPSHOT_TEXT_MAX);
|
|
439
|
+
// Fence must be longer than the longest backtick run in the body, else
|
|
440
|
+
// terminal output containing ``` would break out of the code block.
|
|
441
|
+
const maxRun = (body.match(/`+/g) ?? []).reduce((m, r) => Math.max(m, r.length), 0);
|
|
442
|
+
const fence = '`'.repeat(Math.max(3, maxRun + 1));
|
|
443
|
+
elements.push({ tag: 'markdown', content: `${fence}\n${body}\n${fence}` });
|
|
444
|
+
elements.push({ tag: 'hr' });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
elements.push({
|
|
448
|
+
tag: 'action',
|
|
449
|
+
actions: [
|
|
450
|
+
{
|
|
451
|
+
tag: 'button',
|
|
452
|
+
text: { tag: 'plain_text', content: t('card.btn.open_terminal', undefined, locale) },
|
|
453
|
+
type: 'primary',
|
|
454
|
+
multi_url: { url: terminalUrl, pc_url: terminalUrl, android_url: terminalUrl, ios_url: terminalUrl },
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
tag: 'button',
|
|
458
|
+
text: { tag: 'plain_text', content: t('card.btn.get_write_link', undefined, locale) },
|
|
459
|
+
type: 'default',
|
|
460
|
+
value: { action: 'get_write_link', ...actionBase },
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
tag: 'button',
|
|
464
|
+
text: { tag: 'plain_text', content: t('card.btn.close_session', undefined, locale) },
|
|
465
|
+
type: 'danger',
|
|
466
|
+
value: { action: 'close', ...actionBase },
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
});
|
|
470
|
+
elements.push({
|
|
471
|
+
tag: 'note',
|
|
472
|
+
elements: [{ tag: 'lark_md', content: t('card.private.snapshot_note', undefined, locale) }],
|
|
473
|
+
});
|
|
474
|
+
const card = {
|
|
475
|
+
config: { wide_screen_mode: true },
|
|
476
|
+
header: {
|
|
477
|
+
title: { tag: 'plain_text', content: `🔒 ${cliName} · ${escapeMd(title)} — ${streamStatusLabel(status, usageLimit, locale)}` },
|
|
478
|
+
template: STREAM_TEMPLATE_MAP[displayStatus],
|
|
316
479
|
},
|
|
317
480
|
elements,
|
|
318
481
|
};
|
|
@@ -385,19 +548,22 @@ export function buildGrantCard(o, locale) {
|
|
|
385
548
|
? t('card.grant.body_request', { name: escapeMd(o.requesterName), owner: o.ownerOpenId }, locale)
|
|
386
549
|
: t('card.grant.body_owner', { name: escapeMd(o.requesterName), owner: o.ownerOpenId }, locale);
|
|
387
550
|
const v = { target_open_id: o.requesterOpenId, chat_id: o.chatId, nonce: o.nonce };
|
|
551
|
+
// 「全局授权对话」只在 owner 主动发卡时出现:owner 一眼明确要给全局;request 模式(成员
|
|
552
|
+
// 自助申请)只提供「本群」,避免成员把自己申请到全局。两个授权按钮都是 talk-only。
|
|
553
|
+
const grantButtons = [
|
|
554
|
+
{ tag: 'button', type: 'primary', text: { tag: 'plain_text', content: t('card.grant.btn_chat', undefined, locale) }, value: { action: 'grant_chat', ...v } },
|
|
555
|
+
];
|
|
556
|
+
if (o.mode === 'owner') {
|
|
557
|
+
grantButtons.push({ tag: 'button', type: 'default', text: { tag: 'plain_text', content: t('card.grant.btn_global', undefined, locale) }, value: { action: 'grant_global', ...v } });
|
|
558
|
+
}
|
|
559
|
+
grantButtons.push({ tag: 'button', type: 'danger', text: { tag: 'plain_text', content: t('card.grant.btn_deny', undefined, locale) }, value: { action: 'grant_deny', ...v } });
|
|
388
560
|
const card = {
|
|
389
561
|
config: { wide_screen_mode: true },
|
|
390
562
|
header: { template: 'orange', title: { tag: 'plain_text', content: t('card.grant.title', undefined, locale) } },
|
|
391
563
|
elements: [
|
|
392
564
|
{ tag: 'div', text: { tag: 'lark_md', content: body } },
|
|
393
565
|
{ tag: 'hr' },
|
|
394
|
-
{
|
|
395
|
-
tag: 'action',
|
|
396
|
-
actions: [
|
|
397
|
-
{ tag: 'button', type: 'primary', text: { tag: 'plain_text', content: t('card.grant.btn_chat', undefined, locale) }, value: { action: 'grant_chat', ...v } },
|
|
398
|
-
{ tag: 'button', type: 'danger', text: { tag: 'plain_text', content: t('card.grant.btn_deny', undefined, locale) }, value: { action: 'grant_deny', ...v } },
|
|
399
|
-
],
|
|
400
|
-
},
|
|
566
|
+
{ tag: 'action', actions: grantButtons },
|
|
401
567
|
{ tag: 'note', elements: [{ tag: 'lark_md', content: t('card.grant.note', undefined, locale) }] },
|
|
402
568
|
],
|
|
403
569
|
};
|
|
@@ -570,6 +736,270 @@ function formatDuration(ms) {
|
|
|
570
736
|
const d = Math.floor(h / 24);
|
|
571
737
|
return `${d}d${h % 24}h`;
|
|
572
738
|
}
|
|
739
|
+
function relayPickerTypeTag(mode, locale) {
|
|
740
|
+
switch (mode) {
|
|
741
|
+
case 'p2p': return t('card.relay.type_p2p', undefined, locale);
|
|
742
|
+
case 'topic': return t('card.relay.type_topic', undefined, locale);
|
|
743
|
+
default: return t('card.relay.type_group', undefined, locale); // 'group' or undefined
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const RELAY_PICKER_PAGE_SIZE = 5;
|
|
747
|
+
const RELAY_SEARCH_FIELD = 'search';
|
|
748
|
+
/**
|
|
749
|
+
* Match against title / chatLabel / workingDir / cliId. Case-insensitive
|
|
750
|
+
* substring. Empty / whitespace query matches everything.
|
|
751
|
+
*/
|
|
752
|
+
function relayPickerFilter(entries, query) {
|
|
753
|
+
const q = (query ?? '').trim().toLowerCase();
|
|
754
|
+
if (!q)
|
|
755
|
+
return entries;
|
|
756
|
+
return entries.filter((e) => {
|
|
757
|
+
const haystack = [e.title, e.chatLabel, e.workingDir, e.cliId]
|
|
758
|
+
.filter(Boolean)
|
|
759
|
+
.join(' ')
|
|
760
|
+
.toLowerCase();
|
|
761
|
+
return haystack.includes(q);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Card listing the operator's relayable sessions, paginated 5 per page with
|
|
766
|
+
* a search box at the top and a confirm button at the bottom. Layout:
|
|
767
|
+
*
|
|
768
|
+
* ┌──────────────────────────────────────┐
|
|
769
|
+
* │ 📋 选择要接力的会话 │ header
|
|
770
|
+
* ├──────────────────────────────────────┤
|
|
771
|
+
* │ 🔍 [______________] [搜索] │ form: input + submit button
|
|
772
|
+
* ├──────────────────────────────────────┤
|
|
773
|
+
* │ [interactive_container 1] │ current page (≤5 cards),
|
|
774
|
+
* │ [interactive_container 2] │ each clickable for selection
|
|
775
|
+
* │ ... │
|
|
776
|
+
* ├──────────────────────────────────────┤
|
|
777
|
+
* │ [← 上一页] 1 / 4 [下一页 →] │ paginator row
|
|
778
|
+
* ├──────────────────────────────────────┤
|
|
779
|
+
* │ [确认接力到本群] │ primary button (only when
|
|
780
|
+
* │ │ a selected session is on
|
|
781
|
+
* │ │ the current filtered set)
|
|
782
|
+
* └──────────────────────────────────────┘
|
|
783
|
+
*
|
|
784
|
+
* State (search / page / selected) is propagated entirely via the value
|
|
785
|
+
* objects on each button and container — Lark cards are stateless, so any
|
|
786
|
+
* server-side re-render must reconstruct from what the click sent us.
|
|
787
|
+
* That's why every interactive value here carries `search`, `page`,
|
|
788
|
+
* `target_chat_id`, `root_id`.
|
|
789
|
+
*
|
|
790
|
+
* Note: typing into the search box without clicking 搜索 does NOT update
|
|
791
|
+
* the in-callback state — container/paginator clicks use whatever search
|
|
792
|
+
* was applied at card-render time. To apply a new filter, click 搜索.
|
|
793
|
+
*/
|
|
794
|
+
export function buildRelayPickerCard(entries, targetChatId, targetRootMessageId, invokerOpenId, locale, state) {
|
|
795
|
+
const searchQuery = state?.searchQuery ?? '';
|
|
796
|
+
const requestedPage = state?.page ?? 0;
|
|
797
|
+
const selectedSessionId = state?.selectedSessionId;
|
|
798
|
+
const elements = [];
|
|
799
|
+
// ─── Filter & paginate ───────────────────────────────────────────────
|
|
800
|
+
const filtered = relayPickerFilter(entries, searchQuery);
|
|
801
|
+
const totalPages = Math.max(1, Math.ceil(filtered.length / RELAY_PICKER_PAGE_SIZE));
|
|
802
|
+
const page = Math.min(Math.max(0, requestedPage), totalPages - 1);
|
|
803
|
+
const start = page * RELAY_PICKER_PAGE_SIZE;
|
|
804
|
+
const visible = filtered.slice(start, start + RELAY_PICKER_PAGE_SIZE);
|
|
805
|
+
// Common state object carried by every interactive value so re-renders
|
|
806
|
+
// can reconstruct what the user was looking at. `invoker_open_id` pins the
|
|
807
|
+
// card to the user who originally summoned it — card-handler refuses
|
|
808
|
+
// re-render / confirm clicks from anyone else, so the menu doesn't get
|
|
809
|
+
// silently swapped to a passer-by's session list.
|
|
810
|
+
const stateValue = {
|
|
811
|
+
target_chat_id: targetChatId,
|
|
812
|
+
root_id: targetRootMessageId,
|
|
813
|
+
invoker_open_id: invokerOpenId,
|
|
814
|
+
search: searchQuery,
|
|
815
|
+
page,
|
|
816
|
+
selected: selectedSessionId ?? '',
|
|
817
|
+
};
|
|
818
|
+
// ─── Search box ─────────────────────────────────────────────────────
|
|
819
|
+
// v2 input supports `behaviors` natively — pressing Enter or clicking
|
|
820
|
+
// the built-in submit icon inside the input fires the callback. No
|
|
821
|
+
// separate 搜索 button needed (王皓 reported that button rendered as
|
|
822
|
+
// "..." due to cramped column width; the auto-submit input avoids the
|
|
823
|
+
// problem entirely AND removes the manual click).
|
|
824
|
+
//
|
|
825
|
+
// On submit the callback delivers `action.input_value` (the typed
|
|
826
|
+
// string) and `action.value` (our state object). card-handler reads
|
|
827
|
+
// input_value to update search, resets page to 0 and clears selection.
|
|
828
|
+
elements.push({
|
|
829
|
+
tag: 'input',
|
|
830
|
+
name: RELAY_SEARCH_FIELD,
|
|
831
|
+
placeholder: { tag: 'plain_text', content: t('card.relay.search_placeholder', undefined, locale) },
|
|
832
|
+
default_value: searchQuery,
|
|
833
|
+
width: 'fill',
|
|
834
|
+
behaviors: [
|
|
835
|
+
{
|
|
836
|
+
type: 'callback',
|
|
837
|
+
value: { action: 'relay_search', ...stateValue, selected: '' /* new search → reset selection */ },
|
|
838
|
+
},
|
|
839
|
+
],
|
|
840
|
+
});
|
|
841
|
+
elements.push({ tag: 'hr' });
|
|
842
|
+
// ─── Empty / no-match notice ────────────────────────────────────────
|
|
843
|
+
if (entries.length === 0) {
|
|
844
|
+
elements.push({ tag: 'markdown', content: t('card.relay.empty', undefined, locale) });
|
|
845
|
+
return JSON.stringify(wrapCard(elements, locale));
|
|
846
|
+
}
|
|
847
|
+
if (filtered.length === 0) {
|
|
848
|
+
elements.push({
|
|
849
|
+
tag: 'markdown',
|
|
850
|
+
content: t('card.relay.empty_filtered', { query: searchQuery }, locale),
|
|
851
|
+
});
|
|
852
|
+
return JSON.stringify(wrapCard(elements, locale));
|
|
853
|
+
}
|
|
854
|
+
// ─── Session cards (current page) ───────────────────────────────────
|
|
855
|
+
const p2pLocationLabel = t('card.relay.type_p2p', undefined, locale);
|
|
856
|
+
const labelType = t('card.relay.field_type', undefined, locale);
|
|
857
|
+
const labelLocation = t('card.relay.field_location', undefined, locale);
|
|
858
|
+
const labelTime = t('card.relay.field_time', undefined, locale);
|
|
859
|
+
const selectedTag = t('card.relay.selected_tag', undefined, locale);
|
|
860
|
+
const hasValidSelection = !!selectedSessionId && filtered.some(e => e.sessionId === selectedSessionId);
|
|
861
|
+
visible.forEach((e) => {
|
|
862
|
+
const isSelected = e.sessionId === selectedSessionId;
|
|
863
|
+
const typeTag = relayPickerTypeTag(e.chatMode, locale);
|
|
864
|
+
const locationLine = e.chatMode === 'p2p' ? p2pLocationLabel : e.chatLabel;
|
|
865
|
+
const titleLine = isSelected
|
|
866
|
+
? `**✅ ${escapeMd(e.title)}** \`${selectedTag}\``
|
|
867
|
+
: `**${escapeMd(e.title)}**`;
|
|
868
|
+
const lines = [
|
|
869
|
+
titleLine,
|
|
870
|
+
`${labelType}: ${typeTag}`,
|
|
871
|
+
`${labelLocation}: ${escapeMd(locationLine)}`,
|
|
872
|
+
];
|
|
873
|
+
if (e.lastMessageAt) {
|
|
874
|
+
lines.push(`${labelTime}: ${formatDuration(Date.now() - e.lastMessageAt)}`);
|
|
875
|
+
}
|
|
876
|
+
elements.push({
|
|
877
|
+
tag: 'interactive_container',
|
|
878
|
+
width: 'fill',
|
|
879
|
+
padding: '8px 12px',
|
|
880
|
+
background_style: isSelected ? 'laser' : 'default',
|
|
881
|
+
has_border: true,
|
|
882
|
+
border_color: isSelected ? 'blue-500' : 'grey-200',
|
|
883
|
+
corner_radius: '8px',
|
|
884
|
+
behaviors: [
|
|
885
|
+
{
|
|
886
|
+
type: 'callback',
|
|
887
|
+
value: { action: 'relay_select', session_id: e.sessionId, ...stateValue },
|
|
888
|
+
},
|
|
889
|
+
],
|
|
890
|
+
elements: [{ tag: 'markdown', content: lines.join('\n') }],
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
// ─── Paginator (only when more than one page) ───────────────────────
|
|
894
|
+
if (totalPages > 1) {
|
|
895
|
+
elements.push({
|
|
896
|
+
tag: 'column_set',
|
|
897
|
+
flex_mode: 'none',
|
|
898
|
+
horizontal_spacing: 'default',
|
|
899
|
+
columns: [
|
|
900
|
+
{
|
|
901
|
+
tag: 'column',
|
|
902
|
+
width: 'weighted',
|
|
903
|
+
weight: 1,
|
|
904
|
+
vertical_align: 'center',
|
|
905
|
+
elements: [
|
|
906
|
+
{
|
|
907
|
+
tag: 'button',
|
|
908
|
+
text: { tag: 'plain_text', content: t('card.relay.btn_prev_page', undefined, locale) },
|
|
909
|
+
type: 'default',
|
|
910
|
+
disabled: page === 0,
|
|
911
|
+
behaviors: [
|
|
912
|
+
{
|
|
913
|
+
type: 'callback',
|
|
914
|
+
value: { action: 'relay_page', ...stateValue, page: Math.max(0, page - 1) },
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
tag: 'column',
|
|
922
|
+
width: 'weighted',
|
|
923
|
+
weight: 2,
|
|
924
|
+
vertical_align: 'center',
|
|
925
|
+
elements: [
|
|
926
|
+
{
|
|
927
|
+
tag: 'markdown',
|
|
928
|
+
text_align: 'center',
|
|
929
|
+
content: t('card.relay.page_indicator', { current: page + 1, total: totalPages }, locale),
|
|
930
|
+
},
|
|
931
|
+
],
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
tag: 'column',
|
|
935
|
+
width: 'weighted',
|
|
936
|
+
weight: 1,
|
|
937
|
+
vertical_align: 'center',
|
|
938
|
+
elements: [
|
|
939
|
+
{
|
|
940
|
+
tag: 'button',
|
|
941
|
+
text: { tag: 'plain_text', content: t('card.relay.btn_next_page', undefined, locale) },
|
|
942
|
+
type: 'default',
|
|
943
|
+
disabled: page === totalPages - 1,
|
|
944
|
+
behaviors: [
|
|
945
|
+
{
|
|
946
|
+
type: 'callback',
|
|
947
|
+
value: { action: 'relay_page', ...stateValue, page: Math.min(totalPages - 1, page + 1) },
|
|
948
|
+
},
|
|
949
|
+
],
|
|
950
|
+
},
|
|
951
|
+
],
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
// ─── Confirm button or hint ─────────────────────────────────────────
|
|
957
|
+
elements.push({ tag: 'hr' });
|
|
958
|
+
if (hasValidSelection) {
|
|
959
|
+
elements.push({
|
|
960
|
+
tag: 'column_set',
|
|
961
|
+
flex_mode: 'none',
|
|
962
|
+
columns: [
|
|
963
|
+
{
|
|
964
|
+
tag: 'column',
|
|
965
|
+
width: 'weighted',
|
|
966
|
+
weight: 1,
|
|
967
|
+
elements: [
|
|
968
|
+
{
|
|
969
|
+
tag: 'button',
|
|
970
|
+
text: { tag: 'plain_text', content: t('card.relay.btn_confirm', undefined, locale) },
|
|
971
|
+
type: 'primary',
|
|
972
|
+
behaviors: [
|
|
973
|
+
{
|
|
974
|
+
type: 'callback',
|
|
975
|
+
value: { action: 'relay_confirm', session_id: selectedSessionId, ...stateValue },
|
|
976
|
+
},
|
|
977
|
+
],
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
},
|
|
981
|
+
],
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
elements.push({
|
|
986
|
+
tag: 'markdown',
|
|
987
|
+
content: `<font color='grey'>${t('card.relay.hint_pick_first', undefined, locale)}</font>`,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return JSON.stringify(wrapCard(elements, locale));
|
|
991
|
+
}
|
|
992
|
+
function wrapCard(elements, locale) {
|
|
993
|
+
return {
|
|
994
|
+
schema: '2.0',
|
|
995
|
+
config: { update_multi: true },
|
|
996
|
+
header: {
|
|
997
|
+
title: { tag: 'plain_text', content: t('card.relay.title', undefined, locale) },
|
|
998
|
+
template: 'blue',
|
|
999
|
+
},
|
|
1000
|
+
body: { direction: 'vertical', elements },
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
573
1003
|
export function buildAdoptSelectCard(sessions, rootMessageId, locale) {
|
|
574
1004
|
const unknownUptime = t('card.adopt.uptime_unknown', undefined, locale);
|
|
575
1005
|
const options = sessions.map((s) => {
|