Skip to content

Commit 43c174a

Browse files
committed
Add sandbox CLI and integrate with TUI
- Add full sandbox CLI (amux sandbox run/ls/rm/update, amux claude/codex/etc) - Integrate sandbox runtime selection into TUI (local vs sandbox per worktree) - Add Daytona provider for remote sandbox management - Add credential persistence, workspace sync, and agent installation - Add settings sync, health checks, and doctor commands - Add worktree runtime metadata and selection dialog
1 parent 0265200 commit 43c174a

93 files changed

Lines changed: 16310 additions & 37 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

TEST_PLAN.md

Lines changed: 655 additions & 0 deletions
Large diffs are not rendered by default.

cmd/amux/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
tea "charm.land/bubbletea/v2"
1818

1919
"github.com/andyrewlee/amux/internal/app"
20+
"github.com/andyrewlee/amux/internal/cli"
2021
"github.com/andyrewlee/amux/internal/logging"
2122
"github.com/andyrewlee/amux/internal/safego"
2223
)
@@ -34,6 +35,12 @@ func main() {
3435
fmt.Printf("amux %s (commit: %s, built: %s)\n", version, commit, date)
3536
os.Exit(0)
3637
}
38+
39+
// Handle CLI subcommands
40+
if len(os.Args) > 1 {
41+
os.Exit(cli.Run(os.Args[1:]))
42+
}
43+
3744
// Initialize logging
3845
home, _ := os.UserHomeDir()
3946
logDir := filepath.Join(home, ".amux", "logs")

go.mod

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,34 @@ require (
1010
github.com/charmbracelet/x/ansi v0.11.3
1111
github.com/creack/pty v1.1.21
1212
github.com/fsnotify/fsnotify v1.9.0
13+
github.com/gorilla/websocket v1.5.0
14+
github.com/lrstanley/bubblezone v1.0.0
1315
github.com/mattn/go-runewidth v0.0.19
1416
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6
17+
github.com/spf13/cobra v1.9.1
18+
golang.org/x/term v0.39.0
1519
)
1620

1721
require (
22+
github.com/Masterminds/semver/v3 v3.2.1 // indirect
23+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
1824
github.com/charmbracelet/colorprofile v0.4.1 // indirect
1925
github.com/charmbracelet/ultraviolet v0.0.0-20251212194010-b927aa605560 // indirect
26+
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
2027
github.com/charmbracelet/x/term v0.2.2 // indirect
2128
github.com/charmbracelet/x/termios v0.1.1 // indirect
2229
github.com/charmbracelet/x/windows v0.2.2 // indirect
2330
github.com/clipperhouse/displaywidth v0.6.1 // indirect
2431
github.com/clipperhouse/stringish v0.1.1 // indirect
2532
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
33+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
34+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2635
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
2736
github.com/muesli/cancelreader v0.2.2 // indirect
2837
github.com/rivo/uniseg v0.4.7 // indirect
38+
github.com/spf13/pflag v1.0.6 // indirect
2939
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
3040
golang.org/x/sync v0.18.0 // indirect
31-
golang.org/x/sys v0.38.0 // indirect
41+
golang.org/x/sys v0.40.0 // indirect
42+
golang.org/x/text v0.24.0 // indirect
3243
)

