enya-agent 0.1.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/.env.example +20 -0
- package/.github/workflows/ci.yml +70 -0
- package/.github/workflows/publish.yml +250 -0
- package/.gitmodules +3 -0
- package/Cargo.lock +3584 -0
- package/Cargo.toml +97 -0
- package/crates/enact/Cargo.toml +27 -0
- package/crates/enact/src/lib.rs +60 -0
- package/crates/enact-a2a/Cargo.toml +25 -0
- package/crates/enact-a2a/src/lib.rs +411 -0
- package/crates/enact-channels/Cargo.toml +64 -0
- package/crates/enact-channels/examples/README.md +80 -0
- package/crates/enact-channels/examples/channel_bot.rs +169 -0
- package/crates/enact-channels/examples/telegram-echo.rs +34 -0
- package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
- package/crates/enact-channels/src/config.rs +213 -0
- package/crates/enact-channels/src/lib.rs +25 -0
- package/crates/enact-channels/src/runtime.rs +237 -0
- package/crates/enact-channels/src/security/mod.rs +5 -0
- package/crates/enact-channels/src/security/pairing.rs +205 -0
- package/crates/enact-channels/src/teams.rs +601 -0
- package/crates/enact-channels/src/telegram.rs +2833 -0
- package/crates/enact-channels/src/traits.rs +200 -0
- package/crates/enact-channels/src/webhook.rs +262 -0
- package/crates/enact-channels/src/whatsapp.rs +310 -0
- package/crates/enact-cli/Cargo.toml +40 -0
- package/crates/enact-cli/src/commands/doctor.rs +62 -0
- package/crates/enact-cli/src/commands/mod.rs +3 -0
- package/crates/enact-cli/src/commands/run.rs +69 -0
- package/crates/enact-cli/src/commands/serve.rs +81 -0
- package/crates/enact-cli/src/config.rs +2 -0
- package/crates/enact-cli/src/main.rs +79 -0
- package/crates/enact-config/Cargo.toml +36 -0
- package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
- package/crates/enact-config/QUICK_REFERENCE.md +92 -0
- package/crates/enact-config/README.md +107 -0
- package/crates/enact-config/TESTING.md +161 -0
- package/crates/enact-config/examples/test-env-vars.rs +100 -0
- package/crates/enact-config/src/config.rs +399 -0
- package/crates/enact-config/src/encrypted_store.rs +211 -0
- package/crates/enact-config/src/lib.rs +298 -0
- package/crates/enact-config/src/secrets.rs +149 -0
- package/crates/enact-config/src/sync.rs +260 -0
- package/crates/enact-config/test-env-vars.sh +34 -0
- package/crates/enact-config/tests/README.md +99 -0
- package/crates/enact-config/tests/config_integration_test.rs +202 -0
- package/crates/enact-config/tests/security_test.rs +140 -0
- package/crates/enact-context/Cargo.toml +41 -0
- package/crates/enact-context/src/budget.rs +314 -0
- package/crates/enact-context/src/calibrator.rs +535 -0
- package/crates/enact-context/src/compactor.rs +392 -0
- package/crates/enact-context/src/condenser.rs +826 -0
- package/crates/enact-context/src/lib.rs +94 -0
- package/crates/enact-context/src/segment.rs +238 -0
- package/crates/enact-context/src/step_context.rs +645 -0
- package/crates/enact-context/src/token_counter.rs +148 -0
- package/crates/enact-context/src/window.rs +372 -0
- package/crates/enact-core/Cargo.toml +42 -0
- package/crates/enact-core/README.md +98 -0
- package/crates/enact-core/src/background/executor.rs +524 -0
- package/crates/enact-core/src/background/mod.rs +48 -0
- package/crates/enact-core/src/background/target_binding.rs +390 -0
- package/crates/enact-core/src/background/trigger.rs +511 -0
- package/crates/enact-core/src/callable/callable.rs +152 -0
- package/crates/enact-core/src/callable/composite.rs +817 -0
- package/crates/enact-core/src/callable/graph.rs +104 -0
- package/crates/enact-core/src/callable/llm.rs +211 -0
- package/crates/enact-core/src/callable/mod.rs +64 -0
- package/crates/enact-core/src/callable/registry.rs +206 -0
- package/crates/enact-core/src/context/execution_context.rs +757 -0
- package/crates/enact-core/src/context/invocation.rs +99 -0
- package/crates/enact-core/src/context/mod.rs +50 -0
- package/crates/enact-core/src/context/tenant.rs +175 -0
- package/crates/enact-core/src/context/trace.rs +127 -0
- package/crates/enact-core/src/flow/conditional.rs +293 -0
- package/crates/enact-core/src/flow/mod.rs +43 -0
- package/crates/enact-core/src/flow/parallel.rs +437 -0
- package/crates/enact-core/src/flow/repeat.rs +534 -0
- package/crates/enact-core/src/flow/sequential.rs +248 -0
- package/crates/enact-core/src/graph/checkpoint.rs +79 -0
- package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
- package/crates/enact-core/src/graph/compiled.rs +189 -0
- package/crates/enact-core/src/graph/edge.rs +59 -0
- package/crates/enact-core/src/graph/graph_schema.rs +218 -0
- package/crates/enact-core/src/graph/loader.rs +155 -0
- package/crates/enact-core/src/graph/mod.rs +18 -0
- package/crates/enact-core/src/graph/node/function.rs +49 -0
- package/crates/enact-core/src/graph/node/mod.rs +48 -0
- package/crates/enact-core/src/graph/schema.rs +62 -0
- package/crates/enact-core/src/inbox/message.rs +405 -0
- package/crates/enact-core/src/inbox/mod.rs +31 -0
- package/crates/enact-core/src/inbox/store.rs +355 -0
- package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
- package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
- package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
- package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
- package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
- package/crates/enact-core/src/kernel/error.rs +1200 -0
- package/crates/enact-core/src/kernel/event.rs +1394 -0
- package/crates/enact-core/src/kernel/execution_model.rs +831 -0
- package/crates/enact-core/src/kernel/execution_state.rs +189 -0
- package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
- package/crates/enact-core/src/kernel/ids.rs +2086 -0
- package/crates/enact-core/src/kernel/interrupt.rs +125 -0
- package/crates/enact-core/src/kernel/kernel.rs +1283 -0
- package/crates/enact-core/src/kernel/mod.rs +205 -0
- package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
- package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
- package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
- package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
- package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
- package/crates/enact-core/src/kernel/reducer.rs +808 -0
- package/crates/enact-core/src/kernel/replay.rs +153 -0
- package/crates/enact-core/src/lib.rs +413 -0
- package/crates/enact-core/src/memory/episodic.rs +0 -0
- package/crates/enact-core/src/memory/mod.rs +6 -0
- package/crates/enact-core/src/memory/semantic.rs +0 -0
- package/crates/enact-core/src/memory/trait.rs +0 -0
- package/crates/enact-core/src/memory/vector_db.rs +0 -0
- package/crates/enact-core/src/memory/working.rs +0 -0
- package/crates/enact-core/src/policy/execution_policy.rs +292 -0
- package/crates/enact-core/src/policy/filters.rs +458 -0
- package/crates/enact-core/src/policy/input_processor.rs +407 -0
- package/crates/enact-core/src/policy/long_running.rs +134 -0
- package/crates/enact-core/src/policy/mod.rs +193 -0
- package/crates/enact-core/src/policy/pii_input.rs +274 -0
- package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
- package/crates/enact-core/src/policy/tool_policy.rs +407 -0
- package/crates/enact-core/src/providers/mod.rs +63 -0
- package/crates/enact-core/src/providers/trait.rs +292 -0
- package/crates/enact-core/src/runner/callbacks.rs +6 -0
- package/crates/enact-core/src/runner/execution_runner.rs +476 -0
- package/crates/enact-core/src/runner/loop.rs +117 -0
- package/crates/enact-core/src/runner/mod.rs +58 -0
- package/crates/enact-core/src/runner/protected_runner.rs +280 -0
- package/crates/enact-core/src/signal/inmemory.rs +231 -0
- package/crates/enact-core/src/signal/mod.rs +108 -0
- package/crates/enact-core/src/streaming/event_logger.rs +195 -0
- package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
- package/crates/enact-core/src/streaming/mod.rs +108 -0
- package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
- package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
- package/crates/enact-core/src/streaming/protection/context.rs +136 -0
- package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
- package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
- package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
- package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
- package/crates/enact-core/src/streaming/sse.rs +0 -0
- package/crates/enact-core/src/telemetry/exporter.rs +0 -0
- package/crates/enact-core/src/telemetry/init.rs +0 -0
- package/crates/enact-core/src/telemetry/mod.rs +49 -0
- package/crates/enact-core/src/telemetry/spans.rs +245 -0
- package/crates/enact-core/src/tool/agent_tool.rs +177 -0
- package/crates/enact-core/src/tool/browser/mod.rs +0 -0
- package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
- package/crates/enact-core/src/tool/cost.rs +247 -0
- package/crates/enact-core/src/tool/discovery.rs +0 -0
- package/crates/enact-core/src/tool/dispatcher.rs +347 -0
- package/crates/enact-core/src/tool/filesystem.rs +231 -0
- package/crates/enact-core/src/tool/function.rs +99 -0
- package/crates/enact-core/src/tool/git.rs +162 -0
- package/crates/enact-core/src/tool/http.rs +214 -0
- package/crates/enact-core/src/tool/mcp/client.rs +0 -0
- package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
- package/crates/enact-core/src/tool/mod.rs +51 -0
- package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
- package/crates/enact-core/src/tool/shell.rs +147 -0
- package/crates/enact-core/src/tool/trait.rs +33 -0
- package/crates/enact-core/src/tool/web_search.rs +277 -0
- package/crates/enact-core/src/util/config.rs +0 -0
- package/crates/enact-core/src/util/errors.rs +0 -0
- package/crates/enact-core/src/util/mod.rs +6 -0
- package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
- package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
- package/crates/enact-core/tests/e2e_test.rs +259 -0
- package/crates/enact-core/tests/graph_test.rs +130 -0
- package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
- package/crates/enact-cron/Cargo.toml +28 -0
- package/crates/enact-cron/src/lib.rs +44 -0
- package/crates/enact-cron/src/schedule.rs +156 -0
- package/crates/enact-cron/src/store.rs +589 -0
- package/crates/enact-cron/src/types.rs +148 -0
- package/crates/enact-gateway/Cargo.toml +31 -0
- package/crates/enact-gateway/README.md +30 -0
- package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
- package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
- package/crates/enact-gateway/src/lib.rs +582 -0
- package/crates/enact-mcp/Cargo.toml +24 -0
- package/crates/enact-mcp/src/lib.rs +178 -0
- package/crates/enact-memory/Cargo.toml +25 -0
- package/crates/enact-memory/src/backend.rs +20 -0
- package/crates/enact-memory/src/chunker.rs +230 -0
- package/crates/enact-memory/src/embeddings.rs +221 -0
- package/crates/enact-memory/src/lib.rs +67 -0
- package/crates/enact-memory/src/markdown.rs +127 -0
- package/crates/enact-memory/src/none.rs +61 -0
- package/crates/enact-memory/src/sqlite.rs +276 -0
- package/crates/enact-memory/src/traits.rs +65 -0
- package/crates/enact-memory/src/vector.rs +198 -0
- package/crates/enact-oauth/Cargo.toml +27 -0
- package/crates/enact-oauth/src/lib.rs +584 -0
- package/crates/enact-observability/Cargo.toml +22 -0
- package/crates/enact-observability/src/lib.rs +197 -0
- package/crates/enact-providers/Cargo.toml +33 -0
- package/crates/enact-providers/examples/hello-agent.rs +33 -0
- package/crates/enact-providers/src/anthropic.rs +182 -0
- package/crates/enact-providers/src/azure.rs +96 -0
- package/crates/enact-providers/src/bridge.rs +221 -0
- package/crates/enact-providers/src/gemini.rs +227 -0
- package/crates/enact-providers/src/http.rs +78 -0
- package/crates/enact-providers/src/lib.rs +53 -0
- package/crates/enact-providers/src/openai_compatible.rs +167 -0
- package/crates/enact-providers/src/openrouter.rs +33 -0
- package/crates/enact-runner/Cargo.toml +24 -0
- package/crates/enact-runner/README.md +76 -0
- package/crates/enact-runner/src/compaction.rs +225 -0
- package/crates/enact-runner/src/config.rs +118 -0
- package/crates/enact-runner/src/lib.rs +63 -0
- package/crates/enact-runner/src/loop_driver.rs +414 -0
- package/crates/enact-runner/src/parser.rs +421 -0
- package/crates/enact-runner/src/retry.rs +262 -0
- package/crates/enact-runner/tests/integration.rs +278 -0
- package/crates/enact-security/Cargo.toml +22 -0
- package/crates/enact-security/src/audit.rs +375 -0
- package/crates/enact-security/src/lib.rs +37 -0
- package/crates/enact-security/src/policy.rs +406 -0
- package/crates/enact-skills/Cargo.toml +25 -0
- package/crates/enact-skills/src/lib.rs +506 -0
- package/crates/enact-tools/Cargo.toml +22 -0
- package/crates/enact-tools/src/file_read.rs +166 -0
- package/crates/enact-tools/src/file_write.rs +216 -0
- package/crates/enact-tools/src/git_operations.rs +513 -0
- package/crates/enact-tools/src/http_request.rs +417 -0
- package/crates/enact-tools/src/lib.rs +104 -0
- package/crates/enact-tools/src/security.rs +227 -0
- package/crates/enact-tools/src/shell.rs +191 -0
- package/crates/enact-tools/src/traits.rs +159 -0
- package/docs/Makefile +74 -0
- package/docs/config.toml +62 -0
- package/docs/content/_index.md +174 -0
- package/docs/content/a2a/_index.md +431 -0
- package/docs/content/api/_index.md +323 -0
- package/docs/content/channels/_index.md +160 -0
- package/docs/content/channels/teams.md +205 -0
- package/docs/content/channels/telegram.md +182 -0
- package/docs/content/channels/webhook.md +423 -0
- package/docs/content/channels/whatsapp.md +240 -0
- package/docs/content/cli/_index.md +261 -0
- package/docs/content/concepts/_index.md +273 -0
- package/docs/content/configuration/_index.md +241 -0
- package/docs/content/cron/_index.md +248 -0
- package/docs/content/developers/_index.md +278 -0
- package/docs/content/getting-started/_index.md +180 -0
- package/docs/content/installation/_index.md +186 -0
- package/docs/content/installation/uninstall.md +101 -0
- package/docs/content/installation/updating.md +120 -0
- package/docs/content/mcp/_index.md +215 -0
- package/docs/content/memory/_index.md +163 -0
- package/docs/content/oauth/_index.md +515 -0
- package/docs/content/providers/_index.md +206 -0
- package/docs/content/roadmap/_index.md +199 -0
- package/docs/content/security/_index.md +219 -0
- package/docs/content/skills/_index.md +228 -0
- package/docs/content/tools/_index.md +485 -0
- package/docs/content/troubleshooting/_index.md +259 -0
- package/docs/content/yaml-schema/_index.md +294 -0
- package/docs/static/giallo-dark.css +91 -0
- package/docs/static/giallo-light.css +91 -0
- package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
- package/docs/themes/tanuki/LICENSE +21 -0
- package/docs/themes/tanuki/README.md +166 -0
- package/docs/themes/tanuki/examples/blog/config.toml +58 -0
- package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
- package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
- package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
- package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
- package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
- package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
- package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
- package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
- package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
- package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
- package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
- package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
- package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
- package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
- package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
- package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
- package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
- package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
- package/docs/themes/tanuki/examples/book/config.toml +17 -0
- package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
- package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
- package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
- package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
- package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
- package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
- package/docs/themes/tanuki/examples/book/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/config.toml +28 -0
- package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
- package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
- package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
- package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
- package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
- package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
- package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/examples/index.html +2104 -0
- package/docs/themes/tanuki/mise.toml +108 -0
- package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
- package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
- package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
- package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
- package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
- package/docs/themes/tanuki/sass/components/_code.scss +457 -0
- package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
- package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
- package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
- package/docs/themes/tanuki/sass/components/_print.scss +237 -0
- package/docs/themes/tanuki/sass/components/_search.scss +224 -0
- package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
- package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
- package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
- package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
- package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
- package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
- package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
- package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
- package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
- package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
- package/docs/themes/tanuki/sass/style.scss +47 -0
- package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
- package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
- package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
- package/docs/themes/tanuki/screenshot.png +0 -0
- package/docs/themes/tanuki/sentinel.kdl +59 -0
- package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
- package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
- package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
- package/docs/themes/tanuki/static/js/anchors.js +18 -0
- package/docs/themes/tanuki/static/js/app.js +274 -0
- package/docs/themes/tanuki/static/js/code.js +394 -0
- package/docs/themes/tanuki/static/js/navigation.js +778 -0
- package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
- package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
- package/docs/themes/tanuki/static/js/search.js +215 -0
- package/docs/themes/tanuki/static/js/theme.js +169 -0
- package/docs/themes/tanuki/static/syntax-dark.css +151 -0
- package/docs/themes/tanuki/static/syntax-light.css +151 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
- package/docs/themes/tanuki/templates/404.html +52 -0
- package/docs/themes/tanuki/templates/base.html +428 -0
- package/docs/themes/tanuki/templates/blog.html +66 -0
- package/docs/themes/tanuki/templates/home.html +108 -0
- package/docs/themes/tanuki/templates/index.html +178 -0
- package/docs/themes/tanuki/templates/landing.html +168 -0
- package/docs/themes/tanuki/templates/macros/nav.html +128 -0
- package/docs/themes/tanuki/templates/macros/posts.html +101 -0
- package/docs/themes/tanuki/templates/macros/ui.html +159 -0
- package/docs/themes/tanuki/templates/page.html +135 -0
- package/docs/themes/tanuki/templates/partials/footer.html +38 -0
- package/docs/themes/tanuki/templates/partials/header.html +366 -0
- package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
- package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
- package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
- package/docs/themes/tanuki/templates/partials/search.html +52 -0
- package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
- package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
- package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
- package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
- package/docs/themes/tanuki/templates/print.html +244 -0
- package/docs/themes/tanuki/templates/section.html +186 -0
- package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
- package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
- package/docs/themes/tanuki/theme.toml +58 -0
- package/examples/hello-agent.rs +55 -0
- package/package.json +36 -0
- package/proto/config.proto +60 -0
- package/proto/events.proto +0 -0
- package/proto/runtime.proto +215 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Channel Examples
|
|
2
|
+
|
|
3
|
+
## WhatsApp Echo (`whatsapp-echo`)
|
|
4
|
+
|
|
5
|
+
This example exposes a webhook endpoint that:
|
|
6
|
+
|
|
7
|
+
- verifies Meta webhook challenge (`GET /whatsapp`)
|
|
8
|
+
- accepts incoming WhatsApp events (`POST /whatsapp`)
|
|
9
|
+
- echoes text replies back to sender
|
|
10
|
+
|
|
11
|
+
### 1) Run locally
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
WHATSAPP_ACCESS_TOKEN="<meta_access_token>" \
|
|
15
|
+
WHATSAPP_ENDPOINT_ID="<phone_number_id>" \
|
|
16
|
+
WHATSAPP_VERIFY_TOKEN="<verify_token_you_choose>" \
|
|
17
|
+
WHATSAPP_APP_SECRET="<meta_app_secret_optional_but_recommended>" \
|
|
18
|
+
WHATSAPP_ALLOWED_NUMBERS="+15551234567,*" \
|
|
19
|
+
WHATSAPP_WEBHOOK_PORT=8080 \
|
|
20
|
+
cargo run -p enact-channels --example whatsapp-echo
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Notes:
|
|
24
|
+
|
|
25
|
+
- `WHATSAPP_APP_SECRET` enables signature validation (`X-Hub-Signature-256`).
|
|
26
|
+
- `WHATSAPP_ALLOWED_NUMBERS` filters inbound senders. Use `*` to allow all.
|
|
27
|
+
|
|
28
|
+
### 2) Expose webhook publicly
|
|
29
|
+
|
|
30
|
+
Meta needs a public HTTPS URL. For local testing, use a tunnel (for example ngrok):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ngrok http 8080
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use the generated HTTPS URL + `/whatsapp` as your callback URL.
|
|
37
|
+
|
|
38
|
+
### 3) Configure Meta Webhooks
|
|
39
|
+
|
|
40
|
+
In your Meta app dashboard:
|
|
41
|
+
|
|
42
|
+
1. Open **WhatsApp > Configuration > Webhooks**.
|
|
43
|
+
2. Set callback URL to `https://<public-host>/whatsapp`.
|
|
44
|
+
3. Set verify token to exactly `WHATSAPP_VERIFY_TOKEN`.
|
|
45
|
+
4. Subscribe to `messages` field.
|
|
46
|
+
|
|
47
|
+
### 4) Quick verification tests
|
|
48
|
+
|
|
49
|
+
#### Verify webhook challenge
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
curl -i "http://localhost:8080/whatsapp?hub.mode=subscribe&hub.verify_token=<verify_token_you_choose>&hub.challenge=12345"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Expected body: `12345`
|
|
56
|
+
|
|
57
|
+
#### Send sample message payload
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
curl -i -X POST "http://localhost:8080/whatsapp" \
|
|
61
|
+
-H "Content-Type: application/json" \
|
|
62
|
+
-d '{
|
|
63
|
+
"entry": [{
|
|
64
|
+
"changes": [{
|
|
65
|
+
"value": {
|
|
66
|
+
"messages": [{
|
|
67
|
+
"from": "15551234567",
|
|
68
|
+
"timestamp": "1700000000",
|
|
69
|
+
"type": "text",
|
|
70
|
+
"text": { "body": "hello" }
|
|
71
|
+
}]
|
|
72
|
+
}
|
|
73
|
+
}]
|
|
74
|
+
}]
|
|
75
|
+
}'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Expected JSON response: `{"status":"ok"}`
|
|
79
|
+
|
|
80
|
+
If your tokens and endpoint ID are valid, the sender should receive `echo: hello` on WhatsApp.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
//! Example: Run a bot on Telegram and/or Teams channels.
|
|
2
|
+
//!
|
|
3
|
+
//! This example shows how to:
|
|
4
|
+
//! 1. Load channel configuration
|
|
5
|
+
//! 2. Set up Telegram and Teams channels
|
|
6
|
+
//! 3. Route messages through a custom handler (or AgentRunner)
|
|
7
|
+
//!
|
|
8
|
+
//! ## Usage
|
|
9
|
+
//!
|
|
10
|
+
//! Set environment variables:
|
|
11
|
+
//! ```bash
|
|
12
|
+
//! # Telegram
|
|
13
|
+
//! export TELEGRAM_BOT_TOKEN="your-bot-token"
|
|
14
|
+
//!
|
|
15
|
+
//! # Teams (optional)
|
|
16
|
+
//! export TEAMS_APP_ID="your-app-id"
|
|
17
|
+
//! export TEAMS_APP_PASSWORD="your-app-password"
|
|
18
|
+
//! ```
|
|
19
|
+
//!
|
|
20
|
+
//! Run:
|
|
21
|
+
//! ```bash
|
|
22
|
+
//! cargo run --example channel_bot
|
|
23
|
+
//! ```
|
|
24
|
+
|
|
25
|
+
use async_trait::async_trait;
|
|
26
|
+
use enact_channels::{
|
|
27
|
+
ChannelMessage, ChannelRuntime, MessageHandler,
|
|
28
|
+
TelegramChannel, TeamsChannel, TeamsActivity,
|
|
29
|
+
StreamMode, TelegramConfig, TeamsConfig,
|
|
30
|
+
};
|
|
31
|
+
use tokio::sync::mpsc;
|
|
32
|
+
|
|
33
|
+
/// Simple handler that echoes messages.
|
|
34
|
+
/// Replace this with AgentRunner integration for real use.
|
|
35
|
+
struct SimpleHandler;
|
|
36
|
+
|
|
37
|
+
#[async_trait]
|
|
38
|
+
impl MessageHandler for SimpleHandler {
|
|
39
|
+
async fn handle(&self, message: &ChannelMessage) -> anyhow::Result<String> {
|
|
40
|
+
tracing::info!(
|
|
41
|
+
"Received from {}: {}",
|
|
42
|
+
message.sender,
|
|
43
|
+
message.content
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Simple echo response
|
|
47
|
+
// In production, you'd call AgentRunner here:
|
|
48
|
+
//
|
|
49
|
+
// let mut runner = DefaultAgentRunner::default_new();
|
|
50
|
+
// let outcome = runner.run(&my_callable, &message.content).await?;
|
|
51
|
+
// match outcome {
|
|
52
|
+
// LoopOutcome::Completed(response) => Ok(response),
|
|
53
|
+
// _ => Ok("Processing incomplete".to_string()),
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
Ok(format!(
|
|
57
|
+
"Hello from enact! You said: \"{}\"",
|
|
58
|
+
message.content
|
|
59
|
+
))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async fn on_start(&self, message: &ChannelMessage) {
|
|
63
|
+
tracing::debug!("Processing message {} from {}", message.id, message.sender);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async fn on_complete(&self, message: &ChannelMessage, response: &str) {
|
|
67
|
+
tracing::debug!(
|
|
68
|
+
"Completed message {} with {} chars response",
|
|
69
|
+
message.id,
|
|
70
|
+
response.len()
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[tokio::main]
|
|
76
|
+
async fn main() -> anyhow::Result<()> {
|
|
77
|
+
// Initialize tracing
|
|
78
|
+
tracing_subscriber::fmt()
|
|
79
|
+
.with_env_filter(
|
|
80
|
+
tracing_subscriber::EnvFilter::from_default_env()
|
|
81
|
+
.add_directive("enact_channels=debug".parse().unwrap())
|
|
82
|
+
.add_directive("channel_bot=debug".parse().unwrap()),
|
|
83
|
+
)
|
|
84
|
+
.init();
|
|
85
|
+
|
|
86
|
+
tracing::info!("Starting enact channel bot...");
|
|
87
|
+
|
|
88
|
+
// Create runtime with our handler
|
|
89
|
+
let mut runtime = ChannelRuntime::new(SimpleHandler);
|
|
90
|
+
|
|
91
|
+
// Add Telegram channel if configured
|
|
92
|
+
if let Ok(token) = std::env::var("TELEGRAM_BOT_TOKEN") {
|
|
93
|
+
let config = TelegramConfig {
|
|
94
|
+
bot_token: token,
|
|
95
|
+
allowed_users: std::env::var("TELEGRAM_ALLOWED_USERS")
|
|
96
|
+
.map(|s| s.split(',').map(String::from).collect())
|
|
97
|
+
.unwrap_or_default(),
|
|
98
|
+
mention_only: false,
|
|
99
|
+
stream_mode: StreamMode::Off,
|
|
100
|
+
draft_update_interval_ms: 1000,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
let telegram = TelegramChannel::new(
|
|
104
|
+
config.bot_token.clone(),
|
|
105
|
+
config.allowed_users.clone(),
|
|
106
|
+
config.mention_only,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
runtime = runtime.add_channel(telegram);
|
|
110
|
+
tracing::info!("Telegram channel enabled");
|
|
111
|
+
} else {
|
|
112
|
+
tracing::warn!("TELEGRAM_BOT_TOKEN not set, Telegram disabled");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Add Teams channel if configured
|
|
116
|
+
if let (Ok(app_id), Ok(app_password)) = (
|
|
117
|
+
std::env::var("TEAMS_APP_ID"),
|
|
118
|
+
std::env::var("TEAMS_APP_PASSWORD"),
|
|
119
|
+
) {
|
|
120
|
+
let config = TeamsConfig {
|
|
121
|
+
app_id,
|
|
122
|
+
app_password,
|
|
123
|
+
allowed_users: std::env::var("TEAMS_ALLOWED_USERS")
|
|
124
|
+
.map(|s| s.split(',').map(String::from).collect())
|
|
125
|
+
.unwrap_or_default(),
|
|
126
|
+
bot_name: std::env::var("TEAMS_BOT_NAME")
|
|
127
|
+
.unwrap_or_else(|_| "enact".to_string()),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let teams = TeamsChannel::new(&config);
|
|
131
|
+
let webhook_tx = teams.webhook_sender();
|
|
132
|
+
|
|
133
|
+
runtime = runtime.add_channel(teams);
|
|
134
|
+
tracing::info!("Teams channel enabled");
|
|
135
|
+
|
|
136
|
+
// Start webhook server for Teams
|
|
137
|
+
tokio::spawn(start_teams_webhook_server(webhook_tx));
|
|
138
|
+
} else {
|
|
139
|
+
tracing::warn!("TEAMS_APP_ID/TEAMS_APP_PASSWORD not set, Teams disabled");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Run the channel runtime
|
|
143
|
+
tracing::info!("Bot is running. Press Ctrl+C to stop.");
|
|
144
|
+
runtime.run().await
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Start an HTTP server for Teams webhook.
|
|
148
|
+
async fn start_teams_webhook_server(webhook_tx: mpsc::Sender<TeamsActivity>) {
|
|
149
|
+
use axum::{routing::post, Router};
|
|
150
|
+
|
|
151
|
+
let app = Router::new()
|
|
152
|
+
.route("/api/messages", post(enact_channels::teams_webhook_handler))
|
|
153
|
+
.with_state(webhook_tx);
|
|
154
|
+
|
|
155
|
+
let port = std::env::var("TEAMS_WEBHOOK_PORT")
|
|
156
|
+
.ok()
|
|
157
|
+
.and_then(|s| s.parse().ok())
|
|
158
|
+
.unwrap_or(3978);
|
|
159
|
+
|
|
160
|
+
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
|
|
161
|
+
tracing::info!("Teams webhook server listening on http://{}/api/messages", addr);
|
|
162
|
+
|
|
163
|
+
if let Err(e) = axum::serve(
|
|
164
|
+
tokio::net::TcpListener::bind(addr).await.unwrap(),
|
|
165
|
+
app,
|
|
166
|
+
).await {
|
|
167
|
+
tracing::error!("Teams webhook server error: {}", e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
use async_trait::async_trait;
|
|
2
|
+
use enact_channels::{ChannelMessage, ChannelRuntime, MessageHandler, TelegramChannel};
|
|
3
|
+
|
|
4
|
+
struct EchoHandler;
|
|
5
|
+
|
|
6
|
+
#[async_trait]
|
|
7
|
+
impl MessageHandler for EchoHandler {
|
|
8
|
+
async fn handle(&self, message: &ChannelMessage) -> anyhow::Result<String> {
|
|
9
|
+
Ok(format!("echo: {}", message.content))
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[tokio::main]
|
|
14
|
+
async fn main() -> anyhow::Result<()> {
|
|
15
|
+
let token = std::env::var("TELEGRAM_BOT_TOKEN")
|
|
16
|
+
.map_err(|_| anyhow::anyhow!("TELEGRAM_BOT_TOKEN is required"))?;
|
|
17
|
+
|
|
18
|
+
let allowlist = std::env::var("TELEGRAM_ALLOWED_USERS")
|
|
19
|
+
.ok()
|
|
20
|
+
.map(|value| {
|
|
21
|
+
value
|
|
22
|
+
.split(',')
|
|
23
|
+
.map(|entry| entry.trim().to_string())
|
|
24
|
+
.filter(|entry| !entry.is_empty())
|
|
25
|
+
.collect::<Vec<_>>()
|
|
26
|
+
})
|
|
27
|
+
.unwrap_or_default();
|
|
28
|
+
|
|
29
|
+
let telegram = TelegramChannel::new(token, allowlist, false);
|
|
30
|
+
let mut runtime = ChannelRuntime::new(EchoHandler).add_channel(telegram);
|
|
31
|
+
|
|
32
|
+
println!("telegram-echo running; press Ctrl+C to stop");
|
|
33
|
+
runtime.run().await
|
|
34
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
use axum::body::Bytes;
|
|
2
|
+
use axum::extract::{Query, State};
|
|
3
|
+
use axum::http::{HeaderMap, StatusCode};
|
|
4
|
+
use axum::response::IntoResponse;
|
|
5
|
+
use axum::routing::get;
|
|
6
|
+
use axum::{Json, Router};
|
|
7
|
+
use enact_channels::{Channel, SendMessage, WhatsAppChannel, verify_whatsapp_signature};
|
|
8
|
+
use std::sync::Arc;
|
|
9
|
+
|
|
10
|
+
#[derive(Clone)]
|
|
11
|
+
struct AppState {
|
|
12
|
+
channel: Arc<WhatsAppChannel>,
|
|
13
|
+
app_secret: Option<String>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[derive(Debug, serde::Deserialize)]
|
|
17
|
+
struct VerifyQuery {
|
|
18
|
+
#[serde(rename = "hub.mode")]
|
|
19
|
+
mode: Option<String>,
|
|
20
|
+
#[serde(rename = "hub.verify_token")]
|
|
21
|
+
verify_token: Option<String>,
|
|
22
|
+
#[serde(rename = "hub.challenge")]
|
|
23
|
+
challenge: Option<String>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[tokio::main]
|
|
27
|
+
async fn main() -> anyhow::Result<()> {
|
|
28
|
+
tracing_subscriber::fmt()
|
|
29
|
+
.with_env_filter(
|
|
30
|
+
tracing_subscriber::EnvFilter::from_default_env()
|
|
31
|
+
.add_directive("whatsapp_echo=info".parse().unwrap()),
|
|
32
|
+
)
|
|
33
|
+
.init();
|
|
34
|
+
|
|
35
|
+
let access_token = std::env::var("WHATSAPP_ACCESS_TOKEN")
|
|
36
|
+
.map_err(|_| anyhow::anyhow!("WHATSAPP_ACCESS_TOKEN is required"))?;
|
|
37
|
+
let endpoint_id = std::env::var("WHATSAPP_ENDPOINT_ID")
|
|
38
|
+
.map_err(|_| anyhow::anyhow!("WHATSAPP_ENDPOINT_ID is required"))?;
|
|
39
|
+
let verify_token = std::env::var("WHATSAPP_VERIFY_TOKEN")
|
|
40
|
+
.map_err(|_| anyhow::anyhow!("WHATSAPP_VERIFY_TOKEN is required"))?;
|
|
41
|
+
|
|
42
|
+
let allowed_numbers = std::env::var("WHATSAPP_ALLOWED_NUMBERS")
|
|
43
|
+
.ok()
|
|
44
|
+
.map(|value| {
|
|
45
|
+
value
|
|
46
|
+
.split(',')
|
|
47
|
+
.map(|entry| entry.trim().to_string())
|
|
48
|
+
.filter(|entry| !entry.is_empty())
|
|
49
|
+
.collect::<Vec<_>>()
|
|
50
|
+
})
|
|
51
|
+
.unwrap_or_else(|| vec!["*".to_string()]);
|
|
52
|
+
|
|
53
|
+
let channel = Arc::new(WhatsAppChannel::new(
|
|
54
|
+
access_token,
|
|
55
|
+
endpoint_id,
|
|
56
|
+
verify_token,
|
|
57
|
+
allowed_numbers,
|
|
58
|
+
));
|
|
59
|
+
let app_secret = std::env::var("WHATSAPP_APP_SECRET").ok();
|
|
60
|
+
let state = AppState {
|
|
61
|
+
channel,
|
|
62
|
+
app_secret,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
let app = Router::new()
|
|
66
|
+
.route("/whatsapp", get(handle_verify).post(handle_message))
|
|
67
|
+
.with_state(state);
|
|
68
|
+
|
|
69
|
+
let port = std::env::var("WHATSAPP_WEBHOOK_PORT")
|
|
70
|
+
.ok()
|
|
71
|
+
.and_then(|s| s.parse::<u16>().ok())
|
|
72
|
+
.unwrap_or(8080);
|
|
73
|
+
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
|
|
74
|
+
|
|
75
|
+
tracing::info!("whatsapp-echo listening on http://{}/whatsapp", addr);
|
|
76
|
+
let listener = tokio::net::TcpListener::bind(addr).await?;
|
|
77
|
+
axum::serve(listener, app).await?;
|
|
78
|
+
Ok(())
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async fn handle_verify(
|
|
82
|
+
State(state): State<AppState>,
|
|
83
|
+
Query(query): Query<VerifyQuery>,
|
|
84
|
+
) -> impl IntoResponse {
|
|
85
|
+
let token_matches = query
|
|
86
|
+
.verify_token
|
|
87
|
+
.as_deref()
|
|
88
|
+
.is_some_and(|token| token == state.channel.verify_token());
|
|
89
|
+
|
|
90
|
+
if query.mode.as_deref() == Some("subscribe") && token_matches {
|
|
91
|
+
if let Some(challenge) = query.challenge {
|
|
92
|
+
return (StatusCode::OK, challenge);
|
|
93
|
+
}
|
|
94
|
+
return (StatusCode::BAD_REQUEST, "missing hub.challenge".to_string());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
(StatusCode::FORBIDDEN, "forbidden".to_string())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async fn handle_message(
|
|
101
|
+
State(state): State<AppState>,
|
|
102
|
+
headers: HeaderMap,
|
|
103
|
+
body: Bytes,
|
|
104
|
+
) -> impl IntoResponse {
|
|
105
|
+
if let Some(secret) = state.app_secret.as_deref() {
|
|
106
|
+
let signature = headers
|
|
107
|
+
.get("X-Hub-Signature-256")
|
|
108
|
+
.and_then(|v| v.to_str().ok())
|
|
109
|
+
.unwrap_or("");
|
|
110
|
+
|
|
111
|
+
if !verify_whatsapp_signature(secret, &body, signature) {
|
|
112
|
+
return (
|
|
113
|
+
StatusCode::UNAUTHORIZED,
|
|
114
|
+
Json(serde_json::json!({"error": "Invalid signature"})),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let payload: serde_json::Value = match serde_json::from_slice(&body) {
|
|
120
|
+
Ok(value) => value,
|
|
121
|
+
Err(_) => {
|
|
122
|
+
return (
|
|
123
|
+
StatusCode::BAD_REQUEST,
|
|
124
|
+
Json(serde_json::json!({"error": "Invalid JSON payload"})),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
let messages = state.channel.parse_webhook_payload(&payload);
|
|
130
|
+
for msg in messages {
|
|
131
|
+
let reply = format!("echo: {}", msg.content);
|
|
132
|
+
if let Err(err) = state
|
|
133
|
+
.channel
|
|
134
|
+
.send(&SendMessage::new(reply, &msg.reply_target))
|
|
135
|
+
.await
|
|
136
|
+
{
|
|
137
|
+
tracing::warn!("failed to send WhatsApp reply: {err}");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
(StatusCode::OK, Json(serde_json::json!({"status": "ok"})))
|
|
142
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
//! Channel configuration types.
|
|
2
|
+
//!
|
|
3
|
+
//! Simplified from zeroclaw's config schema for enact-channels.
|
|
4
|
+
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
use std::path::PathBuf;
|
|
7
|
+
|
|
8
|
+
/// Build an HTTP client for channel communication.
|
|
9
|
+
///
|
|
10
|
+
/// The `_service_key` parameter is for future proxy configuration.
|
|
11
|
+
pub fn build_runtime_proxy_client(_service_key: &str) -> reqwest::Client {
|
|
12
|
+
reqwest::Client::builder()
|
|
13
|
+
.timeout(std::time::Duration::from_secs(30))
|
|
14
|
+
.build()
|
|
15
|
+
.unwrap_or_else(|_| reqwest::Client::new())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Streaming mode for progressive message updates.
|
|
19
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
20
|
+
#[serde(rename_all = "lowercase")]
|
|
21
|
+
pub enum StreamMode {
|
|
22
|
+
/// No streaming — send the complete response as a single message.
|
|
23
|
+
#[default]
|
|
24
|
+
Off,
|
|
25
|
+
/// Update a draft message with every flush interval.
|
|
26
|
+
Partial,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Telegram channel configuration.
|
|
30
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
31
|
+
pub struct TelegramConfig {
|
|
32
|
+
/// Bot token from @BotFather.
|
|
33
|
+
pub bot_token: String,
|
|
34
|
+
/// List of allowed user IDs (empty = allow all).
|
|
35
|
+
#[serde(default)]
|
|
36
|
+
pub allowed_users: Vec<String>,
|
|
37
|
+
/// Whether to only respond when mentioned in groups.
|
|
38
|
+
#[serde(default)]
|
|
39
|
+
pub mention_only: bool,
|
|
40
|
+
/// Streaming mode for draft updates.
|
|
41
|
+
#[serde(default)]
|
|
42
|
+
pub stream_mode: StreamMode,
|
|
43
|
+
/// Draft update interval in milliseconds.
|
|
44
|
+
#[serde(default = "default_draft_interval")]
|
|
45
|
+
pub draft_update_interval_ms: u64,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn default_draft_interval() -> u64 {
|
|
49
|
+
1000
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl Default for TelegramConfig {
|
|
53
|
+
fn default() -> Self {
|
|
54
|
+
Self {
|
|
55
|
+
bot_token: String::new(),
|
|
56
|
+
allowed_users: Vec::new(),
|
|
57
|
+
mention_only: false,
|
|
58
|
+
stream_mode: StreamMode::Off,
|
|
59
|
+
draft_update_interval_ms: 1000,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Teams channel configuration.
|
|
65
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
66
|
+
pub struct TeamsConfig {
|
|
67
|
+
/// Microsoft App ID from Azure Bot registration.
|
|
68
|
+
pub app_id: String,
|
|
69
|
+
/// Microsoft App Password (client secret).
|
|
70
|
+
pub app_password: String,
|
|
71
|
+
/// List of allowed user IDs (empty = allow all).
|
|
72
|
+
#[serde(default)]
|
|
73
|
+
pub allowed_users: Vec<String>,
|
|
74
|
+
/// Bot display name.
|
|
75
|
+
#[serde(default = "default_bot_name")]
|
|
76
|
+
pub bot_name: String,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// WhatsApp channel configuration.
|
|
80
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
81
|
+
pub struct WhatsAppConfig {
|
|
82
|
+
/// WhatsApp Cloud API access token.
|
|
83
|
+
pub access_token: String,
|
|
84
|
+
/// Meta phone number ID used in Graph API paths.
|
|
85
|
+
pub endpoint_id: String,
|
|
86
|
+
/// Verify token used by webhook challenge endpoint.
|
|
87
|
+
pub verify_token: String,
|
|
88
|
+
/// Allowed sender numbers in E.164 format (+123...).
|
|
89
|
+
#[serde(default)]
|
|
90
|
+
pub allowed_numbers: Vec<String>,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
impl Default for WhatsAppConfig {
|
|
94
|
+
fn default() -> Self {
|
|
95
|
+
Self {
|
|
96
|
+
access_token: String::new(),
|
|
97
|
+
endpoint_id: String::new(),
|
|
98
|
+
verify_token: String::new(),
|
|
99
|
+
allowed_numbers: Vec::new(),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn default_bot_name() -> String {
|
|
105
|
+
"enact".to_string()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
impl Default for TeamsConfig {
|
|
109
|
+
fn default() -> Self {
|
|
110
|
+
Self {
|
|
111
|
+
app_id: String::new(),
|
|
112
|
+
app_password: String::new(),
|
|
113
|
+
allowed_users: Vec::new(),
|
|
114
|
+
bot_name: "enact".to_string(),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Channels configuration container.
|
|
120
|
+
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
121
|
+
pub struct ChannelsConfig {
|
|
122
|
+
/// Telegram configuration.
|
|
123
|
+
pub telegram: Option<TelegramConfig>,
|
|
124
|
+
/// Teams configuration.
|
|
125
|
+
pub teams: Option<TeamsConfig>,
|
|
126
|
+
/// WhatsApp configuration.
|
|
127
|
+
pub whatsapp: Option<WhatsAppConfig>,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Main configuration (simplified for channels).
|
|
131
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
132
|
+
pub struct Config {
|
|
133
|
+
/// Workspace directory.
|
|
134
|
+
#[serde(skip)]
|
|
135
|
+
pub workspace_dir: PathBuf,
|
|
136
|
+
/// Config file path.
|
|
137
|
+
#[serde(skip)]
|
|
138
|
+
pub config_path: PathBuf,
|
|
139
|
+
/// Channels configuration.
|
|
140
|
+
#[serde(default)]
|
|
141
|
+
pub channels: ChannelsConfig,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
impl Default for Config {
|
|
145
|
+
fn default() -> Self {
|
|
146
|
+
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
|
147
|
+
let workspace_dir = home.join(".enact");
|
|
148
|
+
let config_path = workspace_dir.join("config.toml");
|
|
149
|
+
|
|
150
|
+
Self {
|
|
151
|
+
workspace_dir,
|
|
152
|
+
config_path,
|
|
153
|
+
channels: ChannelsConfig::default(),
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
impl Config {
|
|
159
|
+
/// Load configuration from the default path.
|
|
160
|
+
pub fn load() -> anyhow::Result<Self> {
|
|
161
|
+
let home =
|
|
162
|
+
dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Could not find home directory"))?;
|
|
163
|
+
let workspace_dir = home.join(".enact");
|
|
164
|
+
let config_path = workspace_dir.join("config.toml");
|
|
165
|
+
|
|
166
|
+
Self::load_from(&config_path)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Load configuration from a specific path.
|
|
170
|
+
pub fn load_from(path: &PathBuf) -> anyhow::Result<Self> {
|
|
171
|
+
if !path.exists() {
|
|
172
|
+
return Ok(Self::default());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let contents = std::fs::read_to_string(path)?;
|
|
176
|
+
let mut config: Config = toml::from_str(&contents)?;
|
|
177
|
+
|
|
178
|
+
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
|
179
|
+
config.workspace_dir = home.join(".enact");
|
|
180
|
+
config.config_path = path.clone();
|
|
181
|
+
|
|
182
|
+
Ok(config)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get the workspace directory.
|
|
186
|
+
pub fn workspace_dir(&self) -> &PathBuf {
|
|
187
|
+
&self.workspace_dir
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Save configuration to disk.
|
|
191
|
+
pub fn save(&self) -> anyhow::Result<()> {
|
|
192
|
+
// Ensure parent directory exists
|
|
193
|
+
if let Some(parent) = self.config_path.parent() {
|
|
194
|
+
std::fs::create_dir_all(parent)?;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let contents = toml::to_string_pretty(self)?;
|
|
198
|
+
std::fs::write(&self.config_path, contents)?;
|
|
199
|
+
Ok(())
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Alias for backward compatibility with zeroclaw code.
|
|
203
|
+
#[inline]
|
|
204
|
+
pub fn channels_config(&self) -> &ChannelsConfig {
|
|
205
|
+
&self.channels
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/// Mutable access to channels config.
|
|
209
|
+
#[inline]
|
|
210
|
+
pub fn channels_config_mut(&mut self) -> &mut ChannelsConfig {
|
|
211
|
+
&mut self.channels
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//! enact-channels — Channel integrations for Enact
|
|
2
|
+
//!
|
|
3
|
+
//! Provides messaging platform integrations (Telegram, Teams, etc.)
|
|
4
|
+
//! based on zeroclaw's proven channel abstraction.
|
|
5
|
+
|
|
6
|
+
pub mod config;
|
|
7
|
+
pub mod runtime;
|
|
8
|
+
pub mod security;
|
|
9
|
+
pub mod teams;
|
|
10
|
+
pub mod telegram;
|
|
11
|
+
pub mod traits;
|
|
12
|
+
pub mod webhook;
|
|
13
|
+
pub mod whatsapp;
|
|
14
|
+
|
|
15
|
+
pub use config::{
|
|
16
|
+
ChannelsConfig, Config, StreamMode, TeamsConfig, TelegramConfig, WhatsAppConfig,
|
|
17
|
+
build_runtime_proxy_client,
|
|
18
|
+
};
|
|
19
|
+
pub use runtime::{ChannelRuntime, MessageHandler};
|
|
20
|
+
pub use security::PairingGuard;
|
|
21
|
+
pub use teams::{TeamsChannel, TeamsActivity, teams_webhook_handler};
|
|
22
|
+
pub use traits::{Channel, ChannelMessage, SendMessage};
|
|
23
|
+
pub use telegram::TelegramChannel;
|
|
24
|
+
pub use webhook::{WebhookChannel, WebhookPayload};
|
|
25
|
+
pub use whatsapp::{WhatsAppChannel, verify_whatsapp_signature};
|