@lardexx/arenacode
v2.11.13
Published
Terminal client for ArenaCode: talks to an arena-bridge, streams the Agent Mode timeline, and runs local commands, file operations and computer automation on your PC (windows-use.dll on Windows, Xlib/XTest on Linux, osascript on macOS). Node port of the A
Maintainers
Readme
arenacode
Terminal client for ArenaCode — a full-screen TUI (or a plain CLI, when you prefer) that talks to an arena-bridge over HTTP, streams the Agent Mode timeline, and executes on your own machine the actions the agent asks for (terminal commands, file operations, plan approval, files the agent sends back).
It is a Node port of the ArenaCode 2.5.2 desktop client (ArenaCode-x64.exe): same wire protocol, same meta
payload, same local cards. The bridge is untouched — this is a client only, so it works with any existing
arena-bridge 2.5.x (Python) instance, local or exposed through a tunnl.gg URL.
you ──▶ arenacode CLI ──▶ arena-bridge ──▶ agent
(this package) (unchanged) (Arena, or any agent on agentctl)Install
npm install -g @lardexx/arenacode
arenacode --version # the version of the installed packageOr without installing:
npx @lardexx/arenacode # opens the interface (needs a terminal)Published as @lardexx/arenacode — the bare name arenacode
is blocked by npm's similarity check against the existing arena-code package, so the scope is required. The
command is plain arenacode in every case.
Requires Node.js 18 or newer. One runtime dependency, koffi (prebuilt FFI bindings for Windows, macOS and Linux,
no compiler needed): it is how the client reaches windows-use.dll on Windows and libX11/libXtst on Linux.
Terminal UI
The interface is a copy of opencode's TUI — same screen shape, same widgets, same keybinds —
driving this bridge instead of an LLM provider. Nothing was approximated from screenshots alone: the layout was
rebuilt from opencode 1.19.5's own source (packages/tui/src/component/prompt, routes/session, ui/dialog,
logo.tsx, theme/assets/*.json) and checked against frames captured from the installed binary in a pty.
arenacode # opens the TUI on a terminal; on the very first run it asks for URL + key firstThe client has one entry point: arenacode. There are no subcommands — setup, settings, Stop and the bridge
inspection all live inside the interface (the settings live behind /settings or the command palette). Anything else on the command line is refused
with command removed: <name> and exit code 2.
Home screen — two-tone block wordmark, all lowercase (single-storey a, stem-and-arm r), centred prompt, tips, and the folder you
started in at the bottom left:
▄
▄▀▀█ █▀▀█ █▀▀█ █▀▀▄ ▄▀▀█ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█ █ █ █▀▀▀ █ █ █ █ █ █ █ █ █ █▀▀▀
▀▀▀▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
┃
┃ Ask anything… "Read package.json and add a lint script"
┃
┃ Build · auto Arena
╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
tab agents ctrl+p commands
● Tip Press ctrl+p for every command
~/progetto v2.5.2Build · auto Arena is the agent, then the model (auto, white) and the provider (Arena, grey). The folder
line is simply where you launched arenacode from.
A session — user blocks on a ┃ border, assistant parts indented three cells, tool calls as $ command cards,
▣ Build · model · 1.2s when the turn ends, esc interrupt while it runs:
┃
┃ read hello.txt and run a command
┃
⠹ Thinking: Looking at the folder
┃
┃ → Reading file: hello.txt (23 B)
┃ hello from the project
┃
┃ $ echo from-pc (exit 0)
┃ from-pc
┃
▣ Build · arenacode · 0.4s
┃
┃ Ask anything… "Summarise what this project does"
┃
┃ Build · auto Arena
╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
tab agents ctrl+p commands
~/progetto 4cabe1 · v2.5.2Real frames captured from a pty, colours included — as rendered images
home · a session · the palette,
and as HTML snapshots home · session ·
palette · slash commands ·
confirm dialog · plan approval.
How the copy is built and tested: docs/TUI.md.
Keys (opencode's defaults, leader is ctrl+x):
| Key | Action |
|---|---|
| enter | send · shift+enter newline |
| esc | interrupt the turn · clear the input · quit |
| tab | switch mode: Build → Plan → Computer |
| /sessions (or ctrl+x l) | switch between sessions: every timeline is a slot, n opens a new one |
| ctrl+p | command palette (↑↓ select, enter run, esc close) |
| / | slash commands with an inline list above the prompt |
| @file | attach a file from the open folder (uploaded to uploads/) |
| @folder | inline the folder listing in the task |
| ctrl+v | paste: a clipboard image becomes images/clipboard-*.png and is attached |
| !command | run a command locally |
| ↑ / ↓ | prompt history (or move through the slash list) |
| wheel | scroll the transcript (three lines a notch) |
| pgup / pgdn (or ctrl+u / ctrl+j) | scroll a page; alt+↑ / alt+↓ a line |
| home / end | jump to the top / back to the bottom (when the prompt is empty) |
| ctrl+x then | q quit · n new session · l switch session · e expand the last diff · t theme · a Ask and Proceed / Proceed · d tool output · s stop |
| ctrl+c | interrupt, then quit |
The same four flows as the desktop client. The Windows app is the reference for how a task reaches the bridge, so the port reproduces it byte for byte:
- Paste an image.
ctrl+vreads the clipboard image, saves it as<folder>/images/clipboard-YYYYMMDD-HHMMSS.png(the desktop's naming), uploads it and lists it in the task, so the agent can open it withread. - Switch agent.
tabcycles the three modes of the desktop client — Build ([ArenaCode mode: agent]), Plan ([ArenaCode mode: plan]) and Computer ([ArenaCode mode: computer-agent]) — and the first line of every task is the desktop's own mode line, verbatim. - Send a message.
POST /taskcarriesinstruction,meta,agent_promptandproject_context, the same body the desktop client builds. - Mention something.
@fileis uploaded touploads/<timestamp>/and named in[attachments in the workspace: …];@folderis inlined as[folder contents … (n files, list only, not attached): …]; missing paths outside the folder are reported under[paths on the PC: …]. Messages follow the interface language (it,en,es).
The exact strings are checked by test/parity.js against the ones extracted from ArenaCode-x64.exe.
Computer Agent: mouse, keyboard and screen. The three modes' ui commands now run in the terminal client too,
from the same bridge request the desktop app answers:
| Command | What it does here |
|---|---|
| ui control | move, click, dblclick, rightclick, down, up, drag (smooth, 12 steps), scroll |
| ui type | clicks the field first when the task gives x,y, otherwise types into the focused field; the text is pasted, so accents and emoji survive |
| ui keys | ctrl+s, alt+f4, win, enter, f5, ctrl+shift+esc … |
| ui app | launch (Start menu on Windows, open -a on macOS, gtk-launch on Linux), switch, resize |
| ui shot | full-screen PNG, uploaded to the bridge workspace and returned as file (+ image base64 when small) |
| ui state | windows with position, size and process id, plus screen and system |
| ui apps | the Start menu (Windows), /Applications (macOS), .desktop entries (Linux) |
| ui multi-select, ui multi-edit | built from the primitives above |
Windows runs the same DLL the desktop client runs. windows-use.dll ships inside this package
(vendor/windows-use/windows-use.dll, extracted from ArenaCode-x64.exe's resources) and is called over FFI with
koffi — wu_call_json / wu_free / wu_version — so nothing has to be installed and nothing is
reimplemented: the UI Automation tree, SendInput unicode typing, extended keys for arrows/navigation, Start-menu apps,
annotated screenshots and fuzzy window matching are all the desktop's own code. The tools and argument names the client
sends are the ones in the DLL's symbol table (click_tool, move_tool, mouse_button_tool, scroll_tool, type_tool,
shortcut_tool, wait_tool, app_tool, apps_list_tool, desktop_state), and the answers are the same JSON the
desktop returns ({"ok":true,"result":…}, bounding_box, center, control_type, interactive_elements_text,
screenshot_png_base64 …). ARENACODE_WINDOWS_USE_DLL points the client at another copy; if the file is missing it is
carved out of an installed ArenaCode-x64.exe (ARENACODE_EXE) by the client's own PE resource reader.
Linux talks to the X server directly: libX11 + libXtst through the same FFI layer, no external tools — no
xdotool, no wmctrl, no scrot, no ImageMagick. The pointer is moved with XTestFakeMotionEvent, clicks and the wheel
are XTestFakeButtonEvent with buttons 1–5, keys are keysyms mapped to keycodes with XGetKeyboardMapping, characters
outside the keymap borrow an unused keycode with XChangeKeyboardMapping and give it back a moment later, windows come
from XQueryTree + XGetGeometry + XTranslateCoordinates + _NET_WM_NAME, focus from XSetInputFocus, and
screenshots are XGetImage + XGetPixel encoded to PNG by the client itself (zlib). This is what makes it testable:
node test/native.js runs it against a real X server (Xvfb is enough) — a window is created, the client types into it
and the X events the server delivered are compared with the requested keycodes, chords, buttons and pixels.
ui app launch still starts the program's own .desktop command; nothing else is spawned.
Wayland is a different backend (src/automation/wayland.js), picked automatically when XDG_SESSION_TYPE=wayland
and /dev/uinput is writable. XTest cannot reach a native Wayland client — the compositor has no such entry point — so
input goes through the kernel instead: a virtual pointer and keyboard are declared on /dev/uinput
(EV_KEY/EV_REL, UI_SET_*, UI_DEV_CREATE) and the events written there are the same ones libinput reads, which
means they work in every native Wayland window regardless of toolkit. Typing maps characters through
libxkbcommon (xkb_state_key_get_utf32), so the layout in use is respected and shifted characters are typed with
shift instead of guessed; if the library is missing a complete US map is used instead, and characters that the keymap
cannot produce are refused by name rather than typed as something else. Screenshots take the two routes that Wayland
allows: org.freedesktop.portal.Screenshot first (xdg-desktop-portal, so GNOME, KDE and wlroots all work; the desktop
asks for confirmation once) and the org.gnome.Shell.Screenshot D-Bus service afterwards, with the XWayland root window
as the last resort. Both D-Bus paths are tested for real against a private dbus-daemon and a stub of each service
(test/wayland.js), the uinput path by reading the injected events back from /dev/input/eventN — the same node
libinput opens.
Wayland hides things that X11 exposes, and the client says so instead of failing quietly: ui state lists the XWayland
windows and marks them as such (native windows cannot be enumerated by any client), ui app switch/resize are
refused because a client may not focus or resize another client's window, and an absolute ui move/ui click x y needs
the pointer position, which comes from XWayland — without it the answer is an explicit error rather than a click in the
wrong place. Wheel, keys, typing and clicking at the current position work everywhere.
To let your user open /dev/uinput, install the udev rule that ships in this package:
sudo cp dist/arenacode-uinput.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo usermod -aG input $USER # then log out and back inWithout it the client stays on the X11 backend, keeps working for XWayland windows and reports
native.degraded with the reason.
macOS is native too, again with no helper binaries: mouse with CGEventCreateMouseEvent + CGEventPost
(left/right/middle, double and triple click through the click-state field, drag interpolated), keyboard with
CGEventCreateKeyboardEvent plus modifier flags (cmd, ctrl, alt, shift) and Apple's fixed kVK_*
keycodes, typing through CGEventKeyboardSetUnicodeString so any character or emoji goes in without a layout,
windows from CGWindowListCopyWindowInfo (number, bounds, owner pid), screenshots from CGDisplayCreateImage
converted from BGRA to PNG by the client. brew install cliclick is gone. Only what CoreGraphics cannot do —
launch an app, bring a window to the front, resize it — still uses osascript, which ships with macOS. Grant the
terminal Accessibility (input) and Screen Recording (pixels) in System Settings → Privacy & Security.
On macOS/Linux the accessibility tree is not read: ui state returns windows and geometry as elements.
Ask and Proceed / Proceed. The desktop's Settings switch, ctrl+x a here (also /ask, /proceed,
--proceed, and the Settings panel). The default, Ask and Proceed, asks before commands,
file changes and plans; in Computer mode it asks once, on the plan, and after that the commands run on their own
while administrator commands still ask. Proceed approves its own plan, runs commands and installations and
writes files without asking: turning it on shows the desktop's own warning ("Pericolo: modalità Proceed").
--yes is the older switch and still answers yes to commands and files, but never to a plan.
Dialogs. Local actions open opencode-shaped dialogs — panel background, title on the left, esc on the right,
the selected row painted in the primary colour, hints underneath: Run once / Don't run for terminal commands,
Approve / Reject / Revise for the agent's plan, and file operations reported back to the bridge. --yes skips the
terminal confirmation, --confirm-writes adds one for writes and removals.
Themes. ctrl+x t cycles five palettes lifted from opencode's theme assets:
arenacode (opencode's peach accent), tokyonight, catppuccin, gruvbox, nord.
Quick start
# 1. cd into the folder you want the agent to work on
cd ~/progetto
# 2. open the client
arenacodeOn the first run the interface asks for the bridge URL and the API key (both are in the bridge's
runtime/connection.txt), saves them, and tests /health and /auth/check so you know the bridge accepts the
key before you send anything. The folder is never stored: arenacode works in the directory you start it from.
Later, /settings (or the command palette, ctrl+p) reopens them inside the interface.
Commands
| Command | What it does |
|---|---|
| arenacode | the interface: timeline, input box, cards, settings, Stop, bridge inspection |
| arenacode --help | the options below |
| arenacode --version | the version of the installed package |
Everything else — URL and API key (/settings), sessions (/sessions), the task queue on the bridge
(/queue), Stop, file uploads — is inside the interface. Up to 2.11.2 these were subcommands (init, config, ping,
doctor, ask, chat, ui, tasks, status, events, stop, files, cat, attach, handshake);
they were removed on request, and the terminal has to be interactive: in a pipe the client prints why and exits.
Options
| Flag | Meaning |
|---|---|
| --folder <dir> | override the working folder (default: the directory you are in) |
| --url <url> / --key <key> | override the saved bridge settings |
| --mode agent\|plan | task mode (default agent) |
| --lang it\|en\|es | language for prompts and cards (default it) |
| --no-pc | tell the agent pc_access: false (no terminal on this PC) |
| --yes | run local commands without asking |
| --confirm-writes | ask before write / replace / rm / mv in your folder |
| --attach a,b | upload files together with the task |
| --idle-timeout <s> | close the session after N idle seconds (default 180, like the desktop client) |
| --json | raw JSON output where available |
What runs on your machine
The agent never touches your PC directly: it asks, and this client shows a card. With --yes (or by pressing
r) the card is executed locally and the result goes back to the bridge.
| Card | Source | Executed here |
|---|---|---|
| terminal | POST /task/<id>/local-exec | your shell — zsh/bash/sh/fish on macOS and Linux, PowerShell/pwsh/cmd/WSL/Git Bash on Windows (see Shells) — asked before running unless --yes |
| files | local-fs | ls, stat, read, write, replace, find, mkdir, mv, rm, pull, git_status, git_log, git_diff, git_commit, git_revert, git_publish |
| plan | local-ui plan | approve / reject / ask for a revision, right in the terminal |
| file from the agent | local_file | downloaded from GET /file?raw=1 into the open folder |
| wait / sysinfo | local-ui | sleeps, or reports the local machine |
Every file path is resolved inside the open folder; paths that escape it are refused. rm refuses to delete
the folder itself. Local operations run with your user's permissions — --confirm-writes adds a confirmation for
mutating file operations, --yes skips confirmations for terminal commands.
Project context
Every folder gets its own log, context_<project>.md, next to the files you are working on. It is the desktop
client's file, with the desktop's own template (mined from ArenaCode-x64.exe):
# Contesto del progetto progetto-web
Sessione: 2026-11-02 21:45 (nome-del-bridge.tunnl.gg)
_Nessun task ancora._
<!-- agente:inizio -->
<!-- agente:fine -->
Aggiornato automaticamente da ArenaCode a fine di ogni job. La API key non è scritta qui.The title, the session label, the placeholder and the footer follow the interface language (it, en, es).
What happens when:
- The folder opens or a session starts → the client creates the file if it is missing, and adds a
Sessione:line for the new session. An existing file is never overwritten: the agent's block lives inside it. - At the end of every task the agent rewrites the block between
<!-- agente:inizio -->and<!-- agente:fine -->(date, task id, files changed, commits, decisions, what is still open, how it was checked — at most 40 lines). The client saves it at once, without Accept or Reject: the card only shows what changed. - The first task of a new bridge session carries the whole file as
project_context, so the bridge writes it toruntime/project_context.txtand tells the agent to read it in parts (agentctl.py context --part N). Later tasks do not repeat it — a new bridge (/health→started_at) or a different folder makes it travel again. The file is never summarised or truncated. - Commits never include it:
git add -Aruns, then the sheet is unstaged —git add -A, never context_<project>.md, as the desktop puts it.
test/context.js checks the template verbatim in the three languages, the "create once, never overwrite" rule,
the send-once-per-session rule and the git exclusion; the integration test checks that a real task creates the
sheet and that the bridge receives it.
File changes
A write or a replace is not just a line in the transcript: the card shows the diff, the way the desktop
client's <diff> widget does. Header with the file and the +n −m counts, then the unified rows — old line
number, new line number, the sign, the text — painted on the diff backgrounds of the active theme
(arenacode, tokyonight, catppuccin, gruvbox, nord, colours taken from opencode's own theme assets).
┃ ← Modifico il file: src/app.js +2 −1
┃ @@ -1,4 +1,5 @@
┃ 1 1 one
┃ 2 2 two
┃ 3 3 three
┃ 4 -four
┃ 4 +FOUR
┃ 5 +4.5- Hunks keep three lines of context; distant changes become separate hunks with a
⋯between them. - A new file is all additions, a deleted one all deletions; text that did not change draws no card at all.
- The diff is computed locally for the display and never travels to the bridge: the agent still receives the
same
{ ok, path, size }it always received. - A long change is drawn as 20 rows and then a
⋯ n more linesrow. That row is clickable: the card opens and shows every row, ending with− collapse diff(also clickable) to close it again. Without a mouse,ctrl+x edoes the same on the last diff of the session. ctrl+x dhides the blocks entirely (it toggles every card), and a binary file gets no diff.
Sessions
Every timeline in the interface is a session: its own transcript, its own task id, its own mode. /sessions
(or ctrl+x l) opens the list — the current one is marked with ● and its label is the first instruction you
typed in it; enter switches to the selected session, n (or the + new session row) starts a fresh one.
ctrl+x n is the shortcut for a new session.
The history is on disk, one file per session. The sessions live next to the config file — the same base
folder on the three systems, so a portable install (ARENACODE_CONFIG) carries its history with it:
| System | Directory |
|---|---|
| Windows | %APPDATA%\ArenaCode\sessions\<folder>-<hash>\ |
| macOS | ~/.arenacode/sessions/<folder>-<hash>/ |
| Linux | ~/.arenacode/sessions/<folder>-<hash>/ |
Inside that directory there is one JSON file per session plus a small index.json:
session-1.json the transcript of session 1: items, task id, mode, label, diffs
session-2.json the transcript of session 2 — writing session 1 never touches this
index.json the order of the sessions and which one was openOne file per session means a turn rewrites only its own transcript, and nothing is capped: no limit on the
number of sessions, on the entries of a session, on the length of a message or on the rows of a diff. Writes
are atomic (a temporary file in the same directory, then a rename; on Windows, where an indexer can hold the
file, it retries and then writes in place) and happen a moment after each change, on /quit, and on the way
out through EOF, Ctrl+C or a signal. The file name is sanitised so a project called a folder: <weird>? still
gives a legal name on Windows, and the folder carries a hash of the absolute path: two folders with the same
name never mix. A corrupted index.json is rebuilt from the files, a corrupted session-*.json is skipped and
the others still load; a turn that was running when the client closed comes back marked closed.
The bridge keeps the tasks themselves — /queue shows them with their state and id.
Shells
Terminal cards run in a real shell, and which one is not fixed:
| Platform | Shells |
|---|---|
| Windows | powershell (default), pwsh, cmd, wsl, bash (Git Bash when installed, otherwise the WSL bash) |
| macOS / Linux | zsh, bash, sh, fish, dash, ksh — or any absolute path |
The order is: the shell the bridge names in the local-exec request → the shell you pinned →
your login shell ($SHELL, e.g. zsh on macOS, bash on most Linux boxes; PowerShell on Windows).
Pin it with any of:
arenacode --shell zsh
/shell # in the TUI: pick from the shells installed on this machinesysinfo reports shell and the full shells list, and the !command prompt line in the TUI runs in the
same shell as the agent's cards. Commands are passed as a single argv entry (zsh -lc '<command>',
cmd.exe /d /s /c '<command>', powershell.exe -NoProfile -NonInteractive -Command '<command>'), so quotes,
$variables and pipes are never re-quoted on the way through. test/shell.js checks every mapping and runs
real commands through sh/bash.
How it talks to the bridge
Same requests as the desktop client (see docs/ANALYSIS.md for the reverse-engineered
details):
POST /task
{"instruction":"[ArenaCode mode: agent]\n…","wait":0,
"meta":{"client":"ArenaCode 2.5.2","os":"…","lang":"it","policy":"…","pc_access":true,
"git":{"available":true,"repo":true,"branch":"main"},
"workspace":{"root":"…","cwd":"…","shell":"…","pwsh7":false,"powershell":false,"wsl":false,
"active_file":"","selection":"","selection_line":0,"open_files":[],
"top_level":["src/","package.json"],"tools":{"git":true,"node":true}}},
"agent_prompt":"…", // the client's built-in prompt, sent with every task
"project_context":"…"} // content of context_<project>.md, when present
GET /task/<id>/events?format=json&since=<cursor>&wait=25 ← timeline (long-poll)
POST /task/<id>/local-result {"rid":"lx-…","output":"…","code":0}
POST /task/<id>/cancel ← Stop
GET /auth/check ← key valid on this PCHeaders: Authorization: Bearer <API key>, X-Arena-Device: <machine id>, Content-Type: application/json.
The device header is what binds an API key to one PC (403 device_mismatch if it is already bound elsewhere:
release it on the bridge with keys unbind <ID>).
Configuration
The file lives at:
- Windows —
%APPDATA%\ArenaCode\config.json - macOS / Linux —
~/.arenacode/config.json
{
"url": "https://nome-del-bridge.tunnl.gg",
"key": "arn_…",
"folder": "/home/me/progetto",
"lang": "it",
"mode": "agent",
"pc_access": true,
"device": "",
"vision": false,
"idle_timeout": 180,
"shell": ""
}The file is written with mode 0600. The desktop client stores the key encrypted with Windows DPAPI; a portable
Node client cannot, so the key sits in that file — keep it readable only by you, and use a dedicated API key
(arenacode keys on the bridge side: python3 bridge_server.py keys create "laptop CLI").
Environment variables override the file: ARENACODE_URL, ARENACODE_KEY, ARENACODE_FOLDER,
ARENACODE_SHELL, ARENACODE_CONFIG.
Troubleshooting
| Symptom | Fix |
|---|---|
| auth failed: unauthorized | wrong API key — /settings in the interface, paste the key from runtime/connection.txt |
| 403 device_mismatch | key bound to another PC — on the bridge: python3 bridge_server.py keys unbind <ID> |
| bridge unreachable | tunnel down or wrong URL — check /health on the bridge, then /settings in the interface |
| agent never answers | the agent is not listening: run bash listen.sh in the bridge folder |
| everything hangs after a turn | --idle-timeout closes the session; the task state is in the interface |
| Wayland: ui does nothing in native windows | /dev/uinput is not writable — install dist/arenacode-uinput.rules (see What runs on your machine) and log in again |
| Wayland: ui state shows no windows | only XWayland windows can be listed; open the app with its X11 backend or use ui app |
| Wayland: ui shot asks for confirmation | that is the desktop's own portal dialog — it appears once per session |
| Wayland: ui click 100 200 refuses | absolute positioning needs the XWayland pointer; use ui keys/ui scroll, or run the app under XWayland |
Development
npm test # CLI integration: real Python bridge + fake agent
SCENARIO=approve node test/tui.js # TUI, terminal card confirmation
SCENARIO=plan node test/tui.js # TUI, plan approval
node test/tui.js # TUI: drives the real pty, checks the rendered frames
node tools/frame2html.js /tmp/tui-frames.raw docs/tui-preview.html --match done # frame -> HTML snapshot
python3 tools/ansi2png.py /tmp/tui-frames.raw docs/tui-session.png --match done # frame -> PNG snapshot
BIN=arenacode tools/e2e-installed.sh # the *installed* package against a real bridgetools/e2e-installed.sh runs whatever arenacode is on PATH (or BIN=…) against a fresh bridge and a fake
agent, types a request into the TUI through a pty, and prints the agent's report plus the final screen — the check
that the published tarball, not just the working tree, drives the bridge.
The integration test spins up bridge_server.py from a private copy, runs a fake agent that claims the task,
then checks that the CLI answered the local-fs / local-exec cards with the right payloads, applied the file
write, rendered the timeline and bound the API key to the device.
Set BRIDGE_DIR to point at the bridge sources if they are not in /home/user/bridge_extract/bridge, and
VERBOSE=1 to see bridge logs.
License
MIT.
