diff --git a/go/go.mod b/go/go.mod index 32af162d..780e711e 100644 --- a/go/go.mod +++ b/go/go.mod @@ -6,7 +6,7 @@ require ( dappco.re/go v0.12.0 dappco.re/go/api v0.19.0 dappco.re/go/cli v0.13.0 - dappco.re/go/container v0.12.0 + dappco.re/go/container v0.13.0 dappco.re/go/io v0.15.3 dappco.re/go/mcp v0.19.1 dappco.re/go/orm v0.1.2 @@ -63,7 +63,7 @@ require ( github.com/duckdb/duckdb-go-bindings/lib/linux-arm64 v0.10505.0 // indirect github.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.10505.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/ebitengine/purego v0.10.1 // indirect + github.com/ebitengine/purego v0.10.2 // indirect github.com/gabriel-vasile/mimetype v1.4.15 // indirect github.com/gin-contrib/authz v1.0.7 // indirect github.com/gin-contrib/cors v1.7.7 // indirect @@ -126,7 +126,7 @@ require ( github.com/swaggo/files v1.0.1 // indirect github.com/swaggo/gin-swagger v1.6.1 // indirect github.com/swaggo/swag v1.16.6 // indirect - github.com/tmc/apple v0.6.12 // indirect + github.com/tmc/apple v0.6.15 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect github.com/vektah/gqlparser/v2 v2.5.36 // indirect diff --git a/go/go.sum b/go/go.sum index 82234745..ec6550ff 100644 --- a/go/go.sum +++ b/go/go.sum @@ -4,8 +4,8 @@ dappco.re/go/api v0.19.0 h1:vhkSRaUpDEz7Jm8tWbwWHvz1f4+TiO82JK8Pytv/sEk= dappco.re/go/api v0.19.0/go.mod h1:Q0IEARpCTgQjOU6eGZTG2jq0WLI4GJ+eCt+Ys3n/hxg= dappco.re/go/cli v0.13.0 h1:vDp6eA3ojR6VrPPLn9aE/P4gvB4zABZ4flATsx/QsAs= dappco.re/go/cli v0.13.0/go.mod h1:0T0UcDGQIr1wIAcUs87oGlFD32aAl45bo834MHy47K8= -dappco.re/go/container v0.12.0 h1:MZRDoohrwEOtJBJmjUNrf3XvgSvA8Ytfgpn0F/Ves2s= -dappco.re/go/container v0.12.0/go.mod h1:2kFV8kWjcZXefWHjnIHBNXxM28nfpb7yIfJACJ2/hxs= +dappco.re/go/container v0.13.0 h1:TBFjJk+1btm7duvYS7n9wLxQIjVokAFnkh1QWhBfljQ= +dappco.re/go/container v0.13.0/go.mod h1:aR0YoWs2IBt8yePouttXXa6oHutHvpGqY7nJMOpYT/Q= dappco.re/go/i18n v0.12.1 h1:Pm1DF9I0O8bckTr815OrnR+G+7EvbQhP2Vk4ItC0mB4= dappco.re/go/i18n v0.12.1/go.mod h1:AYx1QkPfW+qvzLCfJnuOlc4J+grwlgFzoHnoke4TAYg= dappco.re/go/io v0.15.3 h1:cG+pHjzi1jja99rPRiTG3DYFxQ9XUognHr8rNrPofGg= @@ -32,8 +32,8 @@ github.com/99designs/gqlgen v0.17.94 h1:+3EUDVgX/8gDyDL+7NUqCo4cy2ylylwW0GvR1dGi github.com/99designs/gqlgen v0.17.94/go.mod h1:o+XaAMpPA/AX4rqeiK03tZUb/5T+WCgpRDD4aujgdas= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Snider/Poindexter v0.1.0 h1:lZ94xfjiFcBK2zaEQWhyW8HKZ2KS9xTRiVfVCERj9w8= github.com/Snider/Poindexter v0.1.0/go.mod h1:nhgkbg4zWA4AS2Ga3RmcvdsyiI9TdxvSqe5EVBSb3Hk= @@ -98,8 +98,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= -github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/cloudflare/circl v1.6.4 h1:pOXuDTCEYyzydgUpQ0CQz3LsinKjiSk6nNP5Lt5K64U= +github.com/cloudflare/circl v1.6.4/go.mod h1:YxarevkLlbaHuWsxG6vmYNWBEsSp4pnp7j+4VljMavY= github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= @@ -128,8 +128,8 @@ github.com/duckdb/duckdb-go/v2 v2.10505.0 h1:SWwvLn2Qx/RQSnQNupwgIF8VbnJ5A6OQU9l github.com/duckdb/duckdb-go/v2 v2.10505.0/go.mod h1:m0PW4J4FG9hlFlVdXi6Ds9owpyIDaBdE2jyce00fGcE= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= -github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE= +github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI= github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ= github.com/gin-contrib/authz v1.0.7 h1:sAKv+rt6U3NwR20MSbzwQYcFGEeUD5I4oC+/sdUsq+Q= @@ -326,8 +326,8 @@ github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs 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/tmc/apple v0.6.12 h1:9GPwKjDy2Ngoyq9+6IzGouL6fjgDXc0w6kMF2cRQK9k= -github.com/tmc/apple v0.6.12/go.mod h1:QJV85Y1rc4sDQtuBobmYe4mIz5akPLzm3caYmVEmdvE= +github.com/tmc/apple v0.6.15 h1:tKPf5WPbWTPjx7zD/Gy05DezkDuQGfo26jOxEvhLZDg= +github.com/tmc/apple v0.6.15/go.mod h1:QJV85Y1rc4sDQtuBobmYe4mIz5akPLzm3caYmVEmdvE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= diff --git a/go/pkg/agentic/dispatch_vz.go b/go/pkg/agentic/dispatch_vz.go index a406d4c2..44e2d060 100644 --- a/go/pkg/agentic/dispatch_vz.go +++ b/go/pkg/agentic/dispatch_vz.go @@ -88,10 +88,9 @@ type vzDispatcher interface { // Run boots a guest image and returns the running *container.Container. Run(image *container.Image, opts ...container.RunOption) core.Result // ExecResult runs a command in the guest over vsock and returns its full - // outcome — Value is a container.ExecResult{Stdout, Stderr, Exit}, which - // vzDecodeExec normalises into vzExec. A command that ran and exited - // non-zero is OK at the verb level (the exit code is preserved); only - // verb-level failures Fail. + // outcome — Value is a container.ExecResult{Stdout, Stderr, Exit}. A command + // that ran and exited non-zero is OK at the verb level (the exit code is + // preserved); only verb-level failures Fail. ExecResult(id, command string, args ...string) core.Result // Stop gracefully stops a running guest. Stop(id string) core.Result @@ -101,15 +100,6 @@ type vzDispatcher interface { // inject a fake; production returns the concrete in-process provider. var newVZProvider = func() vzDispatcher { return newVZProviderImpl() } -// vzExec is the platform-neutral form of an exec outcome. go-container declares -// ExecResult inside its darwin-tagged vz.go, so naming that type here would -// stop the package building on Linux; vzDecodeExec converts into this instead. -type vzExec struct { - Stdout string - Stderr string - Exit int -} - // vzResolveExec runs the go-build image resolver and returns its core.Result — // Value is the captured stdout string on success. It is a package var (a seam) // so unit tests inject a scripted Result instead of shelling out. Production @@ -277,7 +267,7 @@ func (v *vzCompletionProcess) run(provider vzDispatcher) { v.finish(vzExitFailed, process.StatusFailed, vzResultMessage(execResult)) return } - result, ok := vzDecodeExec(execResult) + result, ok := execResult.Value.(container.ExecResult) if !ok { v.finish(vzExitFailed, process.StatusFailed, "vz exec returned unexpected result type") return @@ -295,7 +285,7 @@ func (v *vzCompletionProcess) run(provider vzDispatcher) { // the completionProcess/monitor contract carries. stdout is the agent's // captured output; stderr is appended (labelled) only when present so a failed // run surfaces why without masking the stdout of a successful one. -func vzExecOutput(result vzExec) string { +func vzExecOutput(result container.ExecResult) string { if result.Stderr == "" { return result.Stdout } diff --git a/go/pkg/agentic/dispatch_vz_test.go b/go/pkg/agentic/dispatch_vz_test.go index f34f96ee..ff75bfed 100644 --- a/go/pkg/agentic/dispatch_vz_test.go +++ b/go/pkg/agentic/dispatch_vz_test.go @@ -153,7 +153,7 @@ func TestDispatchVZ_CompletionProcess_Good_ExecStop(t *testing.T) { // drives only the structured ExecResult→Stop tail. fake := &fakeVZDispatcher{ available: true, - execResult: core.Ok(vzExec{Stdout: "agent stdout", Exit: 0}), + execResult: core.Ok(container.ExecResult{Stdout: "agent stdout", Exit: 0}), stopResult: core.Ok(nil), } proc := &vzCompletionProcess{ @@ -209,7 +209,7 @@ func TestDispatchVZ_CompletionProcess_Ugly_NonZeroExitPreserved(t *testing.T) { // vzExitFailed) and fold stderr into the output for the monitor. fake := &fakeVZDispatcher{ available: true, - execResult: core.Ok(vzExec{Stdout: "partial", Stderr: "boom", Exit: 2}), + execResult: core.Ok(container.ExecResult{Stdout: "partial", Stderr: "boom", Exit: 2}), stopResult: core.Ok(nil), } proc := &vzCompletionProcess{id: "vz-test", containerID: "vzfake01", command: "false", startedAt: time.Now(), done: make(chan struct{})} @@ -242,7 +242,7 @@ func TestDispatchVZ_CompletionDrivesOnAgentComplete_Good_Case(t *testing.T) { // A real vzCompletionProcess driven by a fake provider — proving the adapter // satisfies completionProcess AND that the existing monitor consumes it. - fake := &fakeVZDispatcher{available: true, execResult: core.Ok(vzExec{Stdout: "vz output", Exit: 0}), stopResult: core.Ok(nil)} + fake := &fakeVZDispatcher{available: true, execResult: core.Ok(container.ExecResult{Stdout: "vz output", Exit: 0}), stopResult: core.Ok(nil)} proc := &vzCompletionProcess{id: "vz-ws", containerID: "vzfake01", command: "true", startedAt: time.Now(), done: make(chan struct{})} proc.run(fake) diff --git a/go/pkg/agentic/vz_platform_darwin.go b/go/pkg/agentic/vz_platform_darwin.go index 2402dfd2..03cf3696 100644 --- a/go/pkg/agentic/vz_platform_darwin.go +++ b/go/pkg/agentic/vz_platform_darwin.go @@ -5,16 +5,13 @@ package agentic import ( - core "dappco.re/go" "dappco.re/go/container" ) -// Everything go-container gates behind //go:build darwin is reached through -// this file, so the rest of pkg/agentic compiles on every platform. vz.go in -// dappco.re/go/container carries that constraint, which puts IsVZAvailable, -// NewVZProvider and ExecResult out of reach of a Linux build — the package -// referenced all three unguarded and so failed to build in CI while building -// fine on a developer's Mac. +// The darwin half of the VZ seam. Virtualization.framework is Apple-only, so +// go-container declares IsVZAvailable and NewVZProvider behind //go:build +// darwin in vz.go; reaching them through this file is what keeps the rest of +// pkg/agentic compiling on every platform. // vzHostAvailable reports whether Virtualization.framework is usable here. // @@ -25,24 +22,3 @@ func vzHostAvailable() bool { return container.IsVZAvailable() } // // provider := newVZProviderImpl() func newVZProviderImpl() vzDispatcher { return container.NewVZProvider() } - -// vzDecodeExec normalises an ExecResult verb payload into the platform-neutral -// vzExec the fork works with. -// -// container.ExecResult is checked second so an injected vzExec still decodes: -// the unit tests script the fake dispatcher with vzExec directly rather than -// with a type they cannot name on Linux. -// -// exec, ok := vzDecodeExec(provider.ExecResult(id, "sh", "-c", "true")) -func vzDecodeExec(result core.Result) (vzExec, bool) { - if exec, ok := result.Value.(vzExec); ok { - return exec, true - } - - native, ok := result.Value.(container.ExecResult) - if !ok { - return vzExec{}, false - } - - return vzExec{Stdout: native.Stdout, Stderr: native.Stderr, Exit: native.Exit}, true -} diff --git a/go/pkg/agentic/vz_platform_other.go b/go/pkg/agentic/vz_platform_other.go index d6223148..a66990a8 100644 --- a/go/pkg/agentic/vz_platform_other.go +++ b/go/pkg/agentic/vz_platform_other.go @@ -25,20 +25,6 @@ func vzHostAvailable() bool { return false } // provider := newVZProviderImpl() func newVZProviderImpl() vzDispatcher { return vzUnsupportedProvider{} } -// vzDecodeExec normalises an ExecResult verb payload into vzExec. -// -// Only the injected form is decodable off darwin: container.ExecResult is -// declared inside go-container's darwin-tagged vz.go and cannot be named here. -// Production never reaches this — vzUnsupportedProvider fails before exec — but -// the unit tests script the fake dispatcher with vzExec and do run on Linux. -// -// exec, ok := vzDecodeExec(core.Ok(vzExec{Stdout: "hi"})) -func vzDecodeExec(result core.Result) (vzExec, bool) { - exec, ok := result.Value.(vzExec) - - return exec, ok -} - // vzUnsupportedProvider satisfies vzDispatcher on hosts with no // Virtualization.framework, failing every verb rather than pretending. type vzUnsupportedProvider struct{}