@phuetz/code-buddy 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -163
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +12 -12
- package/dist/agent/base-agent.d.ts +4 -0
- package/dist/agent/base-agent.js +6 -0
- package/dist/agent/codebuddy-agent.d.ts +1 -1
- package/dist/agent/codebuddy-agent.js +69 -14
- package/dist/agent/execution/agent-executor.js +122 -12
- package/dist/agent/execution/tool-dependency-graph.js +7 -0
- package/dist/agent/execution/tool-hooks.d.ts +6 -0
- package/dist/agent/execution/tool-hooks.js +13 -5
- package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
- package/dist/agent/execution/tool-selection-strategy.js +67 -10
- package/dist/agent/extended-thinking.d.ts +8 -0
- package/dist/agent/extended-thinking.js +27 -0
- package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
- package/dist/agent/facades/infrastructure-facade.js +15 -6
- package/dist/agent/hermes-browser-backends.js +151 -11
- package/dist/agent/hermes-claw-migrate.d.ts +59 -0
- package/dist/agent/hermes-claw-migrate.js +427 -26
- package/dist/agent/hermes-parity-manifest.js +78 -71
- package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
- package/dist/agent/hermes-runtime-lifecycle.js +468 -0
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
- package/dist/agent/middleware/visual-validation-middleware.js +67 -0
- package/dist/agent/model-benchmark.d.ts +77 -0
- package/dist/agent/model-benchmark.js +309 -0
- package/dist/agent/model-tier.d.ts +14 -0
- package/dist/agent/model-tier.js +71 -0
- package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
- package/dist/agent/multi-agent/multi-agent-system.js +3 -1
- package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
- package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
- package/dist/agent/multi-agent/session-tools.js +22 -5
- package/dist/agent/reasoning/mcts.js +20 -6
- package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
- package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
- package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
- package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
- package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
- package/dist/agent/self-improvement/authored-tool-store.js +57 -0
- package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
- package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
- package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
- package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
- package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
- package/dist/agent/self-improvement/self-knowledge.js +24 -0
- package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
- package/dist/agent/self-improvement/skill-benchmark.js +22 -0
- package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
- package/dist/agent/self-improvement/skill-consolidator.js +137 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
- package/dist/agent/self-improvement/skill-engine.js +87 -0
- package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
- package/dist/agent/self-improvement/skill-gate.js +62 -0
- package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
- package/dist/agent/self-improvement/skill-mutator.js +223 -0
- package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
- package/dist/agent/self-improvement/skill-proposer.js +82 -0
- package/dist/agent/self-improvement/skill-types.d.ts +41 -0
- package/dist/agent/self-improvement/skill-types.js +13 -0
- package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
- package/dist/agent/self-improvement/tool-benchmark.js +37 -0
- package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
- package/dist/agent/self-improvement/tool-engine.js +101 -0
- package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
- package/dist/agent/self-improvement/tool-gate.js +78 -0
- package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
- package/dist/agent/self-improvement/tool-proposer.js +34 -0
- package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
- package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
- package/dist/agent/self-improvement/tool-types.d.ts +48 -0
- package/dist/agent/self-improvement/tool-types.js +9 -0
- package/dist/agent/self-improvement/types.d.ts +3 -1
- package/dist/agent/session-end-flush.d.ts +6 -1
- package/dist/agent/session-end-flush.js +20 -4
- package/dist/agent/streaming/streaming-handler.d.ts +7 -0
- package/dist/agent/streaming/streaming-handler.js +8 -1
- package/dist/agent/tool-executor.js +1 -0
- package/dist/agent/tool-handler.d.ts +5 -0
- package/dist/agent/tool-handler.js +133 -4
- package/dist/browser-automation/browser-operator-executor.js +15 -0
- package/dist/browser-automation/browser-use-runner.d.ts +96 -0
- package/dist/browser-automation/browser-use-runner.js +492 -0
- package/dist/browser-automation/camofox-runner.d.ts +107 -0
- package/dist/browser-automation/camofox-runner.js +287 -0
- package/dist/channels/dingtalk/index.js +3 -0
- package/dist/channels/discord/client.d.ts +9 -0
- package/dist/channels/discord/client.js +12 -0
- package/dist/channels/feishu/index.d.ts +167 -1
- package/dist/channels/feishu/index.js +432 -7
- package/dist/channels/gateway-lifecycle.d.ts +132 -0
- package/dist/channels/gateway-lifecycle.js +219 -0
- package/dist/channels/google-chat/index.d.ts +5 -1
- package/dist/channels/google-chat/index.js +5 -1
- package/dist/channels/imessage/index.d.ts +14 -0
- package/dist/channels/imessage/index.js +64 -17
- package/dist/channels/index.d.ts +4 -0
- package/dist/channels/index.js +4 -0
- package/dist/channels/irc/index.d.ts +73 -3
- package/dist/channels/irc/index.js +446 -11
- package/dist/channels/line/index.js +3 -0
- package/dist/channels/mattermost/index.d.ts +51 -4
- package/dist/channels/mattermost/index.js +303 -20
- package/dist/channels/nextcloud-talk/index.d.ts +89 -8
- package/dist/channels/nextcloud-talk/index.js +367 -16
- package/dist/channels/nostr/index.d.ts +121 -1
- package/dist/channels/nostr/index.js +396 -8
- package/dist/channels/ntfy/index.js +3 -0
- package/dist/channels/qq/index.js +3 -0
- package/dist/channels/slash-parity.d.ts +117 -0
- package/dist/channels/slash-parity.js +185 -0
- package/dist/channels/synology-chat/index.js +3 -0
- package/dist/channels/teams/index.d.ts +3 -0
- package/dist/channels/teams/index.js +3 -0
- package/dist/channels/telegram/client.d.ts +9 -0
- package/dist/channels/telegram/client.js +12 -0
- package/dist/channels/twilio-voice/index.js +3 -0
- package/dist/channels/webchat/index.d.ts +7 -1
- package/dist/channels/webchat/index.js +7 -1
- package/dist/channels/wecom/index.js +3 -0
- package/dist/channels/weixin/index.js +3 -0
- package/dist/channels/zalo/index.js +3 -0
- package/dist/cli/config-loader.js +1 -1
- package/dist/codebuddy/client.d.ts +30 -1
- package/dist/codebuddy/client.js +167 -20
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +7 -1
- package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
- package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
- package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
- package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
- package/dist/codebuddy/tool-definitions/index.js +2 -1
- package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
- package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
- package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
- package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
- package/dist/codebuddy/tools.d.ts +8 -1
- package/dist/codebuddy/tools.js +67 -24
- package/dist/commands/cli/hermes-commands.js +108 -0
- package/dist/commands/cli/improve-command.js +123 -0
- package/dist/commands/cli/native-engine-commands.js +102 -3
- package/dist/commands/cli/utility-commands.js +45 -0
- package/dist/commands/enhanced-command-handler.js +2 -2
- package/dist/commands/goal-cli.d.ts +30 -0
- package/dist/commands/goal-cli.js +210 -27
- package/dist/commands/handlers/goal-handler.d.ts +9 -2
- package/dist/commands/handlers/goal-handler.js +36 -9
- package/dist/commands/handlers/infra-handlers.js +36 -0
- package/dist/commands/handlers/memory-handlers.js +96 -1
- package/dist/commands/handlers/missing-handlers.d.ts +1 -1
- package/dist/commands/handlers/missing-handlers.js +26 -3
- package/dist/commands/headless-slash.d.ts +2 -0
- package/dist/commands/headless-slash.js +12 -0
- package/dist/commands/llm-provider-resolution.d.ts +5 -3
- package/dist/commands/llm-provider-resolution.js +87 -33
- package/dist/commands/ollama.d.ts +25 -0
- package/dist/commands/ollama.js +100 -0
- package/dist/commands/provider.d.ts +5 -0
- package/dist/commands/provider.js +106 -55
- package/dist/commands/skills-cli/index.js +123 -0
- package/dist/commands/slash/builtin-commands.js +1 -1
- package/dist/commands/spec-next.js +2 -1
- package/dist/commands/spec-plan.js +5 -16
- package/dist/commands/tunnel.d.ts +2 -0
- package/dist/commands/tunnel.js +48 -0
- package/dist/companion/percepts.js +11 -1
- package/dist/config/config-resolver.d.ts +2 -1
- package/dist/config/config-resolver.js +54 -41
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.js +7 -0
- package/dist/config/env-schema.js +756 -0
- package/dist/config/model-tools.js +4 -4
- package/dist/config/toml-config.d.ts +16 -0
- package/dist/config/toml-config.js +3 -0
- package/dist/context/bootstrap-loader.js +6 -23
- package/dist/context/context-manager-v2.js +1 -0
- package/dist/context/import-directive-parser.d.ts +4 -0
- package/dist/context/import-directive-parser.js +51 -6
- package/dist/context/instruction-excludes.d.ts +30 -1
- package/dist/context/instruction-excludes.js +71 -1
- package/dist/context/jit-context.d.ts +8 -10
- package/dist/context/jit-context.js +28 -106
- package/dist/context/project-context.d.ts +90 -0
- package/dist/context/project-context.js +295 -0
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +3 -3
- package/dist/daemon/autonomous-loop.d.ts +31 -1
- package/dist/daemon/autonomous-loop.js +91 -4
- package/dist/daemon/colab-goal.js +9 -1
- package/dist/daemon/cron-agent-bridge.d.ts +12 -3
- package/dist/daemon/cron-agent-bridge.js +25 -9
- package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
- package/dist/desktop/codebuddy-engine-adapter.js +257 -80
- package/dist/desktop/engine-adapter.d.ts +14 -0
- package/dist/desktop-automation/automation-manager.js +16 -0
- package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
- package/dist/desktop-automation/omniparser-runner.js +115 -0
- package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
- package/dist/desktop-automation/smart-snapshot.js +72 -5
- package/dist/doctor/index.js +40 -23
- package/dist/events/types.d.ts +11 -0
- package/dist/fleet/colab-store.js +14 -2
- package/dist/fleet/peer-session-bridge.js +50 -12
- package/dist/fleet/peer-tool-bridge.js +14 -0
- package/dist/goals/goal-decomposer.d.ts +27 -0
- package/dist/goals/goal-decomposer.js +293 -0
- package/dist/goals/goal-judge-client.d.ts +15 -0
- package/dist/goals/goal-judge-client.js +45 -0
- package/dist/goals/goal-judge.d.ts +2 -0
- package/dist/goals/goal-judge.js +35 -6
- package/dist/goals/goal-loop.d.ts +14 -0
- package/dist/goals/goal-loop.js +47 -2
- package/dist/goals/goal-manager.d.ts +6 -0
- package/dist/goals/goal-manager.js +67 -14
- package/dist/goals/goal-state.d.ts +13 -0
- package/dist/goals/goal-state.js +83 -13
- package/dist/goals/goal-store.d.ts +3 -0
- package/dist/goals/goal-store.js +31 -15
- package/dist/harness/contract.d.ts +28 -28
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/identity/identity-manager.js +3 -2
- package/dist/index.js +481 -26
- package/dist/input/text-to-speech.d.ts +2 -6
- package/dist/input/text-to-speech.js +2 -27
- package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
- package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
- package/dist/integrations/tailscale.d.ts +13 -0
- package/dist/integrations/tailscale.js +87 -34
- package/dist/knowledge/workspace-indexer.js +53 -9
- package/dist/mcp/client.js +1 -0
- package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
- package/dist/mcp/mcp-desktop-tools.js +158 -0
- package/dist/mcp/mcp-resources.js +2 -3
- package/dist/mcp/mcp-server.js +2 -0
- package/dist/memory/enhanced-memory.d.ts +10 -0
- package/dist/memory/enhanced-memory.js +33 -2
- package/dist/memory/index.d.ts +3 -1
- package/dist/memory/index.js +4 -1
- package/dist/memory/memory-auto-proposer.d.ts +23 -0
- package/dist/memory/memory-auto-proposer.js +308 -0
- package/dist/memory/memory-candidate-queue.d.ts +92 -0
- package/dist/memory/memory-candidate-queue.js +261 -0
- package/dist/memory/persistent-memory.d.ts +45 -2
- package/dist/memory/persistent-memory.js +236 -40
- package/dist/ml/bayesian-qualifier.d.ts +1 -1
- package/dist/ml/bayesian-qualifier.js +35 -2
- package/dist/openclaw/gateway-bridge.js +5 -0
- package/dist/plugins/marketplace.d.ts +1 -0
- package/dist/plugins/marketplace.js +7 -0
- package/dist/prompts/execution-discipline.d.ts +14 -0
- package/dist/prompts/execution-discipline.js +29 -0
- package/dist/prompts/variation-injector.js +9 -2
- package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
- package/dist/protocols/acp/acp-agentic-runner.js +115 -18
- package/dist/protocols/acp/acp-session-store.d.ts +23 -0
- package/dist/protocols/acp/acp-session-store.js +77 -0
- package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
- package/dist/protocols/acp/acp-stdio-server.js +71 -16
- package/dist/providers/active-llm-registry.d.ts +37 -0
- package/dist/providers/active-llm-registry.js +186 -0
- package/dist/providers/auxiliary-provider.d.ts +25 -0
- package/dist/providers/auxiliary-provider.js +192 -0
- package/dist/providers/codex-oauth.d.ts +1 -1
- package/dist/providers/codex-oauth.js +27 -4
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/provider-catalog.d.ts +70 -0
- package/dist/providers/provider-catalog.js +738 -0
- package/dist/providers/provider-fallback.d.ts +35 -0
- package/dist/providers/provider-fallback.js +171 -0
- package/dist/providers/xai-oauth.d.ts +128 -0
- package/dist/providers/xai-oauth.js +735 -0
- package/dist/scheduler/cron-scheduler.d.ts +15 -3
- package/dist/scheduler/cron-scheduler.js +53 -7
- package/dist/scheduler/script-runner.d.ts +2 -0
- package/dist/scheduler/script-runner.js +19 -4
- package/dist/scheduler/watchdog-handlers.js +7 -20
- package/dist/search/usearch-index.js +7 -2
- package/dist/security/tool-policy/tool-groups.js +2 -0
- package/dist/sensory/dreaming.d.ts +45 -0
- package/dist/sensory/dreaming.js +114 -0
- package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
- package/dist/sensory/heartbeat-scheduler.js +72 -0
- package/dist/sensory/reactions.d.ts +24 -0
- package/dist/sensory/reactions.js +31 -0
- package/dist/sensory/screen-reaction.d.ts +23 -0
- package/dist/sensory/screen-reaction.js +59 -0
- package/dist/sensory/sensory-bridge.d.ts +23 -0
- package/dist/sensory/sensory-bridge.js +85 -0
- package/dist/sensory/sensory-memory.d.ts +20 -0
- package/dist/sensory/sensory-memory.js +39 -0
- package/dist/sensory/speech-reaction.d.ts +21 -0
- package/dist/sensory/speech-reaction.js +83 -0
- package/dist/sensory/vision-reaction.d.ts +31 -0
- package/dist/sensory/vision-reaction.js +74 -0
- package/dist/server/index.js +106 -2
- package/dist/server/routes/mobile.d.ts +12 -4
- package/dist/server/routes/mobile.js +116 -1
- package/dist/server/tls-config.d.ts +35 -0
- package/dist/server/tls-config.js +142 -0
- package/dist/server/tunnel-manager.d.ts +20 -0
- package/dist/server/tunnel-manager.js +58 -0
- package/dist/server/websocket/fleet-bridge.js +5 -0
- package/dist/services/prompt-builder.d.ts +11 -0
- package/dist/services/prompt-builder.js +124 -17
- package/dist/shared/engine-types.d.ts +15 -1
- package/dist/sidecar/sidecar-bridge.d.ts +1 -0
- package/dist/sidecar/sidecar-bridge.js +21 -10
- package/dist/skills/parser.js +3 -0
- package/dist/skills/skill-importer.d.ts +58 -0
- package/dist/skills/skill-importer.js +261 -0
- package/dist/skills/skill-sources.d.ts +20 -0
- package/dist/skills/skill-sources.js +102 -0
- package/dist/skills/types.d.ts +6 -0
- package/dist/spec/spec-store.js +8 -1
- package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
- package/dist/talk-mode/providers/audioreader-tts.js +8 -8
- package/dist/templates/project-scaffolding.js +3 -2
- package/dist/themes/theme-schema.d.ts +10 -10
- package/dist/tools/application-profiles.js +38 -0
- package/dist/tools/bash/bash-tool.d.ts +1 -0
- package/dist/tools/bash/bash-tool.js +16 -2
- package/dist/tools/bash/command-validator.js +3 -0
- package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
- package/dist/tools/bash/rtk-rewrite.js +100 -0
- package/dist/tools/bash/streaming-executor.js +14 -1
- package/dist/tools/computer-control-tool.d.ts +16 -1
- package/dist/tools/computer-control-tool.js +317 -5
- package/dist/tools/document-generator.d.ts +14 -0
- package/dist/tools/document-generator.js +79 -0
- package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
- package/dist/tools/execute-code-rpc-invoker.js +46 -5
- package/dist/tools/metadata.js +28 -0
- package/dist/tools/office-macro-tool.d.ts +10 -0
- package/dist/tools/office-macro-tool.js +93 -0
- package/dist/tools/register-tool-handler.d.ts +25 -0
- package/dist/tools/register-tool-handler.js +100 -0
- package/dist/tools/registry/index.d.ts +2 -1
- package/dist/tools/registry/index.js +2 -1
- package/dist/tools/registry/memory-tools.d.ts +20 -0
- package/dist/tools/registry/memory-tools.js +225 -3
- package/dist/tools/registry/multimodal-tools.js +2 -2
- package/dist/tools/registry/vision-tools.d.ts +33 -0
- package/dist/tools/registry/vision-tools.js +308 -6
- package/dist/tools/registry/windows-tools.d.ts +2 -0
- package/dist/tools/registry/windows-tools.js +47 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +8 -0
- package/dist/tools/route-peer-tool.js +14 -0
- package/dist/tools/screenshot-tool.js +14 -2
- package/dist/tools/text-to-speech-tool.d.ts +1 -1
- package/dist/tools/text-to-speech-tool.js +2 -12
- package/dist/tools/tool-selector.js +3 -59
- package/dist/utils/config-validation/schema.d.ts +40 -7
- package/dist/utils/config-validation/schema.js +130 -1
- package/dist/utils/cost-tracker.js +21 -1
- package/dist/utils/disk-guard.d.ts +180 -0
- package/dist/utils/disk-guard.js +404 -0
- package/dist/utils/init-project.d.ts +7 -0
- package/dist/utils/init-project.js +37 -0
- package/dist/utils/interactive-setup.js +35 -2
- package/dist/utils/model-utils.js +11 -2
- package/dist/utils/output-sanitizer.js +11 -0
- package/dist/utils/provider-detector.d.ts +8 -7
- package/dist/utils/provider-detector.js +19 -77
- package/dist/utils/settings-manager.d.ts +19 -0
- package/dist/utils/token-display.d.ts +3 -3
- package/dist/utils/token-display.js +22 -4
- package/dist/wizard/onboarding.d.ts +39 -0
- package/dist/wizard/onboarding.js +433 -21
- package/dist/wizard/provider-onboarding.d.ts +8 -23
- package/dist/wizard/provider-onboarding.js +39 -158
- package/package.json +4 -2
- package/dist/plugins/provider-onboarding.d.ts +0 -23
- package/dist/plugins/provider-onboarding.js +0 -116
- package/dist/utils/kokoro-tts.d.ts +0 -8
- package/dist/utils/kokoro-tts.js +0 -67
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camoufox Runner — Playwright-server protocol (NOT Chrome CDP)
|
|
3
|
+
*
|
|
4
|
+
* Camoufox is an anti-detect **Firefox**. It does NOT speak the Chrome
|
|
5
|
+
* DevTools Protocol and exposes no `/json/version` endpoint. The upstream
|
|
6
|
+
* supported automation surface is a **Playwright server**: the Python
|
|
7
|
+
* `camoufox` package launches Playwright's `firefox` `launchServer`, which
|
|
8
|
+
* prints a Playwright-server WebSocket endpoint (`ws://<host>:<port>/<guid>`).
|
|
9
|
+
* The caller connects to it with `playwright.firefox.connect(wsEndpoint)` —
|
|
10
|
+
* NOT `chromium.connectOverCDP()` (that is Chromium-only and invalid here).
|
|
11
|
+
*
|
|
12
|
+
* This runner spawns the server, parses the printed wsEndpoint, and returns
|
|
13
|
+
* it. The caller owns the `firefox.connect()` step (see
|
|
14
|
+
* `src/agent/hermes-browser-backends.ts → runCamofoxSmoke`).
|
|
15
|
+
*
|
|
16
|
+
* ── Live-validated on this host (camoufox 0.4.11, 2026-06-14) ──────────────
|
|
17
|
+
* The repo's Node Playwright 1.58.2 `firefox.connect()` SUCCESSFULLY connects
|
|
18
|
+
* to a server launched by the venv's Playwright 1.58.0 driver — same-minor
|
|
19
|
+
* patch skew passes Playwright's version guard, so a real end-to-end page
|
|
20
|
+
* load (title + heading round-trip) works. If a future ecosystem bump widens
|
|
21
|
+
* the gap to a different minor (e.g. server 1.49 vs client 1.58), `connect`
|
|
22
|
+
* throws a version-guard error; the caller surfaces that honestly rather than
|
|
23
|
+
* faking success.
|
|
24
|
+
*
|
|
25
|
+
* ── 0.4.11 CLI bug worked around here ──────────────────────────────────────
|
|
26
|
+
* `python -m camoufox server` (and `camoufox.server.launch_server()` with no
|
|
27
|
+
* args) is BROKEN in 0.4.11: `launch_options()` emits `proxy: null`, which the
|
|
28
|
+
* bundled `launchServer.js` rejects with
|
|
29
|
+
* `proxy: expected object, got null → Failed to launch browser`. We drive the
|
|
30
|
+
* same public `launch_server` plumbing from a tiny inline Python launcher that
|
|
31
|
+
* strips null-valued options before piping them to `launchServer.js`, which is
|
|
32
|
+
* the supported path minus the upstream null bug.
|
|
33
|
+
*
|
|
34
|
+
* Falls back gracefully when Camoufox is not installed — every public function
|
|
35
|
+
* returns a typed result rather than throwing.
|
|
36
|
+
*/
|
|
37
|
+
import { spawn } from 'child_process';
|
|
38
|
+
import { logger } from '../utils/logger.js';
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Internals
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
43
|
+
/**
|
|
44
|
+
* Inline Python launcher. Reproduces `camoufox.server.launch_server()` but
|
|
45
|
+
* strips null-valued options (the 0.4.11 `proxy: null` CLI bug) before piping
|
|
46
|
+
* the config to the bundled `launchServer.js`, which prints the wsEndpoint.
|
|
47
|
+
*
|
|
48
|
+
* `headless` is templated in as a literal `True`/`False`.
|
|
49
|
+
* `executable_path` is passed only when a binary override is supplied.
|
|
50
|
+
*/
|
|
51
|
+
function buildLauncherScript(headless, binaryPath) {
|
|
52
|
+
const launchKwargs = binaryPath
|
|
53
|
+
? `headless=${headless ? 'True' : 'False'}, executable_path=${JSON.stringify(binaryPath)}`
|
|
54
|
+
: `headless=${headless ? 'True' : 'False'}`;
|
|
55
|
+
return [
|
|
56
|
+
'import base64, subprocess, sys',
|
|
57
|
+
'try:',
|
|
58
|
+
' import orjson',
|
|
59
|
+
' def _dumps(o): return orjson.dumps(o)',
|
|
60
|
+
'except Exception:',
|
|
61
|
+
' import json',
|
|
62
|
+
' def _dumps(o): return json.dumps(o).encode()',
|
|
63
|
+
'from pathlib import Path',
|
|
64
|
+
'from camoufox.utils import launch_options',
|
|
65
|
+
'from camoufox.server import LAUNCH_SCRIPT, get_nodejs, to_camel_case_dict',
|
|
66
|
+
`config = launch_options(${launchKwargs})`,
|
|
67
|
+
'# Work around camoufox 0.4.11: launch_options emits proxy=None, which',
|
|
68
|
+
"# launchServer.js rejects with 'proxy: expected object, got null'.",
|
|
69
|
+
'config = {k: v for k, v in config.items() if v is not None}',
|
|
70
|
+
'nodejs = get_nodejs()',
|
|
71
|
+
'data = _dumps(to_camel_case_dict(config))',
|
|
72
|
+
'proc = subprocess.Popen([nodejs, str(LAUNCH_SCRIPT)],',
|
|
73
|
+
' cwd=Path(nodejs).parent / "package",',
|
|
74
|
+
' stdin=subprocess.PIPE, text=True)',
|
|
75
|
+
'proc.stdin.write(base64.b64encode(data).decode())',
|
|
76
|
+
'proc.stdin.close()',
|
|
77
|
+
'proc.wait()',
|
|
78
|
+
].join('\n');
|
|
79
|
+
}
|
|
80
|
+
// Strip ANSI escape sequences (the endpoint is printed wrapped in colour codes).
|
|
81
|
+
// eslint-disable-next-line no-control-regex
|
|
82
|
+
const ANSI_PATTERN = /\[[0-9;]*m/g;
|
|
83
|
+
/**
|
|
84
|
+
* Extract the Playwright-server WS endpoint from accumulated server stdout.
|
|
85
|
+
* Upstream prints: `Websocket endpoint:\x1b[93m ws://host:port/guid \x1b[0m`.
|
|
86
|
+
*
|
|
87
|
+
* Only returns an endpoint once the line is **complete** — i.e. the buffer
|
|
88
|
+
* contains a delimiter *after* the `ws://…` token (the trailing ANSI reset is
|
|
89
|
+
* stripped, so we look for whitespace / newline / end-of-input following it).
|
|
90
|
+
* This avoids resolving a truncated URL when the pipe splits mid-line.
|
|
91
|
+
*/
|
|
92
|
+
export function parseWsEndpoint(text) {
|
|
93
|
+
const cleaned = text.replace(ANSI_PATTERN, '');
|
|
94
|
+
// Require the ws token to be followed by whitespace or end-of-string, which
|
|
95
|
+
// only happens once the full line (incl. trailing reset) has arrived.
|
|
96
|
+
const match = cleaned.match(/(ws:\/\/\S+?)(?:\s|$)/i);
|
|
97
|
+
const candidate = match?.[1];
|
|
98
|
+
if (!candidate)
|
|
99
|
+
return null;
|
|
100
|
+
// Guard against an end-of-buffer partial: only accept when something
|
|
101
|
+
// (whitespace/newline) actually terminates the token, not bare EOF on the
|
|
102
|
+
// very last char with no trailing delimiter seen yet.
|
|
103
|
+
const terminated = new RegExp(`${escapeRegExp(candidate)}\\s`).test(cleaned);
|
|
104
|
+
return terminated ? candidate : null;
|
|
105
|
+
}
|
|
106
|
+
function escapeRegExp(value) {
|
|
107
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Public API
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
/**
|
|
113
|
+
* Launch a Camoufox Playwright server and return its wsEndpoint.
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* const result = await launchCamofox({ headless: true });
|
|
117
|
+
* if (result.ok) {
|
|
118
|
+
* const playwright = await import('playwright');
|
|
119
|
+
* // Firefox-based — use firefox.connect(), NOT chromium.connectOverCDP().
|
|
120
|
+
* const browser = await playwright.firefox.connect(result.wsEndpoint!);
|
|
121
|
+
* // ... drive the browser ...
|
|
122
|
+
* await browser.close();
|
|
123
|
+
* await closeCamofox(result.pid!);
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export async function launchCamofox(options = {}) {
|
|
128
|
+
const timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
129
|
+
const headless = options.headless ?? true;
|
|
130
|
+
const python = options.pythonPath ?? (process.env.CODEBUDDY_CAMOFOX_PYTHON?.trim() || 'python3');
|
|
131
|
+
const binaryPath = options.binaryPath ?? (process.env.CODEBUDDY_CAMOFOX_BINARY?.trim() || undefined);
|
|
132
|
+
const script = buildLauncherScript(headless, binaryPath);
|
|
133
|
+
// Spawn the Python launcher detached so it leads its own process group; the
|
|
134
|
+
// server tree is python → node(launchServer.js) → firefox. Killing only the
|
|
135
|
+
// python PID would orphan the node server + Firefox, so we reap the whole
|
|
136
|
+
// group in closeCamofox via `process.kill(-pid)`.
|
|
137
|
+
let proc;
|
|
138
|
+
try {
|
|
139
|
+
proc = spawn(python, ['-c', script], {
|
|
140
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
141
|
+
windowsHide: true,
|
|
142
|
+
detached: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
147
|
+
logger.warn(`[camofox-runner] Failed to spawn ${python}: ${message}`);
|
|
148
|
+
return { ok: false, error: `Failed to spawn ${python}: ${message}` };
|
|
149
|
+
}
|
|
150
|
+
// Don't let the launcher keep our event loop alive once we've handed back.
|
|
151
|
+
proc.unref();
|
|
152
|
+
return await new Promise((resolve) => {
|
|
153
|
+
let settled = false;
|
|
154
|
+
let stdoutBuf = '';
|
|
155
|
+
let stderrBuf = '';
|
|
156
|
+
const finish = (result) => {
|
|
157
|
+
if (settled)
|
|
158
|
+
return;
|
|
159
|
+
settled = true;
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
resolve(result);
|
|
162
|
+
};
|
|
163
|
+
const timer = setTimeout(() => {
|
|
164
|
+
// No endpoint in time — kill the (partial) tree and report.
|
|
165
|
+
const pid = proc.pid;
|
|
166
|
+
if (pid !== undefined) {
|
|
167
|
+
try {
|
|
168
|
+
process.kill(-pid, 'SIGKILL');
|
|
169
|
+
}
|
|
170
|
+
catch { /* best-effort */ }
|
|
171
|
+
}
|
|
172
|
+
const detail = classifyServerError(stderrBuf || stdoutBuf);
|
|
173
|
+
logger.warn(`[camofox-runner] No wsEndpoint within ${timeoutMs}ms. ${detail}`);
|
|
174
|
+
finish({
|
|
175
|
+
ok: false,
|
|
176
|
+
error: `Camoufox server did not print a wsEndpoint within ${timeoutMs}ms. ${detail}`.trim(),
|
|
177
|
+
pid,
|
|
178
|
+
});
|
|
179
|
+
}, timeoutMs);
|
|
180
|
+
proc.stdout?.on('data', (chunk) => {
|
|
181
|
+
stdoutBuf += chunk.toString();
|
|
182
|
+
const endpoint = parseWsEndpoint(stdoutBuf);
|
|
183
|
+
if (endpoint) {
|
|
184
|
+
logger.debug(`[camofox-runner] Playwright server ready at ${endpoint} (pid ${proc.pid})`);
|
|
185
|
+
finish({ ok: true, wsEndpoint: endpoint, pid: proc.pid });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
proc.stderr?.on('data', (chunk) => {
|
|
189
|
+
stderrBuf += chunk.toString();
|
|
190
|
+
});
|
|
191
|
+
proc.on('error', (err) => {
|
|
192
|
+
const message = err.message;
|
|
193
|
+
const hint = /ENOENT/.test(message)
|
|
194
|
+
? ` Python interpreter '${python}' not found, or the camoufox package is not installed. ` +
|
|
195
|
+
'Install with: python3 -m venv <venv> && <venv>/bin/pip install "camoufox[geoip]" && set options.pythonPath.'
|
|
196
|
+
: '';
|
|
197
|
+
logger.warn(`[camofox-runner] Spawn error: ${message}${hint}`);
|
|
198
|
+
finish({ ok: false, error: `Spawn error: ${message}${hint}`, pid: proc.pid });
|
|
199
|
+
});
|
|
200
|
+
proc.on('close', (code) => {
|
|
201
|
+
if (settled)
|
|
202
|
+
return;
|
|
203
|
+
const detail = classifyServerError(stderrBuf || stdoutBuf);
|
|
204
|
+
finish({
|
|
205
|
+
ok: false,
|
|
206
|
+
error: `Camoufox server exited (code ${code ?? 'null'}) before printing a wsEndpoint. ${detail}`.trim(),
|
|
207
|
+
pid: proc.pid,
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Map common server failures to an actionable message. Surfaces the
|
|
214
|
+
* Playwright version-guard case explicitly (HONEST handling of the
|
|
215
|
+
* version-lock the task calls out) rather than masking it.
|
|
216
|
+
*/
|
|
217
|
+
function classifyServerError(output) {
|
|
218
|
+
const text = output.trim();
|
|
219
|
+
if (!text)
|
|
220
|
+
return '';
|
|
221
|
+
// Playwright server/client version guard. The server is launched by the
|
|
222
|
+
// Python camoufox package's Playwright driver; the caller connects with the
|
|
223
|
+
// repo's Node Playwright. A minor-version mismatch trips this guard.
|
|
224
|
+
if (/version|incompatible|handshake|\b428\b/i.test(text) && /playwright/i.test(text)) {
|
|
225
|
+
return ('Camoufox server requires a Playwright version compatible with the ' +
|
|
226
|
+
"installed camoufox package's driver; the repo is pinned to Playwright " +
|
|
227
|
+
'1.58.x. If connect() later fails with a version-guard error, align the ' +
|
|
228
|
+
'venv camoufox Playwright minor with the repo pin (same minor; patch ' +
|
|
229
|
+
`skew is tolerated). Server output: ${firstNonEmptyLine(text)}`);
|
|
230
|
+
}
|
|
231
|
+
if (/No module named ['"]?camoufox|ModuleNotFoundError/i.test(text)) {
|
|
232
|
+
return ('The camoufox Python package is not importable by the chosen interpreter. ' +
|
|
233
|
+
'Install with: python3 -m venv <venv> && <venv>/bin/pip install ' +
|
|
234
|
+
'"camoufox[geoip]" and pass options.pythonPath=<venv>/bin/python.');
|
|
235
|
+
}
|
|
236
|
+
if (/proxy: expected object, got null/i.test(text)) {
|
|
237
|
+
// Should not happen — we strip nulls — but report clearly if upstream changes.
|
|
238
|
+
return 'Camoufox launch options were rejected (proxy: null). The installed camoufox version may differ from the one this runner targets.';
|
|
239
|
+
}
|
|
240
|
+
return `Server output: ${firstNonEmptyLine(text)}`;
|
|
241
|
+
}
|
|
242
|
+
function firstNonEmptyLine(text) {
|
|
243
|
+
return text.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? text.slice(0, 200);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Gracefully terminate a previously launched Camoufox server tree.
|
|
247
|
+
*
|
|
248
|
+
* The launcher is spawned `detached`, so its PID leads a process group that
|
|
249
|
+
* contains the node Playwright server and every Firefox process. We signal the
|
|
250
|
+
* whole group (`-pid`) — signalling only the python leader would orphan the
|
|
251
|
+
* node server and the browser.
|
|
252
|
+
*/
|
|
253
|
+
export async function closeCamofox(pid) {
|
|
254
|
+
const killGroup = (signal) => {
|
|
255
|
+
try {
|
|
256
|
+
process.kill(-pid, signal);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Group already gone, or never became a group leader; fall back to the
|
|
261
|
+
// single PID so we still make a best-effort kill.
|
|
262
|
+
try {
|
|
263
|
+
process.kill(pid, signal);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
if (!killGroup('SIGTERM'))
|
|
272
|
+
return; // already gone
|
|
273
|
+
// Give the tree a moment to exit before escalating.
|
|
274
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
275
|
+
// Is the leader still alive? (signal 0 = existence probe)
|
|
276
|
+
let alive = true;
|
|
277
|
+
try {
|
|
278
|
+
process.kill(pid, 0);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
alive = false;
|
|
282
|
+
}
|
|
283
|
+
if (alive) {
|
|
284
|
+
killGroup('SIGKILL');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=camofox-runner.js.map
|
|
@@ -108,6 +108,9 @@ export class DingTalkChannel extends BaseChannel {
|
|
|
108
108
|
isAtAll: config.isAtAll,
|
|
109
109
|
}, options);
|
|
110
110
|
}
|
|
111
|
+
// REST/webhook adapter — outbound messages are one-shot HTTPS POSTs to the
|
|
112
|
+
// DingTalk robot webhook; there is no persistent connection, so reconnection
|
|
113
|
+
// (ReconnectionManager) is N/A.
|
|
111
114
|
async connect() {
|
|
112
115
|
await this.adapter.start();
|
|
113
116
|
this.status.connected = true;
|
|
@@ -143,6 +143,15 @@ export declare class DiscordChannel extends BaseChannel {
|
|
|
143
143
|
* Build action row from buttons
|
|
144
144
|
*/
|
|
145
145
|
private buildActionRow;
|
|
146
|
+
/**
|
|
147
|
+
* Return the slash command names registered with this adapter.
|
|
148
|
+
*
|
|
149
|
+
* Implements {@link SlashCommandProvider} so the slash-parity checker can
|
|
150
|
+
* report real parity instead of falling back to an empty list. Names come
|
|
151
|
+
* from the configured `commands` (the same set registered with Discord via
|
|
152
|
+
* {@link registerCommands}), normalized without a leading slash.
|
|
153
|
+
*/
|
|
154
|
+
getRegisteredCommands(): string[];
|
|
146
155
|
/**
|
|
147
156
|
* Send typing indicator
|
|
148
157
|
*/
|
|
@@ -712,6 +712,18 @@ export class DiscordChannel extends BaseChannel {
|
|
|
712
712
|
components,
|
|
713
713
|
};
|
|
714
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Return the slash command names registered with this adapter.
|
|
717
|
+
*
|
|
718
|
+
* Implements {@link SlashCommandProvider} so the slash-parity checker can
|
|
719
|
+
* report real parity instead of falling back to an empty list. Names come
|
|
720
|
+
* from the configured `commands` (the same set registered with Discord via
|
|
721
|
+
* {@link registerCommands}), normalized without a leading slash.
|
|
722
|
+
*/
|
|
723
|
+
getRegisteredCommands() {
|
|
724
|
+
const commands = this.discordConfig.commands ?? [];
|
|
725
|
+
return commands.map((cmd) => cmd.name.replace(/^\//, ''));
|
|
726
|
+
}
|
|
715
727
|
/**
|
|
716
728
|
* Send typing indicator
|
|
717
729
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Native Engine v2026.3.11 alignment: approval cards, reasoning streams,
|
|
9
9
|
* identity-aware headers, full thread context.
|
|
10
10
|
*/
|
|
11
|
-
import { BaseChannel, ChannelConfig, DeliveryResult, OutboundMessage } from '../core.js';
|
|
11
|
+
import { BaseChannel, ChannelConfig, DeliveryResult, InboundMessage, OutboundMessage } from '../core.js';
|
|
12
12
|
export interface FeishuConfig {
|
|
13
13
|
appId: string;
|
|
14
14
|
appSecret: string;
|
|
@@ -54,6 +54,67 @@ export interface FeishuMessage {
|
|
|
54
54
|
messageType: 'text' | 'post' | 'image' | 'interactive' | 'file';
|
|
55
55
|
createTime: string;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Shape of the `im.message.receive_v1` event body. Mirrors the Lark Open
|
|
59
|
+
* Platform schema. The official `EventDispatcher` hands the handler the
|
|
60
|
+
* UNWRAPPED `event` body (`{ sender, message }`); the full
|
|
61
|
+
* `{ schema, header, event }` envelope only appears on the raw webhook. We
|
|
62
|
+
* accept both (see {@link parseFeishuMessageEvent}).
|
|
63
|
+
*/
|
|
64
|
+
export interface FeishuReceiveEventBody {
|
|
65
|
+
sender?: {
|
|
66
|
+
sender_id?: {
|
|
67
|
+
open_id?: string;
|
|
68
|
+
union_id?: string;
|
|
69
|
+
user_id?: string;
|
|
70
|
+
};
|
|
71
|
+
sender_type?: string;
|
|
72
|
+
tenant_key?: string;
|
|
73
|
+
};
|
|
74
|
+
message?: {
|
|
75
|
+
message_id?: string;
|
|
76
|
+
root_id?: string;
|
|
77
|
+
parent_id?: string;
|
|
78
|
+
create_time?: string;
|
|
79
|
+
chat_id?: string;
|
|
80
|
+
thread_id?: string;
|
|
81
|
+
chat_type?: 'p2p' | 'group' | string;
|
|
82
|
+
message_type?: string;
|
|
83
|
+
/** JSON-encoded string; structure varies by message_type. */
|
|
84
|
+
content?: string;
|
|
85
|
+
mentions?: Array<Record<string, unknown>>;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** The raw event as it may arrive: either the body, or the full envelope. */
|
|
89
|
+
export interface FeishuReceiveEventEnvelope {
|
|
90
|
+
schema?: string;
|
|
91
|
+
header?: Record<string, unknown>;
|
|
92
|
+
event?: FeishuReceiveEventBody;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse a Lark `im.message.receive_v1` event into a Code Buddy
|
|
96
|
+
* {@link InboundMessage}.
|
|
97
|
+
*
|
|
98
|
+
* Envelope-tolerant: accepts either the unwrapped event body (what the SDK's
|
|
99
|
+
* `EventDispatcher` hands the handler) or the full `{ schema, header, event }`
|
|
100
|
+
* webhook envelope.
|
|
101
|
+
*
|
|
102
|
+
* Returns `null` when the event carries no usable message (e.g. a non-message
|
|
103
|
+
* event slipped through, or no chat/text could be resolved).
|
|
104
|
+
*/
|
|
105
|
+
export declare function parseFeishuMessageEvent(raw: FeishuReceiveEventBody | FeishuReceiveEventEnvelope | undefined | null): InboundMessage | null;
|
|
106
|
+
/**
|
|
107
|
+
* Legacy in-process adapter.
|
|
108
|
+
*
|
|
109
|
+
* The real outbound transport now lives in {@link FeishuChannel}, which mints a
|
|
110
|
+
* genuine `tenant_access_token` and POSTs through the REST `im/v1/messages`
|
|
111
|
+
* API. This class is retained for its still-useful, network-free helpers — the
|
|
112
|
+
* interactive-card builders, reasoning-stream hooks, and `getThreadMessages`
|
|
113
|
+
* (exercised by `tests/channels/feishu-cards.test.ts`). Its `sendText` /
|
|
114
|
+
* `sendCard` / `sendImage` / `replyMessage` methods, however, perform NO
|
|
115
|
+
* network I/O and return synthetic ids; they are kept only for backward-compat
|
|
116
|
+
* and are not on the real send path. Prefer {@link FeishuChannel.send}.
|
|
117
|
+
*/
|
|
57
118
|
export declare class FeishuAdapter {
|
|
58
119
|
private config;
|
|
59
120
|
private running;
|
|
@@ -62,18 +123,22 @@ export declare class FeishuAdapter {
|
|
|
62
123
|
start(): Promise<void>;
|
|
63
124
|
stop(): Promise<void>;
|
|
64
125
|
isRunning(): boolean;
|
|
126
|
+
/** @deprecated No network I/O — returns a synthetic id. Use {@link FeishuChannel.send}. */
|
|
65
127
|
sendText(chatId: string, text: string): Promise<{
|
|
66
128
|
success: boolean;
|
|
67
129
|
messageId: string;
|
|
68
130
|
}>;
|
|
131
|
+
/** @deprecated No network I/O — returns a synthetic id. Use {@link FeishuChannel.send}. */
|
|
69
132
|
sendCard(chatId: string, card: Record<string, unknown>): Promise<{
|
|
70
133
|
success: boolean;
|
|
71
134
|
messageId: string;
|
|
72
135
|
}>;
|
|
136
|
+
/** @deprecated No network I/O — returns a synthetic id. Use {@link FeishuChannel.send}. */
|
|
73
137
|
sendImage(chatId: string, imageKey: string): Promise<{
|
|
74
138
|
success: boolean;
|
|
75
139
|
messageId: string;
|
|
76
140
|
}>;
|
|
141
|
+
/** @deprecated No network I/O. Use {@link FeishuChannel.send} with `replyTo` instead. */
|
|
77
142
|
replyMessage(messageId: string, text: string): Promise<{
|
|
78
143
|
success: boolean;
|
|
79
144
|
}>;
|
|
@@ -113,14 +178,115 @@ export declare class FeishuAdapter {
|
|
|
113
178
|
*/
|
|
114
179
|
getThreadMessages(chatId: string): Promise<FeishuMessage[]>;
|
|
115
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Structured result of attempting to bring up the inbound receive channel.
|
|
183
|
+
*
|
|
184
|
+
* Feishu/Lark's real-time push is delivered over a proprietary persistent
|
|
185
|
+
* "long-connection" (WebSocket) whose wire framing is NOT publicly specified:
|
|
186
|
+
* frames are Protobuf (`pbbp2` Frame messages with a CONTROL/DATA `method`
|
|
187
|
+
* discriminator, internal header keys, and gzip-compressed, chunk-reassembled
|
|
188
|
+
* JSON payloads that each require a server ACK frame). That schema, the control
|
|
189
|
+
* opcodes, and the ACK handshake ship only inside the official Lark SDK
|
|
190
|
+
* (`@larksuiteoapi/node-sdk`). We deliberately do NOT reimplement it from a
|
|
191
|
+
* guess: a hand-rolled framing would not be the real protocol, and any "mock"
|
|
192
|
+
* exercising it would only validate our own invention rather than the wire
|
|
193
|
+
* contract. Instead, when the official SDK IS installed we drive its
|
|
194
|
+
* {@link https://github.com/larksuite/node-sdk WSClient} (see
|
|
195
|
+
* {@link FeishuChannel.connect}); when it is NOT installed we keep the honest
|
|
196
|
+
* send-only state below. The outbound REST path is always fully implemented.
|
|
197
|
+
*/
|
|
198
|
+
export interface FeishuReceiveStatus {
|
|
199
|
+
/**
|
|
200
|
+
* `true` only when the official Lark `WSClient` long-connection is live;
|
|
201
|
+
* `false` when the SDK is absent (or no app credentials are configured).
|
|
202
|
+
*/
|
|
203
|
+
connected: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Machine-readable state code:
|
|
206
|
+
* - `'lark-sdk-required'` — the optional `@larksuiteoapi/node-sdk` is not
|
|
207
|
+
* installed, so no inbound socket exists (outbound REST still works).
|
|
208
|
+
* - `'lark-ws'` — the official SDK's long-connection is live.
|
|
209
|
+
*/
|
|
210
|
+
reason: 'lark-sdk-required' | 'lark-ws';
|
|
211
|
+
/** Human-readable explanation. */
|
|
212
|
+
detail: string;
|
|
213
|
+
}
|
|
116
214
|
export declare class FeishuChannel extends BaseChannel {
|
|
117
215
|
private adapter;
|
|
216
|
+
private readonly baseUrl;
|
|
217
|
+
/** Cached tenant_access_token (re-minted on demand if the API rejects it). */
|
|
218
|
+
private tenantToken;
|
|
219
|
+
private receiveStatus;
|
|
220
|
+
/** Live official-SDK long-connection client, when the SDK is installed. */
|
|
221
|
+
private wsClient;
|
|
118
222
|
constructor(config: FeishuChannelConfig);
|
|
223
|
+
/**
|
|
224
|
+
* Bring the channel up.
|
|
225
|
+
*
|
|
226
|
+
* Outbound (`send()`) is fully functional: it lazily mints a
|
|
227
|
+
* `tenant_access_token` and POSTs to `/open-apis/im/v1/messages`.
|
|
228
|
+
*
|
|
229
|
+
* Inbound (receiving user messages) requires Feishu's proprietary
|
|
230
|
+
* long-connection — see {@link FeishuReceiveStatus}. We do NOT fake it. We
|
|
231
|
+
* attempt to bring up the REAL inbound socket using the official Lark SDK
|
|
232
|
+
* (`@larksuiteoapi/node-sdk`), imported OPTIONALLY at runtime — it is not a
|
|
233
|
+
* declared dependency, so most installs won't have it. When it IS present
|
|
234
|
+
* (and app credentials are configured) we start its `WSClient` and register
|
|
235
|
+
* the `im.message.receive_v1` handler, which parses each message into an
|
|
236
|
+
* {@link InboundMessage} and re-emits it via `this.emit('message' | 'command')`.
|
|
237
|
+
* The SDK owns the long-connection + reconnect internally.
|
|
238
|
+
*
|
|
239
|
+
* When the SDK is absent (the default) we keep the honest "send-only" state:
|
|
240
|
+
* `status.connected = false`, the receive reason surfaced in
|
|
241
|
+
* `status.error` / `status.info`. No throw — outbound still works.
|
|
242
|
+
*/
|
|
119
243
|
connect(): Promise<void>;
|
|
244
|
+
/**
|
|
245
|
+
* Attempt to bring up the REAL inbound long-connection via the optional
|
|
246
|
+
* official Lark SDK. Mutates `this.receiveStatus` / `this.status` to the live
|
|
247
|
+
* `'lark-ws'` state on success; otherwise leaves the honest send-only state
|
|
248
|
+
* untouched. Never throws — a missing SDK or a start() failure degrades
|
|
249
|
+
* gracefully to outbound-only.
|
|
250
|
+
*/
|
|
251
|
+
private tryStartInbound;
|
|
252
|
+
/**
|
|
253
|
+
* Parse a raw `im.message.receive_v1` event and re-emit it as a Code Buddy
|
|
254
|
+
* `message` (and `command`) event. This is the seam the live SDK handler
|
|
255
|
+
* delegates to; it is also unit-testable directly without a live tenant.
|
|
256
|
+
*
|
|
257
|
+
* @returns the parsed {@link InboundMessage}, or `null` if the event carried
|
|
258
|
+
* no usable message.
|
|
259
|
+
*/
|
|
260
|
+
dispatchInboundEvent(event: FeishuReceiveEventBody | FeishuReceiveEventEnvelope): InboundMessage | null;
|
|
120
261
|
disconnect(): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* Send an outbound message through the real Feishu REST API.
|
|
264
|
+
*
|
|
265
|
+
* Cards are sent with `msg_type: 'interactive'`; everything else as
|
|
266
|
+
* `msg_type: 'text'`. The chat id (`message.channelId`) becomes the
|
|
267
|
+
* `receive_id` with `receive_id_type=chat_id`.
|
|
268
|
+
*/
|
|
121
269
|
send(message: OutboundMessage): Promise<DeliveryResult>;
|
|
270
|
+
/**
|
|
271
|
+
* Honest, structured account of the inbound receive channel. Returns `null`
|
|
272
|
+
* before `connect()` has run; otherwise a {@link FeishuReceiveStatus}
|
|
273
|
+
* explaining why no live socket exists.
|
|
274
|
+
*/
|
|
275
|
+
getReceiveStatus(): FeishuReceiveStatus | null;
|
|
122
276
|
/**
|
|
123
277
|
* Get the underlying adapter (for direct card/reasoning API access).
|
|
124
278
|
*/
|
|
125
279
|
getAdapter(): FeishuAdapter | null;
|
|
280
|
+
/**
|
|
281
|
+
* POST a message to `/open-apis/im/v1/messages`, minting (and caching) a
|
|
282
|
+
* tenant_access_token first. On an auth-class failure the token is dropped
|
|
283
|
+
* and the call retried once with a fresh token.
|
|
284
|
+
*/
|
|
285
|
+
private postMessage;
|
|
286
|
+
/**
|
|
287
|
+
* Return a cached tenant_access_token, minting one via
|
|
288
|
+
* `/open-apis/auth/v3/tenant_access_token/internal` if needed. This is the
|
|
289
|
+
* same internal-app credential exchange used by `src/tools/feishu-tool.ts`.
|
|
290
|
+
*/
|
|
291
|
+
private ensureTenantToken;
|
|
126
292
|
}
|