botmux 2.47.0 → 2.47.2
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 +34 -17
- 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 +29 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +787 -312
- 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 +224 -60
- 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 +29 -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 +233 -51
- 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
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
import { writeFileSync, readFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import { config } from '../config.js';
|
|
5
|
+
import { jsonRes } from './workflow-api.js';
|
|
6
|
+
import { buildTeamRoster } from '../services/team-roster.js';
|
|
7
|
+
import { buildFederatedRoster } from '../services/federation-roster.js';
|
|
8
|
+
import { getDeploymentIdentity, setDeploymentName } from '../services/deployment-identity.js';
|
|
9
|
+
import { addMembership, listMemberships, removeMembership } from '../services/federation-membership-store.js';
|
|
10
|
+
import { ensureDefaultTeam, DEFAULT_TEAM_ID, listTeams, createTeam, deleteTeam, getTeam } from '../services/team-store.js';
|
|
11
|
+
import { createInvite, deleteInvitesForTeam } from '../services/invite-store.js';
|
|
12
|
+
import { removeTeamFederation, removeDeployment } from '../services/federation-store.js';
|
|
13
|
+
import { loadBotConfigs, registerBot, getBot } from '../bot-registry.js';
|
|
14
|
+
import { setBotCapability, clearBotCapability } from '../services/bot-profile-store.js';
|
|
15
|
+
import { setBotOwner } from '../services/bot-owner-store.js';
|
|
16
|
+
import { setDeploymentOwner } from '../services/deployment-identity.js';
|
|
17
|
+
import { createPairing, getPairingStatus, consumePairing } from '../services/pairing-store.js';
|
|
18
|
+
import { resolveAllowedUsersWithMap, resolveUserUnionId } from '../im/lark/client.js';
|
|
19
|
+
import { fetchWithTimeout, hubError, orchestrateFederatedGroup } from './federated-group-core.js';
|
|
20
|
+
/** Resolve this deployment's owner identity from bots.json `allowedUsers` using
|
|
21
|
+
* each bot's OWN app credentials (no /pair, no shared pairings.json — immune to
|
|
22
|
+
* the dataDir-split that broke /pair). Walks bots with allowedUsers in order;
|
|
23
|
+
* returns the first bot's distinct resolved {unionId,name} (skips bots that
|
|
24
|
+
* resolve to nothing, so one mis-config doesn't hide the rest). The UI auto-binds
|
|
25
|
+
* when there's exactly one, else lets the owner pick. */
|
|
26
|
+
export async function resolveOwnerCandidatesFromAllowedUsers(d = {}) {
|
|
27
|
+
const loadConfigs = d.configs ?? loadBotConfigs;
|
|
28
|
+
const ensureClient = d.ensureClient ?? ((cfg) => { try {
|
|
29
|
+
getBot(cfg.larkAppId);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
registerBot(cfg);
|
|
33
|
+
} });
|
|
34
|
+
const resolveAllowed = d.resolveAllowed ?? (async (id, a) => (await resolveAllowedUsersWithMap(id, a)).resolved);
|
|
35
|
+
const resolveUnion = d.resolveUnion ?? resolveUserUnionId;
|
|
36
|
+
let configs = [];
|
|
37
|
+
try {
|
|
38
|
+
configs = loadConfigs();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
for (const cfg of configs) {
|
|
44
|
+
const allowed = cfg.allowedUsers ?? [];
|
|
45
|
+
if (allowed.length === 0)
|
|
46
|
+
continue;
|
|
47
|
+
try {
|
|
48
|
+
ensureClient(cfg);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
let openIds = [];
|
|
54
|
+
try {
|
|
55
|
+
openIds = await resolveAllowed(cfg.larkAppId, allowed);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
} // one bot failing → try next
|
|
60
|
+
const byUnion = new Map();
|
|
61
|
+
for (const oid of openIds) {
|
|
62
|
+
if (!oid.startsWith('ou_'))
|
|
63
|
+
continue;
|
|
64
|
+
const u = await resolveUnion(cfg.larkAppId, oid);
|
|
65
|
+
if (u.unionId)
|
|
66
|
+
byUnion.set(u.unionId, { unionId: u.unionId, name: u.name ?? '' });
|
|
67
|
+
}
|
|
68
|
+
if (byUnion.size > 0)
|
|
69
|
+
return [...byUnion.values()];
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const MAX_ROLE_BYTES = 4 * 1024;
|
|
74
|
+
/** Team-level role file at {dataDir}/team-roles/{larkAppId}.md (matches role-resolver). */
|
|
75
|
+
function teamRolePath(dataDir, larkAppId) {
|
|
76
|
+
return join(dataDir, 'team-roles', `${larkAppId}.md`);
|
|
77
|
+
}
|
|
78
|
+
function writeTeamRole(dataDir, larkAppId, content) {
|
|
79
|
+
const fp = teamRolePath(dataDir, larkAppId);
|
|
80
|
+
mkdirSync(dirname(fp), { recursive: true });
|
|
81
|
+
let out = content.trim();
|
|
82
|
+
while (Buffer.byteLength(out, 'utf-8') > MAX_ROLE_BYTES)
|
|
83
|
+
out = out.slice(0, -1);
|
|
84
|
+
writeFileSync(fp, out, 'utf-8');
|
|
85
|
+
}
|
|
86
|
+
async function readBody(req, maxBytes = 64 * 1024) {
|
|
87
|
+
const chunks = [];
|
|
88
|
+
let total = 0;
|
|
89
|
+
for await (const c of req) {
|
|
90
|
+
const b = c;
|
|
91
|
+
total += b.length;
|
|
92
|
+
if (total > maxBytes)
|
|
93
|
+
throw new Error('too_large');
|
|
94
|
+
chunks.push(b);
|
|
95
|
+
}
|
|
96
|
+
if (chunks.length === 0)
|
|
97
|
+
return {};
|
|
98
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
99
|
+
}
|
|
100
|
+
/** Normalize a hub base URL (strip trailing slash); only http/https allowed. */
|
|
101
|
+
function normalizeHubUrl(raw) {
|
|
102
|
+
const s = String(raw ?? '').trim().replace(/\/+$/, '');
|
|
103
|
+
if (!/^https?:\/\/.+/i.test(s))
|
|
104
|
+
return null;
|
|
105
|
+
return s;
|
|
106
|
+
}
|
|
107
|
+
/** bots.json (config) order of larkAppIds, so federated rosters match the dashboard. */
|
|
108
|
+
function botConfigOrder() {
|
|
109
|
+
try {
|
|
110
|
+
return loadBotConfigs().map(b => b.larkAppId);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** This deployment's bots, in the shape the hub federates (bots.json order).
|
|
117
|
+
* Prefer the live daemon registry (authoritative) over bots-info.json. */
|
|
118
|
+
function localBots(dataDir, live) {
|
|
119
|
+
return buildTeamRoster(dataDir, undefined, undefined, live).bots.map(b => ({
|
|
120
|
+
larkAppId: b.larkAppId,
|
|
121
|
+
botName: b.name,
|
|
122
|
+
cliId: b.cliId,
|
|
123
|
+
capability: b.capability,
|
|
124
|
+
hasTeamRole: b.hasTeamRole,
|
|
125
|
+
// owner (union_id+name) federated so the hub can pull owners into 拉群
|
|
126
|
+
ownerUnionId: b.owner?.unionId,
|
|
127
|
+
ownerName: b.owner?.name,
|
|
128
|
+
// botUnionId: not needed — 拉群 adds bots by app_id (larkAppId), see docs
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
/** Push this deployment's current bots to every joined hub. Best-effort. */
|
|
132
|
+
export async function syncAllMemberships(dataDir, fetcher = fetch, live) {
|
|
133
|
+
const bots = localBots(dataDir, live);
|
|
134
|
+
const me = getDeploymentIdentity(dataDir);
|
|
135
|
+
let synced = 0, failed = 0;
|
|
136
|
+
for (const m of listMemberships(dataDir)) {
|
|
137
|
+
try {
|
|
138
|
+
const r = await fetchWithTimeout(fetcher, `${m.hubUrl}/api/federation/sync`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${m.syncToken}` },
|
|
141
|
+
body: JSON.stringify({ syncToken: m.syncToken, bots, ownerUnionId: me.ownerUnionId, ownerName: me.ownerName, name: me.name }),
|
|
142
|
+
});
|
|
143
|
+
if (r.ok)
|
|
144
|
+
synced++;
|
|
145
|
+
else
|
|
146
|
+
failed++;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
failed++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { synced, failed };
|
|
153
|
+
}
|
|
154
|
+
export async function handleFederationSpokeApi(req, res, url, deps = {}) {
|
|
155
|
+
const path = url.pathname;
|
|
156
|
+
const LOCAL = new Set(['/api/team/local', '/api/team/local-invite', '/api/team/rename-deployment', '/api/team/federated-group',
|
|
157
|
+
'/api/team/identity/start', '/api/team/identity/status', '/api/team/identity/consume', '/api/team/identity/auto-bind',
|
|
158
|
+
'/api/team/hosted']);
|
|
159
|
+
const REMOTE = new Set(['/api/team/join-remote', '/api/team/remote-roster', '/api/team/sync-remote', '/api/team/leave-remote', '/api/team/remote-group']);
|
|
160
|
+
const localBotEdit = path.match(/^\/api\/team\/local-bots\/([^/]+)\/(capability|role)$/);
|
|
161
|
+
const memberDel = path.match(/^\/api\/team\/hosted\/([^/]+)\/members\/([^/]+)$/);
|
|
162
|
+
const hostedDel = path.match(/^\/api\/team\/hosted\/([^/]+)$/);
|
|
163
|
+
if (!LOCAL.has(path) && !REMOTE.has(path) && !localBotEdit && !memberDel && !hostedDel)
|
|
164
|
+
return false;
|
|
165
|
+
const dataDir = deps.dataDir ?? config.session.dataDir;
|
|
166
|
+
const fetcher = deps.fetcher ?? fetch;
|
|
167
|
+
const method = req.method ?? 'GET';
|
|
168
|
+
const live = deps.liveBots?.(); // live registry bots (authoritative over bots-info.json)
|
|
169
|
+
// Edit a LOCAL bot's capability label / team role (federated bots are read-only
|
|
170
|
+
// — they're owned by another deployment and synced over). Local bots only.
|
|
171
|
+
if (localBotEdit) {
|
|
172
|
+
const larkAppId = decodeURIComponent(localBotEdit[1]);
|
|
173
|
+
const field = localBotEdit[2];
|
|
174
|
+
const localIds = new Set(buildTeamRoster(dataDir, undefined, undefined, live).bots.map(b => b.larkAppId));
|
|
175
|
+
if (!localIds.has(larkAppId)) {
|
|
176
|
+
jsonRes(res, 404, { ok: false, error: 'not_a_local_bot' });
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
if (field === 'role' && method === 'GET') {
|
|
180
|
+
const fp = teamRolePath(dataDir, larkAppId);
|
|
181
|
+
jsonRes(res, 200, { ok: true, role: existsSync(fp) ? readFileSync(fp, 'utf-8') : '' });
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (method === 'PUT') {
|
|
185
|
+
let body;
|
|
186
|
+
try {
|
|
187
|
+
body = await readBody(req);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (field === 'capability') {
|
|
194
|
+
const cap = String(body?.capability ?? '').trim();
|
|
195
|
+
if (cap)
|
|
196
|
+
setBotCapability(dataDir, larkAppId, cap);
|
|
197
|
+
else
|
|
198
|
+
clearBotCapability(dataDir, larkAppId);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const role = String(body?.role ?? '').trim();
|
|
202
|
+
if (role)
|
|
203
|
+
writeTeamRole(dataDir, larkAppId, role);
|
|
204
|
+
else {
|
|
205
|
+
try {
|
|
206
|
+
unlinkSync(teamRolePath(dataDir, larkAppId));
|
|
207
|
+
}
|
|
208
|
+
catch { /* already gone */ }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
jsonRes(res, 200, { ok: true });
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
jsonRes(res, 405, { ok: false, error: 'method_not_allowed' });
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
// Cross-deployment 拉群: create a Feishu group with selected bots (local +
|
|
218
|
+
// federated). Bots are added by larkAppId (app_id) — the creator is picked
|
|
219
|
+
// from local online bots; federated bots (other apps, same tenant) are added
|
|
220
|
+
// as members. See docs/federation-design.md.
|
|
221
|
+
if (path === '/api/team/federated-group' && method === 'POST') {
|
|
222
|
+
if (!deps.createTeamGroup) {
|
|
223
|
+
jsonRes(res, 501, { ok: false, error: 'group_create_unavailable' });
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
let body;
|
|
227
|
+
try {
|
|
228
|
+
body = await readBody(req);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
const larkAppIds = Array.isArray(body?.larkAppIds) ? body.larkAppIds.filter((x) => typeof x === 'string') : [];
|
|
235
|
+
const name = String(body?.name ?? '').trim() || '协作群';
|
|
236
|
+
// Operator = THIS deployment's bound owner (local initiation). Hub-derived
|
|
237
|
+
// operator (for spoke-initiated /api/federation/group) is handled separately.
|
|
238
|
+
const operatorUnionId = getDeploymentIdentity(dataDir).ownerUnionId;
|
|
239
|
+
const teamId = String(body?.teamId ?? '').trim() || DEFAULT_TEAM_ID; // which hosted team's roster gates the selection
|
|
240
|
+
// Validate the team exists — buildFederatedRoster falls back to the default
|
|
241
|
+
// team for an unknown teamId, so a stale/deleted teamId must be refused here
|
|
242
|
+
// (else 拉群 would silently use the default team's roster). ensureDefaultTeam
|
|
243
|
+
// first so the implicit default team always passes.
|
|
244
|
+
ensureDefaultTeam(dataDir);
|
|
245
|
+
if (!getTeam(dataDir, teamId)) {
|
|
246
|
+
jsonRes(res, 404, { ok: false, error: 'team_not_found' });
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
// Operator must end up in the group; only a bot in the operator's OWN
|
|
250
|
+
// deployment+scope can add them. If this deployment HAS online bots but the
|
|
251
|
+
// user selected none, the group would be built by a remote bot that can't add
|
|
252
|
+
// the operator → make them pick one. (If there are no local online bots at
|
|
253
|
+
// all, fall through to delegate-build — degenerate case, operator may not be
|
|
254
|
+
// addable, surfaced as missingOperatorIdentity.)
|
|
255
|
+
const localOnline = new Set((live ?? []).map(b => b.larkAppId));
|
|
256
|
+
if (localOnline.size > 0 && !larkAppIds.some(id => localOnline.has(id))) {
|
|
257
|
+
jsonRes(res, 400, { ok: false, error: 'no_local_online_bot' });
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
const out = await orchestrateFederatedGroup(dataDir, { name, larkAppIds, operatorUnionId, requestId: randomUUID(), teamId }, { createTeamGroup: deps.createTeamGroup, fetcher, live });
|
|
261
|
+
jsonRes(res, out.status, out.body);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
// ── Bind THIS deployment's owner Feishu identity (reuse /pair) ─────────────
|
|
265
|
+
// Owner sends `/pair <code>` to one of our bots; we capture their union_id so
|
|
266
|
+
// 拉群 can pull the operator into groups, and own our bots (no-steal).
|
|
267
|
+
if (path === '/api/team/identity/start' && method === 'POST') {
|
|
268
|
+
const p = createPairing(dataDir, 5 * 60 * 1000);
|
|
269
|
+
jsonRes(res, 200, { ok: true, pairingId: p.pairingId, code: p.code, browserToken: p.browserToken, expiresAt: p.expiresAt });
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
if (path === '/api/team/identity/status' && method === 'POST') {
|
|
273
|
+
let body;
|
|
274
|
+
try {
|
|
275
|
+
body = await readBody(req);
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
const v = getPairingStatus(dataDir, String(body?.pairingId ?? ''), String(body?.browserToken ?? ''));
|
|
282
|
+
if (v.status === 'not_found') {
|
|
283
|
+
jsonRes(res, 200, { ok: true, status: 'not_found' });
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
jsonRes(res, 200, { ok: true, status: v.status, name: v.status === 'claimed' ? v.claimedBy.name : undefined });
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
if (path === '/api/team/identity/consume' && method === 'POST') {
|
|
290
|
+
let body;
|
|
291
|
+
try {
|
|
292
|
+
body = await readBody(req);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
const c = consumePairing(dataDir, String(body?.pairingId ?? ''), String(body?.browserToken ?? ''));
|
|
299
|
+
if (!c.ok) {
|
|
300
|
+
jsonRes(res, 409, { ok: false, error: c.reason });
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
const owner = { unionId: c.claimedBy.unionId, name: c.claimedBy.name };
|
|
304
|
+
setDeploymentOwner(dataDir, owner);
|
|
305
|
+
// Own THIS deployment's bots (no-steal: only unassigned; keep manual owners).
|
|
306
|
+
for (const b of buildTeamRoster(dataDir, undefined, undefined, live).bots) {
|
|
307
|
+
setBotOwner(dataDir, b.larkAppId, { unionId: owner.unionId, name: owner.name }, { override: false });
|
|
308
|
+
}
|
|
309
|
+
// Push the new owner identity to every joined hub NOW (best-effort) so a 拉群
|
|
310
|
+
// immediately after binding can derive the operator — don't wait for the
|
|
311
|
+
// 2-min periodic sync, otherwise #3 (operator not invited) reproduces.
|
|
312
|
+
const sync = await syncAllMemberships(dataDir, fetcher, live).catch(() => ({ synced: 0, failed: 0 }));
|
|
313
|
+
jsonRes(res, 200, { ok: true, owner, hubsSynced: sync.synced, hubsFailed: sync.failed });
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
// ── Bind owner WITHOUT /pair: resolve allowedUsers via the bots' own creds ──
|
|
317
|
+
// No code to copy, no shared pairings.json (immune to dataDir-split). If the
|
|
318
|
+
// bots' allowedUsers resolve to exactly one person, bind them; if several,
|
|
319
|
+
// return candidates for the owner to pick (re-POST with {unionId}).
|
|
320
|
+
if (path === '/api/team/identity/auto-bind' && method === 'POST') {
|
|
321
|
+
let body;
|
|
322
|
+
try {
|
|
323
|
+
body = await readBody(req);
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
const candidates = await (deps.ownerCandidates ?? resolveOwnerCandidatesFromAllowedUsers)();
|
|
330
|
+
if (candidates.length === 0) {
|
|
331
|
+
jsonRes(res, 200, { ok: false, error: 'no_candidates' });
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
const want = String(body?.unionId ?? '').trim();
|
|
335
|
+
const chosen = want ? candidates.find(c => c.unionId === want) : (candidates.length === 1 ? candidates[0] : undefined);
|
|
336
|
+
if (!chosen) {
|
|
337
|
+
jsonRes(res, 200, { ok: true, needChoice: true, candidates });
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
const owner = { unionId: chosen.unionId, name: chosen.name };
|
|
341
|
+
setDeploymentOwner(dataDir, owner);
|
|
342
|
+
for (const b of buildTeamRoster(dataDir, undefined, undefined, live).bots) {
|
|
343
|
+
setBotOwner(dataDir, b.larkAppId, { unionId: owner.unionId, name: owner.name }, { override: false });
|
|
344
|
+
}
|
|
345
|
+
const sync = await syncAllMemberships(dataDir, fetcher, live).catch(() => ({ synced: 0, failed: 0 }));
|
|
346
|
+
jsonRes(res, 200, { ok: true, owner, hubsSynced: sync.synced, hubsFailed: sync.failed });
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
// ── Initiate 拉群 on a JOINED remote team (spoke → hub orchestrates) ────────
|
|
350
|
+
if (path === '/api/team/remote-group' && method === 'POST') {
|
|
351
|
+
let body;
|
|
352
|
+
try {
|
|
353
|
+
body = await readBody(req);
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const hubUrl = normalizeHubUrl(body?.hubUrl);
|
|
360
|
+
const teamId = String(body?.teamId ?? '').trim();
|
|
361
|
+
const larkAppIds = Array.isArray(body?.larkAppIds) ? body.larkAppIds.filter((x) => typeof x === 'string') : [];
|
|
362
|
+
const name = String(body?.name ?? '').trim() || '协作群';
|
|
363
|
+
if (!hubUrl || !teamId) {
|
|
364
|
+
jsonRes(res, 400, { ok: false, error: 'bad_request' });
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
if (larkAppIds.length === 0) {
|
|
368
|
+
jsonRes(res, 400, { ok: false, error: 'no_bots_selected' });
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
const m = listMemberships(dataDir).find(x => x.hubUrl === hubUrl && x.teamId === teamId);
|
|
372
|
+
if (!m) {
|
|
373
|
+
jsonRes(res, 404, { ok: false, error: 'not_a_member' });
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
// Push our latest owner + bots to the hub BEFORE it orchestrates, so the
|
|
377
|
+
// operator union_id (hub-derived from our synced record) is fresh — otherwise
|
|
378
|
+
// a 拉群 right after binding could still see a stale/empty owner (the periodic
|
|
379
|
+
// sync is every 2 min) → operator not invited.
|
|
380
|
+
await syncAllMemberships(dataDir, fetcher, live).catch(() => { });
|
|
381
|
+
try {
|
|
382
|
+
const r = await fetchWithTimeout(fetcher, `${hubUrl}/api/federation/group`, {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${m.syncToken}` },
|
|
385
|
+
body: JSON.stringify({ name, larkAppIds, requestId: randomUUID() }),
|
|
386
|
+
});
|
|
387
|
+
const j = await r.json().catch(() => ({}));
|
|
388
|
+
jsonRes(res, r.ok ? 200 : (r.status === 400 || r.status === 403 ? r.status : 502), j);
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
const he = hubError(e);
|
|
392
|
+
jsonRes(res, he.status, { ok: false, error: he.error });
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
// ── Local team (this deployment as a Hub: identity + own roster + invites) ──
|
|
397
|
+
if (path === '/api/team/local' && method === 'GET') {
|
|
398
|
+
ensureDefaultTeam(dataDir);
|
|
399
|
+
const me = getDeploymentIdentity(dataDir);
|
|
400
|
+
const suggestedHubUrl = `http://${config.dashboard.externalHost}:${config.dashboard.port}`;
|
|
401
|
+
jsonRes(res, 200, { ok: true, deployment: me, suggestedHubUrl, ...buildFederatedRoster(dataDir, DEFAULT_TEAM_ID, botConfigOrder(), undefined, live) });
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
// All teams THIS deployment hosts (default + any created), each with its
|
|
405
|
+
// aggregated roster — the SPA「我的团队」renders one block per team.
|
|
406
|
+
if (path === '/api/team/hosted' && method === 'GET') {
|
|
407
|
+
ensureDefaultTeam(dataDir);
|
|
408
|
+
const me = getDeploymentIdentity(dataDir);
|
|
409
|
+
const suggestedHubUrl = `http://${config.dashboard.externalHost}:${config.dashboard.port}`;
|
|
410
|
+
const teams = listTeams(dataDir).map(t => ({
|
|
411
|
+
teamId: t.id, name: t.name, isDefault: t.id === DEFAULT_TEAM_ID,
|
|
412
|
+
...buildFederatedRoster(dataDir, t.id, botConfigOrder(), undefined, live),
|
|
413
|
+
}));
|
|
414
|
+
jsonRes(res, 200, { ok: true, deployment: me, suggestedHubUrl, teams });
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
if (path === '/api/team/hosted' && method === 'POST') {
|
|
418
|
+
let body;
|
|
419
|
+
try {
|
|
420
|
+
body = await readBody(req);
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
const name = String(body?.name ?? '').trim();
|
|
427
|
+
if (!name) {
|
|
428
|
+
jsonRes(res, 400, { ok: false, error: 'name_required' });
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
if (name.length > 64) {
|
|
432
|
+
jsonRes(res, 400, { ok: false, error: 'name_too_long' });
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
if (listTeams(dataDir).length >= 100) {
|
|
436
|
+
jsonRes(res, 400, { ok: false, error: 'too_many_teams' });
|
|
437
|
+
return true;
|
|
438
|
+
} // guardrail (team-internal trust, not a security boundary)
|
|
439
|
+
const t = createTeam(dataDir, name);
|
|
440
|
+
jsonRes(res, 200, { ok: true, teamId: t.id, name: t.name });
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
// Remove a member deployment from a team I host (hub kicks a joined spoke).
|
|
444
|
+
if (memberDel && method === 'DELETE') {
|
|
445
|
+
const teamId = decodeURIComponent(memberDel[1]);
|
|
446
|
+
const deploymentId = decodeURIComponent(memberDel[2]);
|
|
447
|
+
if (deploymentId === getDeploymentIdentity(dataDir).deploymentId) {
|
|
448
|
+
jsonRes(res, 400, { ok: false, error: 'cannot_remove_self' });
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
const removed = removeDeployment(dataDir, teamId, deploymentId);
|
|
452
|
+
jsonRes(res, removed ? 200 : 404, { ok: removed, ...(removed ? {} : { error: 'member_not_found' }) });
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
if (hostedDel && method === 'DELETE') {
|
|
456
|
+
const teamId = decodeURIComponent(hostedDel[1]);
|
|
457
|
+
if (teamId === DEFAULT_TEAM_ID) {
|
|
458
|
+
jsonRes(res, 400, { ok: false, error: 'cannot_delete_default' });
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (!getTeam(dataDir, teamId)) {
|
|
462
|
+
jsonRes(res, 404, { ok: false, error: 'team_not_found' });
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
deleteTeam(dataDir, teamId);
|
|
466
|
+
removeTeamFederation(dataDir, teamId); // drop joined spokes' records for this team
|
|
467
|
+
deleteInvitesForTeam(dataDir, teamId); // invalidate its outstanding invites
|
|
468
|
+
jsonRes(res, 200, { ok: true });
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
// Generate an invite for a hosted team (defaults to the default team).
|
|
472
|
+
if (path === '/api/team/local-invite' && method === 'POST') {
|
|
473
|
+
// Empty body → default team (readBody returns {}); malformed/oversized JSON
|
|
474
|
+
// throws → reject with bad_json (don't silently fall back to default team).
|
|
475
|
+
let body;
|
|
476
|
+
try {
|
|
477
|
+
body = await readBody(req);
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
ensureDefaultTeam(dataDir);
|
|
484
|
+
const teamId = String(body?.teamId ?? '').trim() || DEFAULT_TEAM_ID;
|
|
485
|
+
if (!getTeam(dataDir, teamId)) {
|
|
486
|
+
jsonRes(res, 404, { ok: false, error: 'team_not_found' });
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
const inv = createInvite(dataDir, teamId, getDeploymentIdentity(dataDir).deploymentId);
|
|
490
|
+
jsonRes(res, 200, { ok: true, code: inv.code, expiresAt: inv.expiresAt, teamId });
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
if (path === '/api/team/rename-deployment' && method === 'POST') {
|
|
494
|
+
let body;
|
|
495
|
+
try {
|
|
496
|
+
body = await readBody(req);
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
const name = String(body?.name ?? '').trim();
|
|
503
|
+
if (!name) {
|
|
504
|
+
jsonRes(res, 400, { ok: false, error: 'name_required' });
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
jsonRes(res, 200, { ok: true, deployment: setDeploymentName(dataDir, name) });
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
// Accept an invite from another deployment's hub: register our bots there.
|
|
511
|
+
if (path === '/api/team/join-remote' && method === 'POST') {
|
|
512
|
+
let body;
|
|
513
|
+
try {
|
|
514
|
+
body = await readBody(req);
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
const hubUrl = normalizeHubUrl(body?.hubUrl);
|
|
521
|
+
const inviteCode = String(body?.inviteCode ?? '').trim();
|
|
522
|
+
if (!hubUrl) {
|
|
523
|
+
jsonRes(res, 400, { ok: false, error: 'bad_hub_url' });
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
if (!inviteCode) {
|
|
527
|
+
jsonRes(res, 400, { ok: false, error: 'code_required' });
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
const me = getDeploymentIdentity(dataDir);
|
|
531
|
+
// Issue a delegationToken to the hub + tell it our callback URL, so the hub
|
|
532
|
+
// can delegate 拉群 back to us (hub→spoke) when it has no local creator.
|
|
533
|
+
const delegationToken = randomBytes(24).toString('base64url');
|
|
534
|
+
const callbackUrl = `http://${config.dashboard.externalHost}:${config.dashboard.port}`;
|
|
535
|
+
let hubRes;
|
|
536
|
+
try {
|
|
537
|
+
hubRes = await fetchWithTimeout(fetcher, `${hubUrl}/api/federation/join`, {
|
|
538
|
+
method: 'POST',
|
|
539
|
+
headers: { 'content-type': 'application/json' },
|
|
540
|
+
body: JSON.stringify({ inviteCode, deployment: { deploymentId: me.deploymentId, name: me.name, ownerUnionId: me.ownerUnionId, ownerName: me.ownerName, bots: localBots(dataDir, live), callbackUrl, delegationToken } }),
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
const he = hubError(e);
|
|
545
|
+
jsonRes(res, he.status, { ok: false, error: he.error });
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
const j = await hubRes.json().catch(() => ({}));
|
|
549
|
+
if (!hubRes.ok || !j?.ok) {
|
|
550
|
+
const status = [400, 403, 409].includes(hubRes.status) ? hubRes.status : 502;
|
|
551
|
+
jsonRes(res, status, { ok: false, error: j?.error || `hub_${hubRes.status}` });
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
addMembership(dataDir, { hubUrl, teamId: j.teamId, teamName: j.teamName, syncToken: j.syncToken, deploymentId: me.deploymentId, delegationToken });
|
|
555
|
+
jsonRes(res, 200, { ok: true, hubUrl, teamId: j.teamId, teamName: j.teamName });
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
// Pull each joined hub's aggregated roster for display (token in header).
|
|
559
|
+
if (path === '/api/team/remote-roster' && method === 'GET') {
|
|
560
|
+
const out = [];
|
|
561
|
+
for (const m of listMemberships(dataDir)) {
|
|
562
|
+
try {
|
|
563
|
+
const r = await fetchWithTimeout(fetcher, `${m.hubUrl}/api/federation/roster`, {
|
|
564
|
+
headers: { authorization: `Bearer ${m.syncToken}` },
|
|
565
|
+
});
|
|
566
|
+
const j = await r.json().catch(() => ({}));
|
|
567
|
+
out.push({ hubUrl: m.hubUrl, teamId: m.teamId, teamName: m.teamName, ok: r.ok && j?.ok, roster: j?.ok ? { deployments: j.deployments, bots: j.bots, team: j.team } : null, error: j?.error });
|
|
568
|
+
}
|
|
569
|
+
catch (e) {
|
|
570
|
+
out.push({ hubUrl: m.hubUrl, teamId: m.teamId, teamName: m.teamName, ok: false, roster: null, error: hubError(e).error });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
jsonRes(res, 200, { ok: true, memberships: out });
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
// Manually push bots + heartbeat to all joined hubs.
|
|
577
|
+
if (path === '/api/team/sync-remote' && method === 'POST') {
|
|
578
|
+
const r = await syncAllMemberships(dataDir, fetcher, live);
|
|
579
|
+
jsonRes(res, 200, { ok: true, ...r });
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
// Leave a remote team: best-effort revoke at the hub (so it drops our
|
|
583
|
+
// deployment + token + stale bots), then forget the membership locally.
|
|
584
|
+
if (path === '/api/team/leave-remote' && method === 'POST') {
|
|
585
|
+
let body;
|
|
586
|
+
try {
|
|
587
|
+
body = await readBody(req);
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
const hubUrl = normalizeHubUrl(body?.hubUrl);
|
|
594
|
+
const teamId = String(body?.teamId ?? '').trim();
|
|
595
|
+
if (!hubUrl || !teamId) {
|
|
596
|
+
jsonRes(res, 400, { ok: false, error: 'bad_request' });
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
const m = listMemberships(dataDir).find(x => x.hubUrl === hubUrl && x.teamId === teamId);
|
|
600
|
+
let hubRevoked = false;
|
|
601
|
+
if (m) {
|
|
602
|
+
try {
|
|
603
|
+
const r = await fetchWithTimeout(fetcher, `${hubUrl}/api/federation/leave`, {
|
|
604
|
+
method: 'POST',
|
|
605
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${m.syncToken}` },
|
|
606
|
+
body: JSON.stringify({ syncToken: m.syncToken }),
|
|
607
|
+
});
|
|
608
|
+
hubRevoked = r.ok;
|
|
609
|
+
}
|
|
610
|
+
catch { /* hub unreachable — still forget locally below */ }
|
|
611
|
+
}
|
|
612
|
+
const removed = removeMembership(dataDir, hubUrl, teamId);
|
|
613
|
+
jsonRes(res, removed ? 200 : 404, { ok: removed, hubRevoked });
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
//# sourceMappingURL=federation-spoke-api.js.map
|