diff --git a/CLAUDE.md b/CLAUDE.md index 02ba992..0a2eacb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,37 +32,66 @@ cd frontend && npm run test ## Architecture -**Dual-mode application**: a single Go binary that operates as either a GUI desktop app (Wails 3 + Angular) or a headless CI/CD daemon, determined at startup by the `--headless` flag or display availability. +**Thin Wails shell** wiring ecosystem packages via `core.Core` dependency injection. Three operating modes: ### GUI Mode (default) -`main()` → Wails application with embedded Angular frontend, system tray (macOS: accessory app, no Dock icon), and an MCP HTTP server on **port 9877**. The `MCPBridge` service orchestrates `WebviewService` (window/DOM automation), `BrainService` (vector knowledge store via OpenBrain), and a WebSocket hub. +`main()` → Wails 3 application with embedded Angular frontend, system tray (macOS: accessory app, no Dock icon). Core framework manages all services: +- **display** (`core/gui`) — window management, webview automation, 74 MCP tools across 14 categories +- **MCP** (`core/mcp`) — Model Context Protocol server with file ops, brain subsystem, GUI subsystem +- **IDE bridge** (`core/mcp/pkg/mcp/ide`) — WebSocket bridge to Laravel core-agentic backend +- **WS hub** (`core/go-ws`) — WebSocket hub for Angular frontend communication -### Headless Mode (`--headless` or no display) -`startHeadless()` → Daemon with PID file at `~/.core/core-ide.pid`, health check on **port 9878**, and a minimal MCP server on **port 9877**. Polls Forgejo repos (configured via `CORE_REPOS` env var) every 60 seconds and runs jobs through an ordered handler pipeline: PublishDraft → SendFix → DismissReviews → EnableAutoMerge → TickParent → Dispatch. +### MCP Mode (`--mcp`) +`core-ide --mcp` → stdio MCP server for Claude Code integration. No GUI, no HTTP. Configure in `.claude/.mcp.json`: +```json +{ + "mcpServers": { + "core-ide": { + "type": "stdio", + "command": "core-ide", + "args": ["--mcp"] + } + } +} +``` -### Key Services -- **`MCPBridge`** (`mcp_bridge.go`) — Central GUI service. Registers 29 webview tools + 4 brain tools via HTTP MCP protocol. Routes: `/health`, `/mcp`, `/mcp/tools`, `/mcp/call`, `/ws`. -- **`WebviewService`** (`webview_svc.go`) — Wails v3 window management and DOM interaction. Many tools are stubbed pending CDP integration. -- **`BrainService`** (`brain_mcp.go`) — Wraps `lifecycle.Client` for OpenBrain vector store (remember/recall/forget). Shared by both modes. -- **`ClaudeBridge`** (`claude_bridge.go`) — WebSocket relay to upstream MCP at `ws://localhost:9876/ws`. Currently disabled. +### Headless Mode (no display or `gui.enabled: false`) +Core framework runs all services without Wails. MCP transport determined by `MCP_ADDR` env var (TCP if set, stdio otherwise). ### Frontend -Angular 20+ app embedded via `//go:embed`. Two routes: `/tray` (system tray panel, 380x480 frameless) and `/ide` (full IDE layout). Wails bindings auto-generated in `frontend/bindings/`. +Angular 20+ app embedded via `//go:embed`. Two routes: `/tray` (system tray panel, 380x480 frameless) and `/ide` (full IDE layout). + +## Configuration + +```yaml +# .core/config.yaml +gui: + enabled: true # false = no Wails, Core still runs +mcp: + transport: stdio # stdio | tcp | unix + tcp: + port: 9877 +brain: + api_url: http://localhost:8000 + api_token: "" # or CORE_API_TOKEN env var +``` ## Environment Variables | Variable | Default | Purpose | |----------|---------|---------| -| `CORE_REPOS` | `host-uk/core,host-uk/core-php,host-uk/core-tenant,host-uk/core-admin` | Repos to poll in headless mode | -| `CORE_API_URL` | `http://localhost:8000` | OpenBrain API endpoint | -| `CORE_API_TOKEN` | (required) | OpenBrain auth token | +| `CORE_API_URL` | `http://localhost:8000` | Laravel backend WebSocket URL | +| `CORE_API_TOKEN` | (empty) | Bearer token for Laravel backend auth | +| `MCP_ADDR` | (empty) | TCP address for MCP server (headless mode) | ## Workspace Dependencies -This module uses a Go workspace (`~/Code/go.work`) with `replace` directives for sibling modules. Ensure these exist alongside `ide/`: +This module uses a Go workspace (`~/Code/go.work`) with `replace` directives for sibling modules: - `../go` → `forge.lthn.ai/core/go` -- `../go-process` → `forge.lthn.ai/core/go-process` - `../gui` → `forge.lthn.ai/core/gui` +- `../mcp` → `forge.lthn.ai/core/mcp` +- `../go-config` → `forge.lthn.ai/core/go-config` +- `../go-ws` → `forge.lthn.ai/core/go-ws` ## Conventions @@ -70,5 +99,5 @@ This module uses a Go workspace (`~/Code/go.work`) with `replace` directives for - **Conventional commits**: `type(scope): description` with co-author line `Co-Authored-By: Virgil `. - **Licence**: EUPL-1.2. - All Go code is in `package main` (single-package application). -- New Wails services: create struct in a Go file, register in `main.go`'s `Services` slice, then `wails3 dev` to regenerate TS bindings. -- New MCP tools: add metadata to `handleMCPTools` and a case to `executeWebviewTool` in `mcp_bridge.go`. +- Services are registered via `core.WithService` or `core.WithName` factory functions. +- MCP subsystems implement `mcp.Subsystem` interface from `core/mcp`. diff --git a/brain_mcp.go b/brain_mcp.go deleted file mode 100644 index 0bee4c6..0000000 --- a/brain_mcp.go +++ /dev/null @@ -1,136 +0,0 @@ -package main - -import ( - "context" - "os" - - "forge.lthn.ai/core/agent/pkg/lifecycle" -) - -// BrainService wraps the lifecycle.Client for OpenBrain vector knowledge store access. -// Used by both headless and GUI MCP servers. -type BrainService struct { - client *lifecycle.Client -} - -// NewBrainService creates a BrainService from environment variables. -// CORE_API_URL defaults to http://localhost:8000 -// CORE_API_TOKEN must be set for authentication. -func NewBrainService() *BrainService { - baseURL := os.Getenv("CORE_API_URL") - if baseURL == "" { - baseURL = "http://localhost:8000" - } - token := os.Getenv("CORE_API_TOKEN") - return &BrainService{ - client: lifecycle.NewClient(baseURL, token), - } -} - -// Remember stores a memory in OpenBrain. -func (b *BrainService) Remember(ctx context.Context, content, memType, project, agentID string, tags []string) (*lifecycle.RememberResponse, error) { - return b.client.Remember(ctx, lifecycle.RememberRequest{ - Content: content, - Type: memType, - Project: project, - AgentID: agentID, - Tags: tags, - }) -} - -// Recall performs semantic search in OpenBrain. -func (b *BrainService) Recall(ctx context.Context, query string, topK int, project, memType, agentID string) (*lifecycle.RecallResponse, error) { - return b.client.Recall(ctx, lifecycle.RecallRequest{ - Query: query, - TopK: topK, - Project: project, - Type: memType, - AgentID: agentID, - }) -} - -// Forget removes a memory by ID. -func (b *BrainService) Forget(ctx context.Context, id string) error { - return b.client.Forget(ctx, id) -} - -// EnsureCollection ensures the Qdrant collection exists. -func (b *BrainService) EnsureCollection(ctx context.Context) error { - return b.client.EnsureCollection(ctx) -} - -// executeBrainTool handles brain MCP tool calls. Shared by headless and GUI servers. -func executeBrainTool(brain *BrainService, tool string, params map[string]any) map[string]any { - if brain == nil { - return map[string]any{"error": "brain service not configured"} - } - - ctx := context.Background() - - switch tool { - case "brain_remember": - content := getStringParam(params, "content") - memType := getStringParam(params, "type") - if memType == "" { - memType = "fact" - } - project := getStringParam(params, "project") - agentID := getStringParam(params, "agent_id") - var tags []string - if rawTags, ok := params["tags"].([]any); ok { - for _, t := range rawTags { - if s, ok := t.(string); ok { - tags = append(tags, s) - } - } - } - resp, err := brain.Remember(ctx, content, memType, project, agentID, tags) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"id": resp.ID, "type": resp.Type, "project": resp.Project, "created_at": resp.CreatedAt} - - case "brain_recall": - query := getStringParam(params, "query") - topK := getIntParam(params, "top_k") - if topK == 0 { - topK = 5 - } - project := getStringParam(params, "project") - memType := getStringParam(params, "type") - agentID := getStringParam(params, "agent_id") - resp, err := brain.Recall(ctx, query, topK, project, memType, agentID) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"memories": resp.Memories, "scores": resp.Scores} - - case "brain_forget": - id := getStringParam(params, "id") - err := brain.Forget(ctx, id) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "brain_ensure_collection": - err := brain.EnsureCollection(ctx) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - default: - return map[string]any{"error": "unknown brain tool", "tool": tool} - } -} - -// brainToolsList returns the tool definitions for brain MCP tools. -func brainToolsList() []map[string]string { - return []map[string]string{ - {"name": "brain_remember", "description": "Store a memory in OpenBrain (content, type, project, agent_id, tags)"}, - {"name": "brain_recall", "description": "Semantic search in OpenBrain (query, top_k, project, type, agent_id)"}, - {"name": "brain_forget", "description": "Remove a memory by ID"}, - {"name": "brain_ensure_collection", "description": "Ensure the Qdrant vector collection exists"}, - } -} diff --git a/claude_bridge.go b/claude_bridge.go deleted file mode 100644 index a5532e7..0000000 --- a/claude_bridge.go +++ /dev/null @@ -1,183 +0,0 @@ -package main - -import ( - "encoding/json" - "log" - "net/http" - "strings" - "sync" - "time" - - "github.com/gorilla/websocket" -) - -var wsUpgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - origin := r.Header.Get("Origin") - if origin == "" { - return true // Allow requests with no Origin header (same-origin) - } - host := r.Host - return origin == "http://"+host || origin == "https://"+host || - strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") - }, -} - -// ClaudeBridge forwards messages between GUI clients and the MCP core WebSocket. -type ClaudeBridge struct { - mcpConn *websocket.Conn - mcpURL string - clients map[*websocket.Conn]bool - clientsMu sync.RWMutex - broadcast chan []byte - reconnectMu sync.Mutex -} - -// NewClaudeBridge creates a new bridge to the MCP core WebSocket. -func NewClaudeBridge(mcpURL string) *ClaudeBridge { - return &ClaudeBridge{ - mcpURL: mcpURL, - clients: make(map[*websocket.Conn]bool), - broadcast: make(chan []byte, 256), - } -} - -// Start connects to the MCP WebSocket and starts the bridge. -func (cb *ClaudeBridge) Start() { - go cb.connectToMCP() - go cb.broadcastLoop() -} - -// connectToMCP establishes connection to the MCP core WebSocket. -func (cb *ClaudeBridge) connectToMCP() { - for { - cb.reconnectMu.Lock() - if cb.mcpConn != nil { - cb.mcpConn.Close() - } - - log.Printf("Claude bridge connecting to MCP at %s", cb.mcpURL) - conn, _, err := websocket.DefaultDialer.Dial(cb.mcpURL, nil) - if err != nil { - log.Printf("Claude bridge failed to connect to MCP: %v", err) - cb.reconnectMu.Unlock() - time.Sleep(5 * time.Second) - continue - } - - cb.mcpConn = conn - cb.reconnectMu.Unlock() - log.Printf("Claude bridge connected to MCP") - - // Read messages from MCP and broadcast to clients - for { - _, message, err := conn.ReadMessage() - if err != nil { - log.Printf("Claude bridge MCP read error: %v", err) - break - } - select { - case cb.broadcast <- message: - default: - log.Printf("Claude bridge: broadcast channel full, dropping message") - } - } - - // Connection lost, retry - time.Sleep(2 * time.Second) - } -} - -// broadcastLoop sends messages from MCP to all connected clients. -func (cb *ClaudeBridge) broadcastLoop() { - for message := range cb.broadcast { - var failedClients []*websocket.Conn - cb.clientsMu.RLock() - for client := range cb.clients { - err := client.WriteMessage(websocket.TextMessage, message) - if err != nil { - log.Printf("Claude bridge client write error: %v", err) - failedClients = append(failedClients, client) - } - } - cb.clientsMu.RUnlock() - - if len(failedClients) > 0 { - cb.clientsMu.Lock() - for _, client := range failedClients { - delete(cb.clients, client) - client.Close() - } - cb.clientsMu.Unlock() - } - } -} - -// HandleWebSocket handles WebSocket connections from GUI clients. -func (cb *ClaudeBridge) HandleWebSocket(w http.ResponseWriter, r *http.Request) { - conn, err := wsUpgrader.Upgrade(w, r, nil) - if err != nil { - log.Printf("Claude bridge upgrade error: %v", err) - return - } - - // Send connected message before registering to avoid concurrent writes - connMsg, _ := json.Marshal(map[string]any{ - "type": "system", - "data": "Connected to Claude bridge", - "timestamp": time.Now(), - }) - if err := conn.WriteMessage(websocket.TextMessage, connMsg); err != nil { - log.Printf("Claude bridge initial write error: %v", err) - conn.Close() - return - } - - cb.clientsMu.Lock() - cb.clients[conn] = true - cb.clientsMu.Unlock() - - defer func() { - cb.clientsMu.Lock() - delete(cb.clients, conn) - cb.clientsMu.Unlock() - conn.Close() - }() - - // Read messages from client and forward to MCP - for { - _, message, err := conn.ReadMessage() - if err != nil { - break - } - - // Parse the message to check type - var msg map[string]any - if err := json.Unmarshal(message, &msg); err != nil { - continue - } - - // Forward claude_message to MCP - if msgType, ok := msg["type"].(string); ok && msgType == "claude_message" { - cb.sendToMCP(message) - } - } -} - -// sendToMCP sends a message to the MCP WebSocket. -func (cb *ClaudeBridge) sendToMCP(message []byte) { - cb.reconnectMu.Lock() - defer cb.reconnectMu.Unlock() - - if cb.mcpConn == nil { - log.Printf("Claude bridge: MCP not connected") - return - } - - err := cb.mcpConn.WriteMessage(websocket.TextMessage, message) - if err != nil { - log.Printf("Claude bridge MCP write error: %v", err) - } -} diff --git a/go.mod b/go.mod index 4e5a900..6e91187 100644 --- a/go.mod +++ b/go.mod @@ -3,76 +3,184 @@ module forge.lthn.ai/core/ide go 1.26.0 require ( - forge.lthn.ai/core/agent v0.1.3 + forge.lthn.ai/core/go v0.2.2 forge.lthn.ai/core/go-config v0.1.2 - forge.lthn.ai/core/go-process v0.1.2 - forge.lthn.ai/core/go-scm v0.1.7 forge.lthn.ai/core/go-ws v0.1.3 - github.com/gorilla/websocket v1.5.3 + forge.lthn.ai/core/gui v0.0.0 + forge.lthn.ai/core/mcp v0.0.0 github.com/wailsapp/wails/v3 v3.0.0-alpha.74 ) require ( - codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 // indirect dario.cat/mergo v1.0.2 // indirect - forge.lthn.ai/core/go v0.2.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 - github.com/42wim/httpsig v1.2.3 // 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 + github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/adrg/xdg v0.5.3 // indirect + github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/andybalholm/brotli v1.2.0 // indirect + github.com/apache/arrow-go/v18 v18.5.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bep/debounce v1.2.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/casbin/casbin/v2 v2.135.0 // indirect + github.com/casbin/govaluate v1.10.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect github.com/coder/websocket v1.8.14 // indirect + github.com/coreos/go-oidc/v3 v3.17.0 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/ebitengine/purego v0.9.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-fed/httpsig v1.1.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gin-contrib/authz v1.0.6 // indirect + github.com/gin-contrib/cors v1.7.6 // indirect + github.com/gin-contrib/expvar v1.0.3 // indirect + github.com/gin-contrib/gzip v1.2.5 // indirect + github.com/gin-contrib/httpsign v1.0.3 // indirect + github.com/gin-contrib/location/v2 v2.0.0 // indirect + github.com/gin-contrib/pprof v1.5.3 // indirect + github.com/gin-contrib/secure v1.1.2 // indirect + github.com/gin-contrib/sessions v1.0.4 // indirect + github.com/gin-contrib/slog v1.2.0 // indirect + 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/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 + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.2 // indirect + github.com/go-openapi/spec v0.22.0 // indirect + github.com/go-openapi/swag/conv v0.25.1 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.1 // indirect + github.com/go-openapi/swag/loading v0.25.1 // indirect + github.com/go-openapi/swag/stringutils v0.25.1 // indirect + github.com/go-openapi/swag/typeutils v0.25.1 // indirect + github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/flatbuffers v25.12.19+incompatible // indirect + github.com/google/jsonschema-go v0.4.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect + github.com/gorilla/context v1.1.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.4.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect + github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/u v1.1.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lmittmann/tint v1.1.2 // indirect + github.com/mailru/easyjson v0.9.1 // indirect + github.com/marcboeker/go-duckdb v1.8.5 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modelcontextprotocol/go-sdk v1.4.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ollama/ollama v0.16.1 // indirect + github.com/parquet-go/bitpack v1.0.0 // indirect + github.com/parquet-go/jsonlite v1.4.0 // indirect + github.com/parquet-go/parquet-go v0.27.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pierrec/lz4/v4 v4.1.25 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/qdrant/go-client v1.16.2 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect github.com/redis/go-redis/v9 v9.18.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/samber/lo v1.52.0 // indirect + github.com/segmentio/asm v1.1.3 // indirect + github.com/segmentio/encoding v0.5.4 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.2 // indirect + github.com/sosodev/duration v1.3.1 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + 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/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/twpayne/go-geom v1.6.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + github.com/vektah/gqlparser/v2 v2.5.32 // indirect github.com/wailsapp/go-webview2 v1.0.23 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect 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.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 + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.23.0 // indirect 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/oauth2 v0.35.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect + golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 // indirect golang.org/x/text v0.34.0 // indirect + golang.org/x/tools v0.42.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + gonum.org/v1/gonum v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/grpc v1.79.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace ( + forge.lthn.ai/core/gui => ../gui + forge.lthn.ai/core/mcp => ../mcp +) diff --git a/go.sum b/go.sum index da5105c..1565400 100644 --- a/go.sum +++ b/go.sum @@ -1,58 +1,112 @@ -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 h1:HTCWpzyWQOHDWt3LzI6/d2jvUDsw/vgGRWm/8BTvcqI= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0/go.mod h1:ZglEEDj+qkxYUb+SQIeqGtFxQrbaMYqIOgahNKb7uxs= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -forge.lthn.ai/core/agent v0.1.3 h1:Swng0OHXPU+gOPyyjIbWxD3u/mFovQpp/UmeXCGQhmc= -forge.lthn.ai/core/agent v0.1.3/go.mod h1:FVJdDnpxqQnyJn1sAYFG76UZISw1uM8sOnti9zlpAb4= forge.lthn.ai/core/go v0.2.2 h1:JCWaFfiG+agb0f7b5DO1g+h40x6nb4UydxJ7D+oZk5k= forge.lthn.ai/core/go v0.2.2/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc= +forge.lthn.ai/core/go-ai v0.1.5 h1:iOKW5Wv4Pquc5beDw0QqaKspq3pUvqXxT8IEdCT13Go= +forge.lthn.ai/core/go-ai v0.1.5/go.mod h1:h1gcfi7l0m+Z9lSOwzcqzSeqRIR/6Qc2vqezBo74Rl0= +forge.lthn.ai/core/go-api v0.1.0 h1:j4cn/cpDkSecN2TNcksmqtFKlA9R61VSkbIIwSiujaU= +forge.lthn.ai/core/go-api v0.1.0/go.mod h1:ECZtO2R6ZYF80tBBedhmkEUw6Oqjs5jM34+XpdeZUEw= forge.lthn.ai/core/go-config v0.1.2 h1:W2Rf7PhVeugTjHZ9XbEr1yMCYY6r1r5fyUmQcM2C9xY= forge.lthn.ai/core/go-config v0.1.2/go.mod h1:6jZZ0XQaSSiWiZjiNfibMEfSPehsBGwfToh//QGhN7E= +forge.lthn.ai/core/go-inference v0.1.0 h1:pO7etYgqV8LMKFdpW8/2RWncuECZJCIcf8nnezeZ5R4= +forge.lthn.ai/core/go-inference v0.1.0/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw= forge.lthn.ai/core/go-io v0.0.5 h1:oSyngKTkB1gR5fEWYKXftTg9FxwnpddSiCq2dlwfImE= forge.lthn.ai/core/go-io v0.0.5/go.mod h1:ZlU9OQpsvNFNmTJoaHbFIkisZyc0eCq0p8znVWQLRf0= forge.lthn.ai/core/go-log v0.0.1 h1:x/E6EfF9vixzqiLHQOl2KT25HyBcMc9qiBkomqVlpPg= forge.lthn.ai/core/go-log v0.0.1/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= -forge.lthn.ai/core/go-process v0.1.2 h1:0fdLJq/DPssilN9E5yude/xHNfZRKHghIjo++b5aXgc= -forge.lthn.ai/core/go-process v0.1.2/go.mod h1:9oxVALrZaZCqFe8YDdheIS5bRUV1SBz4tVW/MflAtxM= -forge.lthn.ai/core/go-scm v0.1.7 h1:UuIqOLal4MUNS4UoWlBBOz1b/13QlcxnMnx9e5fIHWM= -forge.lthn.ai/core/go-scm v0.1.7/go.mod h1:3GSEnNWyak3ETGehOW2DEcjWscBlNF5JHu31z2hHTVM= +forge.lthn.ai/core/go-ml v0.1.0 h1:nV/XHZMy9VaFhk2dCYW5Jnp5UqpYVsYg85bsKMqdu8o= +forge.lthn.ai/core/go-ml v0.1.0/go.mod h1:FPV9JhIUOZdLeJpX1ggC15BpmM740NPg6rycnOc5vss= +forge.lthn.ai/core/go-mlx v0.1.0 h1:nMDhMma3M9iSm2ymNyqMe+aAbJDasNnxgi/1dZ+Zq7c= +forge.lthn.ai/core/go-mlx v0.1.0/go.mod h1:b4BJX67nx9QZiyREl2lmYIPJ+Yp5amZug3y7vXaRy/Y= +forge.lthn.ai/core/go-process v0.1.0 h1:lRpliQuu/Omt+kAHMFoQYOb5PKEIKg8yTMchFhejpK8= +forge.lthn.ai/core/go-process v0.1.0/go.mod h1:GGEDrgDLBlj55BkyyZTQpgbcEkAttJznxbUDBRxaAww= +forge.lthn.ai/core/go-rag v0.1.0 h1:H5umiRryuq6J6l889s0OsxWpmq5P5c3A9Bkj0cQyO7k= +forge.lthn.ai/core/go-rag v0.1.0/go.mod h1:bB8Fy98G2zxVoe7k2B85gXvim6frJdbAMnDyW4peUVU= +forge.lthn.ai/core/go-webview v0.1.2 h1:KC5AlkAg1SkmeUVsW4ZeEyrNxy+cZLaHu6SPwWLBi1A= +forge.lthn.ai/core/go-webview v0.1.2/go.mod h1:AcIN8cASb7N9c/G5VMmFMIXcPvf6Kk7HcbOQxfiylQ8= forge.lthn.ai/core/go-ws v0.1.3 h1:TzqFpEcDYcZUFFmrTznfEuVcVdnp2jsRNwAGHeTyXN0= forge.lthn.ai/core/go-ws v0.1.3/go.mod h1:iDbJuR1NT27czjtNIluxnEdLrnfsYQdEBIrsoZnpkCk= -github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= -github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= +github.com/99designs/gqlgen v0.17.87 h1:pSnCIMhBQezAE8bc1GNmfdLXFmnWtWl1GRDFEE/nHP8= +github.com/99designs/gqlgen v0.17.87/go.mod h1:fK05f1RqSNfQpd4CfW5qk/810Tqi4/56Wf6Nem0khAg= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +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.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 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/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= +github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +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/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= +github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +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 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/apache/arrow-go/v18 v18.5.1 h1:yaQ6zxMGgf9YCYw4/oaeOU3AULySDlAYDOcnr4LdHdI= +github.com/apache/arrow-go/v18 v18.5.1/go.mod h1:OCCJsmdq8AsRm8FkBSSmYTwL/s4zHW9CqxeBxEytkNE= +github.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc= +github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +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/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= +github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +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/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= 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/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= -github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= 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 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= @@ -63,10 +117,38 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= -github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= @@ -75,30 +157,103 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +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-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e h1:Lf/gRkoycfOBPa42vU2bbgPurFong6zXeFtPoxholzU= github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok= +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 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= +github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= +github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw= +github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= +github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= +github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= +github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= +github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= +github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= +github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= +github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= +github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= +github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= +github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/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/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= +github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +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/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +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/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -112,8 +267,14 @@ github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +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/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w= github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= +github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= @@ -121,10 +282,31 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP 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/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ollama/ollama v0.16.1 h1:DIxnLdS0om3hb7HheJqj6+ZnPCCMWmy/vyUxiQgRYoI= +github.com/ollama/ollama v0.16.1/go.mod h1:FEk95NbAJJZk+t7cLh+bPGTul72j1O3PLLlYNV3FVZ0= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/parquet-go/bitpack v1.0.0 h1:AUqzlKzPPXf2bCdjfj4sTeacrUwsT7NlcYDMUQxPcQA= +github.com/parquet-go/bitpack v1.0.0/go.mod h1:XnVk9TH+O40eOOmvpAVZ7K2ocQFrQwysLMnc6M/8lgs= +github.com/parquet-go/jsonlite v1.4.0 h1:RTG7prqfO0HD5egejU8MUDBN8oToMj55cgSV1I0zNW4= +github.com/parquet-go/jsonlite v1.4.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0= +github.com/parquet-go/parquet-go v0.27.0 h1:vHWK2xaHbj+v1DYps03yDRpEsdtOeKbhiXUaixoPb3g= +github.com/parquet-go/parquet-go v0.27.0/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg= 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.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= +github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -134,6 +316,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/qdrant/go-client v1.16.2 h1:UUMJJfvXTByhwhH1DwWdbkhZ2cTdvSqVkXSIfBrVWSg= +github.com/qdrant/go-client v1.16.2/go.mod h1:I+EL3h4HRoRTeHtbfOd/4kDXwCukZfkd41j/9wryGkw= +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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -145,11 +333,17 @@ github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88ee github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= +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 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -159,59 +353,148 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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/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/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= +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/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= +github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/wailsapp/go-webview2 v1.0.23 h1:jmv8qhz1lHibCc79bMM/a/FqOnnzOGEisLav+a0b9P0= github.com/wailsapp/go-webview2 v1.0.23/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/wails/v3 v3.0.0-alpha.74 h1:wRm1EiDQtxDisXk46NtpiBH90STwfKp36NrTDwOEdxw= github.com/wailsapp/wails/v3 v3.0.0-alpha.74/go.mod h1:4saK4A4K9970X+X7RkMwP2lyGbLogcUz54wVeq4C/V8= +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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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.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= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0/go.mod h1:0Q5ocj6h/+C6KYq8cnl4tDFVd4I1HBdsJ440aeagHos= +go.opentelemetry.io/contrib/propagators/b3 v1.40.0 h1:xariChe8OOVF3rNlfzGFgQc61npQmXhzZj/i82mxMfg= +go.opentelemetry.io/contrib/propagators/b3 v1.40.0/go.mod h1:72WvbdxbOfXaELEQfonFfOL6osvcVjI7uJEE8C2nkrs= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= 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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +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= +golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= +golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +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-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/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= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 h1:QNaHp8YvpPswfDNxlCmJyeesxbGOgaKf41iT9/QrErY= +golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1/go.mod h1:NuITXsA9cTiqnXtVk+/wrBT2Ja4X5hsfGOYRJ6kgYjs= 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.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 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.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -220,5 +503,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.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= diff --git a/greetservice.go b/greetservice.go deleted file mode 100644 index 8972c39..0000000 --- a/greetservice.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -type GreetService struct{} - -func (g *GreetService) Greet(name string) string { - return "Hello " + name + "!" -} diff --git a/headless.go b/headless.go deleted file mode 100644 index e4e770a..0000000 --- a/headless.go +++ /dev/null @@ -1,156 +0,0 @@ -package main - -import ( - "context" - "log" - "os" - "os/signal" - "path/filepath" - "runtime" - "strings" - "syscall" - "time" - - process "forge.lthn.ai/core/go-process" - orchestrator "forge.lthn.ai/core/agent/pkg/orchestrator" - config "forge.lthn.ai/core/go-config" - "forge.lthn.ai/core/go-scm/forge" - "forge.lthn.ai/core/agent/pkg/jobrunner" - forgejosource "forge.lthn.ai/core/agent/pkg/jobrunner/forgejo" - "forge.lthn.ai/core/agent/pkg/jobrunner/handlers" -) - -// hasDisplay returns true if a graphical display is available. -func hasDisplay() bool { - if runtime.GOOS == "windows" { - return true - } - return os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" -} - -// startHeadless runs the job runner in daemon mode without GUI. -func startHeadless() { - log.Println("Starting Core IDE in headless mode...") - - // Signal handling - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) - defer cancel() - - // Journal - journalDir := filepath.Join(os.Getenv("HOME"), ".core", "journal") - journal, err := jobrunner.NewJournal(journalDir) - if err != nil { - log.Fatalf("Failed to create journal: %v", err) - } - - // Forge client - forgeURL, forgeToken, _ := forge.ResolveConfig("", "") - forgeClient, err := forge.New(forgeURL, forgeToken) - if err != nil { - log.Fatalf("Failed to create forge client: %v", err) - } - - // Forgejo source — repos from CORE_REPOS env var or default - repos := parseRepoList(os.Getenv("CORE_REPOS")) - if len(repos) == 0 { - repos = []string{"host-uk/core", "host-uk/core-php", "host-uk/core-tenant", "host-uk/core-admin"} - } - - source := forgejosource.New(forgejosource.Config{ - Repos: repos, - }, forgeClient) - - // Handlers (order matters — first match wins) - publishDraft := handlers.NewPublishDraftHandler(forgeClient) - sendFix := handlers.NewSendFixCommandHandler(forgeClient) - dismissReviews := handlers.NewDismissReviewsHandler(forgeClient) - enableAutoMerge := handlers.NewEnableAutoMergeHandler(forgeClient) - tickParent := handlers.NewTickParentHandler(forgeClient) - - // Agent dispatch — Clotho integration - cfg, cfgErr := config.New() - var agentTargets map[string]orchestrator.AgentConfig - var clothoCfg orchestrator.ClothoConfig - - if cfgErr == nil { - agentTargets, _ = orchestrator.LoadActiveAgents(cfg) - clothoCfg, _ = orchestrator.LoadClothoConfig(cfg) - } - if agentTargets == nil { - agentTargets = map[string]orchestrator.AgentConfig{} - } - - spinner := orchestrator.NewSpinner(clothoCfg, agentTargets) - log.Printf("Loaded %d agent targets. Strategy: %s", len(agentTargets), clothoCfg.Strategy) - - dispatch := handlers.NewDispatchHandler(forgeClient, forgeURL, forgeToken, spinner) - - // Build poller - poller := jobrunner.NewPoller(jobrunner.PollerConfig{ - Sources: []jobrunner.JobSource{source}, - Handlers: []jobrunner.JobHandler{ - publishDraft, - sendFix, - dismissReviews, - enableAutoMerge, - tickParent, - dispatch, // Last — only matches NeedsCoding signals - }, - Journal: journal, - PollInterval: 60 * time.Second, - DryRun: isDryRun(), - }) - - // Daemon with PID file and health check - daemon := process.NewDaemon(process.DaemonOptions{ - PIDFile: filepath.Join(os.Getenv("HOME"), ".core", "core-ide.pid"), - HealthAddr: "127.0.0.1:9878", - }) - - if err := daemon.Start(); err != nil { - log.Fatalf("Failed to start daemon: %v", err) - } - daemon.SetReady(true) - - // Start MCP bridge in headless mode too (port 9877) - go startHeadlessMCP(poller) - - log.Printf("Polling %d repos every %s (dry-run: %v)", len(repos), "60s", poller.DryRun()) - - // Run poller in goroutine, block on context - go func() { - if err := poller.Run(ctx); err != nil && err != context.Canceled { - log.Printf("Poller error: %v", err) - } - }() - - // Block until signal - <-ctx.Done() - log.Println("Shutting down...") - _ = daemon.Stop() -} - -// parseRepoList splits a comma-separated repo list. -func parseRepoList(s string) []string { - if s == "" { - return nil - } - var repos []string - for _, r := range strings.Split(s, ",") { - r = strings.TrimSpace(r) - if r != "" { - repos = append(repos, r) - } - } - return repos -} - -// isDryRun checks if --dry-run flag was passed. -func isDryRun() bool { - for _, arg := range os.Args[1:] { - if arg == "--dry-run" { - return true - } - } - return false -} diff --git a/headless_mcp.go b/headless_mcp.go deleted file mode 100644 index 2525a41..0000000 --- a/headless_mcp.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "net/http" - "strings" - - "forge.lthn.ai/core/agent/pkg/jobrunner" -) - -// startHeadlessMCP starts a minimal MCP HTTP server for headless mode. -// It exposes job handler tools, brain tools, and health endpoints. -func startHeadlessMCP(poller *jobrunner.Poller) { - brain := NewBrainService() - mux := http.NewServeMux() - - mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]any{ - "status": "ok", - "mode": "headless", - "cycle": poller.Cycle(), - }) - }) - - mux.HandleFunc("/mcp", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]any{ - "name": "core-ide", - "version": "0.1.0", - "mode": "headless", - }) - }) - - mux.HandleFunc("/mcp/tools", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - tools := []map[string]string{ - {"name": "job_status", "description": "Get poller status (cycle count, dry-run)"}, - {"name": "job_set_dry_run", "description": "Enable/disable dry-run mode"}, - {"name": "job_run_once", "description": "Trigger a single poll-dispatch cycle"}, - } - tools = append(tools, brainToolsList()...) - json.NewEncoder(w).Encode(map[string]any{"tools": tools}) - }) - - mux.HandleFunc("/mcp/call", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - if r.Method != "POST" { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - var req struct { - Tool string `json:"tool"` - Params map[string]any `json:"params"` - } - r.Body = http.MaxBytesReader(w, r.Body, 1<<20) - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "invalid request body", http.StatusBadRequest) - return - } - - switch req.Tool { - case "job_status": - json.NewEncoder(w).Encode(map[string]any{ - "cycle": poller.Cycle(), - "dry_run": poller.DryRun(), - }) - case "job_set_dry_run": - if v, ok := req.Params["enabled"].(bool); ok { - poller.SetDryRun(v) - } - json.NewEncoder(w).Encode(map[string]any{"dry_run": poller.DryRun()}) - case "job_run_once": - err := poller.RunOnce(context.Background()) - json.NewEncoder(w).Encode(map[string]any{ - "success": err == nil, - "cycle": poller.Cycle(), - }) - default: - // Try brain tools - if strings.HasPrefix(req.Tool, "brain_") { - result := executeBrainTool(brain, req.Tool, req.Params) - json.NewEncoder(w).Encode(result) - return - } - json.NewEncoder(w).Encode(map[string]any{"error": "unknown tool"}) - } - }) - - addr := fmt.Sprintf("127.0.0.1:%d", mcpPort) - log.Printf("Headless MCP server listening on %s", addr) - if err := http.ListenAndServe(addr, mux); err != nil { - log.Printf("Headless MCP server error: %v", err) - } -} diff --git a/main.go b/main.go index 2129f65..81804bd 100644 --- a/main.go +++ b/main.go @@ -1,51 +1,142 @@ package main import ( + "context" "embed" "io/fs" "log" "os" + "os/signal" "runtime" + "syscall" + "forge.lthn.ai/core/go-config" + "forge.lthn.ai/core/go-ws" + "forge.lthn.ai/core/go/pkg/core" + guiMCP "forge.lthn.ai/core/gui/pkg/mcp" + "forge.lthn.ai/core/gui/pkg/display" "forge.lthn.ai/core/ide/icons" + "forge.lthn.ai/core/mcp/pkg/mcp" + "forge.lthn.ai/core/mcp/pkg/mcp/brain" + "forge.lthn.ai/core/mcp/pkg/mcp/ide" "github.com/wailsapp/wails/v3/pkg/application" ) //go:embed all:frontend/dist/wails-angular-template/browser var assets embed.FS -// Default MCP port for the embedded server -const mcpPort = 9877 - func main() { - // Check for headless mode - headless := false + // ── Flags ────────────────────────────────────────────────── + mcpOnly := false for _, arg := range os.Args[1:] { - if arg == "--headless" { - headless = true + if arg == "--mcp" { + mcpOnly = true } } - if headless || !hasDisplay() { - startHeadless() + // ── Configuration ────────────────────────────────────────── + cfg, _ := config.New() + + cwd, err := os.Getwd() + if err != nil { + log.Fatalf("failed to get working directory: %v", err) + } + + // ── Shared resources (built before Core) ─────────────────── + hub := ws.NewHub() + + bridgeCfg := ide.DefaultConfig() + bridgeCfg.WorkspaceRoot = cwd + if url := os.Getenv("CORE_API_URL"); url != "" { + bridgeCfg.LaravelWSURL = url + } + if token := os.Getenv("CORE_API_TOKEN"); token != "" { + bridgeCfg.Token = token + } + bridge := ide.NewBridge(hub, bridgeCfg) + + // ── Core framework ───────────────────────────────────────── + c, err := core.New( + core.WithName("ws", func(c *core.Core) (any, error) { + return hub, nil + }), + core.WithService(display.Register(nil)), // nil platform until Wails starts + core.WithName("mcp", func(c *core.Core) (any, error) { + return mcp.New( + mcp.WithWorkspaceRoot(cwd), + mcp.WithWSHub(hub), + mcp.WithSubsystem(brain.New(bridge)), + mcp.WithSubsystem(guiMCP.New(c)), + ) + }), + ) + if err != nil { + log.Fatalf("failed to create core: %v", err) + } + + // Retrieve the MCP service for transport control + mcpSvc, err := core.ServiceFor[*mcp.Service](c, "mcp") + if err != nil { + log.Fatalf("failed to get MCP service: %v", err) + } + + // ── Mode selection ───────────────────────────────────────── + if mcpOnly { + // stdio mode — Claude Code connects via --mcp flag + ctx, cancel := signal.NotifyContext(context.Background(), + 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) + + if err := mcpSvc.ServeStdio(ctx); err != nil { + log.Printf("MCP stdio error: %v", err) + } + + _ = mcpSvc.Shutdown(ctx) + _ = c.ServiceShutdown(ctx) return } - // Strip the embed path prefix so files are served from root + if !guiEnabled(cfg) { + // No GUI — run Core with MCP transport in background + ctx, cancel := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + if err := c.ServiceStartup(ctx, nil); err != nil { + log.Fatalf("core startup failed: %v", err) + } + bridge.Start(ctx) + + go func() { + if err := mcpSvc.Run(ctx); err != nil { + log.Printf("MCP error: %v", err) + } + }() + + <-ctx.Done() + shutdownCtx := context.Background() + _ = mcpSvc.Shutdown(shutdownCtx) + _ = c.ServiceShutdown(shutdownCtx) + return + } + + // ── GUI mode ─────────────────────────────────────────────── staticAssets, err := fs.Sub(assets, "frontend/dist/wails-angular-template/browser") if err != nil { log.Fatal(err) } - // Create the MCP bridge for Claude Code integration - mcpBridge := NewMCPBridge(mcpPort) - app := application.New(application.Options{ Name: "Core IDE", Description: "Host UK Core IDE - Development Environment", Services: []application.Service{ - application.NewService(&GreetService{}), - application.NewService(mcpBridge), + application.NewService(c), }, Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(staticAssets), @@ -53,6 +144,11 @@ func main() { Mac: application.MacOptions{ ActivationPolicy: application.ActivationPolicyAccessory, }, + OnShutdown: func() { + ctx := context.Background() + _ = mcpSvc.Shutdown(ctx) + bridge.Shutdown() + }, }) // System tray @@ -86,9 +182,36 @@ func main() { }) systray.SetMenu(trayMenu) + // Start MCP transport alongside Wails + go func() { + ctx := context.Background() + bridge.Start(ctx) + if err := mcpSvc.Run(ctx); err != nil { + log.Printf("MCP error: %v", err) + } + }() + log.Println("Starting Core IDE...") if err := app.Run(); err != nil { log.Fatal(err) } } + +// guiEnabled checks whether the GUI should start. +// Returns false if config says gui.enabled: false, or if no display is available. +func guiEnabled(cfg *config.Config) bool { + if cfg != nil { + var guiCfg struct { + Enabled *bool `mapstructure:"enabled"` + } + if err := cfg.Get("gui", &guiCfg); err == nil && guiCfg.Enabled != nil { + return *guiCfg.Enabled + } + } + // Fall back to display detection + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + return true + } + return os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" +} diff --git a/mcp_bridge.go b/mcp_bridge.go deleted file mode 100644 index a69c9df..0000000 --- a/mcp_bridge.go +++ /dev/null @@ -1,527 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "net/http" - "net/url" - "strings" - "sync" - "time" - - ws "forge.lthn.ai/core/go-ws" - "github.com/wailsapp/wails/v3/pkg/application" -) - -// MCPBridge wires together WebView, WebSocket, and Brain services -// and starts the MCP HTTP server after Wails initializes. -type MCPBridge struct { - webview *WebviewService - brain *BrainService - wsHub *ws.Hub - claudeBridge *ClaudeBridge - app *application.App - port int - running bool - mu sync.Mutex -} - -// NewMCPBridge creates a new MCP bridge with all services wired up. -func NewMCPBridge(port int) *MCPBridge { - wv := NewWebviewService() - hub := ws.NewHub() - - // Create Claude bridge to forward messages to MCP core on port 9876 - claudeBridge := NewClaudeBridge("ws://localhost:9876/ws") - - return &MCPBridge{ - webview: wv, - brain: NewBrainService(), - wsHub: hub, - claudeBridge: claudeBridge, - port: port, - } -} - -// ServiceStartup is called by Wails when the app starts. -// This wires up the app reference and starts the HTTP server. -func (b *MCPBridge) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { - b.mu.Lock() - defer b.mu.Unlock() - - // Get the Wails app reference - b.app = application.Get() - if b.app == nil { - return fmt.Errorf("failed to get Wails app reference") - } - - // Wire up the WebView service with the app - b.webview.SetApp(b.app) - - // Set up console listener - b.webview.SetupConsoleListener() - - // Inject console capture into all windows after a short delay - // (windows may not be created yet) - go b.injectConsoleCapture() - - // Start the HTTP server for MCP - go b.startHTTPServer() - - log.Printf("MCP Bridge started on port %d", b.port) - return nil -} - -// injectConsoleCapture injects the console capture script into windows. -func (b *MCPBridge) injectConsoleCapture() { - // Wait for windows to be created (poll with timeout) - var windows []WindowInfo - for i := 0; i < 10; i++ { - time.Sleep(500 * time.Millisecond) - windows = b.webview.ListWindows() - if len(windows) > 0 { - break - } - } - if len(windows) == 0 { - log.Printf("MCP Bridge: no windows found after waiting") - return - } - for _, w := range windows { - if err := b.webview.InjectConsoleCapture(w.Name); err != nil { - log.Printf("Failed to inject console capture in %s: %v", w.Name, err) - } - } -} - -// startHTTPServer starts the HTTP server for MCP and WebSocket. -func (b *MCPBridge) startHTTPServer() { - b.mu.Lock() - b.running = true - b.mu.Unlock() - - // Start the WebSocket hub - hubCtx := context.Background() - go b.wsHub.Run(hubCtx) - - // Claude bridge disabled - port 9876 is not an MCP WebSocket server - // b.claudeBridge.Start() - - mux := http.NewServeMux() - - // WebSocket endpoint for GUI clients - mux.HandleFunc("/ws", b.wsHub.HandleWebSocket) - - // MCP info endpoint - mux.HandleFunc("/mcp", b.handleMCPInfo) - - // MCP tools endpoint - mux.HandleFunc("/mcp/tools", b.handleMCPTools) - mux.HandleFunc("/mcp/call", b.handleMCPCall) - - // Health check - mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]any{ - "status": "ok", - "mcp": true, - "webview": b.webview != nil, - }) - }) - - addr := fmt.Sprintf("127.0.0.1:%d", b.port) - log.Printf("MCP HTTP server listening on %s", addr) - - if err := http.ListenAndServe(addr, mux); err != nil { - log.Printf("MCP HTTP server error: %v", err) - } -} - -// handleMCPInfo returns MCP server information. -func (b *MCPBridge) handleMCPInfo(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "http://localhost") - - info := map[string]any{ - "name": "core-ide", - "version": "0.1.0", - "capabilities": map[string]any{ - "webview": true, - "websocket": fmt.Sprintf("ws://localhost:%d/ws", b.port), - }, - } - json.NewEncoder(w).Encode(info) -} - -// handleMCPTools returns the list of available tools. -func (b *MCPBridge) handleMCPTools(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "http://localhost") - - tools := []map[string]string{ - // WebView interaction (JS runtime, console, DOM) - {"name": "webview_list", "description": "List windows"}, - {"name": "webview_eval", "description": "Execute JavaScript"}, - {"name": "webview_console", "description": "Get console messages"}, - {"name": "webview_console_clear", "description": "Clear console buffer"}, - {"name": "webview_click", "description": "Click element"}, - {"name": "webview_type", "description": "Type into element"}, - {"name": "webview_query", "description": "Query DOM elements"}, - {"name": "webview_navigate", "description": "Navigate to URL"}, - {"name": "webview_source", "description": "Get page source"}, - {"name": "webview_url", "description": "Get current page URL"}, - {"name": "webview_title", "description": "Get current page title"}, - {"name": "webview_screenshot", "description": "Capture page as base64 PNG"}, - {"name": "webview_screenshot_element", "description": "Capture specific element as PNG"}, - {"name": "webview_scroll", "description": "Scroll to element or position"}, - {"name": "webview_hover", "description": "Hover over element"}, - {"name": "webview_select", "description": "Select option in dropdown"}, - {"name": "webview_check", "description": "Check/uncheck checkbox or radio"}, - {"name": "webview_element_info", "description": "Get detailed info about element"}, - {"name": "webview_computed_style", "description": "Get computed styles for element"}, - {"name": "webview_highlight", "description": "Visually highlight element"}, - {"name": "webview_dom_tree", "description": "Get DOM tree structure"}, - {"name": "webview_errors", "description": "Get captured error messages"}, - {"name": "webview_performance", "description": "Get performance metrics"}, - {"name": "webview_resources", "description": "List loaded resources"}, - {"name": "webview_network", "description": "Get network requests log"}, - {"name": "webview_network_clear", "description": "Clear network request log"}, - {"name": "webview_network_inject", "description": "Inject network interceptor for detailed logging"}, - {"name": "webview_pdf", "description": "Export page as PDF (base64 data URI)"}, - {"name": "webview_print", "description": "Open print dialog for window"}, - } - tools = append(tools, brainToolsList()...) - json.NewEncoder(w).Encode(map[string]any{"tools": tools}) -} - -// handleMCPCall handles tool calls via HTTP POST. -func (b *MCPBridge) handleMCPCall(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "http://localhost") - w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - - if r.Method != "POST" { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - var req struct { - Tool string `json:"tool"` - Params map[string]any `json:"params"` - } - - // Limit request body to 1MB - r.Body = http.MaxBytesReader(w, r.Body, 1<<20) - - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "invalid request body", http.StatusBadRequest) - return - } - - result := b.executeWebviewTool(req.Tool, req.Params) - json.NewEncoder(w).Encode(result) -} - -// executeWebviewTool handles webview/JS tool execution. -func (b *MCPBridge) executeWebviewTool(tool string, params map[string]any) map[string]any { - if b.webview == nil { - return map[string]any{"error": "webview service not available"} - } - - switch tool { - case "webview_list": - windows := b.webview.ListWindows() - return map[string]any{"windows": windows} - - case "webview_eval": - windowName := getStringParam(params, "window") - code := getStringParam(params, "code") - result, err := b.webview.ExecJS(windowName, code) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"result": result} - - case "webview_console": - level := getStringParam(params, "level") - limit := getIntParam(params, "limit") - if limit == 0 { - limit = 100 - } - messages := b.webview.GetConsoleMessages(level, limit) - return map[string]any{"messages": messages} - - case "webview_console_clear": - b.webview.ClearConsole() - return map[string]any{"success": true} - - case "webview_click": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - err := b.webview.Click(windowName, selector) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_type": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - text := getStringParam(params, "text") - err := b.webview.Type(windowName, selector, text) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_query": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - result, err := b.webview.QuerySelector(windowName, selector) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"elements": result} - - case "webview_navigate": - windowName := getStringParam(params, "window") - rawURL := getStringParam(params, "url") - parsed, err := url.Parse(rawURL) - if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") { - return map[string]any{"error": "only http/https URLs are allowed"} - } - err = b.webview.Navigate(windowName, rawURL) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_source": - windowName := getStringParam(params, "window") - result, err := b.webview.GetPageSource(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"source": result} - - case "webview_url": - windowName := getStringParam(params, "window") - result, err := b.webview.GetURL(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"url": result} - - case "webview_title": - windowName := getStringParam(params, "window") - result, err := b.webview.GetTitle(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"title": result} - - case "webview_screenshot": - windowName := getStringParam(params, "window") - data, err := b.webview.Screenshot(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"data": data} - - case "webview_screenshot_element": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - data, err := b.webview.ScreenshotElement(windowName, selector) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"data": data} - - case "webview_scroll": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - x := getIntParam(params, "x") - y := getIntParam(params, "y") - err := b.webview.Scroll(windowName, selector, x, y) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_hover": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - err := b.webview.Hover(windowName, selector) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_select": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - value := getStringParam(params, "value") - err := b.webview.Select(windowName, selector, value) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_check": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - checked, _ := params["checked"].(bool) - err := b.webview.Check(windowName, selector, checked) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_element_info": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - result, err := b.webview.GetElementInfo(windowName, selector) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"element": result} - - case "webview_computed_style": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - var properties []string - if props, ok := params["properties"].([]any); ok { - for _, p := range props { - if s, ok := p.(string); ok { - properties = append(properties, s) - } - } - } - result, err := b.webview.GetComputedStyle(windowName, selector, properties) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"styles": result} - - case "webview_highlight": - windowName := getStringParam(params, "window") - selector := getStringParam(params, "selector") - duration := getIntParam(params, "duration") - err := b.webview.Highlight(windowName, selector, duration) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_dom_tree": - windowName := getStringParam(params, "window") - maxDepth := getIntParam(params, "maxDepth") - result, err := b.webview.GetDOMTree(windowName, maxDepth) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"tree": result} - - case "webview_errors": - limit := getIntParam(params, "limit") - if limit == 0 { - limit = 50 - } - errors := b.webview.GetErrors(limit) - return map[string]any{"errors": errors} - - case "webview_performance": - windowName := getStringParam(params, "window") - result, err := b.webview.GetPerformance(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"performance": result} - - case "webview_resources": - windowName := getStringParam(params, "window") - result, err := b.webview.GetResources(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"resources": result} - - case "webview_network": - windowName := getStringParam(params, "window") - limit := getIntParam(params, "limit") - result, err := b.webview.GetNetworkRequests(windowName, limit) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"requests": result} - - case "webview_network_clear": - windowName := getStringParam(params, "window") - err := b.webview.ClearNetworkRequests(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_network_inject": - windowName := getStringParam(params, "window") - err := b.webview.InjectNetworkInterceptor(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - case "webview_pdf": - windowName := getStringParam(params, "window") - options := make(map[string]any) - if filename := getStringParam(params, "filename"); filename != "" { - options["filename"] = filename - } - if margin, ok := params["margin"].(float64); ok { - options["margin"] = margin - } - data, err := b.webview.ExportToPDF(windowName, options) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"data": data} - - case "webview_print": - windowName := getStringParam(params, "window") - err := b.webview.PrintToPDF(windowName) - if err != nil { - return map[string]any{"error": err.Error()} - } - return map[string]any{"success": true} - - default: - // Try brain tools - if strings.HasPrefix(tool, "brain_") { - return executeBrainTool(b.brain, tool, params) - } - return map[string]any{"error": "unknown tool", "tool": tool} - } -} - -// Helper functions for parameter extraction -func getStringParam(params map[string]any, key string) string { - if v, ok := params[key].(string); ok { - return v - } - return "" -} - -func getIntParam(params map[string]any, key string) int { - if v, ok := params[key].(float64); ok { - return int(v) - } - return 0 -} diff --git a/webview_svc.go b/webview_svc.go deleted file mode 100644 index 2714424..0000000 --- a/webview_svc.go +++ /dev/null @@ -1,417 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "github.com/wailsapp/wails/v3/pkg/application" -) - -// WindowInfo describes a Wails webview window. -type WindowInfo struct { - Name string `json:"name"` - Title string `json:"title"` - Width int `json:"width"` - Height int `json:"height"` -} - -// ConsoleEntry is a captured browser console message. -type ConsoleEntry struct { - Level string `json:"level"` - Message string `json:"message"` - Timestamp time.Time `json:"timestamp"` -} - -// WebviewService wraps Wails v3 window management and JS execution -// for MCP tool access. This replaces the deleted gui/pkg/webview package. -type WebviewService struct { - app *application.App - console []ConsoleEntry - mu sync.Mutex -} - -// NewWebviewService creates a new service (no app wired yet). -func NewWebviewService() *WebviewService { - return &WebviewService{} -} - -// SetApp wires the Wails application reference. -func (s *WebviewService) SetApp(app *application.App) { - s.mu.Lock() - defer s.mu.Unlock() - s.app = app -} - -// SetupConsoleListener is a no-op stub — console capture requires -// JS injection (see InjectConsoleCapture). -func (s *WebviewService) SetupConsoleListener() {} - -// ListWindows returns info for all open Wails windows. -func (s *WebviewService) ListWindows() []WindowInfo { - s.mu.Lock() - app := s.app - s.mu.Unlock() - - if app == nil { - return nil - } - - var result []WindowInfo - for _, w := range app.Window.GetAll() { - result = append(result, WindowInfo{Name: w.Name()}) - } - return result -} - -// getWindow looks up a Wails window by name. -func (s *WebviewService) getWindow(name string) (application.Window, error) { - s.mu.Lock() - app := s.app - s.mu.Unlock() - - if app == nil { - return nil, fmt.Errorf("app not initialised") - } - if name == "" { - name = "tray-panel" - } - w, ok := app.Window.Get(name) - if !ok { - return nil, fmt.Errorf("window %q not found", name) - } - return w, nil -} - -// ExecJS runs JavaScript in the named window and returns the result as a string. -// Wails v3 ExecJS is fire-and-forget, so we use a callback pattern via events. -func (s *WebviewService) ExecJS(windowName, code string) (string, error) { - w, err := s.getWindow(windowName) - if err != nil { - return "", err - } - w.ExecJS(code) - return "", nil // Wails v3 ExecJS doesn't return values -} - -// InjectConsoleCapture injects a JS script that captures console messages. -func (s *WebviewService) InjectConsoleCapture(windowName string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - - // Inject console capture script - js := `(function(){ - if(window.__consoleCaptured) return; - window.__consoleCaptured = true; - window.__consoleLog = []; - ['log','warn','error','info','debug'].forEach(function(level){ - var orig = console[level]; - console[level] = function(){ - window.__consoleLog.push({level:level, message:Array.from(arguments).join(' '), ts:Date.now()}); - if(window.__consoleLog.length > 1000) window.__consoleLog.shift(); - orig.apply(console, arguments); - }; - }); - })()` - w.ExecJS(js) - return nil -} - -// GetConsoleMessages returns captured console messages (requires InjectConsoleCapture). -func (s *WebviewService) GetConsoleMessages(level string, limit int) []ConsoleEntry { - s.mu.Lock() - defer s.mu.Unlock() - - if limit <= 0 { - limit = 100 - } - var result []ConsoleEntry - for _, e := range s.console { - if level != "" && e.Level != level { - continue - } - result = append(result, e) - if len(result) >= limit { - break - } - } - return result -} - -// ClearConsole clears the captured console buffer. -func (s *WebviewService) ClearConsole() { - s.mu.Lock() - defer s.mu.Unlock() - s.console = nil -} - -// Click clicks a DOM element by CSS selector. -func (s *WebviewService) Click(windowName, selector string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - w.ExecJS(fmt.Sprintf(`document.querySelector(%s)?.click()`, jsQuote(selector))) - return nil -} - -// Type types text into a DOM element by CSS selector. -func (s *WebviewService) Type(windowName, selector, text string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - js := fmt.Sprintf(`(function(){var el=document.querySelector(%s);if(el){el.focus();el.value=%s;el.dispatchEvent(new Event('input',{bubbles:true}));}})()`, - jsQuote(selector), jsQuote(text)) - w.ExecJS(js) - return nil -} - -// QuerySelector queries DOM elements by CSS selector. -func (s *WebviewService) QuerySelector(windowName, selector string) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("QuerySelector requires CDP — not yet implemented") -} - -// Navigate navigates the window to a URL. -func (s *WebviewService) Navigate(windowName, url string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - w.SetURL(url) - return nil -} - -// GetPageSource returns the page HTML source. -func (s *WebviewService) GetPageSource(windowName string) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("GetPageSource requires CDP — not yet implemented") -} - -// GetURL returns the current page URL. -func (s *WebviewService) GetURL(windowName string) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("GetURL requires CDP — not yet implemented") -} - -// GetTitle returns the current page title. -func (s *WebviewService) GetTitle(windowName string) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("GetTitle requires CDP — not yet implemented") -} - -// Screenshot captures the page as base64 PNG. -func (s *WebviewService) Screenshot(windowName string) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("Screenshot requires CDP — not yet implemented") -} - -// ScreenshotElement captures a specific element as base64 PNG. -func (s *WebviewService) ScreenshotElement(windowName, selector string) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("ScreenshotElement requires CDP — not yet implemented") -} - -// Scroll scrolls the window or element. -func (s *WebviewService) Scroll(windowName, selector string, x, y int) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - if selector != "" { - w.ExecJS(fmt.Sprintf(`document.querySelector(%s)?.scrollTo(%d,%d)`, jsQuote(selector), x, y)) - } else { - w.ExecJS(fmt.Sprintf(`window.scrollTo(%d,%d)`, x, y)) - } - return nil -} - -// Hover dispatches a mouseover event on an element. -func (s *WebviewService) Hover(windowName, selector string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - w.ExecJS(fmt.Sprintf(`document.querySelector(%s)?.dispatchEvent(new MouseEvent('mouseover',{bubbles:true}))`, jsQuote(selector))) - return nil -} - -// Select selects an option in a dropdown. -func (s *WebviewService) Select(windowName, selector, value string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - js := fmt.Sprintf(`(function(){var el=document.querySelector(%s);if(el){el.value=%s;el.dispatchEvent(new Event('change',{bubbles:true}));}})()`, - jsQuote(selector), jsQuote(value)) - w.ExecJS(js) - return nil -} - -// Check checks or unchecks a checkbox/radio. -func (s *WebviewService) Check(windowName, selector string, checked bool) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - js := fmt.Sprintf(`(function(){var el=document.querySelector(%s);if(el){el.checked=%t;el.dispatchEvent(new Event('change',{bubbles:true}));}})()`, - jsQuote(selector), checked) - w.ExecJS(js) - return nil -} - -// GetElementInfo returns info about a DOM element. -func (s *WebviewService) GetElementInfo(windowName, selector string) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetElementInfo requires CDP — not yet implemented") -} - -// GetComputedStyle returns computed CSS styles. -func (s *WebviewService) GetComputedStyle(windowName, selector string, properties []string) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetComputedStyle requires CDP — not yet implemented") -} - -// Highlight visually highlights an element. -func (s *WebviewService) Highlight(windowName, selector string, duration int) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - if duration <= 0 { - duration = 2000 - } - js := fmt.Sprintf(`(function(){var el=document.querySelector(%s);if(el){var old=el.style.outline;el.style.outline='3px solid red';setTimeout(function(){el.style.outline=old;},%d);}})()`, - jsQuote(selector), duration) - w.ExecJS(js) - return nil -} - -// GetDOMTree returns DOM tree structure. -func (s *WebviewService) GetDOMTree(windowName string, maxDepth int) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetDOMTree requires CDP — not yet implemented") -} - -// GetErrors returns captured error messages. -func (s *WebviewService) GetErrors(limit int) []ConsoleEntry { - return s.GetConsoleMessages("error", limit) -} - -// GetPerformance returns performance metrics. -func (s *WebviewService) GetPerformance(windowName string) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetPerformance requires CDP — not yet implemented") -} - -// GetResources returns loaded resources. -func (s *WebviewService) GetResources(windowName string) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetResources requires CDP — not yet implemented") -} - -// GetNetworkRequests returns network request log. -func (s *WebviewService) GetNetworkRequests(windowName string, limit int) (any, error) { - _, err := s.getWindow(windowName) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("GetNetworkRequests requires CDP — not yet implemented") -} - -// ClearNetworkRequests clears the network request log. -func (s *WebviewService) ClearNetworkRequests(windowName string) error { - _, err := s.getWindow(windowName) - if err != nil { - return err - } - return fmt.Errorf("ClearNetworkRequests requires CDP — not yet implemented") -} - -// InjectNetworkInterceptor injects network monitoring JS. -func (s *WebviewService) InjectNetworkInterceptor(windowName string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - js := `(function(){ - if(window.__networkCaptured) return; - window.__networkCaptured = true; - window.__networkLog = []; - var origFetch = window.fetch; - window.fetch = function(){ - var start = Date.now(); - var url = arguments[0]; - if(typeof url === 'object') url = url.url; - return origFetch.apply(this, arguments).then(function(resp){ - window.__networkLog.push({url:url, status:resp.status, duration:Date.now()-start, ts:start}); - if(window.__networkLog.length > 500) window.__networkLog.shift(); - return resp; - }); - }; - })()` - w.ExecJS(js) - return nil -} - -// ExportToPDF exports page as PDF via CDP. -func (s *WebviewService) ExportToPDF(windowName string, options map[string]any) (string, error) { - _, err := s.getWindow(windowName) - if err != nil { - return "", err - } - return "", fmt.Errorf("ExportToPDF requires CDP — not yet implemented") -} - -// PrintToPDF opens print dialog. -func (s *WebviewService) PrintToPDF(windowName string) error { - w, err := s.getWindow(windowName) - if err != nil { - return err - } - w.ExecJS(`window.print()`) - return nil -} - -// jsQuote JSON-encodes a string for safe JS interpolation. -func jsQuote(s string) string { - b, _ := json.Marshal(s) - return string(b) -}