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.
- package/LICENSE +21 -0
- package/README.md +89 -0
- package/bin/minions.mjs +19 -0
- package/dist/server/client/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/server/client/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
- package/dist/server/client/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/server/client/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/server/client/dist/assets/angular-html-CU67Zn6k.js +1 -0
- package/dist/server/client/dist/assets/angular-ts-BwZT4LLn.js +1 -0
- package/dist/server/client/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/server/client/dist/assets/apex-D8_7TLub.js +1 -0
- package/dist/server/client/dist/assets/apl-dKokRX4l.js +1 -0
- package/dist/server/client/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/server/client/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/server/client/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
- package/dist/server/client/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/server/client/dist/assets/astro-CbQHKStN.js +1 -0
- package/dist/server/client/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/server/client/dist/assets/awk-DMzUqQB5.js +1 -0
- package/dist/server/client/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/server/client/dist/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/server/client/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/server/client/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/server/client/dist/assets/bat-BkioyH1T.js +1 -0
- package/dist/server/client/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/server/client/dist/assets/berry-uYugtg8r.js +1 -0
- package/dist/server/client/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/server/client/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/server/client/dist/assets/bird2-DPOp833l.js +1 -0
- package/dist/server/client/dist/assets/blade-D4QpJJKB.js +1 -0
- package/dist/server/client/dist/assets/bsl-BO_Y6i37.js +1 -0
- package/dist/server/client/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/server/client/dist/assets/c3-eo99z4R2.js +1 -0
- package/dist/server/client/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/server/client/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/server/client/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/dist/server/client/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/dist/server/client/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/dist/server/client/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/dist/server/client/dist/assets/clarity-D53aC0YG.js +1 -0
- package/dist/server/client/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/server/client/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/server/client/dist/assets/cobol-nwyudZeR.js +1 -0
- package/dist/server/client/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/server/client/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/server/client/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/server/client/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/server/client/dist/assets/coq-DkFqJrB1.js +1 -0
- package/dist/server/client/dist/assets/cpp-CofmeUqb.js +1 -0
- package/dist/server/client/dist/assets/crystal-tKQVLTB8.js +1 -0
- package/dist/server/client/dist/assets/csharp-COcwbKMJ.js +1 -0
- package/dist/server/client/dist/assets/css-DPfMkruS.js +1 -0
- package/dist/server/client/dist/assets/csv-fuZLfV_i.js +1 -0
- package/dist/server/client/dist/assets/cue-D82EKSYY.js +1 -0
- package/dist/server/client/dist/assets/cypher-COkxafJQ.js +1 -0
- package/dist/server/client/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/server/client/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/server/client/dist/assets/dart-CF10PKvl.js +1 -0
- package/dist/server/client/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/server/client/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/server/client/dist/assets/diff-D97Zzqfu.js +1 -0
- package/dist/server/client/dist/assets/docker-BcOcwvcX.js +1 -0
- package/dist/server/client/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/server/client/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/server/client/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/server/client/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/server/client/dist/assets/edge-BkV0erSs.js +1 -0
- package/dist/server/client/dist/assets/elixir-CDX3lj18.js +1 -0
- package/dist/server/client/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/server/client/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/dist/server/client/dist/assets/erb-B12qg9BL.js +1 -0
- package/dist/server/client/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/server/client/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/server/client/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/server/client/dist/assets/fennel-BYunw83y.js +1 -0
- package/dist/server/client/dist/assets/fish-BvzEVeQv.js +1 -0
- package/dist/server/client/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/server/client/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/server/client/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/server/client/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/server/client/dist/assets/gdresource-BOOCDP_w.js +1 -0
- package/dist/server/client/dist/assets/gdscript-C5YyOfLZ.js +1 -0
- package/dist/server/client/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/server/client/dist/assets/genie-D0YGMca9.js +1 -0
- package/dist/server/client/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/server/client/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/server/client/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/server/client/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/server/client/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/server/client/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/server/client/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/server/client/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/server/client/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/server/client/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/server/client/dist/assets/gleam-BspZqrRM.js +1 -0
- package/dist/server/client/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/dist/server/client/dist/assets/glimmer-ts-U6CK756n.js +1 -0
- package/dist/server/client/dist/assets/glsl-DplSGwfg.js +1 -0
- package/dist/server/client/dist/assets/gn-n2N0HUVH.js +1 -0
- package/dist/server/client/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/server/client/dist/assets/go-CxLEBnE3.js +1 -0
- package/dist/server/client/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/server/client/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/server/client/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/server/client/dist/assets/hack-CaT9iCJl.js +1 -0
- package/dist/server/client/dist/assets/haml-B8DHNrY2.js +1 -0
- package/dist/server/client/dist/assets/handlebars-BL8al0AC.js +1 -0
- package/dist/server/client/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/server/client/dist/assets/haxe-CzTSHFRz.js +1 -0
- package/dist/server/client/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/server/client/dist/assets/highlighted-body-TPN3WLV5-Dmyr2DoJ.js +1 -0
- package/dist/server/client/dist/assets/hjson-D5-asLiD.js +1 -0
- package/dist/server/client/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/server/client/dist/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/server/client/dist/assets/horizon-bright-Cn-bp-IR.js +1 -0
- package/dist/server/client/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/server/client/dist/assets/html-GMplVEZG.js +1 -0
- package/dist/server/client/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/dist/server/client/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/server/client/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/server/client/dist/assets/hxml-Bvhsp5Yf.js +1 -0
- package/dist/server/client/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/server/client/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/server/client/dist/assets/index-BB7507W7.css +1 -0
- package/dist/server/client/dist/assets/index-hLQDnL9J.js +694 -0
- package/dist/server/client/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/server/client/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/server/client/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/server/client/dist/assets/jinja-4LBKfQ-Z.js +1 -0
- package/dist/server/client/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/server/client/dist/assets/json-Cp-IABpG.js +1 -0
- package/dist/server/client/dist/assets/json5-C9tS-k6U.js +1 -0
- package/dist/server/client/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/server/client/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/server/client/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/server/client/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/server/client/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/server/client/dist/assets/julia-CxzCAyBv.js +1 -0
- package/dist/server/client/dist/assets/just-Cw27pwNe.js +1 -0
- package/dist/server/client/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/server/client/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/server/client/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/server/client/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/server/client/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/server/client/dist/assets/kusto-DZf3V79B.js +1 -0
- package/dist/server/client/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/server/client/dist/assets/latex-CWtU0Tv5.js +1 -0
- package/dist/server/client/dist/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/server/client/dist/assets/less-B1dDrJ26.js +1 -0
- package/dist/server/client/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/server/client/dist/assets/liquid-DYVedYrR.js +1 -0
- package/dist/server/client/dist/assets/llvm-DjAJT7YJ.js +1 -0
- package/dist/server/client/dist/assets/log-2UxHyX5q.js +1 -0
- package/dist/server/client/dist/assets/logo-BtOb2qkB.js +1 -0
- package/dist/server/client/dist/assets/lua-BaeVxFsk.js +1 -0
- package/dist/server/client/dist/assets/luau-C-HG3fhB.js +1 -0
- package/dist/server/client/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/server/client/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/server/client/dist/assets/marko-CnJfTvn9.js +1 -0
- package/dist/server/client/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/server/client/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/server/client/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/server/client/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/server/client/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/server/client/dist/assets/matlab-D7o27uSR.js +1 -0
- package/dist/server/client/dist/assets/mdc-BMNejdWA.js +1 -0
- package/dist/server/client/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/server/client/dist/assets/mermaid-mWjccvbQ.js +1 -0
- package/dist/server/client/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/server/client/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/server/client/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/server/client/dist/assets/mojo-rZm6bMo-.js +1 -0
- package/dist/server/client/dist/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/server/client/dist/assets/moonbit-_H4v1dQx.js +1 -0
- package/dist/server/client/dist/assets/move-IF9eRakj.js +1 -0
- package/dist/server/client/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/server/client/dist/assets/nextflow-Zz6hmt5N.js +1 -0
- package/dist/server/client/dist/assets/nextflow-groovy-BeH2EWoN.js +1 -0
- package/dist/server/client/dist/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/server/client/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/server/client/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/server/client/dist/assets/nim-CVrawwO9.js +1 -0
- package/dist/server/client/dist/assets/nix-CwoSXNpI.js +1 -0
- package/dist/server/client/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/server/client/dist/assets/nushell-Cz2AlsmD.js +1 -0
- package/dist/server/client/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/server/client/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/server/client/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/server/client/dist/assets/odin-BBf5iR-q.js +1 -0
- package/dist/server/client/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/server/client/dist/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/server/client/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/server/client/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/server/client/dist/assets/perl-C0TMdlhV.js +1 -0
- package/dist/server/client/dist/assets/php-Dhbhpdrm.js +1 -0
- package/dist/server/client/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/server/client/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/server/client/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/server/client/dist/assets/po-BTJTHyun.js +1 -0
- package/dist/server/client/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/server/client/dist/assets/polar-C0HS_06l.js +1 -0
- package/dist/server/client/dist/assets/postcss-CXtECtnM.js +1 -0
- package/dist/server/client/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/server/client/dist/assets/powershell-Dpen1YoG.js +1 -0
- package/dist/server/client/dist/assets/prisma-Dd19v3D-.js +1 -0
- package/dist/server/client/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/server/client/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/server/client/dist/assets/pug-CGlum2m_.js +1 -0
- package/dist/server/client/dist/assets/puppet-BMWR74SV.js +1 -0
- package/dist/server/client/dist/assets/purescript-CklMAg4u.js +1 -0
- package/dist/server/client/dist/assets/python-B6aJPvgy.js +1 -0
- package/dist/server/client/dist/assets/qml-3beO22l8.js +1 -0
- package/dist/server/client/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/server/client/dist/assets/qss-IeuSbFQv.js +1 -0
- package/dist/server/client/dist/assets/r-Dspwwk_N.js +1 -0
- package/dist/server/client/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/server/client/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/server/client/dist/assets/razor-Uh8Bk_45.js +1 -0
- package/dist/server/client/dist/assets/red-bN70gL4F.js +1 -0
- package/dist/server/client/dist/assets/reg-C-SQnVFl.js +1 -0
- package/dist/server/client/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/server/client/dist/assets/rel-C3B-1QV4.js +1 -0
- package/dist/server/client/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/server/client/dist/assets/ron-D8l8udqQ.js +1 -0
- package/dist/server/client/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- package/dist/server/client/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- package/dist/server/client/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
- package/dist/server/client/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/server/client/dist/assets/rst-BrH8l1NY.js +1 -0
- package/dist/server/client/dist/assets/ruby-Dw2BHqvy.js +1 -0
- package/dist/server/client/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/server/client/dist/assets/sas-cz2c8ADy.js +1 -0
- package/dist/server/client/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/server/client/dist/assets/scala-C151Ov-r.js +1 -0
- package/dist/server/client/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/server/client/dist/assets/scss-OYdSNvt2.js +1 -0
- package/dist/server/client/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/server/client/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/server/client/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/server/client/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/server/client/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/server/client/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/server/client/dist/assets/smalltalk-BERRCDM3.js +1 -0
- package/dist/server/client/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/server/client/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/server/client/dist/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/server/client/dist/assets/solidity-rGO070M0.js +1 -0
- package/dist/server/client/dist/assets/soy-Brmx7dQM.js +1 -0
- package/dist/server/client/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/server/client/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/server/client/dist/assets/sql-BLtJtn59.js +1 -0
- package/dist/server/client/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/server/client/dist/assets/stata-BH5u7GGu.js +1 -0
- package/dist/server/client/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/server/client/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
- package/dist/server/client/dist/assets/svelte-C_ipcX3V.js +1 -0
- package/dist/server/client/dist/assets/swift-D82vCrfD.js +1 -0
- package/dist/server/client/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/server/client/dist/assets/system-verilog-CnnmHF94.js +1 -0
- package/dist/server/client/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/server/client/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/server/client/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/server/client/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/server/client/dist/assets/templ-P3uqSqPl.js +1 -0
- package/dist/server/client/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/server/client/dist/assets/tex-idrVyKtj.js +1 -0
- package/dist/server/client/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/server/client/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/server/client/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/dist/server/client/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/server/client/dist/assets/tsx-COt5Ahok.js +1 -0
- package/dist/server/client/dist/assets/turtle-BsS91CYL.js +1 -0
- package/dist/server/client/dist/assets/twig-DNn4PbVi.js +1 -0
- package/dist/server/client/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/server/client/dist/assets/typespec-BGHnOYBU.js +1 -0
- package/dist/server/client/dist/assets/typst-DHCkPAjA.js +1 -0
- package/dist/server/client/dist/assets/v-BcVCzyr7.js +1 -0
- package/dist/server/client/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/server/client/dist/assets/vb-D17OF-Vu.js +1 -0
- package/dist/server/client/dist/assets/verilog-BQ8w6xss.js +1 -0
- package/dist/server/client/dist/assets/vesper-DU1UobuO.js +1 -0
- package/dist/server/client/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/server/client/dist/assets/viml-CJc9bBzg.js +1 -0
- package/dist/server/client/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/server/client/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/server/client/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/server/client/dist/assets/vue-DN_0RTcg.js +1 -0
- package/dist/server/client/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/server/client/dist/assets/vue-vine-CQOfvN7w.js +1 -0
- package/dist/server/client/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/server/client/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/server/client/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/server/client/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/server/client/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/server/client/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/server/client/dist/assets/wit-5i3qLPDT.js +1 -0
- package/dist/server/client/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/server/client/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/server/client/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/server/client/dist/assets/yaml-Buea-lGh.js +1 -0
- package/dist/server/client/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/server/client/dist/assets/zig-VOosw3JB.js +1 -0
- package/dist/server/client/dist/favicon.ico +0 -0
- package/dist/server/client/dist/index.html +17 -0
- package/dist/server/client/dist/logo.png +0 -0
- package/dist/server/server/adapters/hermes-worker.d.ts +37 -0
- package/dist/server/server/adapters/hermes-worker.js +525 -0
- package/dist/server/server/adapters/types.d.ts +39 -0
- package/dist/server/server/adapters/types.js +1 -0
- package/dist/server/server/adapters/worker-protocol.d.ts +141 -0
- package/dist/server/server/adapters/worker-protocol.js +1 -0
- package/dist/server/server/agent-settings.d.ts +12 -0
- package/dist/server/server/agent-settings.js +56 -0
- package/dist/server/server/app.d.ts +5 -0
- package/dist/server/server/app.js +41 -0
- package/dist/server/server/db/index.d.ts +2 -0
- package/dist/server/server/db/index.js +14 -0
- package/dist/server/server/db/queries.d.ts +22 -0
- package/dist/server/server/db/queries.js +116 -0
- package/dist/server/server/db/schema.sql +16 -0
- package/dist/server/server/errors.d.ts +3 -0
- package/dist/server/server/errors.js +12 -0
- package/dist/server/server/events.d.ts +7 -0
- package/dist/server/server/events.js +50 -0
- package/dist/server/server/frontend.d.ts +4 -0
- package/dist/server/server/frontend.js +32 -0
- package/dist/server/server/index.d.ts +2 -0
- package/dist/server/server/index.js +63 -0
- package/dist/server/server/live-chat.d.ts +17 -0
- package/dist/server/server/live-chat.js +217 -0
- package/dist/server/server/paths.d.ts +7 -0
- package/dist/server/server/paths.js +39 -0
- package/dist/server/server/prompts/task-agent.d.ts +1 -0
- package/dist/server/server/prompts/task-agent.js +26 -0
- package/dist/server/server/routes/agent.d.ts +4 -0
- package/dist/server/server/routes/agent.js +94 -0
- package/dist/server/server/routes/chat.d.ts +1 -0
- package/dist/server/server/routes/chat.js +177 -0
- package/dist/server/server/routes/cron.d.ts +4 -0
- package/dist/server/server/routes/cron.js +109 -0
- package/dist/server/server/routes/files.d.ts +1 -0
- package/dist/server/server/routes/files.js +543 -0
- package/dist/server/server/routes/skills.d.ts +1 -0
- package/dist/server/server/routes/skills.js +13 -0
- package/dist/server/server/routes/tasks.d.ts +1 -0
- package/dist/server/server/routes/tasks.js +107 -0
- package/dist/server/server/skills/catalog.d.ts +21 -0
- package/dist/server/server/skills/catalog.js +160 -0
- package/dist/server/server/workers/hermes_cron.py +241 -0
- package/dist/server/server/workers/hermes_sessions.py +264 -0
- package/dist/server/server/workers/hermes_worker.py +1270 -0
- package/dist/server/server/workers/hermes_worker_utils.py +39 -0
- package/dist/server/shared/types.d.ts +211 -0
- package/dist/server/shared/types.js +2 -0
- package/package.json +74 -0
- 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
|