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.
Files changed (389) hide show
  1. package/.env.example +20 -0
  2. package/.github/workflows/ci.yml +70 -0
  3. package/.github/workflows/publish.yml +250 -0
  4. package/.gitmodules +3 -0
  5. package/Cargo.lock +3584 -0
  6. package/Cargo.toml +97 -0
  7. package/crates/enact/Cargo.toml +27 -0
  8. package/crates/enact/src/lib.rs +60 -0
  9. package/crates/enact-a2a/Cargo.toml +25 -0
  10. package/crates/enact-a2a/src/lib.rs +411 -0
  11. package/crates/enact-channels/Cargo.toml +64 -0
  12. package/crates/enact-channels/examples/README.md +80 -0
  13. package/crates/enact-channels/examples/channel_bot.rs +169 -0
  14. package/crates/enact-channels/examples/telegram-echo.rs +34 -0
  15. package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
  16. package/crates/enact-channels/src/config.rs +213 -0
  17. package/crates/enact-channels/src/lib.rs +25 -0
  18. package/crates/enact-channels/src/runtime.rs +237 -0
  19. package/crates/enact-channels/src/security/mod.rs +5 -0
  20. package/crates/enact-channels/src/security/pairing.rs +205 -0
  21. package/crates/enact-channels/src/teams.rs +601 -0
  22. package/crates/enact-channels/src/telegram.rs +2833 -0
  23. package/crates/enact-channels/src/traits.rs +200 -0
  24. package/crates/enact-channels/src/webhook.rs +262 -0
  25. package/crates/enact-channels/src/whatsapp.rs +310 -0
  26. package/crates/enact-cli/Cargo.toml +40 -0
  27. package/crates/enact-cli/src/commands/doctor.rs +62 -0
  28. package/crates/enact-cli/src/commands/mod.rs +3 -0
  29. package/crates/enact-cli/src/commands/run.rs +69 -0
  30. package/crates/enact-cli/src/commands/serve.rs +81 -0
  31. package/crates/enact-cli/src/config.rs +2 -0
  32. package/crates/enact-cli/src/main.rs +79 -0
  33. package/crates/enact-config/Cargo.toml +36 -0
  34. package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
  35. package/crates/enact-config/QUICK_REFERENCE.md +92 -0
  36. package/crates/enact-config/README.md +107 -0
  37. package/crates/enact-config/TESTING.md +161 -0
  38. package/crates/enact-config/examples/test-env-vars.rs +100 -0
  39. package/crates/enact-config/src/config.rs +399 -0
  40. package/crates/enact-config/src/encrypted_store.rs +211 -0
  41. package/crates/enact-config/src/lib.rs +298 -0
  42. package/crates/enact-config/src/secrets.rs +149 -0
  43. package/crates/enact-config/src/sync.rs +260 -0
  44. package/crates/enact-config/test-env-vars.sh +34 -0
  45. package/crates/enact-config/tests/README.md +99 -0
  46. package/crates/enact-config/tests/config_integration_test.rs +202 -0
  47. package/crates/enact-config/tests/security_test.rs +140 -0
  48. package/crates/enact-context/Cargo.toml +41 -0
  49. package/crates/enact-context/src/budget.rs +314 -0
  50. package/crates/enact-context/src/calibrator.rs +535 -0
  51. package/crates/enact-context/src/compactor.rs +392 -0
  52. package/crates/enact-context/src/condenser.rs +826 -0
  53. package/crates/enact-context/src/lib.rs +94 -0
  54. package/crates/enact-context/src/segment.rs +238 -0
  55. package/crates/enact-context/src/step_context.rs +645 -0
  56. package/crates/enact-context/src/token_counter.rs +148 -0
  57. package/crates/enact-context/src/window.rs +372 -0
  58. package/crates/enact-core/Cargo.toml +42 -0
  59. package/crates/enact-core/README.md +98 -0
  60. package/crates/enact-core/src/background/executor.rs +524 -0
  61. package/crates/enact-core/src/background/mod.rs +48 -0
  62. package/crates/enact-core/src/background/target_binding.rs +390 -0
  63. package/crates/enact-core/src/background/trigger.rs +511 -0
  64. package/crates/enact-core/src/callable/callable.rs +152 -0
  65. package/crates/enact-core/src/callable/composite.rs +817 -0
  66. package/crates/enact-core/src/callable/graph.rs +104 -0
  67. package/crates/enact-core/src/callable/llm.rs +211 -0
  68. package/crates/enact-core/src/callable/mod.rs +64 -0
  69. package/crates/enact-core/src/callable/registry.rs +206 -0
  70. package/crates/enact-core/src/context/execution_context.rs +757 -0
  71. package/crates/enact-core/src/context/invocation.rs +99 -0
  72. package/crates/enact-core/src/context/mod.rs +50 -0
  73. package/crates/enact-core/src/context/tenant.rs +175 -0
  74. package/crates/enact-core/src/context/trace.rs +127 -0
  75. package/crates/enact-core/src/flow/conditional.rs +293 -0
  76. package/crates/enact-core/src/flow/mod.rs +43 -0
  77. package/crates/enact-core/src/flow/parallel.rs +437 -0
  78. package/crates/enact-core/src/flow/repeat.rs +534 -0
  79. package/crates/enact-core/src/flow/sequential.rs +248 -0
  80. package/crates/enact-core/src/graph/checkpoint.rs +79 -0
  81. package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
  82. package/crates/enact-core/src/graph/compiled.rs +189 -0
  83. package/crates/enact-core/src/graph/edge.rs +59 -0
  84. package/crates/enact-core/src/graph/graph_schema.rs +218 -0
  85. package/crates/enact-core/src/graph/loader.rs +155 -0
  86. package/crates/enact-core/src/graph/mod.rs +18 -0
  87. package/crates/enact-core/src/graph/node/function.rs +49 -0
  88. package/crates/enact-core/src/graph/node/mod.rs +48 -0
  89. package/crates/enact-core/src/graph/schema.rs +62 -0
  90. package/crates/enact-core/src/inbox/message.rs +405 -0
  91. package/crates/enact-core/src/inbox/mod.rs +31 -0
  92. package/crates/enact-core/src/inbox/store.rs +355 -0
  93. package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
  94. package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
  95. package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
  96. package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
  97. package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
  98. package/crates/enact-core/src/kernel/error.rs +1200 -0
  99. package/crates/enact-core/src/kernel/event.rs +1394 -0
  100. package/crates/enact-core/src/kernel/execution_model.rs +831 -0
  101. package/crates/enact-core/src/kernel/execution_state.rs +189 -0
  102. package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
  103. package/crates/enact-core/src/kernel/ids.rs +2086 -0
  104. package/crates/enact-core/src/kernel/interrupt.rs +125 -0
  105. package/crates/enact-core/src/kernel/kernel.rs +1283 -0
  106. package/crates/enact-core/src/kernel/mod.rs +205 -0
  107. package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
  108. package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
  109. package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
  110. package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
  111. package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
  112. package/crates/enact-core/src/kernel/reducer.rs +808 -0
  113. package/crates/enact-core/src/kernel/replay.rs +153 -0
  114. package/crates/enact-core/src/lib.rs +413 -0
  115. package/crates/enact-core/src/memory/episodic.rs +0 -0
  116. package/crates/enact-core/src/memory/mod.rs +6 -0
  117. package/crates/enact-core/src/memory/semantic.rs +0 -0
  118. package/crates/enact-core/src/memory/trait.rs +0 -0
  119. package/crates/enact-core/src/memory/vector_db.rs +0 -0
  120. package/crates/enact-core/src/memory/working.rs +0 -0
  121. package/crates/enact-core/src/policy/execution_policy.rs +292 -0
  122. package/crates/enact-core/src/policy/filters.rs +458 -0
  123. package/crates/enact-core/src/policy/input_processor.rs +407 -0
  124. package/crates/enact-core/src/policy/long_running.rs +134 -0
  125. package/crates/enact-core/src/policy/mod.rs +193 -0
  126. package/crates/enact-core/src/policy/pii_input.rs +274 -0
  127. package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
  128. package/crates/enact-core/src/policy/tool_policy.rs +407 -0
  129. package/crates/enact-core/src/providers/mod.rs +63 -0
  130. package/crates/enact-core/src/providers/trait.rs +292 -0
  131. package/crates/enact-core/src/runner/callbacks.rs +6 -0
  132. package/crates/enact-core/src/runner/execution_runner.rs +476 -0
  133. package/crates/enact-core/src/runner/loop.rs +117 -0
  134. package/crates/enact-core/src/runner/mod.rs +58 -0
  135. package/crates/enact-core/src/runner/protected_runner.rs +280 -0
  136. package/crates/enact-core/src/signal/inmemory.rs +231 -0
  137. package/crates/enact-core/src/signal/mod.rs +108 -0
  138. package/crates/enact-core/src/streaming/event_logger.rs +195 -0
  139. package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
  140. package/crates/enact-core/src/streaming/mod.rs +108 -0
  141. package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
  142. package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
  143. package/crates/enact-core/src/streaming/protection/context.rs +136 -0
  144. package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
  145. package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
  146. package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
  147. package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
  148. package/crates/enact-core/src/streaming/sse.rs +0 -0
  149. package/crates/enact-core/src/telemetry/exporter.rs +0 -0
  150. package/crates/enact-core/src/telemetry/init.rs +0 -0
  151. package/crates/enact-core/src/telemetry/mod.rs +49 -0
  152. package/crates/enact-core/src/telemetry/spans.rs +245 -0
  153. package/crates/enact-core/src/tool/agent_tool.rs +177 -0
  154. package/crates/enact-core/src/tool/browser/mod.rs +0 -0
  155. package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
  156. package/crates/enact-core/src/tool/cost.rs +247 -0
  157. package/crates/enact-core/src/tool/discovery.rs +0 -0
  158. package/crates/enact-core/src/tool/dispatcher.rs +347 -0
  159. package/crates/enact-core/src/tool/filesystem.rs +231 -0
  160. package/crates/enact-core/src/tool/function.rs +99 -0
  161. package/crates/enact-core/src/tool/git.rs +162 -0
  162. package/crates/enact-core/src/tool/http.rs +214 -0
  163. package/crates/enact-core/src/tool/mcp/client.rs +0 -0
  164. package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
  165. package/crates/enact-core/src/tool/mod.rs +51 -0
  166. package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
  167. package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
  168. package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
  169. package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
  170. package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
  171. package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
  172. package/crates/enact-core/src/tool/shell.rs +147 -0
  173. package/crates/enact-core/src/tool/trait.rs +33 -0
  174. package/crates/enact-core/src/tool/web_search.rs +277 -0
  175. package/crates/enact-core/src/util/config.rs +0 -0
  176. package/crates/enact-core/src/util/errors.rs +0 -0
  177. package/crates/enact-core/src/util/mod.rs +6 -0
  178. package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
  179. package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
  180. package/crates/enact-core/tests/e2e_test.rs +259 -0
  181. package/crates/enact-core/tests/graph_test.rs +130 -0
  182. package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
  183. package/crates/enact-cron/Cargo.toml +28 -0
  184. package/crates/enact-cron/src/lib.rs +44 -0
  185. package/crates/enact-cron/src/schedule.rs +156 -0
  186. package/crates/enact-cron/src/store.rs +589 -0
  187. package/crates/enact-cron/src/types.rs +148 -0
  188. package/crates/enact-gateway/Cargo.toml +31 -0
  189. package/crates/enact-gateway/README.md +30 -0
  190. package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
  191. package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
  192. package/crates/enact-gateway/src/lib.rs +582 -0
  193. package/crates/enact-mcp/Cargo.toml +24 -0
  194. package/crates/enact-mcp/src/lib.rs +178 -0
  195. package/crates/enact-memory/Cargo.toml +25 -0
  196. package/crates/enact-memory/src/backend.rs +20 -0
  197. package/crates/enact-memory/src/chunker.rs +230 -0
  198. package/crates/enact-memory/src/embeddings.rs +221 -0
  199. package/crates/enact-memory/src/lib.rs +67 -0
  200. package/crates/enact-memory/src/markdown.rs +127 -0
  201. package/crates/enact-memory/src/none.rs +61 -0
  202. package/crates/enact-memory/src/sqlite.rs +276 -0
  203. package/crates/enact-memory/src/traits.rs +65 -0
  204. package/crates/enact-memory/src/vector.rs +198 -0
  205. package/crates/enact-oauth/Cargo.toml +27 -0
  206. package/crates/enact-oauth/src/lib.rs +584 -0
  207. package/crates/enact-observability/Cargo.toml +22 -0
  208. package/crates/enact-observability/src/lib.rs +197 -0
  209. package/crates/enact-providers/Cargo.toml +33 -0
  210. package/crates/enact-providers/examples/hello-agent.rs +33 -0
  211. package/crates/enact-providers/src/anthropic.rs +182 -0
  212. package/crates/enact-providers/src/azure.rs +96 -0
  213. package/crates/enact-providers/src/bridge.rs +221 -0
  214. package/crates/enact-providers/src/gemini.rs +227 -0
  215. package/crates/enact-providers/src/http.rs +78 -0
  216. package/crates/enact-providers/src/lib.rs +53 -0
  217. package/crates/enact-providers/src/openai_compatible.rs +167 -0
  218. package/crates/enact-providers/src/openrouter.rs +33 -0
  219. package/crates/enact-runner/Cargo.toml +24 -0
  220. package/crates/enact-runner/README.md +76 -0
  221. package/crates/enact-runner/src/compaction.rs +225 -0
  222. package/crates/enact-runner/src/config.rs +118 -0
  223. package/crates/enact-runner/src/lib.rs +63 -0
  224. package/crates/enact-runner/src/loop_driver.rs +414 -0
  225. package/crates/enact-runner/src/parser.rs +421 -0
  226. package/crates/enact-runner/src/retry.rs +262 -0
  227. package/crates/enact-runner/tests/integration.rs +278 -0
  228. package/crates/enact-security/Cargo.toml +22 -0
  229. package/crates/enact-security/src/audit.rs +375 -0
  230. package/crates/enact-security/src/lib.rs +37 -0
  231. package/crates/enact-security/src/policy.rs +406 -0
  232. package/crates/enact-skills/Cargo.toml +25 -0
  233. package/crates/enact-skills/src/lib.rs +506 -0
  234. package/crates/enact-tools/Cargo.toml +22 -0
  235. package/crates/enact-tools/src/file_read.rs +166 -0
  236. package/crates/enact-tools/src/file_write.rs +216 -0
  237. package/crates/enact-tools/src/git_operations.rs +513 -0
  238. package/crates/enact-tools/src/http_request.rs +417 -0
  239. package/crates/enact-tools/src/lib.rs +104 -0
  240. package/crates/enact-tools/src/security.rs +227 -0
  241. package/crates/enact-tools/src/shell.rs +191 -0
  242. package/crates/enact-tools/src/traits.rs +159 -0
  243. package/docs/Makefile +74 -0
  244. package/docs/config.toml +62 -0
  245. package/docs/content/_index.md +174 -0
  246. package/docs/content/a2a/_index.md +431 -0
  247. package/docs/content/api/_index.md +323 -0
  248. package/docs/content/channels/_index.md +160 -0
  249. package/docs/content/channels/teams.md +205 -0
  250. package/docs/content/channels/telegram.md +182 -0
  251. package/docs/content/channels/webhook.md +423 -0
  252. package/docs/content/channels/whatsapp.md +240 -0
  253. package/docs/content/cli/_index.md +261 -0
  254. package/docs/content/concepts/_index.md +273 -0
  255. package/docs/content/configuration/_index.md +241 -0
  256. package/docs/content/cron/_index.md +248 -0
  257. package/docs/content/developers/_index.md +278 -0
  258. package/docs/content/getting-started/_index.md +180 -0
  259. package/docs/content/installation/_index.md +186 -0
  260. package/docs/content/installation/uninstall.md +101 -0
  261. package/docs/content/installation/updating.md +120 -0
  262. package/docs/content/mcp/_index.md +215 -0
  263. package/docs/content/memory/_index.md +163 -0
  264. package/docs/content/oauth/_index.md +515 -0
  265. package/docs/content/providers/_index.md +206 -0
  266. package/docs/content/roadmap/_index.md +199 -0
  267. package/docs/content/security/_index.md +219 -0
  268. package/docs/content/skills/_index.md +228 -0
  269. package/docs/content/tools/_index.md +485 -0
  270. package/docs/content/troubleshooting/_index.md +259 -0
  271. package/docs/content/yaml-schema/_index.md +294 -0
  272. package/docs/static/giallo-dark.css +91 -0
  273. package/docs/static/giallo-light.css +91 -0
  274. package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
  275. package/docs/themes/tanuki/LICENSE +21 -0
  276. package/docs/themes/tanuki/README.md +166 -0
  277. package/docs/themes/tanuki/examples/blog/config.toml +58 -0
  278. package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
  279. package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
  280. package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
  281. package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
  282. package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
  283. package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
  284. package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
  285. package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
  286. package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
  287. package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
  288. package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
  289. package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
  290. package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
  291. package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
  292. package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
  293. package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
  294. package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
  295. package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
  296. package/docs/themes/tanuki/examples/book/config.toml +17 -0
  297. package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
  298. package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
  299. package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
  300. package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
  301. package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
  302. package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
  303. package/docs/themes/tanuki/examples/book/content/print.md +6 -0
  304. package/docs/themes/tanuki/examples/docs/config.toml +28 -0
  305. package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
  306. package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
  307. package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
  308. package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
  309. package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
  310. package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
  311. package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
  312. package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
  313. package/docs/themes/tanuki/examples/index.html +2104 -0
  314. package/docs/themes/tanuki/mise.toml +108 -0
  315. package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
  316. package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
  317. package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
  318. package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
  319. package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
  320. package/docs/themes/tanuki/sass/components/_code.scss +457 -0
  321. package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
  322. package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
  323. package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
  324. package/docs/themes/tanuki/sass/components/_print.scss +237 -0
  325. package/docs/themes/tanuki/sass/components/_search.scss +224 -0
  326. package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
  327. package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
  328. package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
  329. package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
  330. package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
  331. package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
  332. package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
  333. package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
  334. package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
  335. package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
  336. package/docs/themes/tanuki/sass/style.scss +47 -0
  337. package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
  338. package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
  339. package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
  340. package/docs/themes/tanuki/screenshot.png +0 -0
  341. package/docs/themes/tanuki/sentinel.kdl +59 -0
  342. package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
  343. package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
  344. package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
  345. package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
  346. package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
  347. package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
  348. package/docs/themes/tanuki/static/js/anchors.js +18 -0
  349. package/docs/themes/tanuki/static/js/app.js +274 -0
  350. package/docs/themes/tanuki/static/js/code.js +394 -0
  351. package/docs/themes/tanuki/static/js/navigation.js +778 -0
  352. package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
  353. package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
  354. package/docs/themes/tanuki/static/js/search.js +215 -0
  355. package/docs/themes/tanuki/static/js/theme.js +169 -0
  356. package/docs/themes/tanuki/static/syntax-dark.css +151 -0
  357. package/docs/themes/tanuki/static/syntax-light.css +151 -0
  358. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
  359. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
  360. package/docs/themes/tanuki/templates/404.html +52 -0
  361. package/docs/themes/tanuki/templates/base.html +428 -0
  362. package/docs/themes/tanuki/templates/blog.html +66 -0
  363. package/docs/themes/tanuki/templates/home.html +108 -0
  364. package/docs/themes/tanuki/templates/index.html +178 -0
  365. package/docs/themes/tanuki/templates/landing.html +168 -0
  366. package/docs/themes/tanuki/templates/macros/nav.html +128 -0
  367. package/docs/themes/tanuki/templates/macros/posts.html +101 -0
  368. package/docs/themes/tanuki/templates/macros/ui.html +159 -0
  369. package/docs/themes/tanuki/templates/page.html +135 -0
  370. package/docs/themes/tanuki/templates/partials/footer.html +38 -0
  371. package/docs/themes/tanuki/templates/partials/header.html +366 -0
  372. package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
  373. package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
  374. package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
  375. package/docs/themes/tanuki/templates/partials/search.html +52 -0
  376. package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
  377. package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
  378. package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
  379. package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
  380. package/docs/themes/tanuki/templates/print.html +244 -0
  381. package/docs/themes/tanuki/templates/section.html +186 -0
  382. package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
  383. package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
  384. package/docs/themes/tanuki/theme.toml +58 -0
  385. package/examples/hello-agent.rs +55 -0
  386. package/package.json +36 -0
  387. package/proto/config.proto +60 -0
  388. package/proto/events.proto +0 -0
  389. package/proto/runtime.proto +215 -0
