fix(core-ide): use path-based routing for multi-window SPA, clean up formatting

Switch Angular from hash-based to path-based routing so each Wails window
(/tray, /main, /settings) loads its correct route. Archive GitHub Actions
workflows to .gh-actions/, update Forgejo deploy registry to dappco.re/osi,
and apply gofmt/alignment fixes across packages.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-02-09 01:50:57 +00:00
parent 0fb9de600d
commit a668c5ab5a
54 changed files with 16239 additions and 73 deletions

View file

@ -16,7 +16,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
REGISTRY: gitea.snider.dev REGISTRY: dappco.re/osi
IMAGE_APP: host-uk/app IMAGE_APP: host-uk/app
IMAGE_WEB: host-uk/web IMAGE_WEB: host-uk/web
IMAGE_CORE: host-uk/core IMAGE_CORE: host-uk/core

16159
cmd/core-ide/frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
import { ApplicationConfig } from '@angular/core'; import { ApplicationConfig } from '@angular/core';
import { provideRouter, withHashLocation } from '@angular/router'; import { provideRouter } from '@angular/router';
import { routes } from './app.routes'; import { routes } from './app.routes';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideRouter(routes, withHashLocation()) provideRouter(routes)
] ]
}; };

View file

@ -1,6 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { ChatComponent } from '../chat/chat.component'; import { ChatComponent } from '../chat/chat.component';
import { BuildComponent } from '../build/build.component'; import { BuildComponent } from '../build/build.component';
import { DashboardComponent } from '../dashboard/dashboard.component'; import { DashboardComponent } from '../dashboard/dashboard.component';
@ -11,7 +10,7 @@ type Panel = 'chat' | 'build' | 'dashboard' | 'jellyfin';
@Component({ @Component({
selector: 'app-main', selector: 'app-main',
standalone: true, standalone: true,
imports: [CommonModule, RouterLink, RouterLinkActive, RouterOutlet, ChatComponent, BuildComponent, DashboardComponent, JellyfinComponent], imports: [CommonModule, ChatComponent, BuildComponent, DashboardComponent, JellyfinComponent],
template: ` template: `
<div class="ide"> <div class="ide">
<nav class="ide__sidebar"> <nav class="ide__sidebar">

View file

@ -81,4 +81,3 @@ func (s *IDEService) ShowWindow(name string) {
w.Focus() w.Focus()
} }
} }

View file

@ -79,8 +79,8 @@ func (b *MCPBridge) startHTTPServer() {
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{ json.NewEncoder(w).Encode(map[string]any{
"status": "ok", "status": "ok",
"mcp": true, "mcp": true,
"claudeBridge": b.claudeBridge.Connected(), "claudeBridge": b.claudeBridge.Connected(),
}) })
}) })

BIN
core-test

Binary file not shown.

View file

View file

View file

