...
This commit is contained in:
parent
92b9c356b8
commit
0e545e56de
@ -1,23 +1,3 @@
|
||||
// @title HeroLauncher API
|
||||
// @version 1.0
|
||||
// @description HeroLauncher API provides endpoints for managing services, processes, and system resources
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name HeroLauncher Support
|
||||
// @contact.url https://github.com/freeflowuniverse/heroagent
|
||||
// @contact.email support@heroagent.io
|
||||
|
||||
// @license.name MIT
|
||||
// @license.url https://opensource.org/licenses/MIT
|
||||
|
||||
// @host localhost:9021
|
||||
// @BasePath /
|
||||
// @schemes http https
|
||||
|
||||
// @securityDefinitions.apikey ApiKeyAuth
|
||||
// @in header
|
||||
// @name Authorization
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -26,8 +6,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/freeflowuniverse/heroagent/pkg/heroagent"
|
||||
_ "github.com/freeflowuniverse/heroagent/pkg/heroagent/docs" // Import generated swagger docs
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/heroagent"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
4
go.mod
4
go.mod
@ -12,7 +12,7 @@ require (
|
||||
github.com/gofiber/fiber/v2 v2.52.6
|
||||
github.com/gofiber/template/pug/v2 v2.1.8
|
||||
github.com/knusbaum/go9p v1.18.0
|
||||
github.com/redis/go-redis/v9 v9.7.1
|
||||
github.com/redis/go-redis/v9 v9.8.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/tidwall/redcon v1.6.2
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
@ -80,8 +80,6 @@ require (
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/speakeasy-api/jsonpath v0.6.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
github.com/swaggo/swag v1.16.4 // indirect
|
||||
github.com/tidwall/btree v1.1.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
|
202
go.sum
Normal file
202
go.sum
Normal file
@ -0,0 +1,202 @@
|
||||
9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Plan9-Archive/libauth v0.0.0-20180917063427-d1ca9e94969d/go.mod h1:UKp8dv9aeaZoQFWin7eQXtz89iHly1YAFZNn3MCutmQ=
|
||||
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6/go.mod h1:BEksegNspIkjCQfmzWgsgbu6KdeJ/4LwUZs7DMBzjzw=
|
||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
|
||||
github.com/emersion/go-webdav v0.6.0/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ=
|
||||
github.com/fhs/mux9p v0.3.1/go.mod h1:F4hwdenmit0WDoNVT2VMWlLJrBVCp/8UhzJa7scfjEQ=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||
github.com/gofiber/template/jet/v2 v2.1.11/go.mod h1:Kb1oBdrx90oEvP71MDTUB9k+IWRF082Td5OPW7SoUMQ=
|
||||
github.com/gofiber/template/pug/v2 v2.1.8/go.mod h1:e0Sg0YBMtC+RQMRm0swaAvqIBDJmhhDIKfFFtQRjvlQ=
|
||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
|
||||
github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY=
|
||||
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knusbaum/go9p v1.18.0/go.mod h1:HtMoJKqZUe1Oqag5uJqG5RKQ9gWPSP+wolsnLLv44r8=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/metoro-io/mcp-golang v0.8.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/openai/openai-go v0.1.0-beta.9/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
|
||||
github.com/pb33f/libopenapi v0.21.8/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
||||
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/speakeasy-api/jsonpath v0.6.1/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
|
||||
github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240815153524-6ea36470d1bd/go.mod h1:DbzwytT4g/odXquuOCqroKvtxxldI4nb3nuesHF/Exo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201020230747-6e5568b54d1a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
@ -1,192 +0,0 @@
|
||||
# Process Manager
|
||||
|
||||
The Process Manager is a component that manages and monitors external processes. It provides functionality to start, stop, restart, and monitor processes, as well as retrieve their status information such as CPU and memory usage.
|
||||
|
||||
## Features
|
||||
|
||||
- Start, stop, restart, and delete processes
|
||||
- Monitor CPU and memory usage of managed processes
|
||||
- Set deadlines for process execution
|
||||
- Support for cron-like scheduling
|
||||
- Telnet interface for remote management
|
||||
- Authentication via secret key
|
||||
|
||||
## Components
|
||||
|
||||
The Process Manager consists of the following components:
|
||||
|
||||
1. **Process Manager Core**: Manages processes and their lifecycle
|
||||
2. **Telnet Server**: Provides a telnet interface for remote management
|
||||
3. **Client Library**: Provides a Go API for interacting with the Process Manager
|
||||
4. **Command-line Client**: Provides a command-line interface for managing processes
|
||||
5. **Interfaces**: Various interface implementations for interacting with the Process Manager
|
||||
|
||||
## Interfaces
|
||||
|
||||
The Process Manager provides several interfaces for integration and extension:
|
||||
|
||||
### ProcessManagerInterface
|
||||
|
||||
The core interface that defines all process management operations. This interface allows for dependency injection and easier testing.
|
||||
|
||||
```go
|
||||
type ProcessManagerInterface interface {
|
||||
// Authentication
|
||||
GetSecret() string
|
||||
|
||||
// Process management
|
||||
StartProcess(name, command string, logEnabled bool, deadline int, cron, jobID string) error
|
||||
StopProcess(name string) error
|
||||
RestartProcess(name string) error
|
||||
DeleteProcess(name string) error
|
||||
GetProcessStatus(name string) (*ProcessInfo, error)
|
||||
ListProcesses() []*ProcessInfo
|
||||
GetProcessLogs(name string, lines int) (string, error)
|
||||
}
|
||||
```
|
||||
|
||||
### Interface Implementations
|
||||
|
||||
The Process Manager includes the following interface implementations:
|
||||
|
||||
1. **Telnet Interface**: Provides a telnet-based interface for remote management
|
||||
- Located in `pkg/processmanager/interfaces/telnet`
|
||||
- Allows command execution via heroscript syntax
|
||||
- Supports authentication and secure communication
|
||||
|
||||
2. **OpenRPC Interface**: Provides a JSON-RPC 2.0 interface following the OpenRPC specification
|
||||
- Located in `pkg/processmanager/interfaces/openrpc`
|
||||
- Enables programmatic access via HTTP or WebSockets
|
||||
- Includes auto-generated client libraries
|
||||
- Provides a standardized API schema
|
||||
|
||||
## Usage
|
||||
|
||||
### Starting the Process Manager
|
||||
|
||||
```bash
|
||||
./processmanager -socket /tmp/processmanager.sock -secret mysecretkey
|
||||
```
|
||||
|
||||
### Using the Command-line Client
|
||||
|
||||
```bash
|
||||
# Start a process
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey start -name myprocess -command "echo hello world" -log
|
||||
|
||||
# List all processes
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey list -format json
|
||||
|
||||
# Get process status
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey status -name myprocess -format json
|
||||
|
||||
# Stop a process
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey stop -name myprocess
|
||||
|
||||
# Restart a process
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey restart -name myprocess
|
||||
|
||||
# Delete a process
|
||||
./pmclient -socket /tmp/processmanager.sock -secret mysecretkey delete -name myprocess
|
||||
```
|
||||
|
||||
### Using the Telnet Interface
|
||||
|
||||
You can connect to the Process Manager using a telnet client:
|
||||
|
||||
```bash
|
||||
telnet /tmp/processmanager.sock
|
||||
```
|
||||
|
||||
After connecting, you need to authenticate with the secret key:
|
||||
|
||||
```
|
||||
mysecretkey
|
||||
```
|
||||
|
||||
Once authenticated, you can send heroscript commands:
|
||||
|
||||
```
|
||||
!!process.start name:'myprocess' command:'echo hello world' log:true
|
||||
!!process.list format:json
|
||||
!!process.status name:'myprocess' format:json
|
||||
!!process.stop name:'myprocess'
|
||||
!!process.restart name:'myprocess'
|
||||
!!process.delete name:'myprocess'
|
||||
```
|
||||
|
||||
## Heroscript Commands
|
||||
|
||||
The Process Manager supports the following heroscript commands:
|
||||
|
||||
### process.start
|
||||
|
||||
Starts a new process.
|
||||
|
||||
```
|
||||
!!process.start name:'processname' command:'command which can be multiline' log:true deadline:30 cron:'0 0 * * *' jobid:'e42'
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `name`: Name of the process (required)
|
||||
- `command`: Command to run (required)
|
||||
- `log`: Enable logging (optional, default: false)
|
||||
- `deadline`: Deadline in seconds (optional)
|
||||
- `cron`: Cron schedule (optional)
|
||||
- `jobid`: Job ID (optional)
|
||||
|
||||
### process.list
|
||||
|
||||
Lists all processes.
|
||||
|
||||
```
|
||||
!!process.list format:json
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `format`: Output format (optional, values: json or empty for text)
|
||||
|
||||
### process.delete
|
||||
|
||||
Deletes a process.
|
||||
|
||||
```
|
||||
!!process.delete name:'processname'
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `name`: Name of the process (required)
|
||||
|
||||
### process.status
|
||||
|
||||
Gets the status of a process.
|
||||
|
||||
```
|
||||
!!process.status name:'processname' format:json
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `name`: Name of the process (required)
|
||||
- `format`: Output format (optional, values: json or empty for text)
|
||||
|
||||
### process.restart
|
||||
|
||||
Restarts a process.
|
||||
|
||||
```
|
||||
!!process.restart name:'processname'
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `name`: Name of the process (required)
|
||||
|
||||
### process.stop
|
||||
|
||||
Stops a process.
|
||||
|
||||
```
|
||||
!!process.stop name:'processname'
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `name`: Name of the process (required)
|
@ -1,85 +0,0 @@
|
||||
# Process Manager OpenRPC Example
|
||||
|
||||
This example demonstrates how to use the Process Manager OpenRPC interface to interact with the process manager. It provides a complete working example of both server and client implementations, with a mock process manager for testing purposes.
|
||||
|
||||
## Overview
|
||||
|
||||
The example includes:
|
||||
|
||||
1. A server that exposes the process manager functionality via OpenRPC over a Unix socket
|
||||
2. A client that connects to the server and performs various operations
|
||||
3. A mock process manager implementation for testing without requiring actual processes
|
||||
|
||||
## Structure
|
||||
|
||||
- `main.go`: The main entry point that runs both the server and client in the same process
|
||||
- `mock_processmanager.go`: A mock implementation of the `ProcessManagerInterface` that simulates process operations
|
||||
- `example_client.go`: Client implementation demonstrating how to use the OpenRPC client to interact with the process manager
|
||||
|
||||
## How to Run
|
||||
|
||||
```bash
|
||||
# From the heroagent root directory
|
||||
go run ./pkg/processmanager/examples/openrpc
|
||||
```
|
||||
|
||||
The example will:
|
||||
1. Start a server using a Unix socket at `/tmp/process-manager.sock`
|
||||
2. Run a series of client operations against the server
|
||||
3. Display the results of each operation
|
||||
4. Clean up and shut down when complete
|
||||
|
||||
## What It Demonstrates
|
||||
|
||||
This example shows:
|
||||
|
||||
1. How to initialize and start an OpenRPC server for the process manager
|
||||
2. How to create and use an OpenRPC client to interact with the process manager
|
||||
3. How to perform common operations like:
|
||||
- Starting a process (`StartProcess`)
|
||||
- Getting process status (`GetProcessStatus`)
|
||||
- Listing all processes (`ListProcesses`)
|
||||
- Getting process logs (`GetProcessLogs`)
|
||||
- Restarting a process (`RestartProcess`)
|
||||
- Stopping a process (`StopProcess`)
|
||||
- Deleting a process (`DeleteProcess`)
|
||||
4. How to handle the response types returned by each operation
|
||||
5. Proper error handling for RPC operations
|
||||
|
||||
## Notes
|
||||
|
||||
- This example uses a mock process manager implementation for demonstration purposes
|
||||
- In a real application, you would use the actual process manager implementation
|
||||
- The server and client run in the same process for simplicity, but they could be in separate processes
|
||||
- The Unix socket communication can be replaced with other transport mechanisms if needed
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Server
|
||||
|
||||
The server is implemented using the `openrpc.Server` type, which wraps the OpenRPC manager and Unix socket server. It:
|
||||
|
||||
1. Creates a Unix socket at the specified path
|
||||
2. Registers handlers for each RPC method
|
||||
3. Authenticates requests using a secret
|
||||
4. Marshals/unmarshals JSON-RPC requests and responses
|
||||
|
||||
### Client
|
||||
|
||||
The client is implemented using the `openrpc.Client` type, which provides methods for each operation. It:
|
||||
|
||||
1. Connects to the Unix socket
|
||||
2. Sends JSON-RPC requests with the appropriate parameters
|
||||
3. Handles authentication using the secret
|
||||
4. Parses the responses into appropriate result types
|
||||
|
||||
### Mock Process Manager
|
||||
|
||||
The mock process manager implements the `interfaces.ProcessManagerInterface` and simulates:
|
||||
|
||||
1. Process creation and management
|
||||
2. Status tracking
|
||||
3. Log collection
|
||||
4. Error handling
|
||||
|
||||
This allows testing the OpenRPC interface without requiring actual processes to be run.
|
@ -1,149 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
|
||||
)
|
||||
|
||||
// RunClientExample runs a complete example of using the process manager OpenRPC client
|
||||
func RunClientExample(socketPath, secret string) error {
|
||||
// Create a new client
|
||||
client := openrpc.NewClient(socketPath, secret)
|
||||
|
||||
log.Println("🚀 Starting example process...")
|
||||
// Start a process
|
||||
result, err := client.StartProcess("example-process", "sleep 60", true, 0, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start process: %w", err)
|
||||
}
|
||||
log.Printf("Start result: success=%v, message=%s, PID=%d", result.Success, result.Message, result.PID)
|
||||
|
||||
// Wait a bit for the process to start
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
log.Println("📊 Getting process status...")
|
||||
// Get the process status
|
||||
status, err := client.GetProcessStatus("example-process", "json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get process status: %w", err)
|
||||
}
|
||||
printProcessStatus(status.(interfaces.ProcessStatus))
|
||||
|
||||
log.Println("📋 Listing all processes...")
|
||||
// List all processes
|
||||
processList, err := client.ListProcesses("json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list processes: %w", err)
|
||||
}
|
||||
|
||||
// For simplicity in this example, just log that we got a response
|
||||
log.Printf("Got process list response: %T", processList)
|
||||
|
||||
// Try to handle the response in a more robust way
|
||||
switch v := processList.(type) {
|
||||
case []interface{}:
|
||||
log.Printf("Found %d processes", len(v))
|
||||
for i, p := range v {
|
||||
log.Printf("Process %d: %T", i, p)
|
||||
if processMap, ok := p.(map[string]interface{}); ok {
|
||||
log.Printf(" Name: %v", processMap["name"])
|
||||
log.Printf(" Command: %v", processMap["command"])
|
||||
log.Printf(" Status: %v", processMap["status"])
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
log.Printf("Process list is a map with %d entries", len(v))
|
||||
for k, val := range v {
|
||||
log.Printf(" %s: %T", k, val)
|
||||
}
|
||||
default:
|
||||
log.Printf("Process list is of unexpected type: %T", processList)
|
||||
}
|
||||
|
||||
log.Println("📜 Getting process logs...")
|
||||
// Get process logs
|
||||
logResult, err := client.GetProcessLogs("example-process", 10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get process logs: %w", err)
|
||||
}
|
||||
log.Printf("Process logs: success=%v, message=%s", logResult.Success, logResult.Message)
|
||||
log.Printf("Logs:\n%s", logResult.Logs)
|
||||
|
||||
log.Println("🔄 Restarting process...")
|
||||
// Restart the process
|
||||
restartResult, err := client.RestartProcess("example-process")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart process: %w", err)
|
||||
}
|
||||
log.Printf("Restart result: success=%v, message=%s, PID=%d", restartResult.Success, restartResult.Message, restartResult.PID)
|
||||
|
||||
// Wait a bit for the process to restart
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Get the process status after restart
|
||||
status, err = client.GetProcessStatus("example-process", "json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get process status after restart: %w", err)
|
||||
}
|
||||
log.Println("Process status after restart:")
|
||||
printProcessStatus(status.(interfaces.ProcessStatus))
|
||||
|
||||
log.Println("⏹️ Stopping process...")
|
||||
// Stop the process
|
||||
stopResult, err := client.StopProcess("example-process")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop process: %w", err)
|
||||
}
|
||||
log.Printf("Stop result: success=%v, message=%s", stopResult.Success, stopResult.Message)
|
||||
|
||||
// Wait a bit for the process to stop
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Get the process status after stop
|
||||
status, err = client.GetProcessStatus("example-process", "json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get process status after stop: %w", err)
|
||||
}
|
||||
log.Println("Process status after stop:")
|
||||
printProcessStatus(status.(interfaces.ProcessStatus))
|
||||
|
||||
log.Println("🗑️ Deleting process...")
|
||||
// Delete the process
|
||||
deleteResult, err := client.DeleteProcess("example-process")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete process: %w", err)
|
||||
}
|
||||
log.Printf("Delete result: success=%v, message=%s", deleteResult.Success, deleteResult.Message)
|
||||
|
||||
// Try to get the process status after delete (should fail)
|
||||
_, err = client.GetProcessStatus("example-process", "json")
|
||||
if err != nil {
|
||||
log.Printf("Expected error after deletion: %v", err)
|
||||
} else {
|
||||
return fmt.Errorf("process still exists after deletion")
|
||||
}
|
||||
|
||||
log.Println("✅ Example completed successfully!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// printProcessStatus prints the status of a process
|
||||
func printProcessStatus(status interfaces.ProcessStatus) {
|
||||
log.Printf("Process: %s", status.Name)
|
||||
log.Printf(" Command: %s", status.Command)
|
||||
log.Printf(" Status: %s", status.Status)
|
||||
log.Printf(" PID: %d", status.PID)
|
||||
if status.CPUPercent > 0 {
|
||||
log.Printf(" CPU: %.2f%%", status.CPUPercent)
|
||||
}
|
||||
if status.MemoryMB > 0 {
|
||||
log.Printf(" Memory: %.2f MB", status.MemoryMB)
|
||||
}
|
||||
if !status.StartTime.IsZero() {
|
||||
log.Printf(" Started: %s", status.StartTime.Format(time.RFC3339))
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting Process Manager OpenRPC Example")
|
||||
|
||||
// Use /tmp directory for the socket as it's more reliable for Unix sockets
|
||||
// Define the socket path
|
||||
socketPath := "/tmp/process-manager.sock"
|
||||
// Remove the socket file if it already exists
|
||||
if _, err := os.Stat(socketPath); err == nil {
|
||||
if err := os.Remove(socketPath); err != nil {
|
||||
log.Fatalf("Failed to remove existing socket file: %v", err)
|
||||
}
|
||||
}
|
||||
log.Printf("Using socket path: %s", socketPath)
|
||||
|
||||
// Create a mock process manager
|
||||
mockPM := NewMockProcessManager()
|
||||
|
||||
// Create and start the server
|
||||
server, err := openrpc.NewServer(mockPM, socketPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create server: %v", err)
|
||||
}
|
||||
|
||||
// Start the server
|
||||
err = server.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
log.Println("Server started successfully")
|
||||
|
||||
// Set up signal handling for graceful shutdown
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Wait a bit for the server to start
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Run the client example in a goroutine
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
err := RunClientExample(socketPath, mockPM.GetSecret())
|
||||
errChan <- err
|
||||
}()
|
||||
|
||||
// Wait for the client to finish or for a signal
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
log.Printf("Client example failed: %v", err)
|
||||
}
|
||||
case sig := <-sigChan:
|
||||
log.Printf("Received signal: %v", sig)
|
||||
}
|
||||
|
||||
// Stop the server
|
||||
log.Println("Stopping server...")
|
||||
err = server.Stop()
|
||||
if err != nil {
|
||||
log.Printf("Failed to stop server: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Example completed")
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
)
|
||||
|
||||
// MockProcessManager implements the interfaces.ProcessManagerInterface for testing purposes
|
||||
type MockProcessManager struct {
|
||||
processes map[string]*processmanager.ProcessInfo
|
||||
logs map[string][]string
|
||||
mutex sync.RWMutex
|
||||
secret string
|
||||
}
|
||||
|
||||
// Ensure MockProcessManager implements interfaces.ProcessManagerInterface
|
||||
var _ interfaces.ProcessManagerInterface = (*MockProcessManager)(nil)
|
||||
|
||||
// NewMockProcessManager creates a new mock process manager
|
||||
func NewMockProcessManager() *MockProcessManager {
|
||||
return &MockProcessManager{
|
||||
processes: make(map[string]*processmanager.ProcessInfo),
|
||||
logs: make(map[string][]string),
|
||||
secret: "mock-secret",
|
||||
}
|
||||
}
|
||||
|
||||
// StartProcess starts a new process
|
||||
func (m *MockProcessManager) StartProcess(name, command string, logEnabled bool, deadline int, cron, jobID string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if _, exists := m.processes[name]; exists {
|
||||
return fmt.Errorf("process %s already exists", name)
|
||||
}
|
||||
|
||||
process := &processmanager.ProcessInfo{
|
||||
Name: name,
|
||||
Command: command,
|
||||
PID: 12345, // Mock PID
|
||||
Status: processmanager.ProcessStatusRunning,
|
||||
CPUPercent: 0.5,
|
||||
MemoryMB: 10.0,
|
||||
StartTime: time.Now(),
|
||||
LogEnabled: logEnabled,
|
||||
Cron: cron,
|
||||
JobID: jobID,
|
||||
Deadline: deadline,
|
||||
}
|
||||
|
||||
m.processes[name] = process
|
||||
m.logs[name] = []string{
|
||||
fmt.Sprintf("[%s] Process started: %s", time.Now().Format(time.RFC3339), command),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopProcess stops a running process
|
||||
func (m *MockProcessManager) StopProcess(name string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
process, exists := m.processes[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("process %s does not exist", name)
|
||||
}
|
||||
|
||||
if process.Status != processmanager.ProcessStatusRunning {
|
||||
return fmt.Errorf("process %s is not running", name)
|
||||
}
|
||||
|
||||
process.Status = processmanager.ProcessStatusStopped
|
||||
|
||||
m.logs[name] = append(m.logs[name], fmt.Sprintf("[%s] Process stopped", time.Now().Format(time.RFC3339)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartProcess restarts a process
|
||||
func (m *MockProcessManager) RestartProcess(name string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
process, exists := m.processes[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("process %s does not exist", name)
|
||||
}
|
||||
|
||||
process.Status = processmanager.ProcessStatusRunning
|
||||
process.StartTime = time.Now()
|
||||
|
||||
m.logs[name] = append(m.logs[name], fmt.Sprintf("[%s] Process restarted", time.Now().Format(time.RFC3339)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProcess deletes a process
|
||||
func (m *MockProcessManager) DeleteProcess(name string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if _, exists := m.processes[name]; !exists {
|
||||
return fmt.Errorf("process %s does not exist", name)
|
||||
}
|
||||
|
||||
delete(m.processes, name)
|
||||
delete(m.logs, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProcessStatus gets the status of a process
|
||||
func (m *MockProcessManager) GetProcessStatus(name string) (*processmanager.ProcessInfo, error) {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
process, exists := m.processes[name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("process %s does not exist", name)
|
||||
}
|
||||
|
||||
// Return a copy to avoid race conditions
|
||||
return &processmanager.ProcessInfo{
|
||||
Name: process.Name,
|
||||
Command: process.Command,
|
||||
PID: process.PID,
|
||||
Status: process.Status,
|
||||
CPUPercent: process.CPUPercent,
|
||||
MemoryMB: process.MemoryMB,
|
||||
StartTime: process.StartTime,
|
||||
LogEnabled: process.LogEnabled,
|
||||
Cron: process.Cron,
|
||||
JobID: process.JobID,
|
||||
Deadline: process.Deadline,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListProcesses lists all processes
|
||||
func (m *MockProcessManager) ListProcesses() []*processmanager.ProcessInfo {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
processes := make([]*processmanager.ProcessInfo, 0, len(m.processes))
|
||||
for _, process := range m.processes {
|
||||
// Create a copy to avoid race conditions
|
||||
processes = append(processes, &processmanager.ProcessInfo{
|
||||
Name: process.Name,
|
||||
Command: process.Command,
|
||||
PID: process.PID,
|
||||
Status: process.Status,
|
||||
CPUPercent: process.CPUPercent,
|
||||
MemoryMB: process.MemoryMB,
|
||||
StartTime: process.StartTime,
|
||||
LogEnabled: process.LogEnabled,
|
||||
Cron: process.Cron,
|
||||
JobID: process.JobID,
|
||||
Deadline: process.Deadline,
|
||||
})
|
||||
}
|
||||
|
||||
return processes
|
||||
}
|
||||
|
||||
// GetProcessLogs gets the logs for a process
|
||||
func (m *MockProcessManager) GetProcessLogs(name string, maxLines int) (string, error) {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
logs, exists := m.logs[name]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("logs for process %s do not exist", name)
|
||||
}
|
||||
|
||||
if maxLines <= 0 || maxLines > len(logs) {
|
||||
return strings.Join(logs, "\n"), nil
|
||||
}
|
||||
|
||||
return strings.Join(logs[len(logs)-maxLines:], "\n"), nil
|
||||
}
|
||||
|
||||
// SetLogsBasePath sets the base path for logs (mock implementation does nothing)
|
||||
func (m *MockProcessManager) SetLogsBasePath(path string) {
|
||||
// No-op for mock
|
||||
}
|
||||
|
||||
// GetSecret returns the authentication secret
|
||||
func (m *MockProcessManager) GetSecret() string {
|
||||
return m.secret
|
||||
}
|
@ -1,242 +0,0 @@
|
||||
package openrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager/client"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
)
|
||||
|
||||
// Client provides a client for interacting with process manager operations via RPC
|
||||
type Client struct {
|
||||
client.BaseClient
|
||||
secret string
|
||||
}
|
||||
|
||||
// NewClient creates a new client for the process manager API
|
||||
func NewClient(socketPath, secret string) *Client {
|
||||
return &Client{
|
||||
BaseClient: *client.NewClient(socketPath, secret),
|
||||
}
|
||||
}
|
||||
|
||||
// StartProcess starts a new process with the given name and command
|
||||
func (c *Client) StartProcess(name, command string, logEnabled bool, deadline int, cron, jobID string) (interfaces.ProcessStartResult, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
"command": command,
|
||||
"log_enabled": logEnabled,
|
||||
"deadline": deadline,
|
||||
"cron": cron,
|
||||
"job_id": jobID,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return interfaces.ProcessStartResult{}, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.start", paramsJSON, "")
|
||||
if err != nil {
|
||||
return interfaces.ProcessStartResult{}, fmt.Errorf("failed to start process: %w", err)
|
||||
}
|
||||
|
||||
// Convert result to ProcessStartResult
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return interfaces.ProcessStartResult{}, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var startResult interfaces.ProcessStartResult
|
||||
if err := json.Unmarshal(resultJSON, &startResult); err != nil {
|
||||
return interfaces.ProcessStartResult{}, fmt.Errorf("failed to unmarshal process start result: %w", err)
|
||||
}
|
||||
|
||||
return startResult, nil
|
||||
}
|
||||
|
||||
// StopProcess stops a running process
|
||||
func (c *Client) StopProcess(name string) (interfaces.ProcessStopResult, error) {
|
||||
params := map[string]string{
|
||||
"name": name,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return interfaces.ProcessStopResult{}, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.stop", paramsJSON, "")
|
||||
if err != nil {
|
||||
return interfaces.ProcessStopResult{}, fmt.Errorf("failed to stop process: %w", err)
|
||||
}
|
||||
|
||||
// Convert result to ProcessStopResult
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return interfaces.ProcessStopResult{}, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var stopResult interfaces.ProcessStopResult
|
||||
if err := json.Unmarshal(resultJSON, &stopResult); err != nil {
|
||||
return interfaces.ProcessStopResult{}, fmt.Errorf("failed to unmarshal process stop result: %w", err)
|
||||
}
|
||||
|
||||
return stopResult, nil
|
||||
}
|
||||
|
||||
// RestartProcess restarts a process
|
||||
func (c *Client) RestartProcess(name string) (interfaces.ProcessRestartResult, error) {
|
||||
params := map[string]string{
|
||||
"name": name,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return interfaces.ProcessRestartResult{}, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.restart", paramsJSON, "")
|
||||
if err != nil {
|
||||
return interfaces.ProcessRestartResult{}, fmt.Errorf("failed to restart process: %w", err)
|
||||
}
|
||||
|
||||
// Convert result to ProcessRestartResult
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return interfaces.ProcessRestartResult{}, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var restartResult interfaces.ProcessRestartResult
|
||||
if err := json.Unmarshal(resultJSON, &restartResult); err != nil {
|
||||
return interfaces.ProcessRestartResult{}, fmt.Errorf("failed to unmarshal process restart result: %w", err)
|
||||
}
|
||||
|
||||
return restartResult, nil
|
||||
}
|
||||
|
||||
// DeleteProcess deletes a process from the manager
|
||||
func (c *Client) DeleteProcess(name string) (interfaces.ProcessDeleteResult, error) {
|
||||
params := map[string]string{
|
||||
"name": name,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return interfaces.ProcessDeleteResult{}, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.delete", paramsJSON, "")
|
||||
if err != nil {
|
||||
return interfaces.ProcessDeleteResult{}, fmt.Errorf("failed to delete process: %w", err)
|
||||
}
|
||||
|
||||
// Convert result to ProcessDeleteResult
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return interfaces.ProcessDeleteResult{}, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var deleteResult interfaces.ProcessDeleteResult
|
||||
if err := json.Unmarshal(resultJSON, &deleteResult); err != nil {
|
||||
return interfaces.ProcessDeleteResult{}, fmt.Errorf("failed to unmarshal process delete result: %w", err)
|
||||
}
|
||||
|
||||
return deleteResult, nil
|
||||
}
|
||||
|
||||
// GetProcessStatus gets the status of a process
|
||||
func (c *Client) GetProcessStatus(name string, format string) (interface{}, error) {
|
||||
params := map[string]string{
|
||||
"name": name,
|
||||
"format": format,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.status", paramsJSON, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get process status: %w", err)
|
||||
}
|
||||
|
||||
if format == "text" {
|
||||
// For text format, return the raw result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// For JSON format, convert to ProcessStatus
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var statusResult interfaces.ProcessStatus
|
||||
if err := json.Unmarshal(resultJSON, &statusResult); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal process status result: %w", err)
|
||||
}
|
||||
|
||||
return statusResult, nil
|
||||
}
|
||||
|
||||
// ListProcesses lists all processes
|
||||
func (c *Client) ListProcesses(format string) (interface{}, error) {
|
||||
params := map[string]string{
|
||||
"format": format,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.list", paramsJSON, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list processes: %w", err)
|
||||
}
|
||||
|
||||
if format == "text" {
|
||||
// For text format, return the raw result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// For JSON format, convert to []ProcessStatus
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var listResult []interfaces.ProcessStatus
|
||||
if err := json.Unmarshal(resultJSON, &listResult); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal process list result: %w", err)
|
||||
}
|
||||
|
||||
return listResult, nil
|
||||
}
|
||||
|
||||
// GetProcessLogs gets logs for a process
|
||||
func (c *Client) GetProcessLogs(name string, lines int) (interfaces.ProcessLogResult, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
"lines": lines,
|
||||
}
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return interfaces.ProcessLogResult{}, fmt.Errorf("failed to marshal parameters: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Request("process.log", paramsJSON, "")
|
||||
if err != nil {
|
||||
return interfaces.ProcessLogResult{}, fmt.Errorf("failed to get process logs: %w", err)
|
||||
}
|
||||
|
||||
// Convert result to ProcessLogResult
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return interfaces.ProcessLogResult{}, fmt.Errorf("failed to marshal result: %w", err)
|
||||
}
|
||||
|
||||
var logResult interfaces.ProcessLogResult
|
||||
if err := json.Unmarshal(resultJSON, &logResult); err != nil {
|
||||
return interfaces.ProcessLogResult{}, fmt.Errorf("failed to unmarshal process log result: %w", err)
|
||||
}
|
||||
|
||||
return logResult, nil
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
package openrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
)
|
||||
|
||||
// Handler implements the OpenRPC handlers for process manager operations
|
||||
type Handler struct {
|
||||
processManager interfaces.ProcessManagerInterface
|
||||
}
|
||||
|
||||
// NewHandler creates a new RPC handler for process manager operations
|
||||
func NewHandler(processManager interfaces.ProcessManagerInterface) *Handler {
|
||||
return &Handler{
|
||||
processManager: processManager,
|
||||
}
|
||||
}
|
||||
|
||||
// GetHandlers returns a map of RPC handlers for the OpenRPC manager
|
||||
func (h *Handler) GetHandlers() map[string]openrpcmanager.RPCHandler {
|
||||
return map[string]openrpcmanager.RPCHandler{
|
||||
"process.start": h.handleProcessStart,
|
||||
"process.stop": h.handleProcessStop,
|
||||
"process.restart": h.handleProcessRestart,
|
||||
"process.delete": h.handleProcessDelete,
|
||||
"process.status": h.handleProcessStatus,
|
||||
"process.list": h.handleProcessList,
|
||||
"process.log": h.handleProcessLog,
|
||||
}
|
||||
}
|
||||
|
||||
// handleProcessStart handles the process.start method
|
||||
func (h *Handler) handleProcessStart(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
LogEnabled bool `json:"log_enabled"`
|
||||
Deadline int `json:"deadline"`
|
||||
Cron string `json:"cron"`
|
||||
JobID string `json:"job_id"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
err := h.processManager.StartProcess(
|
||||
request.Name,
|
||||
request.Command,
|
||||
request.LogEnabled,
|
||||
request.Deadline,
|
||||
request.Cron,
|
||||
request.JobID,
|
||||
)
|
||||
|
||||
result := interfaces.ProcessStartResult{
|
||||
Success: err == nil,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Get the process info to return the PID
|
||||
procInfo, err := h.processManager.GetProcessStatus(request.Name)
|
||||
if err != nil {
|
||||
result.Message = fmt.Sprintf("Process started but failed to get status: %v", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result.PID = procInfo.PID
|
||||
result.Message = fmt.Sprintf("Process '%s' started successfully with PID %d", request.Name, procInfo.PID)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleProcessStop handles the process.stop method
|
||||
func (h *Handler) handleProcessStop(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
err := h.processManager.StopProcess(request.Name)
|
||||
|
||||
result := interfaces.ProcessStopResult{
|
||||
Success: err == nil,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
} else {
|
||||
result.Message = fmt.Sprintf("Process '%s' stopped successfully", request.Name)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleProcessRestart handles the process.restart method
|
||||
func (h *Handler) handleProcessRestart(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
err := h.processManager.RestartProcess(request.Name)
|
||||
|
||||
result := interfaces.ProcessRestartResult{
|
||||
Success: err == nil,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Get the process info to return the PID
|
||||
procInfo, err := h.processManager.GetProcessStatus(request.Name)
|
||||
if err != nil {
|
||||
result.Message = fmt.Sprintf("Process restarted but failed to get status: %v", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result.PID = procInfo.PID
|
||||
result.Message = fmt.Sprintf("Process '%s' restarted successfully with PID %d", request.Name, procInfo.PID)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleProcessDelete handles the process.delete method
|
||||
func (h *Handler) handleProcessDelete(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
err := h.processManager.DeleteProcess(request.Name)
|
||||
|
||||
result := interfaces.ProcessDeleteResult{
|
||||
Success: err == nil,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
} else {
|
||||
result.Message = fmt.Sprintf("Process '%s' deleted successfully", request.Name)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleProcessStatus handles the process.status method
|
||||
func (h *Handler) handleProcessStatus(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
procInfo, err := h.processManager.GetProcessStatus(request.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get process status: %w", err)
|
||||
}
|
||||
|
||||
if request.Format == "text" {
|
||||
// Format as text using the processmanager's FormatProcessInfo function
|
||||
textResult, err := processmanager.FormatProcessInfo(procInfo, "text")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format process info: %w", err)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"text": textResult,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Default to JSON format
|
||||
return convertProcessInfoToStatus(procInfo), nil
|
||||
}
|
||||
|
||||
// handleProcessList handles the process.list method
|
||||
func (h *Handler) handleProcessList(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Format string `json:"format"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
processes := h.processManager.ListProcesses()
|
||||
|
||||
if request.Format == "text" {
|
||||
// Format as text using the processmanager's FormatProcessList function
|
||||
textResult, err := processmanager.FormatProcessList(processes, "text")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format process list: %w", err)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"text": textResult,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Default to JSON format
|
||||
result := make([]interfaces.ProcessStatus, 0, len(processes))
|
||||
for _, proc := range processes {
|
||||
result = append(result, convertProcessInfoToStatus(proc))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// handleProcessLog handles the process.log method
|
||||
func (h *Handler) handleProcessLog(params json.RawMessage) (interface{}, error) {
|
||||
var request struct {
|
||||
Name string `json:"name"`
|
||||
Lines int `json:"lines"`
|
||||
}
|
||||
if err := json.Unmarshal(params, &request); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
logs, err := h.processManager.GetProcessLogs(request.Name, request.Lines)
|
||||
|
||||
result := interfaces.ProcessLogResult{
|
||||
Success: err == nil,
|
||||
Message: "",
|
||||
Logs: logs,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
result.Logs = ""
|
||||
} else {
|
||||
result.Message = fmt.Sprintf("Retrieved %d lines of logs for process '%s'", request.Lines, request.Name)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertProcessInfoToStatus converts a ProcessInfo to a ProcessStatus
|
||||
func convertProcessInfoToStatus(info *processmanager.ProcessInfo) interfaces.ProcessStatus {
|
||||
return interfaces.ProcessStatus{
|
||||
Name: info.Name,
|
||||
Command: info.Command,
|
||||
PID: info.PID,
|
||||
Status: string(info.Status),
|
||||
CPUPercent: info.CPUPercent,
|
||||
MemoryMB: info.MemoryMB,
|
||||
StartTime: info.StartTime,
|
||||
LogEnabled: info.LogEnabled,
|
||||
Cron: info.Cron,
|
||||
JobID: info.JobID,
|
||||
Deadline: info.Deadline,
|
||||
Error: info.Error,
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package openrpc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProcessManagerRPC(t *testing.T) {
|
||||
// Create a temporary directory for the socket
|
||||
tempDir, err := os.MkdirTemp("", "processmanager-rpc-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create a socket path
|
||||
socketPath := filepath.Join(tempDir, "process-manager.sock")
|
||||
|
||||
// Create a process manager
|
||||
pm := processmanager.NewProcessManager()
|
||||
pm.SetLogsBasePath(filepath.Join(tempDir, "logs"))
|
||||
|
||||
// Create and start the server
|
||||
server, err := NewServer(pm, socketPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start the server in a goroutine
|
||||
go func() {
|
||||
err := server.Start()
|
||||
if err != nil {
|
||||
t.Logf("Error starting server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the server to start
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Create a client
|
||||
client := NewClient(socketPath, "")
|
||||
|
||||
// Test process start
|
||||
t.Run("StartProcess", func(t *testing.T) {
|
||||
result, err := client.StartProcess("test-process", "echo 'Hello, World!'", true, 0, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Success)
|
||||
assert.NotEmpty(t, result.Message)
|
||||
assert.NotZero(t, result.PID)
|
||||
})
|
||||
|
||||
// Test process status
|
||||
t.Run("GetProcessStatus", func(t *testing.T) {
|
||||
status, err := client.GetProcessStatus("test-process", "json")
|
||||
require.NoError(t, err)
|
||||
|
||||
processStatus, ok := status.(interfaces.ProcessStatus)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "test-process", processStatus.Name)
|
||||
assert.Equal(t, "echo 'Hello, World!'", processStatus.Command)
|
||||
})
|
||||
|
||||
// Test process list
|
||||
t.Run("ListProcesses", func(t *testing.T) {
|
||||
processList, err := client.ListProcesses("json")
|
||||
require.NoError(t, err)
|
||||
|
||||
processes, ok := processList.([]interfaces.ProcessStatus)
|
||||
require.True(t, ok)
|
||||
assert.NotEmpty(t, processes)
|
||||
|
||||
// Find our test process
|
||||
found := false
|
||||
for _, proc := range processes {
|
||||
if proc.Name == "test-process" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
})
|
||||
|
||||
// Test process logs
|
||||
t.Run("GetProcessLogs", func(t *testing.T) {
|
||||
// Wait a bit for logs to be generated
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
logs, err := client.GetProcessLogs("test-process", 10)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, logs.Success)
|
||||
})
|
||||
|
||||
// Test process restart
|
||||
t.Run("RestartProcess", func(t *testing.T) {
|
||||
result, err := client.RestartProcess("test-process")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Success)
|
||||
assert.NotEmpty(t, result.Message)
|
||||
})
|
||||
|
||||
// Test process stop
|
||||
t.Run("StopProcess", func(t *testing.T) {
|
||||
result, err := client.StopProcess("test-process")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Success)
|
||||
assert.NotEmpty(t, result.Message)
|
||||
})
|
||||
|
||||
// Test process delete
|
||||
t.Run("DeleteProcess", func(t *testing.T) {
|
||||
result, err := client.DeleteProcess("test-process")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Success)
|
||||
assert.NotEmpty(t, result.Message)
|
||||
})
|
||||
|
||||
// Stop the server
|
||||
err = server.Stop()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestProcessManagerRPCWithMock tests the RPC interface with a mock process manager
|
||||
func TestProcessManagerRPCWithMock(t *testing.T) {
|
||||
// This test would use a mock implementation of the ProcessManagerInterface
|
||||
// to test the RPC layer without actually starting real processes
|
||||
t.Skip("Mock implementation test to be added")
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package openrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager"
|
||||
)
|
||||
|
||||
// LoadSchema loads the OpenRPC schema from the embedded JSON file
|
||||
func LoadSchema() (openrpcmanager.OpenRPCSchema, error) {
|
||||
// Get the absolute path to the schema.json file
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
schemaPath := filepath.Join(filepath.Dir(filename), "schema.json")
|
||||
|
||||
// Read the schema file
|
||||
schemaBytes, err := os.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
return openrpcmanager.OpenRPCSchema{}, fmt.Errorf("failed to read schema file: %w", err)
|
||||
}
|
||||
|
||||
// Unmarshal the schema
|
||||
var schema openrpcmanager.OpenRPCSchema
|
||||
if err := json.Unmarshal(schemaBytes, &schema); err != nil {
|
||||
return openrpcmanager.OpenRPCSchema{}, fmt.Errorf("failed to unmarshal schema: %w", err)
|
||||
}
|
||||
|
||||
return schema, nil
|
||||
}
|
@ -1,333 +0,0 @@
|
||||
{
|
||||
"openrpc": "1.2.6",
|
||||
"info": {
|
||||
"title": "Process Manager API",
|
||||
"version": "1.0.0",
|
||||
"description": "API for managing and monitoring processes"
|
||||
},
|
||||
"methods": [
|
||||
{
|
||||
"name": "process.start",
|
||||
"description": "Start a new process with the given name and command",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "command",
|
||||
"description": "Command to execute",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "log_enabled",
|
||||
"description": "Whether to enable logging for the process",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"description": "Deadline in seconds after which the process will be automatically stopped (0 for no deadline)",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cron",
|
||||
"description": "Cron expression for scheduled execution",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "job_id",
|
||||
"description": "Optional job ID for tracking",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process start result",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"pid": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.stop",
|
||||
"description": "Stop a running process",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process to stop",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process stop result",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.restart",
|
||||
"description": "Restart a process",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process to restart",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process restart result",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"pid": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.delete",
|
||||
"description": "Delete a process from the manager",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process to delete",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process delete result",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.status",
|
||||
"description": "Get the status of a process",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"description": "Output format (json or text)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["json", "text"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process status information",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"pid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["running", "stopped", "failed", "completed"]
|
||||
},
|
||||
"cpu_percent": {
|
||||
"type": "number"
|
||||
},
|
||||
"memory_mb": {
|
||||
"type": "number"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"log_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
},
|
||||
"job_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"deadline": {
|
||||
"type": "integer"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.list",
|
||||
"description": "List all processes",
|
||||
"params": [
|
||||
{
|
||||
"name": "format",
|
||||
"description": "Output format (json or text)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["json", "text"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "List of processes",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"pid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["running", "stopped", "failed", "completed"]
|
||||
},
|
||||
"cpu_percent": {
|
||||
"type": "number"
|
||||
},
|
||||
"memory_mb": {
|
||||
"type": "number"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"log_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
},
|
||||
"job_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"deadline": {
|
||||
"type": "integer"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "process.log",
|
||||
"description": "Get logs for a process",
|
||||
"params": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the process",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lines",
|
||||
"description": "Number of log lines to retrieve",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"description": "Process logs",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"logs": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package openrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
|
||||
)
|
||||
|
||||
// Server represents the Process Manager OpenRPC server
|
||||
type Server struct {
|
||||
processManager interfaces.ProcessManagerInterface
|
||||
socketPath string
|
||||
openRPCMgr *openrpcmanager.OpenRPCManager
|
||||
unixServer *openrpcmanager.UnixServer
|
||||
isRunning bool
|
||||
}
|
||||
|
||||
// NewServer creates a new Process Manager OpenRPC server
|
||||
func NewServer(processManager interfaces.ProcessManagerInterface, socketPath string) (*Server, error) {
|
||||
// Ensure the directory for the socket exists
|
||||
socketDir := filepath.Dir(socketPath)
|
||||
if err := os.MkdirAll(socketDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create socket directory: %w", err)
|
||||
}
|
||||
|
||||
// Load the OpenRPC schema
|
||||
schema, err := LoadSchema()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load OpenRPC schema: %w", err)
|
||||
}
|
||||
|
||||
// Create a new handler - no authentication now
|
||||
handler := NewHandler(processManager)
|
||||
|
||||
// Create a new OpenRPC manager - using empty string for auth (no authentication)
|
||||
openRPCMgr, err := openrpcmanager.NewOpenRPCManager(schema, handler.GetHandlers(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create OpenRPC manager: %w", err)
|
||||
}
|
||||
|
||||
// Create a new Unix server
|
||||
unixServer, err := openrpcmanager.NewUnixServer(openRPCMgr, socketPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Unix server: %w", err)
|
||||
}
|
||||
|
||||
return &Server{
|
||||
processManager: processManager,
|
||||
socketPath: socketPath,
|
||||
openRPCMgr: openRPCMgr,
|
||||
unixServer: unixServer,
|
||||
isRunning: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start starts the Process Manager OpenRPC server
|
||||
func (s *Server) Start() error {
|
||||
if s.isRunning {
|
||||
return fmt.Errorf("server is already running")
|
||||
}
|
||||
|
||||
// Start the Unix server
|
||||
if err := s.unixServer.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start Unix server: %w", err)
|
||||
}
|
||||
|
||||
s.isRunning = true
|
||||
log.Printf("Process Manager OpenRPC server started on socket: %s", s.socketPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the Process Manager OpenRPC server
|
||||
func (s *Server) Stop() error {
|
||||
if !s.isRunning {
|
||||
return fmt.Errorf("server is not running")
|
||||
}
|
||||
|
||||
// Stop the Unix server
|
||||
if err := s.unixServer.Stop(); err != nil {
|
||||
return fmt.Errorf("failed to stop Unix server: %w", err)
|
||||
}
|
||||
|
||||
s.isRunning = false
|
||||
log.Printf("Process Manager OpenRPC server stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRunning returns whether the server is running
|
||||
func (s *Server) IsRunning() bool {
|
||||
return s.isRunning
|
||||
}
|
||||
|
||||
// SocketPath returns the socket path
|
||||
func (s *Server) SocketPath() string {
|
||||
return s.socketPath
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
|
||||
)
|
||||
|
||||
// ProcessManagerInterface defines the interface for process management operations
|
||||
type ProcessManagerInterface interface {
|
||||
// StartProcess starts a new process with the given name and command
|
||||
StartProcess(name, command string, logEnabled bool, deadline int, cron, jobID string) error
|
||||
|
||||
// StopProcess stops a running process
|
||||
StopProcess(name string) error
|
||||
|
||||
// RestartProcess restarts a process
|
||||
RestartProcess(name string) error
|
||||
|
||||
// DeleteProcess removes a process from the manager
|
||||
DeleteProcess(name string) error
|
||||
|
||||
// GetProcessStatus returns the status of a process
|
||||
GetProcessStatus(name string) (*processmanager.ProcessInfo, error)
|
||||
|
||||
// ListProcesses returns a list of all processes
|
||||
ListProcesses() []*processmanager.ProcessInfo
|
||||
|
||||
// GetProcessLogs returns the logs for a specific process
|
||||
GetProcessLogs(name string, lines int) (string, error)
|
||||
}
|
||||
|
||||
// ProcessStartResult represents the result of starting a process
|
||||
type ProcessStartResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PID int32 `json:"pid"`
|
||||
}
|
||||
|
||||
// ProcessStopResult represents the result of stopping a process
|
||||
type ProcessStopResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ProcessRestartResult represents the result of restarting a process
|
||||
type ProcessRestartResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PID int32 `json:"pid"`
|
||||
}
|
||||
|
||||
// ProcessDeleteResult represents the result of deleting a process
|
||||
type ProcessDeleteResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ProcessLogResult represents the result of getting process logs
|
||||
type ProcessLogResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Logs string `json:"logs"`
|
||||
}
|
||||
|
||||
// ProcessStatus represents detailed information about a process
|
||||
type ProcessStatus struct {
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
PID int32 `json:"pid"`
|
||||
Status string `json:"status"`
|
||||
CPUPercent float64 `json:"cpu_percent"`
|
||||
MemoryMB float64 `json:"memory_mb"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
LogEnabled bool `json:"log_enabled"`
|
||||
Cron string `json:"cron,omitempty"`
|
||||
JobID string `json:"job_id,omitempty"`
|
||||
Deadline int `json:"deadline,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
90
pkg/system/processmanager/README.md
Normal file
90
pkg/system/processmanager/README.md
Normal file
@ -0,0 +1,90 @@
|
||||
# Process Manager
|
||||
|
||||
The Process Manager (`processmanager`) is a Go package responsible for starting, stopping, monitoring, and managing system processes. It provides a robust way to handle long-running tasks, scheduled jobs (via cron expressions), and general command execution with logging capabilities.
|
||||
|
||||
It can be interacted with via `heroscript` commands, typically through a telnet interface to a `ProcessManagerHandler` server.
|
||||
|
||||
## Features
|
||||
|
||||
- **Start and Stop Processes**: Launch new processes and terminate existing ones.
|
||||
- **Restart Processes**: Convenience function to stop, delete, and restart a process.
|
||||
- **Delete Processes**: Remove a process definition and its associated logs.
|
||||
- **Process Monitoring**: Tracks process status (running, stopped, failed, completed), PID, CPU, and memory usage.
|
||||
- **Logging**: Captures stdout and stderr for each managed process. Logs are stored in a configurable base directory, with each process having its own subdirectory.
|
||||
- **Deadline Management**: Processes can be started with a deadline, after which they will be automatically stopped.
|
||||
- **Cron Scheduling**: Processes can be scheduled to run based on cron expressions (though the actual scheduling logic might be external and trigger `process.start`).
|
||||
- **Heroscript Integration**: Exposes functionality through `heroscript` actions.
|
||||
|
||||
## Direct Usage in Go (Conceptual)
|
||||
|
||||
While primarily designed for `heroscript` interaction, the core `ProcessManager` can be used directly in Go applications:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/system/processmanager"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pm := processmanager.NewProcessManager()
|
||||
|
||||
// Configure logs path (optional, defaults to a temp directory)
|
||||
// pm.SetLogsBasePath("./my_process_logs")
|
||||
|
||||
processName := "my_go_task"
|
||||
command := "sleep 10 && echo 'Task finished'"
|
||||
|
||||
fmt.Printf("Starting process: %s\n", processName)
|
||||
err := pm.StartProcess(processName, command, true, 0, "", "") // name, command, logEnabled, deadline, cron, jobID
|
||||
if err != nil {
|
||||
fmt.Printf("Error starting process: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Process '%s' started. Waiting for it to complete or monitoring...\n", processName)
|
||||
|
||||
// Monitor status (example)
|
||||
for i := 0; i < 12; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
status, err := pm.GetProcessStatus(processName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting status: %v\n", err)
|
||||
break
|
||||
}
|
||||
fmt.Printf("Status of '%s': %s (PID: %d, CPU: %.2f%%, Mem: %.2fMB)\n",
|
||||
status.Name, status.Status, status.PID, status.CPUPercent, status.MemoryMB)
|
||||
|
||||
if status.Status == processmanager.ProcessStatusCompleted || status.Status == processmanager.ProcessStatusFailed {
|
||||
fmt.Printf("Process '%s' ended with status: %s\n", processName, status.Status)
|
||||
if status.Error != "" {
|
||||
fmt.Printf("Error: %s\n", status.Error)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve logs
|
||||
logs, err := pm.GetProcessLogs(processName, 100)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting logs: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("\n--- Logs for %s ---\n%s\n-------------------\n", processName, logs)
|
||||
}
|
||||
|
||||
// Stop (if still running) and delete
|
||||
fmt.Printf("Stopping process: %s\n", processName)
|
||||
pm.StopProcess(processName) // Ignoring error for simplicity if already stopped
|
||||
|
||||
fmt.Printf("Deleting process: %s\n", processName)
|
||||
err = pm.DeleteProcess(processName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting process: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Println("Done.")
|
||||
}
|
||||
```
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user