@xopcai/xopc 0.0.84 → 0.0.85
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/plugin.d.ts +2 -0
- package/dist/extensions/feishu/src/plugin.js +10 -0
- package/dist/extensions/feishu/src/plugin.js.map +1 -1
- package/dist/extensions/feishu/src/workflow-progress.d.ts +27 -0
- package/dist/extensions/feishu/src/workflow-progress.js +99 -0
- package/dist/extensions/feishu/src/workflow-progress.js.map +1 -0
- package/dist/extensions/telegram/src/plugin.d.ts +2 -0
- package/dist/extensions/telegram/src/plugin.js +11 -1
- package/dist/extensions/telegram/src/plugin.js.map +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.d.ts +24 -0
- package/dist/extensions/telegram/src/workflow-progress.js +73 -0
- package/dist/extensions/telegram/src/workflow-progress.js.map +1 -0
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +158 -0
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -0
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.d.ts +2 -0
- package/dist/extensions/weixin/src/plugin.js +11 -1
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +26 -0
- package/dist/extensions/weixin/src/workflow-progress.js +99 -0
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -0
- package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-D7v7649T.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-nCaMb0a7.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-C1gZBcJV.js +8 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/cron-api-CoYK0hlm.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DeGo-Vjc.js +1 -0
- package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +45 -0
- package/dist/gateway/static/root/assets/{dist-CqNMNhJM.js → dist-DaK4dsss.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-gf2L0kY_.js → extension-debug-page-BZngZWbO.js} +1 -1
- package/dist/gateway/static/root/assets/extension-page-D6JSyV27.js +1 -0
- package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +1 -0
- package/dist/gateway/static/root/assets/fetch-B2MYHbWg.js +1 -0
- package/dist/gateway/static/root/assets/{field-primitives-DTtlp-l8.js → field-primitives-Zzl22MvN.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-BtIcpG0O.js +1 -0
- package/dist/gateway/static/root/assets/index-D4vM3-P7.js +4700 -0
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-_d4UJ-qQ.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-5N4aF2Wk.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +3 -0
- package/dist/gateway/static/root/assets/share-preview-page-D4EG_vM1.js +2 -0
- package/dist/gateway/static/root/assets/skills-page-sPAXhh8w.js +2 -0
- package/dist/gateway/static/root/assets/theme-store-DryYl3qD.js +1 -0
- package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +3 -0
- package/dist/gateway/static/root/assets/utils-CYO9eTCM.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-Ds51havm.js +1 -0
- package/dist/gateway/static/root/index.html +7 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +7 -7
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +3 -3
- package/dist/src/agent/embedded/map-stream-events.js +6 -0
- package/dist/src/agent/embedded/map-stream-events.js.map +1 -1
- package/dist/src/agent/embedded/subscribe-session.js +24 -0
- package/dist/src/agent/embedded/subscribe-session.js.map +1 -1
- package/dist/src/agent/embedded/types.d.ts +19 -0
- package/dist/src/agent/goals/goal-locale.js +2 -2
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/startup-context.d.ts +3 -0
- package/dist/src/agent/reply/startup-context.js +25 -2
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +2 -2
- package/dist/src/agent/service/build-direct-message-content.js +1 -1
- package/dist/src/agent/service.d.ts +1 -0
- package/dist/src/agent/service.js +10 -4
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +3 -3
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/create-share-tool.d.ts +27 -0
- package/dist/src/agent/tools/create-share-tool.js +237 -0
- package/dist/src/agent/tools/create-share-tool.js.map +1 -0
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +35 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/index.d.ts +2 -0
- package/dist/src/agent/tools/index.js +3 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.d.ts +41 -0
- package/dist/src/agent/tools/workflow-tool.js +271 -0
- package/dist/src/agent/tools/workflow-tool.js.map +1 -0
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js +115 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +15 -0
- package/dist/src/agent/workflow/builtins/index.js +28 -0
- package/dist/src/agent/workflow/builtins/index.js.map +1 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +113 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/research.js +129 -0
- package/dist/src/agent/workflow/builtins/research.js.map +1 -0
- package/dist/src/agent/workflow/catalog.d.ts +51 -0
- package/dist/src/agent/workflow/catalog.js +155 -0
- package/dist/src/agent/workflow/catalog.js.map +1 -0
- package/dist/src/agent/workflow/channel-capability.d.ts +76 -0
- package/dist/src/agent/workflow/channel-capability.js +1 -0
- package/dist/src/agent/workflow/index.d.ts +11 -0
- package/dist/src/agent/workflow/index.js +10 -0
- package/dist/src/agent/workflow/last-run-memory.d.ts +42 -0
- package/dist/src/agent/workflow/last-run-memory.js +60 -0
- package/dist/src/agent/workflow/last-run-memory.js.map +1 -0
- package/dist/src/agent/workflow/parser.d.ts +20 -0
- package/dist/src/agent/workflow/parser.js +137 -0
- package/dist/src/agent/workflow/parser.js.map +1 -0
- package/dist/src/agent/workflow/progress-broker.d.ts +80 -0
- package/dist/src/agent/workflow/progress-broker.js +263 -0
- package/dist/src/agent/workflow/progress-broker.js.map +1 -0
- package/dist/src/agent/workflow/runtime.d.ts +31 -0
- package/dist/src/agent/workflow/runtime.js +301 -0
- package/dist/src/agent/workflow/runtime.js.map +1 -0
- package/dist/src/agent/workflow/snapshot.d.ts +18 -0
- package/dist/src/agent/workflow/snapshot.js +144 -0
- package/dist/src/agent/workflow/snapshot.js.map +1 -0
- package/dist/src/agent/workflow/structured-output-tool.d.ts +33 -0
- package/dist/src/agent/workflow/structured-output-tool.js +58 -0
- package/dist/src/agent/workflow/structured-output-tool.js.map +1 -0
- package/dist/src/agent/workflow/subagent-runner.d.ts +42 -0
- package/dist/src/agent/workflow/subagent-runner.js +104 -0
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -0
- package/dist/src/agent/workflow/types.d.ts +137 -0
- package/dist/src/agent/workflow/types.js +1 -0
- package/dist/src/auth/credentials.js +3 -3
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cache-dir-policy.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +2 -2
- package/dist/src/browser/providers/browser-ext-install.js +4 -4
- package/dist/src/browser/providers/cloakbrowser.js +4 -4
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/builtins/model.js +40 -23
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/chat-commands/builtins/system.js +30 -15
- package/dist/src/chat-commands/builtins/system.js.map +1 -1
- package/dist/src/chat-commands/builtins/workflow.d.ts +18 -0
- package/dist/src/chat-commands/builtins/workflow.js +167 -0
- package/dist/src/chat-commands/builtins/workflow.js.map +1 -0
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/chat-commands/format-output.d.ts +28 -0
- package/dist/src/chat-commands/format-output.js +45 -0
- package/dist/src/chat-commands/format-output.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/commands/config.js +2 -2
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +10 -4
- package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -1
- package/dist/src/cli/commands/gateway/shared.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/onboard.js +2 -2
- package/dist/src/cli/commands/tunnel.js +2 -2
- package/dist/src/cli/utils/gateway-client.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +5 -5
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/public-url.d.ts +28 -0
- package/dist/src/config/public-url.js +103 -0
- package/dist/src/config/public-url.js.map +1 -0
- package/dist/src/config/schema.d.ts +82 -0
- package/dist/src/config/schema.js +130 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/constants.js +1 -1
- package/dist/src/daemon/install-plan.js +3 -3
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/schtasks.js +38 -1
- package/dist/src/daemon/schtasks.js.map +1 -1
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/hono/app.js +33 -2
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +2 -2
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/shares.js +631 -34
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/site-shares.d.ts +3 -0
- package/dist/src/gateway/hono/routes/site-shares.js +228 -0
- package/dist/src/gateway/hono/routes/site-shares.js.map +1 -0
- package/dist/src/gateway/hono/routes/tunnel.js +97 -8
- package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +5 -5
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/host.d.ts +3 -1
- package/dist/src/gateway/host.js +3 -1
- package/dist/src/gateway/host.js.map +1 -1
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/ports.d.ts +6 -0
- package/dist/src/gateway/ports.js +38 -2
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/public-url.d.ts +8 -0
- package/dist/src/gateway/public-url.js +10 -0
- package/dist/src/gateway/public-url.js.map +1 -0
- package/dist/src/gateway/security/origin-check.d.ts +9 -1
- package/dist/src/gateway/security/origin-check.js +4 -0
- package/dist/src/gateway/security/origin-check.js.map +1 -1
- package/dist/src/gateway/server.js +15 -0
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service/agent-runner.js +2 -2
- package/dist/src/gateway/service/marketplace-service.js +2 -2
- package/dist/src/gateway/service/run-gateway-agent.js +2 -2
- package/dist/src/gateway/service.js +3 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/i18n/goals-bundle.js +1 -1
- package/dist/src/i18n/index.d.ts +1 -0
- package/dist/src/i18n/index.js +2 -1
- package/dist/src/i18n/locales/share-tool.en.js +15 -0
- package/dist/src/i18n/locales/share-tool.en.js.map +1 -0
- package/dist/src/i18n/locales/share-tool.zh.js +15 -0
- package/dist/src/i18n/locales/share-tool.zh.js.map +1 -0
- package/dist/src/i18n/share-tool-bundle.d.ts +20 -0
- package/dist/src/i18n/share-tool-bundle.js +56 -0
- package/dist/src/i18n/share-tool-bundle.js.map +1 -0
- package/dist/src/infra/gateway-processes.js +1 -0
- package/dist/src/infra/gateway-processes.js.map +1 -1
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +2 -2
- package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
- package/dist/src/session/parity/sessions-json-file.js +1 -1
- package/dist/src/session/parity/transcript-file-lock.js +2 -2
- package/dist/src/session/parity/transcript-paths.js +1 -1
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.js +3 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/share/share-auto.d.ts +74 -0
- package/dist/src/share/share-auto.js +247 -0
- package/dist/src/share/share-auto.js.map +1 -0
- package/dist/src/share/share-config.js +63 -4
- package/dist/src/share/share-config.js.map +1 -1
- package/dist/src/share/share-landing.d.ts +28 -2
- package/dist/src/share/share-landing.js +155 -34
- package/dist/src/share/share-landing.js.map +1 -1
- package/dist/src/share/share-store.d.ts +48 -4
- package/dist/src/share/share-store.js +322 -51
- package/dist/src/share/share-store.js.map +1 -1
- package/dist/src/share/share-thumbnail.d.ts +35 -0
- package/dist/src/share/share-thumbnail.js +277 -0
- package/dist/src/share/share-thumbnail.js.map +1 -0
- package/dist/src/share/share-types.d.ts +68 -10
- package/dist/src/share/share-types.js +18 -1
- package/dist/src/share/share-types.js.map +1 -1
- package/dist/src/share/share-url.js +1 -1
- package/dist/src/share/share-zip.d.ts +35 -0
- package/dist/src/share/share-zip.js +303 -0
- package/dist/src/share/share-zip.js.map +1 -0
- package/dist/src/share/site-proxy.d.ts +35 -0
- package/dist/src/share/site-proxy.js +234 -0
- package/dist/src/share/site-proxy.js.map +1 -0
- package/dist/src/share/site-share-config.d.ts +11 -0
- package/dist/src/share/site-share-config.js +103 -0
- package/dist/src/share/site-share-config.js.map +1 -0
- package/dist/src/share/site-share-router.d.ts +23 -0
- package/dist/src/share/site-share-router.js +147 -0
- package/dist/src/share/site-share-router.js.map +1 -0
- package/dist/src/share/site-share-store.d.ts +53 -0
- package/dist/src/share/site-share-store.js +400 -0
- package/dist/src/share/site-share-store.js.map +1 -0
- package/dist/src/share/site-share-types.d.ts +103 -0
- package/dist/src/share/site-share-types.js +41 -0
- package/dist/src/share/site-share-types.js.map +1 -0
- package/dist/src/share/site-static-serve.d.ts +10 -0
- package/dist/src/share/site-static-serve.js +145 -0
- package/dist/src/share/site-static-serve.js.map +1 -0
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-commands.js +18 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui-workflow-slash.d.ts +32 -0
- package/dist/src/tui/tui-workflow-slash.js +63 -0
- package/dist/src/tui/tui-workflow-slash.js.map +1 -0
- package/dist/src/tui/tui.js +2 -2
- package/dist/src/tunnel/enable-lan-pairing.js +1 -1
- package/dist/src/tunnel/frpc-binary.js +3 -3
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/index.js +2 -2
- package/dist/src/tunnel/pair-context.d.ts +7 -1
- package/dist/src/tunnel/pair-context.js +25 -9
- package/dist/src/tunnel/pair-context.js.map +1 -1
- package/dist/src/tunnel/pair-url.d.ts +14 -1
- package/dist/src/tunnel/pair-url.js +14 -1
- package/dist/src/tunnel/pair-url.js.map +1 -1
- package/dist/src/tunnel/tunnel-service.js +2 -2
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/package.json +3 -2
- package/dist/gateway/static/root/assets/agents-tR-nNP04.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-BDw6SP-d.js +0 -1
- package/dist/gateway/static/root/assets/button-KafIU8dx.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-DEFd-jj1.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DI5FHdGe.js +0 -8
- package/dist/gateway/static/root/assets/cron-api-BSqY8LwW.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-D7lVDjcR.js +0 -1
- package/dist/gateway/static/root/assets/dist-C57OMHW8.js +0 -48
- package/dist/gateway/static/root/assets/extension-page-CQo2Xsmg.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CZf0WoZg.js +0 -1
- package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +0 -3
- package/dist/gateway/static/root/assets/heartbeat-config-api-B0drdQEJ.js +0 -1
- package/dist/gateway/static/root/assets/index-0Gt3TG4j.js +0 -4693
- package/dist/gateway/static/root/assets/index-BuFldCsB.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-DMuORLfC.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-_UO8g6NN.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-DkmHkknc.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-Cz8FoW_A.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-HrUOxF7H.js +0 -2
- package/dist/gateway/static/root/assets/theme-store-D01dJt95.js +0 -1
- package/dist/gateway/static/root/assets/utils-BFwcR6pL.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-JF8-aqc5.js +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { checkFileSafety } from "../../../../src/agent/prompt/safety.js";
|
|
2
2
|
import { getMimeType } from "../../../../src/channels/media.js";
|
|
3
3
|
import { getWorkspacePath } from "../../../../src/config/workspace-path-helpers.js";
|
|
4
|
-
import { promises } from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
5
|
+
import { promises } from "node:fs";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
7
|
//#region extensions/feishu/src/outbound/media-load.ts
|
|
8
8
|
function isPathUnderRoots(resolved, roots) {
|
|
@@ -40,6 +40,8 @@ export declare class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishu
|
|
|
40
40
|
private cfg;
|
|
41
41
|
private abortControllers;
|
|
42
42
|
private inboundPipeline?;
|
|
43
|
+
/** Unregister fn for the workflow-progress capability registered against the global broker. */
|
|
44
|
+
private workflowProgressUnregister;
|
|
43
45
|
config: {
|
|
44
46
|
listAccountIds: (cfg: Config) => string[];
|
|
45
47
|
resolveAccount: (cfg: Config, accountId?: string | null) => ResolvedFeishuAccount;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../../src/utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../../src/utils/logger.js";
|
|
3
|
+
import { getWorkflowProgressBroker } from "../../../src/agent/workflow/progress-broker.js";
|
|
4
|
+
import "../../../src/agent/workflow/index.js";
|
|
3
5
|
import { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from "../../../src/channels/security.js";
|
|
4
6
|
import { createStandardPairingAdapter } from "../../../src/channels/pairing/pairing-store-adapter.js";
|
|
5
7
|
import { FeishuConfigSchema } from "./schema/config-schema.js";
|
|
@@ -19,6 +21,7 @@ import { feishuCliLoginAdapter } from "./adapters/cli-login.js";
|
|
|
19
21
|
import { feishuOnboardAdapter } from "./adapters/onboard-cli.js";
|
|
20
22
|
import { handleFeishuChannelMessageAction } from "./actions/message-action-handler.js";
|
|
21
23
|
import { createFeishuInboundPipeline } from "./transport/reliability/inbound-pipeline.js";
|
|
24
|
+
import { createFeishuWorkflowProgressCapability } from "./workflow-progress.js";
|
|
22
25
|
import { isDeepStrictEqual } from "node:util";
|
|
23
26
|
//#region extensions/feishu/src/plugin.ts
|
|
24
27
|
/**
|
|
@@ -78,6 +81,8 @@ var FeishuChannelPlugin = class {
|
|
|
78
81
|
cfg;
|
|
79
82
|
abortControllers = /* @__PURE__ */ new Map();
|
|
80
83
|
inboundPipeline;
|
|
84
|
+
/** Unregister fn for the workflow-progress capability registered against the global broker. */
|
|
85
|
+
workflowProgressUnregister = null;
|
|
81
86
|
config = {
|
|
82
87
|
listAccountIds: (cfg) => listFeishuAccountIds(cfg),
|
|
83
88
|
resolveAccount: (cfg, accountId) => resolveFeishuAccount(cfg, accountId),
|
|
@@ -254,6 +259,7 @@ var FeishuChannelPlugin = class {
|
|
|
254
259
|
}, "Feishu inbound pipeline flush failed");
|
|
255
260
|
}
|
|
256
261
|
});
|
|
262
|
+
this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }));
|
|
257
263
|
log.debug("Feishu plugin initialized");
|
|
258
264
|
}
|
|
259
265
|
async start(options) {
|
|
@@ -317,6 +323,10 @@ var FeishuChannelPlugin = class {
|
|
|
317
323
|
this.abortControllers.delete(id);
|
|
318
324
|
}
|
|
319
325
|
}
|
|
326
|
+
if (!accountId) {
|
|
327
|
+
this.workflowProgressUnregister?.();
|
|
328
|
+
this.workflowProgressUnregister = null;
|
|
329
|
+
}
|
|
320
330
|
}
|
|
321
331
|
channelIsRunning(cfg) {
|
|
322
332
|
return listFeishuAccountIds(cfg).some((id) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../extensions/feishu/src/plugin.ts"],"sourcesContent":["/**\n * Feishu/Lark channel plugin (Socket Mode first).\n *\n * This is intentionally decomposed into small modules so we can grow toward\n * openclaw `extensions/feishu` parity without turning `plugin.ts` into a monolith.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';\nimport type {\n ChannelCapabilities,\n ChannelDoctorAdapter,\n ChannelMessageActionAdapter,\n ChannelOutboundAdapter,\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginStartOptions,\n ChannelSecurityAdapter,\n ChannelSecurityContext,\n ChannelStatusAdapter,\n ChatType,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type { ChannelCliLoginAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from '@xopcai/xopc/channels/security.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\n\nimport { FeishuConfigSchema, type FeishuConfig } from './schema/config-schema.js';\nimport { listFeishuAccountIds, resolveFeishuAccount, type ResolvedFeishuAccount } from './state/accounts.js';\nimport { createFeishuSocketModeMonitor } from './transport/socket-mode/monitor.js';\nimport { createFeishuWebhookMonitor } from './transport/webhook/monitor.js';\nimport { createFeishuOutboundAdapter } from './outbound/outbound-adapter.js';\nimport { createFeishuStatusAdapter } from './status/status-adapter.js';\nimport { createFeishuDoctorAdapter } from './status/doctor.js';\nimport { feishuConfigSurface } from './ui/config-surface.js';\nimport { createFeishuStreamingAdapter } from './streaming/streaming-adapter.js';\nimport { readFrameworkAllowFromList } from './auth/pairing.js';\nimport {\n addReactionFeishu,\n editMessageFeishu,\n getMessageFeishu,\n listPinsFeishu,\n listReactionsFeishu,\n pinMessageFeishu,\n removeReactionFeishu,\n unpinMessageFeishu,\n} from './outbound/actions.js';\nimport { createFeishuDirectoryAdapter } from './directory/directory-adapter.js';\nimport { feishuWhoAmI } from './tools/tools.js';\nimport { feishuCliLoginAdapter } from './adapters/cli-login.js';\nimport { feishuOnboardAdapter } from './adapters/onboard-cli.js';\nimport { handleFeishuChannelMessageAction } from './actions/message-action-handler.js';\nimport { createFeishuInboundPipeline, type FeishuInboundWork } from './transport/reliability/inbound-pipeline.js';\n\nconst log = createLogger('FeishuPlugin');\n\nexport class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishuAccount> {\n readonly id = 'feishu' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.feishu'],\n };\n\n readonly meta = {\n id: 'feishu',\n label: 'Feishu',\n selectionLabel: 'Feishu/Lark (飞书)',\n docsPath: '/channels/feishu',\n blurb: 'Feishu/Lark enterprise messaging (Socket Mode).',\n order: 4,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities: ChannelCapabilities = {\n chatTypes: ['direct', 'channel'] as ChatType[],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: false,\n edit: true,\n reply: true,\n } as any;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: 350 },\n outbound: { textChunkLimit: 4000 },\n streaming: {\n blockStreamingCoalesce: {\n minChars: 200,\n idleMs: 2500,\n },\n },\n };\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = FeishuConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly configSurface = feishuConfigSurface;\n\n readonly pairing = createStandardPairingAdapter('feishu');\n\n onboard = feishuOnboardAdapter;\n\n readonly cliLogin: ChannelCliLoginAdapter = feishuCliLoginAdapter;\n\n private bus!: MessageBus;\n private cfg!: Config;\n private abortControllers = new Map<string, AbortController>();\n private inboundPipeline?: ReturnType<typeof createFeishuInboundPipeline>;\n\n config = {\n listAccountIds: (cfg: Config) => listFeishuAccountIds(cfg),\n resolveAccount: (cfg: Config, accountId?: string | null) => resolveFeishuAccount(cfg, accountId),\n isConfigured: async (account: ResolvedFeishuAccount) => account.configured,\n describeAccount: (account: ResolvedFeishuAccount) => ({\n accountId: account.accountId,\n channelId: 'feishu',\n enabled: account.enabled,\n configured: account.configured,\n status: account.configured ? undefined : 'unconfigured',\n }),\n };\n\n security: ChannelSecurityAdapter<ResolvedFeishuAccount> = {\n resolveDmPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveDmPolicy(account.dmPolicy, 'open'),\n resolveGroupPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveGroupPolicy(account.groupPolicy, 'allowlist'),\n checkAccess: (ctx: ChannelSecurityContext, account: ResolvedFeishuAccount, _cfg: Config) => {\n const isDm = !ctx.isGroup;\n const frameworkAllowFrom = readFrameworkAllowFromList(account.accountId);\n const baseAllowFrom = isDm ? account.allowFrom : account.groupAllowFrom ?? account.allowFrom;\n const allowFrom = [...(baseAllowFrom ?? []), ...frameworkAllowFrom];\n if (isDm) {\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: false,\n isDm: true,\n },\n dmPolicy: account.dmPolicy,\n allowFrom,\n });\n }\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: true,\n isDm: false,\n },\n groupPolicy: account.groupPolicy,\n allowFrom,\n });\n },\n };\n\n outbound: ChannelOutboundAdapter = createFeishuOutboundAdapter();\n\n streaming = createFeishuStreamingAdapter(() => this.cfg);\n\n status: ChannelStatusAdapter<ResolvedFeishuAccount> = createFeishuStatusAdapter();\n\n doctor: ChannelDoctorAdapter = createFeishuDoctorAdapter();\n\n directory = createFeishuDirectoryAdapter();\n\n actions: ChannelMessageActionAdapter = {\n handleAction: handleFeishuChannelMessageAction,\n };\n\n agentTools = [\n {\n name: 'feishu_read',\n description: 'Read a Feishu message by messageId (debug/utility).',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_read requires messageId');\n return await getMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n },\n },\n {\n name: 'feishu_edit',\n description: 'Edit a Feishu message by messageId.',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n const text = typeof (args as any)?.text === 'string' ? (args as any).text : '';\n if (!messageId) throw new Error('feishu_edit requires messageId');\n if (!text.trim()) throw new Error('feishu_edit requires text');\n return await editMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, text });\n },\n },\n {\n name: 'feishu_scopes_probe',\n description: 'Probe Feishu credentials/scopes (placeholder for full docs/wiki/drive tools).',\n execute: async (toolCtx) => {\n return await feishuWhoAmI({ cfg: this.cfg, accountId: toolCtx.accountId });\n },\n },\n {\n name: 'feishu_react',\n description: 'Add/remove/list reactions for a message.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_react requires messageId');\n\n const account = resolveFeishuAccount(this.cfg, toolCtx.accountId ?? 'default');\n const enabled = (account.actions as any)?.reactions !== false;\n if (!enabled) {\n throw new Error('Feishu reactions are disabled via channels.feishu.actions.reactions');\n }\n\n if (a?.list === true) {\n return await listReactionsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n messageId,\n emojiType: typeof a?.emojiType === 'string' ? a.emojiType : undefined,\n });\n }\n\n if (a?.remove === true) {\n const reactionId = typeof a?.reactionId === 'string' ? a.reactionId : '';\n if (!reactionId) throw new Error('feishu_react remove requires reactionId');\n return await removeReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, reactionId });\n }\n\n const emojiType = typeof a?.emojiType === 'string' ? a.emojiType : '';\n if (!emojiType) throw new Error('feishu_react requires emojiType');\n return await addReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, emojiType });\n },\n },\n {\n name: 'feishu_pins',\n description: 'Pin/unpin/list pins for a chat.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const action = typeof a?.action === 'string' ? a.action : '';\n if (action === 'list') {\n const chatId = typeof a?.chatId === 'string' ? a.chatId : toolCtx.chatId;\n if (!chatId) throw new Error('feishu_pins list requires chatId');\n return await listPinsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n chatId,\n startTime: typeof a?.startTime === 'string' ? a.startTime : undefined,\n endTime: typeof a?.endTime === 'string' ? a.endTime : undefined,\n pageSize: typeof a?.pageSize === 'number' ? a.pageSize : undefined,\n pageToken: typeof a?.pageToken === 'string' ? a.pageToken : undefined,\n });\n }\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_pins requires messageId');\n if (action === 'pin') {\n return await pinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n if (action === 'unpin') {\n return await unpinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n throw new Error('feishu_pins requires action: pin | unpin | list');\n },\n },\n ];\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n this.inboundPipeline = createFeishuInboundPipeline({\n bus: this.bus,\n defaultDebounceMs: defaultDebounce,\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Feishu inbound pipeline flush failed');\n },\n });\n log.debug('Feishu plugin initialized');\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.feishu as FeishuConfig | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const ids = options?.accountId ? [options.accountId] : listFeishuAccountIds(this.cfg);\n for (const accountId of ids) {\n const account = resolveFeishuAccount(this.cfg, accountId);\n if (!account.enabled || !account.configured) continue;\n if (this.abortControllers.has(accountId)) continue;\n\n const ac = new AbortController();\n this.abortControllers.set(accountId, ac);\n\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n const enqueueInbound = (work: FeishuInboundWork) => {\n const p = this.inboundPipeline;\n if (!p) {\n return this.bus.publishInbound(work.inbound);\n }\n return p.enqueue(work);\n };\n\n const monitor = createFeishuSocketModeMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) => this.security.checkAccess?.(ctx, account, this.cfg),\n },\n });\n\n const runner =\n account.connectionMode === 'webhook'\n ? createFeishuWebhookMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) =>\n this.security.checkAccess?.(ctx, account, this.cfg),\n },\n })\n : monitor;\n\n void runner.run().catch((err) => {\n if ((err as { name?: string } | undefined)?.name === 'AbortError') {\n log.debug({ accountId }, 'Feishu monitor stopped');\n return;\n }\n log.error({ err, accountId }, 'Feishu monitor exited with error');\n });\n\n log.info({ accountId, mode: account.connectionMode }, 'Feishu monitor started');\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n try {\n await this.inboundPipeline?.flushAll();\n } catch (err) {\n log.warn({ err }, 'Feishu inbound pipeline flushAll failed');\n }\n const ids = accountId ? [accountId] : [...this.abortControllers.keys()];\n for (const id of ids) {\n const ac = this.abortControllers.get(id);\n if (ac) {\n ac.abort();\n this.abortControllers.delete(id);\n }\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n const ids = listFeishuAccountIds(cfg);\n return ids.some((id) => {\n const a = resolveFeishuAccount(cfg, id);\n return a.enabled !== false && a.configured && this.abortControllers.has(id);\n });\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prev = this.cfg.channels?.feishu as unknown;\n const next = cfg.channels?.feishu as { enabled?: boolean } | undefined;\n const channelOff = !next || next.enabled !== true;\n\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n return;\n }\n\n this.cfg = cfg;\n\n if (isDeepStrictEqual(prev, next) && this.channelIsRunning(cfg)) {\n return;\n }\n\n await this.stop();\n await this.start();\n }\n}\n\nexport const feishuPlugin = new FeishuChannelPlugin();\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../extensions/feishu/src/plugin.ts"],"sourcesContent":["/**\n * Feishu/Lark channel plugin (Socket Mode first).\n *\n * This is intentionally decomposed into small modules so we can grow toward\n * openclaw `extensions/feishu` parity without turning `plugin.ts` into a monolith.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';\nimport type {\n ChannelCapabilities,\n ChannelDoctorAdapter,\n ChannelMessageActionAdapter,\n ChannelOutboundAdapter,\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginStartOptions,\n ChannelSecurityAdapter,\n ChannelSecurityContext,\n ChannelStatusAdapter,\n ChatType,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport type { ChannelCliLoginAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { evaluateAccess, resolveDmPolicy, resolveGroupPolicy } from '@xopcai/xopc/channels/security.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\n\nimport { FeishuConfigSchema, type FeishuConfig } from './schema/config-schema.js';\nimport { listFeishuAccountIds, resolveFeishuAccount, type ResolvedFeishuAccount } from './state/accounts.js';\nimport { createFeishuSocketModeMonitor } from './transport/socket-mode/monitor.js';\nimport { createFeishuWebhookMonitor } from './transport/webhook/monitor.js';\nimport { createFeishuOutboundAdapter } from './outbound/outbound-adapter.js';\nimport { createFeishuStatusAdapter } from './status/status-adapter.js';\nimport { createFeishuDoctorAdapter } from './status/doctor.js';\nimport { feishuConfigSurface } from './ui/config-surface.js';\nimport { createFeishuStreamingAdapter } from './streaming/streaming-adapter.js';\nimport { readFrameworkAllowFromList } from './auth/pairing.js';\nimport {\n addReactionFeishu,\n editMessageFeishu,\n getMessageFeishu,\n listPinsFeishu,\n listReactionsFeishu,\n pinMessageFeishu,\n removeReactionFeishu,\n unpinMessageFeishu,\n} from './outbound/actions.js';\nimport { createFeishuDirectoryAdapter } from './directory/directory-adapter.js';\nimport { feishuWhoAmI } from './tools/tools.js';\nimport { feishuCliLoginAdapter } from './adapters/cli-login.js';\nimport { feishuOnboardAdapter } from './adapters/onboard-cli.js';\nimport { handleFeishuChannelMessageAction } from './actions/message-action-handler.js';\nimport { createFeishuInboundPipeline, type FeishuInboundWork } from './transport/reliability/inbound-pipeline.js';\nimport { getWorkflowProgressBroker } from '@xopcai/xopc/agent/workflow/index.js';\nimport { createFeishuWorkflowProgressCapability } from './workflow-progress.js';\n\nconst log = createLogger('FeishuPlugin');\n\nexport class FeishuChannelPlugin implements ChannelPlugin<ResolvedFeishuAccount> {\n readonly id = 'feishu' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.feishu'],\n };\n\n readonly meta = {\n id: 'feishu',\n label: 'Feishu',\n selectionLabel: 'Feishu/Lark (飞书)',\n docsPath: '/channels/feishu',\n blurb: 'Feishu/Lark enterprise messaging (Socket Mode).',\n order: 4,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities: ChannelCapabilities = {\n chatTypes: ['direct', 'channel'] as ChatType[],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: false,\n edit: true,\n reply: true,\n } as any;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: 350 },\n outbound: { textChunkLimit: 4000 },\n streaming: {\n blockStreamingCoalesce: {\n minChars: 200,\n idleMs: 2500,\n },\n },\n };\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = FeishuConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly configSurface = feishuConfigSurface;\n\n readonly pairing = createStandardPairingAdapter('feishu');\n\n onboard = feishuOnboardAdapter;\n\n readonly cliLogin: ChannelCliLoginAdapter = feishuCliLoginAdapter;\n\n private bus!: MessageBus;\n private cfg!: Config;\n private abortControllers = new Map<string, AbortController>();\n private inboundPipeline?: ReturnType<typeof createFeishuInboundPipeline>;\n /** Unregister fn for the workflow-progress capability registered against the global broker. */\n private workflowProgressUnregister: (() => void) | null = null;\n\n config = {\n listAccountIds: (cfg: Config) => listFeishuAccountIds(cfg),\n resolveAccount: (cfg: Config, accountId?: string | null) => resolveFeishuAccount(cfg, accountId),\n isConfigured: async (account: ResolvedFeishuAccount) => account.configured,\n describeAccount: (account: ResolvedFeishuAccount) => ({\n accountId: account.accountId,\n channelId: 'feishu',\n enabled: account.enabled,\n configured: account.configured,\n status: account.configured ? undefined : 'unconfigured',\n }),\n };\n\n security: ChannelSecurityAdapter<ResolvedFeishuAccount> = {\n resolveDmPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveDmPolicy(account.dmPolicy, 'open'),\n resolveGroupPolicy: ({ account }: { account: ResolvedFeishuAccount }) =>\n resolveGroupPolicy(account.groupPolicy, 'allowlist'),\n checkAccess: (ctx: ChannelSecurityContext, account: ResolvedFeishuAccount, _cfg: Config) => {\n const isDm = !ctx.isGroup;\n const frameworkAllowFrom = readFrameworkAllowFromList(account.accountId);\n const baseAllowFrom = isDm ? account.allowFrom : account.groupAllowFrom ?? account.allowFrom;\n const allowFrom = [...(baseAllowFrom ?? []), ...frameworkAllowFrom];\n if (isDm) {\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: false,\n isDm: true,\n },\n dmPolicy: account.dmPolicy,\n allowFrom,\n });\n }\n return evaluateAccess({\n context: {\n channel: 'feishu',\n accountId: account.accountId,\n chatId: ctx.chatId,\n senderId: ctx.senderId,\n senderName: ctx.senderName,\n isGroup: true,\n isDm: false,\n },\n groupPolicy: account.groupPolicy,\n allowFrom,\n });\n },\n };\n\n outbound: ChannelOutboundAdapter = createFeishuOutboundAdapter();\n\n streaming = createFeishuStreamingAdapter(() => this.cfg);\n\n status: ChannelStatusAdapter<ResolvedFeishuAccount> = createFeishuStatusAdapter();\n\n doctor: ChannelDoctorAdapter = createFeishuDoctorAdapter();\n\n directory = createFeishuDirectoryAdapter();\n\n actions: ChannelMessageActionAdapter = {\n handleAction: handleFeishuChannelMessageAction,\n };\n\n agentTools = [\n {\n name: 'feishu_read',\n description: 'Read a Feishu message by messageId (debug/utility).',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_read requires messageId');\n return await getMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n },\n },\n {\n name: 'feishu_edit',\n description: 'Edit a Feishu message by messageId.',\n execute: async (toolCtx, args) => {\n const messageId = typeof (args as any)?.messageId === 'string' ? (args as any).messageId : toolCtx.messageId;\n const text = typeof (args as any)?.text === 'string' ? (args as any).text : '';\n if (!messageId) throw new Error('feishu_edit requires messageId');\n if (!text.trim()) throw new Error('feishu_edit requires text');\n return await editMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, text });\n },\n },\n {\n name: 'feishu_scopes_probe',\n description: 'Probe Feishu credentials/scopes (placeholder for full docs/wiki/drive tools).',\n execute: async (toolCtx) => {\n return await feishuWhoAmI({ cfg: this.cfg, accountId: toolCtx.accountId });\n },\n },\n {\n name: 'feishu_react',\n description: 'Add/remove/list reactions for a message.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_react requires messageId');\n\n const account = resolveFeishuAccount(this.cfg, toolCtx.accountId ?? 'default');\n const enabled = (account.actions as any)?.reactions !== false;\n if (!enabled) {\n throw new Error('Feishu reactions are disabled via channels.feishu.actions.reactions');\n }\n\n if (a?.list === true) {\n return await listReactionsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n messageId,\n emojiType: typeof a?.emojiType === 'string' ? a.emojiType : undefined,\n });\n }\n\n if (a?.remove === true) {\n const reactionId = typeof a?.reactionId === 'string' ? a.reactionId : '';\n if (!reactionId) throw new Error('feishu_react remove requires reactionId');\n return await removeReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, reactionId });\n }\n\n const emojiType = typeof a?.emojiType === 'string' ? a.emojiType : '';\n if (!emojiType) throw new Error('feishu_react requires emojiType');\n return await addReactionFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId, emojiType });\n },\n },\n {\n name: 'feishu_pins',\n description: 'Pin/unpin/list pins for a chat.',\n execute: async (toolCtx, args) => {\n const a = args as any;\n const action = typeof a?.action === 'string' ? a.action : '';\n if (action === 'list') {\n const chatId = typeof a?.chatId === 'string' ? a.chatId : toolCtx.chatId;\n if (!chatId) throw new Error('feishu_pins list requires chatId');\n return await listPinsFeishu({\n cfg: this.cfg,\n accountId: toolCtx.accountId,\n chatId,\n startTime: typeof a?.startTime === 'string' ? a.startTime : undefined,\n endTime: typeof a?.endTime === 'string' ? a.endTime : undefined,\n pageSize: typeof a?.pageSize === 'number' ? a.pageSize : undefined,\n pageToken: typeof a?.pageToken === 'string' ? a.pageToken : undefined,\n });\n }\n const messageId = typeof a?.messageId === 'string' ? a.messageId : toolCtx.messageId;\n if (!messageId) throw new Error('feishu_pins requires messageId');\n if (action === 'pin') {\n return await pinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n if (action === 'unpin') {\n return await unpinMessageFeishu({ cfg: this.cfg, accountId: toolCtx.accountId, messageId });\n }\n throw new Error('feishu_pins requires action: pin | unpin | list');\n },\n },\n ];\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n this.inboundPipeline = createFeishuInboundPipeline({\n bus: this.bus,\n defaultDebounceMs: defaultDebounce,\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Feishu inbound pipeline flush failed');\n },\n });\n\n // Workflow progress broker capability — turns mid-run snapshots into\n // edit-in-place Feishu messages. Broker is the agent-side subscriber for\n // `tool_execution_update` on the `workflow` tool. Registration is\n // idempotent (replaces a prior cap with the same channelId).\n this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(\n createFeishuWorkflowProgressCapability({ getConfig: () => this.cfg }),\n );\n\n log.debug('Feishu plugin initialized');\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.feishu as FeishuConfig | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const ids = options?.accountId ? [options.accountId] : listFeishuAccountIds(this.cfg);\n for (const accountId of ids) {\n const account = resolveFeishuAccount(this.cfg, accountId);\n if (!account.enabled || !account.configured) continue;\n if (this.abortControllers.has(accountId)) continue;\n\n const ac = new AbortController();\n this.abortControllers.set(accountId, ac);\n\n const defaultDebounce = this.defaults.queue?.debounceMs ?? 350;\n const enqueueInbound = (work: FeishuInboundWork) => {\n const p = this.inboundPipeline;\n if (!p) {\n return this.bus.publishInbound(work.inbound);\n }\n return p.enqueue(work);\n };\n\n const monitor = createFeishuSocketModeMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) => this.security.checkAccess?.(ctx, account, this.cfg),\n },\n });\n\n const runner =\n account.connectionMode === 'webhook'\n ? createFeishuWebhookMonitor({\n account,\n config: this.cfg,\n enqueueInbound,\n inboundDebounceDefaultMs: defaultDebounce,\n abortSignal: ac.signal,\n security: {\n checkAccess: (ctx: ChannelSecurityContext) =>\n this.security.checkAccess?.(ctx, account, this.cfg),\n },\n })\n : monitor;\n\n void runner.run().catch((err) => {\n if ((err as { name?: string } | undefined)?.name === 'AbortError') {\n log.debug({ accountId }, 'Feishu monitor stopped');\n return;\n }\n log.error({ err, accountId }, 'Feishu monitor exited with error');\n });\n\n log.info({ accountId, mode: account.connectionMode }, 'Feishu monitor started');\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n try {\n await this.inboundPipeline?.flushAll();\n } catch (err) {\n log.warn({ err }, 'Feishu inbound pipeline flushAll failed');\n }\n const ids = accountId ? [accountId] : [...this.abortControllers.keys()];\n for (const id of ids) {\n const ac = this.abortControllers.get(id);\n if (ac) {\n ac.abort();\n this.abortControllers.delete(id);\n }\n }\n // Only unregister the broker capability on a full stop. Per-account stops\n // still leave other accounts capable of delivering progress through the\n // same registered cap.\n if (!accountId) {\n this.workflowProgressUnregister?.();\n this.workflowProgressUnregister = null;\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n const ids = listFeishuAccountIds(cfg);\n return ids.some((id) => {\n const a = resolveFeishuAccount(cfg, id);\n return a.enabled !== false && a.configured && this.abortControllers.has(id);\n });\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prev = this.cfg.channels?.feishu as unknown;\n const next = cfg.channels?.feishu as { enabled?: boolean } | undefined;\n const channelOff = !next || next.enabled !== true;\n\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n return;\n }\n\n this.cfg = cfg;\n\n if (isDeepStrictEqual(prev, next) && this.channelIsRunning(cfg)) {\n return;\n }\n\n await this.stop();\n await this.start();\n }\n}\n\nexport const feishuPlugin = new FeishuChannelPlugin();\n\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feishu/Lark capability for the workflow progress broker.
|
|
3
|
+
*
|
|
4
|
+
* Feishu exposes `im.v1.message.update`, so we get the same edit-in-place UX
|
|
5
|
+
* as Telegram: send the first snapshot, then update the same message on
|
|
6
|
+
* every subsequent throttled tick. Key events (phase change / new error /
|
|
7
|
+
* tool_end) bypass the throttle so milestones land promptly.
|
|
8
|
+
*
|
|
9
|
+
* Per-bot send rate ~5 msg/sec/account (Feishu open API limit); a 5-second
|
|
10
|
+
* default throttle leaves comfortable headroom even when several workflows
|
|
11
|
+
* run concurrently.
|
|
12
|
+
*
|
|
13
|
+
* Routing: sessionKey `main:feishu:<accountId>:<dm|group|channel>:<peerId>`.
|
|
14
|
+
* - DM peerId is an `open_id` (starts with `ou_`) → use `receive_id_type=open_id`.
|
|
15
|
+
* - Group / channel peerId is a `chat_id` → use `receive_id_type=chat_id`.
|
|
16
|
+
*
|
|
17
|
+
* Failure handling:
|
|
18
|
+
* - "edit must not modify same content" / "message_not_modified" → swallow, keep id.
|
|
19
|
+
* - Edit target gone (deleted / 410-class errors) → fall back to a fresh send.
|
|
20
|
+
* - Other errors → rethrow so the broker logs and drops the update; the next
|
|
21
|
+
* key event (or the next throttle tick) retries.
|
|
22
|
+
*/
|
|
23
|
+
import type { ChannelProgressCapability } from '@xopcai/xopc/agent/workflow/index.js';
|
|
24
|
+
import type { Config } from '@xopcai/xopc/config/schema.js';
|
|
25
|
+
export declare function createFeishuWorkflowProgressCapability(opts: {
|
|
26
|
+
getConfig: () => Config | undefined;
|
|
27
|
+
}): ChannelProgressCapability;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { init_session_key, parseSessionKey } from "../../../src/routing/session-key.js";
|
|
2
|
+
import { createLogger } from "../../../src/utils/logger/index.js";
|
|
3
|
+
import { init_logger } from "../../../src/utils/logger.js";
|
|
4
|
+
import { resolveFeishuAccount } from "./state/accounts.js";
|
|
5
|
+
import { createFeishuClient } from "./transport/client/client.js";
|
|
6
|
+
import { editMessageFeishu } from "./outbound/actions.js";
|
|
7
|
+
//#region extensions/feishu/src/workflow-progress.ts
|
|
8
|
+
init_session_key();
|
|
9
|
+
init_logger();
|
|
10
|
+
const log = createLogger("FeishuWorkflowProgress");
|
|
11
|
+
const FEISHU_TEXT_MAX = 4e3;
|
|
12
|
+
const DEFAULT_THROTTLE_MS = 5e3;
|
|
13
|
+
function createFeishuWorkflowProgressCapability(opts) {
|
|
14
|
+
return {
|
|
15
|
+
channelId: "feishu",
|
|
16
|
+
supportsEdit: true,
|
|
17
|
+
defaultThrottleMs: DEFAULT_THROTTLE_MS,
|
|
18
|
+
defaultMode: "edit",
|
|
19
|
+
async postProgress(input) {
|
|
20
|
+
const cfg = opts.getConfig();
|
|
21
|
+
if (!cfg) throw new Error("feishu workflow progress: no config loaded");
|
|
22
|
+
const target = resolveTarget(input.sessionKey);
|
|
23
|
+
if (!target) throw new Error(`feishu workflow progress: cannot route sessionKey "${input.sessionKey}"`);
|
|
24
|
+
const account = resolveFeishuAccount(cfg, target.accountId);
|
|
25
|
+
if (!account.configured) throw new Error(`feishu workflow progress: account "${target.accountId}" not configured (sessionKey "${input.sessionKey}")`);
|
|
26
|
+
const text = clampForFeishu(input.text);
|
|
27
|
+
if (input.previousMessageId && !input.isFinal) try {
|
|
28
|
+
await editMessageFeishu({
|
|
29
|
+
cfg,
|
|
30
|
+
accountId: target.accountId,
|
|
31
|
+
messageId: input.previousMessageId,
|
|
32
|
+
text
|
|
33
|
+
});
|
|
34
|
+
return { messageId: input.previousMessageId };
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (isMessageNotModified(err)) return { messageId: input.previousMessageId };
|
|
37
|
+
if (isEditTargetGone(err)) log.debug({
|
|
38
|
+
sessionKey: input.sessionKey,
|
|
39
|
+
previousMessageId: input.previousMessageId
|
|
40
|
+
}, "edit target gone; falling back to fresh send");
|
|
41
|
+
else throw err;
|
|
42
|
+
}
|
|
43
|
+
const { api } = createFeishuClient(account);
|
|
44
|
+
const receiveIdType = isOpenId(target.peerId) ? "open_id" : "chat_id";
|
|
45
|
+
const messageId = extractMessageId(await api.im.message.create({
|
|
46
|
+
params: { receive_id_type: receiveIdType },
|
|
47
|
+
data: {
|
|
48
|
+
receive_id: target.peerId,
|
|
49
|
+
msg_type: "text",
|
|
50
|
+
content: JSON.stringify({ text })
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
if (!messageId) throw new Error("feishu workflow progress: send succeeded but no message_id returned");
|
|
54
|
+
return { messageId };
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function resolveTarget(sessionKey) {
|
|
59
|
+
const parsed = parseSessionKey(sessionKey);
|
|
60
|
+
if (!parsed) return null;
|
|
61
|
+
if (parsed.source !== "feishu") return null;
|
|
62
|
+
if (!parsed.peerId) return null;
|
|
63
|
+
return {
|
|
64
|
+
accountId: parsed.accountId || "default",
|
|
65
|
+
peerId: parsed.peerId
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function clampForFeishu(text) {
|
|
69
|
+
if (text.length <= FEISHU_TEXT_MAX) return text;
|
|
70
|
+
return `${text.slice(0, FEISHU_TEXT_MAX - 1)}…`;
|
|
71
|
+
}
|
|
72
|
+
function isOpenId(peerId) {
|
|
73
|
+
return peerId.toLowerCase().startsWith("ou_");
|
|
74
|
+
}
|
|
75
|
+
function extractMessageId(res) {
|
|
76
|
+
if (!res || typeof res !== "object") return void 0;
|
|
77
|
+
const rec = res;
|
|
78
|
+
const data = rec.data;
|
|
79
|
+
const fromData = typeof data?.message_id === "string" ? data.message_id : void 0;
|
|
80
|
+
if (fromData) return fromData;
|
|
81
|
+
return typeof rec.message_id === "string" ? rec.message_id : void 0;
|
|
82
|
+
}
|
|
83
|
+
function isMessageNotModified(err) {
|
|
84
|
+
const msg = errorMessage(err).toLowerCase();
|
|
85
|
+
return msg.includes("not_modified") || msg.includes("not modified");
|
|
86
|
+
}
|
|
87
|
+
function isEditTargetGone(err) {
|
|
88
|
+
const msg = errorMessage(err).toLowerCase();
|
|
89
|
+
return msg.includes("message_not_found") || msg.includes("message not found") || msg.includes("not exist");
|
|
90
|
+
}
|
|
91
|
+
function errorMessage(err) {
|
|
92
|
+
if (!err) return "";
|
|
93
|
+
if (err instanceof Error) return err.message;
|
|
94
|
+
return String(err);
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { createFeishuWorkflowProgressCapability };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=workflow-progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-progress.js","names":[],"sources":["../../../../extensions/feishu/src/workflow-progress.ts"],"sourcesContent":["/**\n * Feishu/Lark capability for the workflow progress broker.\n *\n * Feishu exposes `im.v1.message.update`, so we get the same edit-in-place UX\n * as Telegram: send the first snapshot, then update the same message on\n * every subsequent throttled tick. Key events (phase change / new error /\n * tool_end) bypass the throttle so milestones land promptly.\n *\n * Per-bot send rate ~5 msg/sec/account (Feishu open API limit); a 5-second\n * default throttle leaves comfortable headroom even when several workflows\n * run concurrently.\n *\n * Routing: sessionKey `main:feishu:<accountId>:<dm|group|channel>:<peerId>`.\n * - DM peerId is an `open_id` (starts with `ou_`) → use `receive_id_type=open_id`.\n * - Group / channel peerId is a `chat_id` → use `receive_id_type=chat_id`.\n *\n * Failure handling:\n * - \"edit must not modify same content\" / \"message_not_modified\" → swallow, keep id.\n * - Edit target gone (deleted / 410-class errors) → fall back to a fresh send.\n * - Other errors → rethrow so the broker logs and drops the update; the next\n * key event (or the next throttle tick) retries.\n */\n\nimport type {\n ChannelProgressCapability,\n WorkflowProgressPostInput,\n} from '@xopcai/xopc/agent/workflow/index.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport { parseSessionKey } from '@xopcai/xopc/routing/session-key.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\n\nimport { editMessageFeishu } from './outbound/actions.js';\nimport { resolveFeishuAccount } from './state/accounts.js';\nimport { createFeishuClient } from './transport/client/client.js';\n\nconst log = createLogger('FeishuWorkflowProgress');\n\nconst FEISHU_TEXT_MAX = 4_000;\nconst DEFAULT_THROTTLE_MS = 5_000;\n\nexport function createFeishuWorkflowProgressCapability(opts: {\n getConfig: () => Config | undefined;\n}): ChannelProgressCapability {\n return {\n channelId: 'feishu',\n supportsEdit: true,\n defaultThrottleMs: DEFAULT_THROTTLE_MS,\n defaultMode: 'edit',\n\n async postProgress(input: WorkflowProgressPostInput) {\n const cfg = opts.getConfig();\n if (!cfg) {\n throw new Error('feishu workflow progress: no config loaded');\n }\n const target = resolveTarget(input.sessionKey);\n if (!target) {\n throw new Error(`feishu workflow progress: cannot route sessionKey \"${input.sessionKey}\"`);\n }\n\n const account = resolveFeishuAccount(cfg, target.accountId);\n if (!account.configured) {\n throw new Error(\n `feishu workflow progress: account \"${target.accountId}\" not configured (sessionKey \"${input.sessionKey}\")`,\n );\n }\n\n const text = clampForFeishu(input.text);\n\n // Edit-in-place when we have a previous message id and we're not on the\n // final send. The final tick is always a fresh send so users scrolled\n // past the running bubble can still see the conclusion.\n if (input.previousMessageId && !input.isFinal) {\n try {\n await editMessageFeishu({\n cfg,\n accountId: target.accountId,\n messageId: input.previousMessageId,\n text,\n });\n return { messageId: input.previousMessageId };\n } catch (err) {\n if (isMessageNotModified(err)) {\n return { messageId: input.previousMessageId };\n }\n if (isEditTargetGone(err)) {\n log.debug(\n { sessionKey: input.sessionKey, previousMessageId: input.previousMessageId },\n 'edit target gone; falling back to fresh send',\n );\n // fall through to fresh send below\n } else {\n throw err;\n }\n }\n }\n\n const { api } = createFeishuClient(account);\n const receiveIdType = isOpenId(target.peerId) ? 'open_id' : 'chat_id';\n const res: unknown = await (api as { im: { message: { create(arg: unknown): Promise<unknown> } } }).im.message.create({\n params: { receive_id_type: receiveIdType },\n data: {\n receive_id: target.peerId,\n msg_type: 'text',\n content: JSON.stringify({ text }),\n },\n });\n const messageId = extractMessageId(res);\n if (!messageId) {\n throw new Error('feishu workflow progress: send succeeded but no message_id returned');\n }\n return { messageId };\n },\n };\n}\n\ninterface ResolvedTarget {\n accountId: string;\n peerId: string;\n}\n\nfunction resolveTarget(sessionKey: string): ResolvedTarget | null {\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) return null;\n if (parsed.source !== 'feishu') return null;\n if (!parsed.peerId) return null;\n return {\n accountId: parsed.accountId || 'default',\n peerId: parsed.peerId,\n };\n}\n\nfunction clampForFeishu(text: string): string {\n if (text.length <= FEISHU_TEXT_MAX) return text;\n return `${text.slice(0, FEISHU_TEXT_MAX - 1)}…`;\n}\n\nfunction isOpenId(peerId: string): boolean {\n // Feishu open_id is prefixed `ou_`; chat_id is prefixed `oc_`. Anything\n // else (custom test fixtures, edge cases) defaults to chat_id which is the\n // safer guess for non-DM contexts.\n return peerId.toLowerCase().startsWith('ou_');\n}\n\nfunction extractMessageId(res: unknown): string | undefined {\n if (!res || typeof res !== 'object') return undefined;\n const rec = res as Record<string, unknown>;\n const data = rec.data as Record<string, unknown> | undefined;\n const fromData = typeof data?.message_id === 'string' ? data.message_id : undefined;\n if (fromData) return fromData;\n const direct = typeof rec.message_id === 'string' ? rec.message_id : undefined;\n return direct;\n}\n\nfunction isMessageNotModified(err: unknown): boolean {\n const msg = errorMessage(err).toLowerCase();\n return msg.includes('not_modified') || msg.includes('not modified');\n}\n\nfunction isEditTargetGone(err: unknown): boolean {\n const msg = errorMessage(err).toLowerCase();\n return (\n msg.includes('message_not_found') ||\n msg.includes('message not found') ||\n msg.includes('not exist')\n );\n}\n\nfunction errorMessage(err: unknown): string {\n if (!err) return '';\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":"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"}
|
|
@@ -46,6 +46,8 @@ export declare class TelegramChannelPlugin implements ChannelPlugin<TelegramReso
|
|
|
46
46
|
private commandHandler;
|
|
47
47
|
private inboundProcessor;
|
|
48
48
|
private sessionModelHooks?;
|
|
49
|
+
/** Unregister fn for the workflow-progress capability registered against the global broker. */
|
|
50
|
+
private workflowProgressUnregister;
|
|
49
51
|
config: import('@xopcai/xopc/channels/plugin-types.js').ChannelConfigAdapter<TelegramResolvedAccount>;
|
|
50
52
|
security: import('@xopcai/xopc/channels/plugin-types.js').ChannelSecurityAdapter<TelegramResolvedAccount>;
|
|
51
53
|
status: import('@xopcai/xopc/channels/plugin-types.js').ChannelStatusAdapter<TelegramResolvedAccount>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { TelegramConfigSchema, init_config_schema } from "./config-schema.js";
|
|
1
2
|
import { createLogger } from "../../../src/utils/logger/index.js";
|
|
2
3
|
import { init_logger } from "../../../src/utils/logger.js";
|
|
3
|
-
import {
|
|
4
|
+
import { getWorkflowProgressBroker } from "../../../src/agent/workflow/progress-broker.js";
|
|
5
|
+
import "../../../src/agent/workflow/index.js";
|
|
4
6
|
import { transcribe } from "../../../src/voice/stt/transcribe-core.js";
|
|
5
7
|
import { isSTTAvailable } from "../../../src/voice/stt/availability.js";
|
|
6
8
|
import "../../../src/voice/stt/index.js";
|
|
@@ -15,6 +17,7 @@ import { getChatChannelMeta } from "../../../src/channels/registry.js";
|
|
|
15
17
|
import { getMimeType } from "../../../src/channels/media.js";
|
|
16
18
|
import { TelegramAccountManager } from "./account-manager.js";
|
|
17
19
|
import { createOutboundSender } from "./outbound-sender.js";
|
|
20
|
+
import { createTelegramWorkflowProgressCapability } from "./workflow-progress.js";
|
|
18
21
|
import { createTelegramCommandHandler } from "./command-handler.js";
|
|
19
22
|
import { createInboundProcessor } from "./inbound-processor.js";
|
|
20
23
|
import { TELEGRAM_CHANNEL_DEFAULTS } from "./plugin-defaults.js";
|
|
@@ -95,6 +98,8 @@ var TelegramChannelPlugin = class {
|
|
|
95
98
|
commandHandler;
|
|
96
99
|
inboundProcessor;
|
|
97
100
|
sessionModelHooks;
|
|
101
|
+
/** Unregister fn for the workflow-progress capability registered against the global broker. */
|
|
102
|
+
workflowProgressUnregister = null;
|
|
98
103
|
config;
|
|
99
104
|
security;
|
|
100
105
|
status;
|
|
@@ -109,6 +114,7 @@ var TelegramChannelPlugin = class {
|
|
|
109
114
|
this.accountManager = new TelegramAccountManager();
|
|
110
115
|
this.loadAccounts();
|
|
111
116
|
this.bindOutboundComponents();
|
|
117
|
+
this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(createTelegramWorkflowProgressCapability(this.accountManager));
|
|
112
118
|
const debounceMs = this.defaults.queue?.debounceMs ?? TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs;
|
|
113
119
|
const keyPolicy = telegramDebouncerKeyPolicy();
|
|
114
120
|
this.debouncer = createInboundDebouncer({
|
|
@@ -253,6 +259,10 @@ var TelegramChannelPlugin = class {
|
|
|
253
259
|
await this.accountManager.stopRunner(id);
|
|
254
260
|
log.info({ accountId: id }, "Telegram account stopped");
|
|
255
261
|
}
|
|
262
|
+
if (!accountId) {
|
|
263
|
+
this.workflowProgressUnregister?.();
|
|
264
|
+
this.workflowProgressUnregister = null;
|
|
265
|
+
}
|
|
256
266
|
}
|
|
257
267
|
async startAccount(account) {
|
|
258
268
|
if (this.accountManager.isRunning(account.accountId)) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":["sttTranscribe"],"sources":["../../../../extensions/telegram/src/plugin.ts"],"sourcesContent":["/**\n * Telegram Channel Plugin - Implementation based on ChannelPlugin interface\n *\n * This plugin integrates Telegram with the ChannelPlugin architecture.\n * It reuses components from this package's src tree where possible.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport { Bot, type Context } from 'grammy';\nimport { run } from '@grammyjs/runner';\n\nimport type { Config } from '@xopcai/xopc/config/index.js';\nimport type {\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginSessionModelHooks,\n ChannelPluginStartOptions,\n ChannelOutboundAdapter,\n ChannelSecurityContext,\n ChannelGatewayAdapter,\n ChannelStreamingAdapter,\n ChannelCommandAdapter,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport { generateSessionKey } from '@xopcai/xopc/chat-commands/session-key.js';\nimport { submitClarifyChoiceFromChannel } from '@xopcai/xopc/gateway/clarify-runtime.js';\n\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { issuePairingChallenge, resolveStandardPairingPath } from '@xopcai/xopc/channels/pairing/index.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\nimport { createTimeoutAbortSignal } from './timeout-abort.js';\nimport { createInboundDebouncer } from '@xopcai/xopc/infra/debounce.js';\nimport { getChatChannelMeta } from '@xopcai/xopc/channels/registry.js';\nimport { getMimeType } from '@xopcai/xopc/channels/media.js';\nimport { transcribe as sttTranscribe, isSTTAvailable } from '@xopcai/xopc/voice/stt/index.js';\nimport type { STTConfig } from '@xopcai/xopc/voice/stt/types.js';\n\nimport { TelegramAccountManager } from './account-manager.js';\nimport { createOutboundSender } from './outbound-sender.js';\nimport { createTelegramCommandHandler } from './command-handler.js';\nimport { createInboundProcessor } from './inbound-processor.js';\nimport { TELEGRAM_CHANNEL_DEFAULTS } from './plugin-defaults.js';\nimport {\n createTelegramPluginAdapters,\n createTelegramSetupWizard,\n createTelegramOutboundSendMethods,\n telegramTextChunker,\n TELEGRAM_OUTBOUND_DEFAULTS,\n createTelegramInboundAccessControl,\n telegramDebouncerKeyPolicy,\n type TelegramMessageEvent,\n} from './adapters/index.js';\nimport {\n createTelegramGatewayAdapter,\n createTelegramStreamingAdapter,\n createTelegramCommandAdapter,\n} from './channel.js';\nimport type { TelegramResolvedAccount } from './adapters/index.js';\nimport type { ChannelCronDeliveryAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { normalizeTelegramDeliveryChatId } from './delivery-chat-id.js';\nimport { telegramConfigSurface } from './adapters/config-surface.js';\nimport { telegramOnboardAdapter } from './adapters/onboard-cli.js';\nimport { TelegramConfigSchema } from './config-schema.js';\n\n/** Bound initial `getMe` so a bad `apiRoot` or unreachable API cannot block gateway startup for minutes. */\nconst TELEGRAM_GETME_TIMEOUT_MS = 20_000;\n/** grammY per-request ceiling; must exceed long-poll `getUpdates` (~30s) but avoid multi-minute hangs on bad hosts. */\nconst TELEGRAM_CLIENT_TIMEOUT_SECONDS = 75;\n\nfunction trimOptionalRootUrl(value: string | undefined): string | undefined {\n const t = value?.trim();\n if (!t) return undefined;\n return t.replace(/\\/$/, '');\n}\n\nconst log = createLogger('TelegramPlugin');\n\nconst TELEGRAM_REGISTRY_META = getChatChannelMeta('telegram');\n\nexport type { TelegramResolvedAccount as TelegramAccount } from './channel.js';\n\nexport class TelegramChannelPlugin implements ChannelPlugin<TelegramResolvedAccount> {\n readonly id = 'telegram' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.telegram'],\n };\n\n readonly meta = {\n id: TELEGRAM_REGISTRY_META.id,\n label: TELEGRAM_REGISTRY_META.label,\n selectionLabel: 'Telegram Bot',\n docsPath: '/channels/telegram',\n blurb: TELEGRAM_REGISTRY_META.description,\n order: 0,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities = TELEGRAM_REGISTRY_META.capabilities;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs },\n outbound: { textChunkLimit: TELEGRAM_CHANNEL_DEFAULTS.outbound.textChunkLimit },\n };\n\n readonly setupWizard = createTelegramSetupWizard();\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = TelegramConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly cronDelivery: ChannelCronDeliveryAdapter = {\n async normalizeDeliveryTarget(to) {\n return { chatId: normalizeTelegramDeliveryChatId(to) };\n },\n };\n\n readonly configSurface = telegramConfigSurface;\n\n readonly onboard = telegramOnboardAdapter;\n\n readonly pairing = createStandardPairingAdapter('telegram');\n\n private bus!: NonNullable<ChannelPluginInitOptions['bus']>;\n private cfg!: NonNullable<ChannelPluginInitOptions['config']>;\n private debouncer!: ReturnType<typeof createInboundDebouncer<TelegramMessageEvent>>;\n\n private accountManager!: TelegramAccountManager;\n private outboundSender!: ReturnType<typeof createOutboundSender>;\n private commandHandler!: ReturnType<typeof createTelegramCommandHandler>;\n private inboundProcessor!: ReturnType<typeof createInboundProcessor>;\n private sessionModelHooks?: ChannelPluginSessionModelHooks;\n\n config!: import('@xopcai/xopc/channels/plugin-types.js').ChannelConfigAdapter<TelegramResolvedAccount>;\n security!: import('@xopcai/xopc/channels/plugin-types.js').ChannelSecurityAdapter<TelegramResolvedAccount>;\n status!: import('@xopcai/xopc/channels/plugin-types.js').ChannelStatusAdapter<TelegramResolvedAccount>;\n\n outbound!: ChannelOutboundAdapter;\n\n gateway!: ChannelGatewayAdapter<TelegramResolvedAccount>;\n\n streaming!: ChannelStreamingAdapter;\n\n commands!: ChannelCommandAdapter;\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n this.sessionModelHooks = options.sessionModel;\n\n this.accountManager = new TelegramAccountManager();\n this.loadAccounts();\n this.bindOutboundComponents();\n\n const debounceMs =\n this.defaults.queue?.debounceMs ?? TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs;\n const keyPolicy = telegramDebouncerKeyPolicy();\n this.debouncer = createInboundDebouncer<TelegramMessageEvent>({\n debounceMs,\n ...keyPolicy,\n onFlush: async (items) => {\n await this.processMessages(items);\n },\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Debounced message processing failed');\n },\n });\n\n log.debug('Telegram plugin initialized');\n }\n\n private bindOutboundComponents(): void {\n this.outboundSender = createOutboundSender({\n accountManager: this.accountManager,\n config: this.cfg,\n });\n const sm = this.sessionModelHooks;\n this.commandHandler = createTelegramCommandHandler({\n bus: this.bus,\n config: this.cfg,\n accountManager: this.accountManager,\n getSessionModel: (sessionKey) => sm?.getModelForSession(sessionKey),\n setSessionModel: (sessionKey, modelId) => {\n if (!sm) return;\n void sm.switchModelForSession(sessionKey, modelId);\n },\n });\n const adapters = createTelegramPluginAdapters({\n accountManager: this.accountManager,\n });\n this.config = adapters.config;\n this.security = adapters.security;\n this.status = adapters.status;\n\n const accessControl = createTelegramInboundAccessControl();\n\n this.inboundProcessor = createInboundProcessor({\n bus: this.bus,\n config: this.cfg,\n accountManager: this.accountManager,\n accessControl,\n sessionKeyService: {\n generateSessionKey: (opts) =>\n generateSessionKey({\n source: 'telegram',\n chatId: opts.chatId,\n senderId: opts.senderId,\n isGroup: opts.isGroup,\n threadId: opts.threadId,\n accountId: opts.accountId,\n }),\n },\n sttService: {\n transcribe: async (buffer, config, options) => {\n const result = await sttTranscribe(buffer, config as STTConfig, options);\n return { text: result.text };\n },\n isSTTAvailable: (config) => isSTTAvailable(config as STTConfig | undefined),\n },\n mediaUtils: { getMimeType },\n });\n\n const sends = createTelegramOutboundSendMethods((opts) => this.outboundSender.send(opts));\n this.outbound = {\n deliveryMode: 'direct',\n chunker: telegramTextChunker,\n chunkerMode: 'text',\n textChunkLimit: TELEGRAM_OUTBOUND_DEFAULTS.textChunkLimit,\n ...sends,\n };\n\n this.gateway = createTelegramGatewayAdapter({\n startAccount: (account) => this.startAccount(account),\n stopAccount: (accountId) => this.accountManager.stopRunner(accountId),\n });\n this.streaming = createTelegramStreamingAdapter({ accountManager: this.accountManager });\n this.commands = createTelegramCommandAdapter();\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prevTg = this.cfg.channels?.telegram as unknown;\n const nextTg = cfg.channels?.telegram as { enabled?: boolean } | undefined;\n // Match Weixin: only `enabled === true` keeps inbound (polling). Stops immediately on disable / missing section.\n const channelOff = !nextTg || nextTg.enabled !== true;\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n this.accountManager.reset();\n this.bindOutboundComponents();\n return;\n }\n\n if (isDeepStrictEqual(prevTg, nextTg)) {\n this.cfg = cfg;\n this.bindOutboundComponents();\n // Config unchanged but runners may be stopped (e.g. disable → enable with identical JSON shape after normalize).\n if (!this.channelIsRunning(cfg)) {\n await this.reapplyFromConfig(cfg);\n }\n return;\n }\n await this.reapplyFromConfig(cfg);\n }\n\n private async reapplyFromConfig(cfg: Config): Promise<void> {\n this.cfg = cfg;\n await this.stop();\n this.accountManager.reset();\n const telegramCfg = cfg.channels?.telegram as Record<string, unknown> | undefined;\n const channelOn = telegramCfg != null && telegramCfg.enabled === true;\n if (!channelOn) {\n this.bindOutboundComponents();\n return;\n }\n this.loadAccounts();\n this.bindOutboundComponents();\n await this.start();\n }\n\n private loadAccounts(): void {\n const telegramCfg = this.cfg.channels?.telegram as Record<string, unknown> | undefined;\n if (!telegramCfg) return;\n if (telegramCfg.enabled !== true) {\n return;\n }\n\n const accounts = telegramCfg.accounts as Record<string, any> | undefined;\n if (!accounts || Object.keys(accounts).length === 0) return;\n\n const channelApiRoot = trimOptionalRootUrl(\n typeof telegramCfg.apiRoot === 'string' ? telegramCfg.apiRoot : undefined,\n );\n const channelProxy =\n typeof telegramCfg.proxy === 'string' && telegramCfg.proxy.trim()\n ? telegramCfg.proxy.trim()\n : undefined;\n\n for (const [id, account] of Object.entries(accounts)) {\n const accApiRoot = trimOptionalRootUrl(\n typeof account.apiRoot === 'string' ? account.apiRoot : undefined,\n );\n const accProxy =\n typeof account.proxy === 'string' && account.proxy.trim() ? account.proxy.trim() : undefined;\n this.accountManager.registerAccount({\n ...account,\n accountId: id,\n ...(accApiRoot || channelApiRoot ? { apiRoot: accApiRoot || channelApiRoot } : {}),\n ...(accProxy || channelProxy ? { proxy: accProxy || channelProxy } : {}),\n });\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n return this.accountManager.getAllAccounts().some(\n (a) => a.enabled !== false && !!a.botToken && this.accountManager.isRunning(a.accountId),\n );\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.telegram as { enabled?: boolean } | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const accountIds = options?.accountId\n ? [options.accountId]\n : this.config.listAccountIds(this.cfg);\n\n for (const accountId of accountIds) {\n const account = this.config.resolveAccount(this.cfg, accountId);\n if (!account.enabled || !account.botToken) continue;\n await this.startAccount(account);\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n if (!this.config) return;\n const ids = accountId ? [accountId] : this.config.listAccountIds(this.cfg);\n for (const id of ids) {\n await this.accountManager.stopRunner(id);\n log.info({ accountId: id }, 'Telegram account stopped');\n }\n }\n\n private async startAccount(account: TelegramResolvedAccount): Promise<void> {\n if (this.accountManager.isRunning(account.accountId)) return;\n if (!account.botToken) return;\n\n this.accountManager.markStarting(account.accountId);\n\n try {\n const client = {\n timeoutSeconds: TELEGRAM_CLIENT_TIMEOUT_SECONDS,\n ...(account.apiRoot ? { apiRoot: account.apiRoot } : {}),\n };\n const bot = new Bot(account.botToken, { client });\n const getMeSignal = createTimeoutAbortSignal(TELEGRAM_GETME_TIMEOUT_MS);\n let me;\n try {\n me = await bot.api.getMe(getMeSignal.signal);\n } finally {\n getMeSignal.dispose();\n }\n\n const runner = run(bot, {\n runner: {\n fetch: { timeout: 30 },\n silent: true,\n maxRetryTime: Number.POSITIVE_INFINITY,\n retryInterval: 'exponential',\n },\n });\n\n this.accountManager.registerBot(account.accountId, bot);\n this.accountManager.registerRunner(account.accountId, runner);\n this.attachRunnerExitHandler(account.accountId, runner);\n this.accountManager.setBotUsername(account.accountId, me.username);\n this.accountManager.updateStatus({\n accountId: account.accountId,\n running: true,\n mode: 'polling',\n });\n\n this.setupMessageHandler(account.accountId, bot);\n\n log.info({ accountId: account.accountId, username: me.username }, 'Telegram account started');\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { accountId: account.accountId, apiRootConfigured: !!account.apiRoot, errorMessage: em },\n `Telegram account not started (check token, network, or apiRoot): ${em}`,\n );\n this.accountManager.updateStatus({\n accountId: account.accountId,\n running: false,\n mode: 'stopped',\n lastError: em.slice(0, 400),\n });\n } finally {\n this.accountManager.markStartComplete(account.accountId);\n }\n }\n\n private attachRunnerExitHandler(accountId: string, runner: ReturnType<typeof run>): void {\n const task = runner.task();\n if (!task) return;\n void task.catch((err) => {\n log.error({ err, accountId }, 'Telegram polling runner exited');\n void this.accountManager.stopRunner(accountId).catch((e) => {\n log.error({ err: e, accountId }, 'Telegram runner cleanup failed');\n });\n });\n }\n\n private setupMessageHandler(accountId: string, bot: Bot): void {\n bot.on('message', async (ctx) => {\n try {\n const text = ctx.message.text ?? ctx.message.caption ?? '';\n const command = text.trim().split(' ')[0].split('@')[0].toLowerCase();\n\n if (command === '/models') {\n await this.commandHandler.handleModels(ctx);\n return;\n }\n\n if (command === '/start') {\n await this.commandHandler.handleStart(ctx);\n return;\n }\n\n if (command === '/cleanup') {\n await this.commandHandler.handleCleanup(ctx);\n return;\n }\n\n await this.debouncer.enqueue({ ctx, accountId, message: ctx.message });\n } catch (err) {\n log.error({ accountId, err }, 'Message handler error');\n }\n });\n\n bot.on('callback_query:data', async (ctx) => {\n try {\n await this.handleCallbackQuery(ctx, accountId);\n } catch (err) {\n log.error({ accountId, err }, 'Callback query handler error');\n await ctx.answerCallbackQuery('An error occurred').catch(() => {});\n }\n });\n }\n\n private async handleCallbackQuery(ctx: Context, _accountId: string): Promise<void> {\n const data = ctx.callbackQuery?.data;\n if (!data) return;\n\n if (data.startsWith('provider:')) {\n const providerId = data.substring('provider:'.length);\n await this.commandHandler.handleProviderSelect(ctx, providerId);\n return;\n }\n\n if (data.startsWith('model:')) {\n const modelId = data.substring('model:'.length);\n await this.commandHandler.handleModelSelect(ctx, modelId);\n return;\n }\n\n if (data === 'cancel') {\n await this.commandHandler.handleCancel(ctx);\n return;\n }\n\n if (data === 'providers') {\n await this.commandHandler.handleShowProviders(ctx);\n return;\n }\n\n if (data === 'cleanup:confirm') {\n await this.commandHandler.handleCleanupConfirm(ctx);\n return;\n }\n\n if (data.startsWith('clarify:')) {\n const rest = data.slice('clarify:'.length);\n const lastColon = rest.lastIndexOf(':');\n if (lastColon > 0) {\n const requestId = rest.slice(0, lastColon);\n const idx = Number.parseInt(rest.slice(lastColon + 1), 10);\n if (requestId && Number.isFinite(idx) && submitClarifyChoiceFromChannel(requestId, idx)) {\n await ctx.answerCallbackQuery();\n return;\n }\n }\n await ctx.answerCallbackQuery('No pending question');\n return;\n }\n\n await ctx.answerCallbackQuery('Unknown action');\n }\n\n private async processMessages(items: TelegramMessageEvent[]): Promise<void> {\n if (items.length === 0) return;\n\n const tgSection = this.cfg.channels?.telegram as { enabled?: boolean } | undefined;\n if (!tgSection || tgSection.enabled !== true) {\n return;\n }\n\n const last = items[items.length - 1];\n const ctx = last.ctx;\n const accountId = last.accountId;\n if (!this.accountManager.getAccount(accountId)) {\n log.warn({ accountId }, 'Unknown account');\n return;\n }\n\n const account = this.config.resolveAccount(this.cfg, accountId);\n\n const isGroup = ctx.chat?.type === 'group' || ctx.chat?.type === 'supergroup';\n const senderId = ctx.from?.id?.toString() ?? '';\n const senderUsername = ctx.from?.username;\n const chatId = ctx.chat?.id?.toString() ?? '';\n\n const securityCtx: ChannelSecurityContext = {\n accountId,\n chatId,\n senderId,\n senderName: senderUsername,\n isGroup,\n };\n\n const accessResult = this.security.checkAccess?.(securityCtx, account, this.cfg);\n\n if (!accessResult?.allowed) {\n if (!isGroup && accessResult?.reason === 'pairing-required') {\n const cid = ctx.chat?.id;\n const bot = cid != null ? this.accountManager.getBot(accountId) : undefined;\n if (bot && cid != null && senderId) {\n void issuePairingChallenge({\n channel: 'telegram',\n pairingFilePath: resolveStandardPairingPath('telegram', accountId),\n accountId,\n senderId,\n senderIdLine: `Your Telegram user id: ${senderId}`,\n sendPairingReply: async (text) => {\n await bot.api.sendMessage(cid, text);\n },\n onCreated: ({ code }) => {\n log.info({ accountId, senderId, code }, 'Telegram DM pairing code issued');\n },\n onReplyError: (err) => {\n log.warn({ err, accountId, chatId: String(cid) }, 'Telegram pairing reply failed');\n },\n });\n }\n return;\n }\n log.warn(\n {\n accountId,\n chatId,\n senderId,\n isGroup,\n reason: accessResult?.reason,\n dmPolicy: account.dmPolicy,\n groupPolicy: account.groupPolicy,\n },\n 'Telegram: message dropped by channel security (check dmPolicy/groupPolicy and allowFrom)',\n );\n return;\n }\n\n await this.inboundProcessor(ctx, accountId);\n }\n}\n\nexport const telegramPlugin = new TelegramChannelPlugin();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","names":["sttTranscribe"],"sources":["../../../../extensions/telegram/src/plugin.ts"],"sourcesContent":["/**\n * Telegram Channel Plugin - Implementation based on ChannelPlugin interface\n *\n * This plugin integrates Telegram with the ChannelPlugin architecture.\n * It reuses components from this package's src tree where possible.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport { Bot, type Context } from 'grammy';\nimport { run } from '@grammyjs/runner';\n\nimport type { Config } from '@xopcai/xopc/config/index.js';\nimport type {\n ChannelPlugin,\n ChannelPluginDefaults,\n ChannelPluginInitOptions,\n ChannelPluginReloadMeta,\n ChannelPluginSessionModelHooks,\n ChannelPluginStartOptions,\n ChannelOutboundAdapter,\n ChannelSecurityContext,\n ChannelGatewayAdapter,\n ChannelStreamingAdapter,\n ChannelCommandAdapter,\n} from '@xopcai/xopc/channels/plugin-types.js';\nimport { generateSessionKey } from '@xopcai/xopc/chat-commands/session-key.js';\nimport { submitClarifyChoiceFromChannel } from '@xopcai/xopc/gateway/clarify-runtime.js';\n\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\nimport { issuePairingChallenge, resolveStandardPairingPath } from '@xopcai/xopc/channels/pairing/index.js';\nimport { createStandardPairingAdapter } from '@xopcai/xopc/channels/pairing/pairing-store-adapter.js';\nimport { createTimeoutAbortSignal } from './timeout-abort.js';\nimport { createInboundDebouncer } from '@xopcai/xopc/infra/debounce.js';\nimport { getChatChannelMeta } from '@xopcai/xopc/channels/registry.js';\nimport { getMimeType } from '@xopcai/xopc/channels/media.js';\nimport { transcribe as sttTranscribe, isSTTAvailable } from '@xopcai/xopc/voice/stt/index.js';\nimport type { STTConfig } from '@xopcai/xopc/voice/stt/types.js';\n\nimport { TelegramAccountManager } from './account-manager.js';\nimport { createOutboundSender } from './outbound-sender.js';\nimport { getWorkflowProgressBroker } from '@xopcai/xopc/agent/workflow/index.js';\nimport { createTelegramWorkflowProgressCapability } from './workflow-progress.js';\nimport { createTelegramCommandHandler } from './command-handler.js';\nimport { createInboundProcessor } from './inbound-processor.js';\nimport { TELEGRAM_CHANNEL_DEFAULTS } from './plugin-defaults.js';\nimport {\n createTelegramPluginAdapters,\n createTelegramSetupWizard,\n createTelegramOutboundSendMethods,\n telegramTextChunker,\n TELEGRAM_OUTBOUND_DEFAULTS,\n createTelegramInboundAccessControl,\n telegramDebouncerKeyPolicy,\n type TelegramMessageEvent,\n} from './adapters/index.js';\nimport {\n createTelegramGatewayAdapter,\n createTelegramStreamingAdapter,\n createTelegramCommandAdapter,\n} from './channel.js';\nimport type { TelegramResolvedAccount } from './adapters/index.js';\nimport type { ChannelCronDeliveryAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport { normalizeTelegramDeliveryChatId } from './delivery-chat-id.js';\nimport { telegramConfigSurface } from './adapters/config-surface.js';\nimport { telegramOnboardAdapter } from './adapters/onboard-cli.js';\nimport { TelegramConfigSchema } from './config-schema.js';\n\n/** Bound initial `getMe` so a bad `apiRoot` or unreachable API cannot block gateway startup for minutes. */\nconst TELEGRAM_GETME_TIMEOUT_MS = 20_000;\n/** grammY per-request ceiling; must exceed long-poll `getUpdates` (~30s) but avoid multi-minute hangs on bad hosts. */\nconst TELEGRAM_CLIENT_TIMEOUT_SECONDS = 75;\n\nfunction trimOptionalRootUrl(value: string | undefined): string | undefined {\n const t = value?.trim();\n if (!t) return undefined;\n return t.replace(/\\/$/, '');\n}\n\nconst log = createLogger('TelegramPlugin');\n\nconst TELEGRAM_REGISTRY_META = getChatChannelMeta('telegram');\n\nexport type { TelegramResolvedAccount as TelegramAccount } from './channel.js';\n\nexport class TelegramChannelPlugin implements ChannelPlugin<TelegramResolvedAccount> {\n readonly id = 'telegram' as const;\n\n readonly reload: ChannelPluginReloadMeta = {\n configPrefixes: ['channels.telegram'],\n };\n\n readonly meta = {\n id: TELEGRAM_REGISTRY_META.id,\n label: TELEGRAM_REGISTRY_META.label,\n selectionLabel: 'Telegram Bot',\n docsPath: '/channels/telegram',\n blurb: TELEGRAM_REGISTRY_META.description,\n order: 0,\n deferConnectUntilAfterListen: true,\n } as const;\n\n readonly capabilities = TELEGRAM_REGISTRY_META.capabilities;\n\n readonly defaults: ChannelPluginDefaults = {\n queue: { debounceMs: TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs },\n outbound: { textChunkLimit: TELEGRAM_CHANNEL_DEFAULTS.outbound.textChunkLimit },\n };\n\n readonly setupWizard = createTelegramSetupWizard();\n\n readonly configSchema = {\n schema: {},\n validate: (raw: unknown) => {\n const r = TelegramConfigSchema.safeParse(raw);\n return r.success ? { ok: true as const } : { ok: false as const, errors: [r.error.message] };\n },\n };\n\n readonly cronDelivery: ChannelCronDeliveryAdapter = {\n async normalizeDeliveryTarget(to) {\n return { chatId: normalizeTelegramDeliveryChatId(to) };\n },\n };\n\n readonly configSurface = telegramConfigSurface;\n\n readonly onboard = telegramOnboardAdapter;\n\n readonly pairing = createStandardPairingAdapter('telegram');\n\n private bus!: NonNullable<ChannelPluginInitOptions['bus']>;\n private cfg!: NonNullable<ChannelPluginInitOptions['config']>;\n private debouncer!: ReturnType<typeof createInboundDebouncer<TelegramMessageEvent>>;\n\n private accountManager!: TelegramAccountManager;\n private outboundSender!: ReturnType<typeof createOutboundSender>;\n private commandHandler!: ReturnType<typeof createTelegramCommandHandler>;\n private inboundProcessor!: ReturnType<typeof createInboundProcessor>;\n private sessionModelHooks?: ChannelPluginSessionModelHooks;\n /** Unregister fn for the workflow-progress capability registered against the global broker. */\n private workflowProgressUnregister: (() => void) | null = null;\n\n config!: import('@xopcai/xopc/channels/plugin-types.js').ChannelConfigAdapter<TelegramResolvedAccount>;\n security!: import('@xopcai/xopc/channels/plugin-types.js').ChannelSecurityAdapter<TelegramResolvedAccount>;\n status!: import('@xopcai/xopc/channels/plugin-types.js').ChannelStatusAdapter<TelegramResolvedAccount>;\n\n outbound!: ChannelOutboundAdapter;\n\n gateway!: ChannelGatewayAdapter<TelegramResolvedAccount>;\n\n streaming!: ChannelStreamingAdapter;\n\n commands!: ChannelCommandAdapter;\n\n async init(options: ChannelPluginInitOptions): Promise<void> {\n this.bus = options.bus;\n this.cfg = options.config;\n this.sessionModelHooks = options.sessionModel;\n\n this.accountManager = new TelegramAccountManager();\n this.loadAccounts();\n this.bindOutboundComponents();\n\n // Workflow progress is fed from the agent-side broker (subscribes to\n // tool_execution_update for the `workflow` tool); the capability turns\n // each snapshot into an edit-in-place Telegram message. Registration is\n // idempotent — the broker replaces a prior cap with the same channelId.\n this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(\n createTelegramWorkflowProgressCapability(this.accountManager),\n );\n\n const debounceMs =\n this.defaults.queue?.debounceMs ?? TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs;\n const keyPolicy = telegramDebouncerKeyPolicy();\n this.debouncer = createInboundDebouncer<TelegramMessageEvent>({\n debounceMs,\n ...keyPolicy,\n onFlush: async (items) => {\n await this.processMessages(items);\n },\n onError: (err, items) => {\n log.error({ err, count: items.length }, 'Debounced message processing failed');\n },\n });\n\n log.debug('Telegram plugin initialized');\n }\n\n private bindOutboundComponents(): void {\n this.outboundSender = createOutboundSender({\n accountManager: this.accountManager,\n config: this.cfg,\n });\n const sm = this.sessionModelHooks;\n this.commandHandler = createTelegramCommandHandler({\n bus: this.bus,\n config: this.cfg,\n accountManager: this.accountManager,\n getSessionModel: (sessionKey) => sm?.getModelForSession(sessionKey),\n setSessionModel: (sessionKey, modelId) => {\n if (!sm) return;\n void sm.switchModelForSession(sessionKey, modelId);\n },\n });\n const adapters = createTelegramPluginAdapters({\n accountManager: this.accountManager,\n });\n this.config = adapters.config;\n this.security = adapters.security;\n this.status = adapters.status;\n\n const accessControl = createTelegramInboundAccessControl();\n\n this.inboundProcessor = createInboundProcessor({\n bus: this.bus,\n config: this.cfg,\n accountManager: this.accountManager,\n accessControl,\n sessionKeyService: {\n generateSessionKey: (opts) =>\n generateSessionKey({\n source: 'telegram',\n chatId: opts.chatId,\n senderId: opts.senderId,\n isGroup: opts.isGroup,\n threadId: opts.threadId,\n accountId: opts.accountId,\n }),\n },\n sttService: {\n transcribe: async (buffer, config, options) => {\n const result = await sttTranscribe(buffer, config as STTConfig, options);\n return { text: result.text };\n },\n isSTTAvailable: (config) => isSTTAvailable(config as STTConfig | undefined),\n },\n mediaUtils: { getMimeType },\n });\n\n const sends = createTelegramOutboundSendMethods((opts) => this.outboundSender.send(opts));\n this.outbound = {\n deliveryMode: 'direct',\n chunker: telegramTextChunker,\n chunkerMode: 'text',\n textChunkLimit: TELEGRAM_OUTBOUND_DEFAULTS.textChunkLimit,\n ...sends,\n };\n\n this.gateway = createTelegramGatewayAdapter({\n startAccount: (account) => this.startAccount(account),\n stopAccount: (accountId) => this.accountManager.stopRunner(accountId),\n });\n this.streaming = createTelegramStreamingAdapter({ accountManager: this.accountManager });\n this.commands = createTelegramCommandAdapter();\n }\n\n async onConfigUpdated(cfg: Config): Promise<void> {\n const prevTg = this.cfg.channels?.telegram as unknown;\n const nextTg = cfg.channels?.telegram as { enabled?: boolean } | undefined;\n // Match Weixin: only `enabled === true` keeps inbound (polling). Stops immediately on disable / missing section.\n const channelOff = !nextTg || nextTg.enabled !== true;\n if (channelOff) {\n this.cfg = cfg;\n await this.stop();\n this.accountManager.reset();\n this.bindOutboundComponents();\n return;\n }\n\n if (isDeepStrictEqual(prevTg, nextTg)) {\n this.cfg = cfg;\n this.bindOutboundComponents();\n // Config unchanged but runners may be stopped (e.g. disable → enable with identical JSON shape after normalize).\n if (!this.channelIsRunning(cfg)) {\n await this.reapplyFromConfig(cfg);\n }\n return;\n }\n await this.reapplyFromConfig(cfg);\n }\n\n private async reapplyFromConfig(cfg: Config): Promise<void> {\n this.cfg = cfg;\n await this.stop();\n this.accountManager.reset();\n const telegramCfg = cfg.channels?.telegram as Record<string, unknown> | undefined;\n const channelOn = telegramCfg != null && telegramCfg.enabled === true;\n if (!channelOn) {\n this.bindOutboundComponents();\n return;\n }\n this.loadAccounts();\n this.bindOutboundComponents();\n await this.start();\n }\n\n private loadAccounts(): void {\n const telegramCfg = this.cfg.channels?.telegram as Record<string, unknown> | undefined;\n if (!telegramCfg) return;\n if (telegramCfg.enabled !== true) {\n return;\n }\n\n const accounts = telegramCfg.accounts as Record<string, any> | undefined;\n if (!accounts || Object.keys(accounts).length === 0) return;\n\n const channelApiRoot = trimOptionalRootUrl(\n typeof telegramCfg.apiRoot === 'string' ? telegramCfg.apiRoot : undefined,\n );\n const channelProxy =\n typeof telegramCfg.proxy === 'string' && telegramCfg.proxy.trim()\n ? telegramCfg.proxy.trim()\n : undefined;\n\n for (const [id, account] of Object.entries(accounts)) {\n const accApiRoot = trimOptionalRootUrl(\n typeof account.apiRoot === 'string' ? account.apiRoot : undefined,\n );\n const accProxy =\n typeof account.proxy === 'string' && account.proxy.trim() ? account.proxy.trim() : undefined;\n this.accountManager.registerAccount({\n ...account,\n accountId: id,\n ...(accApiRoot || channelApiRoot ? { apiRoot: accApiRoot || channelApiRoot } : {}),\n ...(accProxy || channelProxy ? { proxy: accProxy || channelProxy } : {}),\n });\n }\n }\n\n channelIsRunning(cfg: Config): boolean {\n return this.accountManager.getAllAccounts().some(\n (a) => a.enabled !== false && !!a.botToken && this.accountManager.isRunning(a.accountId),\n );\n }\n\n async start(options?: ChannelPluginStartOptions): Promise<void> {\n const section = this.cfg.channels?.telegram as { enabled?: boolean } | undefined;\n if (!section || section.enabled !== true) {\n return;\n }\n\n const accountIds = options?.accountId\n ? [options.accountId]\n : this.config.listAccountIds(this.cfg);\n\n for (const accountId of accountIds) {\n const account = this.config.resolveAccount(this.cfg, accountId);\n if (!account.enabled || !account.botToken) continue;\n await this.startAccount(account);\n }\n }\n\n async stop(accountId?: string): Promise<void> {\n if (!this.config) return;\n const ids = accountId ? [accountId] : this.config.listAccountIds(this.cfg);\n for (const id of ids) {\n await this.accountManager.stopRunner(id);\n log.info({ accountId: id }, 'Telegram account stopped');\n }\n // Only unregister the broker capability on a full stop (no specific\n // account). A per-account stop still leaves other accounts capable of\n // sending progress through the same registered cap.\n if (!accountId) {\n this.workflowProgressUnregister?.();\n this.workflowProgressUnregister = null;\n }\n }\n\n private async startAccount(account: TelegramResolvedAccount): Promise<void> {\n if (this.accountManager.isRunning(account.accountId)) return;\n if (!account.botToken) return;\n\n this.accountManager.markStarting(account.accountId);\n\n try {\n const client = {\n timeoutSeconds: TELEGRAM_CLIENT_TIMEOUT_SECONDS,\n ...(account.apiRoot ? { apiRoot: account.apiRoot } : {}),\n };\n const bot = new Bot(account.botToken, { client });\n const getMeSignal = createTimeoutAbortSignal(TELEGRAM_GETME_TIMEOUT_MS);\n let me;\n try {\n me = await bot.api.getMe(getMeSignal.signal);\n } finally {\n getMeSignal.dispose();\n }\n\n const runner = run(bot, {\n runner: {\n fetch: { timeout: 30 },\n silent: true,\n maxRetryTime: Number.POSITIVE_INFINITY,\n retryInterval: 'exponential',\n },\n });\n\n this.accountManager.registerBot(account.accountId, bot);\n this.accountManager.registerRunner(account.accountId, runner);\n this.attachRunnerExitHandler(account.accountId, runner);\n this.accountManager.setBotUsername(account.accountId, me.username);\n this.accountManager.updateStatus({\n accountId: account.accountId,\n running: true,\n mode: 'polling',\n });\n\n this.setupMessageHandler(account.accountId, bot);\n\n log.info({ accountId: account.accountId, username: me.username }, 'Telegram account started');\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { accountId: account.accountId, apiRootConfigured: !!account.apiRoot, errorMessage: em },\n `Telegram account not started (check token, network, or apiRoot): ${em}`,\n );\n this.accountManager.updateStatus({\n accountId: account.accountId,\n running: false,\n mode: 'stopped',\n lastError: em.slice(0, 400),\n });\n } finally {\n this.accountManager.markStartComplete(account.accountId);\n }\n }\n\n private attachRunnerExitHandler(accountId: string, runner: ReturnType<typeof run>): void {\n const task = runner.task();\n if (!task) return;\n void task.catch((err) => {\n log.error({ err, accountId }, 'Telegram polling runner exited');\n void this.accountManager.stopRunner(accountId).catch((e) => {\n log.error({ err: e, accountId }, 'Telegram runner cleanup failed');\n });\n });\n }\n\n private setupMessageHandler(accountId: string, bot: Bot): void {\n bot.on('message', async (ctx) => {\n try {\n const text = ctx.message.text ?? ctx.message.caption ?? '';\n const command = text.trim().split(' ')[0].split('@')[0].toLowerCase();\n\n if (command === '/models') {\n await this.commandHandler.handleModels(ctx);\n return;\n }\n\n if (command === '/start') {\n await this.commandHandler.handleStart(ctx);\n return;\n }\n\n if (command === '/cleanup') {\n await this.commandHandler.handleCleanup(ctx);\n return;\n }\n\n await this.debouncer.enqueue({ ctx, accountId, message: ctx.message });\n } catch (err) {\n log.error({ accountId, err }, 'Message handler error');\n }\n });\n\n bot.on('callback_query:data', async (ctx) => {\n try {\n await this.handleCallbackQuery(ctx, accountId);\n } catch (err) {\n log.error({ accountId, err }, 'Callback query handler error');\n await ctx.answerCallbackQuery('An error occurred').catch(() => {});\n }\n });\n }\n\n private async handleCallbackQuery(ctx: Context, _accountId: string): Promise<void> {\n const data = ctx.callbackQuery?.data;\n if (!data) return;\n\n if (data.startsWith('provider:')) {\n const providerId = data.substring('provider:'.length);\n await this.commandHandler.handleProviderSelect(ctx, providerId);\n return;\n }\n\n if (data.startsWith('model:')) {\n const modelId = data.substring('model:'.length);\n await this.commandHandler.handleModelSelect(ctx, modelId);\n return;\n }\n\n if (data === 'cancel') {\n await this.commandHandler.handleCancel(ctx);\n return;\n }\n\n if (data === 'providers') {\n await this.commandHandler.handleShowProviders(ctx);\n return;\n }\n\n if (data === 'cleanup:confirm') {\n await this.commandHandler.handleCleanupConfirm(ctx);\n return;\n }\n\n if (data.startsWith('clarify:')) {\n const rest = data.slice('clarify:'.length);\n const lastColon = rest.lastIndexOf(':');\n if (lastColon > 0) {\n const requestId = rest.slice(0, lastColon);\n const idx = Number.parseInt(rest.slice(lastColon + 1), 10);\n if (requestId && Number.isFinite(idx) && submitClarifyChoiceFromChannel(requestId, idx)) {\n await ctx.answerCallbackQuery();\n return;\n }\n }\n await ctx.answerCallbackQuery('No pending question');\n return;\n }\n\n await ctx.answerCallbackQuery('Unknown action');\n }\n\n private async processMessages(items: TelegramMessageEvent[]): Promise<void> {\n if (items.length === 0) return;\n\n const tgSection = this.cfg.channels?.telegram as { enabled?: boolean } | undefined;\n if (!tgSection || tgSection.enabled !== true) {\n return;\n }\n\n const last = items[items.length - 1];\n const ctx = last.ctx;\n const accountId = last.accountId;\n if (!this.accountManager.getAccount(accountId)) {\n log.warn({ accountId }, 'Unknown account');\n return;\n }\n\n const account = this.config.resolveAccount(this.cfg, accountId);\n\n const isGroup = ctx.chat?.type === 'group' || ctx.chat?.type === 'supergroup';\n const senderId = ctx.from?.id?.toString() ?? '';\n const senderUsername = ctx.from?.username;\n const chatId = ctx.chat?.id?.toString() ?? '';\n\n const securityCtx: ChannelSecurityContext = {\n accountId,\n chatId,\n senderId,\n senderName: senderUsername,\n isGroup,\n };\n\n const accessResult = this.security.checkAccess?.(securityCtx, account, this.cfg);\n\n if (!accessResult?.allowed) {\n if (!isGroup && accessResult?.reason === 'pairing-required') {\n const cid = ctx.chat?.id;\n const bot = cid != null ? this.accountManager.getBot(accountId) : undefined;\n if (bot && cid != null && senderId) {\n void issuePairingChallenge({\n channel: 'telegram',\n pairingFilePath: resolveStandardPairingPath('telegram', accountId),\n accountId,\n senderId,\n senderIdLine: `Your Telegram user id: ${senderId}`,\n sendPairingReply: async (text) => {\n await bot.api.sendMessage(cid, text);\n },\n onCreated: ({ code }) => {\n log.info({ accountId, senderId, code }, 'Telegram DM pairing code issued');\n },\n onReplyError: (err) => {\n log.warn({ err, accountId, chatId: String(cid) }, 'Telegram pairing reply failed');\n },\n });\n }\n return;\n }\n log.warn(\n {\n accountId,\n chatId,\n senderId,\n isGroup,\n reason: accessResult?.reason,\n dmPolicy: account.dmPolicy,\n groupPolicy: account.groupPolicy,\n },\n 'Telegram: message dropped by channel security (check dmPolicy/groupPolicy and allowFrom)',\n );\n return;\n }\n\n await this.inboundProcessor(ctx, accountId);\n }\n}\n\nexport const telegramPlugin = new TelegramChannelPlugin();\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createLogger } from "../../../src/utils/logger/index.js";
|
|
2
|
-
import { init_logger } from "../../../src/utils/logger.js";
|
|
3
1
|
import { buildSessionKey } from "../../../src/routing/session-key.js";
|
|
4
2
|
import { applyIdentityLinks, resolveRoute } from "../../../src/routing/resolve-route.js";
|
|
3
|
+
import { createLogger } from "../../../src/utils/logger/index.js";
|
|
4
|
+
import { init_logger } from "../../../src/utils/logger.js";
|
|
5
5
|
//#region extensions/telegram/src/routing-integration.ts
|
|
6
6
|
init_logger();
|
|
7
7
|
const log = createLogger("TelegramRouting");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram capability for the {@link WorkflowProgressBroker}.
|
|
3
|
+
*
|
|
4
|
+
* Strategy: send the first progress update as a fresh message, then edit it in
|
|
5
|
+
* place on every subsequent update. The broker enforces per-channel throttle
|
|
6
|
+
* (default 5 s) and bypasses it for key events (phase change / new error /
|
|
7
|
+
* tool_end), so users always see milestones promptly without us hammering
|
|
8
|
+
* Telegram's edit rate limit.
|
|
9
|
+
*
|
|
10
|
+
* Failure handling is intentionally narrow:
|
|
11
|
+
* - `message is not modified` from `editMessageText` → swallow (it's a
|
|
12
|
+
* no-op the broker shouldn't retry).
|
|
13
|
+
* - Lost / inaccessible message during edit → fall back to a new send so
|
|
14
|
+
* the user always sees the latest snapshot.
|
|
15
|
+
* - 429 / network → bubble up, broker logs and drops the update; the next
|
|
16
|
+
* scheduled (or key) tick will retry.
|
|
17
|
+
*
|
|
18
|
+
* We deliberately use plain text (no parse_mode) — the snapshot renderer
|
|
19
|
+
* emits ASCII-friendly output and we don't want HTML/markdown surprises with
|
|
20
|
+
* `<` in agent labels or shell-style payloads.
|
|
21
|
+
*/
|
|
22
|
+
import type { ChannelProgressCapability } from '@xopcai/xopc/agent/workflow/index.js';
|
|
23
|
+
import type { TelegramAccountManager } from './account-manager.js';
|
|
24
|
+
export declare function createTelegramWorkflowProgressCapability(accountManager: TelegramAccountManager): ChannelProgressCapability;
|