Phase 2 - Go Agent Core: - gRPC client with exponential backoff reconnect logic - Command executor (PowerShell/sh cross-platform) - Proto stubs regenerated with module= option (correct output path) - gRPC upgraded to v1.79.3 (BidiStreamingClient support) Phase 3 - React Frontend MVP: - Vite + React 18 + TypeScript setup with Tailwind CSS v4 - TanStack Query for data fetching, API client + TypeScript types - Dashboard page: stats cards (agents/status/tickets) + sortable agents table - Agent detail page: CPU/RAM charts (Recharts), disk usage, shell command executor - Tickets page: CRUD with modals, filters, sortable table - Dark mode with CSS custom properties Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
50 lines
972 B
Go
50 lines
972 B
Go
package executor
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os/exec"
|
|
"runtime"
|
|
"time"
|
|
)
|
|
|
|
type Result struct {
|
|
ExitCode int
|
|
Stdout string
|
|
Stderr string
|
|
Success bool
|
|
}
|
|
|
|
func Execute(ctx context.Context, command string, timeoutSec int) *Result {
|
|
if timeoutSec <= 0 {
|
|
timeoutSec = 300
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSec)*time.Second)
|
|
defer cancel()
|
|
|
|
var cmd *exec.Cmd
|
|
if runtime.GOOS == "windows" {
|
|
cmd = exec.CommandContext(ctx, "powershell", "-NoProfile", "-NonInteractive", "-Command", command)
|
|
} else {
|
|
cmd = exec.CommandContext(ctx, "/bin/sh", "-c", command)
|
|
}
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
err := cmd.Run()
|
|
result := &Result{
|
|
Stdout: stdout.String(),
|
|
Stderr: stderr.String(),
|
|
Success: err == nil,
|
|
}
|
|
if cmd.ProcessState != nil {
|
|
result.ExitCode = cmd.ProcessState.ExitCode()
|
|
}
|
|
if err != nil && result.ExitCode == 0 {
|
|
result.ExitCode = -1
|
|
}
|
|
return result
|
|
}
|