Compare commits

..

2 Commits

Author SHA1 Message Date
92b9c356b8 ... 2025-05-23 15:11:03 +04:00
c86165f88c ... 2025-05-23 14:05:09 +04:00
92 changed files with 450 additions and 3109 deletions

11
go.mod
View File

@ -1,4 +1,4 @@
module github.com/freeflowuniverse/heroagent
module git.ourworld.tf/herocode/heroagent
go 1.23.0
@ -10,12 +10,10 @@ require (
github.com/emersion/go-smtp v0.21.3
github.com/emersion/go-webdav v0.6.0
github.com/gofiber/fiber/v2 v2.52.6
github.com/gofiber/swagger v1.1.1
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/shirou/gopsutil/v3 v3.24.5
github.com/stretchr/testify v1.10.0
github.com/tidwall/redcon v1.6.2
github.com/yuin/goldmark v1.7.8
golang.org/x/crypto v0.36.0
@ -37,7 +35,6 @@ require (
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@ -49,7 +46,6 @@ require (
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/template/jet/v2 v2.1.11 // indirect
github.com/gofiber/utils v1.1.0 // indirect
@ -69,9 +65,8 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/metoro-io/mcp-golang v0.8.0 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9 // indirect
github.com/openai/openai-go v0.1.0-beta.9 // indirect
github.com/pb33f/libopenapi v0.21.8 // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -116,3 +111,5 @@ require (
modernc.org/sqlite v1.23.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
replace github.com/openaiproxy/openaiproxy-go => github.com/sashabaranov/go-openai v1.20.0

351
go.sum
View File

@ -1,351 +0,0 @@
9fans.net/go v0.0.2 h1:RYM6lWITV8oADrwLfdzxmt8ucfW6UtP9v1jg4qAbqts=
9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=
github.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
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.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
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 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
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 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
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 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
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 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
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 h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
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 h1:rbnBUEXvUM2Zk65Him13LwJOBY0ISltgqM5k6T5Lq4w=
github.com/emersion/go-webdav v0.6.0/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ=
github.com/fhs/mux9p v0.3.1 h1:x1UswUWZoA9vrA02jfisndCq3xQm+wrQUxUt5N99E08=
github.com/fhs/mux9p v0.3.1/go.mod h1:F4hwdenmit0WDoNVT2VMWlLJrBVCp/8UhzJa7scfjEQ=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
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.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA=
github.com/gofiber/swagger v1.1.1/go.mod h1:vtvY/sQAMc/lGTUCg0lqmBL7Ht9O7uzChpbvJeJQINw=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/jet/v2 v2.1.11 h1:irnR6GeM2SGTdvg7dxFts564a5evApMUKpOn3mt/RNE=
github.com/gofiber/template/jet/v2 v2.1.11/go.mod h1:Kb1oBdrx90oEvP71MDTUB9k+IWRF082Td5OPW7SoUMQ=
github.com/gofiber/template/pug/v2 v2.1.8 h1:SNs0wE96S5P5Ggb54jNOtlP5Qads63gR31PvBBEgNns=
github.com/gofiber/template/pug/v2 v2.1.8/go.mod h1:e0Sg0YBMtC+RQMRm0swaAvqIBDJmhhDIKfFFtQRjvlQ=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
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 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
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.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
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 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knusbaum/go9p v1.18.0 h1:/Y67RNvNKX1ZV1IOdnO1lIetiF0X+CumOyvEc0011GI=
github.com/knusbaum/go9p v1.18.0/go.mod h1:HtMoJKqZUe1Oqag5uJqG5RKQ9gWPSP+wolsnLLv44r8=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
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.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/metoro-io/mcp-golang v0.8.0 h1:DkigHa3w7WwMFomcEz5wiMDX94DsvVm/3mCV3d1obnc=
github.com/metoro-io/mcp-golang v0.8.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9 h1:ABpubc5yU/3ejee2GgRrbFta81SG/d7bQbB8mIdP0Xo=
github.com/openaiproxy/openaiproxy-go v0.1.0-beta.9/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
github.com/pb33f/libopenapi v0.21.8 h1:Fi2dAogMwC6av/5n3YIo7aMOGBZH/fBMO4OnzFB3dQA=
github.com/pb33f/libopenapi v0.21.8/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU=
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
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 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
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 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
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 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/speakeasy-api/jsonpath v0.6.1 h1:FWbuCEPGaJTVB60NZg2orcYHGZlelbNJAcIk/JGnZvo=
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 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM=
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 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
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 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow=
github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
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 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
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 h1:dLuIF2kX9c+KknGJUdJi1Il1SDiTSK158/BB9kdgAew=
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 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
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 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
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 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
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/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
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-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
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/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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-20210420072515-93ed5bcd2bfe/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 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -11,7 +11,7 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/mycelium_client"
"git.ourworld.tf/herocode/heroagent/pkg/mycelium_client"
)
type config struct {

View File

@ -8,7 +8,7 @@ import (
"os"
"time"
"github.com/freeflowuniverse/heroagent/pkg/mycelium_client"
"git.ourworld.tf/herocode/heroagent/pkg/mycelium_client"
)
func main() {

View File

@ -14,7 +14,7 @@ Dedupestor is a Go package that provides a key-value store with deduplication ba
```go
import (
"github.com/freeflowuniverse/heroagent/pkg/dedupestor"
"git.ourworld.tf/herocode/heroagent/pkg/dedupestor"
)
// Create a new dedupe store

View File

@ -7,8 +7,8 @@ import (
"errors"
"path/filepath"
"github.com/freeflowuniverse/heroagent/pkg/data/ourdb"
"github.com/freeflowuniverse/heroagent/pkg/data/radixtree"
"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/data/radixtree"
)
// MaxValueSize is the maximum allowed size for values (1MB)

View File

@ -18,7 +18,7 @@ The DocTree package provides functionality for managing collections of markdown
### Creating a DocTree
```go
import "github.com/freeflowuniverse/heroagent/pkg/doctree"
import "git.ourworld.tf/herocode/heroagent/pkg/doctree"
// Create a new DocTree with a path and name
dt, err := doctree.New("/path/to/collection", "My Collection")

View File

@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// Collection represents a collection of markdown pages and files

View File

@ -5,7 +5,7 @@ import (
"context"
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
"github.com/redis/go-redis/v9"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"

View File

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// Global variable to track the current DocTree instance

View File

@ -34,7 +34,7 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/ourdb"
)
func main() {
@ -78,7 +78,7 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/ourdb"
)
func main() {

View File

@ -4,7 +4,7 @@ package radixtree
import (
"errors"
"github.com/freeflowuniverse/heroagent/pkg/data/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb"
)
// Node represents a node in the radix tree

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/gofiber/fiber/v2"
)
@ -77,7 +77,7 @@ func (h *AdminHandler) getProcessStatsJSON(c *fiber.Ctx) error {
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"error": "Failed to get process stats: " + err.Error(),
"error": "Failed to get process stats: " + err.Error(),
})
}

View File

@ -3,7 +3,7 @@ package api
import (
"time"
"github.com/freeflowuniverse/heroagent/pkg/sal/executor"
"git.ourworld.tf/herocode/heroagent/pkg/sal/executor"
"github.com/gofiber/fiber/v2"
)

View File

@ -7,9 +7,9 @@ import (
"strconv"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
"github.com/gofiber/fiber/v2"
)
@ -102,26 +102,26 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
if !ok {
// Try to handle the result as a map or other structure
h.logger.Printf("Warning: unexpected result type from ListProcesses, trying alternative parsing")
// Try to convert the result to JSON and then parse it
resultJSON, err := json.Marshal(result)
if err != nil {
h.logger.Printf("Error marshaling result to JSON: %v", err)
return nil, fmt.Errorf("failed to marshal result: %w", err)
}
var processStatuses []interfaces.ProcessStatus
if err := json.Unmarshal(resultJSON, &processStatuses); err != nil {
h.logger.Printf("Error unmarshaling result to ProcessStatus: %v", err)
return nil, fmt.Errorf("failed to unmarshal process list result: %w", err)
}
// Convert to display info format
displayInfoList := make([]ProcessDisplayInfo, 0, len(processStatuses))
for _, proc := range processStatuses {
// Calculate uptime based on start time
uptime := formatUptime(time.Since(proc.StartTime))
displayInfo := ProcessDisplayInfo{
ID: fmt.Sprintf("%d", proc.PID),
Name: proc.Name,
@ -133,7 +133,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
}
displayInfoList = append(displayInfoList, displayInfo)
}
// Debug: Log the number of processes
h.logger.Printf("Found %d processes", len(displayInfoList))
return displayInfoList, nil
@ -144,7 +144,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
for _, proc := range processStatuses {
// Calculate uptime based on start time
uptime := formatUptime(time.Since(proc.StartTime))
displayInfo := ProcessDisplayInfo{
ID: fmt.Sprintf("%d", proc.PID),
Name: proc.Name,
@ -276,7 +276,7 @@ func (h *ServiceHandler) stopService(c *fiber.Ctx) error {
"error": fmt.Sprintf("Failed to stop service: %v", err),
})
}
// Check if the result indicates success
if !result.Success {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
@ -331,7 +331,7 @@ func (h *ServiceHandler) restartService(c *fiber.Ctx) error {
"error": fmt.Sprintf("Failed to restart service: %v", err),
})
}
// Check if the result indicates success
if !result.Success {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
@ -382,7 +382,7 @@ func (h *ServiceHandler) deleteService(c *fiber.Ctx) error {
"error": fmt.Sprintf("Failed to delete service: %v", err),
})
}
// Check if the result indicates success
if !result.Success {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{

View File

@ -12,16 +12,16 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/heroagent/api"
"github.com/freeflowuniverse/heroagent/pkg/heroagent/handlers"
"github.com/freeflowuniverse/heroagent/pkg/heroagent/pages"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"github.com/freeflowuniverse/heroagent/pkg/sal/executor"
"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/heroagent/api"
"git.ourworld.tf/herocode/heroagent/pkg/heroagent/handlers"
"git.ourworld.tf/herocode/heroagent/pkg/heroagent/pages"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/sal/executor"
"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
// "github.com/freeflowuniverse/heroagent/pkg/vfs/interfaces"
// "github.com/freeflowuniverse/heroagent/pkg/vfs/interfaces/mock"
// "git.ourworld.tf/herocode/heroagent/pkg/vfs/interfaces"
// "git.ourworld.tf/herocode/heroagent/pkg/vfs/interfaces/mock"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
@ -239,7 +239,7 @@ func (hl *HeroLauncher) GetUptime() string {
func (hl *HeroLauncher) startProcessManager() error {
_, filename, _, _ := runtime.Caller(0)
projectRoot := filepath.Join(filepath.Dir(filename), "../..")
processManagerPath := filepath.Join(projectRoot, "cmd/processmanager/main.go")
processManagerPath := filepath.Join(projectRoot, "pkg/processmanager/examples/openrpc/main.go")
log.Printf("Starting process manager from: %s", processManagerPath)

View File

@ -5,7 +5,7 @@ import (
"log"
"strconv" // Added strconv for JobID parsing
"github.com/freeflowuniverse/heroagent/pkg/herojobs"
"git.ourworld.tf/herocode/heroagent/pkg/herojobs"
"github.com/gofiber/fiber/v2"
)

View File

@ -10,7 +10,7 @@ import (
"net/http/httptest"
"testing"
"github.com/freeflowuniverse/heroagent/pkg/herojobs"
"git.ourworld.tf/herocode/heroagent/pkg/herojobs"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

View File

@ -7,7 +7,7 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/logger"
"git.ourworld.tf/herocode/heroagent/pkg/logger"
"github.com/gofiber/fiber/v2"
)
@ -65,40 +65,40 @@ func NewLogHandler(logPath string) (*LogHandler, error) {
type LogType string
const (
LogTypeSystem LogType = "system"
LogTypeService LogType = "service"
LogTypeJob LogType = "job"
LogTypeProcess LogType = "process"
LogTypeAll LogType = "all" // Special type to retrieve logs from all sources
LogTypeSystem LogType = "system"
LogTypeService LogType = "service"
LogTypeJob LogType = "job"
LogTypeProcess LogType = "process"
LogTypeAll LogType = "all" // Special type to retrieve logs from all sources
)
// GetLogs renders the logs page with logs content
func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
// Check which logger to use based on the log type parameter
logTypeParam := c.Query("log_type", string(LogTypeSystem))
// Parse query parameters
category := c.Query("category", "")
logItemType := parseLogType(c.Query("type", ""))
maxItems := c.QueryInt("max_items", 100)
page := c.QueryInt("page", 1)
itemsPerPage := 20 // Default items per page
// Parse time range
fromTime := parseTimeParam(c.Query("from", ""))
toTime := parseTimeParam(c.Query("to", ""))
// Create search arguments
searchArgs := logger.SearchArgs{
Category: category,
LogType: logItemType,
MaxItems: maxItems,
Category: category,
LogType: logItemType,
MaxItems: maxItems,
}
if !fromTime.IsZero() {
searchArgs.TimestampFrom = &fromTime
}
if !toTime.IsZero() {
searchArgs.TimestampTo = &toTime
}
@ -107,7 +107,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
var logs []logger.LogItem
var err error
var logTypeTitle string
// Check if we want to merge logs from all sources
if LogType(logTypeParam) == LogTypeAll {
// Get merged logs from all loggers
@ -116,7 +116,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
} else {
// Select the appropriate logger based on the log type
var selectedLogger *logger.Logger
switch LogType(logTypeParam) {
case LogTypeService:
selectedLogger = h.serviceLogger
@ -131,13 +131,13 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
selectedLogger = h.systemLogger
logTypeTitle = "System Logs"
}
// Check if the selected logger is properly initialized
if selectedLogger == nil {
return c.Render("admin/system/logs", fiber.Map{
"title": logTypeTitle,
"error": "Logger not initialized",
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"error": "Logger not initialized",
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
})
}
@ -149,25 +149,24 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
// Handle search error
if err != nil {
return c.Render("admin/system/logs", fiber.Map{
"title": logTypeTitle,
"error": err.Error(),
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"error": err.Error(),
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
})
}
// Calculate total pages
totalLogs := len(logs)
totalPages := (totalLogs + itemsPerPage - 1) / itemsPerPage
// Apply pagination
startIndex := (page - 1) * itemsPerPage
endIndex := startIndex + itemsPerPage
if endIndex > totalLogs {
endIndex = totalLogs
}
// Slice logs for current page
pagedLogs := logs
if startIndex < totalLogs {
@ -175,7 +174,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
} else {
pagedLogs = []logger.LogItem{}
}
// Convert logs to a format suitable for the UI
formattedLogs := make([]fiber.Map, 0, len(pagedLogs))
for _, log := range pagedLogs {
@ -185,7 +184,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
logTypeStr = "ERROR"
logTypeClass = "log-error"
}
formattedLogs = append(formattedLogs, fiber.Map{
"timestamp": log.Timestamp.Format("2006-01-02T15:04:05"),
"category": log.Category,
@ -194,20 +193,20 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
"typeClass": logTypeClass,
})
}
return c.Render("admin/system/logs", fiber.Map{
"title": logTypeTitle,
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
"logs": formattedLogs,
"total": totalLogs,
"showing": len(formattedLogs),
"page": page,
"totalPages": totalPages,
"categoryParam": category,
"typeParam": c.Query("type", ""),
"fromParam": c.Query("from", ""),
"toParam": c.Query("to", ""),
"logs": formattedLogs,
"total": totalLogs,
"showing": len(formattedLogs),
"page": page,
"totalPages": totalPages,
"categoryParam": category,
"typeParam": c.Query("type", ""),
"fromParam": c.Query("from", ""),
"toParam": c.Query("to", ""),
})
}
@ -215,27 +214,27 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error {
func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
// Check which logger to use based on the log type parameter
logTypeParam := c.Query("log_type", string(LogTypeSystem))
// Parse query parameters
category := c.Query("category", "")
logItemType := parseLogType(c.Query("type", ""))
maxItems := c.QueryInt("max_items", 100)
// Parse time range
fromTime := parseTimeParam(c.Query("from", ""))
toTime := parseTimeParam(c.Query("to", ""))
// Create search arguments
searchArgs := logger.SearchArgs{
Category: category,
LogType: logItemType,
MaxItems: maxItems,
Category: category,
LogType: logItemType,
MaxItems: maxItems,
}
if !fromTime.IsZero() {
searchArgs.TimestampFrom = &fromTime
}
if !toTime.IsZero() {
searchArgs.TimestampTo = &toTime
}
@ -243,7 +242,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
// Variables for logs and error
var logs []logger.LogItem
var err error
// Check if we want to merge logs from all sources
if LogType(logTypeParam) == LogTypeAll {
// Get merged logs from all loggers
@ -251,7 +250,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
} else {
// Select the appropriate logger based on the log type
var selectedLogger *logger.Logger
switch LogType(logTypeParam) {
case LogTypeService:
selectedLogger = h.serviceLogger
@ -262,7 +261,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
default:
selectedLogger = h.systemLogger
}
// Check if the selected logger is properly initialized
if selectedLogger == nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
@ -280,7 +279,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
"error": err.Error(),
})
}
// Convert logs to a format suitable for the UI
response := make([]fiber.Map, 0, len(logs))
for _, log := range logs {
@ -288,7 +287,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
if log.LogType == logger.LogTypeError {
logTypeStr = "ERROR"
}
response = append(response, fiber.Map{
"timestamp": log.Timestamp.Format(time.RFC3339),
"category": log.Category,
@ -296,9 +295,9 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error {
"type": logTypeStr,
})
}
return c.JSON(fiber.Map{
"logs": response,
"logs": response,
"total": len(logs),
})
}
@ -309,29 +308,29 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
// Check which logger to use based on the log type parameter
logTypeParam := c.Query("log_type", string(LogTypeSystem))
// Parse query parameters
category := c.Query("category", "")
logItemType := parseLogType(c.Query("type", ""))
maxItems := c.QueryInt("max_items", 100)
page := c.QueryInt("page", 1)
itemsPerPage := 20 // Default items per page
// Parse time range
fromTime := parseTimeParam(c.Query("from", ""))
toTime := parseTimeParam(c.Query("to", ""))
// Create search arguments
searchArgs := logger.SearchArgs{
Category: category,
LogType: logItemType,
MaxItems: maxItems,
Category: category,
LogType: logItemType,
MaxItems: maxItems,
}
if !fromTime.IsZero() {
searchArgs.TimestampFrom = &fromTime
}
if !toTime.IsZero() {
searchArgs.TimestampTo = &toTime
}
@ -340,7 +339,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
var logs []logger.LogItem
var err error
var logTypeTitle string
// Check if we want to merge logs from all sources
if LogType(logTypeParam) == LogTypeAll {
// Get merged logs from all loggers
@ -349,7 +348,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
} else {
// Select the appropriate logger based on the log type
var selectedLogger *logger.Logger
switch LogType(logTypeParam) {
case LogTypeService:
selectedLogger = h.serviceLogger
@ -364,13 +363,13 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
selectedLogger = h.systemLogger
logTypeTitle = "System Logs"
}
// Check if the selected logger is properly initialized
if selectedLogger == nil {
return c.Render("admin/system/logs_fragment", fiber.Map{
"title": logTypeTitle,
"error": "Logger not initialized",
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"error": "Logger not initialized",
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
})
}
@ -382,24 +381,24 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
// Handle search error
if err != nil {
return c.Render("admin/system/logs_fragment", fiber.Map{
"title": logTypeTitle,
"error": err.Error(),
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"error": err.Error(),
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
})
}
// Calculate total pages
totalLogs := len(logs)
totalPages := (totalLogs + itemsPerPage - 1) / itemsPerPage
// Apply pagination
startIndex := (page - 1) * itemsPerPage
endIndex := startIndex + itemsPerPage
if endIndex > totalLogs {
endIndex = totalLogs
}
// Slice logs for current page
pagedLogs := logs
if startIndex < totalLogs {
@ -407,7 +406,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
} else {
pagedLogs = []logger.LogItem{}
}
// Convert logs to a format suitable for the UI
formattedLogs := make([]fiber.Map, 0, len(pagedLogs))
for _, log := range pagedLogs {
@ -417,7 +416,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
logTypeStr = "ERROR"
logTypeClass = "log-error"
}
formattedLogs = append(formattedLogs, fiber.Map{
"timestamp": log.Timestamp.Format("2006-01-02T15:04:05"),
"category": log.Category,
@ -426,18 +425,18 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error {
"typeClass": logTypeClass,
})
}
// Set layout to empty to disable the layout for fragment responses
return c.Render("admin/system/logs_fragment", fiber.Map{
"title": logTypeTitle,
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"title": logTypeTitle,
"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess},
"selectedLogType": logTypeParam,
"logs": formattedLogs,
"total": totalLogs,
"showing": len(formattedLogs),
"page": page,
"totalPages": totalPages,
"layout": "", // Disable layout for partial template
"logs": formattedLogs,
"total": totalLogs,
"showing": len(formattedLogs),
"page": page,
"totalPages": totalPages,
"layout": "", // Disable layout for partial template
})
}
@ -458,12 +457,12 @@ func parseTimeParam(timeStr string) time.Time {
if timeStr == "" {
return time.Time{}
}
t, err := time.Parse(time.RFC3339, timeStr)
if err != nil {
return time.Time{}
}
return t
}
@ -471,10 +470,10 @@ func parseTimeParam(timeStr string) time.Time {
func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, error) {
// Create a slice to hold all logs
allLogs := make([]logger.LogItem, 0)
// Create a map to track errors
errors := make(map[string]error)
// Get logs from system logger if available
if h.systemLogger != nil {
systemLogs, err := h.systemLogger.Search(args)
@ -488,7 +487,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er
allLogs = append(allLogs, systemLogs...)
}
}
// Get logs from service logger if available
if h.serviceLogger != nil {
serviceLogs, err := h.serviceLogger.Search(args)
@ -502,7 +501,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er
allLogs = append(allLogs, serviceLogs...)
}
}
// Get logs from job logger if available
if h.jobLogger != nil {
jobLogs, err := h.jobLogger.Search(args)
@ -516,7 +515,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er
allLogs = append(allLogs, jobLogs...)
}
}
// Get logs from process logger if available
if h.processLogger != nil {
processLogs, err := h.processLogger.Search(args)
@ -530,7 +529,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er
allLogs = append(allLogs, processLogs...)
}
}
// Check if we have any logs
if len(allLogs) == 0 && len(errors) > 0 {
// Combine error messages
@ -540,16 +539,16 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er
}
return nil, fmt.Errorf("failed to retrieve logs: %s", strings.Join(errorMsgs, "; "))
}
// Sort logs by timestamp (newest first)
sort.Slice(allLogs, func(i, j int) bool {
return allLogs[i].Timestamp.After(allLogs[j].Timestamp)
})
// Apply max items limit if specified
if args.MaxItems > 0 && len(allLogs) > args.MaxItems {
allLogs = allLogs[:args.MaxItems]
}
return allLogs, nil
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/gofiber/fiber/v2"
)
@ -43,8 +43,8 @@ func (h *ProcessHandler) GetProcessStatsJSON(c *fiber.Ctx) error {
// Convert to fiber.Map for JSON response
response := fiber.Map{
"total": processData.Total,
"filtered": processData.Filtered,
"total": processData.Total,
"filtered": processData.Filtered,
"timestamp": time.Now().Unix(),
}
@ -61,7 +61,7 @@ func (h *ProcessHandler) GetProcessStatsJSON(c *fiber.Ctx) error {
"is_current": proc.IsCurrent,
}
}
response["processes"] = processes
// Return JSON response
@ -127,8 +127,8 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error {
// Check if StatsManager is properly initialized
if h.statsManager == nil {
return c.Render("admin/system/processes_data", fiber.Map{
"error": "System error: Stats manager not initialized",
"layout": "",
"error": "System error: Stats manager not initialized",
"layout": "",
})
}
@ -156,7 +156,7 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error {
} else {
processData, err = h.statsManager.GetProcessStatsFresh(0)
}
if err != nil {
// Handle AJAX requests differently from regular requests
isAjax := c.Get("X-Requested-With") == "XMLHttpRequest"
@ -165,8 +165,8 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error {
}
// For regular requests, render the error within the fragment
return c.Render("admin/system/processes_data", fiber.Map{
"error": "Failed to get process data: " + err.Error(),
"layout": "",
"error": "Failed to get process data: " + err.Error(),
"layout": "",
})
}
}
@ -197,9 +197,7 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error {
"processStats": processStats,
"layout": "", // Disable layout for partial template
}
// Return only the table HTML content directly to be injected into the processes-table-content div
return c.Render("admin/system/processes_data", templateData)
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
"github.com/gofiber/fiber/v2"
)