go.sum

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251216153312-819e2e89c62e h1:tXwTmgGpwZT
44
charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251216153312-819e2e89c62e/go.mod h1:pDM18flq3Z4njKZPA3zCvyVSSIJbMcoqlE82BdGUtL8=
55
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251205162909-7869489d8971 h1:xZFcNsJMiIDbFtWRyDmkKNk1sjojfaom4Zoe0cyH/8c=
66
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251205162909-7869489d8971/go.mod h1:i61Y3FmdbcBNSKa+pKB3DaE4uVQmBLMs/xlvRyHcXAE=
7+
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
8+
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
79
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
810
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
911
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
1012
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
13+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
14+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
1115
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
1216
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
1317
github.com/charmbracelet/ultraviolet v0.0.0-20251212194010-b927aa605560 h1:j3PW2hypGoPKBy3ooKzW0TFxaxhyHK3NbkLLn4KeRFc=
1418
github.com/charmbracelet/ultraviolet v0.0.0-20251212194010-b927aa605560/go.mod h1:VWATWLRwYP06VYCEur7FsNR2B1xAo7Y+xl1PTbd1ePc=
1519
github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
1620
github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
21+
github.com/charmbracelet/x/cellbuf v0.0.14 h1:lfjq/R7q0YSSU3XLvB0BkLWRxE9bKVKZ6n9HaAeFR24=
22+
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:5//lJr5H8Z1kP1BC93BOGYXuGvIV3qjQXOGGpATT2s0=
1723
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
1824
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
1925
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
@@ -28,12 +34,21 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa
2834
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
2935
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
3036
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
37+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
3138
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
3239
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
3340
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
3441
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
42+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
43+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
3544
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
3645
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
46+
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
47+
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
48+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
49+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
50+
github.com/lrstanley/bubblezone v1.0.0 h1:bIpUaBilD42rAQwlg/4u5aTqVAt6DSRKYZuSdmkr8UA=
51+
github.com/lrstanley/bubblezone v1.0.0/go.mod h1:kcTekA8HE/0Ll2bWzqHlhA2c513KDNLW7uDfDP4Mly8=
3752
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
3853
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
3954
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
@@ -44,11 +59,24 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
4459
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
4560
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
4661
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
62+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
63+
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
64+
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
65+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
66+
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
4767
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
4868
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
4969
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
5070
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
5171
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
5272
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
53-
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
54-
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
73+
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
74+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
75+
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
76+
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
77+
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
78+
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
79+
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
80+
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
81+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
82+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/app/app.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const (
3434
DialogDeleteWorkspace = "delete_workspace"
3535
DialogRemoveProject = "remove_project"
3636
DialogSelectAssistant = "select_assistant"
37+
DialogSelectRuntime = "select_runtime"
38+
DialogSandboxAPIKey = "sandbox_api_key"
3739
DialogQuit = "quit"
3840
DialogCleanupTmux = "cleanup_tmux"
3941
)
@@ -96,7 +98,9 @@ type App struct {
9698
dialogWorkspace *data.Workspace
9799

98100
// Process management
99-
scripts *process.ScriptRunner
101+
scripts *process.ScriptRunner
102+
sandboxManager *SandboxManager
103+
runtimeProvider *RuntimeAgentProvider
100104

101105
// Git status management
102106
statusManager *git.StatusManager
@@ -238,6 +242,8 @@ func New(version, commit, date string) (*App, error) {
238242
registry := data.NewRegistry(cfg.Paths.RegistryPath)
239243
workspaces := data.NewWorkspaceStore(cfg.Paths.MetadataRoot)
240244
scripts := process.NewScriptRunner(cfg.PortStart, cfg.PortRangeSize)
245+
sandboxManager := NewSandboxManager(cfg)
246+
runtimeProvider := NewRuntimeAgentProvider(cfg, sandboxManager)
241247

242248
// Create status manager (callback will be nil, we use it for caching only)
243249
statusManager := git.NewStatusManager(nil)
@@ -264,13 +270,15 @@ func New(version, commit, date string) (*App, error) {
264270
registry: registry,
265271
workspaces: workspaces,
266272
scripts: scripts,
273+
sandboxManager: sandboxManager,
274+
runtimeProvider: runtimeProvider,
267275
statusManager: statusManager,
268276
fileWatcher: fileWatcher,
269277
fileWatcherCh: fileWatcherCh,
270278
fileWatcherErr: fileWatcherErr,
271279
layout: layout.NewManager(),
272280
dashboard: dashboard.New(),
273-
center: center.New(cfg),
281+
center: center.New(cfg, runtimeProvider),
274282
sidebar: sidebar.NewTabbedSidebar(),
275283
sidebarTerminal: sidebar.NewTerminalModel(),
276284
helpOverlay: common.NewHelpOverlay(),
@@ -307,6 +315,7 @@ func New(version, commit, date string) (*App, error) {
307315
app.center.SetStyles(app.styles)
308316
app.toast.SetStyles(app.styles)
309317
app.helpOverlay.SetStyles(app.styles)
318+
app.sidebarTerminal.SetTerminalFactory(runtimeProvider.CreateTerminalForWorkspace)
310319
app.setKeymapHintsEnabled(cfg.UI.ShowKeymapHints)
311320
// Propagate tmux config to components
312321
app.center.SetTmuxConfig(tmuxOpts.ServerName, tmuxOpts.ConfigPath)

internal/app/harness.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func NewHarness(opts HarnessOptions) (*Harness, error) {
8888
}
8989

9090
func newMonitorHarness(cfg *config.Config, opts HarnessOptions) *Harness {
91-
centerModel := center.New(cfg)
91+
centerModel := center.New(cfg, nil)
9292
centerModel.SetShowKeymapHints(opts.ShowKeymapHints)
9393
centerModel.SetMonitorMode(true)
9494

@@ -147,7 +147,7 @@ func newMonitorHarness(cfg *config.Config, opts HarnessOptions) *Harness {
147147
}
148148

149149
func newCenterHarness(cfg *config.Config, opts HarnessOptions) *Harness {
150-
centerModel := center.New(cfg)
150+
centerModel := center.New(cfg, nil)
151151
centerModel.SetShowKeymapHints(opts.ShowKeymapHints)
152152

153153
dash := dashboard.New()
@@ -220,7 +220,7 @@ func newCenterHarness(cfg *config.Config, opts HarnessOptions) *Harness {
220220
}
221221

222222
func newSidebarHarness(cfg *config.Config, opts HarnessOptions) *Harness {
223-
centerModel := center.New(cfg)
223+
centerModel := center.New(cfg, nil)
224224
centerModel.SetShowKeymapHints(opts.ShowKeymapHints)
225225

226226
dash := dashboard.New()

internal/app/keybindings.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type KeyMap struct {
2424
NewTerminalTab key.Binding
2525
Help key.Binding
2626
Monitor key.Binding
27+
CopyMode key.Binding
2728

2829
// Dashboard
2930
Enter key.Binding

internal/app/runtime_provider.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/andyrewlee/amux/internal/config"
8+
"github.com/andyrewlee/amux/internal/data"
9+
"github.com/andyrewlee/amux/internal/pty"
10+
"github.com/andyrewlee/amux/internal/tmux"
11+
)
12+
13+
// RuntimeAgentProvider routes agent creation based on workspace runtime.
14+
type RuntimeAgentProvider struct {
15+
local *pty.AgentManager
16+
sandbox *SandboxManager
17+
}
18+
19+
func NewRuntimeAgentProvider(cfg *config.Config, sandboxManager *SandboxManager) *RuntimeAgentProvider {
20+
return &RuntimeAgentProvider{
21+
local: pty.NewAgentManager(cfg),
22+
sandbox: sandboxManager,
23+
}
24+
}
25+
26+
func (p *RuntimeAgentProvider) CreateAgent(wt *data.Workspace, agentType pty.AgentType, rows, cols uint16) (*pty.Agent, error) {
27+
if wt != nil && data.NormalizeRuntime(wt.Runtime) == data.RuntimeCloudSandbox {
28+
return p.sandbox.CreateAgent(wt, agentType, rows, cols)
29+
}
30+
return p.local.CreateAgent(wt, agentType, "", rows, cols)
31+
}
32+
33+
func (p *RuntimeAgentProvider) CreateAgentWithTags(wt *data.Workspace, agentType pty.AgentType, sessionName string, rows, cols uint16, tags tmux.SessionTags) (*pty.Agent, error) {
34+
if wt != nil && data.NormalizeRuntime(wt.Runtime) == data.RuntimeCloudSandbox {
35+
return p.sandbox.CreateAgent(wt, agentType, rows, cols)
36+
}
37+
return p.local.CreateAgentWithTags(wt, agentType, sessionName, rows, cols, tags)
38+
}
39+
40+
func (p *RuntimeAgentProvider) CreateViewer(wt *data.Workspace, command string, rows, cols uint16) (*pty.Agent, error) {
41+
if wt != nil && data.NormalizeRuntime(wt.Runtime) == data.RuntimeCloudSandbox {
42+
return p.sandbox.CreateViewer(wt, command, rows, cols)
43+
}
44+
return p.local.CreateViewer(wt, command, "", rows, cols)
45+
}
46+
47+
func (p *RuntimeAgentProvider) CreateViewerWithTags(wt *data.Workspace, command string, sessionName string, rows, cols uint16, tags tmux.SessionTags) (*pty.Agent, error) {
48+
if wt != nil && data.NormalizeRuntime(wt.Runtime) == data.RuntimeCloudSandbox {
49+
return p.sandbox.CreateViewer(wt, command, rows, cols)
50+
}
51+
return p.local.CreateViewerWithTags(wt, command, sessionName, rows, cols, tags)
52+
}
53+
54+
func (p *RuntimeAgentProvider) CloseAgent(agent *pty.Agent) error {
55+
if agent == nil {
56+
return nil
57+
}
58+
// Local manager will remove it from its list; sandbox manager doesn't track agents.
59+
if agent.Workspace != nil && data.NormalizeRuntime(agent.Workspace.Runtime) == data.RuntimeCloudSandbox {
60+
if agent.Terminal != nil {
61+
return agent.Terminal.Close()
62+
}
63+
return nil
64+
}
65+
return p.local.CloseAgent(agent)
66+
}
67+
68+
func (p *RuntimeAgentProvider) CloseAll() {
69+
p.local.CloseAll()
70+
}
71+
72+
// CreateTerminalForWorkspace returns a shell terminal based on runtime.
73+
func (p *RuntimeAgentProvider) CreateTerminalForWorkspace(wt *data.Workspace) (*pty.Terminal, error) {
74+
if wt == nil {
75+
return nil, fmt.Errorf("workspace is required")
76+
}
77+
if data.NormalizeRuntime(wt.Runtime) == data.RuntimeCloudSandbox {
78+
return p.sandbox.CreateShell(wt)
79+
}
80+
81+
shell := os.Getenv("SHELL")
82+
if shell == "" {
83+
shell = "/bin/bash"
84+
}
85+
return pty.New(shell, wt.Root, nil)
86+
}

0 commit comments

Comments
 (0)