feat(witness): add --env flag for environment variable overrides
Extends the --agent flag with a more general --env flag that allows setting arbitrary environment variables when starting a witness. Precedence (highest to lowest): 1. CLI --env overrides 2. Role bead env_vars 3. config.AgentEnv() defaults Examples: gt witness start greenplace --env ANTHROPIC_MODEL=claude-3-haiku gt witness restart greenplace --env DEBUG=1 --env VERBOSE=true Co-authored-by: joshuavial <git@codewithjv.com>
This commit is contained in:
committed by
Steve Yegge
parent
f9473c7b9e
commit
86751e1ea5
@@ -759,7 +759,7 @@ func runRigBoot(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Printf(" Starting witness...\n")
|
fmt.Printf(" Starting witness...\n")
|
||||||
witMgr := witness.NewManager(r)
|
witMgr := witness.NewManager(r)
|
||||||
if err := witMgr.Start(false, ""); err != nil {
|
if err := witMgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
skipped = append(skipped, "witness (already running)")
|
skipped = append(skipped, "witness (already running)")
|
||||||
} else {
|
} else {
|
||||||
@@ -839,7 +839,7 @@ func runRigStart(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Printf(" Starting witness...\n")
|
fmt.Printf(" Starting witness...\n")
|
||||||
witMgr := witness.NewManager(r)
|
witMgr := witness.NewManager(r)
|
||||||
if err := witMgr.Start(false, ""); err != nil {
|
if err := witMgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
skipped = append(skipped, "witness")
|
skipped = append(skipped, "witness")
|
||||||
} else {
|
} else {
|
||||||
@@ -1418,7 +1418,7 @@ func runRigRestart(cmd *cobra.Command, args []string) error {
|
|||||||
skipped = append(skipped, "witness")
|
skipped = append(skipped, "witness")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" Starting witness...\n")
|
fmt.Printf(" Starting witness...\n")
|
||||||
if err := witMgr.Start(false, ""); err != nil {
|
if err := witMgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
skipped = append(skipped, "witness")
|
skipped = append(skipped, "witness")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func startRigAgents(t *tmux.Tmux, townRoot string) {
|
|||||||
fmt.Printf(" %s %s witness already running\n", style.Dim.Render("○"), r.Name)
|
fmt.Printf(" %s %s witness already running\n", style.Dim.Render("○"), r.Name)
|
||||||
} else {
|
} else {
|
||||||
witMgr := witness.NewManager(r)
|
witMgr := witness.NewManager(r)
|
||||||
if err := witMgr.Start(false, ""); err != nil {
|
if err := witMgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
fmt.Printf(" %s %s witness already running\n", style.Dim.Render("○"), r.Name)
|
fmt.Printf(" %s %s witness already running\n", style.Dim.Render("○"), r.Name)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func runUp(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mgr := witness.NewManager(r)
|
mgr := witness.NewManager(r)
|
||||||
if err := mgr.Start(false, ""); err != nil {
|
if err := mgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
printStatus(fmt.Sprintf("Witness (%s)", rigName), true, mgr.SessionName())
|
printStatus(fmt.Sprintf("Witness (%s)", rigName), true, mgr.SessionName())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ var (
|
|||||||
witnessForeground bool
|
witnessForeground bool
|
||||||
witnessStatusJSON bool
|
witnessStatusJSON bool
|
||||||
witnessAgentOverride string
|
witnessAgentOverride string
|
||||||
|
witnessEnvOverrides []string
|
||||||
)
|
)
|
||||||
|
|
||||||
var witnessCmd = &cobra.Command{
|
var witnessCmd = &cobra.Command{
|
||||||
@@ -43,6 +44,7 @@ states and takes action to keep work flowing.
|
|||||||
Examples:
|
Examples:
|
||||||
gt witness start greenplace
|
gt witness start greenplace
|
||||||
gt witness start greenplace --agent codex
|
gt witness start greenplace --agent codex
|
||||||
|
gt witness start greenplace --env ANTHROPIC_MODEL=claude-3-haiku
|
||||||
gt witness start greenplace --foreground`,
|
gt witness start greenplace --foreground`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: runWitnessStart,
|
RunE: runWitnessStart,
|
||||||
@@ -96,7 +98,8 @@ Stops the current session (if running) and starts a fresh one.
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
gt witness restart greenplace
|
gt witness restart greenplace
|
||||||
gt witness restart greenplace --agent codex`,
|
gt witness restart greenplace --agent codex
|
||||||
|
gt witness restart greenplace --env ANTHROPIC_MODEL=claude-3-haiku`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: runWitnessRestart,
|
RunE: runWitnessRestart,
|
||||||
}
|
}
|
||||||
@@ -105,12 +108,14 @@ func init() {
|
|||||||
// Start flags
|
// Start flags
|
||||||
witnessStartCmd.Flags().BoolVar(&witnessForeground, "foreground", false, "Run in foreground (default: background)")
|
witnessStartCmd.Flags().BoolVar(&witnessForeground, "foreground", false, "Run in foreground (default: background)")
|
||||||
witnessStartCmd.Flags().StringVar(&witnessAgentOverride, "agent", "", "Agent alias to run the Witness with (overrides town default)")
|
witnessStartCmd.Flags().StringVar(&witnessAgentOverride, "agent", "", "Agent alias to run the Witness with (overrides town default)")
|
||||||
|
witnessStartCmd.Flags().StringArrayVar(&witnessEnvOverrides, "env", nil, "Environment variable override (KEY=VALUE, can be repeated)")
|
||||||
|
|
||||||
// Status flags
|
// Status flags
|
||||||
witnessStatusCmd.Flags().BoolVar(&witnessStatusJSON, "json", false, "Output as JSON")
|
witnessStatusCmd.Flags().BoolVar(&witnessStatusJSON, "json", false, "Output as JSON")
|
||||||
|
|
||||||
// Restart flags
|
// Restart flags
|
||||||
witnessRestartCmd.Flags().StringVar(&witnessAgentOverride, "agent", "", "Agent alias to run the Witness with (overrides town default)")
|
witnessRestartCmd.Flags().StringVar(&witnessAgentOverride, "agent", "", "Agent alias to run the Witness with (overrides town default)")
|
||||||
|
witnessRestartCmd.Flags().StringArrayVar(&witnessEnvOverrides, "env", nil, "Environment variable override (KEY=VALUE, can be repeated)")
|
||||||
|
|
||||||
// Add subcommands
|
// Add subcommands
|
||||||
witnessCmd.AddCommand(witnessStartCmd)
|
witnessCmd.AddCommand(witnessStartCmd)
|
||||||
@@ -143,7 +148,7 @@ func runWitnessStart(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
fmt.Printf("Starting witness for %s...\n", rigName)
|
fmt.Printf("Starting witness for %s...\n", rigName)
|
||||||
|
|
||||||
if err := mgr.Start(witnessForeground, witnessAgentOverride); err != nil {
|
if err := mgr.Start(witnessForeground, witnessAgentOverride, witnessEnvOverrides); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
fmt.Printf("%s Witness is already running\n", style.Dim.Render("⚠"))
|
fmt.Printf("%s Witness is already running\n", style.Dim.Render("⚠"))
|
||||||
fmt.Printf(" %s\n", style.Dim.Render("Use 'gt witness attach' to connect"))
|
fmt.Printf(" %s\n", style.Dim.Render("Use 'gt witness attach' to connect"))
|
||||||
@@ -296,7 +301,7 @@ func runWitnessAttach(cmd *cobra.Command, args []string) error {
|
|||||||
sessionName := witnessSessionName(rigName)
|
sessionName := witnessSessionName(rigName)
|
||||||
|
|
||||||
// Ensure session exists (creates if needed)
|
// Ensure session exists (creates if needed)
|
||||||
if err := mgr.Start(false, ""); err != nil && err != witness.ErrAlreadyRunning {
|
if err := mgr.Start(false, "", nil); err != nil && err != witness.ErrAlreadyRunning {
|
||||||
return err
|
return err
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
fmt.Printf("Started witness session for %s\n", rigName)
|
fmt.Printf("Started witness session for %s\n", rigName)
|
||||||
@@ -329,7 +334,7 @@ func runWitnessRestart(cmd *cobra.Command, args []string) error {
|
|||||||
_ = mgr.Stop()
|
_ = mgr.Stop()
|
||||||
|
|
||||||
// Start fresh
|
// Start fresh
|
||||||
if err := mgr.Start(false, witnessAgentOverride); err != nil {
|
if err := mgr.Start(false, witnessAgentOverride, witnessEnvOverrides); err != nil {
|
||||||
return fmt.Errorf("starting witness: %w", err)
|
return fmt.Errorf("starting witness: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ func (d *Daemon) ensureWitnessRunning(rigName string) {
|
|||||||
}
|
}
|
||||||
mgr := witness.NewManager(r)
|
mgr := witness.NewManager(r)
|
||||||
|
|
||||||
if err := mgr.Start(false, ""); err != nil {
|
if err := mgr.Start(false, "", nil); err != nil {
|
||||||
if err == witness.ErrAlreadyRunning {
|
if err == witness.ErrAlreadyRunning {
|
||||||
// Already running - nothing to do
|
// Already running - nothing to do
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/gastown/internal/agent"
|
"github.com/steveyegge/gastown/internal/agent"
|
||||||
@@ -101,7 +102,8 @@ func (m *Manager) witnessDir() string {
|
|||||||
// If foreground is true, only updates state (no tmux session - deprecated).
|
// If foreground is true, only updates state (no tmux session - deprecated).
|
||||||
// Otherwise, spawns a Claude agent in a tmux session.
|
// Otherwise, spawns a Claude agent in a tmux session.
|
||||||
// agentOverride optionally specifies a different agent alias to use.
|
// agentOverride optionally specifies a different agent alias to use.
|
||||||
func (m *Manager) Start(foreground bool, agentOverride string) error {
|
// envOverrides are KEY=VALUE pairs that override all other env var sources.
|
||||||
|
func (m *Manager) Start(foreground bool, agentOverride string, envOverrides []string) error {
|
||||||
w, err := m.loadState()
|
w, err := m.loadState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -159,19 +161,6 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
|
|||||||
return fmt.Errorf("creating tmux session: %w", err)
|
return fmt.Errorf("creating tmux session: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set environment variables (non-fatal: session works without these)
|
|
||||||
// Use centralized AgentEnv for consistency across all role startup paths
|
|
||||||
townRoot := filepath.Dir(m.rig.Path)
|
|
||||||
envVars := config.AgentEnv(config.AgentEnvConfig{
|
|
||||||
Role: "witness",
|
|
||||||
Rig: m.rig.Name,
|
|
||||||
TownRoot: townRoot,
|
|
||||||
BeadsDir: beads.ResolveBeadsDir(m.rig.Path),
|
|
||||||
})
|
|
||||||
for k, v := range envVars {
|
|
||||||
_ = t.SetEnvironment(sessionID, k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply Gas Town theming (non-fatal: theming failure doesn't affect operation)
|
// Apply Gas Town theming (non-fatal: theming failure doesn't affect operation)
|
||||||
theme := tmux.AssignTheme(m.rig.Name)
|
theme := tmux.AssignTheme(m.rig.Name)
|
||||||
_ = t.ConfigureGasTownSession(sessionID, theme, m.rig.Name, "witness", "witness")
|
_ = t.ConfigureGasTownSession(sessionID, theme, m.rig.Name, "witness", "witness")
|
||||||
@@ -183,10 +172,28 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
townRoot := m.townRoot()
|
townRoot := m.townRoot()
|
||||||
|
|
||||||
|
// Set environment variables (non-fatal: session works without these)
|
||||||
|
// Use centralized AgentEnv for consistency across all role startup paths
|
||||||
|
envVars := config.AgentEnv(config.AgentEnvConfig{
|
||||||
|
Role: "witness",
|
||||||
|
Rig: m.rig.Name,
|
||||||
|
TownRoot: townRoot,
|
||||||
|
BeadsDir: beads.ResolveBeadsDir(m.rig.Path),
|
||||||
|
})
|
||||||
|
for k, v := range envVars {
|
||||||
|
_ = t.SetEnvironment(sessionID, k, v)
|
||||||
|
}
|
||||||
// Apply role config env vars if present (non-fatal).
|
// Apply role config env vars if present (non-fatal).
|
||||||
for key, value := range roleConfigEnvVars(roleConfig, townRoot, m.rig.Name) {
|
for key, value := range roleConfigEnvVars(roleConfig, townRoot, m.rig.Name) {
|
||||||
_ = t.SetEnvironment(sessionID, key, value)
|
_ = t.SetEnvironment(sessionID, key, value)
|
||||||
}
|
}
|
||||||
|
// Apply CLI env overrides (highest priority, non-fatal).
|
||||||
|
for _, override := range envOverrides {
|
||||||
|
if key, value, ok := strings.Cut(override, "="); ok {
|
||||||
|
_ = t.SetEnvironment(sessionID, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update state to running
|
// Update state to running
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|||||||
Reference in New Issue
Block a user