View File

@ -5,7 +5,7 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/gofiber/fiber/v2"
"github.com/shirou/gopsutil/v3/host"
)
@ -335,7 +335,7 @@ func (h *SystemHandler) GetProcessStatsAPI(c *fiber.Ctx) error {
"is_current": proc.IsCurrent,
}
}
response["processes"] = processes
// Return JSON response

View File

@ -7,8 +7,8 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/heroagent/handlers"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/heroagent/handlers"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/gofiber/fiber/v2"
"github.com/shirou/gopsutil/v3/host"
)
@ -68,12 +68,12 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) {
// System routes
admin.Get("/system/info", h.getSystemInfo)
admin.Get("/system/hardware-stats", h.getHardwareStats)
// Create process handler
processHandler := handlers.NewProcessHandler(h.statsManager)
admin.Get("/system/processes", processHandler.GetProcesses)
admin.Get("/system/processes-data", processHandler.GetProcessesData)
// Create log handler
// Ensure log directory exists
// Using the same shared logs path as process manager
@ -81,7 +81,7 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) {
if err := os.MkdirAll(logDir, 0755); err != nil {
fmt.Printf("Error creating log directory: %v\n", err)
}
logHandler, err := handlers.NewLogHandler(logDir)
if err != nil {
fmt.Printf("Error creating log handler: %v\n", err)
@ -96,11 +96,11 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) {
// but it now just redirects to the main logs endpoint
admin.Get("/system/logs-fragment", logHandler.GetLogsFragment)
admin.Get("/system/logs-test", h.getSystemLogsTest) // Keep the test logs route
// Log API endpoints
app.Get("/api/logs", logHandler.GetLogsAPI)
}
admin.Get("/system/settings", h.getSystemSettings)
// OpenRPC routes

View File

@ -4,7 +4,7 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/herojobs"
"git.ourworld.tf/herocode/heroagent/pkg/herojobs"
"github.com/gofiber/fiber/v2"
)

View File

