fix(agentic): align workspace flow with AX design
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
6bb4fb8d57
commit
6ac195c2e6
37 changed files with 1156 additions and 335 deletions
|
|
@ -1,3 +1,5 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
@ -7,7 +9,6 @@ import (
|
|||
"dappco.re/go/agent/pkg/brain"
|
||||
"dappco.re/go/agent/pkg/monitor"
|
||||
"dappco.re/go/agent/pkg/runner"
|
||||
"dappco.re/go/mcp/pkg/mcp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -22,7 +23,7 @@ func main() {
|
|||
core.WithService(runner.Register),
|
||||
core.WithService(monitor.Register),
|
||||
core.WithService(brain.Register),
|
||||
core.WithService(mcp.Register),
|
||||
core.WithName("mcp", registerMCPService),
|
||||
)
|
||||
c.App().Version = appVersion()
|
||||
|
||||
|
|
|
|||
33
cmd/core-agent/mcp_service.go
Normal file
33
cmd/core-agent/mcp_service.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
"dappco.re/go/agent/pkg/brain"
|
||||
"dappco.re/go/agent/pkg/monitor"
|
||||
core "dappco.re/go/core"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp"
|
||||
)
|
||||
|
||||
func registerMCPService(c *core.Core) core.Result {
|
||||
var subsystems []mcp.Subsystem
|
||||
|
||||
if prep, ok := core.ServiceFor[*agentic.PrepSubsystem](c, "agentic"); ok {
|
||||
subsystems = append(subsystems, prep)
|
||||
}
|
||||
if mon, ok := core.ServiceFor[*monitor.Subsystem](c, "monitor"); ok {
|
||||
subsystems = append(subsystems, mon)
|
||||
}
|
||||
if brn, ok := core.ServiceFor[*brain.DirectSubsystem](c, "brain"); ok {
|
||||
subsystems = append(subsystems, brn)
|
||||
}
|
||||
|
||||
svc, err := mcp.New(mcp.Options{
|
||||
Subsystems: subsystems,
|
||||
})
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("main.registerMCPService", "create mcp service", err), OK: false}
|
||||
}
|
||||
return core.Result{Value: svc, OK: true}
|
||||
}
|
||||
14
go.mod
14
go.mod
|
|
@ -7,6 +7,7 @@ require (
|
|||
dappco.re/go/core/api v0.2.0
|
||||
dappco.re/go/core/process v0.3.0
|
||||
dappco.re/go/core/ws v0.3.0
|
||||
forge.lthn.ai/core/mcp v0.4.8
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.1
|
||||
|
|
@ -14,11 +15,22 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require dappco.re/go/core/forge v0.2.0 // indirect
|
||||
require (
|
||||
dappco.re/go/core/forge v0.2.0
|
||||
forge.lthn.ai/core/go-ws v0.2.5
|
||||
)
|
||||
|
||||
require (
|
||||
dappco.re/go/core/io v0.2.0 // indirect
|
||||
dappco.re/go/core/log v0.1.0 // indirect
|
||||
forge.lthn.ai/core/api v0.1.5 // indirect
|
||||
forge.lthn.ai/core/go v0.3.3 // indirect
|
||||
forge.lthn.ai/core/go-ai v0.1.12 // indirect
|
||||
forge.lthn.ai/core/go-io v0.1.7 // indirect
|
||||
forge.lthn.ai/core/go-log v0.0.4 // indirect
|
||||
forge.lthn.ai/core/go-process v0.2.9 // indirect
|
||||
forge.lthn.ai/core/go-rag v0.1.11 // indirect
|
||||
forge.lthn.ai/core/go-webview v0.1.6 // indirect
|
||||
github.com/99designs/gqlgen v0.17.88 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
|
|
|
|||
506
go.sum
Normal file
506
go.sum
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk=
|
||||
dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
|
||||
dappco.re/go/core/api v0.2.0 h1:5OcN9nawpp18Jp6dB1OwI2CBfs0Tacb0y0zqxFB6TJ0=
|
||||
dappco.re/go/core/api v0.2.0/go.mod h1:AtgNAx8lDY+qhVObFdNQOjSUQrHX1BeiDdMuA6RIfzo=
|
||||
dappco.re/go/core/forge v0.2.0 h1:EBCHaUdzEAbYpDwRTXMmJoSfSrK30IJTOVBPRxxkJTg=
|
||||
dappco.re/go/core/forge v0.2.0/go.mod h1:XMz9ZNVl9xane9Rg3AEBuVV5UNNBGWbPY9rSKbqYgnM=
|
||||
dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4=
|
||||
dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E=
|
||||
dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc=
|
||||
dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs=
|
||||
dappco.re/go/core/process v0.3.0 h1:BPF9R79+8ZWe34qCIy/sZy+P4HwbaO95js2oPJL7IqM=
|
||||
dappco.re/go/core/process v0.3.0/go.mod h1:qwx8kt6x+J9gn7fu8lavuess72Ye9jPBODqDZQ9K0as=
|
||||
dappco.re/go/core/ws v0.3.0 h1:ZxR8y5pfrWvnCHVN7qExXz7fdP5a063uNqyqE0Ab8pQ=
|
||||
dappco.re/go/core/ws v0.3.0/go.mod h1:aLyXrJnbCOGL0SW9rC1EHAAIS83w3djO374gHIz4Nic=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
forge.lthn.ai/Snider/Borg v0.3.1/go.mod h1:Z7DJD0yHXsxSyM7Mjl6/g4gH1NBsIz44Bf5AFlV76Wg=
|
||||
forge.lthn.ai/core/api v0.1.5 h1:NwZrcOyBjaiz5/cn0n0tnlMUodi8Or6FHMx59C7Kv2o=
|
||||
forge.lthn.ai/core/api v0.1.5/go.mod h1:PBnaWyOVXSOGy+0x2XAPUFMYJxQ2CNhppia/D06ZPII=
|
||||
forge.lthn.ai/core/cli v0.3.7/go.mod h1:DBUppJkA9P45ZFGgI2B8VXw1rAZxamHoI/KG7fRvTNs=
|
||||
forge.lthn.ai/core/go v0.3.2/go.mod h1:f7/zb3Labn4ARfwTq5Bi2AFHY+uxyPHozO+hLb54eFo=
|
||||
forge.lthn.ai/core/go v0.3.3 h1:kYYZ2nRYy0/Be3cyuLJspRjLqTMxpckVyhb/7Sw2gd0=
|
||||
forge.lthn.ai/core/go v0.3.3/go.mod h1:Cp4ac25pghvO2iqOu59t1GyngTKVOzKB5/VPdhRi9CQ=
|
||||
forge.lthn.ai/core/go-ai v0.1.12 h1:OHt0bUABlyhvgxZxyMwueRoh8rS3YKWGFY6++zCAwC8=
|
||||
forge.lthn.ai/core/go-ai v0.1.12/go.mod h1:5Pc9lszxgkO7Aj2Z3dtq4L9Xk9l/VNN+Baj1t///OCM=
|
||||
forge.lthn.ai/core/go-crypt v0.1.6/go.mod h1:4VZAGqxlbadhSB66sJkdj54/HSJ+bSxVgwWK5kMMYDo=
|
||||
forge.lthn.ai/core/go-i18n v0.1.7/go.mod h1:0VDjwtY99NSj2iqwrI09h5GUsJeM9s48MLkr+/Dn4G8=
|
||||
forge.lthn.ai/core/go-inference v0.1.7/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.5/go.mod h1:FRtXSsi8W+U9vewCU+LBAqqbIj3wjXA4dBdSv3SAtWI=
|
||||
forge.lthn.ai/core/go-io v0.1.7 h1:Tdb6sqh+zz1lsGJaNX9RFWM6MJ/RhSAyxfulLXrJsbk=
|
||||
forge.lthn.ai/core/go-io v0.1.7/go.mod h1:8lRLFk4Dnp5cR/Cyzh9WclD5566TbpdRgwcH7UZLWn4=
|
||||
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
|
||||
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
forge.lthn.ai/core/go-ml v0.1.0/go.mod h1:FPV9JhIUOZdLeJpX1ggC15BpmM740NPg6rycnOc5vss=
|
||||
forge.lthn.ai/core/go-mlx v0.1.0/go.mod h1:b4BJX67nx9QZiyREl2lmYIPJ+Yp5amZug3y7vXaRy/Y=
|
||||
forge.lthn.ai/core/go-process v0.2.9 h1:Wql+5TUF+lfU2oJ9I+S764MkTqJhBsuyMM0v1zsfZC4=
|
||||
forge.lthn.ai/core/go-process v0.2.9/go.mod h1:NIzZOF5IVYYCjHkcNIGcg1mZH+bzGoie4SlZUDYOKIM=
|
||||
forge.lthn.ai/core/go-rag v0.1.11 h1:KXTOtnOdrx8YKmvnj0EOi2EI/+cKjE8w2PpJCQIrSd8=
|
||||
forge.lthn.ai/core/go-rag v0.1.11/go.mod h1:vIlOKVD1SdqqjkJ2XQyXPuKPtiajz/STPLCaDpqOzk8=
|
||||
forge.lthn.ai/core/go-scm v0.2.0/go.mod h1:Q/PV2FbqDlWnAOsXAd1pgSiHOlRCPW4HcPmOt8Z9H+E=
|
||||
forge.lthn.ai/core/go-webview v0.1.6 h1:szXQxRJf2bOZJKh3v1P01B1Vf9mgXaBCXzh0EZu9aoc=
|
||||
forge.lthn.ai/core/go-webview v0.1.6/go.mod h1:5n1tECD1wBV/uFZRY9ZjfPFO5TYZrlaR3mQFwvO2nek=
|
||||
forge.lthn.ai/core/go-ws v0.2.5 h1:ZIV7Yrv01R/xpJUogA5vrfP9yB9li1w7EV3eZFMt8h0=
|
||||
forge.lthn.ai/core/go-ws v0.2.5/go.mod h1:C3riJyLLcV6QhLvYlq3P/XkGTsN598qQeGBoLdoHBU4=
|
||||
forge.lthn.ai/core/mcp v0.4.8 h1:nd1x3AL8AkUfl0kziltoJUX96Nx1BeFWEbgHmfrkKz8=
|
||||
forge.lthn.ai/core/mcp v0.4.8/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
|
||||
github.com/99designs/gqlgen v0.17.88 h1:neMQDgehMwT1vYIOx/w5ZYPUU/iMNAJzRO44I5Intoc=
|
||||
github.com/99designs/gqlgen v0.17.88/go.mod h1:qeqYFEgOeSKqWedOjogPizimp2iu4E23bdPvl4jTYic=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
||||
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
|
||||
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/antonlindstrom/pgstore v0.0.0-20220421113606-e3a6e3fed12a/go.mod h1:Sdr/tmSOLEnncCuXS5TwZRxuk7deH1WXVY8cve3eVBM=
|
||||
github.com/apache/arrow-go/v18 v18.5.2/go.mod h1:yNoizNTT4peTciJ7V01d2EgOkE1d0fQ1vZcFOsVtFsw=
|
||||
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21/go.mod h1:UUxgWxofmOdAMuqEsSppbDtGKLfR04HGsD0HXzvhI1k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12/go.mod h1:v2pNpJbRNl4vEUWEh5ytQok0zACAKfdmKS51Hotc3pQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20/go.mod h1:4TLZCmVJDM3FOu5P5TJP0zOlu9zWgDWU7aUxWbr+rcw=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1/go.mod h1:qXVal5H0ChqXP63t6jze5LmFalc7+ZE7wOdLtZ0LCP0=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
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/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/boj/redistore v1.4.1/go.mod h1:c0Tvw6aMjslog4jHIAcNv6EtJM849YoOAhMY7JBbWpI=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
||||
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20240916143655-c0e34fd2f304/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
|
||||
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/casbin/casbin/v2 v2.135.0 h1:6BLkMQiGotYyS5yYeWgW19vxqugUlvHFkFiLnLR/bxk=
|
||||
github.com/casbin/casbin/v2 v2.135.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18=
|
||||
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||
github.com/casbin/govaluate v1.10.0 h1:ffGw51/hYH3w3rZcxO/KcaUIDOLP84w7nsidMVgaDG0=
|
||||
github.com/casbin/govaluate v1.10.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
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/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chewxy/hm v1.0.0/go.mod h1:qg9YI4q6Fkj/whwHR1D+bOGeF7SniIP40VweVepLjg0=
|
||||
github.com/chewxy/math32 v1.11.0/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/d4l3k/go-bfloat16 v0.0.0-20211005043715-690c3bdd05f1/go.mod h1:uw2gLcxEuYUlAd/EXyjc/v55nd3+47YAgWbSXVxPrNI=
|
||||
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/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/authz v1.0.6 h1:qAO4sSSzOPCwYRZI6YtubC+h2tZVwhwSJeyEZn2W+5k=
|
||||
github.com/gin-contrib/authz v1.0.6/go.mod h1:A2B5Im1M/HIoHPjLc31j3RlENSE6j8euJY9NFdzZeYo=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/expvar v1.0.3 h1:nIbUaokxZfUEC/35h+RyWCP1SMF/suV/ARbXL3H3jrw=
|
||||
github.com/gin-contrib/expvar v1.0.3/go.mod h1:bwqqmhty1Zl2JYVLzBIL6CSHDWDbQoQoicalAnBvUnY=
|
||||
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
|
||||
github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw=
|
||||
github.com/gin-contrib/httpsign v1.0.3 h1:NpeDQjmUV0qFjGCm/rkXSp3HH0hU7r84q1v+VtTiI5I=
|
||||
github.com/gin-contrib/httpsign v1.0.3/go.mod h1:n4GC7StmHNBhIzWzuW2njKbZMeEWh4tDbmn3bD1ab+k=
|
||||
github.com/gin-contrib/location/v2 v2.0.0 h1:iLx5RatHQHSxgC0tm2AG0sIuQKecI7FhREessVd6RWY=
|
||||
github.com/gin-contrib/location/v2 v2.0.0/go.mod h1:276TDNr25NENBA/NQZUuEIlwxy/I5CYVFIr/d2TgOdU=
|
||||
github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM=
|
||||
github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY=
|
||||
github.com/gin-contrib/secure v1.1.2 h1:6G8/NCOTSywWY7TeaH/0Yfaa6bfkE5ukkqtIm7lK11U=
|
||||
github.com/gin-contrib/secure v1.1.2/go.mod h1:xI3jI5/BpOYMCBtjgmIVrMA3kI7y9LwCFxs+eLf5S3w=
|
||||
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
|
||||
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
|
||||
github.com/gin-contrib/slog v1.2.0 h1:vAxZfr7knD1ZYK5+pMJLP52sZXIkJXkcRPa/0dx9hSk=
|
||||
github.com/gin-contrib/slog v1.2.0/go.mod h1:vYK6YltmpsEFkO0zfRMLTKHrWS3DwUSn0TMpT+kMagI=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-contrib/static v1.1.5 h1:bAPqT4KTZN+4uDY1b90eSrD1t8iNzod7Jj8njwmnzz4=
|
||||
github.com/gin-contrib/static v1.1.5/go.mod h1:8JSEXwZHcQ0uCrLPcsvnAJ4g+ODxeupP8Zetl9fd8wM=
|
||||
github.com/gin-contrib/timeout v1.1.0 h1:WAmWseo5gfBUbMrMJu5hJxDclehfSJUmK2wGwCC/EFw=
|
||||
github.com/gin-contrib/timeout v1.1.0/go.mod h1:NpRo4gd1Ad8ZQ4T6bQLVFDqiplCmPRs2nvfckxS2Fw4=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
|
||||
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
|
||||
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
|
||||
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
|
||||
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
|
||||
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
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/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/laziness-coders/mongostore v0.0.14/go.mod h1:Rh+yJax2Vxc2QY62clIM/kRnLk+TxivgSLHOXENXPtk=
|
||||
github.com/ledongthuc/pdf v0.0.0-20250511090121-5959a4027728/go.mod h1:1fEHWurg7pvf5SG6XNE5Q8UZmOwex51Mkx3SLhrW5B4=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M=
|
||||
github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8=
|
||||
github.com/matryer/moq v0.6.0/go.mod h1:iEVhY/XBwFG/nbRyEf0oV+SqnTHZJ5wectzx7yT+y98=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
|
||||
github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.1 h1:M4x9GyIPj+HoIlHNGpK2hq5o3BFhC+78PkEaldQRphc=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.1/go.mod h1:Bo/mS87hPQqHSRkMv4dQq1XCu6zv4INdXnFZabkNU6s=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nlpodyssey/gopickle v0.3.0/go.mod h1:f070HJ/yR+eLi5WmM1OXJEGaTpuJEUiib19olXgYha0=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/ollama/ollama v0.18.2 h1:RsOY8oZ6TufRiPgsSlKJp4/V/X+oBREscUlEHZfd554=
|
||||
github.com/ollama/ollama v0.18.2/go.mod h1:tCX4IMV8DHjl3zY0THxuEkpWDZSOchJpzTuLACpMwFw=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/parquet-go/bitpack v1.0.0/go.mod h1:XnVk9TH+O40eOOmvpAVZ7K2ocQFrQwysLMnc6M/8lgs=
|
||||
github.com/parquet-go/jsonlite v1.4.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0=
|
||||
github.com/parquet-go/parquet-go v0.29.0/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg=
|
||||
github.com/pdevine/tensor v0.0.0-20240510204454-f88f4562727c/go.mod h1:PSojXDXF7TbgQiD6kkd98IHOS0QqTyUEaWRiS8+BLu8=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
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-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/qdrant/go-client v1.17.1 h1:7QmPwDddrHL3hC4NfycwtQlraVKRLcRi++BX6TTm+3g=
|
||||
github.com/qdrant/go-client v1.17.1/go.mod h1:n1h6GhkdAzcohoXt/5Z19I2yxbCkMA6Jejob3S6NZT8=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
|
||||
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
|
||||
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE=
|
||||
github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
|
||||
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||
github.com/tkrajina/typescriptify-golang-structs v0.2.0/go.mod h1:sjU00nti/PMEOZb07KljFlR+lJ+RotsC0GBQMv9EKls=
|
||||
github.com/tree-sitter/go-tree-sitter v0.25.0/go.mod h1:r77ig7BikoZhHrrsjAnv8RqGti5rtSyvDHPzgTPsUuU=
|
||||
github.com/tree-sitter/tree-sitter-cpp v0.23.4/go.mod h1:doqNW64BriC7WBCQ1klf0KmJpdEvfxyXtoEybnBo6v8=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/urfave/cli/v3 v3.7.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
|
||||
github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xtgo/set v1.0.0/go.mod h1:d3NHzGzSa0NmB2NhFyECA+QdRp29oEn2xbT+TpeFoM8=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0 h1:E7DmskpIO7ZR6QI6zKSEKIDNUYoKw9oHXP23gzbCdU0=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0/go.mod h1:WB2cS9y+AwqqKhoo9gw6/ZxlSjFBUQGZ8BQOaD3FVXM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.42.0/go.mod h1:iPgUcSEF5DORW6+yNbdw/YevUy+QqJ508ncjhrRSCjc=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
|
||||
golang.org/x/telemetry v0.0.0-20260312161427-1546bf4b83fe/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
|
||||
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/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
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.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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorgonia.org/vecf32 v0.9.0/go.mod h1:NCc+5D2oxddRL11hd+pCB1PEyXWOyiQxfZ/1wwhOXCA=
|
||||
gorgonia.org/vecf64 v0.9.0/go.mod h1:hp7IOWCnRiVQKON73kkC/AUMtEXyf9kGlVrtPQ9ccVA=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/sqlite v1.47.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
|
@ -111,7 +111,13 @@ func (s *PrepSubsystem) handleScan(ctx context.Context, opts core.Options) core.
|
|||
// core.Option{Key: "workspace", Value: "core/go-io/task-5"},
|
||||
// ))
|
||||
func (s *PrepSubsystem) handleWatch(ctx context.Context, opts core.Options) core.Result {
|
||||
input := WatchInput{}
|
||||
input := WatchInput{
|
||||
PollInterval: opts.Int("poll_interval"),
|
||||
Timeout: opts.Int("timeout"),
|
||||
}
|
||||
if workspace := opts.String("workspace"); workspace != "" {
|
||||
input.Workspaces = []string{workspace}
|
||||
}
|
||||
_, out, err := s.watch(ctx, nil, input)
|
||||
if err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
|
|
@ -151,7 +157,7 @@ func (s *PrepSubsystem) handleQA(ctx context.Context, opts core.Options) core.Re
|
|||
repo = st.Repo
|
||||
}
|
||||
s.Core().ACTION(messages.QAResult{
|
||||
Workspace: core.PathBase(wsDir),
|
||||
Workspace: WorkspaceName(wsDir),
|
||||
Repo: repo,
|
||||
Passed: passed,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ import (
|
|||
"context"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/process"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
)
|
||||
|
||||
func ExampleRegister() {
|
||||
c := core.New(
|
||||
core.WithService(process.Register),
|
||||
core.WithService(agentic.ProcessRegister),
|
||||
core.WithService(agentic.Register),
|
||||
)
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
|
|
@ -30,7 +29,7 @@ func ExampleRegister() {
|
|||
|
||||
func ExampleRegister_actions() {
|
||||
c := core.New(
|
||||
core.WithService(process.Register),
|
||||
core.WithService(agentic.ProcessRegister),
|
||||
core.WithService(agentic.Register),
|
||||
)
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
|
|
@ -42,7 +41,7 @@ func ExampleRegister_actions() {
|
|||
|
||||
func ExampleRegister_task() {
|
||||
c := core.New(
|
||||
core.WithService(process.Register),
|
||||
core.WithService(agentic.ProcessRegister),
|
||||
core.WithService(agentic.Register),
|
||||
)
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,66 @@ import (
|
|||
forge_types "dappco.re/go/core/forge/types"
|
||||
)
|
||||
|
||||
type issueView struct {
|
||||
Index int64 `json:"index"`
|
||||
Number int64 `json:"number"`
|
||||
Title string `json:"title"`
|
||||
State string `json:"state"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type prBranchView struct {
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
type prUserView struct {
|
||||
Login string `json:"login"`
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
type prLabelView struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type pullRequestView struct {
|
||||
Index int64 `json:"index"`
|
||||
Number int64 `json:"number"`
|
||||
Title string `json:"title"`
|
||||
State string `json:"state"`
|
||||
Mergeable bool `json:"mergeable"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Body string `json:"body"`
|
||||
Head prBranchView `json:"head"`
|
||||
Base prBranchView `json:"base"`
|
||||
User *prUserView `json:"user"`
|
||||
Labels []prLabelView `json:"labels"`
|
||||
}
|
||||
|
||||
func issueNumber(issue issueView) int64 {
|
||||
if issue.Index != 0 {
|
||||
return issue.Index
|
||||
}
|
||||
return issue.Number
|
||||
}
|
||||
|
||||
func pullRequestNumber(pr pullRequestView) int64 {
|
||||
if pr.Index != 0 {
|
||||
return pr.Index
|
||||
}
|
||||
return pr.Number
|
||||
}
|
||||
|
||||
func pullRequestAuthor(pr pullRequestView) string {
|
||||
if pr.User == nil {
|
||||
return ""
|
||||
}
|
||||
if pr.User.UserName != "" {
|
||||
return pr.User.UserName
|
||||
}
|
||||
return pr.User.Login
|
||||
}
|
||||
|
||||
// parseForgeArgs extracts org and repo from opts.
|
||||
func parseForgeArgs(opts core.Options) (org, repo string, num int64) {
|
||||
org = opts.String("org")
|
||||
|
|
@ -47,12 +107,13 @@ func (s *PrepSubsystem) cmdIssueGet(opts core.Options) core.Result {
|
|||
core.Print(nil, "usage: core-agent issue get <repo> --number=N [--org=core]")
|
||||
return core.Result{OK: false}
|
||||
}
|
||||
issue, err := s.forge.Issues.Get(ctx, forge.Params{"owner": org, "repo": repo, "index": fmtIndex(num)})
|
||||
var issue issueView
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/issues/%d", org, repo, num), &issue)
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
core.Print(nil, "#%d %s", issue.Index, issue.Title)
|
||||
core.Print(nil, "#%d %s", issueNumber(issue), issue.Title)
|
||||
core.Print(nil, " state: %s", issue.State)
|
||||
core.Print(nil, " url: %s", issue.HTMLURL)
|
||||
if issue.Body != "" {
|
||||
|
|
@ -69,13 +130,14 @@ func (s *PrepSubsystem) cmdIssueList(opts core.Options) core.Result {
|
|||
core.Print(nil, "usage: core-agent issue list <repo> [--org=core]")
|
||||
return core.Result{OK: false}
|
||||
}
|
||||
issues, err := s.forge.Issues.ListAll(ctx, forge.Params{"owner": org, "repo": repo})
|
||||
var issues []issueView
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/issues?limit=50&page=1", org, repo), &issues)
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
for _, issue := range issues {
|
||||
core.Print(nil, " #%-4d %-6s %s", issue.Index, issue.State, issue.Title)
|
||||
core.Print(nil, " #%-4d %-6s %s", issueNumber(issue), issue.State, issue.Title)
|
||||
}
|
||||
if len(issues) == 0 {
|
||||
core.Print(nil, " no issues")
|
||||
|
|
@ -117,7 +179,8 @@ func (s *PrepSubsystem) cmdIssueCreate(opts core.Options) core.Result {
|
|||
createOpts := &forge_types.CreateIssueOption{Title: title, Body: body, Ref: ref}
|
||||
|
||||
if milestone != "" {
|
||||
milestones, err := s.forge.Milestones.ListAll(ctx, forge.Params{"owner": org, "repo": repo})
|
||||
var milestones []forge_types.Milestone
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/milestones", org, repo), &milestones)
|
||||
if err == nil {
|
||||
for _, m := range milestones {
|
||||
if m.Title == milestone {
|
||||
|
|
@ -163,12 +226,13 @@ func (s *PrepSubsystem) cmdPRGet(opts core.Options) core.Result {
|
|||
core.Print(nil, "usage: core-agent pr get <repo> --number=N [--org=core]")
|
||||
return core.Result{OK: false}
|
||||
}
|
||||
pr, err := s.forge.Pulls.Get(ctx, forge.Params{"owner": org, "repo": repo, "index": fmtIndex(num)})
|
||||
var pr pullRequestView
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls/%d", org, repo, num), &pr)
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
core.Print(nil, "#%d %s", pr.Index, pr.Title)
|
||||
core.Print(nil, "#%d %s", pullRequestNumber(pr), pr.Title)
|
||||
core.Print(nil, " state: %s", pr.State)
|
||||
core.Print(nil, " head: %s", pr.Head.Ref)
|
||||
core.Print(nil, " base: %s", pr.Base.Ref)
|
||||
|
|
@ -188,13 +252,14 @@ func (s *PrepSubsystem) cmdPRList(opts core.Options) core.Result {
|
|||
core.Print(nil, "usage: core-agent pr list <repo> [--org=core]")
|
||||
return core.Result{OK: false}
|
||||
}
|
||||
prs, err := s.forge.Pulls.ListAll(ctx, forge.Params{"owner": org, "repo": repo})
|
||||
var prs []pullRequestView
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls?limit=50&page=1", org, repo), &prs)
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
for _, pr := range prs {
|
||||
core.Print(nil, " #%-4d %-6s %s → %s %s", pr.Index, pr.State, pr.Head.Ref, pr.Base.Ref, pr.Title)
|
||||
core.Print(nil, " #%-4d %-6s %s → %s %s", pullRequestNumber(pr), pr.State, pr.Head.Ref, pr.Base.Ref, pr.Title)
|
||||
}
|
||||
if len(prs) == 0 {
|
||||
core.Print(nil, " no PRs")
|
||||
|
|
|
|||
|
|
@ -19,13 +19,12 @@ func (s *PrepSubsystem) registerWorkspaceCommands() {
|
|||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdWorkspaceList(opts core.Options) core.Result {
|
||||
wsRoot := WorkspaceRoot()
|
||||
fsys := s.Core().Fs()
|
||||
|
||||
statusFiles := core.PathGlob(core.JoinPath(wsRoot, "*", "status.json"))
|
||||
statusFiles := WorkspaceStatusPaths()
|
||||
count := 0
|
||||
for _, sf := range statusFiles {
|
||||
wsName := core.PathBase(core.PathDir(sf))
|
||||
wsName := WorkspaceName(core.PathDir(sf))
|
||||
if sr := fsys.Read(sf); sr.OK {
|
||||
content := sr.Value.(string)
|
||||
status := extractField(content, "status")
|
||||
|
|
@ -49,11 +48,11 @@ func (s *PrepSubsystem) cmdWorkspaceClean(opts core.Options) core.Result {
|
|||
filter = "all"
|
||||
}
|
||||
|
||||
statusFiles := core.PathGlob(core.JoinPath(wsRoot, "*", "status.json"))
|
||||
statusFiles := WorkspaceStatusPaths()
|
||||
var toRemove []string
|
||||
|
||||
for _, sf := range statusFiles {
|
||||
wsName := core.PathBase(core.PathDir(sf))
|
||||
wsName := WorkspaceName(core.PathDir(sf))
|
||||
sr := fsys.Read(sf)
|
||||
if !sr.OK {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ func (s *PrepSubsystem) stopIssueTracking(wsDir string) {
|
|||
|
||||
// broadcastStart emits IPC + audit events for agent start.
|
||||
func (s *PrepSubsystem) broadcastStart(agent, wsDir string) {
|
||||
wsName := WorkspaceName(wsDir)
|
||||
st, _ := ReadStatus(wsDir)
|
||||
repo := ""
|
||||
if st != nil {
|
||||
|
|
@ -295,15 +296,16 @@ func (s *PrepSubsystem) broadcastStart(agent, wsDir string) {
|
|||
}
|
||||
if s.ServiceRuntime != nil {
|
||||
s.Core().ACTION(messages.AgentStarted{
|
||||
Agent: agent, Repo: repo, Workspace: core.PathBase(wsDir),
|
||||
Agent: agent, Repo: repo, Workspace: wsName,
|
||||
})
|
||||
}
|
||||
emitStartEvent(agent, core.PathBase(wsDir))
|
||||
emitStartEvent(agent, wsName)
|
||||
}
|
||||
|
||||
// broadcastComplete emits IPC + audit events for agent completion.
|
||||
func (s *PrepSubsystem) broadcastComplete(agent, wsDir, finalStatus string) {
|
||||
emitCompletionEvent(agent, core.PathBase(wsDir), finalStatus)
|
||||
wsName := WorkspaceName(wsDir)
|
||||
emitCompletionEvent(agent, wsName, finalStatus)
|
||||
if s.ServiceRuntime != nil {
|
||||
st, _ := ReadStatus(wsDir)
|
||||
repo := ""
|
||||
|
|
@ -312,7 +314,7 @@ func (s *PrepSubsystem) broadcastComplete(agent, wsDir, finalStatus string) {
|
|||
}
|
||||
s.Core().ACTION(messages.AgentCompleted{
|
||||
Agent: agent, Repo: repo,
|
||||
Workspace: core.PathBase(wsDir), Status: finalStatus,
|
||||
Workspace: wsName, Status: finalStatus,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -383,16 +385,15 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir string) (int, string, er
|
|||
if !ok {
|
||||
return 0, "", core.E("dispatch.spawnAgent", "process service not registered", nil)
|
||||
}
|
||||
sr := procSvc.StartWithOptions(context.Background(), process.RunOptions{
|
||||
proc, err := procSvc.StartWithOptions(context.Background(), process.RunOptions{
|
||||
Command: command,
|
||||
Args: args,
|
||||
Dir: repoDir,
|
||||
Detach: true,
|
||||
})
|
||||
if !sr.OK {
|
||||
return 0, "", core.E("dispatch.spawnAgent", core.Concat("failed to spawn ", agent), nil)
|
||||
if err != nil {
|
||||
return 0, "", core.E("dispatch.spawnAgent", core.Concat("failed to spawn ", agent), err)
|
||||
}
|
||||
proc := sr.Value.(*process.Process)
|
||||
|
||||
proc.CloseStdin()
|
||||
pid := proc.Info().PID
|
||||
|
|
@ -402,7 +403,7 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir string) (int, string, er
|
|||
|
||||
// Register a one-shot Action that monitors this agent, then run it via PerformAsync.
|
||||
// PerformAsync tracks it in Core's WaitGroup — ServiceShutdown waits for it.
|
||||
monitorAction := core.Concat("agentic.monitor.", core.PathBase(wsDir))
|
||||
monitorAction := core.Concat("agentic.monitor.", core.Replace(WorkspaceName(wsDir), "/", "."))
|
||||
s.Core().Action(monitorAction, func(_ context.Context, _ core.Options) core.Result {
|
||||
<-proc.Done()
|
||||
s.onAgentComplete(agent, wsDir, outputFile,
|
||||
|
|
@ -468,21 +469,6 @@ func (s *PrepSubsystem) dispatch(ctx context.Context, req *mcp.CallToolRequest,
|
|||
input.Template = "coding"
|
||||
}
|
||||
|
||||
// Concurrency check — ask the runner
|
||||
r := s.Core().Action("runner.dispatch").Run(ctx, core.NewOptions(
|
||||
core.Option{Key: "agent", Value: input.Agent},
|
||||
core.Option{Key: "repo", Value: input.Repo},
|
||||
))
|
||||
if !r.OK {
|
||||
reason, _ := r.Value.(string)
|
||||
out := DispatchOutput{
|
||||
Repo: input.Repo,
|
||||
Success: true,
|
||||
OutputFile: core.Concat("queued — ", reason),
|
||||
}
|
||||
return nil, out, nil
|
||||
}
|
||||
|
||||
// Step 1: Prep workspace — clone + build prompt
|
||||
prepInput := PrepInput{
|
||||
Repo: input.Repo,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ import (
|
|||
core "dappco.re/go/core"
|
||||
)
|
||||
|
||||
func agentHomeDir() string {
|
||||
if home := core.Env("HOME"); home != "" {
|
||||
return home
|
||||
}
|
||||
return core.Env("DIR_HOME")
|
||||
}
|
||||
|
||||
// ingestFindings reads the agent output log and creates issues via the API
|
||||
// for scan/audit results. Only runs for conventions and security templates.
|
||||
func (s *PrepSubsystem) ingestFindings(wsDir string) {
|
||||
|
|
@ -17,7 +24,7 @@ func (s *PrepSubsystem) ingestFindings(wsDir string) {
|
|||
}
|
||||
|
||||
// Read the log file
|
||||
logFiles := core.PathGlob(core.JoinPath(wsDir, "agent-*.log"))
|
||||
logFiles := workspaceLogFiles(wsDir)
|
||||
if len(logFiles) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -88,7 +95,7 @@ func (s *PrepSubsystem) createIssueViaAPI(repo, title, description, issueType, p
|
|||
}
|
||||
|
||||
// Read the agent API key from file
|
||||
r := fs.Read(core.JoinPath(core.Env("DIR_HOME"), ".claude", "agent-api.key"))
|
||||
r := fs.Read(core.JoinPath(agentHomeDir(), ".claude", "agent-api.key"))
|
||||
if !r.OK {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,20 +45,21 @@ func TestIngest_IngestFindings_Good_WithFindings(t *testing.T) {
|
|||
"- `pkg/core/service.go:100` has a missing error check\n" +
|
||||
"- `pkg/core/config.go:25` needs documentation\n" +
|
||||
"This is padding to get past the 100 char minimum length requirement for the log file content parsing."
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, "agent-codex.log"), logContent).OK)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(wsDir, ".meta")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, ".meta", "agent-codex.log"), logContent).OK)
|
||||
|
||||
// Set up HOME for the agent-api.key read
|
||||
home := t.TempDir()
|
||||
t.Setenv("DIR_HOME", home)
|
||||
t.Setenv("HOME", home)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(home, ".claude")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(home, ".claude", "agent-api.key"), "test-api-key").OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
s.ingestFindings(wsDir)
|
||||
|
|
@ -74,8 +75,8 @@ func TestIngest_IngestFindings_Bad_NotCompleted(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should return early — status is not "completed"
|
||||
|
|
@ -93,8 +94,8 @@ func TestIngest_IngestFindings_Bad_NoLogFile(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should return early — no log files
|
||||
|
|
@ -112,12 +113,13 @@ func TestIngest_IngestFindings_Bad_TooFewFindings(t *testing.T) {
|
|||
|
||||
// Only 1 finding (need >= 2 to ingest)
|
||||
logContent := "Found: `main.go:1` has an issue. This padding makes the content long enough to pass the 100 char minimum check."
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, "agent-codex.log"), logContent).OK)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(wsDir, ".meta")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, ".meta", "agent-codex.log"), logContent).OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
|
|
@ -134,12 +136,13 @@ func TestIngest_IngestFindings_Bad_QuotaExhausted(t *testing.T) {
|
|||
|
||||
// Log contains quota error — should skip
|
||||
logContent := "QUOTA_EXHAUSTED: Rate limit exceeded. `main.go:1` `other.go:2` padding to ensure we pass length check and get past the threshold."
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, "agent-codex.log"), logContent).OK)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(wsDir, ".meta")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, ".meta", "agent-codex.log"), logContent).OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
|
|
@ -152,8 +155,8 @@ func TestIngest_IngestFindings_Bad_NoStatusFile(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
|
|
@ -169,12 +172,13 @@ func TestIngest_IngestFindings_Bad_ShortLogFile(t *testing.T) {
|
|||
}))
|
||||
|
||||
// Log content is less than 100 bytes — should skip
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, "agent-codex.log"), "short").OK)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(wsDir, ".meta")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(wsDir, ".meta", "agent-codex.log"), "short").OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
|
|
@ -203,12 +207,17 @@ func TestIngest_CreateIssueViaAPI_Good_Success(t *testing.T) {
|
|||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(home, ".claude")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(home, ".claude", "agent-api.key"), "test-key").OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
s.createIssueViaAPI("go-io", "Test Issue", "Description", "bug", "high", "scan")
|
||||
|
|
@ -218,9 +227,9 @@ func TestIngest_CreateIssueViaAPI_Good_Success(t *testing.T) {
|
|||
func TestIngest_CreateIssueViaAPI_Bad_NoBrainKey(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainKey: "",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainKey: "",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should return early without panic
|
||||
|
|
@ -231,15 +240,15 @@ func TestIngest_CreateIssueViaAPI_Bad_NoBrainKey(t *testing.T) {
|
|||
|
||||
func TestIngest_CreateIssueViaAPI_Bad_NoAPIKey(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("DIR_HOME", home)
|
||||
t.Setenv("HOME", home)
|
||||
// No agent-api.key file
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainURL: "https://example.com",
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainURL: "https://example.com",
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should return early — no API key file
|
||||
|
|
@ -255,16 +264,16 @@ func TestIngest_CreateIssueViaAPI_Bad_ServerError(t *testing.T) {
|
|||
t.Cleanup(srv.Close)
|
||||
|
||||
home := t.TempDir()
|
||||
t.Setenv("DIR_HOME", home)
|
||||
t.Setenv("HOME", home)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(home, ".claude")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(home, ".claude", "agent-api.key"), "test-key").OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should not panic even on server error
|
||||
|
|
@ -296,8 +305,8 @@ func TestIngest_IngestFindings_Ugly(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
// Should return early without panic — no log files
|
||||
|
|
@ -323,16 +332,16 @@ func TestIngest_CreateIssueViaAPI_Ugly(t *testing.T) {
|
|||
t.Cleanup(srv.Close)
|
||||
|
||||
home := t.TempDir()
|
||||
t.Setenv("DIR_HOME", home)
|
||||
t.Setenv("HOME", home)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(home, ".claude")).OK)
|
||||
require.True(t, fs.Write(core.JoinPath(home, ".claude", "agent-api.key"), "test-key").OK)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
brainURL: srv.URL,
|
||||
brainKey: "test-brain-key",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
s.createIssueViaAPI("go-io", "XSS Test", "<script>alert('xss')</script><b>bold</b>&", "bug", "high", "scan")
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ func WorkspaceRoot() string {
|
|||
return core.JoinPath(CoreRoot(), "workspace")
|
||||
}
|
||||
|
||||
// WorkspaceStatusPaths returns all workspace status files across supported layouts.
|
||||
//
|
||||
// paths := agentic.WorkspaceStatusPaths()
|
||||
func WorkspaceStatusPaths() []string {
|
||||
return workspaceStatusPaths(WorkspaceRoot())
|
||||
}
|
||||
|
||||
// WorkspaceName extracts the unique workspace name from a full path.
|
||||
// Given /Users/snider/Code/.core/workspace/core/go-io/dev → core/go-io/dev
|
||||
//
|
||||
|
|
@ -54,6 +61,32 @@ func CoreRoot() string {
|
|||
return core.JoinPath(core.Env("DIR_HOME"), "Code", ".core")
|
||||
}
|
||||
|
||||
func workspaceStatusPaths(wsRoot string) []string {
|
||||
old := core.PathGlob(core.JoinPath(wsRoot, "*", "status.json"))
|
||||
deep := core.PathGlob(core.JoinPath(wsRoot, "*", "*", "*", "status.json"))
|
||||
return append(old, deep...)
|
||||
}
|
||||
|
||||
func workspaceRepoDir(wsDir string) string {
|
||||
return core.JoinPath(wsDir, "repo")
|
||||
}
|
||||
|
||||
func workspaceMetaDir(wsDir string) string {
|
||||
return core.JoinPath(wsDir, ".meta")
|
||||
}
|
||||
|
||||
func workspaceBlockedPath(wsDir string) string {
|
||||
return core.JoinPath(workspaceRepoDir(wsDir), "BLOCKED.md")
|
||||
}
|
||||
|
||||
func workspaceAnswerPath(wsDir string) string {
|
||||
return core.JoinPath(workspaceRepoDir(wsDir), "ANSWER.md")
|
||||
}
|
||||
|
||||
func workspaceLogFiles(wsDir string) []string {
|
||||
return core.PathGlob(core.JoinPath(workspaceMetaDir(wsDir), "agent-*.log"))
|
||||
}
|
||||
|
||||
// PlansRoot returns the root directory for agent plans.
|
||||
//
|
||||
// plansDir := agentic.PlansRoot()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"context"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/forge"
|
||||
forge_types "dappco.re/go/core/forge/types"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
|
@ -157,16 +156,17 @@ func (s *PrepSubsystem) buildPRBody(st *WorkspaceStatus) string {
|
|||
}
|
||||
|
||||
func (s *PrepSubsystem) forgeCreatePR(ctx context.Context, org, repo, head, base, title, body string) (string, int, error) {
|
||||
pr, err := s.forge.Pulls.Create(ctx, forge.Params{"owner": org, "repo": repo}, &forge_types.CreatePullRequestOption{
|
||||
var pr pullRequestView
|
||||
err := s.forge.Client().Post(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls", org, repo), &forge_types.CreatePullRequestOption{
|
||||
Title: title,
|
||||
Body: body,
|
||||
Head: head,
|
||||
Base: base,
|
||||
})
|
||||
}, &pr)
|
||||
if err != nil {
|
||||
return "", 0, core.E("forgeCreatePR", "create PR failed", err)
|
||||
}
|
||||
return pr.HTMLURL, int(pr.Index), nil
|
||||
return pr.HTMLURL, int(pullRequestNumber(pr)), nil
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) commentOnIssue(ctx context.Context, org, repo string, issue int, comment string) {
|
||||
|
|
@ -269,30 +269,31 @@ func (s *PrepSubsystem) listPRs(ctx context.Context, _ *mcp.CallToolRequest, inp
|
|||
}
|
||||
|
||||
func (s *PrepSubsystem) listRepoPRs(ctx context.Context, org, repo, state string) ([]PRInfo, error) {
|
||||
prs, err := s.forge.Pulls.ListAll(ctx, forge.Params{"owner": org, "repo": repo})
|
||||
var prs []pullRequestView
|
||||
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls?limit=50&page=1", org, repo), &prs)
|
||||
if err != nil {
|
||||
return nil, core.E("listRepoPRs", core.Concat("failed to list PRs for ", repo), err)
|
||||
}
|
||||
|
||||
var result []PRInfo
|
||||
for _, pr := range prs {
|
||||
if state != "" && state != "all" && string(pr.State) != state {
|
||||
prState := pr.State
|
||||
if prState == "" {
|
||||
prState = "open"
|
||||
}
|
||||
if state != "" && state != "all" && prState != state {
|
||||
continue
|
||||
}
|
||||
var labels []string
|
||||
for _, l := range pr.Labels {
|
||||
labels = append(labels, l.Name)
|
||||
}
|
||||
author := ""
|
||||
if pr.User != nil {
|
||||
author = pr.User.UserName
|
||||
}
|
||||
result = append(result, PRInfo{
|
||||
Repo: repo,
|
||||
Number: int(pr.Index),
|
||||
Number: int(pullRequestNumber(pr)),
|
||||
Title: pr.Title,
|
||||
State: string(pr.State),
|
||||
Author: author,
|
||||
State: prState,
|
||||
Author: pullRequestAuthor(pr),
|
||||
Branch: pr.Head.Ref,
|
||||
Base: pr.Base.Ref,
|
||||
Labels: labels,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
"dappco.re/go/agent/pkg/lib"
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/forge"
|
||||
coremcp "dappco.re/go/mcp/pkg/mcp"
|
||||
coremcp "forge.lthn.ai/core/mcp/pkg/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
|
@ -37,8 +37,8 @@ type PrepSubsystem struct {
|
|||
drainMu sync.Mutex
|
||||
pokeCh chan struct{}
|
||||
frozen bool
|
||||
backoff map[string]time.Time // pool → paused until
|
||||
failCount map[string]int // pool → consecutive fast failures
|
||||
backoff map[string]time.Time // pool → paused until
|
||||
failCount map[string]int // pool → consecutive fast failures
|
||||
workspaces *core.Registry[*WorkspaceStatus] // in-memory workspace state
|
||||
}
|
||||
|
||||
|
|
@ -410,8 +410,8 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
|
|||
return nil, PrepOutput{}, err
|
||||
}
|
||||
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
metaDir := core.JoinPath(wsDir, ".meta")
|
||||
repoDir := workspaceRepoDir(wsDir)
|
||||
metaDir := workspaceMetaDir(wsDir)
|
||||
out := PrepOutput{WorkspaceDir: wsDir, RepoDir: repoDir}
|
||||
|
||||
// Source repo path — sanitise to prevent path traversal
|
||||
|
|
@ -431,9 +431,22 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
|
|||
out.Resumed = resumed
|
||||
|
||||
if resumed {
|
||||
// Pull latest from origin so the workspace has current code
|
||||
s.gitCmd(ctx, repoDir, "checkout", "main")
|
||||
s.gitCmd(ctx, repoDir, "pull", "origin", "main")
|
||||
// Preserve the current branch on resume. Pull it only if it exists on
|
||||
// origin; otherwise refresh the default branch refs without abandoning the
|
||||
// workspace branch.
|
||||
currentBranch := s.gitOutput(ctx, repoDir, "rev-parse", "--abbrev-ref", "HEAD")
|
||||
defaultBranch := s.DefaultBranch(repoDir)
|
||||
if currentBranch == "" || currentBranch == "HEAD" {
|
||||
currentBranch = defaultBranch
|
||||
}
|
||||
if currentBranch != "" {
|
||||
s.gitCmd(ctx, repoDir, "checkout", currentBranch)
|
||||
if s.gitCmdOK(ctx, repoDir, "ls-remote", "--exit-code", "--heads", "origin", currentBranch) {
|
||||
s.gitCmd(ctx, repoDir, "pull", "--ff-only", "origin", currentBranch)
|
||||
} else if defaultBranch != "" {
|
||||
s.gitCmd(ctx, repoDir, "fetch", "origin", defaultBranch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract default workspace template (go.work etc.)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/process"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -24,7 +23,7 @@ var testCore *core.Core
|
|||
// TestMain sets up a PrepSubsystem with go-process registered for all tests in the package.
|
||||
func TestMain(m *testing.M) {
|
||||
testCore = core.New(
|
||||
core.WithService(process.Register),
|
||||
core.WithService(ProcessRegister),
|
||||
)
|
||||
testCore.ServiceStartup(context.Background(), nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,99 @@
|
|||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/process"
|
||||
)
|
||||
|
||||
// ProcessRegister is the service factory for go-process.
|
||||
// Delegates to process.Register — named Actions (process.run, process.start,
|
||||
// process.kill, process.list, process.get) are registered during OnStartup.
|
||||
// Registers the process service under the canonical "process" name and exposes
|
||||
// the Core `process.*` Actions expected by `c.Process()`.
|
||||
//
|
||||
// core.New(
|
||||
// core.WithService(agentic.ProcessRegister),
|
||||
// )
|
||||
func ProcessRegister(c *core.Core) core.Result {
|
||||
return process.Register(c)
|
||||
if c == nil {
|
||||
return core.Result{Value: core.E("agentic.ProcessRegister", "core is required", nil), OK: false}
|
||||
}
|
||||
if _, ok := core.ServiceFor[*process.Service](c, "process"); ok {
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
factory := process.NewService(process.Options{})
|
||||
instance, err := factory(c)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("agentic.ProcessRegister", "create process service", err), OK: false}
|
||||
}
|
||||
svc, ok := instance.(*process.Service)
|
||||
if !ok {
|
||||
return core.Result{Value: core.E("agentic.ProcessRegister", "unexpected process service type", nil), OK: false}
|
||||
}
|
||||
if r := c.RegisterService("process", svc); !r.OK {
|
||||
return r
|
||||
}
|
||||
|
||||
c.Action("process.run", func(ctx context.Context, opts core.Options) core.Result {
|
||||
output, err := svc.RunWithOptions(ctx, process.RunOptions{
|
||||
Command: opts.String("command"),
|
||||
Args: optionStrings(opts, "args"),
|
||||
Dir: opts.String("dir"),
|
||||
Env: optionStrings(opts, "env"),
|
||||
})
|
||||
if err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
})
|
||||
|
||||
c.Action("process.start", func(ctx context.Context, opts core.Options) core.Result {
|
||||
proc, err := svc.StartWithOptions(ctx, process.RunOptions{
|
||||
Command: opts.String("command"),
|
||||
Args: optionStrings(opts, "args"),
|
||||
Dir: opts.String("dir"),
|
||||
Env: optionStrings(opts, "env"),
|
||||
Detach: opts.Bool("detach"),
|
||||
})
|
||||
if err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
return core.Result{Value: proc, OK: true}
|
||||
})
|
||||
|
||||
c.Action("process.kill", func(_ context.Context, opts core.Options) core.Result {
|
||||
id := opts.String("id")
|
||||
if id == "" {
|
||||
return core.Result{Value: core.E("agentic.ProcessRegister", "process id is required", nil), OK: false}
|
||||
}
|
||||
if err := svc.Kill(id); err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
})
|
||||
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
func optionStrings(opts core.Options, key string) []string {
|
||||
r := opts.Get(key)
|
||||
if !r.OK {
|
||||
return nil
|
||||
}
|
||||
switch values := r.Value.(type) {
|
||||
case []string:
|
||||
return values
|
||||
case []any:
|
||||
out := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
out = append(out, core.Sprint(value))
|
||||
}
|
||||
return out
|
||||
default:
|
||||
if text := core.Sprint(values); text != "" {
|
||||
return []string{text}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func TestRegister_ProcessRegister_Good(t *testing.T) {
|
|||
c := core.New()
|
||||
result := ProcessRegister(c)
|
||||
assert.True(t, result.OK, "ProcessRegister should succeed with a real Core instance")
|
||||
assert.NotNil(t, result.Value)
|
||||
assert.True(t, c.Process().Exists(), "ProcessRegister should register the process service")
|
||||
}
|
||||
|
||||
func TestRegister_ProcessRegister_Bad(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func (s *PrepSubsystem) resume(ctx context.Context, _ *mcp.CallToolRequest, inpu
|
|||
|
||||
// Write ANSWER.md if answer provided
|
||||
if input.Answer != "" {
|
||||
answerPath := core.JoinPath(repoDir, "ANSWER.md")
|
||||
answerPath := workspaceAnswerPath(wsDir)
|
||||
content := core.Sprintf("# Answer\n\n%s\n", input.Answer)
|
||||
if r := fs.Write(answerPath, content); !r.OK {
|
||||
err, _ := r.Value.(error)
|
||||
|
|
@ -111,6 +111,6 @@ func (s *PrepSubsystem) resume(ctx context.Context, _ *mcp.CallToolRequest, inpu
|
|||
Workspace: input.Workspace,
|
||||
Agent: agent,
|
||||
PID: pid,
|
||||
OutputFile: core.JoinPath(wsDir, core.Sprintf("agent-%s.log", agent)),
|
||||
OutputFile: agentOutputFile(wsDir, agent),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ import (
|
|||
// Workspace status file convention:
|
||||
//
|
||||
// {workspace}/status.json — current state of the workspace
|
||||
// {workspace}/BLOCKED.md — question the agent needs answered (written by agent)
|
||||
// {workspace}/ANSWER.md — response from human (written by reviewer)
|
||||
// {workspace}/repo/BLOCKED.md — question the agent needs answered (written by agent)
|
||||
// {workspace}/repo/ANSWER.md — response from human (written by reviewer)
|
||||
// {workspace}/.meta/agent-*.log — captured agent output
|
||||
//
|
||||
// Status lifecycle:
|
||||
// running → completed (normal finish)
|
||||
|
|
@ -29,20 +30,20 @@ import (
|
|||
// st, err := ReadStatus(wsDir)
|
||||
// if err == nil && st.Status == "completed" { autoCreatePR(wsDir) }
|
||||
type WorkspaceStatus struct {
|
||||
Status string `json:"status"` // running, completed, blocked, failed
|
||||
Agent string `json:"agent"` // gemini, claude, codex
|
||||
Repo string `json:"repo"` // target repo
|
||||
Org string `json:"org,omitempty"` // forge org (e.g. "core")
|
||||
Task string `json:"task"` // task description
|
||||
Branch string `json:"branch,omitempty"` // git branch name
|
||||
Status string `json:"status"` // running, completed, blocked, failed
|
||||
Agent string `json:"agent"` // gemini, claude, codex
|
||||
Repo string `json:"repo"` // target repo
|
||||
Org string `json:"org,omitempty"` // forge org (e.g. "core")
|
||||
Task string `json:"task"` // task description
|
||||
Branch string `json:"branch,omitempty"` // git branch name
|
||||
Issue int `json:"issue,omitempty"` // forge issue number
|
||||
PID int `json:"pid,omitempty"` // OS process ID (if running)
|
||||
ProcessID string `json:"process_id,omitempty"` // go-process ID for managed lookup
|
||||
StartedAt time.Time `json:"started_at"` // when dispatch started
|
||||
UpdatedAt time.Time `json:"updated_at"` // last status change
|
||||
Question string `json:"question,omitempty"` // from BLOCKED.md
|
||||
Runs int `json:"runs"` // how many times dispatched/resumed
|
||||
PRURL string `json:"pr_url,omitempty"` // pull request URL (after PR created)
|
||||
UpdatedAt time.Time `json:"updated_at"` // last status change
|
||||
Question string `json:"question,omitempty"` // from BLOCKED.md
|
||||
Runs int `json:"runs"` // how many times dispatched/resumed
|
||||
PRURL string `json:"pr_url,omitempty"` // pull request URL (after PR created)
|
||||
}
|
||||
|
||||
// WorkspaceQuery is the QUERY type for workspace state lookups.
|
||||
|
|
@ -98,12 +99,12 @@ type StatusInput struct {
|
|||
//
|
||||
// out := agentic.StatusOutput{Total: 42, Running: 3, Queued: 10, Completed: 25}
|
||||
type StatusOutput struct {
|
||||
Total int `json:"total"`
|
||||
Running int `json:"running"`
|
||||
Queued int `json:"queued"`
|
||||
Completed int `json:"completed"`
|
||||
Failed int `json:"failed"`
|
||||
Blocked []BlockedInfo `json:"blocked,omitempty"`
|
||||
Total int `json:"total"`
|
||||
Running int `json:"running"`
|
||||
Queued int `json:"queued"`
|
||||
Completed int `json:"completed"`
|
||||
Failed int `json:"failed"`
|
||||
Blocked []BlockedInfo `json:"blocked,omitempty"`
|
||||
}
|
||||
|
||||
// BlockedInfo shows a workspace that needs human input.
|
||||
|
|
@ -126,10 +127,7 @@ func (s *PrepSubsystem) registerStatusTool(server *mcp.Server) {
|
|||
func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, input StatusInput) (*mcp.CallToolResult, StatusOutput, error) {
|
||||
wsRoot := WorkspaceRoot()
|
||||
|
||||
// Scan both old (*/status.json) and new (*/*/*/status.json) layouts
|
||||
old := core.PathGlob(core.JoinPath(wsRoot, "*", "status.json"))
|
||||
deep := core.PathGlob(core.JoinPath(wsRoot, "*", "*", "*", "status.json"))
|
||||
statusFiles := append(old, deep...)
|
||||
statusFiles := WorkspaceStatusPaths()
|
||||
|
||||
var out StatusOutput
|
||||
|
||||
|
|
@ -147,13 +145,12 @@ func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, inpu
|
|||
// If status is "running", check if PID is still alive
|
||||
if st.Status == "running" && st.PID > 0 {
|
||||
if err := syscall.Kill(st.PID, 0); err != nil {
|
||||
blockedPath := core.JoinPath(wsDir, "repo", "BLOCKED.md")
|
||||
blockedPath := workspaceBlockedPath(wsDir)
|
||||
if r := fs.Read(blockedPath); r.OK {
|
||||
st.Status = "blocked"
|
||||
st.Question = core.Trim(r.Value.(string))
|
||||
} else {
|
||||
logFile := core.JoinPath(wsDir, core.Sprintf("agent-%s.log", st.Agent))
|
||||
if r := fs.Read(logFile); !r.OK {
|
||||
if len(workspaceLogFiles(wsDir)) == 0 {
|
||||
st.Status = "failed"
|
||||
st.Question = "Agent process died (no output log)"
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -199,13 +199,14 @@ func TestStatus_Status_Ugly(t *testing.T) {
|
|||
// Case 2: running + dead PID + agent log → completed
|
||||
ws2 := core.JoinPath(wsRoot, "dead-completed")
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(ws2, "repo")).OK)
|
||||
require.True(t, fs.EnsureDir(core.JoinPath(ws2, ".meta")).OK)
|
||||
require.NoError(t, writeStatus(ws2, &WorkspaceStatus{
|
||||
Status: "running",
|
||||
Repo: "go-log",
|
||||
Agent: "claude",
|
||||
PID: 999999,
|
||||
}))
|
||||
require.True(t, fs.Write(core.JoinPath(ws2, "agent-claude.log"), "agent finished ok").OK)
|
||||
require.True(t, fs.Write(core.JoinPath(ws2, ".meta", "agent-claude.log"), "agent finished ok").OK)
|
||||
|
||||
// Case 3: running + dead PID + no log + no BLOCKED.md → failed
|
||||
ws3 := core.JoinPath(wsRoot, "dead-failed")
|
||||
|
|
@ -219,8 +220,8 @@ func TestStatus_Status_Ugly(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, out, err := s.status(nil, nil, StatusInput{})
|
||||
|
|
@ -336,8 +337,8 @@ func TestStatus_Status_Good_PopulatedWorkspaces(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, out, err := s.status(context.Background(), nil, StatusInput{})
|
||||
|
|
@ -356,8 +357,8 @@ func TestStatus_Status_Bad_EmptyWorkspaceRoot(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, out, err := s.status(context.Background(), nil, StatusInput{})
|
||||
|
|
|
|||
|
|
@ -191,6 +191,16 @@ type verifyResult struct {
|
|||
testCmd string
|
||||
}
|
||||
|
||||
func resultText(r core.Result) string {
|
||||
if text, ok := r.Value.(string); ok {
|
||||
return text
|
||||
}
|
||||
if r.Value != nil {
|
||||
return core.Sprint(r.Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// runVerification detects the project type and runs the appropriate test suite.
|
||||
func (s *PrepSubsystem) runVerification(repoDir string) verifyResult {
|
||||
if fileExists(core.JoinPath(repoDir, "go.mod")) {
|
||||
|
|
@ -208,7 +218,7 @@ func (s *PrepSubsystem) runVerification(repoDir string) verifyResult {
|
|||
func (s *PrepSubsystem) runGoTests(repoDir string) verifyResult {
|
||||
ctx := context.Background()
|
||||
r := s.runCmdEnv(ctx, repoDir, []string{"GOWORK=off"}, "go", "test", "./...", "-count=1", "-timeout", "120s")
|
||||
out := r.Value.(string)
|
||||
out := resultText(r)
|
||||
exitCode := 0
|
||||
if !r.OK {
|
||||
exitCode = 1
|
||||
|
|
@ -225,9 +235,9 @@ func (s *PrepSubsystem) runPHPTests(repoDir string) verifyResult {
|
|||
if !r2.OK {
|
||||
return verifyResult{passed: false, testCmd: "none", output: "No PHP test runner found (composer test and vendor/bin/pest both unavailable)", exitCode: 1}
|
||||
}
|
||||
return verifyResult{passed: true, output: r2.Value.(string), exitCode: 0, testCmd: "vendor/bin/pest"}
|
||||
return verifyResult{passed: true, output: resultText(r2), exitCode: 0, testCmd: "vendor/bin/pest"}
|
||||
}
|
||||
return verifyResult{passed: true, output: r.Value.(string), exitCode: 0, testCmd: "composer test"}
|
||||
return verifyResult{passed: true, output: resultText(r), exitCode: 0, testCmd: "composer test"}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) runNodeTests(repoDir string) verifyResult {
|
||||
|
|
@ -245,7 +255,7 @@ func (s *PrepSubsystem) runNodeTests(repoDir string) verifyResult {
|
|||
|
||||
ctx := context.Background()
|
||||
r = s.runCmd(ctx, repoDir, "npm", "test")
|
||||
out := r.Value.(string)
|
||||
out := resultText(r)
|
||||
exitCode := 0
|
||||
if !r.OK {
|
||||
exitCode = 1
|
||||
|
|
|
|||
|
|
@ -190,18 +190,15 @@ func (s *PrepSubsystem) watch(ctx context.Context, req *mcp.CallToolRequest, inp
|
|||
|
||||
// findActiveWorkspaces returns workspace names that are running or queued.
|
||||
func (s *PrepSubsystem) findActiveWorkspaces() []string {
|
||||
wsRoot := WorkspaceRoot()
|
||||
entries := core.PathGlob(core.JoinPath(wsRoot, "*/status.json"))
|
||||
|
||||
var active []string
|
||||
for _, entry := range entries {
|
||||
for _, entry := range WorkspaceStatusPaths() {
|
||||
wsDir := core.PathDir(entry)
|
||||
st, err := ReadStatus(wsDir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if st.Status == "running" || st.Status == "queued" {
|
||||
active = append(active, core.PathBase(wsDir))
|
||||
active = append(active, WorkspaceName(wsDir))
|
||||
}
|
||||
}
|
||||
return active
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import (
|
|||
func TestWatch_ResolveWorkspaceDir_Good_RelativeName(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
dir := s.resolveWorkspaceDir("go-io-abc123")
|
||||
assert.Contains(t, dir, "go-io-abc123")
|
||||
|
|
@ -26,8 +26,8 @@ func TestWatch_ResolveWorkspaceDir_Good_RelativeName(t *testing.T) {
|
|||
func TestWatch_ResolveWorkspaceDir_Good_AbsolutePath(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
abs := "/some/absolute/path"
|
||||
assert.Equal(t, abs, s.resolveWorkspaceDir(abs))
|
||||
|
|
@ -58,8 +58,8 @@ func TestWatch_FindActiveWorkspaces_Good_WithActive(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
active := s.findActiveWorkspaces()
|
||||
assert.Contains(t, active, "ws-running")
|
||||
|
|
@ -67,6 +67,25 @@ func TestWatch_FindActiveWorkspaces_Good_WithActive(t *testing.T) {
|
|||
assert.NotContains(t, active, "ws-done")
|
||||
}
|
||||
|
||||
func TestWatch_FindActiveWorkspaces_Good_DeepLayout(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
t.Setenv("CORE_WORKSPACE", root)
|
||||
|
||||
ws := core.JoinPath(root, "workspace", "core", "go-io", "task-15")
|
||||
fs.EnsureDir(ws)
|
||||
fs.Write(core.JoinPath(ws, "status.json"), core.JSONMarshalString(WorkspaceStatus{
|
||||
Status: "running", Repo: "go-io", Agent: "codex",
|
||||
}))
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
active := s.findActiveWorkspaces()
|
||||
assert.Contains(t, active, "core/go-io/task-15")
|
||||
}
|
||||
|
||||
func TestWatch_FindActiveWorkspaces_Good_Empty(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
t.Setenv("CORE_WORKSPACE", root)
|
||||
|
|
@ -76,8 +95,8 @@ func TestWatch_FindActiveWorkspaces_Good_Empty(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
active := s.findActiveWorkspaces()
|
||||
assert.Empty(t, active)
|
||||
|
|
@ -92,8 +111,8 @@ func TestWatch_FindActiveWorkspaces_Bad(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
assert.NotPanics(t, func() {
|
||||
active := s.findActiveWorkspaces()
|
||||
|
|
@ -119,8 +138,8 @@ func TestWatch_FindActiveWorkspaces_Ugly(t *testing.T) {
|
|||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
active := s.findActiveWorkspaces()
|
||||
|
|
@ -135,8 +154,8 @@ func TestWatch_ResolveWorkspaceDir_Bad(t *testing.T) {
|
|||
// Empty name
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
dir := s.resolveWorkspaceDir("")
|
||||
assert.NotEmpty(t, dir, "empty name should still resolve to workspace root")
|
||||
|
|
@ -147,8 +166,8 @@ func TestWatch_ResolveWorkspaceDir_Ugly(t *testing.T) {
|
|||
// Name with path traversal "../.."
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
assert.NotPanics(t, func() {
|
||||
dir := s.resolveWorkspaceDir("../..")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/mcp/pkg/mcp/ide"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp/ide"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import (
|
|||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
providerws "dappco.re/go/core/ws"
|
||||
bridgews "forge.lthn.ai/core/go-ws"
|
||||
"dappco.re/go/mcp/pkg/mcp/ide"
|
||||
providerws "forge.lthn.ai/core/go-ws"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp/ide"
|
||||
"github.com/gorilla/websocket"
|
||||
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -46,7 +45,7 @@ func testBridge(t *testing.T) *ide.Bridge {
|
|||
srv := testWSServer(t)
|
||||
|
||||
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http")
|
||||
hub := bridgews.NewHub()
|
||||
hub := providerws.NewHub()
|
||||
bridge := ide.NewBridge(hub, ide.Config{
|
||||
LaravelWSURL: wsURL,
|
||||
ReconnectInterval: 100 * time.Millisecond,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
core "dappco.re/go/core"
|
||||
coremcp "dappco.re/go/mcp/pkg/mcp"
|
||||
coremcp "forge.lthn.ai/core/mcp/pkg/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
"dappco.re/go/core/api"
|
||||
"dappco.re/go/core/api/pkg/provider"
|
||||
"dappco.re/go/core/ws"
|
||||
"dappco.re/go/mcp/pkg/mcp/ide"
|
||||
"forge.lthn.ai/core/go-ws"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp/ide"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/mcp/pkg/mcp/ide"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp/ide"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -88,35 +88,35 @@ func (m *Subsystem) harvestWorkspace(wsDir string) *harvestResult {
|
|||
return nil
|
||||
}
|
||||
|
||||
srcDir := core.Concat(wsDir, "/src")
|
||||
if !fs.IsDir(srcDir) {
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
if !fs.IsDir(repoDir) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if there are commits to push
|
||||
branch := st.Branch
|
||||
if branch == "" {
|
||||
branch = m.detectBranch(srcDir)
|
||||
branch = m.detectBranch(repoDir)
|
||||
}
|
||||
base := m.defaultBranch(srcDir)
|
||||
base := m.defaultBranch(repoDir)
|
||||
if branch == "" || branch == base {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for unpushed commits
|
||||
unpushed := m.countUnpushed(srcDir, branch)
|
||||
unpushed := m.countUnpushed(repoDir, branch)
|
||||
if unpushed == 0 {
|
||||
return nil // already pushed or no commits
|
||||
}
|
||||
|
||||
// Safety checks before pushing
|
||||
if reason := m.checkSafety(srcDir); reason != "" {
|
||||
if reason := m.checkSafety(repoDir); reason != "" {
|
||||
updateStatus(wsDir, "rejected", reason)
|
||||
return &harvestResult{repo: st.Repo, branch: branch, rejected: reason}
|
||||
}
|
||||
|
||||
// Count changed files
|
||||
files := m.countChangedFiles(srcDir)
|
||||
files := m.countChangedFiles(repoDir)
|
||||
|
||||
// Mark ready for review — do NOT auto-push.
|
||||
// Pushing is a high-impact mutation that should happen during
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/process"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -26,17 +26,17 @@ func initTestRepo(t *testing.T) (sourceDir, wsDir string) {
|
|||
run(t, sourceDir, "git", "add", ".")
|
||||
run(t, sourceDir, "git", "commit", "-m", "init")
|
||||
|
||||
// Create workspace dir with src/ clone
|
||||
// Create workspace dir with repo/ clone
|
||||
wsDir = core.JoinPath(t.TempDir(), "workspace")
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
fs.EnsureDir(wsDir)
|
||||
run(t, wsDir, "git", "clone", sourceDir, "src")
|
||||
run(t, wsDir, "git", "clone", sourceDir, "repo")
|
||||
|
||||
// Create agent branch with a commit
|
||||
run(t, srcDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(srcDir, "new.go"), "package main\n")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "agent work")
|
||||
run(t, repoDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(repoDir, "new.go"), "package main\n")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "agent work")
|
||||
|
||||
return sourceDir, wsDir
|
||||
}
|
||||
|
|
@ -62,9 +62,9 @@ func writeStatus(t *testing.T, wsDir, status, repo, branch string) {
|
|||
|
||||
func TestHarvest_DetectBranch_Good(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
branch := testMon.detectBranch(srcDir)
|
||||
branch := testMon.detectBranch(repoDir)
|
||||
assert.Equal(t, "agent/test-task", branch)
|
||||
}
|
||||
|
||||
|
|
@ -75,53 +75,53 @@ func TestHarvest_DetectBranch_Bad_NoRepo(t *testing.T) {
|
|||
|
||||
func TestHarvest_CountUnpushed_Good(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
count := testMon.countUnpushed(srcDir, "agent/test-task")
|
||||
count := testMon.countUnpushed(repoDir, "agent/test-task")
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestHarvest_CountChangedFiles_Good(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
count := testMon.countChangedFiles(srcDir)
|
||||
count := testMon.countChangedFiles(repoDir)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestHarvest_CheckSafety_Good_CleanWorkspace(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
reason := testMon.checkSafety(srcDir)
|
||||
reason := testMon.checkSafety(repoDir)
|
||||
assert.Equal(t, "", reason)
|
||||
}
|
||||
|
||||
func TestHarvest_CheckSafety_Bad_BinaryFile(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
// Add a binary file
|
||||
fs.Write(core.JoinPath(srcDir, "app.exe"), "binary")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "add binary")
|
||||
fs.Write(core.JoinPath(repoDir, "app.exe"), "binary")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "add binary")
|
||||
|
||||
reason := testMon.checkSafety(srcDir)
|
||||
reason := testMon.checkSafety(repoDir)
|
||||
assert.Contains(t, reason, "binary file added")
|
||||
assert.Contains(t, reason, "app.exe")
|
||||
}
|
||||
|
||||
func TestHarvest_CheckSafety_Bad_LargeFile(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
// Add a file > 1MB
|
||||
bigData := make([]byte, 1024*1024+1)
|
||||
fs.Write(core.JoinPath(srcDir, "huge.txt"), string(bigData))
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "add large file")
|
||||
fs.Write(core.JoinPath(repoDir, "huge.txt"), string(bigData))
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "add large file")
|
||||
|
||||
reason := testMon.checkSafety(srcDir)
|
||||
reason := testMon.checkSafety(repoDir)
|
||||
assert.Contains(t, reason, "large file")
|
||||
assert.Contains(t, reason, "huge.txt")
|
||||
}
|
||||
|
|
@ -160,8 +160,8 @@ func TestHarvest_HarvestWorkspace_Bad_MainBranch(t *testing.T) {
|
|||
_, wsDir := initTestRepo(t)
|
||||
|
||||
// Switch back to main
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
run(t, srcDir, "git", "checkout", "main")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
run(t, repoDir, "git", "checkout", "main")
|
||||
|
||||
writeStatus(t, wsDir, "completed", "test-repo", "main")
|
||||
|
||||
|
|
@ -173,12 +173,12 @@ func TestHarvest_HarvestWorkspace_Bad_MainBranch(t *testing.T) {
|
|||
|
||||
func TestHarvest_HarvestWorkspace_Bad_BinaryRejected(t *testing.T) {
|
||||
_, wsDir := initTestRepo(t)
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
|
||||
// Add binary
|
||||
fs.Write(core.JoinPath(srcDir, "build.so"), "elf")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "add binary")
|
||||
fs.Write(core.JoinPath(repoDir, "build.so"), "elf")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "add binary")
|
||||
|
||||
writeStatus(t, wsDir, "completed", "test-repo", "agent/test-task")
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ func TestHarvest_HarvestCompleted_Good_ChannelEvents(t *testing.T) {
|
|||
|
||||
// Create a Core with process + IPC handler to capture HarvestComplete messages
|
||||
var captured []messages.HarvestComplete
|
||||
c := core.New(core.WithService(process.Register))
|
||||
c := core.New(core.WithService(agentic.ProcessRegister))
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
|
||||
if ev, ok := msg.(messages.HarvestComplete); ok {
|
||||
|
|
@ -251,4 +251,3 @@ func TestHarvest_UpdateStatus_Good_WithQuestion(t *testing.T) {
|
|||
assert.Equal(t, "rejected", st["status"])
|
||||
assert.Equal(t, "binary file: app.exe", st["question"])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
"dappco.re/go/agent/pkg/agentic"
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
coremcp "dappco.re/go/mcp/pkg/mcp"
|
||||
coremcp "forge.lthn.ai/core/mcp/pkg/mcp"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
|
|
@ -28,6 +28,10 @@ import (
|
|||
// if text, ok := resultString(r); ok { json.Unmarshal([]byte(text), &st) }
|
||||
var fs = agentic.LocalFs()
|
||||
|
||||
type channelSender interface {
|
||||
ChannelSend(ctx context.Context, channel string, data any)
|
||||
}
|
||||
|
||||
// workspaceStatusPaths returns all status.json files across both old and new workspace layouts.
|
||||
// Old: workspace/{name}/status.json (1 level)
|
||||
// New: workspace/{org}/{repo}/{identifier}/status.json (3 levels)
|
||||
|
|
@ -54,6 +58,9 @@ func monitorHomeDir() string {
|
|||
if d := core.Env("CORE_HOME"); d != "" {
|
||||
return d
|
||||
}
|
||||
if d := core.Env("HOME"); d != "" {
|
||||
return d
|
||||
}
|
||||
return core.Env("DIR_HOME")
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +207,7 @@ func (m *Subsystem) Start(ctx context.Context) {
|
|||
monCtx, cancel := context.WithCancel(ctx)
|
||||
m.cancel = cancel
|
||||
|
||||
core.Info( "monitor: started (interval=%s)", m.interval)
|
||||
core.Info("monitor: started (interval=%s)", m.interval)
|
||||
|
||||
m.wg.Add(1)
|
||||
go func() {
|
||||
|
|
@ -240,7 +247,6 @@ func (m *Subsystem) Poke() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// checkIdleAfterDelay waits briefly then checks if the fleet is genuinely idle.
|
||||
// Only emits queue.drained when there are truly zero running or queued agents,
|
||||
// verified by checking PIDs are alive, not just trusting status files.
|
||||
|
|
@ -447,26 +453,13 @@ func (m *Subsystem) checkCompletions() string {
|
|||
|
||||
// checkInbox checks for unread messages.
|
||||
func (m *Subsystem) checkInbox() string {
|
||||
apiKeyStr := core.Env("CORE_BRAIN_KEY")
|
||||
apiKeyStr := monitorBrainKey()
|
||||
if apiKeyStr == "" {
|
||||
home := core.Env("DIR_HOME")
|
||||
keyFile := brainKeyPath(home)
|
||||
r := fs.Read(keyFile)
|
||||
if !r.OK {
|
||||
return ""
|
||||
}
|
||||
value, ok := resultString(r)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
apiKeyStr = value
|
||||
return ""
|
||||
}
|
||||
|
||||
// Call the API to check inbox
|
||||
apiURL := core.Env("CORE_API_URL")
|
||||
if apiURL == "" {
|
||||
apiURL = "https://api.lthn.sh"
|
||||
}
|
||||
apiURL := monitorAPIURL()
|
||||
inboxURL := core.Concat(apiURL, "/v1/messages/inbox?agent=", core.Replace(agentic.AgentName(), " ", "%20"))
|
||||
hr := agentic.HTTPGet(context.Background(), inboxURL, core.Trim(apiKeyStr), "Bearer")
|
||||
if !hr.OK {
|
||||
|
|
@ -539,15 +532,14 @@ func (m *Subsystem) checkInbox() string {
|
|||
|
||||
// Push each message as a channel event so it lands in the session
|
||||
if m.ServiceRuntime != nil {
|
||||
for _, msg := range newMessages {
|
||||
m.Core().ACTION(coremcp.ChannelPush{
|
||||
Channel: "inbox.message",
|
||||
Data: map[string]any{
|
||||
if notifier, ok := core.ServiceFor[channelSender](m.Core(), "mcp"); ok {
|
||||
for _, msg := range newMessages {
|
||||
notifier.ChannelSend(context.Background(), "inbox.message", map[string]any{
|
||||
"from": msg.From,
|
||||
"subject": msg.Subject,
|
||||
"content": msg.Content,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,27 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
coremcp "dappco.re/go/mcp/pkg/mcp"
|
||||
"dappco.re/go/core/process"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type capturedChannelEvent struct {
|
||||
Channel string
|
||||
Data any
|
||||
}
|
||||
|
||||
type captureNotifier struct {
|
||||
events []capturedChannelEvent
|
||||
}
|
||||
|
||||
func (n *captureNotifier) ChannelSend(_ context.Context, channel string, data any) {
|
||||
n.events = append(n.events, capturedChannelEvent{Channel: channel, Data: data})
|
||||
}
|
||||
|
||||
// setupBrainKey creates a ~/.claude/brain.key file for API auth tests.
|
||||
func setupBrainKey(t *testing.T, key string) {
|
||||
t.Helper()
|
||||
|
|
@ -260,15 +272,10 @@ func TestMonitor_CheckInbox_Good_UnreadMessages(t *testing.T) {
|
|||
t.Setenv("CORE_API_URL", srv.URL)
|
||||
t.Setenv("AGENT_NAME", "test-agent")
|
||||
|
||||
// Create Core with IPC handler to capture ChannelPush
|
||||
var captured []coremcp.ChannelPush
|
||||
// Create Core with an MCP notifier to capture channel events.
|
||||
c := core.New()
|
||||
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
|
||||
if ev, ok := msg.(coremcp.ChannelPush); ok {
|
||||
captured = append(captured, ev)
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
})
|
||||
notifier := &captureNotifier{}
|
||||
require.True(t, c.RegisterService("mcp", notifier).OK)
|
||||
|
||||
mon := New()
|
||||
mon.ServiceRuntime = core.NewServiceRuntime(c, MonitorOptions{})
|
||||
|
|
@ -277,10 +284,10 @@ func TestMonitor_CheckInbox_Good_UnreadMessages(t *testing.T) {
|
|||
msg := mon.checkInbox()
|
||||
assert.Contains(t, msg, "2 unread message(s) in inbox")
|
||||
|
||||
// 3 new messages (ids 1,2,3 all > prevMaxID=0), but only 2 unread
|
||||
// Monitor sends one ChannelPush per NEW message (higher ID than last seen)
|
||||
require.Len(t, captured, 3)
|
||||
data := captured[0].Data.(map[string]any)
|
||||
// 3 new messages (ids 1,2,3 all > prevMaxID=0), but only 2 unread.
|
||||
// Monitor sends one channel notification per new message.
|
||||
require.Len(t, notifier.events, 3)
|
||||
data := notifier.events[0].Data.(map[string]any)
|
||||
assert.Equal(t, "clotho", data["from"])
|
||||
}
|
||||
|
||||
|
|
@ -376,15 +383,10 @@ func TestMonitor_CheckInbox_Good_MultipleSameSender(t *testing.T) {
|
|||
|
||||
setupAPIEnv(t, srv.URL)
|
||||
|
||||
// Create Core with IPC handler to capture ChannelPush
|
||||
var captured []coremcp.ChannelPush
|
||||
// Create Core with an MCP notifier to capture channel events.
|
||||
c := core.New()
|
||||
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
|
||||
if ev, ok := msg.(coremcp.ChannelPush); ok {
|
||||
captured = append(captured, ev)
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
})
|
||||
notifier := &captureNotifier{}
|
||||
require.True(t, c.RegisterService("mcp", notifier).OK)
|
||||
|
||||
mon := New()
|
||||
mon.ServiceRuntime = core.NewServiceRuntime(c, MonitorOptions{})
|
||||
|
|
@ -393,7 +395,7 @@ func TestMonitor_CheckInbox_Good_MultipleSameSender(t *testing.T) {
|
|||
msg := mon.checkInbox()
|
||||
assert.Contains(t, msg, "3 unread message(s)")
|
||||
|
||||
require.True(t, len(captured) > 0, "should capture at least one ChannelPush")
|
||||
require.True(t, len(notifier.events) > 0, "should capture at least one channel event")
|
||||
}
|
||||
|
||||
// --- check (integration of sub-checks) ---
|
||||
|
|
@ -808,19 +810,19 @@ func TestMonitor_HarvestCompleted_Good_MultipleWorkspaces(t *testing.T) {
|
|||
run(t, sourceDir, "git", "commit", "-m", "init")
|
||||
|
||||
fs.EnsureDir(wsDir)
|
||||
run(t, wsDir, "git", "clone", sourceDir, "src")
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
run(t, srcDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(srcDir, "new.go"), "package main\n")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "agent work")
|
||||
run(t, wsDir, "git", "clone", sourceDir, "repo")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
run(t, repoDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(repoDir, "new.go"), "package main\n")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "agent work")
|
||||
|
||||
writeStatus(t, wsDir, "completed", fmt.Sprintf("repo-%d", i), "agent/test-task")
|
||||
}
|
||||
|
||||
// Create Core with IPC handler to capture HarvestComplete messages
|
||||
var harvests []messages.HarvestComplete
|
||||
c := core.New(core.WithService(process.Register))
|
||||
c := core.New(core.WithService(agentic.ProcessRegister))
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
|
||||
if ev, ok := msg.(messages.HarvestComplete); ok {
|
||||
|
|
@ -865,23 +867,23 @@ func TestMonitor_HarvestCompleted_Good_RejectedWorkspace(t *testing.T) {
|
|||
|
||||
wsDir := core.JoinPath(wsRoot, "workspace", "ws-rej")
|
||||
fs.EnsureDir(wsDir)
|
||||
run(t, wsDir, "git", "clone", sourceDir, "src")
|
||||
srcDir := core.JoinPath(wsDir, "src")
|
||||
run(t, srcDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(srcDir, "new.go"), "package main\n")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "agent work")
|
||||
run(t, wsDir, "git", "clone", sourceDir, "repo")
|
||||
repoDir := core.JoinPath(wsDir, "repo")
|
||||
run(t, repoDir, "git", "checkout", "-b", "agent/test-task")
|
||||
fs.Write(core.JoinPath(repoDir, "new.go"), "package main\n")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "agent work")
|
||||
|
||||
// Add binary to trigger rejection
|
||||
fs.Write(core.JoinPath(srcDir, "app.exe"), "binary")
|
||||
run(t, srcDir, "git", "add", ".")
|
||||
run(t, srcDir, "git", "commit", "-m", "add binary")
|
||||
fs.Write(core.JoinPath(repoDir, "app.exe"), "binary")
|
||||
run(t, repoDir, "git", "add", ".")
|
||||
run(t, repoDir, "git", "commit", "-m", "add binary")
|
||||
|
||||
writeStatus(t, wsDir, "completed", "rej-repo", "agent/test-task")
|
||||
|
||||
// Create Core with IPC handler to capture HarvestRejected messages
|
||||
var rejections []messages.HarvestRejected
|
||||
c := core.New(core.WithService(process.Register))
|
||||
c := core.New(core.WithService(agentic.ProcessRegister))
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
|
||||
if ev, ok := msg.(messages.HarvestRejected); ok {
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/process"
|
||||
)
|
||||
|
||||
var testMon *Subsystem
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
c := core.New(core.WithService(process.Register))
|
||||
c := core.New(core.WithService(agentic.ProcessRegister))
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
testMon = New()
|
||||
testMon.ServiceRuntime = core.NewServiceRuntime(c, MonitorOptions{})
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package runner
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
|
|
@ -65,10 +66,10 @@ func (c *ConcurrencyLimit) UnmarshalYAML(value *yaml.Node) error {
|
|||
|
||||
// AgentsConfig is the root of agents.yaml.
|
||||
type AgentsConfig struct {
|
||||
Version int `yaml:"version"`
|
||||
Dispatch DispatchConfig `yaml:"dispatch"`
|
||||
Concurrency map[string]ConcurrencyLimit `yaml:"concurrency"`
|
||||
Rates map[string]RateConfig `yaml:"rates"`
|
||||
Version int `yaml:"version"`
|
||||
Dispatch DispatchConfig `yaml:"dispatch"`
|
||||
Concurrency map[string]ConcurrencyLimit `yaml:"concurrency"`
|
||||
Rates map[string]RateConfig `yaml:"rates"`
|
||||
}
|
||||
|
||||
// loadAgentsConfig reads agents.yaml from known paths.
|
||||
|
|
@ -147,7 +148,13 @@ func (s *Service) canDispatchAgent(agent string) (bool, string) {
|
|||
func (s *Service) countRunningByAgent(agent string) int {
|
||||
count := 0
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Status == "running" && baseAgent(st.Agent) == agent {
|
||||
if st.Status != "running" || baseAgent(st.Agent) != agent {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case st.PID < 0:
|
||||
count++
|
||||
case st.PID > 0 && syscall.Kill(st.PID, 0) == nil:
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
|
@ -158,7 +165,13 @@ func (s *Service) countRunningByAgent(agent string) int {
|
|||
func (s *Service) countRunningByModel(agent string) int {
|
||||
count := 0
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Status == "running" && st.Agent == agent {
|
||||
if st.Status != "running" || st.Agent != agent {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case st.PID < 0:
|
||||
count++
|
||||
case st.PID > 0 && syscall.Kill(st.PID, 0) == nil:
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -97,12 +97,14 @@ func TestQueue_ModelVariant_Ugly_Empty(t *testing.T) {
|
|||
|
||||
func TestQueue_CanDispatchAgent_Good_NoConfig(t *testing.T) {
|
||||
svc := New()
|
||||
assert.True(t, svc.canDispatchAgent("codex"), "no config → unlimited")
|
||||
can, _ := svc.canDispatchAgent("codex")
|
||||
assert.True(t, can, "no config → unlimited")
|
||||
}
|
||||
|
||||
func TestQueue_CanDispatchAgent_Good_UnknownAgent(t *testing.T) {
|
||||
svc := New()
|
||||
assert.True(t, svc.canDispatchAgent("unknown-agent"))
|
||||
can, _ := svc.canDispatchAgent("unknown-agent")
|
||||
assert.True(t, can)
|
||||
}
|
||||
|
||||
func TestQueue_CanDispatchAgent_Bad_AtLimit(t *testing.T) {
|
||||
|
|
@ -114,13 +116,15 @@ func TestQueue_CanDispatchAgent_Bad_AtLimit(t *testing.T) {
|
|||
})
|
||||
}
|
||||
// Since PIDs aren't alive, count should be 0
|
||||
assert.True(t, svc.canDispatchAgent("codex"), "dead PIDs don't count")
|
||||
can, _ := svc.canDispatchAgent("codex")
|
||||
assert.True(t, can, "dead PIDs don't count")
|
||||
}
|
||||
|
||||
func TestQueue_CanDispatchAgent_Ugly_ZeroLimit(t *testing.T) {
|
||||
svc := New()
|
||||
// Zero total means unlimited
|
||||
assert.True(t, svc.canDispatchAgent("codex"))
|
||||
can, _ := svc.canDispatchAgent("codex")
|
||||
assert.True(t, can)
|
||||
}
|
||||
|
||||
// --- countRunningByAgent ---
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
coremcp "dappco.re/go/mcp/pkg/mcp"
|
||||
)
|
||||
|
||||
// Options configures the runner service.
|
||||
|
|
@ -38,6 +37,10 @@ type Service struct {
|
|||
workspaces *core.Registry[*WorkspaceStatus]
|
||||
}
|
||||
|
||||
type channelSender interface {
|
||||
ChannelSend(ctx context.Context, channel string, data any)
|
||||
}
|
||||
|
||||
// New creates a runner service.
|
||||
//
|
||||
// svc := runner.New()
|
||||
|
|
@ -140,26 +143,35 @@ func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) core.Result {
|
|||
}
|
||||
}
|
||||
}
|
||||
c.ACTION(coremcp.ChannelPush{
|
||||
Channel: "agent.status",
|
||||
Data: &AgentNotification{
|
||||
Status: "started",
|
||||
Repo: ev.Repo,
|
||||
Agent: ev.Agent,
|
||||
Workspace: ev.Workspace,
|
||||
Running: running,
|
||||
Limit: limit,
|
||||
},
|
||||
})
|
||||
notification := &AgentNotification{
|
||||
Status: "started",
|
||||
Repo: ev.Repo,
|
||||
Agent: ev.Agent,
|
||||
Workspace: ev.Workspace,
|
||||
Running: running,
|
||||
Limit: limit,
|
||||
}
|
||||
if notifier, ok := core.ServiceFor[channelSender](c, "mcp"); ok {
|
||||
notifier.ChannelSend(context.Background(), "agent.status", notification)
|
||||
}
|
||||
|
||||
case messages.AgentCompleted:
|
||||
// Update workspace status in Registry so concurrency count drops
|
||||
s.workspaces.Each(func(name string, st *WorkspaceStatus) {
|
||||
if st.Repo == ev.Repo && st.Status == "running" {
|
||||
st.Status = ev.Status
|
||||
st.PID = 0
|
||||
if ev.Workspace != "" {
|
||||
if r := s.workspaces.Get(ev.Workspace); r.OK {
|
||||
if st, ok := r.Value.(*WorkspaceStatus); ok && st.Status == "running" {
|
||||
st.Status = ev.Status
|
||||
st.PID = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Repo == ev.Repo && st.Status == "running" {
|
||||
st.Status = ev.Status
|
||||
st.PID = 0
|
||||
}
|
||||
})
|
||||
}
|
||||
cBase := baseAgent(ev.Agent)
|
||||
cRunning := s.countRunningByAgent(cBase)
|
||||
var cLimit int
|
||||
|
|
@ -171,17 +183,17 @@ func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) core.Result {
|
|||
}
|
||||
}
|
||||
}
|
||||
c.ACTION(coremcp.ChannelPush{
|
||||
Channel: "agent.status",
|
||||
Data: &AgentNotification{
|
||||
Status: ev.Status,
|
||||
Repo: ev.Repo,
|
||||
Agent: ev.Agent,
|
||||
Workspace: ev.Workspace,
|
||||
Running: cRunning,
|
||||
Limit: cLimit,
|
||||
},
|
||||
})
|
||||
notification := &AgentNotification{
|
||||
Status: ev.Status,
|
||||
Repo: ev.Repo,
|
||||
Agent: ev.Agent,
|
||||
Workspace: ev.Workspace,
|
||||
Running: cRunning,
|
||||
Limit: cLimit,
|
||||
}
|
||||
if notifier, ok := core.ServiceFor[channelSender](c, "mcp"); ok {
|
||||
notifier.ChannelSend(context.Background(), "agent.status", notification)
|
||||
}
|
||||
s.Poke()
|
||||
|
||||
case messages.PokeQueue:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"dappco.re/go/agent/pkg/messages"
|
||||
core "dappco.re/go/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -304,6 +305,30 @@ func TestRunner_HandleIPCEvents_Good_UnknownMessage(t *testing.T) {
|
|||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestRunner_HandleIPCEvents_Good_UpdatesMatchingWorkspaceOnly(t *testing.T) {
|
||||
c := core.New(core.WithOption("name", "test"))
|
||||
svc := New()
|
||||
svc.ServiceRuntime = core.NewServiceRuntime(c, Options{})
|
||||
svc.TrackWorkspace("core/go-io/task-1", &WorkspaceStatus{
|
||||
Status: "running", Agent: "codex", Repo: "go-io", PID: 111,
|
||||
})
|
||||
svc.TrackWorkspace("core/go-io/task-2", &WorkspaceStatus{
|
||||
Status: "running", Agent: "codex", Repo: "go-io", PID: 222,
|
||||
})
|
||||
|
||||
r := svc.HandleIPCEvents(c, messages.AgentCompleted{
|
||||
Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-1", Status: "completed",
|
||||
})
|
||||
assert.True(t, r.OK)
|
||||
|
||||
first := svc.workspaces.Get("core/go-io/task-1").Value.(*WorkspaceStatus)
|
||||
second := svc.workspaces.Get("core/go-io/task-2").Value.(*WorkspaceStatus)
|
||||
assert.Equal(t, "completed", first.Status)
|
||||
assert.Equal(t, 0, first.PID)
|
||||
assert.Equal(t, "running", second.Status)
|
||||
assert.Equal(t, 222, second.PID)
|
||||
}
|
||||
|
||||
// --- WriteStatus / ReadStatus ---
|
||||
|
||||
func TestRunner_WriteReadStatus_Good(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue