2026-03-17 17:45:04 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package monitor
|
|
|
|
|
|
|
|
|
|
import (
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
"context"
|
2026-03-30 20:37:23 +00:00
|
|
|
"net/url"
|
2026-03-17 17:45:04 +00:00
|
|
|
"time"
|
2026-03-17 19:19:04 +00:00
|
|
|
|
2026-03-21 11:10:31 +00:00
|
|
|
"dappco.re/go/agent/pkg/agentic"
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
core "dappco.re/go/core"
|
2026-03-17 17:45:04 +00:00
|
|
|
)
|
|
|
|
|
|
2026-03-30 20:45:23 +00:00
|
|
|
// resp := monitor.CheckinResponse{Changed: []monitor.ChangedRepo{{Repo: "core-agent", Branch: "main", SHA: "abc123"}}, Timestamp: 1712345678}
|
2026-03-17 17:45:04 +00:00
|
|
|
type CheckinResponse struct {
|
2026-03-30 20:45:23 +00:00
|
|
|
Changed []ChangedRepo `json:"changed,omitempty"`
|
|
|
|
|
Timestamp int64 `json:"timestamp"`
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 20:45:23 +00:00
|
|
|
// repo := monitor.ChangedRepo{Repo: "core-agent", Branch: "main", SHA: "abc123"}
|
2026-03-17 17:45:04 +00:00
|
|
|
type ChangedRepo struct {
|
|
|
|
|
Repo string `json:"repo"`
|
|
|
|
|
Branch string `json:"branch"`
|
|
|
|
|
SHA string `json:"sha"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Subsystem) syncRepos() string {
|
2026-03-17 19:19:04 +00:00
|
|
|
agentName := agentic.AgentName()
|
2026-03-30 20:37:23 +00:00
|
|
|
checkinURL := core.Sprintf("%s/v1/agent/checkin?agent=%s&since=%d", monitorAPIURL(), url.QueryEscape(agentName), m.lastSyncTimestamp)
|
2026-03-17 17:45:04 +00:00
|
|
|
|
2026-03-22 13:40:14 +00:00
|
|
|
brainKey := monitorBrainKey()
|
2026-03-30 20:45:23 +00:00
|
|
|
httpResult := agentic.HTTPGet(context.Background(), checkinURL, brainKey, "Bearer")
|
|
|
|
|
if !httpResult.OK {
|
2026-03-17 17:45:04 +00:00
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var checkin CheckinResponse
|
2026-03-30 20:45:23 +00:00
|
|
|
if parseResult := core.JSONUnmarshalString(httpResult.Value.(string), &checkin); !parseResult.OK {
|
2026-03-17 17:45:04 +00:00
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(checkin.Changed) == 0 {
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock := m.monitorLock()
|
2026-03-21 16:05:59 +00:00
|
|
|
m.lastSyncTimestamp = checkin.Timestamp
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock()
|
2026-03-17 17:45:04 +00:00
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 13:40:14 +00:00
|
|
|
basePath := core.Env("CODE_PATH")
|
2026-03-17 17:45:04 +00:00
|
|
|
if basePath == "" {
|
2026-03-30 20:32:17 +00:00
|
|
|
basePath = core.JoinPath(agentic.HomeDir(), "Code", "core")
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pulled []string
|
|
|
|
|
for _, repo := range checkin.Changed {
|
2026-03-30 16:52:49 +00:00
|
|
|
repoName := core.PathBase(core.Replace(repo.Repo, "\\", "/"))
|
2026-03-21 17:25:23 +00:00
|
|
|
if repoName == "." || repoName == ".." || repoName == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-03-30 16:46:20 +00:00
|
|
|
repoDir := core.JoinPath(basePath, repoName)
|
2026-03-22 13:40:14 +00:00
|
|
|
if !fs.Exists(repoDir) || fs.IsFile(repoDir) {
|
2026-03-17 17:45:04 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
current := m.gitOutput(repoDir, "rev-parse", "--abbrev-ref", "HEAD")
|
|
|
|
|
if current == "" {
|
2026-03-21 16:05:59 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 17:10:43 +00:00
|
|
|
targetBranch := repo.Branch
|
|
|
|
|
if targetBranch == "" {
|
|
|
|
|
targetBranch = current
|
2026-03-21 16:05:59 +00:00
|
|
|
}
|
2026-03-21 17:10:43 +00:00
|
|
|
|
|
|
|
|
if current != targetBranch {
|
2026-03-30 20:45:23 +00:00
|
|
|
continue
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
status := m.gitOutput(repoDir, "status", "--porcelain")
|
|
|
|
|
if len(status) > 0 {
|
2026-03-30 20:45:23 +00:00
|
|
|
continue
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
if m.gitOK(repoDir, "pull", "--ff-only", "origin", targetBranch) {
|
2026-03-17 17:45:04 +00:00
|
|
|
pulled = append(pulled, repo.Repo)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 16:22:18 +00:00
|
|
|
skipped := len(checkin.Changed) - len(pulled)
|
|
|
|
|
if skipped == 0 {
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock := m.monitorLock()
|
2026-03-21 16:22:18 +00:00
|
|
|
m.lastSyncTimestamp = checkin.Timestamp
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock()
|
2026-03-21 16:22:18 +00:00
|
|
|
}
|
2026-03-21 16:05:59 +00:00
|
|
|
|
2026-03-17 17:45:04 +00:00
|
|
|
if len(pulled) == 0 {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
return core.Sprintf("Synced %d repo(s): %s", len(pulled), core.Join(", ", pulled...))
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 14:54:32 +00:00
|
|
|
func (m *Subsystem) syncWorkspacePush(repo, branch, org string) bool {
|
|
|
|
|
if m.ServiceRuntime == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repoDir := localRepoDir(org, repo)
|
|
|
|
|
if repoDir == "" || !fs.Exists(repoDir) || fs.IsFile(repoDir) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetBranch := core.Trim(branch)
|
|
|
|
|
if targetBranch == "" {
|
|
|
|
|
targetBranch = m.detectBranch(repoDir)
|
|
|
|
|
}
|
|
|
|
|
if targetBranch == "" {
|
|
|
|
|
targetBranch = m.defaultBranch(repoDir)
|
|
|
|
|
}
|
|
|
|
|
if targetBranch == "" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !m.gitOK(repoDir, "fetch", "origin", targetBranch) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentBranch := m.detectBranch(repoDir)
|
|
|
|
|
if currentBranch != "" && currentBranch != targetBranch {
|
2026-04-02 01:53:46 +00:00
|
|
|
if !m.gitOK(repoDir, "checkout", "-B", targetBranch, core.Concat("origin/", targetBranch)) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2026-03-31 14:54:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m.gitOK(repoDir, "reset", "--hard", core.Concat("origin/", targetBranch))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func localRepoDir(org, repo string) string {
|
|
|
|
|
basePath := core.Env("CODE_PATH")
|
|
|
|
|
if basePath == "" {
|
|
|
|
|
basePath = core.JoinPath(agentic.HomeDir(), "Code")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
normalisedRepo := core.Replace(repo, "\\", "/")
|
|
|
|
|
repoName := core.PathBase(normalisedRepo)
|
|
|
|
|
orgName := core.PathBase(core.Replace(org, "\\", "/"))
|
|
|
|
|
repoParts := core.Split(normalisedRepo, "/")
|
|
|
|
|
if orgName == "" && len(repoParts) > 1 {
|
|
|
|
|
orgName = repoParts[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
candidates := []string{}
|
|
|
|
|
if orgName != "" {
|
|
|
|
|
candidates = append(candidates, core.JoinPath(basePath, orgName, repoName))
|
|
|
|
|
}
|
|
|
|
|
candidates = append(candidates, core.JoinPath(basePath, repoName))
|
|
|
|
|
|
|
|
|
|
for _, candidate := range candidates {
|
|
|
|
|
if fs.Exists(candidate) && !fs.IsFile(candidate) {
|
|
|
|
|
return candidate
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(candidates) == 0 {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return candidates[0]
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 17:45:04 +00:00
|
|
|
func (m *Subsystem) initSyncTimestamp() {
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock := m.monitorLock()
|
2026-03-17 17:45:04 +00:00
|
|
|
if m.lastSyncTimestamp == 0 {
|
|
|
|
|
m.lastSyncTimestamp = time.Now().Unix()
|
|
|
|
|
}
|
refactor: AX compliance sweep — replace banned stdlib imports with core primitives
Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath,
errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim,
core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(),
core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives.
Framework boundary exceptions preserved where stdlib types are required
by external interfaces (Gin, net/http, CGo, Wails, bubbletea).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-13 09:32:00 +01:00
|
|
|
unlock()
|
2026-03-17 17:45:04 +00:00
|
|
|
}
|