@ -4,14 +4,14 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
"github.com/gofiber/fiber/v2"
)
// ServiceHandler handles service-related page routes
type ServiceHandler struct {
client *openrpc.Client
logger *log.Logger
client *openrpc.Client
logger *log.Logger
}
// NewServiceHandler creates a new service handler with the provided socket path and secret
@ -90,7 +90,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
if !ok {
continue
}
// Create a ProcessDisplayInfo from the map
displayInfo := ProcessDisplayInfo{
ID: fmt.Sprintf("%v", procMap["pid"]),

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
)
// ProcessDisplayInfo represents information about a process for display purposes
@ -22,7 +22,7 @@ type ProcessDisplayInfo struct {
func ConvertToDisplayInfo(info *processmanager.ProcessInfo) ProcessDisplayInfo {
// Calculate uptime from start time
uptime := formatUptime(time.Since(info.StartTime))
return ProcessDisplayInfo{
ID: fmt.Sprintf("%d", info.PID),
Name: info.Name,

View File

@ -7,8 +7,8 @@ import (
"path/filepath"
"time"
"github.com/freeflowuniverse/heroagent/pkg/data/ourdb"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// JobStatus represents the status of a job

View File

@ -6,7 +6,7 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
"github.com/redis/go-redis/v9"
)

View File

@ -35,7 +35,7 @@ Key features:
```go
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// Create a new playbook from HeroScript text

View File

@ -7,7 +7,7 @@ import (
"os/signal"
"syscall"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/herohandler"
)
func main() {
@ -24,7 +24,7 @@ func main() {
fmt.Printf("Failed to initialize hero handler: %v\n", err)
os.Exit(1)
}
// Get the default instance
handler := herohandler.DefaultInstance
@ -43,7 +43,7 @@ func main() {
tcpAddressStr = *tcpAddress
fmt.Printf("TCP address: %s\n", tcpAddressStr)
}
err = handler.StartTelnet(socketPathStr, tcpAddressStr)
if err != nil {
fmt.Printf("Failed to start telnet server: %v\n", err)

View File

@ -8,7 +8,7 @@ import (
"os"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
func main() {

View File

@ -3,8 +3,8 @@ package internal
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlers"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlers"
)
// ExampleHandler handles example actions

View File

@ -7,7 +7,7 @@ import (
"os"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/cmd/herohandler/internal"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/cmd/herohandler/internal"
)
func main() {
@ -30,7 +30,7 @@ func main() {
// Get the command from arguments
command := strings.Join(os.Args[1:], " ")
// Format as proper HeroScript with !! prefix if not already prefixed
script := command
if !strings.HasPrefix(script, "!!") {

View File

@ -10,7 +10,7 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/herohandler"
)
func main() {
@ -92,7 +92,7 @@ func showSupportedActions() {
// We need to implement this function to get supported actions
// Since we can't directly access the factory field, we'll use the telnet interface
script := "!!core.actions"
// Try TCP first, then Unix socket if TCP fails
result, err := Send(script, "localhost:8023", false)
if err != nil {
@ -103,7 +103,7 @@ func showSupportedActions() {
return
}
}
fmt.Println("Supported actions by actor:")
fmt.Println(result)
}
@ -151,7 +151,7 @@ func Send(command string, address string, isUnixSocket bool) (string, error) {
return
}
response.WriteString(line)
// If we've received a complete response, break
if strings.Contains(line, "\n") && strings.TrimSpace(line) == "" {
break
@ -187,7 +187,7 @@ func runTestScript() {
result, err = Send(script, "/tmp/hero.sock", true)
if err != nil {
fmt.Printf("Unix socket connection failed: %v\n", err)
// We can't directly access the factory field, so we'll just report the error
fmt.Printf("Error: %v\n", err)
return

View File

@ -4,7 +4,7 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
const exampleScript = `

View File

@ -16,7 +16,7 @@ The VM handler example shows how to:
To run the example:
```bash
cd ~/code/github/freeflowuniverse/heroagent/pkg/handlerfactory/cmd/vmhandler
cd ~/code/github/freeflowuniverse/herocode/heroagent/pkg/handlerfactory/cmd/vmhandler
go run . tutorial
#to run just the server do
go run .

View File

@ -6,7 +6,7 @@ import (
"os"
"time"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// runTutorial runs an interactive tutorial demonstrating the VM handler
@ -50,7 +50,7 @@ func runTutorial() {
// Process heroscript commands
fmt.Println("\nStep 5: Process heroscript commands")
// Define a VM
defineScript := `!!vm.define name:'tutorial_vm' cpu:2 memory:'4GB' storage:'50GB'
description: 'A tutorial VM for demonstration purposes'`

View File

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// VMHandler handles VM-related actions

View File

@ -8,7 +8,7 @@ import (
"path/filepath"
"syscall"
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// The tutorial functions are defined in tutorial.go

View File

@ -30,7 +30,7 @@ The Handler Factory exposes two interfaces for communication:
to get started
```bash
cd /root/code/github/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler/cmd
cd /root/code/github/freeflowuniverse/herocode/heroagent/pkg/handlerfactory/herohandler/cmd
go run .
```

View File

@ -5,8 +5,8 @@ import (
"reflect"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// Handler interface defines methods that all handlers must implement

View File

@ -5,7 +5,7 @@ import (
"reflect"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// HandlerFactory manages a collection of handlers

View File

@ -12,7 +12,7 @@ import (
"sync"
"syscall"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// ANSI color codes for terminal output
@ -43,23 +43,23 @@ type TelnetServer struct {
sigCh chan os.Signal
onShutdown func()
// Map to store client preferences (like json formatting)
clientPrefs map[net.Conn]map[string]bool
prefsMutex sync.RWMutex
clientPrefs map[net.Conn]map[string]bool
prefsMutex sync.RWMutex
}
// NewTelnetServer creates a new telnet server
func NewTelnetServer(factory *HandlerFactory, secrets ...string) *TelnetServer {
ctx, cancel := context.WithCancel(context.Background())
return &TelnetServer{
factory: factory,
secrets: secrets,
clients: make(map[net.Conn]bool),
factory: factory,
secrets: secrets,
clients: make(map[net.Conn]bool),
clientPrefs: make(map[net.Conn]map[string]bool),
running: false,
ctx: ctx,
cancel: cancel,
sigCh: make(chan os.Signal, 1),
onShutdown: func() {},
running: false,
ctx: ctx,
cancel: cancel,
sigCh: make(chan os.Signal, 1),
onShutdown: func() {},
}
}
@ -205,7 +205,7 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) {
ts.clientsMutex.Lock()
ts.clients[conn] = false
ts.clientsMutex.Unlock()
// Initialize client preferences
ts.prefsMutex.Lock()
ts.clientPrefs[conn] = make(map[string]bool)
@ -284,7 +284,7 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) {
}
continue
}
// Handle JSON format toggle
if line == "!!json" {
ts.prefsMutex.Lock()
@ -293,12 +293,12 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) {
prefs = make(map[string]bool)
ts.clientPrefs[conn] = prefs
}
// Toggle JSON format preference
currentSetting := prefs["json"]
prefs["json"] = !currentSetting
ts.prefsMutex.Unlock()
if prefs["json"] {
conn.Write([]byte("JSON format will be automatically added to all heroscripts.\n"))
} else {
@ -416,7 +416,7 @@ func (ts *TelnetServer) executeHeroscript(script string, conn net.Conn, interact
ts.prefsMutex.RLock()
prefs, exists := ts.clientPrefs[conn]
ts.prefsMutex.RUnlock()
if exists && prefs["json"] {
// Add format:json if not already present
if !strings.Contains(script, "format:json") {
@ -424,7 +424,7 @@ func (ts *TelnetServer) executeHeroscript(script string, conn net.Conn, interact
}
}
}
if interactive {
// Format the script with colors
formattedScript := formatHeroscript(script)
@ -466,8 +466,6 @@ func (ts *TelnetServer) addJsonFormat(script string) string {
return strings.Join(lines, "\n")
}
// formatHeroscript formats heroscript with colors for console output only
// This is not used for telnet responses, only for server-side logging
func formatHeroscript(script string) string {
@ -611,12 +609,12 @@ func (ts *TelnetServer) generateHelpText(interactive bool) string {
// Try to call the Help method on each handler using reflection
handlerValue := reflect.ValueOf(handler)
helpMethod := handlerValue.MethodByName("Help")
if helpMethod.IsValid() {
// Call the Help method
args := []reflect.Value{reflect.ValueOf("")}
result := helpMethod.Call(args)
// Get the result
if len(result) > 0 && result[0].Kind() == reflect.String {
helpText := result[0].String()

View File

@ -3,7 +3,7 @@ package handlers
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
)
// AuthHandler handles authentication actions

View File

@ -5,9 +5,9 @@ import (
"reflect"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// BaseHandler provides common functionality for all handlers

View File

@ -4,8 +4,8 @@ import (
"fmt"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
// HandlerFactory manages a collection of handlers for processing HeroScript commands

View File

@ -1,7 +1,7 @@
package herohandler
import (
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
)
// GetFactory returns the handler factory

View File

@ -4,7 +4,7 @@ import (
"log"
"sync"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/herohandler"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/herohandler"
)
func main() {

View File

@ -4,10 +4,10 @@ import (
"fmt"
"log"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
// "github.com/freeflowuniverse/heroagent/pkg/handlerfactory/heroscript/handlerfactory/fakehandler"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/processmanagerhandler"
// "git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/heroscript/handlerfactory/fakehandler"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/processmanagerhandler"
)
// HeroHandler is the main handler factory that manages all registered handlers

View File

@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook"
)
func main() {

View File

@ -3,8 +3,8 @@ package processmanagerhandler
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
)
// ProcessManagerHandler handles process manager-related actions

View File

@ -19,7 +19,7 @@ A Go package for parsing and manipulating parameters from text in a key-value fo
```go
import (
"github.com/freeflowuniverse/heroagent/pkg/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/paramsparser"
)
// Create a new parser

View File

@ -4,7 +4,7 @@ package main
import (
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
)
func main() {

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// ParamsParser represents a parameter parser that can handle various parameter sources
@ -32,12 +32,12 @@ func New() *ParamsParser {
func (p *ParamsParser) Parse(input string) error {
// Normalize line endings
input = strings.ReplaceAll(input, "\r\n", "\n")
// Track the current state
var currentKey string
var currentValue strings.Builder
var inMultilineString bool
// Process each line
lines := strings.Split(input, "\n")
for i := 0; i < len(lines); i++ {
@ -48,12 +48,12 @@ func (p *ParamsParser) Parse(input string) error {
} else {
line = lines[i]
}
// Skip empty lines unless we're in a multiline string
if line == "" && !inMultilineString {
continue
}
// If we're in a multiline string
if inMultilineString {
// Check if this line ends the multiline string
@ -71,7 +71,7 @@ func (p *ParamsParser) Parse(input string) error {
}
continue
}
// Process the line to extract key-value pairs
var processedPos int
for processedPos < len(line) {
@ -79,62 +79,62 @@ func (p *ParamsParser) Parse(input string) error {
for processedPos < len(line) && (line[processedPos] == ' ' || line[processedPos] == '\t') {
processedPos++
}
if processedPos >= len(line) {
break
}
// Find the next key by looking for a colon
keyStart := processedPos
colonPos := -1
for j := processedPos; j < len(line); j++ {
if line[j] == ':' {
colonPos = j
break
}
}
if colonPos == -1 {
// No colon found, skip this part
break
}
// Extract key and use NameFix to standardize it
rawKey := strings.TrimSpace(line[keyStart:colonPos])
key := tools.NameFix(rawKey)
if key == "" {
// Invalid key, move past the colon and continue
processedPos = colonPos + 1
continue
}
// Move position past the colon
processedPos = colonPos + 1
if processedPos >= len(line) {
// End of line reached, store empty value
p.params[key] = ""
break
}
// Skip whitespace after the colon
for processedPos < len(line) && (line[processedPos] == ' ' || line[processedPos] == '\t') {
processedPos++
}
if processedPos >= len(line) {
// End of line reached after whitespace, store empty value
p.params[key] = ""
break
}
// Check if the value is quoted
if line[processedPos] == '\'' {
// This is a quoted string
processedPos++ // Skip the opening quote
// Look for the closing quote
quoteEnd := -1
for j := processedPos; j < len(line); j++ {
@ -144,7 +144,7 @@ func (p *ParamsParser) Parse(input string) error {
break
}
}
if quoteEnd != -1 {
// Single-line quoted string
value := line[processedPos:quoteEnd]
@ -167,12 +167,12 @@ func (p *ParamsParser) Parse(input string) error {
// This is an unquoted value
valueStart := processedPos
valueEnd := valueStart
// Find the end of the value (space or end of line)
for valueEnd < len(line) && line[valueEnd] != ' ' && line[valueEnd] != '\t' {
valueEnd++
}
value := line[valueStart:valueEnd]
// For unquoted values, use NameFix to standardize them
// This handles the 'without' keyword and other special cases
@ -181,12 +181,12 @@ func (p *ParamsParser) Parse(input string) error {
}
}
}
// If we're still in a multiline string at the end, that's an error
if inMultilineString {
return errors.New("unterminated multiline string")
}
return nil
}
@ -196,7 +196,7 @@ func (p *ParamsParser) Parse(input string) error {
func (p *ParamsParser) ParseString(input string) error {
// Trim the input
input = strings.TrimSpace(input)
// Process the input to extract key-value pairs
var processedPos int
for processedPos < len(input) {
@ -204,62 +204,62 @@ func (p *ParamsParser) ParseString(input string) error {
for processedPos < len(input) && (input[processedPos] == ' ' || input[processedPos] == '\t') {
processedPos++
}
if processedPos >= len(input) {
break
}
// Find the next key by looking for a colon
keyStart := processedPos
colonPos := -1
for j := processedPos; j < len(input); j++ {
if input[j] == ':' {
colonPos = j
break
}
}
if colonPos == -1 {
// No colon found, skip this part
break
}
// Extract key and use NameFix to standardize it
rawKey := strings.TrimSpace(input[keyStart:colonPos])
key := tools.NameFix(rawKey)
if key == "" {
// Invalid key, move past the colon and continue
processedPos = colonPos + 1
continue
}
// Move position past the colon
processedPos = colonPos + 1
if processedPos >= len(input) {
// End of input reached, store empty value
p.params[key] = ""
break
}
// Skip whitespace after the colon
for processedPos < len(input) && (input[processedPos] == ' ' || input[processedPos] == '\t') {
processedPos++
}
if processedPos >= len(input) {
// End of input reached after whitespace, store empty value
p.params[key] = ""
break
}
// Check if the value is quoted
if input[processedPos] == '\'' {
// This is a quoted string
processedPos++ // Skip the opening quote
// Look for the closing quote
quoteEnd := -1
for j := processedPos; j < len(input); j++ {
@ -269,11 +269,11 @@ func (p *ParamsParser) ParseString(input string) error {
break
}
}
if quoteEnd == -1 {
return errors.New("unterminated quoted string")
}
value := input[processedPos:quoteEnd]
// For quoted values in ParseString, we can apply NameFix
// since this method doesn't handle multiline strings
@ -286,12 +286,12 @@ func (p *ParamsParser) ParseString(input string) error {
// This is an unquoted value
valueStart := processedPos
valueEnd := valueStart
// Find the end of the value (space or end of input)
for valueEnd < len(input) && input[valueEnd] != ' ' && input[valueEnd] != '\t' {
valueEnd++
}
value := input[valueStart:valueEnd]
// For unquoted values, use NameFix to standardize them
// This handles the 'without' keyword and other special cases
@ -299,7 +299,7 @@ func (p *ParamsParser) ParseString(input string) error {
processedPos = valueEnd
}
}
return nil
}
@ -364,7 +364,7 @@ func (p *ParamsParser) GetBool(key string) bool {
if value == "" {
return false
}
// Check for common boolean string representations
value = strings.ToLower(value)
return value == "true" || value == "yes" || value == "1" || value == "on"
@ -405,17 +405,17 @@ func (p *ParamsParser) Has(key string) bool {
// GetAll returns all parameters as a map
func (p *ParamsParser) GetAll() map[string]string {
result := make(map[string]string)
// First add defaults
for k, v := range p.defaultParams {
result[k] = v
}
// Then override with actual params
for k, v := range p.params {
result[k] = v
}
return result
}

View File

@ -3,8 +3,8 @@ package playbook
import (
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// State represents the parser state

View File

@ -7,7 +7,7 @@ import (
"sort"
"strings"
"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser"
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
)
// ActionType represents the type of action

View File

@ -11,7 +11,7 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/heroservices/billing/models"
"git.ourworld.tf/herocode/heroagent/pkg/heroservices/billing/models"
)
const (

View File

@ -7,9 +7,9 @@ import (
"os"
"path/filepath"
"github.com/freeflowuniverse/heroagent/pkg/data/ourdb"
"github.com/freeflowuniverse/heroagent/pkg/data/radixtree"
"github.com/freeflowuniverse/heroagent/pkg/tools"
"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb"
"git.ourworld.tf/herocode/heroagent/pkg/data/radixtree"
"git.ourworld.tf/herocode/heroagent/pkg/tools"
)
// DBStore represents the central database store for all models

View File

@ -9,8 +9,7 @@ import (
"syscall"
"time"
openaiproxy "github.com/freeflowuniverse/heroagent/pkg/heroservices/openaiproxy"
"github.com/openai/openai-go"
openaiproxy "git.ourworld.tf/herocode/heroagent/pkg/heroservices/openaiproxy"
"github.com/openai/openai-go/option"
)

View File

@ -10,7 +10,7 @@ import (
"strings"
"time"
"github.com/freeflowuniverse/heroagent/pkg/jobsmanager"
"git.ourworld.tf/herocode/heroagent/pkg/jobsmanager"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
)

View File

@ -6,7 +6,7 @@ import (
"path/filepath"
"time"
"github.com/freeflowuniverse/heroagent/pkg/logger"
"git.ourworld.tf/herocode/heroagent/pkg/logger"
)
func main() {

View File

@ -1,209 +0,0 @@
package client
import (
"encoding/json"
"errors"
"fmt"
"net"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
// Common errors
var (
ErrConnectionFailed = errors.New("failed to connect to OpenRPC server")
ErrRequestFailed = errors.New("failed to send request to OpenRPC server")
ErrResponseFailed = errors.New("failed to read response from OpenRPC server")
ErrUnmarshalFailed = errors.New("failed to unmarshal response")
ErrUnexpectedResponse = errors.New("unexpected response format")
ErrRPCError = errors.New("RPC error")
ErrAuthenticationFailed = errors.New("authentication failed")
)
// RPCRequest represents an outgoing RPC request
type RPCRequest struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID int `json:"id"`
Secret string `json:"secret,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCResponse represents an incoming RPC response
type RPCResponse struct {
Result interface{} `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
ID interface{} `json:"id,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCError represents an RPC error
type RPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// Error returns a string representation of the RPC error
func (e *RPCError) Error() string {
if e.Data != nil {
return fmt.Sprintf("RPC error %d: %s - %v", e.Code, e.Message, e.Data)
}
return fmt.Sprintf("RPC error %d: %s", e.Code, e.Message)
}
// IntrospectionResponse represents the response from the rpc.introspect method
type IntrospectionResponse struct {
Logs []openrpcmanager.CallLog `json:"logs"`
Total int `json:"total"`
Filtered int `json:"filtered"`
}
// Client is the interface that all OpenRPC clients must implement
type Client interface {
// Discover returns the OpenRPC schema
Discover() (openrpcmanager.OpenRPCSchema, error)
// Introspect returns information about recent RPC calls
Introspect(limit int, method string, status string) (IntrospectionResponse, error)
// Request sends a request to the OpenRPC server and returns the result
Request(method string, params json.RawMessage, secret string) (interface{}, error)
// Close closes the client connection
Close() error
}
// BaseClient provides a base implementation of the Client interface
type BaseClient struct {
socketPath string
secret string
nextID int
}
// NewClient creates a new OpenRPC client
func NewClient(socketPath, secret string) *BaseClient {
return &BaseClient{
socketPath: socketPath,
secret: secret,
nextID: 1,
}
}
// Discover returns the OpenRPC schema
func (c *BaseClient) Discover() (openrpcmanager.OpenRPCSchema, error) {
result, err := c.Request("rpc.discover", json.RawMessage("{}"), "")
if err != nil {
return openrpcmanager.OpenRPCSchema{}, err
}
// Convert result to schema
resultJSON, err := json.Marshal(result)
if err != nil {
return openrpcmanager.OpenRPCSchema{}, fmt.Errorf("%w: %v", ErrUnmarshalFailed, err)
}
var schema openrpcmanager.OpenRPCSchema
if err := json.Unmarshal(resultJSON, &schema); err != nil {
return openrpcmanager.OpenRPCSchema{}, fmt.Errorf("%w: %v", ErrUnmarshalFailed, err)
}
return schema, nil
}
// Introspect returns information about recent RPC calls
func (c *BaseClient) Introspect(limit int, method string, status string) (IntrospectionResponse, error) {
// Create the params object
params := struct {
Limit int `json:"limit,omitempty"`
Method string `json:"method,omitempty"`
Status string `json:"status,omitempty"`
}{
Limit: limit,
Method: method,
Status: status,
}
// Marshal the params
paramsJSON, err := json.Marshal(params)
if err != nil {
return IntrospectionResponse{}, fmt.Errorf("failed to marshal introspection params: %v", err)
}
// Make the request
result, err := c.Request("rpc.introspect", paramsJSON, c.secret)
if err != nil {
return IntrospectionResponse{}, err
}
// Convert result to introspection response
resultJSON, err := json.Marshal(result)
if err != nil {
return IntrospectionResponse{}, fmt.Errorf("%w: %v", ErrUnmarshalFailed, err)
}
var response IntrospectionResponse
if err := json.Unmarshal(resultJSON, &response); err != nil {
return IntrospectionResponse{}, fmt.Errorf("%w: %v", ErrUnmarshalFailed, err)
}
return response, nil
}
// Request sends a request to the OpenRPC server and returns the result
func (c *BaseClient) Request(method string, params json.RawMessage, secret string) (interface{}, error) {
// Connect to the Unix socket
conn, err := net.Dial("unix", c.socketPath)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrConnectionFailed, err)
}
defer conn.Close()
// Create the request
request := RPCRequest{
Method: method,
Params: params,
ID: c.nextID,
Secret: secret,
JSONRPC: "2.0",
}
c.nextID++
// Marshal the request
requestData, err := json.Marshal(request)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %v", err)
}
// Send the request
_, err = conn.Write(requestData)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrRequestFailed, err)
}
// Read the response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrResponseFailed, err)
}
// Parse the response
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
return nil, fmt.Errorf("%w: %v", ErrUnmarshalFailed, err)
}
// Check for errors
if response.Error != nil {
return nil, fmt.Errorf("%w: %v", ErrRPCError, response.Error)
}
return response.Result, nil
}
// Close closes the client connection
func (c *BaseClient) Close() error {
// Nothing to do for the base client since we create a new connection for each request
return nil
}

View File

@ -1,283 +0,0 @@
package client
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
// MockClient implements the Client interface for testing
type MockClient struct {
BaseClient
}
// TestMethod is a test method that returns a greeting
func (c *MockClient) TestMethod(name string) (string, error) {
params := map[string]string{"name": name}
paramsJSON, err := json.Marshal(params)
if err != nil {
return "", err
}
result, err := c.Request("test.method", paramsJSON, "")
if err != nil {
return "", err
}
// Convert result to string
greeting, ok := result.(string)
if !ok {
return "", ErrUnexpectedResponse
}
return greeting, nil
}
// SecureMethod is a test method that requires authentication
func (c *MockClient) SecureMethod() (map[string]interface{}, error) {
result, err := c.Request("secure.method", json.RawMessage("{}"), c.secret)
if err != nil {
return nil, err
}
// Convert result to map
data, ok := result.(map[string]interface{})
if !ok {
return nil, ErrUnexpectedResponse
}
return data, nil
}
func TestClient(t *testing.T) {
// Create a temporary socket path
tempDir, err := os.MkdirTemp("", "openrpc-client-test")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
socketPath := filepath.Join(tempDir, "openrpc.sock")
secret := "test-secret"
// Create test schema and handlers
schema := openrpcmanager.OpenRPCSchema{
OpenRPC: "1.2.6",
Info: openrpcmanager.InfoObject{
Title: "Test API",
Version: "1.0.0",
},
Methods: []openrpcmanager.MethodObject{
{
Name: "test.method",
Params: []openrpcmanager.ContentDescriptorObject{
{
Name: "name",
Schema: openrpcmanager.SchemaObject{"type": "string"},
},
},
Result: &openrpcmanager.ContentDescriptorObject{
Name: "result",
Schema: openrpcmanager.SchemaObject{"type": "string"},
},
},
{
Name: "secure.method",
Params: []openrpcmanager.ContentDescriptorObject{},
Result: &openrpcmanager.ContentDescriptorObject{
Name: "result",
Schema: openrpcmanager.SchemaObject{"type": "object"},
},
},
},
}
handlers := map[string]openrpcmanager.RPCHandler{
"test.method": func(params json.RawMessage) (interface{}, error) {
var request struct {
Name string `json:"name"`
}
if err := json.Unmarshal(params, &request); err != nil {
return nil, err
}
return "Hello, " + request.Name + "!", nil
},
"secure.method": func(params json.RawMessage) (interface{}, error) {
return map[string]interface{}{
"secure": true,
"data": "sensitive information",
}, nil
},
}
// Create and start OpenRPC manager and Unix server
manager, err := openrpcmanager.NewOpenRPCManager(schema, handlers, secret)
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
server, err := openrpcmanager.NewUnixServer(manager, socketPath)
if err != nil {
t.Fatalf("Failed to create UnixServer: %v", err)
}
if err := server.Start(); err != nil {
t.Fatalf("Failed to start UnixServer: %v", err)
}
defer server.Stop()
// Wait for server to start
time.Sleep(100 * time.Millisecond)
// Create client
client := &MockClient{
BaseClient: BaseClient{
socketPath: socketPath,
secret: secret,
},
}
// Test Discover method
t.Run("Discover", func(t *testing.T) {
schema, err := client.Discover()
if err != nil {
t.Fatalf("Discover failed: %v", err)
}
if schema.OpenRPC != "1.2.6" {
t.Errorf("Expected OpenRPC version 1.2.6, got: %s", schema.OpenRPC)
}
if len(schema.Methods) < 2 {
t.Errorf("Expected at least 2 methods, got: %d", len(schema.Methods))
}
// Check if our test methods are in the schema
foundTestMethod := false
foundSecureMethod := false
for _, method := range schema.Methods {
if method.Name == "test.method" {
foundTestMethod = true
}
if method.Name == "secure.method" {
foundSecureMethod = true
}
}
if !foundTestMethod {
t.Error("test.method not found in schema")
}
if !foundSecureMethod {
t.Error("secure.method not found in schema")
}
})
// Test TestMethod
t.Run("TestMethod", func(t *testing.T) {
greeting, err := client.TestMethod("World")
if err != nil {
t.Fatalf("TestMethod failed: %v", err)
}
expected := "Hello, World!"
if greeting != expected {
t.Errorf("Expected greeting %q, got: %q", expected, greeting)
}
})
// Test Introspect method
t.Run("Introspect", func(t *testing.T) {
// Make several requests to generate logs
_, err := client.TestMethod("World")
if err != nil {
t.Fatalf("TestMethod failed: %v", err)
}
_, err = client.SecureMethod()
if err != nil {
t.Fatalf("SecureMethod failed: %v", err)
}
// Test introspection
response, err := client.Introspect(10, "", "")
if err != nil {
t.Fatalf("Introspect failed: %v", err)
}
// Verify we have logs
if response.Total < 2 {
t.Errorf("Expected at least 2 logs, got: %d", response.Total)
}
// Test filtering by method
response, err = client.Introspect(10, "test.method", "")
if err != nil {
t.Fatalf("Introspect with method filter failed: %v", err)
}
// Verify filtering works
for _, log := range response.Logs {
if log.Method != "test.method" {
t.Errorf("Expected only test.method logs, got: %s", log.Method)
}
}
// Test filtering by status
response, err = client.Introspect(10, "", "success")
if err != nil {
t.Fatalf("Introspect with status filter failed: %v", err)
}
// Verify status filtering works
for _, log := range response.Logs {
if log.Status != "success" {
t.Errorf("Expected only success logs, got: %s", log.Status)
}
}
})
// Test SecureMethod with valid secret
t.Run("SecureMethod", func(t *testing.T) {
data, err := client.SecureMethod()
if err != nil {
t.Fatalf("SecureMethod failed: %v", err)
}
secure, ok := data["secure"].(bool)
if !ok || !secure {
t.Errorf("Expected secure to be true, got: %v", data["secure"])
}
sensitiveData, ok := data["data"].(string)
if !ok || sensitiveData != "sensitive information" {
t.Errorf("Expected data to be 'sensitive information', got: %v", data["data"])
}
})
// Test SecureMethod with invalid secret
t.Run("SecureMethod with invalid secret", func(t *testing.T) {
invalidClient := &MockClient{
BaseClient: BaseClient{
socketPath: socketPath,
secret: "wrong-secret",
},
}
_, err := invalidClient.SecureMethod()
if err == nil {
t.Error("Expected error for invalid secret, but got nil")
}
})
// Test non-existent method
t.Run("Non-existent method", func(t *testing.T) {
_, err := client.Request("non.existent", json.RawMessage("{}"), "")
if err == nil {
t.Error("Expected error for non-existent method, but got nil")
}
})
}

View File

@ -1,113 +0,0 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
)
func main() {
// Parse command line arguments
socketPath := flag.String("socket", "/tmp/openrpc.sock", "Path to the Unix socket")
secret := flag.String("secret", "test-secret", "Secret for authenticated methods")
flag.Parse()
// Create a simple OpenRPC schema
schema := openrpcmanager.OpenRPCSchema{
OpenRPC: "1.2.6",
Info: openrpcmanager.InfoObject{
Title: "Hero Launcher API",
Version: "1.0.0",
},
Methods: []openrpcmanager.MethodObject{
{
Name: "echo",
Params: []openrpcmanager.ContentDescriptorObject{
{
Name: "message",
Schema: openrpcmanager.SchemaObject{"type": "object"},
},
},
Result: &openrpcmanager.ContentDescriptorObject{
Name: "result",
Schema: openrpcmanager.SchemaObject{"type": "object"},
},
},
{
Name: "ping",
Params: []openrpcmanager.ContentDescriptorObject{},
Result: &openrpcmanager.ContentDescriptorObject{
Name: "result",
Schema: openrpcmanager.SchemaObject{"type": "string"},
},
},
{
Name: "secure.info",
Params: []openrpcmanager.ContentDescriptorObject{},
Result: &openrpcmanager.ContentDescriptorObject{
Name: "result",
Schema: openrpcmanager.SchemaObject{"type": "object"},
},
},
},
}
// Create handlers
handlers := map[string]openrpcmanager.RPCHandler{
"echo": func(params json.RawMessage) (interface{}, error) {
var data interface{}
if err := json.Unmarshal(params, &data); err != nil {
return nil, err
}
return data, nil
},
"ping": func(params json.RawMessage) (interface{}, error) {
return "pong", nil
},
"secure.info": func(params json.RawMessage) (interface{}, error) {
return map[string]interface{}{
"server": "Hero Launcher",
"version": "1.0.0",
"status": "running",
}, nil
},
}
// Create OpenRPC manager
manager, err := openrpcmanager.NewOpenRPCManager(schema, handlers, *secret)
if err != nil {
log.Fatalf("Failed to create OpenRPC manager: %v", err)
}
// Create Unix server
server, err := openrpcmanager.NewUnixServer(manager, *socketPath)
if err != nil {
log.Fatalf("Failed to create Unix server: %v", err)
}
// Start the server
if err := server.Start(); err != nil {
log.Fatalf("Failed to start Unix server: %v", err)
}
defer server.Stop()
fmt.Printf("OpenRPC server started on Unix socket: %s\n", *socketPath)
fmt.Println("Available methods:")
for _, method := range manager.ListMethods() {
fmt.Printf(" - %s\n", method)
}
fmt.Println("\nPress Ctrl+C to stop the server")
// Wait for interrupt signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
fmt.Println("\nShutting down...")
}

View File

@ -1,112 +0,0 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"net"
"os"
)
// RPCRequest represents an outgoing RPC request
type RPCRequest struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID interface{} `json:"id,omitempty"`
Secret string `json:"secret,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCResponse represents an incoming RPC response
type RPCResponse struct {
Result interface{} `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
ID interface{} `json:"id,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCError represents an RPC error
type RPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func main() {
// Parse command line arguments
socketPath := flag.String("socket", "/tmp/openrpc.sock", "Path to the Unix socket")
method := flag.String("method", "rpc.discover", "RPC method to call")
params := flag.String("params", "{}", "JSON parameters for the method")
secret := flag.String("secret", "", "Secret for authenticated methods")
flag.Parse()
// Connect to the Unix socket
conn, err := net.Dial("unix", *socketPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to connect to Unix socket: %v\n", err)
os.Exit(1)
}
defer conn.Close()
// Create the request
var paramsJSON json.RawMessage
if err := json.Unmarshal([]byte(*params), &paramsJSON); err != nil {
fmt.Fprintf(os.Stderr, "Invalid JSON parameters: %v\n", err)
os.Exit(1)
}
request := RPCRequest{
Method: *method,
Params: paramsJSON,
ID: 1,
Secret: *secret,
JSONRPC: "2.0",
}
// Marshal the request
requestData, err := json.Marshal(request)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to marshal request: %v\n", err)
os.Exit(1)
}
// Send the request
_, err = conn.Write(requestData)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to send request: %v\n", err)
os.Exit(1)
}
// Read the response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read response: %v\n", err)
os.Exit(1)
}
// Parse the response
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
fmt.Fprintf(os.Stderr, "Failed to unmarshal response: %v\n", err)
os.Exit(1)
}
// Check for errors
if response.Error != nil {
fmt.Fprintf(os.Stderr, "Error: %s (code: %d)\n", response.Error.Message, response.Error.Code)
if response.Error.Data != nil {
fmt.Fprintf(os.Stderr, "Error data: %v\n", response.Error.Data)
}
os.Exit(1)
}
// Print the result
resultJSON, err := json.MarshalIndent(response.Result, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to marshal result: %v\n", err)
os.Exit(1)
}
fmt.Println(string(resultJSON))
}

View File

@ -1,416 +0,0 @@
// Package openrpcmanager provides functionality for managing and handling OpenRPC method calls.
package openrpcmanager
import (
"encoding/json"
"fmt"
"sync"
"time"
)
// RPCHandler is a function that handles an OpenRPC method call
type RPCHandler func(params json.RawMessage) (interface{}, error)
// CallLog represents a log entry for an RPC method call
type CallLog struct {
Timestamp time.Time `json:"timestamp"` // When the call was made
Method string `json:"method"` // Method that was called
Params interface{} `json:"params"` // Parameters passed to the method (may be redacted for security)
Duration time.Duration `json:"duration"` // How long the call took to execute
Status string `json:"status"` // Success or error
ErrorMsg string `json:"error,omitempty"` // Error message if status is error
Authenticated bool `json:"authenticated"` // Whether the call was authenticated
}
// OpenRPCManager manages OpenRPC method handlers and processes requests
type OpenRPCManager struct {
handlers map[string]RPCHandler
schema OpenRPCSchema
mutex sync.RWMutex
secret string
// Call logging
callLogs []CallLog
callLogsMutex sync.RWMutex
maxCallLogs int // Maximum number of call logs to keep
}
// NewOpenRPCManager creates a new OpenRPC manager with the given schema and handlers
func NewOpenRPCManager(schema OpenRPCSchema, handlers map[string]RPCHandler, secret string) (*OpenRPCManager, error) {
manager := &OpenRPCManager{
handlers: make(map[string]RPCHandler),
schema: schema,
secret: secret,
callLogs: make([]CallLog, 0, 100),
maxCallLogs: 1000, // Default to keeping the last 1000 calls
}
// Validate that all methods in the schema have corresponding handlers
for _, method := range schema.Methods {
handler, exists := handlers[method.Name]
if !exists {
return nil, fmt.Errorf("missing handler for method '%s' defined in schema", method.Name)
}
manager.handlers[method.Name] = handler
}
// Check for handlers that don't have a corresponding method in the schema
for name := range handlers {
found := false
for _, method := range schema.Methods {
if method.Name == name {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("handler '%s' has no corresponding method in schema", name)
}
}
// Add the discovery method
manager.handlers["rpc.discover"] = manager.handleDiscovery
// Add the introspection method
manager.handlers["rpc.introspect"] = manager.handleIntrospection
return manager, nil
}
// handleDiscovery implements the OpenRPC service discovery method
func (m *OpenRPCManager) handleDiscovery(params json.RawMessage) (interface{}, error) {
return m.schema, nil
}
// handleIntrospection implements the OpenRPC service introspection method
// It returns information about recent RPC calls for monitoring and debugging
func (m *OpenRPCManager) handleIntrospection(params json.RawMessage) (interface{}, error) {
m.callLogsMutex.RLock()
defer m.callLogsMutex.RUnlock()
// Parse parameters to see if we need to filter or limit results
var requestParams struct {
Limit int `json:"limit,omitempty"`
Method string `json:"method,omitempty"`
Status string `json:"status,omitempty"`
}
// Default limit to 100 if not specified
requestParams.Limit = 100
// Try to parse parameters, but don't fail if they're invalid
if len(params) > 0 {
_ = json.Unmarshal(params, &requestParams)
}
// Apply limit
if requestParams.Limit <= 0 || requestParams.Limit > m.maxCallLogs {
requestParams.Limit = 100
}
// Create a copy of the logs to avoid holding the lock while filtering
allLogs := make([]CallLog, len(m.callLogs))
copy(allLogs, m.callLogs)
// Filter logs based on parameters
filteredLogs := []CallLog{}
for i := len(allLogs) - 1; i >= 0 && len(filteredLogs) < requestParams.Limit; i-- {
log := allLogs[i]
// Apply method filter if specified
if requestParams.Method != "" && log.Method != requestParams.Method {
continue
}
// Apply status filter if specified
if requestParams.Status != "" && log.Status != requestParams.Status {
continue
}
filteredLogs = append(filteredLogs, log)
}
// Create response
response := struct {
Logs []CallLog `json:"logs"`
Total int `json:"total"`
Filtered int `json:"filtered"`
}{
Logs: filteredLogs,
Total: len(allLogs),
Filtered: len(filteredLogs),
}
return response, nil
}
// RegisterHandler is deprecated. Use NewOpenRPCManager with a complete set of handlers instead.
// This method is kept for backward compatibility but will return an error for methods not in the schema.
func (m *OpenRPCManager) RegisterHandler(method string, handler RPCHandler) error {
m.mutex.Lock()
defer m.mutex.Unlock()
// Check if handler already exists
if _, exists := m.handlers[method]; exists {
return fmt.Errorf("handler for method '%s' already registered", method)
}
// Check if method exists in schema
found := false
for _, schemaMethod := range m.schema.Methods {
if schemaMethod.Name == method {
found = true
break
}
}
if !found && method != "rpc.discover" {
return fmt.Errorf("method '%s' not defined in schema", method)
}
m.handlers[method] = handler
return nil
}
// UnregisterHandler removes a handler for the specified method
// Note: This will make the service non-compliant with its schema
func (m *OpenRPCManager) UnregisterHandler(method string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
// Check if handler exists
if _, exists := m.handlers[method]; !exists {
return fmt.Errorf("handler for method '%s' not found", method)
}
// Don't allow unregistering the discovery method
if method == "rpc.discover" {
return fmt.Errorf("cannot unregister the discovery method 'rpc.discover'")
}
delete(m.handlers, method)
return nil
}
// HandleRequest processes an RPC request for the specified method
func (m *OpenRPCManager) HandleRequest(method string, params json.RawMessage) (interface{}, error) {
// Start timing the request
startTime := time.Now()
// Create a call log entry
callLog := CallLog{
Timestamp: startTime,
Method: method,
Authenticated: false,
}
// Parse params for logging, but don't fail if we can't
var parsedParams interface{}
if len(params) > 0 {
if err := json.Unmarshal(params, &parsedParams); err == nil {
callLog.Params = parsedParams
} else {
// If we can't parse the params, just store them as a string
callLog.Params = string(params)
}
}
// Find the handler
m.mutex.RLock()
handler, exists := m.handlers[method]
m.mutex.RUnlock()
if !exists {
// Log the error
callLog.Status = "error"
callLog.ErrorMsg = fmt.Sprintf("method '%s' not found", method)
callLog.Duration = time.Since(startTime)
// Add to call logs
m.logCall(callLog)
return nil, fmt.Errorf("method '%s' not found", method)
}
// Execute the handler
result, err := handler(params)
// Complete the call log
callLog.Duration = time.Since(startTime)
if err != nil {
callLog.Status = "error"
callLog.ErrorMsg = err.Error()
} else {
callLog.Status = "success"
}
// Add to call logs
m.logCall(callLog)
return result, err
}
// logCall adds a call log entry to the call logs, maintaining the maximum size
func (m *OpenRPCManager) logCall(log CallLog) {
m.callLogsMutex.Lock()
defer m.callLogsMutex.Unlock()
// Add the log to the call logs
m.callLogs = append(m.callLogs, log)
// Trim the call logs if they exceed the maximum size
if len(m.callLogs) > m.maxCallLogs {
m.callLogs = m.callLogs[len(m.callLogs)-m.maxCallLogs:]
}
}
// HandleRequestWithAuthentication processes an authenticated RPC request
func (m *OpenRPCManager) HandleRequestWithAuthentication(method string, params json.RawMessage, secret string) (interface{}, error) {
// Start timing the request
startTime := time.Now()
// Create a call log entry
callLog := CallLog{
Timestamp: startTime,
Method: method,
Authenticated: true,
}
// Parse params for logging, but don't fail if we can't
var parsedParams interface{}
if len(params) > 0 {
if err := json.Unmarshal(params, &parsedParams); err == nil {
callLog.Params = parsedParams
} else {
// If we can't parse the params, just store them as a string
callLog.Params = string(params)
}
}
// Verify the secret
if secret != m.secret {
// Log the authentication failure
callLog.Status = "error"
callLog.ErrorMsg = "authentication failed"
callLog.Duration = time.Since(startTime)
m.logCall(callLog)
return nil, fmt.Errorf("authentication failed")
}
// Execute the handler
m.mutex.RLock()
handler, exists := m.handlers[method]
m.mutex.RUnlock()
if !exists {
// Log the error
callLog.Status = "error"
callLog.ErrorMsg = fmt.Sprintf("method '%s' not found", method)
callLog.Duration = time.Since(startTime)
m.logCall(callLog)
return nil, fmt.Errorf("method '%s' not found", method)
}
// Execute the handler
result, err := handler(params)
// Complete the call log
callLog.Duration = time.Since(startTime)
if err != nil {
callLog.Status = "error"
callLog.ErrorMsg = err.Error()
} else {
callLog.Status = "success"
}
// Add to call logs
m.logCall(callLog)
return result, err
}
// ListMethods returns a list of all registered method names
func (m *OpenRPCManager) ListMethods() []string {
m.mutex.RLock()
defer m.mutex.RUnlock()
methods := make([]string, 0, len(m.handlers))
for method := range m.handlers {
methods = append(methods, method)
}
return methods
}
// GetSecret returns the authentication secret
func (m *OpenRPCManager) GetSecret() string {
return m.secret
}
// GetSchema returns the OpenRPC schema
func (m *OpenRPCManager) GetSchema() OpenRPCSchema {
return m.schema
}
// NewDefaultOpenRPCManager creates a new OpenRPC manager with a default schema
// This is provided for backward compatibility and testing
func NewDefaultOpenRPCManager(secret string) *OpenRPCManager {
// Create a minimal default schema
defaultSchema := OpenRPCSchema{
OpenRPC: "1.2.6",
Info: InfoObject{
Title: "Default OpenRPC Service",
Version: "1.0.0",
},
Methods: []MethodObject{
{
Name: "rpc.discover",
Description: "Returns the OpenRPC schema for this service",
Params: []ContentDescriptorObject{},
Result: &ContentDescriptorObject{
Name: "schema",
Description: "The OpenRPC schema",
Schema: SchemaObject{"type": "object"},
},
},
{
Name: "rpc.introspect",
Description: "Returns information about recent RPC calls for monitoring and debugging",
Params: []ContentDescriptorObject{
{
Name: "limit",
Description: "Maximum number of call logs to return",
Required: false,
Schema: SchemaObject{"type": "integer", "default": 100},
},
{
Name: "method",
Description: "Filter logs by method name",
Required: false,
Schema: SchemaObject{"type": "string"},
},
{
Name: "status",
Description: "Filter logs by status (success or error)",
Required: false,
Schema: SchemaObject{"type": "string", "enum": []interface{}{"success", "error"}},
},
},
Result: &ContentDescriptorObject{
Name: "introspection",
Description: "Introspection data including call logs",
Schema: SchemaObject{"type": "object"},
},
},
},
}
// Create the manager directly without validation since we're starting with empty methods
return &OpenRPCManager{
handlers: make(map[string]RPCHandler),
schema: defaultSchema,
secret: secret,
}
}

View File

@ -1,446 +0,0 @@
package openrpcmanager
import (
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// createTestSchema creates a test OpenRPC schema
func createTestSchema() OpenRPCSchema {
return OpenRPCSchema{
OpenRPC: "1.2.6",
Info: InfoObject{
Title: "Test API",
Version: "1.0.0",
},
Methods: []MethodObject{
{
Name: "echo",
Params: []ContentDescriptorObject{
{
Name: "message",
Schema: SchemaObject{"type": "object"},
},
},
Result: &ContentDescriptorObject{
Name: "result",
Schema: SchemaObject{"type": "object"},
},
},
{
Name: "add",
Params: []ContentDescriptorObject{
{
Name: "numbers",
Schema: SchemaObject{"type": "object"},
},
},
Result: &ContentDescriptorObject{
Name: "result",
Schema: SchemaObject{"type": "number"},
},
},
{
Name: "secure.method",
Params: []ContentDescriptorObject{},
Result: &ContentDescriptorObject{
Name: "result",
Schema: SchemaObject{"type": "string"},
},
},
},
}
}
// createTestHandlers creates test handlers for the schema
func createTestHandlers() map[string]RPCHandler {
return map[string]RPCHandler{
"echo": func(params json.RawMessage) (interface{}, error) {
var data interface{}
if err := json.Unmarshal(params, &data); err != nil {
return nil, err
}
return data, nil
},
"add": func(params json.RawMessage) (interface{}, error) {
var numbers struct {
A float64 `json:"a"`
B float64 `json:"b"`
}
if err := json.Unmarshal(params, &numbers); err != nil {
return nil, err
}
return numbers.A + numbers.B, nil
},
"secure.method": func(params json.RawMessage) (interface{}, error) {
return "secure data", nil
},
}
}
// TestNewOpenRPCManager tests the creation of a new OpenRPC manager
func TestNewOpenRPCManager(t *testing.T) {
secret := "test-secret"
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, secret)
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
if manager == nil {
t.Fatal("Manager is nil")
}
if manager.GetSecret() != secret {
t.Errorf("Secret mismatch. Expected: %s, Got: %s", secret, manager.GetSecret())
}
if manager.handlers == nil {
t.Error("handlers map not initialized")
}
// Test the default manager for backward compatibility
defaultManager := NewDefaultOpenRPCManager(secret)
if defaultManager == nil {
t.Fatal("Default manager is nil")
}
if defaultManager.GetSecret() != secret {
t.Errorf("Secret mismatch in default manager. Expected: %s, Got: %s", secret, defaultManager.GetSecret())
}
}
// TestRegisterHandler tests registering a handler to the OpenRPC manager
func TestRegisterHandler(t *testing.T) {
manager := NewDefaultOpenRPCManager("test-secret")
// Define a mock handler
mockHandler := func(params json.RawMessage) (interface{}, error) {
return "mock response", nil
}
// Add a method to the schema
manager.schema.Methods = append(manager.schema.Methods, MethodObject{
Name: "test.method",
Params: []ContentDescriptorObject{},
Result: &ContentDescriptorObject{
Name: "result",
Schema: SchemaObject{"type": "string"},
},
})
// Register the handler
err := manager.RegisterHandler("test.method", mockHandler)
if err != nil {
t.Fatalf("Failed to register handler: %v", err)
}
// Check if handler was registered
if _, exists := manager.handlers["test.method"]; !exists {
t.Error("Handler was not registered")
}
// Try to register the same handler again, should fail
err = manager.RegisterHandler("test.method", mockHandler)
if err == nil {
t.Error("Expected error when registering duplicate handler, but got nil")
}
// Try to register a handler for a method not in the schema
err = manager.RegisterHandler("not.in.schema", mockHandler)
if err == nil {
t.Error("Expected error when registering handler for method not in schema, but got nil")
}
}
// TestHandleRequest tests handling an RPC request
func TestHandleRequest(t *testing.T) {
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, "test-secret")
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
// Test the echo handler
testParams := json.RawMessage(`{"message":"hello world"}`)
result, err := manager.HandleRequest("echo", testParams)
if err != nil {
t.Fatalf("Failed to handle request: %v", err)
}
// Convert result to map for comparison
resultMap, ok := result.(map[string]interface{})
if !ok {
t.Fatalf("Expected map result, got: %T", result)
}
if resultMap["message"] != "hello world" {
t.Errorf("Expected 'hello world', got: %v", resultMap["message"])
}
// Test the add handler
addParams := json.RawMessage(`{"a":5,"b":7}`)
addResult, err := manager.HandleRequest("add", addParams)
if err != nil {
t.Fatalf("Failed to handle add request: %v", err)
}
// Check the result type and value
resultValue, ok := addResult.(float64)
if !ok {
t.Fatalf("Expected result type float64, got: %T", addResult)
}
if resultValue != float64(12) {
t.Errorf("Expected 12, got: %v", resultValue)
}
// Test with non-existent method
_, err = manager.HandleRequest("nonexistent", testParams)
if err == nil {
t.Error("Expected error for non-existent method, but got nil")
}
// Test the discovery method
discoveryResult, err := manager.HandleRequest("rpc.discover", json.RawMessage(`{}`))
if err != nil {
t.Fatalf("Failed to handle discovery request: %v", err)
}
// Verify the discovery result is the schema
discoverySchema, ok := discoveryResult.(OpenRPCSchema)
if !ok {
t.Fatalf("Expected OpenRPCSchema result, got: %T", discoveryResult)
}
if discoverySchema.OpenRPC != schema.OpenRPC {
t.Errorf("Expected OpenRPC version %s, got: %s", schema.OpenRPC, discoverySchema.OpenRPC)
}
if len(discoverySchema.Methods) != len(schema.Methods) {
t.Errorf("Expected %d methods, got: %d", len(schema.Methods), len(discoverySchema.Methods))
}
}
// TestHandleRequestWithAuthentication tests handling a request with authentication
func TestHandleRequestWithAuthentication(t *testing.T) {
secret := "test-secret"
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, secret)
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
// Test with correct secret
result, err := manager.HandleRequestWithAuthentication("secure.method", json.RawMessage(`{}`), secret)
if err != nil {
t.Fatalf("Failed to handle authenticated request: %v", err)
}
if result != "secure data" {
t.Errorf("Expected 'secure data', got: %v", result)
}
// Test with incorrect secret
_, err = manager.HandleRequestWithAuthentication("secure.method", json.RawMessage(`{}`), "wrong-secret")
if err == nil {
t.Error("Expected authentication error, but got nil")
}
}
// TestUnregisterHandler tests removing a handler from the OpenRPC manager
func TestUnregisterHandler(t *testing.T) {
manager := NewDefaultOpenRPCManager("test-secret")
// Define a mock handler
mockHandler := func(params json.RawMessage) (interface{}, error) {
return "mock response", nil
}
// Add a method to the schema
manager.schema.Methods = append(manager.schema.Methods, MethodObject{
Name: "test.method",
Params: []ContentDescriptorObject{},
Result: &ContentDescriptorObject{
Name: "result",
Schema: SchemaObject{"type": "string"},
},
})
// Register the handler
manager.RegisterHandler("test.method", mockHandler)
// Unregister the handler
err := manager.UnregisterHandler("test.method")
if err != nil {
t.Fatalf("Failed to unregister handler: %v", err)
}
// Check if handler was unregistered
if _, exists := manager.handlers["test.method"]; exists {
t.Error("Handler was not unregistered")
}
// Try to unregister non-existent handler
err = manager.UnregisterHandler("nonexistent")
if err == nil {
t.Error("Expected error when unregistering non-existent handler, but got nil")
}
// Try to unregister the discovery method
err = manager.UnregisterHandler("rpc.discover")
if err == nil {
t.Error("Expected error when unregistering discovery method, but got nil")
}
}
// TestIntrospection tests the introspection functionality
func TestIntrospection(t *testing.T) {
// Create a test manager
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, "test-secret")
assert.NoError(t, err)
// The introspection handler is already registered in NewOpenRPCManager
// Make some test calls to generate logs
_, err = manager.HandleRequest("echo", json.RawMessage(`{"message":"hello"}`))
assert.NoError(t, err)
_, err = manager.HandleRequestWithAuthentication("echo", json.RawMessage(`{"message":"authenticated"}`), "test-secret")
assert.NoError(t, err)
_, err = manager.HandleRequestWithAuthentication("echo", json.RawMessage(`{"message":"auth-fail"}`), "wrong-secret")
assert.Error(t, err)
// Wait a moment to ensure timestamps are different
time.Sleep(10 * time.Millisecond)
// Call the introspection handler
result, err := manager.handleIntrospection(json.RawMessage(`{"limit":10}`))
assert.NoError(t, err)
// Verify the result
response, ok := result.(struct {
Logs []CallLog `json:"logs"`
Total int `json:"total"`
Filtered int `json:"filtered"`
})
assert.True(t, ok)
// Should have 3 logs (2 successful calls, 1 auth failure)
assert.Equal(t, 3, response.Total)
assert.Equal(t, 3, response.Filtered)
assert.Len(t, response.Logs, 3)
// Test filtering by method
result, err = manager.handleIntrospection(json.RawMessage(`{"method":"echo"}`))
assert.NoError(t, err)
response, ok = result.(struct {
Logs []CallLog `json:"logs"`
Total int `json:"total"`
Filtered int `json:"filtered"`
})
assert.True(t, ok)
assert.Equal(t, 3, response.Total) // Total is still 3
assert.Equal(t, 3, response.Filtered) // All 3 match the method filter
// Test filtering by status
result, err = manager.handleIntrospection(json.RawMessage(`{"status":"error"}`))
assert.NoError(t, err)
response, ok = result.(struct {
Logs []CallLog `json:"logs"`
Total int `json:"total"`
Filtered int `json:"filtered"`
})
assert.True(t, ok)
assert.Equal(t, 3, response.Total) // Total is still 3
assert.Equal(t, 1, response.Filtered) // Only 1 error
assert.Len(t, response.Logs, 1)
assert.Equal(t, "error", response.Logs[0].Status)
}
// TestListMethods tests listing all registered methods
func TestListMethods(t *testing.T) {
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, "test-secret")
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
// List all methods
registeredMethods := manager.ListMethods()
// Check if all methods plus discovery and introspection methods are listed
expectedCount := len(schema.Methods) + 2 // +2 for rpc.discover and rpc.introspect
if len(registeredMethods) != expectedCount {
t.Errorf("Expected %d methods, got %d", expectedCount, len(registeredMethods))
}
// Check if all schema methods are in the list
for _, methodObj := range schema.Methods {
found := false
for _, registeredMethod := range registeredMethods {
if registeredMethod == methodObj.Name {
found = true
break
}
}
if !found {
t.Errorf("Method %s not found in list", methodObj.Name)
}
}
// Check if discovery method is in the list
found := false
for _, registeredMethod := range registeredMethods {
if registeredMethod == "rpc.discover" {
found = true
break
}
}
if !found {
t.Error("Discovery method 'rpc.discover' not found in list")
}
}
// TestSchemaValidation tests that the schema validation works correctly
func TestSchemaValidation(t *testing.T) {
secret := "test-secret"
schema := createTestSchema()
// Test with missing handler
incompleteHandlers := map[string]RPCHandler{
"echo": func(params json.RawMessage) (interface{}, error) {
return nil, nil
},
// Missing "add" handler
"secure.method": func(params json.RawMessage) (interface{}, error) {
return nil, nil
},
}
_, err := NewOpenRPCManager(schema, incompleteHandlers, secret)
if err == nil {
t.Error("Expected error when missing handler for schema method, but got nil")
}
// Test with extra handler not in schema
extraHandlers := createTestHandlers()
extraHandlers["not.in.schema"] = func(params json.RawMessage) (interface{}, error) {
return nil, nil
}
_, err = NewOpenRPCManager(schema, extraHandlers, secret)
if err == nil {
t.Error("Expected error when handler has no corresponding method in schema, but got nil")
}
}

View File

@ -1,117 +0,0 @@
package openrpcmanager
// OpenRPCSchema represents the OpenRPC specification document
// Based on OpenRPC Specification 1.2.6: https://spec.open-rpc.org/
type OpenRPCSchema struct {
OpenRPC string `json:"openrpc"` // Required: Version of the OpenRPC specification
Info InfoObject `json:"info"` // Required: Information about the API
Methods []MethodObject `json:"methods"` // Required: List of method objects
ExternalDocs *ExternalDocsObject `json:"externalDocs,omitempty"` // Optional: External documentation
Servers []ServerObject `json:"servers,omitempty"` // Optional: List of servers
Components *ComponentsObject `json:"components,omitempty"` // Optional: Reusable components
}
// InfoObject provides metadata about the API
type InfoObject struct {
Title string `json:"title"` // Required: Title of the API
Description string `json:"description,omitempty"` // Optional: Description of the API
Version string `json:"version"` // Required: Version of the API
TermsOfService string `json:"termsOfService,omitempty"` // Optional: Terms of service URL
Contact *ContactObject `json:"contact,omitempty"` // Optional: Contact information
License *LicenseObject `json:"license,omitempty"` // Optional: License information
}
// ContactObject provides contact information for the API
type ContactObject struct {
Name string `json:"name,omitempty"` // Optional: Name of the contact
URL string `json:"url,omitempty"` // Optional: URL of the contact
Email string `json:"email,omitempty"` // Optional: Email of the contact
}
// LicenseObject provides license information for the API
type LicenseObject struct {
Name string `json:"name"` // Required: Name of the license
URL string `json:"url,omitempty"` // Optional: URL of the license
}
// ExternalDocsObject provides a URL to external documentation
type ExternalDocsObject struct {
Description string `json:"description,omitempty"` // Optional: Description of the external docs
URL string `json:"url"` // Required: URL of the external docs
}
// ServerObject provides connection information to a server
type ServerObject struct {
Name string `json:"name,omitempty"` // Optional: Name of the server
Description string `json:"description,omitempty"` // Optional: Description of the server
URL string `json:"url"` // Required: URL of the server
Variables map[string]ServerVariable `json:"variables,omitempty"` // Optional: Server variables
}
// ServerVariable is a variable for server URL template substitution
type ServerVariable struct {
Default string `json:"default"` // Required: Default value of the variable
Description string `json:"description,omitempty"` // Optional: Description of the variable
Enum []string `json:"enum,omitempty"` // Optional: Enumeration of possible values
}
// MethodObject describes an RPC method
type MethodObject struct {
Name string `json:"name"` // Required: Name of the method
Description string `json:"description,omitempty"` // Optional: Description of the method
Summary string `json:"summary,omitempty"` // Optional: Summary of the method
Params []ContentDescriptorObject `json:"params"` // Required: List of parameters
Result *ContentDescriptorObject `json:"result"` // Required: Description of the result
Deprecated bool `json:"deprecated,omitempty"` // Optional: Whether the method is deprecated
Errors []ErrorObject `json:"errors,omitempty"` // Optional: List of possible errors
Tags []TagObject `json:"tags,omitempty"` // Optional: List of tags
ExternalDocs *ExternalDocsObject `json:"externalDocs,omitempty"` // Optional: External documentation
ParamStructure string `json:"paramStructure,omitempty"` // Optional: Structure of the parameters
}
// ContentDescriptorObject describes the content of a parameter or result
type ContentDescriptorObject struct {
Name string `json:"name"` // Required: Name of the parameter
Description string `json:"description,omitempty"` // Optional: Description of the parameter
Summary string `json:"summary,omitempty"` // Optional: Summary of the parameter
Required bool `json:"required,omitempty"` // Optional: Whether the parameter is required
Deprecated bool `json:"deprecated,omitempty"` // Optional: Whether the parameter is deprecated
Schema SchemaObject `json:"schema"` // Required: JSON Schema of the parameter
}
// SchemaObject is a JSON Schema definition
// This is a simplified version, in a real implementation you would use a full JSON Schema library
type SchemaObject map[string]interface{}
// ErrorObject describes an error that may be returned
type ErrorObject struct {
Code int `json:"code"` // Required: Error code
Message string `json:"message"` // Required: Error message
Data interface{} `json:"data,omitempty"` // Optional: Additional error data
}
// TagObject describes a tag for documentation purposes
type TagObject struct {
Name string `json:"name"` // Required: Name of the tag
Description string `json:"description,omitempty"` // Optional: Description of the tag
ExternalDocs *ExternalDocsObject `json:"externalDocs,omitempty"` // Optional: External documentation
}
// ComponentsObject holds reusable objects for different aspects of the OpenRPC spec
type ComponentsObject struct {
Schemas map[string]SchemaObject `json:"schemas,omitempty"` // Optional: Reusable schemas
ContentDescriptors map[string]ContentDescriptorObject `json:"contentDescriptors,omitempty"` // Optional: Reusable content descriptors
Examples map[string]interface{} `json:"examples,omitempty"` // Optional: Reusable examples
Links map[string]LinkObject `json:"links,omitempty"` // Optional: Reusable links
Errors map[string]ErrorObject `json:"errors,omitempty"` // Optional: Reusable errors
}
// LinkObject describes a link between operations
type LinkObject struct {
Name string `json:"name,omitempty"` // Optional: Name of the link
Description string `json:"description,omitempty"` // Optional: Description of the link
Summary string `json:"summary,omitempty"` // Optional: Summary of the link
Method string `json:"method"` // Required: Method name
Params map[string]interface{} `json:"params,omitempty"` // Optional: Parameters for the method
Server *ServerObject `json:"server,omitempty"` // Optional: Server for the method
}

View File

@ -1,241 +0,0 @@
package openrpcmanager
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"path/filepath"
"sync"
)
// RPCRequest represents an incoming RPC request
type RPCRequest struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID interface{} `json:"id,omitempty"`
Secret string `json:"secret,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCResponse represents an outgoing RPC response
type RPCResponse struct {
Result interface{} `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
ID interface{} `json:"id,omitempty"`
JSONRPC string `json:"jsonrpc"`
}
// RPCError represents an RPC error
type RPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// UnixServer represents a Unix socket server for the OpenRPC manager
type UnixServer struct {
manager *OpenRPCManager
socketPath string
listener net.Listener
connections map[net.Conn]bool
mutex sync.Mutex
wg sync.WaitGroup
done chan struct{}
}
// NewUnixServer creates a new Unix socket server for the OpenRPC manager
func NewUnixServer(manager *OpenRPCManager, socketPath string) (*UnixServer, error) {
// Create directory if it doesn't exist
dir := filepath.Dir(socketPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, fmt.Errorf("failed to create socket directory: %w", err)
}
// Remove socket if it already exists
if _, err := os.Stat(socketPath); err == nil {
if err := os.Remove(socketPath); err != nil {
return nil, fmt.Errorf("failed to remove existing socket: %w", err)
}
}
return &UnixServer{
manager: manager,
socketPath: socketPath,
connections: make(map[net.Conn]bool),
done: make(chan struct{}),
}, nil
}
// Start starts the Unix socket server
func (s *UnixServer) Start() error {
listener, err := net.Listen("unix", s.socketPath)
if err != nil {
return fmt.Errorf("failed to listen on unix socket: %w", err)
}
s.listener = listener
// Set socket permissions
if err := os.Chmod(s.socketPath, 0660); err != nil {
s.listener.Close()
return fmt.Errorf("failed to set socket permissions: %w", err)
}
s.wg.Add(1)
go s.acceptConnections()
return nil
}
// Stop stops the Unix socket server
func (s *UnixServer) Stop() error {
close(s.done)
// Close the listener
if s.listener != nil {
s.listener.Close()
}
// Close all connections
s.mutex.Lock()
for conn := range s.connections {
conn.Close()
}
s.mutex.Unlock()
// Wait for all goroutines to finish
s.wg.Wait()
// Remove the socket file
os.Remove(s.socketPath)
return nil
}
// acceptConnections accepts incoming connections
func (s *UnixServer) acceptConnections() {
defer s.wg.Done()
for {
select {
case <-s.done:
return
default:
conn, err := s.listener.Accept()
if err != nil {
select {
case <-s.done:
return
default:
fmt.Printf("Error accepting connection: %v\n", err)
continue
}
}
s.mutex.Lock()
s.connections[conn] = true
s.mutex.Unlock()
s.wg.Add(1)
go s.handleConnection(conn)
}
}
}
// handleConnection handles a client connection
func (s *UnixServer) handleConnection(conn net.Conn) {
defer func() {
s.mutex.Lock()
delete(s.connections, conn)
s.mutex.Unlock()
conn.Close()
s.wg.Done()
}()
buf := make([]byte, 4096)
for {
select {
case <-s.done:
return
default:
n, err := conn.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Printf("Error reading from connection: %v\n", err)
}
return
}
if n > 0 {
go s.handleRequest(conn, buf[:n])
}
}
}
}
// handleRequest processes an RPC request
func (s *UnixServer) handleRequest(conn net.Conn, data []byte) {
var req RPCRequest
if err := json.Unmarshal(data, &req); err != nil {
s.sendErrorResponse(conn, nil, -32700, "Parse error", err)
return
}
// Validate JSON-RPC version
if req.JSONRPC != "2.0" {
s.sendErrorResponse(conn, req.ID, -32600, "Invalid Request", "Invalid JSON-RPC version")
return
}
var result interface{}
var err error
// Check if authentication is required
if req.Secret != "" {
result, err = s.manager.HandleRequestWithAuthentication(req.Method, req.Params, req.Secret)
} else {
result, err = s.manager.HandleRequest(req.Method, req.Params)
}
if err != nil {
s.sendErrorResponse(conn, req.ID, -32603, "Internal error", err.Error())
return
}
// Send success response
response := RPCResponse{
Result: result,
ID: req.ID,
JSONRPC: "2.0",
}
responseData, err := json.Marshal(response)
if err != nil {
s.sendErrorResponse(conn, req.ID, -32603, "Internal error", err.Error())
return
}
conn.Write(responseData)
}
// sendErrorResponse sends an error response
func (s *UnixServer) sendErrorResponse(conn net.Conn, id interface{}, code int, message string, data interface{}) {
response := RPCResponse{
Error: &RPCError{
Code: code,
Message: message,
Data: data,
},
ID: id,
JSONRPC: "2.0",
}
responseData, err := json.Marshal(response)
if err != nil {
fmt.Printf("Error marshaling error response: %v\n", err)
return
}
conn.Write(responseData)
}

View File

@ -1,362 +0,0 @@
package openrpcmanager
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"testing"
"time"
)
func TestUnixServer(t *testing.T) {
// Create a temporary socket path
tempDir, err := os.MkdirTemp("", "openrpc-test")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir)
socketPath := filepath.Join(tempDir, "openrpc.sock")
// Create OpenRPC manager
schema := createTestSchema()
handlers := createTestHandlers()
manager, err := NewOpenRPCManager(schema, handlers, "test-secret")
if err != nil {
t.Fatalf("Failed to create OpenRPCManager: %v", err)
}
// Create and start Unix server
server, err := NewUnixServer(manager, socketPath)
if err != nil {
t.Fatalf("Failed to create UnixServer: %v", err)
}
if err := server.Start(); err != nil {
t.Fatalf("Failed to start UnixServer: %v", err)
}
defer server.Stop()
// Wait for server to start
time.Sleep(100 * time.Millisecond)
// Test connection
conn, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatalf("Failed to connect to Unix socket: %v", err)
}
defer conn.Close()
// Test echo method
t.Run("Echo method", func(t *testing.T) {
request := RPCRequest{
Method: "echo",
Params: json.RawMessage(`{"message":"hello world"}`),
ID: 1,
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error != nil {
t.Fatalf("Received error response: %v", response.Error)
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
// Check result
resultMap, ok := response.Result.(map[string]interface{})
if !ok {
t.Fatalf("Expected map result, got: %T", response.Result)
}
if resultMap["message"] != "hello world" {
t.Errorf("Expected 'hello world', got: %v", resultMap["message"])
}
})
// Test add method
t.Run("Add method", func(t *testing.T) {
request := RPCRequest{
Method: "add",
Params: json.RawMessage(`{"a":5,"b":7}`),
ID: 2,
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error != nil {
t.Fatalf("Received error response: %v", response.Error)
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
// Check result
resultValue, ok := response.Result.(float64)
if !ok {
t.Fatalf("Expected float64 result, got: %T", response.Result)
}
if resultValue != float64(12) {
t.Errorf("Expected 12, got: %v", resultValue)
}
})
// Test authenticated method
t.Run("Authenticated method", func(t *testing.T) {
request := RPCRequest{
Method: "secure.method",
Params: json.RawMessage(`{}`),
ID: 3,
Secret: "test-secret",
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error != nil {
t.Fatalf("Received error response: %v", response.Error)
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
// Check result
resultValue, ok := response.Result.(string)
if !ok {
t.Fatalf("Expected string result, got: %T", response.Result)
}
if resultValue != "secure data" {
t.Errorf("Expected 'secure data', got: %v", resultValue)
}
})
// Test authentication failure
t.Run("Authentication failure", func(t *testing.T) {
request := RPCRequest{
Method: "secure.method",
Params: json.RawMessage(`{}`),
ID: 4,
Secret: "wrong-secret",
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error == nil {
t.Fatal("Expected error response, but got nil")
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
if response.Error.Code != -32603 {
t.Errorf("Expected error code -32603, got: %v", response.Error.Code)
}
})
// Test non-existent method
t.Run("Non-existent method", func(t *testing.T) {
request := RPCRequest{
Method: "nonexistent",
Params: json.RawMessage(`{}`),
ID: 5,
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error == nil {
t.Fatal("Expected error response, but got nil")
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
if response.Error.Code != -32603 {
t.Errorf("Expected error code -32603, got: %v", response.Error.Code)
}
})
// Test discovery method
t.Run("Discovery method", func(t *testing.T) {
request := RPCRequest{
Method: "rpc.discover",
Params: json.RawMessage(`{}`),
ID: 6,
JSONRPC: "2.0",
}
requestData, err := json.Marshal(request)
if err != nil {
t.Fatalf("Failed to marshal request: %v", err)
}
_, err = conn.Write(requestData)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
// Read response
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var response RPCResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
// Check response
if response.Error != nil {
t.Fatalf("Received error response: %v", response.Error)
}
// Note: JSON unmarshaling may convert numbers to float64, so we need to check the value not exact type
if fmt.Sprintf("%v", response.ID) != fmt.Sprintf("%v", request.ID) {
t.Errorf("Response ID mismatch. Expected: %v, Got: %v", request.ID, response.ID)
}
// Check that we got a valid schema
resultMap, ok := response.Result.(map[string]interface{})
if !ok {
t.Fatalf("Expected map result, got: %T", response.Result)
}
if resultMap["openrpc"] != "1.2.6" {
t.Errorf("Expected OpenRPC version 1.2.6, got: %v", resultMap["openrpc"])
}
methods, ok := resultMap["methods"].([]interface{})
if !ok {
t.Fatalf("Expected methods array, got: %T", resultMap["methods"])
}
if len(methods) < 3 {
t.Errorf("Expected at least 3 methods, got: %d", len(methods))
}
})
}

View File

@ -5,8 +5,8 @@ import (
"log"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc"
"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
@ -39,10 +39,10 @@ func RunClientExample(socketPath, secret string) error {
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{}:

View File

@ -7,7 +7,7 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
)
func main() {

View File

@ -6,8 +6,8 @@ import (
"sync"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
)
// MockProcessManager implements the interfaces.ProcessManagerInterface for testing purposes

View File

@ -4,8 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager/client"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"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

View File

@ -4,9 +4,9 @@ import (
"encoding/json"
"fmt"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"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

View File

@ -6,8 +6,8 @@ import (
"testing"
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"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"
)

View File

@ -7,7 +7,7 @@ import (
"path/filepath"
"runtime"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager"
)
// LoadSchema loads the OpenRPC schema from the embedded JSON file

View File

@ -6,8 +6,8 @@ import (
"os"
"path/filepath"
"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager"
"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces"
"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces"
)
// Server represents the Process Manager OpenRPC server

View File

@ -3,7 +3,7 @@ package interfaces
import (
"time"
"github.com/freeflowuniverse/heroagent/pkg/processmanager"
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
)
// ProcessManagerInterface defines the interface for process management operations

View File

@ -14,7 +14,7 @@ import (
"time"
"unicode"
"github.com/freeflowuniverse/heroagent/pkg/logger"
"git.ourworld.tf/herocode/heroagent/pkg/logger"
"github.com/shirou/gopsutil/v3/process"
)

View File

@ -24,7 +24,7 @@ The server implements the following Redis commands:
### Basic Usage
```go
import "github.com/freeflowuniverse/heroagent/pkg/redisserver"
import "git.ourworld.tf/herocode/heroagent/pkg/redisserver"
// Create a new server with default configuration
server := redisserver.NewServer(redisserver.ServerConfig{

View File

@ -10,7 +10,7 @@ import (
"sync"
"time"
"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver"
"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver"
"github.com/redis/go-redis/v9"
)

View File

@ -10,7 +10,7 @@ import (
"sync"
"time"
"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver"
"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver"
"github.com/redis/go-redis/v9"
)

View File

@ -5,7 +5,7 @@ import (
"fmt"
"os"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/hetznerinstall"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/hetznerinstall"
)
func main() {

View File

@ -6,10 +6,10 @@ import (
"os/exec"
"path/filepath"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/dependencies"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/gosp"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/postgres"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/verification"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/dependencies"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/gosp"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/postgres"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/verification"
)
// Constants for PostgreSQL installation

View File

@ -4,7 +4,7 @@ import (
"fmt"
"os"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql"
)
func main() {

View File

@ -6,7 +6,7 @@ import (
"os/exec"
"path/filepath"
"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/postgres"
"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/postgres"
)
// Constants for Go stored procedure
@ -46,10 +46,10 @@ func (b *GoSPBuilder) run(cmd string, args ...string) error {
fmt.Println("Running:", cmd, args)
c := exec.Command(cmd, args...)
// Set environment variables
c.Env = append(os.Environ(),
c.Env = append(os.Environ(),
"GOROOT=/usr/local/go",
"GOPATH=/root/go",
"PATH=/usr/local/go/bin:" + os.Getenv("PATH"))
"GOPATH=/root/go",
"PATH=/usr/local/go/bin:"+os.Getenv("PATH"))
c.Stdout = os.Stdout
c.Stderr = os.Stderr
return c.Run()
@ -58,7 +58,7 @@ func (b *GoSPBuilder) run(cmd string, args ...string) error {
// Build builds a Go stored procedure
func (b *GoSPBuilder) Build() error {
fmt.Println("Building Go stored procedure...")
// Use the explicitly provided Go path if available
var goExePath string
if b.GoPath != "" {
@ -74,7 +74,7 @@ func (b *GoSPBuilder) Build() error {
}
fmt.Printf("Using detected Go executable from: %s\n", goExePath)
}
if err := os.MkdirAll(b.GoSharedLibDir, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
@ -98,27 +98,27 @@ func main() {}
// Use the full path to Go rather than relying on PATH
fmt.Println("Running Go build with full path:", goExePath)
// Show debug information
fmt.Println("Environment variables that will be set:")
fmt.Println(" GOROOT=/usr/local/go")
fmt.Println(" GOPATH=/root/go")
fmt.Println(" PATH=/usr/local/go/bin:" + os.Getenv("PATH"))
// Verify that the Go executable exists before using it
if _, err := os.Stat(goExePath); err != nil {
return fmt.Errorf("Go executable not found at %s: %w", goExePath, err)
}
// Create the output directory if it doesn't exist
outputDir := filepath.Join(b.InstallPrefix, "lib")
if err := os.MkdirAll(outputDir, 0755); err != nil {
return fmt.Errorf("failed to create output directory %s: %w", outputDir, err)
}
// Prepare output path
outputPath := filepath.Join(outputDir, "libgosp.so")
// Instead of relying on environment variables, create a wrapper shell script
// that sets all required environment variables and then calls the Go executable
tempDir, err := os.MkdirTemp("", "go-build-")
@ -126,7 +126,7 @@ func main() {}
return fmt.Errorf("failed to create temp directory: %w", err)
}
defer os.RemoveAll(tempDir) // Clean up when done
goRoot := filepath.Dir(filepath.Dir(goExePath)) // /usr/local/go
wrapperScript := filepath.Join(tempDir, "go-wrapper.sh")
wrapperContent := fmt.Sprintf(`#!/bin/sh
@ -143,25 +143,25 @@ echo "PATH=$PATH"
echo "=== Running Go command ==="
echo "%s $@"
exec %s "$@"
`,
goRoot,
`,
goRoot,
filepath.Dir(goExePath),
goExePath,
goExePath)
// Write the wrapper script
if err := os.WriteFile(wrapperScript, []byte(wrapperContent), 0755); err != nil {
return fmt.Errorf("failed to write wrapper script: %w", err)
}
fmt.Printf("Created wrapper script at %s\n", wrapperScript)
// Use the wrapper script to build the Go shared library
cmd := exec.Command(wrapperScript, "build", "-buildmode=c-shared", "-o", outputPath, libPath)
cmd.Dir = filepath.Dir(libPath) // Set working directory to where the source file is
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Printf("Executing Go build via wrapper script\n")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to build Go stored procedure: %w", err)

View File

@ -8,7 +8,7 @@ import (
"runtime"
"strings"
"github.com/mholt/archiver/v3"
"github.com/mholt/archiver/v4"
)
const (
@ -51,14 +51,14 @@ func (g *GoInstaller) GetGoVersion() (string, error) {
if err != nil {
return "", fmt.Errorf("failed to get Go version: %w", err)
}
// Parse go version output (format: "go version go1.x.x ...")
version := strings.TrimSpace(string(output))
parts := strings.Split(version, " ")
if len(parts) < 3 {
return "", fmt.Errorf("unexpected go version output format: %s", version)
}
// Return just the version number without the "go" prefix
return strings.TrimPrefix(parts[2], "go"), nil
}
@ -77,7 +77,7 @@ func (g *GoInstaller) InstallGo() (string, error) {
// Default Go installation location
var installDir string = "/usr/local"
var goExePath string = filepath.Join(installDir, "go", "bin", "go")
// Check if Go is already installed by checking the binary directly
if _, err := os.Stat(goExePath); err == nil {
version, err := g.GetGoVersion()
@ -86,7 +86,7 @@ func (g *GoInstaller) InstallGo() (string, error) {
return goExePath, nil
}
}
// Also check if Go is available in PATH as a fallback
if g.IsGoInstalled() {
path, err := exec.LookPath("go")
@ -98,31 +98,31 @@ func (g *GoInstaller) InstallGo() (string, error) {
}
}
}
fmt.Printf("Installing Go version %s...\n", g.Version)
// Determine architecture and OS
goOS := runtime.GOOS
goArch := runtime.GOARCH
// Construct download URL
downloadURL := fmt.Sprintf("https://golang.org/dl/go%s.%s-%s.tar.gz", g.Version, goOS, goArch)
// Create a temporary directory for download
tempDir, err := os.MkdirTemp("", "go-install-")
if err != nil {
return "", fmt.Errorf("failed to create temporary directory: %w", err)
}
defer os.RemoveAll(tempDir)
// Download Go tarball
tarballPath := filepath.Join(tempDir, "go.tar.gz")
if err := downloadFile(downloadURL, tarballPath); err != nil {
return "", fmt.Errorf("failed to download Go: %w", err)
}
// Install directory - typically /usr/local for Linux/macOS
// Check if existing Go installation exists and remove it
existingGoDir := filepath.Join(installDir, "go")
if _, err := os.Stat(existingGoDir); err == nil {
@ -131,34 +131,34 @@ func (g *GoInstaller) InstallGo() (string, error) {
return "", fmt.Errorf("failed to remove existing Go installation: %w", err)
}
}
// Extract tarball to install directory
fmt.Printf("Extracting Go to %s\n", installDir)
err = extractTarGz(tarballPath, installDir)
if err != nil {
return "", fmt.Errorf("failed to extract Go tarball: %w", err)
}
// Verify installation
var goExePathVerify = filepath.Join(installDir, "go", "bin", "go") // Use = instead of := to avoid variable shadowing
// Check if the Go binary exists
var statErr error
_, statErr = os.Stat(goExePathVerify)
if statErr != nil {
return "", fmt.Errorf("Go installation failed - go executable not found at %s", goExePathVerify)
}
// Set up environment variables
fmt.Println("Setting up Go environment variables...")
// Update PATH in /etc/profile
profilePath := "/etc/profile"
profileContent, err := os.ReadFile(profilePath)
if err != nil {
return "", fmt.Errorf("failed to read profile: %w", err)
}
// Add Go bin to PATH if not already there
goBinPath := filepath.Join(installDir, "go", "bin")
if !strings.Contains(string(profileContent), goBinPath) {
@ -167,7 +167,7 @@ func (g *GoInstaller) InstallGo() (string, error) {
return "", fmt.Errorf("failed to update profile: %w", err)
}
}
fmt.Printf("✅ Go %s installed successfully!\n", g.Version)
return goExePath, nil
}

View File

@ -8,7 +8,7 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
)
func main() {
@ -18,11 +18,11 @@ func main() {
// Create a new stats manager with Redis connection
// Create a custom configuration
config := &stats.Config{
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
DefaultTimeout: 5 * time.Second,
ExpirationTimes: map[string]time.Duration{
"system": 60 * time.Second, // System info expires after 60 seconds
@ -32,7 +32,7 @@ func main() {
"hardware": 120 * time.Second, // Hardware stats expire after 2 minutes
},
}
manager, err := stats.NewStatsManager(config)
if err != nil {
fmt.Printf("Error creating stats manager: %v\n", err)
@ -61,12 +61,12 @@ func main() {
fmt.Printf(" Cores: %d\n", sysInfo.CPU.Cores)
fmt.Printf(" Model: %s\n", sysInfo.CPU.ModelName)
fmt.Printf(" Usage: %.1f%%\n", sysInfo.CPU.UsagePercent)
fmt.Println("\nMemory Information:")
fmt.Printf(" Total: %.1f GB\n", sysInfo.Memory.Total)
fmt.Printf(" Used: %.1f GB (%.1f%%)\n", sysInfo.Memory.Used, sysInfo.Memory.UsedPercent)
fmt.Printf(" Free: %.1f GB\n", sysInfo.Memory.Free)
fmt.Println("\nNetwork Information:")
fmt.Printf(" Upload Speed: %s\n", sysInfo.Network.UploadSpeed)
fmt.Printf(" Download Speed: %s\n", sysInfo.Network.DownloadSpeed)
@ -81,7 +81,7 @@ func main() {
} else {
fmt.Printf("Found %d disks:\n", len(diskStats.Disks))
for _, disk := range diskStats.Disks {
fmt.Printf(" %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",
fmt.Printf(" %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",
disk.Path, disk.Total, disk.Free, disk.UsedPercent)
}
}
@ -93,12 +93,12 @@ func main() {
if err != nil {
fmt.Printf("Error getting process stats: %v\n", err)
} else {
fmt.Printf("Total processes: %d (showing top %d)\n",
fmt.Printf("Total processes: %d (showing top %d)\n",
processStats.Total, len(processStats.Processes))
fmt.Println("\nTop Processes by CPU Usage:")
for i, proc := range processStats.Processes {
fmt.Printf(" %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",
fmt.Printf(" %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",
i+1, proc.PID, proc.Name, proc.CPUPercent, proc.MemoryMB)
}
}
@ -107,10 +107,10 @@ func main() {
fmt.Println("\n4. CACHING DEMONSTRATION")
fmt.Println("----------------------")
fmt.Println("Getting network speed multiple times (should use cache):")
for i := 0; i < 3; i++ {
netSpeed := manager.GetNetworkSpeedResult()
fmt.Printf(" Request %d: Upload: %s, Download: %s\n",
fmt.Printf(" Request %d: Upload: %s, Download: %s\n",
i+1, netSpeed.UploadSpeed, netSpeed.DownloadSpeed)
time.Sleep(500 * time.Millisecond)
}
@ -127,17 +127,17 @@ func main() {
fmt.Println("--------------------------")
fmt.Println("Enabling debug mode (direct fetching without cache)...")
manager.Debug = true
fmt.Println("Getting system info in debug mode:")
debugSysInfo, err := manager.GetSystemInfo()
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf(" CPU Usage: %.1f%%\n", debugSysInfo.CPU.UsagePercent)
fmt.Printf(" Memory Used: %.1f GB (%.1f%%)\n",
fmt.Printf(" Memory Used: %.1f GB (%.1f%%)\n",
debugSysInfo.Memory.Used, debugSysInfo.Memory.UsedPercent)
}
// Reset debug mode
manager.Debug = false
@ -148,17 +148,17 @@ func main() {
for statsType, duration := range manager.Expiration {
fmt.Printf(" %s: %v\n", statsType, duration)
}
fmt.Println("\nChanging system stats expiration to 10 seconds...")
manager.Expiration["system"] = 10 * time.Second
fmt.Println("Updated expiration times:")
for statsType, duration := range manager.Expiration {
fmt.Printf(" %s: %v\n", statsType, duration)
}
fmt.Println("\nDemo complete. Press Ctrl+C to exit.")
// Keep the program running
select {}
}

View File

@ -11,27 +11,27 @@ import (
"syscall"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/process"
)
// TestResult stores the results of a single test run
type TestResult struct {
StartTime time.Time
EndTime time.Time
SystemInfoTime time.Duration
DiskStatsTime time.Duration
ProcessTime time.Duration
NetworkTime time.Duration
HardwareTime time.Duration
TotalTime time.Duration
UserCPU float64
SystemCPU float64
TotalCPU float64
OverallCPU float64
MemoryUsageMB float32
NumGoroutines int
StartTime time.Time
EndTime time.Time
SystemInfoTime time.Duration
DiskStatsTime time.Duration
ProcessTime time.Duration
NetworkTime time.Duration
HardwareTime time.Duration
TotalTime time.Duration
UserCPU float64
SystemCPU float64
TotalCPU float64
OverallCPU float64
MemoryUsageMB float32
NumGoroutines int
}
func main() {
@ -66,11 +66,11 @@ func main() {
// Create a new stats manager with Redis connection
config := &stats.Config{
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
DefaultTimeout: 5 * time.Second,
ExpirationTimes: map[string]time.Duration{
"system": 60 * time.Second, // System info expires after 60 seconds
@ -80,7 +80,7 @@ func main() {
"hardware": 120 * time.Second, // Hardware stats expire after 2 minutes
},
}
manager, err := stats.NewStatsManager(config)
if err != nil {
fmt.Printf("Error creating stats manager: %v\n", err)
@ -101,11 +101,11 @@ func main() {
// Set up signal handling for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Create a ticker for running tests at the specified interval
ticker := time.NewTicker(time.Duration(*intervalPtr) * time.Second)
defer ticker.Stop()
// Store the sleep duration between operations
sleepDuration := time.Duration(*sleepPtr) * time.Millisecond
@ -118,7 +118,7 @@ func main() {
// Store test results
var results []TestResult
// Print header
fmt.Printf("%-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n",
"Start Time", "End Time", "System(ms)", "Disk(ms)", "Process(ms)", "Network(ms)", "Hardware(ms)", "Total(ms)", "UserCPU(%)", "SysCPU(%)", "TotalCPU(%)", "Memory(MB)", "Goroutines")
@ -131,7 +131,7 @@ func main() {
// Run a test and record the results
result := runTest(manager, currentProcess, sleepDuration)
results = append(results, result)
// Print the result
fmt.Printf("%-20s %-20s %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12d\n",
result.StartTime.Format("15:04:05.000000"),
@ -147,16 +147,16 @@ func main() {
result.TotalCPU,
result.MemoryUsageMB,
result.NumGoroutines)
case <-sigChan:
// Calculate and print summary statistics
fmt.Println("\nTest Summary:")
fmt.Println(strings.Repeat("-", 50))
var totalSystemTime, totalDiskTime, totalProcessTime, totalNetworkTime, totalHardwareTime, totalTime time.Duration
var totalUserCPU, totalSystemCPU, totalCombinedCPU, totalOverallCPU float64
var totalMemory float32
for _, r := range results {
totalSystemTime += r.SystemInfoTime
totalDiskTime += r.DiskStatsTime
@ -170,7 +170,7 @@ func main() {
totalOverallCPU += r.OverallCPU
totalMemory += r.MemoryUsageMB
}
count := float64(len(results))
if count > 0 {
fmt.Printf("Average System Info Time: %.2f ms\n", float64(totalSystemTime.Microseconds())/(count*1000))
@ -185,7 +185,7 @@ func main() {
fmt.Printf("Average Overall CPU: %.2f%%\n", totalOverallCPU/count)
fmt.Printf("Average Memory Usage: %.2f MB\n", float64(totalMemory)/count)
}
fmt.Println("\nTest completed. Exiting...")
return
}
@ -196,90 +196,90 @@ func main() {
func runTest(manager *stats.StatsManager, proc *process.Process, sleepBetweenOps time.Duration) TestResult {
// Get initial CPU times for the process
initialTimes, _ := proc.Times()
// Get initial overall CPU usage
_, _ = cpu.Percent(0, false) // Discard initial reading, we'll only use the final reading
result := TestResult{
StartTime: time.Now(),
}
// Measure total time
totalStart := time.Now()
// Measure system info time
start := time.Now()
_, _ = manager.GetSystemInfo()
result.SystemInfoTime = time.Since(start)
// Sleep between operations if configured
if sleepBetweenOps > 0 {
time.Sleep(sleepBetweenOps)
}
// Measure disk stats time
start = time.Now()
_, _ = manager.GetDiskStats()
result.DiskStatsTime = time.Since(start)
// Sleep between operations if configured
if sleepBetweenOps > 0 {
time.Sleep(sleepBetweenOps)
}
// Measure process stats time
start = time.Now()
_, _ = manager.GetProcessStats(10)
result.ProcessTime = time.Since(start)
// Sleep between operations if configured
if sleepBetweenOps > 0 {
time.Sleep(sleepBetweenOps)
}
// Measure network speed time
start = time.Now()
_ = manager.GetNetworkSpeedResult()
result.NetworkTime = time.Since(start)
// Sleep between operations if configured
if sleepBetweenOps > 0 {
time.Sleep(sleepBetweenOps)
}
// Measure hardware stats time
start = time.Now()
_ = manager.GetHardwareStatsJSON()
result.HardwareTime = time.Since(start)
// Record total time
result.TotalTime = time.Since(totalStart)
result.EndTime = time.Now()
// Get final CPU times for the process
finalTimes, _ := proc.Times()
// Calculate CPU usage for this specific operation
if initialTimes != nil && finalTimes != nil {
result.UserCPU = (finalTimes.User - initialTimes.User) * 100
result.SystemCPU = (finalTimes.System - initialTimes.System) * 100
result.TotalCPU = result.UserCPU + result.SystemCPU
}
// Get overall CPU usage
finalOverallCPU, _ := cpu.Percent(0, false)
if len(finalOverallCPU) > 0 {
result.OverallCPU = finalOverallCPU[0]
}
// Measure memory usage
memInfo, _ := proc.MemoryInfo()
if memInfo != nil {
result.MemoryUsageMB = float32(memInfo.RSS) / (1024 * 1024)
}
// Record number of goroutines
result.NumGoroutines = runtime.NumGoroutine()
return result
}

View File

@ -6,7 +6,7 @@ import (
"os"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
"github.com/redis/go-redis/v9"
)

View File

@ -6,7 +6,7 @@ import (
"os"
"time"
"github.com/freeflowuniverse/heroagent/pkg/system/stats"
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
)
func main() {
@ -15,21 +15,21 @@ func main() {
// Create a new stats manager with Redis connection
config := &stats.Config{
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
RedisAddr: "localhost:6379",
RedisPassword: "",
RedisDB: 0,
Debug: false,
QueueSize: 100,
DefaultTimeout: 5 * time.Second,
ExpirationTimes: map[string]time.Duration{
"system": 30 * time.Second, // System info expires after 30 seconds
"disk": 60 * time.Second, // Disk info expires after 1 minute
"process": 15 * time.Second, // Process info expires after 15 seconds
"network": 20 * time.Second, // Network info expires after 20 seconds
"hardware": 60 * time.Second, // Hardware stats expire after 1 minute
"system": 30 * time.Second, // System info expires after 30 seconds
"disk": 60 * time.Second, // Disk info expires after 1 minute
"process": 15 * time.Second, // Process info expires after 15 seconds
"network": 20 * time.Second, // Network info expires after 20 seconds
"hardware": 60 * time.Second, // Hardware stats expire after 1 minute
},
}
manager, err := stats.NewStatsManager(config)
if err != nil {
fmt.Printf("Error creating stats manager: %v\n", err)
@ -40,7 +40,7 @@ func main() {
// DISK INFORMATION
fmt.Println("\n1. DISK INFORMATION")
fmt.Println("------------------")
// Get all disk stats using the manager
diskStats, err := manager.GetDiskStats()
if err != nil {
@ -48,7 +48,7 @@ func main() {
} else {
fmt.Printf("Found %d disks:\n", len(diskStats.Disks))
for _, disk := range diskStats.Disks {
fmt.Printf(" %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",
fmt.Printf(" %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",
disk.Path, disk.Total, disk.Free, disk.UsedPercent)
}
}
@ -58,7 +58,7 @@ func main() {
if err != nil {
fmt.Printf("Error getting root disk info: %v\n", err)
} else {
fmt.Printf("\nRoot Disk: %.1f GB total, %.1f GB free (%.1f%% used)\n",
fmt.Printf("\nRoot Disk: %.1f GB total, %.1f GB free (%.1f%% used)\n",
rootDisk.Total, rootDisk.Free, rootDisk.UsedPercent)
}
@ -68,7 +68,7 @@ func main() {
// SYSTEM INFORMATION
fmt.Println("\n2. SYSTEM INFORMATION")
fmt.Println("--------------------")
// Get system info using the manager
sysInfo, err := manager.GetSystemInfo()
if err != nil {
@ -78,12 +78,12 @@ func main() {
fmt.Printf(" Cores: %d\n", sysInfo.CPU.Cores)
fmt.Printf(" Model: %s\n", sysInfo.CPU.ModelName)
fmt.Printf(" Usage: %.1f%%\n", sysInfo.CPU.UsagePercent)
fmt.Println("\nMemory Information:")
fmt.Printf(" Total: %.1f GB\n", sysInfo.Memory.Total)
fmt.Printf(" Used: %.1f GB (%.1f%%)\n", sysInfo.Memory.Used, sysInfo.Memory.UsedPercent)
fmt.Printf(" Free: %.1f GB\n", sysInfo.Memory.Free)
fmt.Println("\nNetwork Information:")
fmt.Printf(" Upload Speed: %s\n", sysInfo.Network.UploadSpeed)
fmt.Printf(" Download Speed: %s\n", sysInfo.Network.DownloadSpeed)
@ -98,18 +98,18 @@ func main() {
// PROCESS INFORMATION
fmt.Println("\n3. PROCESS INFORMATION")
fmt.Println("---------------------")
// Get process stats using the manager
processStats, err := manager.GetProcessStats(5) // Get top 5 processes
if err != nil {
fmt.Printf("Error getting process stats: %v\n", err)
} else {
fmt.Printf("Total processes: %d (showing top %d)\n",
fmt.Printf("Total processes: %d (showing top %d)\n",
processStats.Total, len(processStats.Processes))
fmt.Println("\nTop Processes by CPU Usage:")
for i, proc := range processStats.Processes {
fmt.Printf(" %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",
fmt.Printf(" %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",
i+1, proc.PID, proc.Name, proc.CPUPercent, proc.MemoryMB)
}
}
@ -128,7 +128,7 @@ func main() {
// COMBINED STATS
fmt.Println("\n4. COMBINED STATS FUNCTIONS")
fmt.Println("--------------------------")
// Hardware stats using the manager
fmt.Println("\nHardware Stats:")
hardwareStats := manager.GetHardwareStats()
@ -151,17 +151,17 @@ func main() {
// Wait and measure network speed again
fmt.Println("\nWaiting 2 seconds for another network speed measurement...")
time.Sleep(2 * time.Second)
// Get updated network speed using the manager
updatedNetSpeed := manager.GetNetworkSpeedResult()
fmt.Println("\nUpdated Network Speed:")
fmt.Printf(" Upload: %s\n", updatedNetSpeed.UploadSpeed)
fmt.Printf(" Download: %s\n", updatedNetSpeed.DownloadSpeed)
// CACHE MANAGEMENT
fmt.Println("\n5. CACHE MANAGEMENT")
fmt.Println("------------------")
// Force update of system stats
fmt.Println("\nForcing update of system stats...")
err = manager.ForceUpdate("system")
@ -170,7 +170,7 @@ func main() {
} else {
fmt.Println("System stats updated successfully")
}
// Get updated system info
updatedSysInfo, err := manager.GetSystemInfo()
if err != nil {
@ -178,7 +178,7 @@ func main() {
} else {
fmt.Println("\nUpdated CPU Usage: " + fmt.Sprintf("%.1f%%", updatedSysInfo.CPU.UsagePercent))
}
// Clear cache for disk stats
fmt.Println("\nClearing cache for disk stats...")
err = manager.ClearCache("disk")
@ -187,7 +187,7 @@ func main() {
} else {
fmt.Println("Disk stats cache cleared successfully")
}
// Toggle debug mode
fmt.Println("\nToggling debug mode (direct fetching without cache)...")
manager.Debug = !manager.Debug