Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/internal/adapters/scm/github/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"context"
"errors"
"os"
"os/exec"
"strings"
"sync"
"time"

aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

// TokenSource yields a GitHub bearer token on demand. Production wires this
Expand Down Expand Up @@ -169,7 +170,7 @@ func (s *GHTokenSource) ttl() time.Duration {
}

func ghAuthToken(ctx context.Context) (string, error) {
out, err := exec.CommandContext(ctx, "gh", "auth", "token").Output()
out, err := aoprocess.CommandContext(ctx, "gh", "auth", "token").Output()
if err != nil {
return "", err
}
Expand Down
3 changes: 2 additions & 1 deletion backend/internal/adapters/workspace/gitworktree/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/ports"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

const (
Expand Down Expand Up @@ -527,7 +528,7 @@ func pathExistsNonEmpty(path string) (bool, error) {
}

func runCommand(ctx context.Context, binary string, args ...string) ([]byte, error) {
cmd := exec.CommandContext(ctx, binary, args...)
cmd := aoprocess.CommandContext(ctx, binary, args...)
out, err := cmd.CombinedOutput()
if err != nil {
return out, commandError{args: append([]string{binary}, args...), output: string(out), err: err}
Expand Down
5 changes: 3 additions & 2 deletions backend/internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/spf13/cobra"

"github.com/aoagents/agent-orchestrator/backend/internal/daemon"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
"github.com/aoagents/agent-orchestrator/backend/internal/processalive"
)

Expand Down Expand Up @@ -96,11 +97,11 @@ func DefaultDeps() Deps {
}

func commandOutput(ctx context.Context, name string, args ...string) ([]byte, error) {
return exec.CommandContext(ctx, name, args...).CombinedOutput()
return aoprocess.CommandContext(ctx, name, args...).CombinedOutput()
}

func commandOutputInDir(ctx context.Context, dir, name string, args ...string) ([]byte, error) {
cmd := exec.CommandContext(ctx, name, args...)
cmd := aoprocess.CommandContext(ctx, name, args...)
cmd.Dir = dir
return cmd.CombinedOutput()
}
Expand Down
4 changes: 2 additions & 2 deletions backend/internal/legacyimport/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"context"
"fmt"
"os"
"os/exec"
"regexp"
"sort"
"strings"
"time"

"github.com/aoagents/agent-orchestrator/backend/internal/domain"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

// Store is the narrow slice of the rewrite's native storage layer the importer
Expand Down Expand Up @@ -235,7 +235,7 @@ func defaultRepoOriginURL(path string) string {
if path == "" {
return ""
}
cmd := exec.Command("git", "-C", path, "remote", "get-url", "origin")
cmd := aoprocess.Command("git", "-C", path, "remote", "get-url", "origin")
out, err := cmd.Output()
if err != nil {
return ""
Expand Down
4 changes: 2 additions & 2 deletions backend/internal/observe/scm/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import (
"errors"
"fmt"
"log/slog"
"os/exec"
"strings"
"sync"
"time"

"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/observe"
"github.com/aoagents/agent-orchestrator/backend/internal/ports"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

const (
Expand Down Expand Up @@ -1212,7 +1212,7 @@ func normalizePRState(draft, merged, closed bool) string {
// The observer uses this to backfill projects that were registered before
// project.Add resolved origin URLs at add time.
func resolveGitOriginURL(path string) string {
out, err := exec.Command("git", "-C", path, "remote", "get-url", "origin").Output()
out, err := aoprocess.Command("git", "-C", path, "remote", "get-url", "origin").Output()
if err != nil {
return ""
}
Expand Down
21 changes: 21 additions & 0 deletions backend/internal/process/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package process

import (
"context"
"os/exec"
)

// Command creates a non-interactive child process. On Windows it suppresses
// transient console windows for CLI tools launched by the desktop daemon.
func Command(name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)
configureHidden(cmd)
return cmd
}

// CommandContext is Command with cancellation support.
func CommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, name, args...)
configureHidden(cmd)
return cmd
}
7 changes: 7 additions & 0 deletions backend/internal/process/command_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build !windows

package process

import "os/exec"

func configureHidden(_ *exec.Cmd) {}
17 changes: 17 additions & 0 deletions backend/internal/process/command_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build windows

package process

import (
"os/exec"
"syscall"

"golang.org/x/sys/windows"
)

func configureHidden(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: windows.CREATE_NO_WINDOW,
HideWindow: true,
}
}
23 changes: 23 additions & 0 deletions backend/internal/process/command_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build windows

package process

import (
"context"
"testing"

"golang.org/x/sys/windows"
)

func TestCommandContextHidesConsoleWindow(t *testing.T) {
cmd := CommandContext(context.Background(), "git", "--version")
if cmd.SysProcAttr == nil {
t.Fatal("SysProcAttr = nil, want hidden Windows process attributes")
}
if got := cmd.SysProcAttr.CreationFlags; got&windows.CREATE_NO_WINDOW == 0 {
t.Fatalf("CreationFlags = %#x, want CREATE_NO_WINDOW", got)
}
if !cmd.SysProcAttr.HideWindow {
t.Fatal("HideWindow = false, want true")
}
}
10 changes: 5 additions & 5 deletions backend/internal/service/project/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package project
import (
"context"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
Expand All @@ -14,6 +13,7 @@ import (
"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/httpd/apierr"
"github.com/aoagents/agent-orchestrator/backend/internal/ports"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

// Manager is the controller-facing contract for the /api/v1/projects surface.
Expand Down Expand Up @@ -294,7 +294,7 @@ func (m *Service) SetConfig(ctx context.Context, id domain.ProjectID, in SetConf
// other git error returns an empty string — `project add` must not fail just
// because no origin is configured (the SCM observer skips such projects).
func resolveGitOriginURL(path string) string {
out, err := exec.Command("git", "-C", path, "remote", "get-url", "origin").Output()
out, err := aoprocess.Command("git", "-C", path, "remote", "get-url", "origin").Output()
if err != nil {
return ""
}
Expand All @@ -313,14 +313,14 @@ func resolveGitOriginURL(path string) string {
// returns an empty string — `project add` must not fail just because the branch
// can't be resolved (the caller falls back to DefaultBranchName).
func resolveDefaultBranch(path string) string {
if out, err := exec.Command(
if out, err := aoprocess.Command(
"git", "-C", path, "symbolic-ref", "--short", "refs/remotes/origin/HEAD",
).Output(); err == nil {
if ref := strings.TrimSpace(string(out)); ref != "" {
return strings.TrimPrefix(ref, "origin/")
}
}
out, err := exec.Command("git", "-C", path, "symbolic-ref", "--short", "HEAD").Output()
out, err := aoprocess.Command("git", "-C", path, "symbolic-ref", "--short", "HEAD").Output()
if err != nil {
return ""
}
Expand Down Expand Up @@ -412,7 +412,7 @@ func normalizePath(raw string) (string, error) {
}

func isGitRepo(path string) bool {
cmd := exec.Command("git", "-C", path, "rev-parse", "--show-toplevel")
cmd := aoprocess.Command("git", "-C", path, "rev-parse", "--show-toplevel")
out, err := cmd.Output()
if err != nil {
return false
Expand Down
4 changes: 2 additions & 2 deletions backend/internal/service/project/workspace_registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"time"

"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/httpd/apierr"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

var workspaceRootIgnoreDenylist = []string{
Expand Down Expand Up @@ -358,7 +358,7 @@ func workspaceReposFromRecords(records []domain.WorkspaceRepoRecord) []Workspace
}

func gitOutput(ctx context.Context, dir string, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, "git", append([]string{"-C", dir}, args...)...)
cmd := aoprocess.CommandContext(ctx, "git", append([]string{"-C", dir}, args...)...)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("git -C %s %s: %w: %s", dir, strings.Join(args, " "), err, strings.TrimSpace(string(out)))
Expand Down
5 changes: 3 additions & 2 deletions backend/internal/session_manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/ports"
aoprocess "github.com/aoagents/agent-orchestrator/backend/internal/process"
)

// Sentinel errors returned by the Session Manager; callers match them with
Expand Down Expand Up @@ -875,9 +876,9 @@ func runPostCreate(ctx context.Context, workspacePath string, commands []string)
}
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.CommandContext(ctx, "cmd", "/c", command)
cmd = aoprocess.CommandContext(ctx, "cmd", "/c", command)
} else {
cmd = exec.CommandContext(ctx, "sh", "-c", command)
cmd = aoprocess.CommandContext(ctx, "sh", "-c", command)
}
cmd.Dir = workspacePath
if out, err := cmd.CombinedOutput(); err != nil {
Expand Down
Loading