minionsai 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 (361) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/bin/minions.mjs +19 -0
  4. package/dist/server/client/dist/assets/abap-BdImnpbu.js +1 -0
  5. package/dist/server/client/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
  6. package/dist/server/client/dist/assets/ada-bCR0ucgS.js +1 -0
  7. package/dist/server/client/dist/assets/andromeeda-C4gqWexZ.js +1 -0
  8. package/dist/server/client/dist/assets/angular-html-CU67Zn6k.js +1 -0
  9. package/dist/server/client/dist/assets/angular-ts-BwZT4LLn.js +1 -0
  10. package/dist/server/client/dist/assets/apache-Pmp26Uib.js +1 -0
  11. package/dist/server/client/dist/assets/apex-D8_7TLub.js +1 -0
  12. package/dist/server/client/dist/assets/apl-dKokRX4l.js +1 -0
  13. package/dist/server/client/dist/assets/applescript-Co6uUVPk.js +1 -0
  14. package/dist/server/client/dist/assets/ara-BRHolxvo.js +1 -0
  15. package/dist/server/client/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
  16. package/dist/server/client/dist/assets/asm-D_Q5rh1f.js +1 -0
  17. package/dist/server/client/dist/assets/astro-CbQHKStN.js +1 -0
  18. package/dist/server/client/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  19. package/dist/server/client/dist/assets/awk-DMzUqQB5.js +1 -0
  20. package/dist/server/client/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
  21. package/dist/server/client/dist/assets/ayu-light-BA47KaF1.js +1 -0
  22. package/dist/server/client/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
  23. package/dist/server/client/dist/assets/ballerina-BFfxhgS-.js +1 -0
  24. package/dist/server/client/dist/assets/bat-BkioyH1T.js +1 -0
  25. package/dist/server/client/dist/assets/beancount-k_qm7-4y.js +1 -0
  26. package/dist/server/client/dist/assets/berry-uYugtg8r.js +1 -0
  27. package/dist/server/client/dist/assets/bibtex-CHM0blh-.js +1 -0
  28. package/dist/server/client/dist/assets/bicep-Bmn6On1c.js +1 -0
  29. package/dist/server/client/dist/assets/bird2-DPOp833l.js +1 -0
  30. package/dist/server/client/dist/assets/blade-D4QpJJKB.js +1 -0
  31. package/dist/server/client/dist/assets/bsl-BO_Y6i37.js +1 -0
  32. package/dist/server/client/dist/assets/c-BIGW1oBm.js +1 -0
  33. package/dist/server/client/dist/assets/c3-eo99z4R2.js +1 -0
  34. package/dist/server/client/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  35. package/dist/server/client/dist/assets/cairo-KRGpt6FW.js +1 -0
  36. package/dist/server/client/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  37. package/dist/server/client/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  38. package/dist/server/client/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  39. package/dist/server/client/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  40. package/dist/server/client/dist/assets/clarity-D53aC0YG.js +1 -0
  41. package/dist/server/client/dist/assets/clojure-P80f7IUj.js +1 -0
  42. package/dist/server/client/dist/assets/cmake-D1j8_8rp.js +1 -0
  43. package/dist/server/client/dist/assets/cobol-nwyudZeR.js +1 -0
  44. package/dist/server/client/dist/assets/codeowners-Bp6g37R7.js +1 -0
  45. package/dist/server/client/dist/assets/codeql-DsOJ9woJ.js +1 -0
  46. package/dist/server/client/dist/assets/coffee-Ch7k5sss.js +1 -0
  47. package/dist/server/client/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  48. package/dist/server/client/dist/assets/coq-DkFqJrB1.js +1 -0
  49. package/dist/server/client/dist/assets/cpp-CofmeUqb.js +1 -0
  50. package/dist/server/client/dist/assets/crystal-tKQVLTB8.js +1 -0
  51. package/dist/server/client/dist/assets/csharp-COcwbKMJ.js +1 -0
  52. package/dist/server/client/dist/assets/css-DPfMkruS.js +1 -0
  53. package/dist/server/client/dist/assets/csv-fuZLfV_i.js +1 -0
  54. package/dist/server/client/dist/assets/cue-D82EKSYY.js +1 -0
  55. package/dist/server/client/dist/assets/cypher-COkxafJQ.js +1 -0
  56. package/dist/server/client/dist/assets/d-85-TOEBH.js +1 -0
  57. package/dist/server/client/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  58. package/dist/server/client/dist/assets/dart-CF10PKvl.js +1 -0
  59. package/dist/server/client/dist/assets/dax-CEL-wOlO.js +1 -0
  60. package/dist/server/client/dist/assets/desktop-BmXAJ9_W.js +1 -0
  61. package/dist/server/client/dist/assets/diff-D97Zzqfu.js +1 -0
  62. package/dist/server/client/dist/assets/docker-BcOcwvcX.js +1 -0
  63. package/dist/server/client/dist/assets/dotenv-Da5cRb03.js +1 -0
  64. package/dist/server/client/dist/assets/dracula-BzJJZx-M.js +1 -0
  65. package/dist/server/client/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  66. package/dist/server/client/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  67. package/dist/server/client/dist/assets/edge-BkV0erSs.js +1 -0
  68. package/dist/server/client/dist/assets/elixir-CDX3lj18.js +1 -0
  69. package/dist/server/client/dist/assets/elm-DbKCFpqz.js +1 -0
  70. package/dist/server/client/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
  71. package/dist/server/client/dist/assets/erb-B12qg9BL.js +1 -0
  72. package/dist/server/client/dist/assets/erlang-DsQrWhSR.js +1 -0
  73. package/dist/server/client/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  74. package/dist/server/client/dist/assets/everforest-light-C8M2exoo.js +1 -0
  75. package/dist/server/client/dist/assets/fennel-BYunw83y.js +1 -0
  76. package/dist/server/client/dist/assets/fish-BvzEVeQv.js +1 -0
  77. package/dist/server/client/dist/assets/fluent-C4IJs8-o.js +1 -0
  78. package/dist/server/client/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  79. package/dist/server/client/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
  80. package/dist/server/client/dist/assets/fsharp-CXgrBDvD.js +1 -0
  81. package/dist/server/client/dist/assets/gdresource-BOOCDP_w.js +1 -0
  82. package/dist/server/client/dist/assets/gdscript-C5YyOfLZ.js +1 -0
  83. package/dist/server/client/dist/assets/gdshader-DkwncUOv.js +1 -0
  84. package/dist/server/client/dist/assets/genie-D0YGMca9.js +1 -0
  85. package/dist/server/client/dist/assets/gherkin-DyxjwDmM.js +1 -0
  86. package/dist/server/client/dist/assets/git-commit-F4YmCXRG.js +1 -0
  87. package/dist/server/client/dist/assets/git-rebase-r7XF79zn.js +1 -0
  88. package/dist/server/client/dist/assets/github-dark-DHJKELXO.js +1 -0
  89. package/dist/server/client/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  90. package/dist/server/client/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  91. package/dist/server/client/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  92. package/dist/server/client/dist/assets/github-light-DAi9KRSo.js +1 -0
  93. package/dist/server/client/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  94. package/dist/server/client/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  95. package/dist/server/client/dist/assets/gleam-BspZqrRM.js +1 -0
  96. package/dist/server/client/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
  97. package/dist/server/client/dist/assets/glimmer-ts-U6CK756n.js +1 -0
  98. package/dist/server/client/dist/assets/glsl-DplSGwfg.js +1 -0
  99. package/dist/server/client/dist/assets/gn-n2N0HUVH.js +1 -0
  100. package/dist/server/client/dist/assets/gnuplot-DdkO51Og.js +1 -0
  101. package/dist/server/client/dist/assets/go-CxLEBnE3.js +1 -0
  102. package/dist/server/client/dist/assets/graphql-ChdNCCLP.js +1 -0
  103. package/dist/server/client/dist/assets/groovy-gcz8RCvz.js +1 -0
  104. package/dist/server/client/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  105. package/dist/server/client/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  106. package/dist/server/client/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  107. package/dist/server/client/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  108. package/dist/server/client/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  109. package/dist/server/client/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  110. package/dist/server/client/dist/assets/hack-CaT9iCJl.js +1 -0
  111. package/dist/server/client/dist/assets/haml-B8DHNrY2.js +1 -0
  112. package/dist/server/client/dist/assets/handlebars-BL8al0AC.js +1 -0
  113. package/dist/server/client/dist/assets/haskell-Df6bDoY_.js +1 -0
  114. package/dist/server/client/dist/assets/haxe-CzTSHFRz.js +1 -0
  115. package/dist/server/client/dist/assets/hcl-BWvSN4gD.js +1 -0
  116. package/dist/server/client/dist/assets/highlighted-body-TPN3WLV5-Dmyr2DoJ.js +1 -0
  117. package/dist/server/client/dist/assets/hjson-D5-asLiD.js +1 -0
  118. package/dist/server/client/dist/assets/hlsl-D3lLCCz7.js +1 -0
  119. package/dist/server/client/dist/assets/horizon-BUw7H-hv.js +1 -0
  120. package/dist/server/client/dist/assets/horizon-bright-Cn-bp-IR.js +1 -0
  121. package/dist/server/client/dist/assets/houston-DnULxvSX.js +1 -0
  122. package/dist/server/client/dist/assets/html-GMplVEZG.js +1 -0
  123. package/dist/server/client/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
  124. package/dist/server/client/dist/assets/http-jrhK8wxY.js +1 -0
  125. package/dist/server/client/dist/assets/hurl-irOxFIW8.js +1 -0
  126. package/dist/server/client/dist/assets/hxml-Bvhsp5Yf.js +1 -0
  127. package/dist/server/client/dist/assets/hy-DFXneXwc.js +1 -0
  128. package/dist/server/client/dist/assets/imba-DGztddWO.js +1 -0
  129. package/dist/server/client/dist/assets/index-BB7507W7.css +1 -0
  130. package/dist/server/client/dist/assets/index-hLQDnL9J.js +694 -0
  131. package/dist/server/client/dist/assets/ini-BEwlwnbL.js +1 -0
  132. package/dist/server/client/dist/assets/java-CylS5w8V.js +1 -0
  133. package/dist/server/client/dist/assets/javascript-wDzz0qaB.js +1 -0
  134. package/dist/server/client/dist/assets/jinja-4LBKfQ-Z.js +1 -0
  135. package/dist/server/client/dist/assets/jison-wvAkD_A8.js +1 -0
  136. package/dist/server/client/dist/assets/json-Cp-IABpG.js +1 -0
  137. package/dist/server/client/dist/assets/json5-C9tS-k6U.js +1 -0
  138. package/dist/server/client/dist/assets/jsonc-Des-eS-w.js +1 -0
  139. package/dist/server/client/dist/assets/jsonl-DcaNXYhu.js +1 -0
  140. package/dist/server/client/dist/assets/jsonnet-DFQXde-d.js +1 -0
  141. package/dist/server/client/dist/assets/jssm-C2t-YnRu.js +1 -0
  142. package/dist/server/client/dist/assets/jsx-g9-lgVsj.js +1 -0
  143. package/dist/server/client/dist/assets/julia-CxzCAyBv.js +1 -0
  144. package/dist/server/client/dist/assets/just-Cw27pwNe.js +1 -0
  145. package/dist/server/client/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  146. package/dist/server/client/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  147. package/dist/server/client/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
  148. package/dist/server/client/dist/assets/kdl-DV7GczEv.js +1 -0
  149. package/dist/server/client/dist/assets/kotlin-BdnUsdx6.js +1 -0
  150. package/dist/server/client/dist/assets/kusto-DZf3V79B.js +1 -0
  151. package/dist/server/client/dist/assets/laserwave-DUszq2jm.js +1 -0
  152. package/dist/server/client/dist/assets/latex-CWtU0Tv5.js +1 -0
  153. package/dist/server/client/dist/assets/lean-BZvkOJ9d.js +1 -0
  154. package/dist/server/client/dist/assets/less-B1dDrJ26.js +1 -0
  155. package/dist/server/client/dist/assets/light-plus-B7mTdjB0.js +1 -0
  156. package/dist/server/client/dist/assets/liquid-DYVedYrR.js +1 -0
  157. package/dist/server/client/dist/assets/llvm-DjAJT7YJ.js +1 -0
  158. package/dist/server/client/dist/assets/log-2UxHyX5q.js +1 -0
  159. package/dist/server/client/dist/assets/logo-BtOb2qkB.js +1 -0
  160. package/dist/server/client/dist/assets/lua-BaeVxFsk.js +1 -0
  161. package/dist/server/client/dist/assets/luau-C-HG3fhB.js +1 -0
  162. package/dist/server/client/dist/assets/make-CHLpvVh8.js +1 -0
  163. package/dist/server/client/dist/assets/markdown-Cvjx9yec.js +1 -0
  164. package/dist/server/client/dist/assets/marko-CnJfTvn9.js +1 -0
  165. package/dist/server/client/dist/assets/material-theme-D5KoaKCx.js +1 -0
  166. package/dist/server/client/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  167. package/dist/server/client/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  168. package/dist/server/client/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  169. package/dist/server/client/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  170. package/dist/server/client/dist/assets/matlab-D7o27uSR.js +1 -0
  171. package/dist/server/client/dist/assets/mdc-BMNejdWA.js +1 -0
  172. package/dist/server/client/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  173. package/dist/server/client/dist/assets/mermaid-mWjccvbQ.js +1 -0
  174. package/dist/server/client/dist/assets/min-dark-CafNBF8u.js +1 -0
  175. package/dist/server/client/dist/assets/min-light-CTRr51gU.js +1 -0
  176. package/dist/server/client/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  177. package/dist/server/client/dist/assets/mojo-rZm6bMo-.js +1 -0
  178. package/dist/server/client/dist/assets/monokai-D4h5O-jR.js +1 -0
  179. package/dist/server/client/dist/assets/moonbit-_H4v1dQx.js +1 -0
  180. package/dist/server/client/dist/assets/move-IF9eRakj.js +1 -0
  181. package/dist/server/client/dist/assets/narrat-DRg8JJMk.js +1 -0
  182. package/dist/server/client/dist/assets/nextflow-Zz6hmt5N.js +1 -0
  183. package/dist/server/client/dist/assets/nextflow-groovy-BeH2EWoN.js +1 -0
  184. package/dist/server/client/dist/assets/nginx-BpAMiNFr.js +1 -0
  185. package/dist/server/client/dist/assets/night-owl-C39BiMTA.js +1 -0
  186. package/dist/server/client/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
  187. package/dist/server/client/dist/assets/nim-CVrawwO9.js +1 -0
  188. package/dist/server/client/dist/assets/nix-CwoSXNpI.js +1 -0
  189. package/dist/server/client/dist/assets/nord-Ddv68eIx.js +1 -0
  190. package/dist/server/client/dist/assets/nushell-Cz2AlsmD.js +1 -0
  191. package/dist/server/client/dist/assets/objective-c-DXmwc3jG.js +1 -0
  192. package/dist/server/client/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  193. package/dist/server/client/dist/assets/ocaml-C0hk2d4L.js +1 -0
  194. package/dist/server/client/dist/assets/odin-BBf5iR-q.js +1 -0
  195. package/dist/server/client/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  196. package/dist/server/client/dist/assets/one-light-C3Wv6jpd.js +1 -0
  197. package/dist/server/client/dist/assets/openscad-C4EeE6gA.js +1 -0
  198. package/dist/server/client/dist/assets/pascal-D93ZcfNL.js +1 -0
  199. package/dist/server/client/dist/assets/perl-C0TMdlhV.js +1 -0
  200. package/dist/server/client/dist/assets/php-Dhbhpdrm.js +1 -0
  201. package/dist/server/client/dist/assets/pkl-u5AG7uiY.js +1 -0
  202. package/dist/server/client/dist/assets/plastic-3e1v2bzS.js +1 -0
  203. package/dist/server/client/dist/assets/plsql-ChMvpjG-.js +1 -0
  204. package/dist/server/client/dist/assets/po-BTJTHyun.js +1 -0
  205. package/dist/server/client/dist/assets/poimandres-CS3Unz2-.js +1 -0
  206. package/dist/server/client/dist/assets/polar-C0HS_06l.js +1 -0
  207. package/dist/server/client/dist/assets/postcss-CXtECtnM.js +1 -0
  208. package/dist/server/client/dist/assets/powerquery-CEu0bR-o.js +1 -0
  209. package/dist/server/client/dist/assets/powershell-Dpen1YoG.js +1 -0
  210. package/dist/server/client/dist/assets/prisma-Dd19v3D-.js +1 -0
  211. package/dist/server/client/dist/assets/prolog-CbFg5uaA.js +1 -0
  212. package/dist/server/client/dist/assets/proto-C7zT0LnQ.js +1 -0
  213. package/dist/server/client/dist/assets/pug-CGlum2m_.js +1 -0
  214. package/dist/server/client/dist/assets/puppet-BMWR74SV.js +1 -0
  215. package/dist/server/client/dist/assets/purescript-CklMAg4u.js +1 -0
  216. package/dist/server/client/dist/assets/python-B6aJPvgy.js +1 -0
  217. package/dist/server/client/dist/assets/qml-3beO22l8.js +1 -0
  218. package/dist/server/client/dist/assets/qmldir-C8lEn-DE.js +1 -0
  219. package/dist/server/client/dist/assets/qss-IeuSbFQv.js +1 -0
  220. package/dist/server/client/dist/assets/r-Dspwwk_N.js +1 -0
  221. package/dist/server/client/dist/assets/racket-BqYA7rlc.js +1 -0
  222. package/dist/server/client/dist/assets/raku-DXvB9xmW.js +1 -0
  223. package/dist/server/client/dist/assets/razor-Uh8Bk_45.js +1 -0
  224. package/dist/server/client/dist/assets/red-bN70gL4F.js +1 -0
  225. package/dist/server/client/dist/assets/reg-C-SQnVFl.js +1 -0
  226. package/dist/server/client/dist/assets/regexp-CDVJQ6XC.js +1 -0
  227. package/dist/server/client/dist/assets/rel-C3B-1QV4.js +1 -0
  228. package/dist/server/client/dist/assets/riscv-BM1_JUlF.js +1 -0
  229. package/dist/server/client/dist/assets/ron-D8l8udqQ.js +1 -0
  230. package/dist/server/client/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  231. package/dist/server/client/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  232. package/dist/server/client/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
  233. package/dist/server/client/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  234. package/dist/server/client/dist/assets/rst-BrH8l1NY.js +1 -0
  235. package/dist/server/client/dist/assets/ruby-Dw2BHqvy.js +1 -0
  236. package/dist/server/client/dist/assets/rust-B1yitclQ.js +1 -0
  237. package/dist/server/client/dist/assets/sas-cz2c8ADy.js +1 -0
  238. package/dist/server/client/dist/assets/sass-Cj5Yp3dK.js +1 -0
  239. package/dist/server/client/dist/assets/scala-C151Ov-r.js +1 -0
  240. package/dist/server/client/dist/assets/scheme-C98Dy4si.js +1 -0
  241. package/dist/server/client/dist/assets/scss-OYdSNvt2.js +1 -0
  242. package/dist/server/client/dist/assets/sdbl-DVxCFoDh.js +1 -0
  243. package/dist/server/client/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  244. package/dist/server/client/dist/assets/shellscript-Yzrsuije.js +1 -0
  245. package/dist/server/client/dist/assets/shellsession-BADoaaVG.js +1 -0
  246. package/dist/server/client/dist/assets/slack-dark-BthQWCQV.js +1 -0
  247. package/dist/server/client/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  248. package/dist/server/client/dist/assets/smalltalk-BERRCDM3.js +1 -0
  249. package/dist/server/client/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  250. package/dist/server/client/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
  251. package/dist/server/client/dist/assets/solarized-light-L9t79GZl.js +1 -0
  252. package/dist/server/client/dist/assets/solidity-rGO070M0.js +1 -0
  253. package/dist/server/client/dist/assets/soy-Brmx7dQM.js +1 -0
  254. package/dist/server/client/dist/assets/sparql-rVzFXLq3.js +1 -0
  255. package/dist/server/client/dist/assets/splunk-BtCnVYZw.js +1 -0
  256. package/dist/server/client/dist/assets/sql-BLtJtn59.js +1 -0
  257. package/dist/server/client/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  258. package/dist/server/client/dist/assets/stata-BH5u7GGu.js +1 -0
  259. package/dist/server/client/dist/assets/stylus-BEDo0Tqx.js +1 -0
  260. package/dist/server/client/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
  261. package/dist/server/client/dist/assets/svelte-C_ipcX3V.js +1 -0
  262. package/dist/server/client/dist/assets/swift-D82vCrfD.js +1 -0
  263. package/dist/server/client/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  264. package/dist/server/client/dist/assets/system-verilog-CnnmHF94.js +1 -0
  265. package/dist/server/client/dist/assets/systemd-4A_iFExJ.js +1 -0
  266. package/dist/server/client/dist/assets/talonscript-CkByrt1z.js +1 -0
  267. package/dist/server/client/dist/assets/tasl-QIJgUcNo.js +1 -0
  268. package/dist/server/client/dist/assets/tcl-dwOrl1Do.js +1 -0
  269. package/dist/server/client/dist/assets/templ-P3uqSqPl.js +1 -0
  270. package/dist/server/client/dist/assets/terraform-BETggiCN.js +1 -0
  271. package/dist/server/client/dist/assets/tex-idrVyKtj.js +1 -0
  272. package/dist/server/client/dist/assets/tokyo-night-hegEt444.js +1 -0
  273. package/dist/server/client/dist/assets/toml-vGWfd6FD.js +1 -0
  274. package/dist/server/client/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
  275. package/dist/server/client/dist/assets/tsv-B_m7g4N7.js +1 -0
  276. package/dist/server/client/dist/assets/tsx-COt5Ahok.js +1 -0
  277. package/dist/server/client/dist/assets/turtle-BsS91CYL.js +1 -0
  278. package/dist/server/client/dist/assets/twig-DNn4PbVi.js +1 -0
  279. package/dist/server/client/dist/assets/typescript-BPQ3VLAy.js +1 -0
  280. package/dist/server/client/dist/assets/typespec-BGHnOYBU.js +1 -0
  281. package/dist/server/client/dist/assets/typst-DHCkPAjA.js +1 -0
  282. package/dist/server/client/dist/assets/v-BcVCzyr7.js +1 -0
  283. package/dist/server/client/dist/assets/vala-CsfeWuGM.js +1 -0
  284. package/dist/server/client/dist/assets/vb-D17OF-Vu.js +1 -0
  285. package/dist/server/client/dist/assets/verilog-BQ8w6xss.js +1 -0
  286. package/dist/server/client/dist/assets/vesper-DU1UobuO.js +1 -0
  287. package/dist/server/client/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  288. package/dist/server/client/dist/assets/viml-CJc9bBzg.js +1 -0
  289. package/dist/server/client/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  290. package/dist/server/client/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  291. package/dist/server/client/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  292. package/dist/server/client/dist/assets/vue-DN_0RTcg.js +1 -0
  293. package/dist/server/client/dist/assets/vue-html-AaS7Mt5G.js +1 -0
  294. package/dist/server/client/dist/assets/vue-vine-CQOfvN7w.js +1 -0
  295. package/dist/server/client/dist/assets/vyper-CDx5xZoG.js +1 -0
  296. package/dist/server/client/dist/assets/wasm-CG6Dc4jp.js +1 -0
  297. package/dist/server/client/dist/assets/wasm-MzD3tlZU.js +1 -0
  298. package/dist/server/client/dist/assets/wenyan-BV7otONQ.js +1 -0
  299. package/dist/server/client/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  300. package/dist/server/client/dist/assets/wikitext-BhOHFoWU.js +1 -0
  301. package/dist/server/client/dist/assets/wit-5i3qLPDT.js +1 -0
  302. package/dist/server/client/dist/assets/wolfram-lXgVvXCa.js +1 -0
  303. package/dist/server/client/dist/assets/xml-sdJ4AIDG.js +1 -0
  304. package/dist/server/client/dist/assets/xsl-CtQFsRM5.js +1 -0
  305. package/dist/server/client/dist/assets/yaml-Buea-lGh.js +1 -0
  306. package/dist/server/client/dist/assets/zenscript-DVFEvuxE.js +1 -0
  307. package/dist/server/client/dist/assets/zig-VOosw3JB.js +1 -0
  308. package/dist/server/client/dist/favicon.ico +0 -0
  309. package/dist/server/client/dist/index.html +17 -0
  310. package/dist/server/client/dist/logo.png +0 -0
  311. package/dist/server/server/adapters/hermes-worker.d.ts +37 -0
  312. package/dist/server/server/adapters/hermes-worker.js +525 -0
  313. package/dist/server/server/adapters/types.d.ts +39 -0
  314. package/dist/server/server/adapters/types.js +1 -0
  315. package/dist/server/server/adapters/worker-protocol.d.ts +141 -0
  316. package/dist/server/server/adapters/worker-protocol.js +1 -0
  317. package/dist/server/server/agent-settings.d.ts +12 -0
  318. package/dist/server/server/agent-settings.js +56 -0
  319. package/dist/server/server/app.d.ts +5 -0
  320. package/dist/server/server/app.js +41 -0
  321. package/dist/server/server/db/index.d.ts +2 -0
  322. package/dist/server/server/db/index.js +14 -0
  323. package/dist/server/server/db/queries.d.ts +22 -0
  324. package/dist/server/server/db/queries.js +116 -0
  325. package/dist/server/server/db/schema.sql +16 -0
  326. package/dist/server/server/errors.d.ts +3 -0
  327. package/dist/server/server/errors.js +12 -0
  328. package/dist/server/server/events.d.ts +7 -0
  329. package/dist/server/server/events.js +50 -0
  330. package/dist/server/server/frontend.d.ts +4 -0
  331. package/dist/server/server/frontend.js +32 -0
  332. package/dist/server/server/index.d.ts +2 -0
  333. package/dist/server/server/index.js +63 -0
  334. package/dist/server/server/live-chat.d.ts +17 -0
  335. package/dist/server/server/live-chat.js +217 -0
  336. package/dist/server/server/paths.d.ts +7 -0
  337. package/dist/server/server/paths.js +39 -0
  338. package/dist/server/server/prompts/task-agent.d.ts +1 -0
  339. package/dist/server/server/prompts/task-agent.js +26 -0
  340. package/dist/server/server/routes/agent.d.ts +4 -0
  341. package/dist/server/server/routes/agent.js +94 -0
  342. package/dist/server/server/routes/chat.d.ts +1 -0
  343. package/dist/server/server/routes/chat.js +177 -0
  344. package/dist/server/server/routes/cron.d.ts +4 -0
  345. package/dist/server/server/routes/cron.js +109 -0
  346. package/dist/server/server/routes/files.d.ts +1 -0
  347. package/dist/server/server/routes/files.js +543 -0
  348. package/dist/server/server/routes/skills.d.ts +1 -0
  349. package/dist/server/server/routes/skills.js +13 -0
  350. package/dist/server/server/routes/tasks.d.ts +1 -0
  351. package/dist/server/server/routes/tasks.js +107 -0
  352. package/dist/server/server/skills/catalog.d.ts +21 -0
  353. package/dist/server/server/skills/catalog.js +160 -0
  354. package/dist/server/server/workers/hermes_cron.py +241 -0
  355. package/dist/server/server/workers/hermes_sessions.py +264 -0
  356. package/dist/server/server/workers/hermes_worker.py +1270 -0
  357. package/dist/server/server/workers/hermes_worker_utils.py +39 -0
  358. package/dist/server/shared/types.d.ts +211 -0
  359. package/dist/server/shared/types.js +2 -0
  360. package/package.json +74 -0
  361. package/skills/lead-generation/SKILL.md +41 -0
