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,227 @@
1
+ //! Security policy for tool execution
2
+ //!
3
+ //! Provides autonomy levels, rate limiting, and path sandboxing.
4
+
5
+ use std::path::{Path, PathBuf};
6
+ use std::sync::atomic::{AtomicU32, Ordering};
7
+
8
+ /// Autonomy level for agent actions
9
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10
+ pub enum AutonomyLevel {
11
+ /// Read-only access, no modifications allowed
12
+ ReadOnly,
13
+ /// Supervised mode (default) - some actions require approval
14
+ #[default]
15
+ Supervised,
16
+ /// Full autonomy - all actions allowed
17
+ Full,
18
+ }
19
+
20
+ /// Security policy configuration for tools
21
+ pub struct SecurityPolicy {
22
+ /// Autonomy level
23
+ pub autonomy: AutonomyLevel,
24
+ /// Workspace directory (tools are sandboxed to this)
25
+ pub workspace_dir: PathBuf,
26
+ /// Allowed shell commands (empty = allow all safe commands)
27
+ pub allowed_commands: Vec<String>,
28
+ /// Blocked shell commands
29
+ pub blocked_commands: Vec<String>,
30
+ /// Maximum actions per hour (0 = unlimited)
31
+ pub max_actions_per_hour: u32,
32
+ /// Action counter for rate limiting
33
+ action_count: AtomicU32,
34
+ }
35
+
36
+ impl Default for SecurityPolicy {
37
+ fn default() -> Self {
38
+ Self {
39
+ autonomy: AutonomyLevel::Supervised,
40
+ workspace_dir: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
41
+ allowed_commands: Vec::new(),
42
+ blocked_commands: vec![
43
+ "rm -rf /".into(),
44
+ "sudo".into(),
45
+ "chmod 777".into(),
46
+ "dd if=".into(),
47
+ "> /dev/".into(),
48
+ ],
49
+ max_actions_per_hour: 1000,
50
+ action_count: AtomicU32::new(0),
51
+ }
52
+ }
53
+ }
54
+
55
+ impl SecurityPolicy {
56
+ /// Create a new security policy
57
+ pub fn new(workspace_dir: PathBuf) -> Self {
58
+ Self {
59
+ workspace_dir,
60
+ ..Self::default()
61
+ }
62
+ }
63
+
64
+ /// Check if the agent can perform write actions
65
+ pub fn can_act(&self) -> bool {
66
+ !matches!(self.autonomy, AutonomyLevel::ReadOnly)
67
+ }
68
+
69
+ /// Check if rate limited
70
+ pub fn is_rate_limited(&self) -> bool {
71
+ if self.max_actions_per_hour == 0 {
72
+ return false;
73
+ }
74
+ self.action_count.load(Ordering::Relaxed) >= self.max_actions_per_hour
75
+ }
76
+
77
+ /// Record an action and return true if allowed
78
+ pub fn record_action(&self) -> bool {
79
+ if self.max_actions_per_hour == 0 {
80
+ return true;
81
+ }
82
+ let current = self.action_count.fetch_add(1, Ordering::Relaxed);
83
+ current < self.max_actions_per_hour
84
+ }
85
+
86
+ /// Check if a relative path is allowed (no path traversal)
87
+ pub fn is_path_allowed(&self, path: &str) -> bool {
88
+ // Block absolute paths
89
+ if path.starts_with('/') || path.starts_with('\\') {
90
+ return false;
91
+ }
92
+ // Block path traversal
93
+ if path.contains("..") {
94
+ return false;
95
+ }
96
+ // Block null bytes
97
+ if path.contains('\0') {
98
+ return false;
99
+ }
100
+ true
101
+ }
102
+
103
+ /// Check if a resolved (canonicalized) path is within workspace
104
+ pub fn is_resolved_path_allowed(&self, resolved: &Path) -> bool {
105
+ resolved.starts_with(&self.workspace_dir)
106
+ }
107
+
108
+ /// Validate a shell command for execution
109
+ pub fn validate_command_execution(&self, command: &str, approved: bool) -> Result<(), String> {
110
+ if !self.can_act() {
111
+ return Err("Shell command not allowed: autonomy is read-only".into());
112
+ }
113
+
114
+ // Check blocked commands
115
+ for blocked in &self.blocked_commands {
116
+ if command.contains(blocked) {
117
+ return Err(format!("Command blocked by security policy: {blocked}"));
118
+ }
119
+ }
120
+
121
+ // High-risk command patterns
122
+ let high_risk = [
123
+ "rm -rf",
124
+ "mkfs",
125
+ "dd if=",
126
+ "> /dev/",
127
+ "sudo",
128
+ "chmod 777",
129
+ "curl | sh",
130
+ "wget | sh",
131
+ ];
132
+
133
+ for pattern in high_risk {
134
+ if command.contains(pattern) {
135
+ return Err(format!(
136
+ "high-risk command not allowed without explicit override: {pattern}"
137
+ ));
138
+ }
139
+ }
140
+
141
+ // Medium-risk commands need approval in supervised mode
142
+ if self.autonomy == AutonomyLevel::Supervised && !approved {
143
+ let medium_risk = ["rm", "mv", "cp", "touch", "mkdir", "chmod", "chown"];
144
+ for pattern in medium_risk {
145
+ if command.split_whitespace().next() == Some(pattern) {
146
+ return Err(format!(
147
+ "Command '{pattern}' requires explicit approval in supervised mode. Set approved=true."
148
+ ));
149
+ }
150
+ }
151
+ }
152
+
153
+ // If allowed_commands is set, check against it
154
+ if !self.allowed_commands.is_empty() {
155
+ let cmd_name = command.split_whitespace().next().unwrap_or("");
156
+ if !self.allowed_commands.iter().any(|c| c == cmd_name) {
157
+ return Err(format!("Command '{cmd_name}' not in allowed commands list"));
158
+ }
159
+ }
160
+
161
+ Ok(())
162
+ }
163
+ }
164
+
165
+ #[cfg(test)]
166
+ mod tests {
167
+ use super::*;
168
+
169
+ #[test]
170
+ fn default_is_supervised() {
171
+ let policy = SecurityPolicy::default();
172
+ assert_eq!(policy.autonomy, AutonomyLevel::Supervised);
173
+ }
174
+
175
+ #[test]
176
+ fn can_act_respects_autonomy() {
177
+ let mut policy = SecurityPolicy::default();
178
+ assert!(policy.can_act());
179
+
180
+ policy.autonomy = AutonomyLevel::ReadOnly;
181
+ assert!(!policy.can_act());
182
+
183
+ policy.autonomy = AutonomyLevel::Full;
184
+ assert!(policy.can_act());
185
+ }
186
+
187
+ #[test]
188
+ fn path_blocks_traversal() {
189
+ let policy = SecurityPolicy::default();
190
+ assert!(!policy.is_path_allowed("../etc/passwd"));
191
+ assert!(!policy.is_path_allowed("/etc/passwd"));
192
+ assert!(policy.is_path_allowed("src/main.rs"));
193
+ assert!(policy.is_path_allowed("file.txt"));
194
+ }
195
+
196
+ #[test]
197
+ fn blocks_high_risk_commands() {
198
+ let policy = SecurityPolicy::default();
199
+ assert!(policy.validate_command_execution("rm -rf /", false).is_err());
200
+ assert!(policy.validate_command_execution("sudo apt install", false).is_err());
201
+ }
202
+
203
+ #[test]
204
+ fn medium_risk_needs_approval() {
205
+ let policy = SecurityPolicy::default();
206
+ let result = policy.validate_command_execution("rm file.txt", false);
207
+ assert!(result.is_err());
208
+ assert!(result.unwrap_err().contains("approval"));
209
+
210
+ let result = policy.validate_command_execution("rm file.txt", true);
211
+ assert!(result.is_ok());
212
+ }
213
+
214
+ #[test]
215
+ fn rate_limiting_works() {
216
+ let policy = SecurityPolicy {
217
+ max_actions_per_hour: 2,
218
+ ..SecurityPolicy::default()
219
+ };
220
+
221
+ assert!(!policy.is_rate_limited());
222
+ assert!(policy.record_action());
223
+ assert!(policy.record_action());
224
+ assert!(!policy.record_action());
225
+ assert!(policy.is_rate_limited());
226
+ }
227
+ }
@@ -0,0 +1,191 @@
1
+ //! Shell command execution tool with sandboxing
2
+
3
+ use crate::security::SecurityPolicy;
4
+ use crate::traits::{Tool, ToolResult};
5
+ use async_trait::async_trait;
6
+ use serde_json::json;
7
+ use std::sync::Arc;
8
+ use std::time::Duration;
9
+
10
+ /// Maximum shell command execution time before kill.
11
+ const SHELL_TIMEOUT_SECS: u64 = 60;
12
+ /// Maximum output size in bytes (1MB).
13
+ const MAX_OUTPUT_BYTES: usize = 1_048_576;
14
+ /// Environment variables safe to pass to shell commands.
15
+ const SAFE_ENV_VARS: &[&str] = &[
16
+ "PATH", "HOME", "TERM", "LANG", "LC_ALL", "LC_CTYPE", "USER", "SHELL", "TMPDIR",
17
+ ];
18
+
19
+ /// Shell command execution tool with sandboxing
20
+ pub struct ShellTool {
21
+ security: Arc<SecurityPolicy>,
22
+ }
23
+
24
+ impl ShellTool {
25
+ pub fn new(security: Arc<SecurityPolicy>) -> Self {
26
+ Self { security }
27
+ }
28
+ }
29
+
30
+ #[async_trait]
31
+ impl Tool for ShellTool {
32
+ fn name(&self) -> &str {
33
+ "shell"
34
+ }
35
+
36
+ fn description(&self) -> &str {
37
+ "Execute a shell command in the workspace directory"
38
+ }
39
+
40
+ fn parameters_schema(&self) -> serde_json::Value {
41
+ json!({
42
+ "type": "object",
43
+ "properties": {
44
+ "command": {
45
+ "type": "string",
46
+ "description": "The shell command to execute"
47
+ },
48
+ "approved": {
49
+ "type": "boolean",
50
+ "description": "Set true to explicitly approve medium/high-risk commands in supervised mode",
51
+ "default": false
52
+ }
53
+ },
54
+ "required": ["command"]
55
+ })
56
+ }
57
+
58
+ async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
59
+ let command = args
60
+ .get("command")
61
+ .and_then(|v| v.as_str())
62
+ .ok_or_else(|| anyhow::anyhow!("Missing 'command' parameter"))?;
63
+ let approved = args
64
+ .get("approved")
65
+ .and_then(|v| v.as_bool())
66
+ .unwrap_or(false);
67
+
68
+ if self.security.is_rate_limited() {
69
+ return Ok(ToolResult::failure(
70
+ "Rate limit exceeded: too many actions in the last hour",
71
+ ));
72
+ }
73
+
74
+ if let Err(reason) = self.security.validate_command_execution(command, approved) {
75
+ return Ok(ToolResult::failure(reason));
76
+ }
77
+
78
+ if !self.security.record_action() {
79
+ return Ok(ToolResult::failure(
80
+ "Rate limit exceeded: action budget exhausted",
81
+ ));
82
+ }
83
+
84
+ // Build command with cleaned environment
85
+ let mut cmd = tokio::process::Command::new("sh");
86
+ cmd.arg("-c")
87
+ .arg(command)
88
+ .current_dir(&self.security.workspace_dir)
89
+ .env_clear();
90
+
91
+ for var in SAFE_ENV_VARS {
92
+ if let Ok(val) = std::env::var(var) {
93
+ cmd.env(var, val);
94
+ }
95
+ }
96
+
97
+ let result =
98
+ tokio::time::timeout(Duration::from_secs(SHELL_TIMEOUT_SECS), cmd.output()).await;
99
+
100
+ match result {
101
+ Ok(Ok(output)) => {
102
+ let mut stdout = String::from_utf8_lossy(&output.stdout).to_string();
103
+ let mut stderr = String::from_utf8_lossy(&output.stderr).to_string();
104
+
105
+ // Truncate output to prevent OOM
106
+ if stdout.len() > MAX_OUTPUT_BYTES {
107
+ stdout.truncate(stdout.floor_char_boundary(MAX_OUTPUT_BYTES));
108
+ stdout.push_str("\n... [output truncated at 1MB]");
109
+ }
110
+ if stderr.len() > MAX_OUTPUT_BYTES {
111
+ stderr.truncate(stderr.floor_char_boundary(MAX_OUTPUT_BYTES));
112
+ stderr.push_str("\n... [stderr truncated at 1MB]");
113
+ }
114
+
115
+ Ok(ToolResult {
116
+ success: output.status.success(),
117
+ output: stdout,
118
+ error: if stderr.is_empty() {
119
+ None
120
+ } else {
121
+ Some(stderr)
122
+ },
123
+ })
124
+ }
125
+ Ok(Err(e)) => Ok(ToolResult::failure(format!(
126
+ "Failed to execute command: {e}"
127
+ ))),
128
+ Err(_) => Ok(ToolResult::failure(format!(
129
+ "Command timed out after {SHELL_TIMEOUT_SECS}s and was killed"
130
+ ))),
131
+ }
132
+ }
133
+ }
134
+
135
+ #[cfg(test)]
136
+ mod tests {
137
+ use super::*;
138
+ use crate::security::AutonomyLevel;
139
+
140
+ fn test_security(autonomy: AutonomyLevel) -> Arc<SecurityPolicy> {
141
+ Arc::new(SecurityPolicy {
142
+ autonomy,
143
+ workspace_dir: std::env::temp_dir(),
144
+ ..SecurityPolicy::default()
145
+ })
146
+ }
147
+
148
+ #[test]
149
+ fn shell_tool_name() {
150
+ let tool = ShellTool::new(test_security(AutonomyLevel::Supervised));
151
+ assert_eq!(tool.name(), "shell");
152
+ }
153
+
154
+ #[test]
155
+ fn shell_tool_schema_has_command() {
156
+ let tool = ShellTool::new(test_security(AutonomyLevel::Supervised));
157
+ let schema = tool.parameters_schema();
158
+ assert!(schema["properties"]["command"].is_object());
159
+ assert!(schema["required"]
160
+ .as_array()
161
+ .unwrap()
162
+ .contains(&json!("command")));
163
+ }
164
+
165
+ #[tokio::test]
166
+ async fn shell_executes_allowed_command() {
167
+ let tool = ShellTool::new(test_security(AutonomyLevel::Supervised));
168
+ let result = tool
169
+ .execute(json!({"command": "echo hello"}))
170
+ .await
171
+ .unwrap();
172
+ assert!(result.success);
173
+ assert!(result.output.trim().contains("hello"));
174
+ }
175
+
176
+ #[tokio::test]
177
+ async fn shell_blocks_disallowed_command() {
178
+ let tool = ShellTool::new(test_security(AutonomyLevel::Supervised));
179
+ let result = tool.execute(json!({"command": "rm -rf /"})).await.unwrap();
180
+ assert!(!result.success);
181
+ assert!(result.error.is_some());
182
+ }
183
+
184
+ #[tokio::test]
185
+ async fn shell_blocks_readonly() {
186
+ let tool = ShellTool::new(test_security(AutonomyLevel::ReadOnly));
187
+ let result = tool.execute(json!({"command": "ls"})).await.unwrap();
188
+ assert!(!result.success);
189
+ assert!(result.error.as_ref().unwrap().contains("read-only"));
190
+ }
191
+ }
@@ -0,0 +1,159 @@
1
+ //! Core tool traits and types
2
+
3
+ use async_trait::async_trait;
4
+ use serde::{Deserialize, Serialize};
5
+
6
+ /// Result of a tool execution
7
+ #[derive(Debug, Clone, Serialize, Deserialize)]
8
+ pub struct ToolResult {
9
+ pub success: bool,
10
+ pub output: String,
11
+ pub error: Option<String>,
12
+ }
13
+
14
+ impl ToolResult {
15
+ /// Create a successful result
16
+ pub fn success(output: impl Into<String>) -> Self {
17
+ Self {
18
+ success: true,
19
+ output: output.into(),
20
+ error: None,
21
+ }
22
+ }
23
+
24
+ /// Create a failure result
25
+ pub fn failure(error: impl Into<String>) -> Self {
26
+ Self {
27
+ success: false,
28
+ output: String::new(),
29
+ error: Some(error.into()),
30
+ }
31
+ }
32
+ }
33
+
34
+ /// Description of a tool for the LLM
35
+ #[derive(Debug, Clone, Serialize, Deserialize)]
36
+ pub struct ToolSpec {
37
+ pub name: String,
38
+ pub description: String,
39
+ pub parameters: serde_json::Value,
40
+ }
41
+
42
+ /// Core tool trait — implement for any capability
43
+ #[async_trait]
44
+ pub trait Tool: Send + Sync {
45
+ /// Tool name (used in LLM function calling)
46
+ fn name(&self) -> &str;
47
+
48
+ /// Human-readable description
49
+ fn description(&self) -> &str;
50
+
51
+ /// JSON schema for parameters
52
+ fn parameters_schema(&self) -> serde_json::Value;
53
+
54
+ /// Execute the tool with given arguments
55
+ async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult>;
56
+
57
+ /// Get the full spec for LLM registration
58
+ fn spec(&self) -> ToolSpec {
59
+ ToolSpec {
60
+ name: self.name().to_string(),
61
+ description: self.description().to_string(),
62
+ parameters: self.parameters_schema(),
63
+ }
64
+ }
65
+ }
66
+
67
+ #[cfg(test)]
68
+ mod tests {
69
+ use super::*;
70
+
71
+ struct DummyTool;
72
+
73
+ #[async_trait]
74
+ impl Tool for DummyTool {
75
+ fn name(&self) -> &str {
76
+ "dummy_tool"
77
+ }
78
+
79
+ fn description(&self) -> &str {
80
+ "A deterministic test tool"
81
+ }
82
+
83
+ fn parameters_schema(&self) -> serde_json::Value {
84
+ serde_json::json!({
85
+ "type": "object",
86
+ "properties": {
87
+ "value": { "type": "string" }
88
+ }
89
+ })
90
+ }
91
+
92
+ async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
93
+ Ok(ToolResult {
94
+ success: true,
95
+ output: args
96
+ .get("value")
97
+ .and_then(serde_json::Value::as_str)
98
+ .unwrap_or_default()
99
+ .to_string(),
100
+ error: None,
101
+ })
102
+ }
103
+ }
104
+
105
+ #[test]
106
+ fn spec_uses_tool_metadata_and_schema() {
107
+ let tool = DummyTool;
108
+ let spec = tool.spec();
109
+
110
+ assert_eq!(spec.name, "dummy_tool");
111
+ assert_eq!(spec.description, "A deterministic test tool");
112
+ assert_eq!(spec.parameters["type"], "object");
113
+ assert_eq!(spec.parameters["properties"]["value"]["type"], "string");
114
+ }
115
+
116
+ #[tokio::test]
117
+ async fn execute_returns_expected_output() {
118
+ let tool = DummyTool;
119
+ let result = tool
120
+ .execute(serde_json::json!({ "value": "hello-tool" }))
121
+ .await
122
+ .unwrap();
123
+
124
+ assert!(result.success);
125
+ assert_eq!(result.output, "hello-tool");
126
+ assert!(result.error.is_none());
127
+ }
128
+
129
+ #[test]
130
+ fn tool_result_success_helper() {
131
+ let result = ToolResult::success("done");
132
+ assert!(result.success);
133
+ assert_eq!(result.output, "done");
134
+ assert!(result.error.is_none());
135
+ }
136
+
137
+ #[test]
138
+ fn tool_result_failure_helper() {
139
+ let result = ToolResult::failure("boom");
140
+ assert!(!result.success);
141
+ assert_eq!(result.output, "");
142
+ assert_eq!(result.error.as_deref(), Some("boom"));
143
+ }
144
+
145
+ #[test]
146
+ fn tool_result_serialization_roundtrip() {
147
+ let result = ToolResult {
148
+ success: false,
149
+ output: String::new(),
150
+ error: Some("boom".into()),
151
+ };
152
+
153
+ let json = serde_json::to_string(&result).unwrap();
154
+ let parsed: ToolResult = serde_json::from_str(&json).unwrap();
155
+
156
+ assert!(!parsed.success);
157
+ assert_eq!(parsed.error.as_deref(), Some("boom"));
158
+ }
159
+ }
package/docs/Makefile ADDED
@@ -0,0 +1,74 @@
1
+ # Enact Documentation - Makefile
2
+ # Built with Zola (https://www.getzola.org/)
3
+
4
+ .PHONY: build serve clean check install-zola help
5
+
6
+ # Default target
7
+ help:
8
+ @echo "Enact Documentation Commands"
9
+ @echo "============================"
10
+ @echo ""
11
+ @echo " make build Build the documentation site"
12
+ @echo " make serve Start development server with hot reload"
13
+ @echo " make clean Remove build artifacts"
14
+ @echo " make check Check for broken links and errors"
15
+ @echo " make install-zola Install Zola static site generator"
16
+ @echo ""
17
+ @echo "Development server runs at http://127.0.0.1:1111"
18
+
19
+ # Build the documentation site
20
+ build:
21
+ @echo "Building documentation..."
22
+ zola build
23
+ @echo "Build complete! Output in public/"
24
+
25
+ # Start development server with hot reload
26
+ serve:
27
+ @echo "Starting development server at http://127.0.0.1:1111"
28
+ zola serve
29
+
30
+ # Serve on all interfaces (useful for testing on other devices)
31
+ serve-public:
32
+ @echo "Starting public server at http://0.0.0.0:1111"
33
+ zola serve --interface 0.0.0.0
34
+
35
+ # Remove build artifacts
36
+ clean:
37
+ @echo "Cleaning build artifacts..."
38
+ rm -rf public/
39
+ @echo "Clean complete!"
40
+
41
+ # Check for errors without building
42
+ check:
43
+ @echo "Checking documentation..."
44
+ zola check
45
+ @echo "Check complete!"
46
+
47
+ # Install Zola (macOS)
48
+ install-zola:
49
+ @echo "Installing Zola..."
50
+ @if command -v brew >/dev/null 2>&1; then \
51
+ brew install zola; \
52
+ elif command -v cargo >/dev/null 2>&1; then \
53
+ cargo install zola; \
54
+ else \
55
+ echo "Please install Homebrew or Cargo first"; \
56
+ exit 1; \
57
+ fi
58
+ @echo "Zola installed!"
59
+
60
+ # Build for production (minified)
61
+ build-prod:
62
+ @echo "Building for production..."
63
+ zola build --force
64
+ @echo "Production build complete!"
65
+
66
+ # Watch for changes and rebuild (alternative to serve)
67
+ watch:
68
+ @echo "Watching for changes..."
69
+ zola build --drafts
70
+ @while true; do \
71
+ fswatch -1 content/ templates/ sass/ static/ config.toml && \
72
+ echo "Rebuilding..." && \
73
+ zola build --drafts; \
74
+ done