feat: wire service provider framework into IDE
Some checks failed
Security Scan / security (push) Successful in 11s
Test / test (push) Failing after 53s

Adds provider registry and API engine to all three modes (MCP-only,
headless, GUI). Registers process and brain providers, starts the API
server on :9880 (configurable via CORE_API_ADDR), and runs the WS hub
for real-time event streaming. go-api and go-process promoted to direct
dependencies.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-14 09:47:59 +00:00
parent 44f49c97cb
commit 4375919ca3
3 changed files with 59 additions and 10 deletions

9
go.mod
View file

@ -4,7 +4,9 @@ go 1.26.0
require (
forge.lthn.ai/core/go v0.2.2
forge.lthn.ai/core/go-api v0.1.0
forge.lthn.ai/core/go-config v0.1.2
forge.lthn.ai/core/go-process v0.1.0
forge.lthn.ai/core/go-ws v0.1.3
forge.lthn.ai/core/gui v0.0.0
forge.lthn.ai/core/mcp v0.0.0
@ -14,13 +16,11 @@ require (
require (
dario.cat/mergo v1.0.2 // indirect
forge.lthn.ai/core/go-ai v0.1.5 // indirect
forge.lthn.ai/core/go-api v0.1.0 // indirect
forge.lthn.ai/core/go-inference v0.1.0 // indirect
forge.lthn.ai/core/go-io v0.0.5 // indirect
forge.lthn.ai/core/go-log v0.0.1 // indirect
forge.lthn.ai/core/go-ml v0.1.0 // indirect
forge.lthn.ai/core/go-mlx v0.1.0 // indirect
forge.lthn.ai/core/go-process v0.1.0 // indirect
forge.lthn.ai/core/go-rag v0.1.0 // indirect
forge.lthn.ai/core/go-webview v0.1.2 // indirect
github.com/99designs/gqlgen v0.17.87 // indirect
@ -64,7 +64,7 @@ require (
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-contrib/static v1.1.5 // indirect
github.com/gin-contrib/timeout v1.1.0 // indirect
github.com/gin-gonic/gin v1.11.0 // indirect
github.com/gin-gonic/gin v1.12.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.7.0 // indirect
github.com/go-git/go-git/v5 v5.16.4 // indirect
@ -152,6 +152,7 @@ require (
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
@ -164,7 +165,7 @@ require (
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect

7
go.sum
View file

@ -145,8 +145,7 @@ github.com/gin-contrib/static v1.1.5 h1:bAPqT4KTZN+4uDY1b90eSrD1t8iNzod7Jj8njwmn
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.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@ -398,6 +397,7 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
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/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 h1:LSJsvNqhj2sBNFb5NWHbyDK4QJ/skQ2ydjeOZ9OYNZ4=
@ -442,8 +442,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

53
main.go
View file

@ -5,12 +5,17 @@ import (
"embed"
"io/fs"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"syscall"
"forge.lthn.ai/core/go-api"
"forge.lthn.ai/core/go-api/pkg/provider"
"forge.lthn.ai/core/go-config"
process "forge.lthn.ai/core/go-process"
processapi "forge.lthn.ai/core/go-process/pkg/api"
"forge.lthn.ai/core/go-ws"
"forge.lthn.ai/core/go/pkg/core"
guiMCP "forge.lthn.ai/core/gui/pkg/mcp"
@ -55,6 +60,24 @@ func main() {
}
bridge := ide.NewBridge(hub, bridgeCfg)
// ── Service Provider Registry ──────────────────────────────
reg := provider.NewRegistry()
reg.Add(processapi.NewProvider(process.DefaultRegistry(), hub))
reg.Add(brain.NewProvider(bridge, hub))
// ── API Engine ─────────────────────────────────────────────
apiAddr := ":9880"
if addr := os.Getenv("CORE_API_ADDR"); addr != "" {
apiAddr = addr
}
engine, _ := api.New(
api.WithAddr(apiAddr),
api.WithCORS("*"),
api.WithWSHandler(http.Handler(hub.Handler())),
api.WithSwagger("Core IDE", "Service Provider API", "0.1.0"),
)
reg.MountAll(engine)
// ── Core framework ─────────────────────────────────────────
c, err := core.New(
core.WithName("ws", func(c *core.Core) (any, error) {
@ -87,11 +110,18 @@ func main() {
syscall.SIGINT, syscall.SIGTERM)
defer cancel()
// Start Core lifecycle manually
if err := c.ServiceStartup(ctx, nil); err != nil {
log.Fatalf("core startup failed: %v", err)
}
bridge.Start(ctx)
go hub.Run(ctx)
// Start API server in background for provider endpoints
go func() {
if err := engine.Serve(ctx); err != nil {
log.Printf("API server error: %v", err)
}
}()
if err := mcpSvc.ServeStdio(ctx); err != nil {
log.Printf("MCP stdio error: %v", err)
@ -112,6 +142,15 @@ func main() {
log.Fatalf("core startup failed: %v", err)
}
bridge.Start(ctx)
go hub.Run(ctx)
// Start API server
go func() {
log.Printf("API server listening on %s", apiAddr)
if err := engine.Serve(ctx); err != nil {
log.Printf("API server error: %v", err)
}
}()
go func() {
if err := mcpSvc.Run(ctx); err != nil {
@ -182,10 +221,20 @@ func main() {
})
systray.SetMenu(trayMenu)
// Start MCP transport alongside Wails
// Start MCP transport and API server alongside Wails
go func() {
ctx := context.Background()
bridge.Start(ctx)
go hub.Run(ctx)
// Start API server
go func() {
log.Printf("API server listening on %s", apiAddr)
if err := engine.Serve(ctx); err != nil {
log.Printf("API server error: %v", err)
}
}()
if err := mcpSvc.Run(ctx); err != nil {
log.Printf("MCP error: %v", err)
}