@@ -0,0 +1,107 @@
1
+ import { Router } from 'express';
2
+ import { getAllTasks, getTask, insertTask, updateTask, deleteTask, markTaskViewed } from '../db/queries.js';
3
+ import { broadcast } from '../events.js';
4
+ import { adapter } from '../app.js';
5
+ import { TASK_STATUSES } from '../../shared/types.js';
6
+ export const tasksRouter = Router();
7
+ const LOW_INFORMATION_TITLES = new Set(['?', 'hi', 'hello', 'hey', 'yo']);
8
+ tasksRouter.get('/', (req, res) => {
9
+ const status = req.query.status;
10
+ const tasks = getAllTasks(status);
11
+ res.json({ tasks });
12
+ });
13
+ tasksRouter.get('/:id', (req, res) => {
14
+ const task = getTask(req.params.id);
15
+ if (!task)
16
+ return res.status(404).json({ error: 'Task not found' });
17
+ res.json({ task });
18
+ });
19
+ function generateTitle(text) {
20
+ const firstLine = text.split(/\n/)[0].trim();
21
+ const normalizedFirstLine = firstLine.toLowerCase().replace(/\s+/g, ' ').replace(/[.!?]+$/g, '').trim();
22
+ if (!normalizedFirstLine || LOW_INFORMATION_TITLES.has(normalizedFirstLine))
23
+ return 'Untitled task';
24
+ const firstSentence = firstLine.split(/[.!?]/)[0].trim();
25
+ if (!firstSentence)
26
+ return text.slice(0, 60).trim() || 'Untitled task';
27
+ if (firstSentence.length <= 60)
28
+ return firstSentence;
29
+ return firstSentence.slice(0, 57) + '...';
30
+ }
31
+ async function enrichTaskTitle(taskId, fallbackTitle, description) {
32
+ try {
33
+ const { title } = await adapter.generateTitle(description);
34
+ const cleaned = title.trim();
35
+ if (!cleaned || cleaned === fallbackTitle)
36
+ return;
37
+ const current = getTask(taskId);
38
+ if (!current || current.title !== fallbackTitle)
39
+ return;
40
+ const updated = updateTask(taskId, { title: cleaned });
41
+ if (updated)
42
+ broadcast({ type: 'task_updated', task: updated });
43
+ }
44
+ catch {
45
+ // Best-effort: leave the fallback title in place if the LLM call fails.
46
+ }
47
+ }
48
+ tasksRouter.post('/', (req, res) => {
49
+ const { description, title } = req.body;
50
+ if (!description || typeof description !== 'string') {
51
+ return res.status(400).json({ error: 'description is required' });
52
+ }
53
+ const userTitle = typeof title === 'string' ? title.trim() : '';
54
+ const resolvedTitle = userTitle || generateTitle(description);
55
+ const task = insertTask({
56
+ title: resolvedTitle,
57
+ description,
58
+ status: 'in_progress',
59
+ });
60
+ broadcast({ type: 'task_created', task });
61
+ res.status(201).json({ task });
62
+ if (!userTitle) {
63
+ void enrichTaskTitle(task.id, resolvedTitle, description);
64
+ }
65
+ });
66
+ tasksRouter.patch('/:id', (req, res) => {
67
+ const allowed = ['title', 'description', 'status'];
68
+ const fields = {};
69
+ for (const key of allowed) {
70
+ if (req.body[key] !== undefined)
71
+ fields[key] = req.body[key];
72
+ }
73
+ if (fields.status && !TASK_STATUSES.includes(fields.status)) {
74
+ return res.status(400).json({ error: `status must be one of: ${TASK_STATUSES.join(', ')}` });
75
+ }
76
+ const updated = updateTask(req.params.id, fields);
77
+ if (!updated)
78
+ return res.status(404).json({ error: 'Task not found' });
79
+ broadcast({ type: 'task_updated', task: updated });
80
+ res.json({ task: updated });
81
+ });
82
+ tasksRouter.post('/:id/viewed', (req, res) => {
83
+ const { task, changed } = markTaskViewed(req.params.id);
84
+ if (!task)
85
+ return res.status(404).json({ error: 'Task not found' });
86
+ if (changed)
87
+ broadcast({ type: 'task_updated', task });
88
+ res.json({ task });
89
+ });
90
+ tasksRouter.delete('/:id', (req, res) => {
91
+ const deleted = deleteTask(req.params.id);
92
+ if (!deleted)
93
+ return res.status(404).json({ error: 'Task not found' });
94
+ broadcast({ type: 'task_deleted', taskId: req.params.id });
95
+ res.json({ ok: true });
96
+ });
97
+ tasksRouter.post('/:id/move', (req, res) => {
98
+ const { status } = req.body;
99
+ if (!TASK_STATUSES.includes(status)) {
100
+ return res.status(400).json({ error: `status must be one of: ${TASK_STATUSES.join(', ')}` });
101
+ }
102
+ const updated = updateTask(req.params.id, { status });
103
+ if (!updated)
104
+ return res.status(404).json({ error: 'Task not found' });
105
+ broadcast({ type: 'task_updated', task: updated });
106
+ res.json({ task: updated });
107
+ });
@@ -0,0 +1,21 @@
1
+ export interface BundledSkillMeta {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ key: string;
6
+ source: 'Minions bundled';
7
+ bundled: true;
8
+ readOnly: true;
9
+ autoIncluded: true;
10
+ }
11
+ export interface BundledSkill extends BundledSkillMeta {
12
+ filePath: string;
13
+ }
14
+ export declare function resolveBundledSkillsDir(): string;
15
+ export declare function listBundledSkills(): Promise<BundledSkill[]>;
16
+ export declare function getBundledSkillWithContent(id: string): Promise<{
17
+ skill: BundledSkill;
18
+ content: string;
19
+ } | null>;
20
+ export declare function toSkillMeta(skill: BundledSkill): BundledSkillMeta;
21
+ export declare function ensureBundledSkillsLinked(): void;
@@ -0,0 +1,160 @@
1
+ import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync } from 'node:fs';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { expandHomePrefix } from '../paths.js';
7
+ const VALID_SKILL_ID = /^[a-z0-9][a-z0-9._-]*$/;
8
+ function serverDir() {
9
+ return dirname(fileURLToPath(import.meta.url));
10
+ }
11
+ export function resolveBundledSkillsDir() {
12
+ const here = serverDir();
13
+ const candidates = [
14
+ resolve(process.cwd(), 'skills'),
15
+ resolve(here, '../../skills'),
16
+ resolve(here, '../../../skills'),
17
+ resolve(here, '../../../../skills'),
18
+ ];
19
+ return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
20
+ }
21
+ function stripQuotes(value) {
22
+ const trimmed = value.trim();
23
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"'))
24
+ || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
25
+ return trimmed.slice(1, -1);
26
+ }
27
+ return trimmed;
28
+ }
29
+ function parseFrontmatter(content) {
30
+ if (!content.startsWith('---\n'))
31
+ return {};
32
+ const end = content.indexOf('\n---', 4);
33
+ if (end === -1)
34
+ return {};
35
+ const fields = {};
36
+ const raw = content.slice(4, end);
37
+ for (const line of raw.split('\n')) {
38
+ const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
39
+ if (!match)
40
+ continue;
41
+ fields[match[1]] = stripQuotes(match[2]);
42
+ }
43
+ return fields;
44
+ }
45
+ function bodyWithoutFrontmatter(content) {
46
+ if (!content.startsWith('---\n'))
47
+ return content;
48
+ const end = content.indexOf('\n---', 4);
49
+ return end === -1 ? content : content.slice(end + 4);
50
+ }
51
+ function firstBodyLine(body) {
52
+ for (const line of body.split('\n')) {
53
+ const trimmed = line.trim();
54
+ if (trimmed && !trimmed.startsWith('#'))
55
+ return trimmed;
56
+ }
57
+ return '';
58
+ }
59
+ function firstHeading(body) {
60
+ for (const line of body.split('\n')) {
61
+ const match = /^#\s+(.+)$/.exec(line.trim());
62
+ if (match)
63
+ return match[1].trim();
64
+ }
65
+ return null;
66
+ }
67
+ function humanizeSkillId(id) {
68
+ return id
69
+ .split(/[-_]+/)
70
+ .filter(Boolean)
71
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
72
+ .join(' ');
73
+ }
74
+ function parseSkillContent(id, filePath, content) {
75
+ const frontmatter = parseFrontmatter(content);
76
+ const body = bodyWithoutFrontmatter(content);
77
+ const name = firstHeading(body) || humanizeSkillId(frontmatter.name || id);
78
+ const description = frontmatter.description || firstBodyLine(body);
79
+ return {
80
+ id,
81
+ name,
82
+ description,
83
+ key: `minions/${id}`,
84
+ source: 'Minions bundled',
85
+ bundled: true,
86
+ readOnly: true,
87
+ autoIncluded: true,
88
+ filePath,
89
+ };
90
+ }
91
+ export async function listBundledSkills() {
92
+ const root = resolveBundledSkillsDir();
93
+ let entries;
94
+ try {
95
+ entries = await readdir(root, { withFileTypes: true });
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ const skills = await Promise.all(entries
101
+ .filter((entry) => entry.isDirectory() && VALID_SKILL_ID.test(entry.name))
102
+ .map(async (entry) => {
103
+ const id = entry.name;
104
+ const filePath = join(root, id, 'SKILL.md');
105
+ try {
106
+ const content = await readFile(filePath, 'utf-8');
107
+ return parseSkillContent(id, filePath, content);
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ }));
113
+ return skills
114
+ .filter((skill) => skill !== null)
115
+ .sort((a, b) => a.name.localeCompare(b.name));
116
+ }
117
+ export async function getBundledSkillWithContent(id) {
118
+ if (!VALID_SKILL_ID.test(id))
119
+ return null;
120
+ const root = resolveBundledSkillsDir();
121
+ const filePath = join(root, id, 'SKILL.md');
122
+ try {
123
+ const content = await readFile(filePath, 'utf-8');
124
+ return { skill: parseSkillContent(id, filePath, content), content };
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ export function toSkillMeta(skill) {
131
+ const { filePath: _filePath, ...meta } = skill;
132
+ return meta;
133
+ }
134
+ export function ensureBundledSkillsLinked() {
135
+ const source = resolveBundledSkillsDir();
136
+ if (!existsSync(source))
137
+ return;
138
+ const hermesHome = process.env.HERMES_HOME
139
+ ? expandHomePrefix(process.env.HERMES_HOME)
140
+ : join(homedir(), '.hermes');
141
+ const target = join(hermesHome, 'skills', 'minions');
142
+ mkdirSync(dirname(target), { recursive: true });
143
+ try {
144
+ const stat = lstatSync(target);
145
+ if (!stat.isSymbolicLink()) {
146
+ console.warn(`Bundled skills not linked: ${target} already exists and is not a symlink.`);
147
+ return;
148
+ }
149
+ const current = readlinkSync(target);
150
+ const resolvedCurrent = resolve(dirname(target), current);
151
+ if (relative(source, resolvedCurrent) === '')
152
+ return;
153
+ unlinkSync(target);
154
+ }
155
+ catch (error) {
156
+ if (error.code !== 'ENOENT')
157
+ throw error;
158
+ }
159
+ symlinkSync(source, target, 'dir');
160
+ }
@@ -0,0 +1,241 @@
1
+ """Cron job operations and run-output projection for the Hermes worker.
2
+
3
+ Wraps Hermes's `cron.jobs` / `cron.scheduler` modules with input validation,
4
+ shape normalization, and a background ticker thread.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import stat
10
+ import sys
11
+ import threading
12
+ import time
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from hermes_worker_utils import (
18
+ WorkerError,
19
+ json_safe,
20
+ string_or_none,
21
+ truncate_with_ellipsis,
22
+ )
23
+
24
+
25
+ _CRON_TICKER_STARTED = False
26
+ _CRON_TICKER_LOCK = threading.Lock()
27
+
28
+
29
+ def _ensure_imports() -> None:
30
+ # Lazy import so this module does not need a top-level dep on hermes_worker.
31
+ # `hermes_worker._ensure_imports()` adds the Hermes agent dir to sys.path,
32
+ # making `cron.jobs` / `cron.scheduler` importable below.
33
+ import hermes_worker
34
+
35
+ hermes_worker._ensure_imports()
36
+
37
+
38
+ def _normalize_cron_job(job: dict[str, Any] | None) -> dict[str, Any] | None:
39
+ if not isinstance(job, dict):
40
+ return None
41
+
42
+ job_id = string_or_none(job.get("id")) or ""
43
+ raw_schedule = job.get("schedule")
44
+ raw_origin = job.get("origin")
45
+ raw_skills = job.get("skills")
46
+ if raw_skills is None and job.get("skill"):
47
+ raw_skills = [job.get("skill")]
48
+
49
+ return {
50
+ "id": job_id,
51
+ "name": string_or_none(job.get("name")) or job_id,
52
+ "prompt": string_or_none(job.get("prompt")),
53
+ "schedule": json_safe(raw_schedule) if isinstance(raw_schedule, dict) else None,
54
+ "scheduleDisplay": string_or_none(job.get("schedule_display")),
55
+ "enabled": bool(job.get("enabled", True)),
56
+ "state": string_or_none(job.get("state")),
57
+ "nextRunAt": string_or_none(job.get("next_run_at")),
58
+ "lastRunAt": string_or_none(job.get("last_run_at")),
59
+ "lastStatus": string_or_none(job.get("last_status")),
60
+ "lastError": string_or_none(job.get("last_error")),
61
+ "lastDeliveryError": string_or_none(job.get("last_delivery_error")),
62
+ "model": string_or_none(job.get("model")),
63
+ "provider": string_or_none(job.get("provider")),
64
+ "baseUrl": string_or_none(job.get("base_url")),
65
+ "deliver": string_or_none(job.get("deliver")),
66
+ "origin": json_safe(raw_origin) if isinstance(raw_origin, dict) else None,
67
+ "skills": [str(item) for item in raw_skills] if isinstance(raw_skills, list) else [],
68
+ "createdAt": string_or_none(job.get("created_at")),
69
+ }
70
+
71
+
72
+ def _validate_path_segment(value: Any, label: str) -> str:
73
+ raw = string_or_none(value)
74
+ if not raw:
75
+ raise WorkerError(f"{label} is required.", code="bad_request")
76
+ if "/" in raw or "\\" in raw or ".." in raw:
77
+ raise WorkerError(f"Invalid {label}.", code="bad_request")
78
+ return raw
79
+
80
+
81
+ def list_cron_jobs(include_disabled: bool = False) -> dict[str, Any]:
82
+ _ensure_imports()
83
+ from cron.jobs import list_jobs
84
+
85
+ jobs = [_normalize_cron_job(job) for job in list_jobs(include_disabled=include_disabled)]
86
+ return {"jobs": [job for job in jobs if job is not None]}
87
+
88
+
89
+ def get_cron_job(job_id: Any) -> dict[str, Any]:
90
+ _ensure_imports()
91
+ from cron.jobs import get_job
92
+
93
+ job = _normalize_cron_job(get_job(_validate_path_segment(job_id, "Cron job ID")))
94
+ return {"job": job}
95
+
96
+
97
+ def pause_cron_job(job_id: Any, reason: Any = None) -> dict[str, Any]:
98
+ _ensure_imports()
99
+ from cron.jobs import pause_job
100
+
101
+ raw_reason = string_or_none(reason)
102
+ job = _normalize_cron_job(pause_job(_validate_path_segment(job_id, "Cron job ID"), reason=raw_reason))
103
+ return {"job": job}
104
+
105
+
106
+ def resume_cron_job(job_id: Any) -> dict[str, Any]:
107
+ _ensure_imports()
108
+ from cron.jobs import resume_job
109
+
110
+ job = _normalize_cron_job(resume_job(_validate_path_segment(job_id, "Cron job ID")))
111
+ return {"job": job}
112
+
113
+
114
+ def trigger_cron_job(job_id: Any) -> dict[str, Any]:
115
+ _ensure_imports()
116
+ from cron.jobs import trigger_job
117
+
118
+ job = _normalize_cron_job(trigger_job(_validate_path_segment(job_id, "Cron job ID")))
119
+ return {"job": job}
120
+
121
+
122
+ def remove_cron_job(job_id: Any) -> dict[str, Any]:
123
+ _ensure_imports()
124
+ from cron.jobs import remove_job
125
+
126
+ removed = bool(remove_job(_validate_path_segment(job_id, "Cron job ID")))
127
+ return {"ok": removed}
128
+
129
+
130
+ def _run_preview(content: str, max_chars: int = 420) -> str:
131
+ lines = [line.strip() for line in content.splitlines() if line.strip()]
132
+ return truncate_with_ellipsis("\n".join(lines[:6]), max_chars)
133
+
134
+
135
+ def _run_timestamp_from_stem(stem: str) -> str | None:
136
+ try:
137
+ return datetime.strptime(stem, "%Y-%m-%d_%H-%M-%S").isoformat()
138
+ except ValueError:
139
+ return None
140
+
141
+
142
+ def _cron_run_status(content: str) -> str:
143
+ first_line = next((line.strip() for line in content.splitlines() if line.strip()), "")
144
+ if first_line.startswith("# Cron Job:") and "(FAILED)" in first_line:
145
+ return "error"
146
+ if first_line.startswith("# Cron Job:"):
147
+ return "ok"
148
+ return "unknown"
149
+
150
+
151
+ def _read_run_head(path: Path, max_bytes: int = 2048) -> str:
152
+ try:
153
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
154
+ return f.read(max_bytes)
155
+ except OSError:
156
+ return ""
157
+
158
+
159
+ def list_cron_runs(job_id: Any, limit: Any = 20) -> dict[str, Any]:
160
+ _ensure_imports()
161
+ from cron.jobs import OUTPUT_DIR
162
+
163
+ cron_job_id = _validate_path_segment(job_id, "Cron job ID")
164
+ try:
165
+ parsed_limit = int(limit)
166
+ except (TypeError, ValueError):
167
+ parsed_limit = 20
168
+ parsed_limit = max(1, min(parsed_limit, 100))
169
+
170
+ output_dir = Path(OUTPUT_DIR) / cron_job_id
171
+ entries: list[tuple[float, Path]] = []
172
+ for path in output_dir.glob("*.md"):
173
+ try:
174
+ st = path.stat()
175
+ except OSError:
176
+ continue
177
+ if not stat.S_ISREG(st.st_mode):
178
+ continue
179
+ entries.append((st.st_mtime, path))
180
+ entries.sort(key=lambda entry: (entry[0], entry[1].name), reverse=True)
181
+
182
+ runs: list[dict[str, Any]] = []
183
+ for _, path in entries[:parsed_limit]:
184
+ head = _read_run_head(path)
185
+ runs.append({
186
+ "id": path.stem,
187
+ "jobId": cron_job_id,
188
+ "ranAt": _run_timestamp_from_stem(path.stem),
189
+ "path": str(path),
190
+ "status": _cron_run_status(head),
191
+ "preview": _run_preview(head),
192
+ })
193
+
194
+ return {"runs": runs}
195
+
196
+
197
+ def get_cron_run_content(job_id: Any, run_id: Any) -> dict[str, Any]:
198
+ _ensure_imports()
199
+ from cron.jobs import OUTPUT_DIR
200
+
201
+ cron_job_id = _validate_path_segment(job_id, "Cron job ID")
202
+ raw_run_id = _validate_path_segment(run_id, "Run ID")
203
+
204
+ path = Path(OUTPUT_DIR) / cron_job_id / f"{raw_run_id}.md"
205
+ if not path.is_file():
206
+ raise WorkerError("Cron run output not found.", code="not_found")
207
+
208
+ try:
209
+ content = path.read_text(encoding="utf-8")
210
+ except UnicodeDecodeError:
211
+ content = path.read_text(encoding="utf-8", errors="replace")
212
+
213
+ return {"content": content}
214
+
215
+
216
+ def tick_cron() -> int:
217
+ _ensure_imports()
218
+ from cron.scheduler import tick
219
+
220
+ return int(tick(verbose=False) or 0)
221
+
222
+
223
+ def _cron_ticker_loop() -> None:
224
+ while True:
225
+ try:
226
+ executed = tick_cron()
227
+ if executed:
228
+ print(f"[hermes-worker] cron tick executed {executed} job(s)", file=sys.stderr, flush=True)
229
+ except Exception as exc:
230
+ print(f"[hermes-worker] cron tick failed: {exc}", file=sys.stderr, flush=True)
231
+ time.sleep(60)
232
+
233
+
234
+ def start_cron_ticker() -> None:
235
+ global _CRON_TICKER_STARTED
236
+ with _CRON_TICKER_LOCK:
237
+ if _CRON_TICKER_STARTED:
238
+ return
239
+ thread = threading.Thread(target=_cron_ticker_loop, name="hermes-cron-ticker", daemon=True)
240
+ thread.start()
241
+ _CRON_TICKER_STARTED = True