Skip to content

Commit 5b76f89

Browse files
authored
feat(agentos): add inspector Terminal tab with server-side shell replay (#1895)
1 parent 55b2296 commit 5b76f89

21 files changed

Lines changed: 1705 additions & 80 deletions

File tree

docker/dev/Dockerfile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# syntax=docker/dockerfile:1.10.0
2+
#
3+
# Interactive dev container for agentOS. Unlike docker/build/*.Dockerfile, this
4+
# image contains no repo source: the working tree is bind-mounted at /build and
5+
# every build output lands in named volumes, so host edits are visible
6+
# immediately and rebuilds stay incremental.
7+
#
8+
# The deno sysroot / LTO dance from the release build is deliberately omitted —
9+
# dev binaries only need to run in this container, not on old glibc.
10+
FROM ubuntu:24.04
11+
12+
ARG RUST_TOOLCHAIN=1.91.1
13+
ARG NODE_MAJOR=22
14+
ARG PNPM_VERSION=10.13.1
15+
ARG JUST_VERSION=1.36.0
16+
17+
ENV DEBIAN_FRONTEND=noninteractive \
18+
CARGO_HOME=/usr/local/cargo \
19+
RUSTUP_HOME=/usr/local/rustup \
20+
PNPM_HOME=/usr/local/pnpm \
21+
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
22+
PATH=/usr/local/cargo/bin:/usr/local/pnpm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
23+
24+
RUN set -eux; \
25+
apt-get update; \
26+
apt-get install -y --no-install-recommends \
27+
ca-certificates curl git make cmake ninja-build build-essential \
28+
pkg-config libssl-dev xz-utils unzip jq python3 python3-venv \
29+
clang lld binaryen file procps less vim-tiny; \
30+
rm -rf /var/lib/apt/lists/*
31+
32+
# Node + pnpm
33+
RUN set -eux; \
34+
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -; \
35+
apt-get install -y --no-install-recommends nodejs; \
36+
corepack enable; \
37+
corepack prepare "pnpm@${PNPM_VERSION}" --activate; \
38+
rm -rf /var/lib/apt/lists/*
39+
40+
# Rust: host target for the sidecar, wasm32-wasip1 + rust-src for the WASM
41+
# command toolchain (scripts/patch-std.sh rebuilds std from source).
42+
RUN set -eux; \
43+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
44+
| sh -s -- -y --default-toolchain "${RUST_TOOLCHAIN}" --profile minimal --no-modify-path; \
45+
rustup target add wasm32-wasip1; \
46+
rustup component add rust-src clippy rustfmt
47+
48+
RUN set -eux; \
49+
arch="$(uname -m)"; \
50+
case "$arch" in \
51+
x86_64) just_arch=x86_64-unknown-linux-musl ;; \
52+
aarch64) just_arch=aarch64-unknown-linux-musl ;; \
53+
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
54+
esac; \
55+
curl -fsSL "https://github.com/casey/just/releases/download/${JUST_VERSION}/just-${JUST_VERSION}-${just_arch}.tar.gz" \
56+
| tar -xz -C /usr/local/bin just
57+
58+
WORKDIR /build
59+
60+
CMD ["sleep", "infinity"]

docker/dev/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Dev container
2+
3+
agentOS targets native Linux execution, so on macOS the engine, sidecar, and VMs
4+
run in this container while the working tree stays on the host. The repo is
5+
bind-mounted at `/build`; every build output lives in a named volume, so host
6+
edits are visible immediately and no darwin-native binary is ever dragged into
7+
Linux.
8+
9+
This is a dev environment. For release artifacts see `docker/build/`.
10+
11+
## First run
12+
13+
```bash
14+
just dev-up # build the image, start the container
15+
just dev-bootstrap # pnpm install + WASM command set + sidecar (slow, one time)
16+
```
17+
18+
`dev-bootstrap` builds the full default WASM tool set from source, which is the
19+
long pole. It only needs re-running when the toolchain or software sources
20+
change.
21+
22+
## Daily use
23+
24+
```bash
25+
just dev-terminal-example # engine on :6420, Vite on :5173
26+
just dev-shell # interactive shell in the container
27+
just dev-exec 'cargo check --workspace'
28+
just dev-build-tabs # rebuild the inspector custom-tab bundle
29+
just dev-down # stop
30+
```
31+
32+
Open <http://localhost:5173> for the example UI. The hosted Rivet dashboard runs
33+
in the host browser and points at <http://localhost:6420>, which serves the actor
34+
gateway, `/inspector/*`, and custom-tab assets.
35+
36+
## Layout
37+
38+
| Path | Backing | Why |
39+
| --- | --- | --- |
40+
| `/build` | bind mount | host edits visible immediately |
41+
| `/build/node_modules` | volume | host tree holds darwin binaries |
42+
| `/build/target` | volume | Linux cargo output, survives recreation |
43+
| `/build/toolchain/target`, `toolchain/c/*` | volumes | generated WASM/sysroot trees |
44+
| cargo registry/git, pnpm store | volumes | warm caches across rebuilds |
45+
46+
## Caveats
47+
48+
- **Run `pnpm install` inside the container, not on the host.** Both write
49+
package-level `node_modules` symlinks into the bind mount, and their
50+
platform-specific optional dependencies differ. If you need host typechecks,
51+
re-run `pnpm install` on the host afterwards.
52+
- DNS is pinned to 1.1.1.1/8.8.8.8. Docker Desktop's embedded resolver
53+
intermittently stops answering, which surfaces as an `EAI_AGAIN` storm during
54+
install.
55+
- `AGENTOS_SIDECAR_BIN` points at `/build/target/debug/agentos-sidecar`. Rebuild
56+
it with `just dev-exec 'cargo build -p agentos-sidecar'`.
57+
- The container publishes 6420 and 5173. Free them on the host first if another
58+
engine is already bound.

docker/dev/compose.yaml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: agentos-dev
2+
3+
services:
4+
dev:
5+
build:
6+
context: ../..
7+
dockerfile: docker/dev/Dockerfile
8+
working_dir: /build
9+
# Keep the container resident; drive it with `just dev-shell` / `docker compose exec`.
10+
command: ["sleep", "infinity"]
11+
stdin_open: true
12+
tty: true
13+
ports:
14+
# RivetKit engine: actor gateway + /inspector/* + custom-tab assets. The
15+
# hosted dashboard runs in the host browser and points at this port.
16+
- "6420:6420"
17+
# Vite dev server for examples/browser-terminal.
18+
- "5173:5173"
19+
# Docker Desktop's embedded resolver (192.168.65.7) intermittently stops
20+
# answering, which surfaces as EAI_AGAIN storms during `pnpm install`.
21+
dns:
22+
- 1.1.1.1
23+
- 8.8.8.8
24+
environment:
25+
# Unambiguous sidecar resolution; `just dev-bootstrap` builds this path.
26+
AGENTOS_SIDECAR_BIN: /build/target/debug/agentos-sidecar
27+
RUST_BACKTRACE: "1"
28+
# Vite/RivetKit must listen on all interfaces to be reachable from the host.
29+
HOST: 0.0.0.0
30+
# RivetKit binds the engine it spawns to loopback, which a published port
31+
# cannot reach. This is the supported override for that bind address
32+
# (rivetkit's `engineHost`); setting RIVET__GUARD__HOST does not work,
33+
# because RivetKit passes its own value to the engine process.
34+
RIVET_RUN_ENGINE_HOST: 0.0.0.0
35+
volumes:
36+
- ../..:/build:cached
37+
38+
# Host node_modules are darwin-native — mask every bind-mounted tree that
39+
# holds platform binaries or build output with a container-owned volume.
40+
- node_modules:/build/node_modules
41+
- cargo_target:/build/target
42+
- toolchain_target:/build/toolchain/target
43+
44+
# Only `toolchain/c/vendor` is safe to mount: it holds the large wasi-sdk
45+
# and llvm-project downloads and nothing deletes it. `sysroot`, `libs`,
46+
# `.cache`, and `toolchain/vendor` are `rm -rf`'d wholesale by the build,
47+
# which fails with EBUSY on a mount point — leave them on the bind mount.
48+
- toolchain_c_vendor:/build/toolchain/c/vendor
49+
50+
# Caches outside the working tree.
51+
- cargo_registry:/usr/local/cargo/registry
52+
- cargo_git:/usr/local/cargo/git
53+
- pnpm_store:/root/.local/share/pnpm/store
54+
55+
volumes:
56+
node_modules:
57+
cargo_target:
58+
cargo_registry:
59+
cargo_git:
60+
pnpm_store:
61+
toolchain_target:
62+
toolchain_c_vendor:

examples/browser-terminal/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ Override the web→server endpoint with `VITE_AGENTOS_ENDPOINT` (default
7171
- Software: `@agentos-software/common` (provides `sh` + coreutils) plus `git`,
7272
`curl`, `ripgrep`, `jq`, and `sqlite3`. Agent OS has no vim/editor package, so
7373
there is no in-VM editor.
74-
- The shipped actor has no `listShells` action and keeps no server-side
75-
scrollback, so reconnect re-adopts saved shell ids and resumes **live** output
76-
only (history from before the reload is not replayed). Stale ids (VM recreated)
77-
are dropped after a liveness probe.
74+
- This client predates the actor's `listShells` and `shellSnapshot` actions: it
75+
re-adopts saved shell ids on reconnect and resumes **live** output only, so
76+
history from before the reload is not replayed. Stale ids (VM recreated) are
77+
dropped after a liveness probe. The dashboard Terminal tab uses the
78+
server-owned list and server-rendered repaint instead.
7879
- The VM shell is line-buffered (it only echoes a line on Enter), so the client
7980
does **local echo + line editing** (printable chars, Backspace, Ctrl-C) and
8081
suppresses the shell's own echo of the submitted line to avoid double display.

justfile

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,65 @@ test-bounded cmd='pnpm test':
341341
342342
test-risky-probe *tests:
343343
./.agent/scripts/run-risky-test-probe.sh "$@"
344+
345+
# --- Dev container (docker/dev) ---
346+
#
347+
# agentOS runs Linux-only, so on macOS the engine, sidecar, and VMs live in a
348+
# container while the working tree stays on the host. The repo is bind-mounted
349+
# at /build; node_modules, cargo target, and the toolchain build trees are
350+
# container-owned volumes, so host edits are visible instantly without dragging
351+
# darwin-native binaries into Linux.
352+
#
353+
# First run: `just dev-up && just dev-bootstrap` (the bootstrap is slow — it
354+
# builds the WASM command set and the sidecar from source). After that,
355+
# `just dev-terminal-example` and edit TypeScript with hot reload.
356+
357+
dev-compose := "docker compose -f docker/dev/compose.yaml"
358+
359+
# Build the image and start the container.
360+
dev-up:
361+
{{dev-compose}} up -d --build
362+
363+
dev-down:
364+
{{dev-compose}} down
365+
366+
# Drop into a shell inside the dev container.
367+
dev-shell:
368+
{{dev-compose}} exec dev bash
369+
370+
# Run any command inside the dev container: `just dev-exec 'cargo check --workspace'`
371+
dev-exec cmd:
372+
{{dev-compose}} exec dev bash -lc "{{cmd}}"
373+
374+
# One-time (slow) build of everything the engine needs from source.
375+
dev-bootstrap:
376+
{{dev-compose}} exec dev bash -lc '\
377+
set -euo pipefail; \
378+
echo "==> pnpm install"; \
379+
pnpm install --frozen-lockfile; \
380+
echo "==> WASM command set (long)"; \
381+
just toolchain-build; \
382+
just toolchain-copy-commands; \
383+
echo "==> software packages"; \
384+
pnpm --filter "@agentos-software/*" \
385+
--filter "!@agentos-software/codex" \
386+
--filter "!@agentos-software/codex-cli" \
387+
--filter "!@agentos-software/everything" build; \
388+
echo "==> agentos-sidecar (debug)"; \
389+
cargo build -p agentos-sidecar; \
390+
echo "==> workspace TypeScript + inspector tab bundle"; \
391+
pnpm --filter @rivet-dev/agentos-core --filter @rivet-dev/agentos-runtime-core build; \
392+
pnpm --filter @rivet-dev/agentos build; \
393+
echo "==> done"'
394+
395+
# Serve examples/browser-terminal: engine on :6420, Vite on :5173.
396+
dev-terminal-example:
397+
{{dev-compose}} exec dev bash -lc '\
398+
cd examples/browser-terminal && \
399+
pnpm concurrently -k -n server,web -c blue,magenta \
400+
"tsx server.ts" \
401+
"vite --host 0.0.0.0"'
402+
403+
# Rebuild the inspector custom-tab bundle (run after editing src/inspector-tabs).
404+
dev-build-tabs:
405+
{{dev-compose}} exec dev bash -lc 'pnpm --filter @rivet-dev/agentos build:tabs'

packages/agentos/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"@agentos-software/common": "workspace:*",
6969
"@rivet-dev/agentos-core": "workspace:*",
7070
"@rivetkit/react": "catalog:rivetkit",
71+
"ghostty-web": "^0.4.0",
7172
"rivetkit": "catalog:rivetkit",
7273
"zod": "^4.1.11"
7374
},
@@ -85,9 +86,9 @@
8586
},
8687
"devDependencies": {
8788
"@agentos-software/coreutils": "workspace:*",
88-
"@rivet-dev/agentos-test-harness": "workspace:*",
8989
"@radix-ui/react-collapsible": "^1.1.2",
9090
"@radix-ui/react-scroll-area": "^1.2.2",
91+
"@rivet-dev/agentos-test-harness": "workspace:*",
9192
"@tanstack/react-query": "^5.87.1",
9293
"@types/node": "^22.19.15",
9394
"@types/react": "^19.0.0",

0 commit comments

Comments
 (0)