@madarco/agentbox 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -0
- package/README.md +11 -8
- package/dist/{_cloud-attach-HJC672UR.js → _cloud-attach-R6TRWG5L.js} +4 -4
- package/dist/{chunk-QYRK5H6Q.js → chunk-43Q5GWP6.js} +108 -56
- package/dist/chunk-43Q5GWP6.js.map +1 -0
- package/dist/{chunk-ECLLV5JH.js → chunk-72CJTXN6.js} +156 -5
- package/dist/chunk-72CJTXN6.js.map +1 -0
- package/dist/{chunk-R5XIDQFR.js → chunk-BKU34KYY.js} +170 -6
- package/dist/chunk-BKU34KYY.js.map +1 -0
- package/dist/{chunk-4NQXNQ53.js → chunk-E7CHS7ZR.js} +168 -58
- package/dist/chunk-E7CHS7ZR.js.map +1 -0
- package/dist/chunk-MCOU6CZS.js +346 -0
- package/dist/chunk-MCOU6CZS.js.map +1 -0
- package/dist/{chunk-B4QG2MCW.js → chunk-MLMFNN4T.js} +762 -483
- package/dist/chunk-MLMFNN4T.js.map +1 -0
- package/dist/{chunk-2LF5YILI.js → chunk-RSKG7AFU.js} +80 -6
- package/dist/chunk-RSKG7AFU.js.map +1 -0
- package/dist/{chunk-SNTHHWKY.js → chunk-XKH7NTT7.js} +80 -22
- package/dist/chunk-XKH7NTT7.js.map +1 -0
- package/dist/{dist-7KVUIKJX.js → dist-AGTIA7AD.js} +37 -226
- package/dist/dist-AGTIA7AD.js.map +1 -0
- package/dist/{dist-OPIBZ7XM.js → dist-FIFEFKJ7.js} +14 -69
- package/dist/dist-FIFEFKJ7.js.map +1 -0
- package/dist/dist-JZ3XO6EB.js +662 -0
- package/dist/dist-JZ3XO6EB.js.map +1 -0
- package/dist/{dist-OG6NW6SM.js → dist-OGJGZETZ.js} +5 -3
- package/dist/{dist-JAN5VABY.js → dist-S4XR4ACV.js} +25 -177
- package/dist/dist-S4XR4ACV.js.map +1 -0
- package/dist/index.js +2229 -1314
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js → prepared-state-MQHD3M5F-Q27AZU53.js} +2 -2
- package/package.json +6 -4
- package/runtime/docker/Dockerfile.box +21 -26
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +67 -1
- package/runtime/docker/packages/ctl/dist/bin.cjs +361 -43
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +17 -6
- package/runtime/docker/packages/sandbox-docker/scripts/chromium-resolver +57 -0
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +2 -1
- package/runtime/e2b/agentbox-checkpoint-cleanup +52 -0
- package/runtime/e2b/agentbox-codex-hooks.json +68 -0
- package/runtime/e2b/agentbox-open +28 -0
- package/runtime/e2b/agentbox-setup-skill.md +263 -0
- package/runtime/e2b/agentbox-vnc-start +102 -0
- package/runtime/e2b/attach-helper.cjs +167 -0
- package/runtime/e2b/claude-managed-settings.json +116 -0
- package/runtime/e2b/ctl.cjs +24158 -0
- package/runtime/e2b/custom-system-CLAUDE.md +46 -0
- package/runtime/e2b/gh-shim +344 -0
- package/runtime/e2b/git-shim +131 -0
- package/runtime/e2b/scripts/build-template.sh +295 -0
- package/runtime/hetzner/agentbox-setup-skill.md +67 -1
- package/runtime/hetzner/agentbox-vnc-start +17 -6
- package/runtime/hetzner/claude-managed-settings.json +2 -1
- package/runtime/hetzner/ctl.cjs +361 -43
- package/runtime/relay/bin.cjs +380 -233
- package/runtime/vercel/agentbox-setup-skill.md +67 -1
- package/runtime/vercel/agentbox-vnc-start +17 -6
- package/runtime/vercel/claude-managed-settings.json +2 -1
- package/runtime/vercel/ctl.cjs +361 -43
- package/share/agentbox-setup/SKILL.md +67 -1
- package/share/host-skills/agentbox-info/SKILL.md +47 -35
- package/dist/chunk-2LF5YILI.js.map +0 -1
- package/dist/chunk-4NQXNQ53.js.map +0 -1
- package/dist/chunk-B4QG2MCW.js.map +0 -1
- package/dist/chunk-ECLLV5JH.js.map +0 -1
- package/dist/chunk-QYRK5H6Q.js.map +0 -1
- package/dist/chunk-R5XIDQFR.js.map +0 -1
- package/dist/chunk-SNTHHWKY.js.map +0 -1
- package/dist/dist-7KVUIKJX.js.map +0 -1
- package/dist/dist-JAN5VABY.js.map +0 -1
- package/dist/dist-OPIBZ7XM.js.map +0 -1
- /package/dist/{_cloud-attach-HJC672UR.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
- /package/dist/{dist-OG6NW6SM.js.map → dist-OGJGZETZ.js.map} +0 -0
- /package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js.map → prepared-state-MQHD3M5F-Q27AZU53.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,131 @@ Entries are generated from the commit history with `/release-notes` and then
|
|
|
9
9
|
hand-reviewed — they describe what changed for someone using the `agentbox`
|
|
10
10
|
CLI, not the raw commits.
|
|
11
11
|
|
|
12
|
+
## [0.15.0] - 2026-06-05
|
|
13
|
+
|
|
14
|
+
### Breaking
|
|
15
|
+
|
|
16
|
+
- Carry and `cp` now share a single size cap. The `AGENTBOX_CARRY_MAX_BYTES`
|
|
17
|
+
env var is removed; both the `carry:` step and `agentbox cp` are governed by
|
|
18
|
+
the `box.cpMaxBytes` config key (default 100 MiB, up from carry's old 50 MiB).
|
|
19
|
+
Scripts that set `AGENTBOX_CARRY_MAX_BYTES` no longer have any effect — set
|
|
20
|
+
`box.cpMaxBytes` instead.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `queue.openIn` config key: when a background `-i` job's box becomes ready,
|
|
25
|
+
optionally open an attached terminal onto it — `split`, `window`, or `tab`
|
|
26
|
+
(default `none`, the previous behavior). Fires only when you submit from
|
|
27
|
+
inside tmux, cmux, or iTerm2.
|
|
28
|
+
- `agentbox cp` (and the `carry:` copy step) now stream the tar instead of
|
|
29
|
+
buffering it, so copies are no longer capped by Node's buffer limit (large
|
|
30
|
+
folders that silently failed with "tar: Write error" now work). Added a
|
|
31
|
+
repeatable `--exclude=<glob|name>` and `--no-default-excludes`; heavy
|
|
32
|
+
regenerable dirs (`.git`, `node_modules`, `dist`, `.next`, `target`, …) are
|
|
33
|
+
excluded by default. Copies larger than `box.cpMaxBytes` are blocked with a
|
|
34
|
+
du-style tree of the biggest folders and a suggested strategy unless `--yes`.
|
|
35
|
+
- `agentbox agent approvals` / `agentbox agent approve`: inspect and answer
|
|
36
|
+
relay host-action confirmations (git push, `cp`, `gh` PR writes, checkpoint)
|
|
37
|
+
from a host orchestrator, instead of hand-curling the loopback endpoint.
|
|
38
|
+
Prompt ids are content-derived, so a prompt that changed since you listed it
|
|
39
|
+
is refused rather than mis-answered. Adds an opt-in per-box
|
|
40
|
+
`box.autoApproveHostActions` (default off, audited) for unattended runs.
|
|
41
|
+
- The attach footer's `(...)` slot now shows aggregate box service status —
|
|
42
|
+
`starting N/M…` while services boot, `service error` on a crash/failed task,
|
|
43
|
+
`ready` once all are up (probe-aware: a `ready_when` service counts as up only
|
|
44
|
+
once its probe passes, so the footer no longer flashes `ready` early). Boxes
|
|
45
|
+
with no services fall back to the agent activity label.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- `queue.openIn` under cmux: `split` and `tab` queued jobs now open in the
|
|
50
|
+
workspace you submitted from (split targets the original pane, falling back to
|
|
51
|
+
the parent workspace) instead of always spawning a new top-level workspace.
|
|
52
|
+
`window` still opens a separate workspace.
|
|
53
|
+
- `agentbox config set queue.openIn` now warns that the feature only fires
|
|
54
|
+
inside tmux/cmux/iTerm2, and that cmux additionally needs `socketControlMode`
|
|
55
|
+
set to `automation`/`password` plus `cmux reload-config`.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- The `carry:` block is now documented in the published `agentbox.yaml` JSON
|
|
60
|
+
schema, so editors and in-box agents that fetch the schema no longer see it as
|
|
61
|
+
invalid or undiscoverable.
|
|
62
|
+
- The stale default-checkpoint recreate prompt now fires for already-configured
|
|
63
|
+
projects too (it was skipped for them, silently booting old base layers), and
|
|
64
|
+
on recreate it reuses the existing `agentbox.yaml` instead of telling the agent
|
|
65
|
+
to regenerate a config that already exists.
|
|
66
|
+
- `agentbox cp` now enforces `box.cpMaxBytes` on single-file uploads, not just
|
|
67
|
+
directories.
|
|
68
|
+
- A supervisor screen-scrape safety net flips a stuck Claude `working` state to
|
|
69
|
+
`waiting` when its hooks miss a prompt (MCP dialogs, dropped notifications), so
|
|
70
|
+
`agent wait-for input-needed` reliably wakes.
|
|
71
|
+
|
|
72
|
+
## [0.14.0] - 2026-06-04
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- E2B as a fifth provider (`--provider e2b`): a Firecracker microVM per box with
|
|
77
|
+
public HTTPS preview URLs and free pause/resume. Unlike the other clouds, E2B
|
|
78
|
+
builds its base image directly from a Dockerfile — `agentbox prepare --provider
|
|
79
|
+
e2b` drives the build. Full lifecycle is supported: `agentbox e2b login`,
|
|
80
|
+
create, attach (`shell` / `claude` / `codex` / `opencode`), checkpoints, VNC,
|
|
81
|
+
and `agentbox prune --provider e2b`.
|
|
82
|
+
- `agentbox agent wait-for input-needed` — a single state that fires whenever the
|
|
83
|
+
agent needs you: the turn finished and the prompt is ready, or it's blocked on a
|
|
84
|
+
question, plan approval, permission prompt, or error. Replaces racing separate
|
|
85
|
+
`wait-for` calls that each hang to timeout, and prints the concrete state it
|
|
86
|
+
matched so callers can branch on why it woke.
|
|
87
|
+
- Cloud `<provider> login` now nudges you to run `agentbox prepare` when no base
|
|
88
|
+
has been baked, and `create` detects a stale cloud base (by content checksum)
|
|
89
|
+
and folds a rebuild prompt into the existing recreate wizard. Non-interactive
|
|
90
|
+
runs (`-y` / no TTY) warn and boot on the existing base rather than auto-baking.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- Cloud provider docs lead with how to use each provider, and the recommended
|
|
95
|
+
setup is the one-flow `agentbox install` wizard (login + base bake in one step).
|
|
96
|
+
- `create` / `claude` / `codex` / `opencode` no longer print the `log: <path>`
|
|
97
|
+
startup line; logs are still written and `~/.agentbox/logs/latest.log` still
|
|
98
|
+
tracks the latest run.
|
|
99
|
+
- Cloud attach shows a "starting <agent>" banner so a freshly attached cold cloud
|
|
100
|
+
box is never blank during cold-start, and credential seeding no longer corrupts
|
|
101
|
+
the create spinner.
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- Parallel boxes are now reliable: `~/.agentbox/state.json` is written atomically
|
|
106
|
+
under a cross-process lock, so concurrent `create` / `destroy` (the `-i` use
|
|
107
|
+
case) no longer lose records, wedge the queue counter, or leave boxes missing
|
|
108
|
+
from `agentbox list`. Concurrent creates get distinct project indices, and a
|
|
109
|
+
box is recorded as soon as its container starts so a mid-create failure is still
|
|
110
|
+
resolvable by `destroy` / `prune`.
|
|
111
|
+
- A box created from a checkpoint now gets a fresh per-box git branch and worktree
|
|
112
|
+
— previously all boxes from one checkpoint shared a branch and their `.git`
|
|
113
|
+
broke once the source box was destroyed (no diff, commit, or `/review`).
|
|
114
|
+
- `agentbox shell <box> -- <argv>` passes the post-`--` arguments verbatim instead
|
|
115
|
+
of re-parsing them through `bash -c`, fixing corrupted redirects and quoting
|
|
116
|
+
(e.g. `curl -w '%{http_code}'`). One-shot `shell -- cmd` against a cloud box no
|
|
117
|
+
longer hangs.
|
|
118
|
+
- Docker-based services in `agentbox.yaml` no longer race a not-yet-ready docker
|
|
119
|
+
socket: dockerd is launched and awaited before the in-box supervisor on every
|
|
120
|
+
create/restart across all DinD providers.
|
|
121
|
+
- Cloud boxes are seeded with working agent credentials and onboarding state, so a
|
|
122
|
+
fresh box lands at a ready prompt instead of a 401, a bypass-permissions accept
|
|
123
|
+
screen, or the first-run theme picker. Credential seeding is best-effort and
|
|
124
|
+
refreshes from the host before each create.
|
|
125
|
+
- A working box is no longer auto-paused (autopause now considers codex/opencode,
|
|
126
|
+
not just claude), and `agentbox drive` auto-unpauses a paused box before
|
|
127
|
+
attaching.
|
|
128
|
+
- Chromium is resolved lazily and shared with the project's own Playwright build,
|
|
129
|
+
fixing browser launches that hung waiting on a stale baked-in binary; the base
|
|
130
|
+
image is also smaller.
|
|
131
|
+
- Resync only flags an untracked-file conflict when the box and host content
|
|
132
|
+
actually differ, so byte-identical files no longer needlessly skip
|
|
133
|
+
`agentbox.yaml` services.
|
|
134
|
+
- A missing cloud base (skipped `agentbox prepare`) now reports a one-line
|
|
135
|
+
actionable error instead of a full stack trace.
|
|
136
|
+
|
|
12
137
|
## [0.13.0] - 2026-06-02
|
|
13
138
|
|
|
14
139
|
### Added
|
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
</h1>
|
|
7
7
|
|
|
8
8
|
Run multiple agents in parallel, with a single command, on your PC, self-hosted, or in the cloud
|
|
9
|
+
|
|
10
|
+
Works with [iterm2](https://agent-box.sh/docs/integrations-iterm2) - [cmux](https://agent-box.sh/docs/integrations-cmux) - [tmux](https://agent-box.sh/docs/integrations-tmux)
|
|
9
11
|
<br>
|
|
10
12
|
|
|
11
13
|
<p align="center">
|
|
@@ -79,12 +81,12 @@ Uses `portless` to give box web apps the same URL from inside the box and on the
|
|
|
79
81
|
|
|
80
82
|
## Cloud Providers
|
|
81
83
|
|
|
82
|
-
| | local docker | hetzner | daytona | vercel
|
|
83
|
-
| ------------------- | ------------------------- | ---------------------- | ------------------ | ------------------ |
|
|
84
|
-
| Support | ✅ | ✅ | ⚠️ Partial | ✅ |
|
|
85
|
-
| Base image | Dockerfile | Setup script (Ubuntu) | Dockerfile | Setup script |
|
|
86
|
-
| Live snapshots | ✅ | ✅ | 🧪 Experimental | ✅ |
|
|
87
|
-
| Private preview URLs| ✅ (portless or OrbStack) | ✅ (portless) | ✅ (native) | ✅ (native) |
|
|
84
|
+
| | local docker | hetzner | daytona | vercel | e2b |
|
|
85
|
+
| ------------------- | ------------------------- | ---------------------- | ------------------ | ------------------ | ------------------ |
|
|
86
|
+
| Support | ✅ | ✅ | ⚠️ Partial | ✅ | ✅ |
|
|
87
|
+
| Base image | Dockerfile | Setup script (Ubuntu) | Dockerfile | Setup script | Dockerfile (`Template.build`) |
|
|
88
|
+
| Live snapshots | ✅ | ✅ | 🧪 Experimental | ✅ | ✅ |
|
|
89
|
+
| Private preview URLs| ✅ (portless or OrbStack) | ✅ (portless) | ✅ (native) | ✅ (native) | ✅ (native) |
|
|
88
90
|
|
|
89
91
|
**Cloud setup** (optional — skip for local Docker)
|
|
90
92
|
|
|
@@ -92,7 +94,8 @@ Uses `portless` to give box web apps the same URL from inside the box and on the
|
|
|
92
94
|
- `agentbox vercel login` — interactive Vercel Sandbox token setup, saved to `~/.agentbox/secrets.env`
|
|
93
95
|
- `agentbox hetzner login` — interactive Hetzner Cloud token setup, saved to `~/.agentbox/secrets.env`
|
|
94
96
|
- `agentbox daytona login` — interactive Daytona API key setup, saved to `~/.agentbox/secrets.env`
|
|
95
|
-
- `agentbox
|
|
97
|
+
- `agentbox e2b login` — interactive E2B API key setup, saved to `~/.agentbox/secrets.env`
|
|
98
|
+
- `agentbox prepare [--provider daytona|hetzner|vercel|e2b]` — build the image and initial snapshot (e2b builds from a Dockerfile via `Template.build()`)
|
|
96
99
|
- `agentbox hetzner claude`, `agentbox hetzner codex`, `agentbox hetzner create`, etc.
|
|
97
100
|
|
|
98
101
|
## How to use
|
|
@@ -150,7 +153,7 @@ Full documentation lives at **[agent-box.sh/docs](https://agent-box.sh/docs)**:
|
|
|
150
153
|
- [Quickstart](https://agent-box.sh/docs) and [Core concepts](https://agent-box.sh/docs/core-concepts)
|
|
151
154
|
- [Teleport a project](https://agent-box.sh/docs/teleport-a-project), [Run an agent](https://agent-box.sh/docs/run-an-agent), [Access your box](https://agent-box.sh/docs/access-your-box)
|
|
152
155
|
- [Configuration](https://agent-box.sh/docs/configuration), [Services & tasks](https://agent-box.sh/docs/services-and-tasks), [Sync & git](https://agent-box.sh/docs/sync-and-git)
|
|
153
|
-
- Cloud providers: [Hetzner](https://agent-box.sh/docs/hetzner), [Daytona](https://agent-box.sh/docs/daytona), [Vercel](https://agent-box.sh/docs/vercel)
|
|
156
|
+
- Cloud providers: [Hetzner](https://agent-box.sh/docs/hetzner), [Daytona](https://agent-box.sh/docs/daytona), [Vercel](https://agent-box.sh/docs/vercel), [E2B](https://agent-box.sh/docs/e2b)
|
|
154
157
|
- Full [CLI reference](https://agent-box.sh/docs/cli)
|
|
155
158
|
|
|
156
159
|
## Development
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
buildCloudAttachInnerCommand,
|
|
4
4
|
cloudAgentAttach,
|
|
5
5
|
cloudAgentStartDetached
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-43Q5GWP6.js";
|
|
7
|
+
import "./chunk-MLMFNN4T.js";
|
|
8
|
+
import "./chunk-XKH7NTT7.js";
|
|
9
9
|
import "./chunk-G3H2L3O2.js";
|
|
10
10
|
export {
|
|
11
11
|
buildCloudAttachInnerCommand,
|
|
12
12
|
cloudAgentAttach,
|
|
13
13
|
cloudAgentStartDetached
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=_cloud-attach-
|
|
15
|
+
//# sourceMappingURL=_cloud-attach-R6TRWG5L.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_RELAY_PORT,
|
|
4
4
|
loadEffectiveConfig,
|
|
5
5
|
readBoxStatus
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MLMFNN4T.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/_cloud-attach.ts
|
|
9
9
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -13,31 +13,36 @@ import { join as join2 } from "path";
|
|
|
13
13
|
import { spinner } from "@clack/prompts";
|
|
14
14
|
|
|
15
15
|
// src/provider/registry.ts
|
|
16
|
-
var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
|
|
16
|
+
var KNOWN = ["docker", "daytona", "hetzner", "vercel", "e2b"];
|
|
17
17
|
function isKnownProvider(name) {
|
|
18
18
|
return KNOWN.includes(name);
|
|
19
19
|
}
|
|
20
20
|
async function getProvider(name) {
|
|
21
21
|
switch (name) {
|
|
22
22
|
case "docker": {
|
|
23
|
-
const mod = await import("./dist-
|
|
23
|
+
const mod = await import("./dist-OGJGZETZ.js");
|
|
24
24
|
return mod.dockerProvider;
|
|
25
25
|
}
|
|
26
26
|
case "daytona": {
|
|
27
|
-
const mod = await import("./dist-
|
|
27
|
+
const mod = await import("./dist-FIFEFKJ7.js");
|
|
28
28
|
await mod.ensureDaytonaCredentials();
|
|
29
29
|
return mod.daytonaProvider;
|
|
30
30
|
}
|
|
31
31
|
case "hetzner": {
|
|
32
|
-
const mod = await import("./dist-
|
|
32
|
+
const mod = await import("./dist-AGTIA7AD.js");
|
|
33
33
|
await mod.ensureHetznerCredentials();
|
|
34
34
|
return mod.hetznerProvider;
|
|
35
35
|
}
|
|
36
36
|
case "vercel": {
|
|
37
|
-
const mod = await import("./dist-
|
|
37
|
+
const mod = await import("./dist-S4XR4ACV.js");
|
|
38
38
|
await mod.ensureVercelCredentials();
|
|
39
39
|
return mod.vercelProvider;
|
|
40
40
|
}
|
|
41
|
+
case "e2b": {
|
|
42
|
+
const mod = await import("./dist-JZ3XO6EB.js");
|
|
43
|
+
await mod.ensureE2bCredentials();
|
|
44
|
+
return mod.e2bProvider;
|
|
45
|
+
}
|
|
41
46
|
default:
|
|
42
47
|
throw new Error(`unknown sandbox provider: ${String(name)}`);
|
|
43
48
|
}
|
|
@@ -59,6 +64,24 @@ async function providerForCreate(choice) {
|
|
|
59
64
|
// src/wrapped-pty/run.ts
|
|
60
65
|
import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
|
|
61
66
|
|
|
67
|
+
// src/wrapped-pty/service-status.ts
|
|
68
|
+
function serviceStatusLabel(status) {
|
|
69
|
+
if (!status) return null;
|
|
70
|
+
const services = status.services ?? [];
|
|
71
|
+
const tasks = status.tasks ?? [];
|
|
72
|
+
if (services.length === 0 && tasks.length === 0) return null;
|
|
73
|
+
const errored = services.some((s) => s.state === "crashed" || s.state === "unhealthy" || s.state === "backoff") || tasks.some((t) => t.state === "failed");
|
|
74
|
+
if (errored) return "service error";
|
|
75
|
+
const isUp = (s) => s.state === "ready" || s.state === "running" && !s.probed;
|
|
76
|
+
const counted = services.filter((s) => s.state !== "stopped");
|
|
77
|
+
const total = counted.length;
|
|
78
|
+
const up = counted.filter(isUp).length;
|
|
79
|
+
const settling = services.some((s) => s.state === "pending" || s.state === "waiting" || s.state === "starting") || services.some((s) => s.state === "running" && Boolean(s.probed)) || tasks.some((t) => t.state === "pending" || t.state === "waiting" || t.state === "running");
|
|
80
|
+
if (total === 0) return settling ? "starting\u2026" : null;
|
|
81
|
+
if (settling || up < total) return `starting ${String(up)}/${String(total)}\u2026`;
|
|
82
|
+
return "ready";
|
|
83
|
+
}
|
|
84
|
+
|
|
62
85
|
// src/pty/pty-backend.ts
|
|
63
86
|
async function loadPtyBackend() {
|
|
64
87
|
try {
|
|
@@ -110,16 +133,17 @@ async function spawnInNewTerminal(args) {
|
|
|
110
133
|
}
|
|
111
134
|
async function spawnInTmux(args) {
|
|
112
135
|
const cmdStr = shellJoin(args.argv);
|
|
136
|
+
const target = args.tmuxTarget ? ["-t", args.tmuxTarget] : [];
|
|
113
137
|
let tmuxArgv;
|
|
114
138
|
let noteKind;
|
|
115
139
|
if (args.mode === "split") {
|
|
116
|
-
tmuxArgv = ["split-window", "-h", "-c", args.cwd, "--", cmdStr];
|
|
140
|
+
tmuxArgv = ["split-window", "-h", ...target, "-c", args.cwd, "--", cmdStr];
|
|
117
141
|
noteKind = "tmux split";
|
|
118
142
|
} else {
|
|
119
|
-
tmuxArgv = ["new-window", "-n", args.title, "-c", args.cwd, "--", cmdStr];
|
|
143
|
+
tmuxArgv = ["new-window", ...target, "-n", args.title, "-c", args.cwd, "--", cmdStr];
|
|
120
144
|
noteKind = "tmux window";
|
|
121
145
|
}
|
|
122
|
-
const r = await runQuiet("tmux", tmuxArgv);
|
|
146
|
+
const r = await runQuiet("tmux", tmuxArgv, args.env);
|
|
123
147
|
if (r.code !== 0) {
|
|
124
148
|
return {
|
|
125
149
|
launched: false,
|
|
@@ -133,9 +157,54 @@ async function spawnInTmux(args) {
|
|
|
133
157
|
};
|
|
134
158
|
}
|
|
135
159
|
async function spawnInCmux(args) {
|
|
136
|
-
const bin = cmuxBinary();
|
|
137
|
-
if (args.mode === "window")
|
|
138
|
-
|
|
160
|
+
const bin = cmuxBinary(args.env);
|
|
161
|
+
if (args.mode === "window") return newCmuxWorkspace(bin, args);
|
|
162
|
+
const base = args.mode === "split" ? ["new-split", "right"] : ["new-surface"];
|
|
163
|
+
const noteKind = args.mode === "split" ? "cmux split" : "cmux tab";
|
|
164
|
+
const attempts = [];
|
|
165
|
+
if (args.mode === "split" && args.cmuxTargetSurface) {
|
|
166
|
+
attempts.push([...base, "--surface", args.cmuxTargetSurface, "--focus", "true"]);
|
|
167
|
+
}
|
|
168
|
+
if (args.cmuxTargetWorkspace) {
|
|
169
|
+
attempts.push([...base, "--workspace", args.cmuxTargetWorkspace, "--focus", "true"]);
|
|
170
|
+
}
|
|
171
|
+
if (!args.cmuxWorkspaceFallback) {
|
|
172
|
+
attempts.push([...base, "--focus", "true"]);
|
|
173
|
+
}
|
|
174
|
+
const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${shellJoin(args.argv)}`;
|
|
175
|
+
let lastError = "";
|
|
176
|
+
for (const createArgv of attempts) {
|
|
177
|
+
const created = await runQuiet(bin, createArgv, args.env);
|
|
178
|
+
if (created.code !== 0) {
|
|
179
|
+
lastError = `cmux ${createArgv.join(" ")} exited ${String(created.code)}: ${created.stderr.trim()}`;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const surfaceRef = parseCmuxRef(created.stdout);
|
|
183
|
+
if (!surfaceRef) {
|
|
184
|
+
return {
|
|
185
|
+
launched: false,
|
|
186
|
+
note: "",
|
|
187
|
+
error: `cmux ${createArgv[0]} gave no surface ref: ${created.stdout.trim()}`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const sent = await runQuiet(bin, ["send", "--surface", surfaceRef, `${cmdLine}
|
|
191
|
+
`], args.env);
|
|
192
|
+
if (sent.code !== 0) {
|
|
193
|
+
return {
|
|
194
|
+
launched: false,
|
|
195
|
+
note: "",
|
|
196
|
+
error: `cmux send exited ${String(sent.code)}: ${sent.stderr.trim()}`
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return { launched: true, note: `Attached in new ${noteKind}.` };
|
|
200
|
+
}
|
|
201
|
+
if (args.cmuxWorkspaceFallback) return newCmuxWorkspace(bin, args);
|
|
202
|
+
return { launched: false, note: "", error: lastError };
|
|
203
|
+
}
|
|
204
|
+
async function newCmuxWorkspace(bin, args) {
|
|
205
|
+
const r = await runQuiet(
|
|
206
|
+
bin,
|
|
207
|
+
[
|
|
139
208
|
"new-workspace",
|
|
140
209
|
"--name",
|
|
141
210
|
args.title,
|
|
@@ -145,45 +214,17 @@ async function spawnInCmux(args) {
|
|
|
145
214
|
shellJoin(args.argv),
|
|
146
215
|
"--focus",
|
|
147
216
|
"true"
|
|
148
|
-
]
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
note: "",
|
|
153
|
-
error: `cmux new-workspace exited ${String(r.code)}: ${r.stderr.trim()}`
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
return { launched: true, note: "Attached in new cmux workspace." };
|
|
157
|
-
}
|
|
158
|
-
const createArgv = args.mode === "split" ? ["new-split", "right", "--focus", "true"] : ["new-surface", "--focus", "true"];
|
|
159
|
-
const noteKind = args.mode === "split" ? "cmux split" : "cmux tab";
|
|
160
|
-
const created = await runQuiet(bin, createArgv);
|
|
161
|
-
if (created.code !== 0) {
|
|
162
|
-
return {
|
|
163
|
-
launched: false,
|
|
164
|
-
note: "",
|
|
165
|
-
error: `cmux ${createArgv[0]} exited ${String(created.code)}: ${created.stderr.trim()}`
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
const surfaceRef = parseCmuxRef(created.stdout);
|
|
169
|
-
if (!surfaceRef) {
|
|
170
|
-
return {
|
|
171
|
-
launched: false,
|
|
172
|
-
note: "",
|
|
173
|
-
error: `cmux ${createArgv[0]} gave no surface ref: ${created.stdout.trim()}`
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${shellJoin(args.argv)}`;
|
|
177
|
-
const sent = await runQuiet(bin, ["send", "--surface", surfaceRef, `${cmdLine}
|
|
178
|
-
`]);
|
|
179
|
-
if (sent.code !== 0) {
|
|
217
|
+
],
|
|
218
|
+
args.env
|
|
219
|
+
);
|
|
220
|
+
if (r.code !== 0) {
|
|
180
221
|
return {
|
|
181
222
|
launched: false,
|
|
182
223
|
note: "",
|
|
183
|
-
error: `cmux
|
|
224
|
+
error: `cmux new-workspace exited ${String(r.code)}: ${r.stderr.trim()}`
|
|
184
225
|
};
|
|
185
226
|
}
|
|
186
|
-
return { launched: true, note:
|
|
227
|
+
return { launched: true, note: "Attached in new cmux workspace." };
|
|
187
228
|
}
|
|
188
229
|
function parseCmuxRef(stdout) {
|
|
189
230
|
const m = stdout.match(/\b(?:surface|pane):\d+\b/);
|
|
@@ -237,9 +278,9 @@ async function spawnInITerm2(args) {
|
|
|
237
278
|
note: `Attached in new ${noteKind}.`
|
|
238
279
|
};
|
|
239
280
|
}
|
|
240
|
-
function runQuiet(cmd, argv) {
|
|
281
|
+
function runQuiet(cmd, argv, env) {
|
|
241
282
|
return new Promise((resolve) => {
|
|
242
|
-
const child = spawn(cmd, argv, { stdio: ["ignore", "pipe", "pipe"] });
|
|
283
|
+
const child = spawn(cmd, argv, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
243
284
|
let stdout = "";
|
|
244
285
|
let stderr = "";
|
|
245
286
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -956,7 +997,8 @@ function renderFooter(state, cols) {
|
|
|
956
997
|
};
|
|
957
998
|
const isClaude = state.mode === "claude";
|
|
958
999
|
const detachable = state.detachable ?? isClaude;
|
|
959
|
-
const
|
|
1000
|
+
const modeLabel = isClaude ? void 0 : state.mode === "shell" ? "shell" : state.mode;
|
|
1001
|
+
const stateLabel = state.boxServiceStatus ?? modeLabel;
|
|
960
1002
|
if (state.leaderActive) {
|
|
961
1003
|
const leaderHints = detachable ? DETACHABLE_LEADER_HINTS : PLAIN_LEADER_HINTS;
|
|
962
1004
|
return statusLine(sidebarBox, cols, stateLabel, leaderHints);
|
|
@@ -1318,11 +1360,12 @@ async function runWrappedAttach(opts) {
|
|
|
1318
1360
|
setTerminalTitle(lastEmittedTitle);
|
|
1319
1361
|
const detachable = opts.detachable ?? opts.mode === "claude";
|
|
1320
1362
|
let leaderActive = false;
|
|
1321
|
-
const buildIdle = (sessionTitle, claudeActivity) => ({
|
|
1363
|
+
const buildIdle = (sessionTitle, claudeActivity, boxServiceStatus) => ({
|
|
1322
1364
|
kind: "idle",
|
|
1323
1365
|
boxName: opts.boxName,
|
|
1324
1366
|
sessionTitle,
|
|
1325
1367
|
claudeActivity,
|
|
1368
|
+
boxServiceStatus,
|
|
1326
1369
|
mode: opts.mode,
|
|
1327
1370
|
detachable,
|
|
1328
1371
|
leaderActive
|
|
@@ -1330,6 +1373,7 @@ async function runWrappedAttach(opts) {
|
|
|
1330
1373
|
let footerState = buildIdle();
|
|
1331
1374
|
let lastSessionTitle;
|
|
1332
1375
|
let lastActivity;
|
|
1376
|
+
let lastServiceStatus;
|
|
1333
1377
|
let capturingPrompt = null;
|
|
1334
1378
|
let activeNotice = null;
|
|
1335
1379
|
let reconnectBanner = null;
|
|
@@ -1377,7 +1421,7 @@ async function runWrappedAttach(opts) {
|
|
|
1377
1421
|
} else if (flashMessage) {
|
|
1378
1422
|
footerState = { kind: "flash", message: flashMessage };
|
|
1379
1423
|
} else {
|
|
1380
|
-
footerState = buildIdle(lastSessionTitle, lastActivity);
|
|
1424
|
+
footerState = buildIdle(lastSessionTitle, lastActivity, lastServiceStatus);
|
|
1381
1425
|
}
|
|
1382
1426
|
};
|
|
1383
1427
|
const recomputeBand = () => {
|
|
@@ -1645,6 +1689,7 @@ async function runWrappedAttach(opts) {
|
|
|
1645
1689
|
const body = opts.mode === "codex" ? status?.codex : opts.mode === "opencode" ? status?.opencode : opts.mode === "shell" ? void 0 : status?.claude;
|
|
1646
1690
|
const nextTitle = body?.sessionTitle?.trim() || void 0;
|
|
1647
1691
|
const nextActivity = body?.state || void 0;
|
|
1692
|
+
const nextServiceStatus = serviceStatusLabel(status) ?? void 0;
|
|
1648
1693
|
if (cmuxOn && nextActivity !== lastActivity) {
|
|
1649
1694
|
applyCmuxAgentState(opts.mode, nextActivity);
|
|
1650
1695
|
if (isAttentionState(nextActivity) && !isAttentionState(lastActivity)) {
|
|
@@ -1662,9 +1707,11 @@ async function runWrappedAttach(opts) {
|
|
|
1662
1707
|
questionPayload = nextQuestion;
|
|
1663
1708
|
applyBandChange();
|
|
1664
1709
|
}
|
|
1665
|
-
if (nextTitle === lastSessionTitle && nextActivity === lastActivity)
|
|
1710
|
+
if (nextTitle === lastSessionTitle && nextActivity === lastActivity && nextServiceStatus === lastServiceStatus)
|
|
1711
|
+
return;
|
|
1666
1712
|
lastSessionTitle = nextTitle;
|
|
1667
1713
|
lastActivity = nextActivity;
|
|
1714
|
+
lastServiceStatus = nextServiceStatus;
|
|
1668
1715
|
if (footerState.kind === "idle") {
|
|
1669
1716
|
recomputeFooter();
|
|
1670
1717
|
redrawChrome();
|
|
@@ -1962,12 +2009,15 @@ function abortableSleep(ms, signal) {
|
|
|
1962
2009
|
);
|
|
1963
2010
|
});
|
|
1964
2011
|
}
|
|
2012
|
+
function agentStartBanner(binary) {
|
|
2013
|
+
return `printf " agentbox: starting ${binary} (first paint may take a few seconds)...\\r\\n"; `;
|
|
2014
|
+
}
|
|
1965
2015
|
function buildCloudAttachInnerCommand(binary, extraArgs) {
|
|
1966
2016
|
if (!extraArgs || extraArgs.length === 0) {
|
|
1967
|
-
return `bash -lc exec
|
|
2017
|
+
return `bash -lc '${agentStartBanner(binary)}exec ${binary}'`;
|
|
1968
2018
|
}
|
|
1969
2019
|
const blob = Buffer.from(extraArgs.join("\n"), "utf8").toString("base64");
|
|
1970
|
-
return `bash -lc 'mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
|
|
2020
|
+
return `bash -lc '${agentStartBanner(binary)}mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
|
|
1971
2021
|
}
|
|
1972
2022
|
async function cloudAgentAttach(args) {
|
|
1973
2023
|
const provider = await providerForBox(args.box);
|
|
@@ -2095,8 +2145,10 @@ export {
|
|
|
2095
2145
|
getProvider,
|
|
2096
2146
|
providerForBox,
|
|
2097
2147
|
providerForCreate,
|
|
2098
|
-
loadPtyBackend,
|
|
2099
2148
|
detectHostTerminal,
|
|
2149
|
+
cmuxBinary,
|
|
2150
|
+
spawnInNewTerminal,
|
|
2151
|
+
loadPtyBackend,
|
|
2100
2152
|
setTerminalTitle,
|
|
2101
2153
|
pushTerminalTitle,
|
|
2102
2154
|
popTerminalTitle,
|
|
@@ -2121,4 +2173,4 @@ export {
|
|
|
2121
2173
|
cloudAgentAttach,
|
|
2122
2174
|
cloudAgentStartDetached
|
|
2123
2175
|
};
|
|
2124
|
-
//# sourceMappingURL=chunk-
|
|
2176
|
+
//# sourceMappingURL=chunk-43Q5GWP6.js.map
|