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:
env:
REGISTRY: gitea.snider.dev
REGISTRY: dappco.re/osi
IMAGE_APP: host-uk/app
IMAGE_WEB: host-uk/web
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 { provideRouter, withHashLocation } from '@angular/router';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withHashLocation())
provideRouter(routes)
]
};

View file

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

View file

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

View file

@ -79,8 +79,8 @@ func (b *MCPBridge) startHTTPServer() {
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,
"status": "ok",
"mcp": true,
"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
}
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 {
Hours() float64
Minutes() float64

View file

@ -1,7 +1,8 @@
// cmd_agent.go manages persistent agent context within task workspaces.
//
// 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
// understanding over time — QA agents accumulate findings, reviewers
@ -34,7 +35,7 @@ import (
var (
agentProvider string
agentName string
agentName string
)
func addAgentCommands(parent *cobra.Command) {

View file

@ -24,11 +24,11 @@ import (
)
var (
taskEpic int
taskIssue int
taskRepos []string
taskForce bool
taskBranch string
taskEpic int
taskIssue int
taskRepos []string
taskForce bool
taskBranch string
)
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.
type Challenge struct {
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"`
}

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 {
key := target.OS + "/" + target.Arch
profiles := map[string]string{
"linux/amd64": "gcc-linux-x86_64",
"linux/x86_64": "gcc-linux-x86_64",
"linux/arm64": "gcc-linux-armv8",
"linux/armv8": "gcc-linux-armv8",
"darwin/arm64": "apple-clang-armv8",
"darwin/armv8": "apple-clang-armv8",
"darwin/amd64": "apple-clang-x86_64",
"darwin/x86_64": "apple-clang-x86_64",
"linux/amd64": "gcc-linux-x86_64",
"linux/x86_64": "gcc-linux-x86_64",
"linux/arm64": "gcc-linux-armv8",
"linux/armv8": "gcc-linux-armv8",
"darwin/arm64": "apple-clang-armv8",
"darwin/armv8": "apple-clang-armv8",
"darwin/amd64": "apple-clang-x86_64",
"darwin/x86_64": "apple-clang-x86_64",
"windows/amd64": "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.BuildPreRelease=dev.8"
var (
AppVersion = "0.0.0"
BuildCommit = "unknown"
BuildDate = "unknown"
AppVersion = "0.0.0"
BuildCommit = "unknown"
BuildDate = "unknown"
BuildPreRelease = ""
)

View file

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

View file

@ -552,10 +552,12 @@ type dirEntry struct {
name string
}
func (d *dirEntry) Name() string { return d.name }
func (d *dirEntry) IsDir() bool { return true }
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) Name() string { return d.name }
func (d *dirEntry) IsDir() bool { return true }
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
}
type fileInfo struct {
name string

View file

@ -335,7 +335,7 @@ func TestOverwrite_Good(t *testing.T) {
func TestExists_Good(t *testing.T) {
m := New()
assert.True(t, m.Exists("")) // root
assert.True(t, m.Exists("")) // root
assert.False(t, m.Exists("x"))
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) Read(_ []byte) (int, error) { return 0, goio.EOF }
func (d *dataFile) Close() error { return nil }
func (d *dataFile) Read(_ []byte) (int, error) { return 0, goio.EOF }
func (d *dataFile) Close() error { return nil }
// dataFileInfo implements fs.FileInfo for a dataFile.
type dataFileInfo struct{ file *dataFile }
@ -496,11 +496,13 @@ var _ fs.ReadDirFS = (*Node)(nil)
// Unexported helper: ensure ReadStream result also satisfies fs.File
// (for cases where callers do a type assertion).
var _ goio.ReadCloser = goio.NopCloser(nil)
// Ensure nodeWriter satisfies goio.WriteCloser.
var _ goio.WriteCloser = (*nodeWriter)(nil)
// Ensure dirFile satisfies fs.File.
var _ fs.File = (*dirFile)(nil)
// Ensure dataFileReader satisfies fs.File.
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) IsDir() bool { return de.isDir }
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.
type s3File struct {

View file

@ -239,9 +239,9 @@ func TestHashSigil_Good(t *testing.T) {
data := []byte("hash me")
tests := []struct {
name string
name string
sigilName string
size int
size int
}{
{"md5", "md5", md5.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) IsDir() bool { return de.isDir }
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.
type sqliteFile struct {

View file

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