@@ -0,0 +1,310 @@
1
+ use crate::traits::{Channel, ChannelMessage, SendMessage};
2
+ use async_trait::async_trait;
3
+ use hmac::{Hmac, Mac};
4
+ use sha2::Sha256;
5
+ use uuid::Uuid;
6
+
7
+ /// WhatsApp channel using the WhatsApp Business Cloud API.
8
+ ///
9
+ /// This channel runs in webhook mode for inbound traffic. `listen` keeps the
10
+ /// runtime alive while webhook handlers push payloads through `parse_webhook_payload`.
11
+ pub struct WhatsAppChannel {
12
+ access_token: String,
13
+ endpoint_id: String,
14
+ verify_token: String,
15
+ allowed_numbers: Vec<String>,
16
+ }
17
+
18
+ impl WhatsAppChannel {
19
+ pub fn new(
20
+ access_token: String,
21
+ endpoint_id: String,
22
+ verify_token: String,
23
+ allowed_numbers: Vec<String>,
24
+ ) -> Self {
25
+ Self {
26
+ access_token,
27
+ endpoint_id,
28
+ verify_token,
29
+ allowed_numbers,
30
+ }
31
+ }
32
+
33
+ fn http_client(&self) -> reqwest::Client {
34
+ crate::config::build_runtime_proxy_client("channel.whatsapp")
35
+ }
36
+
37
+ fn normalize_phone(phone: &str) -> String {
38
+ if phone.starts_with('+') {
39
+ phone.to_string()
40
+ } else {
41
+ format!("+{phone}")
42
+ }
43
+ }
44
+
45
+ /// Check if a phone number is allowlisted.
46
+ fn is_number_allowed(&self, phone: &str) -> bool {
47
+ self.allowed_numbers.iter().any(|n| n == "*" || n == phone)
48
+ }
49
+
50
+ /// Verify token used by the GET webhook challenge endpoint.
51
+ pub fn verify_token(&self) -> &str {
52
+ &self.verify_token
53
+ }
54
+
55
+ /// Parse inbound webhook payload and extract text messages.
56
+ pub fn parse_webhook_payload(&self, payload: &serde_json::Value) -> Vec<ChannelMessage> {
57
+ let mut messages = Vec::new();
58
+
59
+ let Some(entries) = payload.get("entry").and_then(|e| e.as_array()) else {
60
+ return messages;
61
+ };
62
+
63
+ for entry in entries {
64
+ let Some(changes) = entry.get("changes").and_then(|c| c.as_array()) else {
65
+ continue;
66
+ };
67
+
68
+ for change in changes {
69
+ let Some(value) = change.get("value") else {
70
+ continue;
71
+ };
72
+
73
+ let Some(inbound) = value.get("messages").and_then(|m| m.as_array()) else {
74
+ continue;
75
+ };
76
+
77
+ for msg in inbound {
78
+ let Some(raw_from) = msg.get("from").and_then(|f| f.as_str()) else {
79
+ continue;
80
+ };
81
+
82
+ let from = Self::normalize_phone(raw_from);
83
+ if !self.is_number_allowed(&from) {
84
+ tracing::warn!(
85
+ "WhatsApp: ignoring unauthorized sender {}. Add it to allowed_numbers.",
86
+ from
87
+ );
88
+ continue;
89
+ }
90
+
91
+ let content = if let Some(text_obj) = msg.get("text") {
92
+ text_obj
93
+ .get("body")
94
+ .and_then(|b| b.as_str())
95
+ .unwrap_or("")
96
+ .to_string()
97
+ } else {
98
+ continue;
99
+ };
100
+
101
+ if content.is_empty() {
102
+ continue;
103
+ }
104
+
105
+ let timestamp = msg
106
+ .get("timestamp")
107
+ .and_then(|t| t.as_str())
108
+ .and_then(|t| t.parse::<u64>().ok())
109
+ .unwrap_or_else(|| {
110
+ std::time::SystemTime::now()
111
+ .duration_since(std::time::UNIX_EPOCH)
112
+ .unwrap_or_default()
113
+ .as_secs()
114
+ });
115
+
116
+ messages.push(ChannelMessage {
117
+ id: Uuid::new_v4().to_string(),
118
+ reply_target: from.clone(),
119
+ sender: from,
120
+ content,
121
+ channel: "whatsapp".to_string(),
122
+ timestamp,
123
+ });
124
+ }
125
+ }
126
+ }
127
+
128
+ messages
129
+ }
130
+ }
131
+
132
+ #[async_trait]
133
+ impl Channel for WhatsAppChannel {
134
+ fn name(&self) -> &str {
135
+ "whatsapp"
136
+ }
137
+
138
+ async fn send(&self, message: &SendMessage) -> anyhow::Result<()> {
139
+ let url = format!(
140
+ "https://graph.facebook.com/v18.0/{}/messages",
141
+ self.endpoint_id
142
+ );
143
+
144
+ let to = message
145
+ .recipient
146
+ .strip_prefix('+')
147
+ .unwrap_or(&message.recipient);
148
+
149
+ let body = serde_json::json!({
150
+ "messaging_product": "whatsapp",
151
+ "recipient_type": "individual",
152
+ "to": to,
153
+ "type": "text",
154
+ "text": {
155
+ "preview_url": false,
156
+ "body": message.content
157
+ }
158
+ });
159
+
160
+ let resp = self
161
+ .http_client()
162
+ .post(&url)
163
+ .bearer_auth(&self.access_token)
164
+ .header("Content-Type", "application/json")
165
+ .json(&body)
166
+ .send()
167
+ .await?;
168
+
169
+ if !resp.status().is_success() {
170
+ let status = resp.status();
171
+ let error_body = resp.text().await.unwrap_or_default();
172
+ anyhow::bail!("WhatsApp API error ({}): {}", status, error_body);
173
+ }
174
+
175
+ Ok(())
176
+ }
177
+
178
+ async fn listen(&self, _tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {
179
+ tracing::info!(
180
+ "WhatsApp channel active (webhook mode). Configure Meta webhook endpoint separately."
181
+ );
182
+ loop {
183
+ tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
184
+ }
185
+ }
186
+
187
+ async fn health_check(&self) -> bool {
188
+ let url = format!("https://graph.facebook.com/v18.0/{}", self.endpoint_id);
189
+ self.http_client()
190
+ .get(url)
191
+ .bearer_auth(&self.access_token)
192
+ .send()
193
+ .await
194
+ .map(|r| r.status().is_success())
195
+ .unwrap_or(false)
196
+ }
197
+ }
198
+
199
+ /// Verify the WhatsApp webhook signature (`X-Hub-Signature-256`).
200
+ pub fn verify_whatsapp_signature(app_secret: &str, body: &[u8], signature_header: &str) -> bool {
201
+ let Some(hex_sig) = signature_header.strip_prefix("sha256=") else {
202
+ return false;
203
+ };
204
+
205
+ let Ok(expected) = hex::decode(hex_sig) else {
206
+ return false;
207
+ };
208
+
209
+ let Ok(mut mac) = Hmac::<Sha256>::new_from_slice(app_secret.as_bytes()) else {
210
+ return false;
211
+ };
212
+ mac.update(body);
213
+ mac.verify_slice(&expected).is_ok()
214
+ }
215
+
216
+ #[cfg(test)]
217
+ mod tests {
218
+ use super::*;
219
+
220
+ fn make_channel() -> WhatsAppChannel {
221
+ WhatsAppChannel::new(
222
+ "test-token".into(),
223
+ "123456789".into(),
224
+ "verify-me".into(),
225
+ vec!["+1234567890".into()],
226
+ )
227
+ }
228
+
229
+ #[test]
230
+ fn whatsapp_channel_name() {
231
+ let ch = make_channel();
232
+ assert_eq!(ch.name(), "whatsapp");
233
+ }
234
+
235
+ #[test]
236
+ fn whatsapp_verify_token() {
237
+ let ch = make_channel();
238
+ assert_eq!(ch.verify_token(), "verify-me");
239
+ }
240
+
241
+ #[test]
242
+ fn whatsapp_parse_valid_text_message() {
243
+ let ch = make_channel();
244
+ let payload = serde_json::json!({
245
+ "object": "whatsapp_business_account",
246
+ "entry": [{
247
+ "changes": [{
248
+ "value": {
249
+ "messages": [{
250
+ "from": "1234567890",
251
+ "timestamp": "1699999999",
252
+ "type": "text",
253
+ "text": { "body": "Hello Enact!" }
254
+ }]
255
+ }
256
+ }]
257
+ }]
258
+ });
259
+
260
+ let msgs = ch.parse_webhook_payload(&payload);
261
+ assert_eq!(msgs.len(), 1);
262
+ assert_eq!(msgs[0].sender, "+1234567890");
263
+ assert_eq!(msgs[0].content, "Hello Enact!");
264
+ assert_eq!(msgs[0].channel, "whatsapp");
265
+ }
266
+
267
+ #[test]
268
+ fn whatsapp_parse_unauthorized_number() {
269
+ let ch = make_channel();
270
+ let payload = serde_json::json!({
271
+ "entry": [{
272
+ "changes": [{
273
+ "value": {
274
+ "messages": [{
275
+ "from": "9999999999",
276
+ "timestamp": "1",
277
+ "type": "text",
278
+ "text": { "body": "Spam" }
279
+ }]
280
+ }
281
+ }]
282
+ }]
283
+ });
284
+
285
+ let msgs = ch.parse_webhook_payload(&payload);
286
+ assert!(msgs.is_empty());
287
+ }
288
+
289
+ #[test]
290
+ fn whatsapp_signature_valid() {
291
+ let secret = "test_app_secret";
292
+ let body = b"test payload";
293
+
294
+ let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
295
+ mac.update(body);
296
+ let sig = format!("sha256={}", hex::encode(mac.finalize().into_bytes()));
297
+
298
+ assert!(verify_whatsapp_signature(secret, body, &sig));
299
+ }
300
+
301
+ #[test]
302
+ fn whatsapp_signature_invalid_cases() {
303
+ let secret = "test_app_secret";
304
+ let body = b"test payload";
305
+
306
+ assert!(!verify_whatsapp_signature(secret, body, ""));
307
+ assert!(!verify_whatsapp_signature(secret, body, "abc123"));
308
+ assert!(!verify_whatsapp_signature(secret, body, "sha256=not-valid-hex!!"));
309
+ }
310
+ }
@@ -0,0 +1,40 @@
1
+ [package]
2
+ name = "enact"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "Enact CLI - Primary interface for running agents and managing configuration"
7
+ repository.workspace = true
8
+ homepage.workspace = true
9
+ keywords = ["cli", "agent", "ai", "llm", "graph-native"]
10
+ categories.workspace = true
11
+
12
+ [[bin]]
13
+ name = "enact"
14
+ path = "src/main.rs"
15
+
16
+ [dependencies]
17
+ enact-core.workspace = true
18
+ enact-config.workspace = true
19
+ enact-providers.workspace = true
20
+ enact-runner.workspace = true
21
+ enact-channels.workspace = true
22
+ enact-gateway.workspace = true
23
+ enact-memory.workspace = true
24
+
25
+ anyhow.workspace = true
26
+ async-trait.workspace = true
27
+ tokio.workspace = true
28
+ tokio-util.workspace = true
29
+ tracing.workspace = true
30
+ tracing-subscriber = { workspace = true, features = ["env-filter"] }
31
+ serde.workspace = true
32
+ serde_json.workspace = true
33
+ toml.workspace = true
34
+ clap = { version = "4.5", features = ["derive", "env"] }
35
+ clap_complete = "4.5"
36
+ comfy-table = "7.1"
37
+ dirs = "5.0"
38
+
39
+ [dev-dependencies]
40
+ tempfile.workspace = true
@@ -0,0 +1,62 @@
1
+ use comfy_table::Table;
2
+ use std::env;
3
+
4
+ pub async fn execute() -> anyhow::Result<()> {
5
+ println!("Enact System Check\n");
6
+
7
+ let mut table = Table::new();
8
+ table.set_header(vec!["Component", "Status", "Details"]);
9
+
10
+ // Check API keys
11
+ let openai_key = env::var("OPENAI_API_KEY").is_ok();
12
+ let azure_key = env::var("AZURE_OPENAI_API_KEY").is_ok();
13
+ let api_status = if openai_key || azure_key {
14
+ ("✓".to_string(), "Configured".to_string())
15
+ } else {
16
+ ("✗".to_string(), "Missing - set OPENAI_API_KEY or AZURE_OPENAI_API_KEY".to_string())
17
+ };
18
+ table.add_row(vec![
19
+ "API Key",
20
+ &api_status.0,
21
+ &api_status.1,
22
+ ]);
23
+
24
+ // Check WhatsApp config
25
+ let whatsapp_configured = env::var("WHATSAPP_ACCESS_TOKEN").is_ok() &&
26
+ env::var("WHATSAPP_ENDPOINT_ID").is_ok() &&
27
+ env::var("WHATSAPP_VERIFY_TOKEN").is_ok();
28
+ table.add_row(vec![
29
+ "WhatsApp",
30
+ if whatsapp_configured { "✓" } else { "○" },
31
+ if whatsapp_configured { "Configured" } else { "Optional - not configured" },
32
+ ]);
33
+
34
+ // Check model configuration
35
+ let model = env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-4o-mini (default)".to_string());
36
+ table.add_row(vec![
37
+ "Model",
38
+ "✓",
39
+ &model,
40
+ ]);
41
+
42
+ println!("{}", table);
43
+
44
+ println!("\nEnvironment Variables:");
45
+ let vars = vec![
46
+ "OPENAI_API_KEY",
47
+ "AZURE_OPENAI_API_KEY",
48
+ "OPENAI_BASE_URL",
49
+ "OPENAI_MODEL",
50
+ "WHATSAPP_ACCESS_TOKEN",
51
+ "WHATSAPP_ENDPOINT_ID",
52
+ "WHATSAPP_VERIFY_TOKEN",
53
+ "WHATSAPP_APP_SECRET",
54
+ ];
55
+
56
+ for var in vars {
57
+ let status = if env::var(var).is_ok() { "✓ set" } else { " not set" };
58
+ println!(" {} {}", status, var);
59
+ }
60
+
61
+ Ok(())
62
+ }
@@ -0,0 +1,3 @@
1
+ pub mod doctor;
2
+ pub mod run;
3
+ pub mod serve;
@@ -0,0 +1,69 @@
1
+ use enact_core::callable::LlmCallable;
2
+ use enact_providers::OpenAICompatible;
3
+ use enact_runner::{DefaultAgentRunner, LoopOutcome};
4
+ use std::sync::Arc;
5
+ use tracing::{info, error};
6
+
7
+ pub async fn execute(
8
+ input: String,
9
+ agent_name: String,
10
+ system_prompt: Option<String>,
11
+ ) -> anyhow::Result<()> {
12
+ info!("Initializing agent '{}'...", agent_name);
13
+
14
+ // Load API key from environment
15
+ let api_key = std::env::var("OPENAI_API_KEY")
16
+ .or_else(|_| std::env::var("AZURE_OPENAI_API_KEY"))
17
+ .map_err(|_| anyhow::anyhow!("No API key found. Set OPENAI_API_KEY or AZURE_OPENAI_API_KEY"))?;
18
+
19
+ let base_url = std::env::var("OPENAI_BASE_URL")
20
+ .unwrap_or_else(|_| "https://api.openai.com".to_string());
21
+
22
+ let model = std::env::var("OPENAI_MODEL")
23
+ .unwrap_or_else(|_| "gpt-4o-mini".to_string());
24
+
25
+ // Create provider
26
+ let provider = Arc::new(OpenAICompatible::new(
27
+ base_url,
28
+ model.clone(),
29
+ Some(api_key),
30
+ ));
31
+
32
+ // Create callable with system prompt
33
+ let system = system_prompt.unwrap_or_else(|| {
34
+ "You are a helpful assistant.".to_string()
35
+ });
36
+
37
+ let callable = LlmCallable::with_provider(
38
+ agent_name.clone(),
39
+ system,
40
+ provider,
41
+ );
42
+
43
+ info!("Running agent with model: {}", model);
44
+
45
+ // Create runner and execute
46
+ let mut runner = DefaultAgentRunner::default_new();
47
+
48
+ match runner.run(&callable, &input).await {
49
+ Ok(LoopOutcome::Completed(output)) => {
50
+ println!("\n{}", output);
51
+ }
52
+ Ok(LoopOutcome::MaxIterationsReached { last_output, iterations }) => {
53
+ println!("\n{}", last_output);
54
+ eprintln!("\nWarning: Max iterations ({}) reached", iterations);
55
+ }
56
+ Ok(LoopOutcome::Cancelled) => {
57
+ eprintln!("Execution cancelled");
58
+ }
59
+ Ok(LoopOutcome::TimedOut { elapsed_secs }) => {
60
+ eprintln!("Execution timed out after {} seconds", elapsed_secs);
61
+ }
62
+ Err(e) => {
63
+ error!("Execution failed: {}", e);
64
+ return Err(e);
65
+ }
66
+ }
67
+
68
+ Ok(())
69
+ }
@@ -0,0 +1,81 @@
1
+ use enact_channels::WhatsAppChannel;
2
+ use enact_gateway::{serve as start_gateway, GatewayConfig, GatewayState, RunnerResponder};
3
+ use enact_core::callable::LlmCallable;
4
+ use enact_providers::OpenAICompatible;
5
+ use std::sync::Arc;
6
+ use tracing::info;
7
+
8
+ pub async fn execute(http_port: u16, air_gapped: bool) -> anyhow::Result<()> {
9
+ if air_gapped {
10
+ info!("Running in air-gapped mode - external network calls disabled");
11
+ }
12
+
13
+ // Check for WhatsApp configuration
14
+ let whatsapp_config = match (
15
+ std::env::var("WHATSAPP_ACCESS_TOKEN"),
16
+ std::env::var("WHATSAPP_ENDPOINT_ID"),
17
+ std::env::var("WHATSAPP_VERIFY_TOKEN"),
18
+ ) {
19
+ (Ok(token), Ok(endpoint), Ok(verify)) => {
20
+ info!("WhatsApp channel configured");
21
+ Some((token, endpoint, verify))
22
+ }
23
+ _ => {
24
+ info!("WhatsApp not configured - webhook endpoints will not be available");
25
+ None
26
+ }
27
+ };
28
+
29
+ if let Some((access_token, endpoint_id, verify_token)) = whatsapp_config {
30
+ // Create WhatsApp channel
31
+ let allowed_numbers = std::env::var("WHATSAPP_ALLOWED_NUMBERS")
32
+ .ok()
33
+ .map(|v| v.split(',').map(|s| s.trim().to_string()).collect())
34
+ .unwrap_or_else(|| vec!["*".to_string()]);
35
+
36
+ let whatsapp = Arc::new(WhatsAppChannel::new(
37
+ access_token,
38
+ endpoint_id,
39
+ verify_token,
40
+ allowed_numbers,
41
+ ));
42
+
43
+ // Create callable for handling messages
44
+ let api_key = std::env::var("OPENAI_API_KEY").ok();
45
+ let responder: Arc<dyn enact_gateway::InboundResponder> = if let Some(key) = api_key {
46
+ let base_url = std::env::var("OPENAI_BASE_URL")
47
+ .unwrap_or_else(|_| "https://api.openai.com".to_string());
48
+ let model = std::env::var("OPENAI_MODEL")
49
+ .unwrap_or_else(|_| "gpt-4o-mini".to_string());
50
+
51
+ let provider = Arc::new(OpenAICompatible::new(base_url, model, Some(key)));
52
+ let callable = LlmCallable::with_provider(
53
+ "whatsapp_agent",
54
+ "You are a helpful WhatsApp assistant. Keep responses concise.",
55
+ provider,
56
+ );
57
+
58
+ Arc::new(RunnerResponder::new(Arc::new(callable)))
59
+ } else {
60
+ // Fallback to echo responder if no API key
61
+ Arc::new(enact_gateway::EchoResponder)
62
+ };
63
+
64
+ let app_secret = std::env::var("WHATSAPP_APP_SECRET").ok();
65
+
66
+ let config = GatewayConfig {
67
+ host: "0.0.0.0".to_string(),
68
+ port: http_port,
69
+ ..Default::default()
70
+ };
71
+
72
+ let state = GatewayState::new(whatsapp, app_secret, responder, &config);
73
+
74
+ info!("Starting gateway server on port {}", http_port);
75
+ start_gateway(config, state).await?;
76
+ } else {
77
+ anyhow::bail!("No channels configured. Set WhatsApp environment variables to start server.");
78
+ }
79
+
80
+ Ok(())
81
+ }
@@ -0,0 +1,2 @@
1
+ // Configuration loading and management
2
+ // TODO: Implement config file loading (TOML/JSON)
@@ -0,0 +1,79 @@
1
+ use clap::{Parser, Subcommand};
2
+ use tracing::info;
3
+
4
+ mod commands;
5
+ mod config;
6
+
7
+ use commands::{run, serve};
8
+
9
+ #[derive(Parser)]
10
+ #[command(name = "enact")]
11
+ #[command(about = "Enact CLI - Run agents, manage configuration, and start servers")]
12
+ #[command(version)]
13
+ struct Cli {
14
+ #[command(subcommand)]
15
+ command: Commands,
16
+ }
17
+
18
+ #[derive(Subcommand)]
19
+ enum Commands {
20
+ /// Execute an agent once with given input
21
+ Run {
22
+ /// Input to send to the agent
23
+ #[arg(short, long)]
24
+ input: String,
25
+
26
+ /// Agent name
27
+ #[arg(short, long, default_value = "assistant")]
28
+ agent: String,
29
+
30
+ /// System prompt override
31
+ #[arg(long)]
32
+ system_prompt: Option<String>,
33
+ },
34
+
35
+ /// Start HTTP/gRPC server for API access
36
+ Serve {
37
+ /// HTTP port for REST/webhook endpoints
38
+ #[arg(long, default_value = "8080")]
39
+ http_port: u16,
40
+
41
+ /// Run in air-gapped mode (no external network)
42
+ #[arg(long)]
43
+ air_gapped: bool,
44
+ },
45
+
46
+ /// Check configuration and system status
47
+ Doctor,
48
+
49
+ /// Show version information
50
+ Version,
51
+ }
52
+
53
+ #[tokio::main]
54
+ async fn main() -> anyhow::Result<()> {
55
+ tracing_subscriber::fmt()
56
+ .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
57
+ .init();
58
+
59
+ let cli = Cli::parse();
60
+
61
+ match cli.command {
62
+ Commands::Run { input, agent, system_prompt } => {
63
+ info!("Running agent '{}' with input: {}", agent, input);
64
+ run::execute(input, agent, system_prompt).await?;
65
+ }
66
+ Commands::Serve { http_port, air_gapped } => {
67
+ info!("Starting server on port {} (air_gapped: {})", http_port, air_gapped);
68
+ serve::execute(http_port, air_gapped).await?;
69
+ }
70
+ Commands::Doctor => {
71
+ commands::doctor::execute().await?;
72
+ }
73
+ Commands::Version => {
74
+ println!("enact {}", env!("CARGO_PKG_VERSION"));
75
+ }
76
+ }
77
+
78
+ Ok(())
79
+ }
@@ -0,0 +1,36 @@
1
+ [package]
2
+ name = "enact-config"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "Unified configuration management for Enact - secure storage with keychain and encrypted files"
7
+ repository.workspace = true
8
+ homepage.workspace = true
9
+ keywords = ["config", "security", "encryption", "keychain"]
10
+ categories.workspace = true
11
+
12
+ [dependencies]
13
+ tokio.workspace = true
14
+ serde.workspace = true
15
+ serde_json.workspace = true
16
+ toml.workspace = true
17
+ aes-gcm.workspace = true
18
+ rand.workspace = true
19
+ dirs.workspace = true
20
+ hex.workspace = true
21
+ reqwest = { workspace = true, features = ["json"] }
22
+ chrono = { workspace = true, features = ["serde"] }
23
+ thiserror.workspace = true
24
+ anyhow.workspace = true
25
+ tracing.workspace = true
26
+ dotenv = "0.15.0"
27
+
28
+ [dev-dependencies]
29
+ tempfile.workspace = true
30
+ tokio = { workspace = true, features = ["test-util"] }
31
+ tracing-subscriber.workspace = true
32
+
33
+ [[example]]
34
+ name = "test-env-vars"
35
+ path = "examples/test-env-vars.rs"
36
+