@ -208,7 +208,11 @@ func shortID(id string) string {
return id return id
} }
func formatDur(d interface{ Hours() float64; Minutes() float64; Seconds() float64 }) string { func formatDur(d interface {
Hours() float64
Minutes() float64
Seconds() float64
}) string {
type dur interface { type dur interface {
Hours() float64 Hours() float64
Minutes() float64 Minutes() float64

View file

@ -1,7 +1,8 @@
// cmd_agent.go manages persistent agent context within task workspaces. // cmd_agent.go manages persistent agent context within task workspaces.
// //
// Each agent gets a directory at: // Each agent gets a directory at:
// .core/workspace/p{epic}/i{issue}/agents/{provider}/{agent-name}/ //
// .core/workspace/p{epic}/i{issue}/agents/{provider}/{agent-name}/
// //
// This directory persists across invocations, allowing agents to build // This directory persists across invocations, allowing agents to build
// understanding over time — QA agents accumulate findings, reviewers // understanding over time — QA agents accumulate findings, reviewers
@ -34,7 +35,7 @@ import (
var ( var (
agentProvider string agentProvider string
agentName string agentName string
) )
func addAgentCommands(parent *cobra.Command) { func addAgentCommands(parent *cobra.Command) {

View file

@ -24,11 +24,11 @@ import (
) )
var ( var (
taskEpic int taskEpic int
taskIssue int taskIssue int
taskRepos []string taskRepos []string
taskForce bool taskForce bool
taskBranch string taskBranch string
) )
func addTaskCommands(parent *cobra.Command) { func addTaskCommands(parent *cobra.Command) {

View file

@ -66,7 +66,7 @@ type User struct {
// Challenge is a PGP-encrypted nonce sent to a client during authentication. // Challenge is a PGP-encrypted nonce sent to a client during authentication.
type Challenge struct { type Challenge struct {
Nonce []byte `json:"nonce"` Nonce []byte `json:"nonce"`
Encrypted string `json:"encrypted"` // PGP-encrypted nonce (armored) Encrypted string `json:"encrypted"` // PGP-encrypted nonce (armored)
ExpiresAt time.Time `json:"expires_at"` ExpiresAt time.Time `json:"expires_at"`
} }

View file

@ -226,14 +226,14 @@ func (b *CPPBuilder) findBinaries(fs io.Medium, projectDir string, target build.
func (b *CPPBuilder) targetToProfile(target build.Target) string { func (b *CPPBuilder) targetToProfile(target build.Target) string {
key := target.OS + "/" + target.Arch key := target.OS + "/" + target.Arch
profiles := map[string]string{ profiles := map[string]string{
"linux/amd64": "gcc-linux-x86_64", "linux/amd64": "gcc-linux-x86_64",
"linux/x86_64": "gcc-linux-x86_64", "linux/x86_64": "gcc-linux-x86_64",
"linux/arm64": "gcc-linux-armv8", "linux/arm64": "gcc-linux-armv8",
"linux/armv8": "gcc-linux-armv8", "linux/armv8": "gcc-linux-armv8",
"darwin/arm64": "apple-clang-armv8", "darwin/arm64": "apple-clang-armv8",
"darwin/armv8": "apple-clang-armv8", "darwin/armv8": "apple-clang-armv8",
"darwin/amd64": "apple-clang-x86_64", "darwin/amd64": "apple-clang-x86_64",
"darwin/x86_64": "apple-clang-x86_64", "darwin/x86_64": "apple-clang-x86_64",
"windows/amd64": "msvc-194-x86_64", "windows/amd64": "msvc-194-x86_64",
"windows/x86_64": "msvc-194-x86_64", "windows/x86_64": "msvc-194-x86_64",
} }

View file

@ -24,9 +24,9 @@ const (
// -X github.com/host-uk/core/pkg/cli.BuildDate=2026-02-06 \ // -X github.com/host-uk/core/pkg/cli.BuildDate=2026-02-06 \
// -X github.com/host-uk/core/pkg/cli.BuildPreRelease=dev.8" // -X github.com/host-uk/core/pkg/cli.BuildPreRelease=dev.8"
var ( var (
AppVersion = "0.0.0" AppVersion = "0.0.0"
BuildCommit = "unknown" BuildCommit = "unknown"
BuildDate = "unknown" BuildDate = "unknown"
BuildPreRelease = "" BuildPreRelease = ""
) )

View file

@ -12,19 +12,19 @@ import (
// Config is the top-level infrastructure configuration parsed from infra.yaml. // Config is the top-level infrastructure configuration parsed from infra.yaml.
type Config struct { type Config struct {
Hosts map[string]*Host `yaml:"hosts"` Hosts map[string]*Host `yaml:"hosts"`
LoadBalancer LoadBalancer `yaml:"load_balancer"` LoadBalancer LoadBalancer `yaml:"load_balancer"`
Network Network `yaml:"network"` Network Network `yaml:"network"`
DNS DNS `yaml:"dns"` DNS DNS `yaml:"dns"`
SSL SSL `yaml:"ssl"` SSL SSL `yaml:"ssl"`
Database Database `yaml:"database"` Database Database `yaml:"database"`
Cache Cache `yaml:"cache"` Cache Cache `yaml:"cache"`
Containers map[string]*Container `yaml:"containers"` Containers map[string]*Container `yaml:"containers"`
S3 S3Config `yaml:"s3"` S3 S3Config `yaml:"s3"`
CDN CDN `yaml:"cdn"` CDN CDN `yaml:"cdn"`
CICD CICD `yaml:"cicd"` CICD CICD `yaml:"cicd"`
Monitoring Monitoring `yaml:"monitoring"` Monitoring Monitoring `yaml:"monitoring"`
Backups Backups `yaml:"backups"` Backups Backups `yaml:"backups"`
} }
// Host represents a server in the infrastructure. // Host represents a server in the infrastructure.
@ -47,16 +47,16 @@ type SSHConf struct {
// LoadBalancer represents a Hetzner managed load balancer. // LoadBalancer represents a Hetzner managed load balancer.
type LoadBalancer struct { type LoadBalancer struct {
Name string `yaml:"name"` Name string `yaml:"name"`
FQDN string `yaml:"fqdn"` FQDN string `yaml:"fqdn"`
Provider string `yaml:"provider"` Provider string `yaml:"provider"`
Type string `yaml:"type"` Type string `yaml:"type"`
Location string `yaml:"location"` Location string `yaml:"location"`
Algorithm string `yaml:"algorithm"` Algorithm string `yaml:"algorithm"`
Backends []Backend `yaml:"backends"` Backends []Backend `yaml:"backends"`
Health HealthCheck `yaml:"health_check"` Health HealthCheck `yaml:"health_check"`
Listeners []Listener `yaml:"listeners"` Listeners []Listener `yaml:"listeners"`
SSL LBCert `yaml:"ssl"` SSL LBCert `yaml:"ssl"`
} }
// Backend is a load balancer backend target. // Backend is a load balancer backend target.
@ -94,9 +94,9 @@ type Network struct {
// DNS holds DNS provider configuration and zone records. // DNS holds DNS provider configuration and zone records.
type DNS struct { type DNS struct {
Provider string `yaml:"provider"` Provider string `yaml:"provider"`
Nameservers []string `yaml:"nameservers"` Nameservers []string `yaml:"nameservers"`
Zones map[string]*Zone `yaml:"zones"` Zones map[string]*Zone `yaml:"zones"`
} }
// Zone is a DNS zone with its records. // Zone is a DNS zone with its records.
@ -203,8 +203,8 @@ type CICD struct {
// Monitoring describes monitoring configuration. // Monitoring describes monitoring configuration.
type Monitoring struct { type Monitoring struct {
HealthEndpoints []HealthEndpoint `yaml:"health_endpoints"` HealthEndpoints []HealthEndpoint `yaml:"health_endpoints"`
Alerts map[string]int `yaml:"alerts"` Alerts map[string]int `yaml:"alerts"`
} }
// HealthEndpoint is a URL to monitor. // HealthEndpoint is a URL to monitor.

View file

@ -552,10 +552,12 @@ type dirEntry struct {
name string name string
} }
func (d *dirEntry) Name() string { return d.name } func (d *dirEntry) Name() string { return d.name }
func (d *dirEntry) IsDir() bool { return true } func (d *dirEntry) IsDir() bool { return true }
func (d *dirEntry) Type() fs.FileMode { return fs.ModeDir } func (d *dirEntry) Type() fs.FileMode { return fs.ModeDir }
func (d *dirEntry) Info() (fs.FileInfo, error) { return &fileInfo{name: d.name, isDir: true, mode: fs.ModeDir | 0755}, nil } func (d *dirEntry) Info() (fs.FileInfo, error) {
return &fileInfo{name: d.name, isDir: true, mode: fs.ModeDir | 0755}, nil
}
type fileInfo struct { type fileInfo struct {
name string name string

View file

@ -335,7 +335,7 @@ func TestOverwrite_Good(t *testing.T) {
func TestExists_Good(t *testing.T) { func TestExists_Good(t *testing.T) {
m := New() m := New()
assert.True(t, m.Exists("")) // root assert.True(t, m.Exists("")) // root
assert.False(t, m.Exists("x")) assert.False(t, m.Exists("x"))
require.NoError(t, m.Write("x", "y")) require.NoError(t, m.Write("x", "y"))

View file

@ -431,8 +431,8 @@ type dataFile struct {
} }
func (d *dataFile) Stat() (fs.FileInfo, error) { return &dataFileInfo{file: d}, nil } func (d *dataFile) Stat() (fs.FileInfo, error) { return &dataFileInfo{file: d}, nil }
func (d *dataFile) Read(_ []byte) (int, error) { return 0, goio.EOF } func (d *dataFile) Read(_ []byte) (int, error) { return 0, goio.EOF }
func (d *dataFile) Close() error { return nil } func (d *dataFile) Close() error { return nil }
// dataFileInfo implements fs.FileInfo for a dataFile. // dataFileInfo implements fs.FileInfo for a dataFile.
type dataFileInfo struct{ file *dataFile } type dataFileInfo struct{ file *dataFile }
@ -496,11 +496,13 @@ var _ fs.ReadDirFS = (*Node)(nil)
// Unexported helper: ensure ReadStream result also satisfies fs.File // Unexported helper: ensure ReadStream result also satisfies fs.File
// (for cases where callers do a type assertion). // (for cases where callers do a type assertion).
var _ goio.ReadCloser = goio.NopCloser(nil) var _ goio.ReadCloser = goio.NopCloser(nil)
// Ensure nodeWriter satisfies goio.WriteCloser. // Ensure nodeWriter satisfies goio.WriteCloser.
var _ goio.WriteCloser = (*nodeWriter)(nil) var _ goio.WriteCloser = (*nodeWriter)(nil)
// Ensure dirFile satisfies fs.File. // Ensure dirFile satisfies fs.File.
var _ fs.File = (*dirFile)(nil) var _ fs.File = (*dirFile)(nil)
// Ensure dataFileReader satisfies fs.File. // Ensure dataFileReader satisfies fs.File.
var _ fs.File = (*dataFileReader)(nil) var _ fs.File = (*dataFileReader)(nil)

View file

@ -567,7 +567,7 @@ type dirEntry struct {
func (de *dirEntry) Name() string { return de.name } func (de *dirEntry) Name() string { return de.name }
func (de *dirEntry) IsDir() bool { return de.isDir } func (de *dirEntry) IsDir() bool { return de.isDir }
func (de *dirEntry) Type() fs.FileMode { return de.mode.Type() } func (de *dirEntry) Type() fs.FileMode { return de.mode.Type() }
func (de *dirEntry) Info() (fs.FileInfo, error) { return de.info, nil } func (de *dirEntry) Info() (fs.FileInfo, error) { return de.info, nil }
// s3File implements fs.File for S3 objects. // s3File implements fs.File for S3 objects.
type s3File struct { type s3File struct {

View file

@ -239,9 +239,9 @@ func TestHashSigil_Good(t *testing.T) {
data := []byte("hash me") data := []byte("hash me")
tests := []struct { tests := []struct {
name string name string
sigilName string sigilName string
size int size int
}{ }{
{"md5", "md5", md5.Size}, {"md5", "md5", md5.Size},
{"sha1", "sha1", sha1.Size}, {"sha1", "sha1", sha1.Size},

View file

@ -611,7 +611,7 @@ type dirEntry struct {
func (de *dirEntry) Name() string { return de.name } func (de *dirEntry) Name() string { return de.name }
func (de *dirEntry) IsDir() bool { return de.isDir } func (de *dirEntry) IsDir() bool { return de.isDir }
func (de *dirEntry) Type() fs.FileMode { return de.mode.Type() } func (de *dirEntry) Type() fs.FileMode { return de.mode.Type() }
func (de *dirEntry) Info() (fs.FileInfo, error) { return de.info, nil } func (de *dirEntry) Info() (fs.FileInfo, error) { return de.info, nil }
// sqliteFile implements fs.File for SQLite entries. // sqliteFile implements fs.File for SQLite entries.
type sqliteFile struct { type sqliteFile struct {

View file

@ -21,14 +21,14 @@ import (
// For full GUI features, use the core-gui package. // For full GUI features, use the core-gui package.
type Service struct { type Service struct {
server *mcp.Server server *mcp.Server
workspaceRoot string // Root directory for file operations (empty = unrestricted) workspaceRoot string // Root directory for file operations (empty = unrestricted)
medium io.Medium // Filesystem medium for sandboxed operations medium io.Medium // Filesystem medium for sandboxed operations
subsystems []Subsystem // Additional subsystems registered via WithSubsystem subsystems []Subsystem // Additional subsystems registered via WithSubsystem
logger *log.Logger // Logger for tool execution auditing logger *log.Logger // Logger for tool execution auditing
processService *process.Service // Process management service (optional) processService *process.Service // Process management service (optional)
wsHub *ws.Hub // WebSocket hub for real-time streaming (optional) wsHub *ws.Hub // WebSocket hub for real-time streaming (optional)
wsServer *http.Server // WebSocket HTTP server (optional) wsServer *http.Server // WebSocket HTTP server (optional)
wsAddr string // WebSocket server address wsAddr string // WebSocket server address
} }
// Option configures a Service. // Option configures a Service.