@lardexx/arenacode 0.0.0-stage → 2.5.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 arenacode contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,289 @@
1
- # Temporary Holding Version
1
+ # arenacode
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Terminal client for **ArenaCode** — a full-screen TUI (or a plain CLI, when you prefer) that talks to an
4
+ [arena-bridge](https://github.com/) over HTTP, streams the Agent Mode timeline, and executes on your own machine
5
+ the actions the agent asks for (terminal commands, file operations, plan approval, files the agent sends back).
6
+
7
+ It is a Node port of the ArenaCode 2.5.2 desktop client (`ArenaCode-x64.exe`): same wire protocol, same `meta`
8
+ payload, same local cards. **The bridge is untouched** — this is a client only, so it works with any existing
9
+ `arena-bridge` 2.5.x (Python) instance, local or exposed through a tunnl.gg URL.
10
+
11
+ ```
12
+ you ──▶ arenacode CLI ──▶ arena-bridge ──▶ agent
13
+ (this package) (unchanged) (Arena, or any agent on agentctl)
14
+ ```
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install -g arenacode
20
+ arenacode --version # 2.5.2
21
+ ```
22
+
23
+ Or without installing:
24
+
25
+ ```bash
26
+ npx arenacode ask "list the files in my folder"
27
+ ```
28
+
29
+ Requires Node.js 18 or newer. No runtime dependencies.
30
+
31
+ ## Terminal UI
32
+
33
+ The interface is a copy of [opencode](https://opencode.ai)'s TUI — same screen shape, same widgets, same keybinds —
34
+ driving this bridge instead of an LLM provider. Nothing was approximated from screenshots alone: the layout was
35
+ rebuilt from opencode 1.19.5's own source (`packages/tui/src/component/prompt`, `routes/session`, `ui/dialog`,
36
+ `logo.tsx`, `theme/assets/*.json`) and checked against frames captured from the installed binary in a pty.
37
+
38
+ ```bash
39
+ arenacode # opens the TUI (the default when stdout is a terminal)
40
+ ```
41
+
42
+ Home screen — two-tone block wordmark, centred prompt, tips, folder and version in the footer:
43
+
44
+ ```
45
+ ▄
46
+ █▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀█ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
47
+ █ █ █▀▀▄ █▀▀▀ █ █ █ █ █ █ █ █ █ █▀▀▀
48
+ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀
49
+
50
+ /connect bridge offline at http://127.0.0.1:9
51
+ ┃
52
+ ┃ Ask anything… "Read package.json and add a lint script"
53
+ ┃
54
+ ┃ Build · arenacode bridge 2.5.2
55
+ ╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
56
+ tab agents ctrl+p commands
57
+
58
+ ● Tip Press ctrl+p for every command
59
+
60
+ ~/progetto v2.5.2
61
+ ```
62
+
63
+ A session — user blocks on a `┃` border, assistant parts indented three cells, tool calls as `$ command` cards,
64
+ `▣ Build · model · 1.2s` when the turn ends, `esc interrupt` while it runs:
65
+
66
+ ```
67
+ ┃
68
+ ┃ read hello.txt and run a command
69
+ ┃
70
+ ⠹ Thinking: Looking at the folder
71
+ ┃
72
+ ┃ → Reading file: hello.txt (23 B)
73
+ ┃ hello from the project
74
+ ┃
75
+ ┃ $ echo from-pc (exit 0)
76
+ ┃ from-pc
77
+ ┃
78
+ ▣ Build · arenacode · 0.4s
79
+ ┃
80
+ ┃ Ask anything… "Summarise what this project does"
81
+ ┃
82
+ ┃ Build · arenacode bridge 2.5.2
83
+ ╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
84
+ tab agents ctrl+p commands
85
+ ~/progetto 4cabe1 · v2.5.2
86
+ ```
87
+
88
+ Real frames captured from a pty, colours included — as rendered images
89
+ [home](docs/tui-home.png) · [a session](docs/tui-session.png) · [the palette](docs/tui-palette.png),
90
+ and as HTML snapshots [home](docs/tui-home.html) · [session](docs/tui-preview.html) ·
91
+ [palette](docs/tui-palette.html) · [slash commands](docs/tui-slash.html) ·
92
+ [confirm dialog](docs/tui-approve.html) · [plan approval](docs/tui-plan.html).
93
+ How the copy is built and tested: [`docs/TUI.md`](docs/TUI.md).
94
+
95
+ **Keys** (opencode's defaults, leader is `ctrl+x`):
96
+
97
+ | Key | Action |
98
+ |---|---|
99
+ | `enter` | send · `shift+enter` newline |
100
+ | `esc` | interrupt the turn · clear the input · quit |
101
+ | `tab` | switch agent ↔ plan |
102
+ | `ctrl+p` | command palette (`↑↓` select, `enter` run, `esc` close) |
103
+ | `/` | slash commands with an inline list above the prompt |
104
+ | `@file` | attach a file from the open folder |
105
+ | `!command` | run a command locally |
106
+ | `↑` / `↓` | prompt history (or move through the slash list) |
107
+ | `pgup` / `pgdn` | scroll the transcript |
108
+ | `ctrl+x` then | `q` quit · `n` new session · `l` task queue · `t` theme · `d` tool output · `b` sidebar · `s` stop |
109
+ | `ctrl+c` | interrupt, then quit |
110
+
111
+ **Dialogs.** Local actions open opencode-shaped dialogs — panel background, title on the left, `esc` on the right,
112
+ the selected row painted in the primary colour, hints underneath: `Run once / Don't run` for terminal commands,
113
+ `Approve / Reject / Revise` for the agent's plan, and file operations reported back to the bridge. `--yes` skips the
114
+ terminal confirmation, `--confirm-writes` adds one for writes and removals.
115
+
116
+ **Themes.** `ctrl+x t` cycles five palettes lifted from opencode's theme assets:
117
+ `arenacode` (opencode's peach accent), `tokyonight`, `catppuccin`, `gruvbox`, `nord`.
118
+
119
+ ## Quick start
120
+
121
+ ```bash
122
+ # 1. point the client at your bridge (URL + API key come from the bridge's runtime/connection.txt)
123
+ arenacode init
124
+
125
+ # 2. open the TUI in the folder you want the agent to work on
126
+ arenacode --folder ~/progetto
127
+
128
+ # 3. or stay in the plain CLI
129
+ arenacode ask "read package.json and add a lint script"
130
+ arenacode chat
131
+ ```
132
+
133
+ `init` saves the URL, the API key and the open folder, then tests `/health` and `/auth/check` so you know the
134
+ bridge accepts the key before you send anything.
135
+
136
+ ## Commands
137
+
138
+ | Command | What it does |
139
+ |---|---|
140
+ | `arenacode init` | configure URL, API key and open folder; test the connection |
141
+ | `arenacode config [show\|path\|set <k> <v>\|clear]` | read or change the saved configuration |
142
+ | `arenacode ping` | `/health` + `/auth/check`: version, public URL, queue, key |
143
+ | `arenacode doctor` | local checks: node, platform, folder, git, bridge, API key |
144
+ | `arenacode` (no args) | full-screen TUI — the default on a terminal |
145
+ | `arenacode ui` | full-screen TUI, explicitly |
146
+ | `arenacode ask "<instruction>"` | one task, streamed timeline, exit when it finishes |
147
+ | `arenacode chat` | interactive session (`/help` shows the in-session commands) |
148
+ | `arenacode stop [task-id]` | Stop: cancels the turn and the queued tasks on the bridge |
149
+ | `arenacode handshake` | vision/handshake check task, like the client's |
150
+
151
+ **Inspect the bridge**
152
+
153
+ | Command | What it does |
154
+ |---|---|
155
+ | `arenacode tasks [state]` | task queue (`all`, `claimable`, `pending`, `claimed`, `done`, `error`) |
156
+ | `arenacode status <task-id>` | state, claimant, result of one task |
157
+ | `arenacode events <task-id>` | replayed timeline of a task |
158
+ | `arenacode files [path]` | bridge workspace listing (`GET /fs`) |
159
+ | `arenacode cat <path>` | preview a text file in the bridge workspace (`GET /file`) |
160
+ | `arenacode attach <file…>` | upload files into the bridge workspace (`POST /upload`) |
161
+
162
+ **Options**
163
+
164
+ | Flag | Meaning |
165
+ |---|---|
166
+ | `--folder <dir>` | open folder: the project the agent reads and edits |
167
+ | `--url <url>` / `--key <key>` | override the saved bridge settings |
168
+ | `--mode agent\|plan` | task mode (default `agent`) |
169
+ | `--lang it\|en\|es` | language for prompts and cards (default `it`) |
170
+ | `--no-pc` | tell the agent `pc_access: false` (no terminal on this PC) |
171
+ | `--yes` | run local commands without asking |
172
+ | `--confirm-writes` | ask before `write` / `replace` / `rm` / `mv` in your folder |
173
+ | `--attach a,b` | upload files together with the task |
174
+ | `--idle-timeout <s>` | close the session after N idle seconds (default 180, like the desktop client) |
175
+ | `--json` | raw JSON output where available |
176
+
177
+ ## What runs on your machine
178
+
179
+ The agent never touches your PC directly: it asks, and this client shows a card. With `--yes` (or by pressing
180
+ `r`) the card is executed locally and the result goes back to the bridge.
181
+
182
+ | Card | Source | Executed here |
183
+ |---|---|---|
184
+ | **terminal** | `POST /task/<id>/local-exec` | PowerShell / `cmd` / WSL on Windows, `/bin/sh` elsewhere — asked before running unless `--yes` |
185
+ | **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` |
186
+ | **plan** | `local-ui` `plan` | approve / reject / ask for a revision, right in the terminal |
187
+ | **file from the agent** | `local_file` | downloaded from `GET /file?raw=1` into the open folder |
188
+ | **wait / sysinfo** | `local-ui` | sleeps, or reports the local machine |
189
+
190
+ Every file path is resolved **inside the open folder**; paths that escape it are refused. `rm` refuses to delete
191
+ the folder itself. Local operations run with your user's permissions — `--confirm-writes` adds a confirmation for
192
+ mutating file operations, `--yes` skips confirmations for terminal commands.
193
+
194
+ ### Not available in a terminal
195
+
196
+ The desktop client can drive the whole PC (mouse, keyboard, screenshots, app launching — `local-ui`
197
+ `control` / `type` / `keystroke` / `screenshot` / `app` / `state` / `apps` / `multi_*`). A terminal cannot, so
198
+ those cards are answered with an explicit error instead of silently doing nothing:
199
+
200
+ ```json
201
+ { "ok": false, "error": "operation \"screenshot\" is not available in the terminal client: the ArenaCode desktop client is required" }
202
+ ```
203
+
204
+ Everything else in the protocol is implemented.
205
+
206
+ ## How it talks to the bridge
207
+
208
+ Same requests as the desktop client (see [`docs/ANALYSIS.md`](docs/ANALYSIS.md) for the reverse-engineered
209
+ details):
210
+
211
+ ```
212
+ POST /task
213
+ {"instruction":"[ArenaCode mode: agent]\n…","wait":0,
214
+ "meta":{"client":"ArenaCode 2.5.2","os":"…","lang":"it","policy":"…","pc_access":true,
215
+ "git":{"available":true,"repo":true,"branch":"main"},
216
+ "workspace":{"root":"…","cwd":"…","shell":"…","pwsh7":false,"powershell":false,"wsl":false,
217
+ "active_file":"","selection":"","selection_line":0,"open_files":[],
218
+ "top_level":["src/","package.json"],"tools":{"git":true,"node":true}}},
219
+ "agent_prompt":"…", // the client's built-in prompt, sent with every task
220
+ "project_context":"…"} // content of context_<project>.md, when present
221
+
222
+ GET /task/<id>/events?format=json&since=<cursor>&wait=25 ← timeline (long-poll)
223
+ POST /task/<id>/local-result {"rid":"lx-…","output":"…","code":0}
224
+ POST /task/<id>/cancel ← Stop
225
+ GET /auth/check ← key valid on this PC
226
+ ```
227
+
228
+ Headers: `Authorization: Bearer <API key>`, `X-Arena-Device: <machine id>`, `Content-Type: application/json`.
229
+ The device header is what binds an API key to one PC (`403 device_mismatch` if it is already bound elsewhere:
230
+ release it on the bridge with `keys unbind <ID>`).
231
+
232
+ ## Configuration
233
+
234
+ `arenacode config path` prints the file:
235
+
236
+ * Windows — `%APPDATA%\ArenaCode\config.json`
237
+ * macOS / Linux — `~/.arenacode/config.json`
238
+
239
+ ```json
240
+ {
241
+ "url": "https://nome-del-bridge.tunnl.gg",
242
+ "key": "arn_…",
243
+ "folder": "/home/me/progetto",
244
+ "lang": "it",
245
+ "mode": "agent",
246
+ "pc_access": true,
247
+ "device": "",
248
+ "vision": false,
249
+ "idle_timeout": 180
250
+ }
251
+ ```
252
+
253
+ The file is written with mode `0600`. The desktop client stores the key encrypted with Windows DPAPI; a portable
254
+ Node client cannot, so the key sits in that file — keep it readable only by you, and use a dedicated API key
255
+ (`arenacode keys` on the bridge side: `python3 bridge_server.py keys create "laptop CLI"`).
256
+
257
+ Environment variables override the file: `ARENACODE_URL`, `ARENACODE_KEY`, `ARENACODE_FOLDER`,
258
+ `ARENACODE_CONFIG`.
259
+
260
+ ## Troubleshooting
261
+
262
+ | Symptom | Fix |
263
+ |---|---|
264
+ | `auth failed: unauthorized` | wrong API key — `arenacode config set key <key>` (from `runtime/connection.txt`) |
265
+ | `403 device_mismatch` | key bound to another PC — on the bridge: `python3 bridge_server.py keys unbind <ID>` |
266
+ | `bridge unreachable` | tunnel down or wrong URL — check `arenacode ping`, and `/health` on the bridge |
267
+ | agent never answers | the agent is not listening: run `bash listen.sh` in the bridge folder |
268
+ | everything hangs after a turn | `--idle-timeout` closes the session; check `arenacode status <id>` |
269
+
270
+ ## Development
271
+
272
+ ```bash
273
+ npm test # CLI integration: real Python bridge + fake agent
274
+ SCENARIO=approve node test/tui.js # TUI, terminal card confirmation
275
+ SCENARIO=plan node test/tui.js # TUI, plan approval
276
+ node test/tui.js # TUI: drives the real pty, checks the rendered frames
277
+ node tools/frame2html.js /tmp/tui-frames.raw docs/tui-preview.html --match done # frame -> HTML snapshot
278
+ ```
279
+
280
+ The integration test spins up `bridge_server.py` from a private copy, runs a fake agent that claims the task,
281
+ then checks that the CLI answered the `local-fs` / `local-exec` cards with the right payloads, applied the file
282
+ write, rendered the timeline and bound the API key to the device.
283
+
284
+ Set `BRIDGE_DIR` to point at the bridge sources if they are not in `/home/user/bridge_extract/bridge`, and
285
+ `VERBOSE=1` to see bridge logs.
286
+
287
+ ## License
288
+
289
+ MIT.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { main } = require('../src/cli');
5
+
6
+ // Piping into `head`/`less` closes stdout early: that is not an error.
7
+ process.stdout.on('error', (e) => {
8
+ if (e && e.code === 'EPIPE') process.exit(0);
9
+ throw e;
10
+ });
11
+
12
+ // Exit explicitly: undici keeps keep-alive sockets open, which would keep the
13
+ // process alive after the turn is finished.
14
+ main(process.argv.slice(2))
15
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
16
+ .catch((e) => {
17
+ process.stderr.write(`arenacode: ${e && e.stack ? e.stack : e}\n`);
18
+ process.exit(1);
19
+ });
@@ -0,0 +1,201 @@
1
+ # Reverse-engineering notes — `ArenaCode-x64.exe` (2.5.2)
2
+
3
+ What this document is: the evidence behind the port. Every claim about the desktop client's behaviour below is
4
+ backed by a static observation on the binary, the strings it embeds, or the payloads the bridge expects. Nothing
5
+ here was obtained by running the executable (it is a Windows GUI binary; the analysis host is Linux).
6
+
7
+ ## 1. Target
8
+
9
+ | | |
10
+ |---|---|
11
+ | File | `ArenaCode-x64.exe` (inside `arenacode-x64.zip`, 1.85 MB zip / 5 372 416 B exe) |
12
+ | SHA-256 | `1be348946c5c101a0122941c9c1b0c48b831138b8bbc98ac9c6eba66ba6e9c54` |
13
+ | Format | PE32+ executable, x86-64, 10 sections, stripped to external PDB |
14
+ | Toolchain | MinGW/GCC + MSVCRT (`__wgetmainargs`, `_beginthreadex`, `mingw` style imports), Win32 GUI subsystem |
15
+ | Manifest | `<assemblyIdentity version="2.5.2.0" … name="ArenaCode" type="win32"/>` |
16
+ | Entropy | 6.30 file-wide (max block 6.40) → **not packed, not encrypted** |
17
+ | TLS | 3 callbacks in `.text` (`0x140135c80`, `0x140135c50`, `0x140148ea0`) — normal MinGW runtime setup |
18
+
19
+ ## 2. How it was analysed
20
+
21
+ [REA](https://github.com/morluto/rea) 6.8.0 (`rea-agents` on npm), run with Node 22.23.4 — provider-free static
22
+ commands, since no Ghidra/Hopper/IDA instance was available for decompilation on this host:
23
+
24
+ ```bash
25
+ rea doctor # provider audit (no native engine present)
26
+ rea inspect-pe-imports ArenaCode-x64.exe --format json # ev_51542780…583e
27
+ rea inspect-byte-entropy ArenaCode-x64.exe --format json # ev_aca82405…eb14
28
+ rea inspect-pe-load-config ArenaCode-x64.exe --format json # ev_3d8aab65…1339
29
+ ```
30
+
31
+ Deep decompilation (`rea analyze`, `rea decompile`) failed closed with `provider_unavailable`: it needs
32
+ `GHIDRA_INSTALL_DIR` / Hopper / IDA. That is why the reconstruction below rests on imports, embedded wide
33
+ strings and the payload contract rather than on pseudocode. For a C/GUI client that is enough: the interesting
34
+ surface is the HTTP layer and the JSON it builds, and both are visible as literal format strings.
35
+
36
+ ## 3. What the binary is
37
+
38
+ A Win32 GUI client (USER32 + COMCTL32 + GDI32/GDI+ widgets, `GetOpenFileNameW`, `SHBrowseForFolderW`,
39
+ drag-and-drop) that
40
+
41
+ * keeps its state in `%APPDATA%\ArenaCode\` — `config.ini`, `sessions\`, `chats\`;
42
+ * talks HTTPS to an **arena-bridge** with **WinHTTP** (`WINHTTP.dll`: `WinHttpOpen`, `WinHttpOpenRequest`,
43
+ `WinHttpSendRequest`, `WinHttpReadData`, …) plus `WS2_32` for sockets;
44
+ * executes things locally with `CreateProcessW` (+ `PeekNamedPipe`, `CreatePipe`), `ShellExecuteW`,
45
+ `\\WindowsPowerShell\\v1.0\\powershell.exe`;
46
+ * protects the stored API key with **DPAPI**: `CRYPT32.dll!CryptProtectData` / `CryptUnprotectData`, stored as
47
+ `secret_dpapi`;
48
+ * identifies the machine with `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid` (`ADVAPI32` registry +
49
+ `SystemFunction036` = `RtlGenRandom`);
50
+ * carries a bundled CA store (curl's `ca-bundle.crt`) and a runtime-DLL loader (`LoadLibraryW`/`GetProcAddress`)
51
+ for the optional `windows-use` automation DLL — the binary contains the message *"windows-use DLL not
52
+ embedded in the program"*.
53
+
54
+ String evidence (wide strings, file offset ≈ `0x22b108` onward for the prompt):
55
+
56
+ ```
57
+ %s\%s\config.ini %APPDATA%\ArenaCode\config.ini
58
+ MachineGuid SOFTWARE\Microsoft\Cryptography
59
+ secret_dpapi sessions chats
60
+ User-Agent: ArenaCode/1.0 application/json
61
+ Content-Type: Authorization: Bearer X-Arena-Device:
62
+ ```
63
+
64
+ ## 4. HTTP protocol (as the client sends it)
65
+
66
+ | Method | Path | Body |
67
+ |---|---|---|
68
+ | POST | `/task` | `{"instruction":"…","wait":0,"meta":{…},"agent_prompt":"…","project_context":"…"}` |
69
+ | GET | `/task/<id>/events?format=json&since=<cursor>&wait=25` | — (long-poll timeline; fields `events`, `next`, `done`) |
70
+ | GET | `/task/<id>/events?since=0&follow=1` | — (SSE fallback, `Accept: text/event-stream`) |
71
+ | POST | `/task/<id>/local-result` | `{"rid":"…","output":"…","code":0}` |
72
+ | POST | `/task/<id>/local-state` | `{"allow":true\|false}` — terminal consent |
73
+ | POST | `/task/<id>/user-event` | `{"kind":"reject","path":"…"}` |
74
+ | POST | `/task/<id>/cancel` | `{}` — Stop |
75
+ | GET | `/task/<id>/ui-payload/<rid>` | — (text typed by a `local-ui type` step, delivered once) |
76
+ | GET | `/health`, `/auth/check` | — (version check, key valid on this PC) |
77
+ | GET | `/fs?path=`, `/file?path=&raw=1` | — (bridge workspace preview) |
78
+ | POST | `/upload?path=` | raw bytes (`application/octet-stream`) |
79
+
80
+ `meta`, exactly as the client serialises it:
81
+
82
+ ```json
83
+ {"client":"ArenaCode 2.5.2","os":"…","lang":"it",
84
+ "policy":"work_directly_on_pc: read and edit files in the open folder on the PC, also for existing projects; …",
85
+ "pc_access":true,
86
+ "git":{"available":true,"repo":true,"branch":"main"},
87
+ "workspace":{"root":"…","cwd":"…","shell":"…","pwsh7":false,"powershell":true,"wsl":false,
88
+ "active_file":"","selection":"","selection_line":0,
89
+ "open_files":[],"top_level":[],"tools":{}}}
90
+ ```
91
+
92
+ Task framing: `instruction` starts with a mode line — `[ArenaCode mode: agent]`, `[ArenaCode mode: plan]`,
93
+ `[ArenaCode mode: computer-agent]`, or `[ArenaCode handshake] …` — followed by the mode paragraph and the user's
94
+ text. Plan approval answers come back through `local-result` as
95
+ `{"ok":true,"approved":true}`, `{"ok":false,"approved":false}` or
96
+ `{"ok":false,"approved":false,"revise":true,"feedback":"…"}`.
97
+
98
+ ## 5. Embedded agent prompt
99
+
100
+ The client embeds its whole system prompt (33 147 characters, UTF-16LE at ≈`0x22b108`) and ships it with every
101
+ task as `agent_prompt`; the bridge writes it to `runtime/agent_instructions.txt` on the first task after a start.
102
+ The port sends the same text byte-for-byte: [`src/prompts/agent-prompt.md`](../src/prompts/agent-prompt.md)
103
+ (extracted with `strings -e l` + a UTF-16 decode around the marker `# ArenaCode agent: system prompt`).
104
+
105
+ That prompt doubles as protocol documentation from the agent's side: it lists the `agentctl` commands, the IDE
106
+ vs Computer Agent modes, the git rules, the vision handshake (`vision-png`,
107
+ `--has-vision-enabled:true|false`) and the "closing a task" contract (`context_<project>.md`).
108
+
109
+ ## 6. Local card types the client must execute
110
+
111
+ | Event field | Ops | Executed by |
112
+ |---|---|---|
113
+ | `local_exec` | `command`, `cwd`, `shell` = `powershell` \| `cmd` \| `wsl`, `admin` (UAC), `sandbox_reason` | `CreateProcessW` + pipes; card shows *"Command not run: rejected by the user"* (`code -1`) when declined |
114
+ | `local_fs` | `ls`, `stat`, `read`, `write`, `replace`, `mkdir`, `mv`, `rm`, `pull`, `find`, `git_status`, `git_log`, `git_diff`, `git_commit`, `git_revert`, `git_publish` | in-process file APIs + `git` CLI; the result is a JSON document returned as `output` |
115
+ | `local_ui` | `plan`, `control`, `type`, `keystroke`, `screenshot`, `app`, `state`, `sysinfo`, `apps`, `wait`, `multi_select`, `multi_edit` | GUI automation (`windows-use` DLL) + the Accept/Reject approval cards |
116
+ | `local_file` | `remote`, `name`, `dest` | `GET /file?raw=1` → write into the open folder |
117
+
118
+ Event shapes on the timeline: `thought`, `tool` (with `icon`, `state`, `secs`, `detail`), `text` (with `delta`),
119
+ `status`, `vision` (`{"vision":bool}`), `user` (`{"kind":"reject","path":"…"}`), `done`.
120
+
121
+ ## 7. What the port reproduces, and what it cannot
122
+
123
+ | Desktop client behaviour | `arenacode` (this package) |
124
+ |---|---|
125
+ | WinHTTP + bearer key + `X-Arena-Device` (MachineGuid) | `fetch` + same headers; device id from the registry on Windows, `/etc/machine-id` elsewhere |
126
+ | `POST /task` with `meta` / `agent_prompt` / `project_context` | identical payload (see `src/meta.js`) |
127
+ | Long-poll timeline, mode-line framing, `wait: 0` | identical (`src/turn.js`) |
128
+ | `local-exec` with PowerShell/cmd/WSL and refusal payload | same, with `r`/`d` confirmation (`src/local.js`) |
129
+ | `local-fs` ops incl. git, JSON result contract | same operations, same JSON keys |
130
+ | Plan approval / revision / `wait` / `sysinfo` cards | same |
131
+ | mouse, keyboard, screenshots, app launch, UIA element dumps | **not possible from a terminal** — answered with an explicit `{"ok":false,"error":"…"}` |
132
+ | DPAPI-encrypted `secret_dpapi` in `config.ini` | JSON config, mode `0600` (a portable client has no DPAPI) |
133
+ | GUI session/chat list, drag-and-drop, tray | out of scope; history stays on the bridge (`/tasks`, `/task/<id>`) |
134
+
135
+ ## 8. Reproducing the analysis
136
+
137
+ ```bash
138
+ # REA (needs Node ≥ 22.19; no analysis engine required for the commands below)
139
+ npx -y rea-agents@latest doctor
140
+ npx -y rea-agents@latest inspect-pe-imports ArenaCode-x64.exe --format json
141
+ npx -y rea-agents@latest inspect-byte-entropy ArenaCode-x64.exe --format json
142
+
143
+ # protocol strings (the client stores them as UTF-16LE)
144
+ strings -a -e l -n 4 ArenaCode-x64.exe | grep -E '^/(task|health|events|file|fs|upload)|Authorization|X-Arena'
145
+
146
+ # the embedded system prompt, byte-exact
147
+ python3 - <<'PY'
148
+ data = open('ArenaCode-x64.exe','rb').read()
149
+ i = data.find('# ArenaCode agent: system prompt'.encode('utf-16le'))
150
+ end = i
151
+ while data[end:end+2] != b'\x00\x00':
152
+ end += 2
153
+ print(data[i:end].decode('utf-16le'))
154
+ PY
155
+ ```
156
+
157
+ ## 9. Deep analysis with Ghidra
158
+
159
+ REA's provider-free commands (section 2) reconstruct the protocol surface, but the decompiler is what turns
160
+ "these strings exist" into "this function builds this request". Setup on this host, and what it cost:
161
+
162
+ | Step | Result |
163
+ |---|---|
164
+ | Ghidra **12.2** + Temurin JDK 21, `GHIDRA_INSTALL_DIR` set | provider refused it: `unsupported_version` — REA 6.8.0 accepts the **12.1.x** line (verified with 12.1.4) |
165
+ | Ghidra **12.1.4**, `MAXMEM=1300M`, 5 min deadline | killed by the kernel OOM killer (`resource_constraint`): the container has 2 GB and Ghidra forks one native decompiler per CPU |
166
+ | Ghidra **12.1.4**, `taskset -c 0`, `MAXMEM=900M`, 30 min deadline | **succeeded** — `ev_63854c5644837a356103bc1033918e6946018a8bacd457b1256a8b911a309d0f` |
167
+
168
+ The successful import + analysis of the 5 MB image reports:
169
+
170
+ ```
171
+ document target-1be348946c5c.exe
172
+ image_base 0x140000000
173
+ segments 11
174
+ procedure_count 6376
175
+ string_count 5260
176
+ provider rea-workflow / ghidra 12.1.4
177
+ ```
178
+
179
+ ### What it changes about the port
180
+
181
+ Nothing in the wire format: every field in section 4 had already been confirmed by running the port against a real
182
+ bridge. What it changes is *where to look next*. `rea decompile --file-offset` on a string's offset does **not**
183
+ land inside a procedure — string literals live in `.rdata`, so the useful move is procedure-name search plus
184
+ decompiling the callers of `WinHttpOpenRequest`, which is the next step if the port ever needs the internals:
185
+
186
+ 1. the exact `local-fs` JSON response shapes — currently written against the consumer contract in `agentctl.py`
187
+ and verified empirically;
188
+ 2. the Accept/Reject card state machine (file-change cards and `user-event` on reject);
189
+ 3. the `windows-use` DLL ABI behind `local-ui` mouse/keyboard actions.
190
+
191
+ None of these block a working client: (1) is implemented and tested end to end, (2) has a terminal-appropriate
192
+ equivalent (`--confirm-writes` + the confirm modal), and (3) is answered with an explicit
193
+ "not available in the terminal client" error, which is what the protocol expects from a client that cannot drive
194
+ a GUI.
195
+
196
+ ### Practical note
197
+
198
+ Decompiling a GUI client is the expensive path and usually the wrong first one. For a program whose interesting
199
+ behaviour is `WinHttpOpenRequest` plus `swprintf` of JSON, **the literals are the specification**. The eight
200
+ sections above were reconstructed with zero decompilation, deterministically, in minutes; the Ghidra run cost
201
+ about 25 minutes of a single core to add an inventory, not new protocol facts.
@@ -0,0 +1,65 @@
1
+ # Publishing
2
+
3
+ The package is ready: `arenacode@2.5.2`, 23 files, ~60 kB tarball, no runtime dependencies.
4
+
5
+ ```bash
6
+ cd arenacode-cli
7
+ npm publish --dry-run # + arenacode@2.5.2
8
+ ```
9
+
10
+ ## Status of the publish attempt
11
+
12
+ | Attempt | Result |
13
+ |---|---|
14
+ | `npm whoami` with the supplied token | ✅ authenticated as `lardexx` |
15
+ | `npm view arenacode version` | ✅ 404 → the name `arenacode` is free |
16
+ | `npm publish` (unscoped `arenacode`) | ❌ `403 Forbidden — You may not perform that action with these credentials.` |
17
+ | `npm publish` (`@lardexx/arenacode`, public) | ❌ same 403 |
18
+
19
+ A 403 on PUT with a token that authenticates fine means the token itself is not allowed to write. Three usual
20
+ causes, in order of likelihood:
21
+
22
+ 1. **The token is read-only.** Granular tokens have a *Permissions → Read and write* switch; the default in some
23
+ flows is read-only. Create a new one with **Read and write** and package access **All packages**.
24
+ 2. **The token is restricted to existing packages.** A granular token limited to a selection of packages cannot
25
+ create a *new* package name: pick “All packages”.
26
+ 3. **The account requires 2FA for publishing.** Then `npm publish --otp=<code>` (or an interactive
27
+ `npm login` + `npm publish`) is needed; automation tokens must be created with the *bypass 2FA* option.
28
+
29
+ ## Commands
30
+
31
+ ```bash
32
+ # A. with a fresh token that can write
33
+ npm login # or: npm config set //registry.npmjs.org/:_authToken=<token>
34
+ npm publish # unscoped: arenacode
35
+
36
+ # B. with the tarball
37
+ npm publish arenacode-2.5.2.tgz
38
+
39
+ # C. under your scope (always available to your account)
40
+ node -e "const p=require('./package.json');p.name='@lardexx/arenacode';p.publishConfig={access:'public'};require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')"
41
+ npm publish --access public
42
+ ```
43
+
44
+ The `bin` stays `arenacode` in all three cases, so the installed command is the same:
45
+
46
+ ```bash
47
+ npx arenacode # or npx @lardexx/arenacode
48
+ ```
49
+
50
+ ## Checklist before publishing
51
+
52
+ - [x] `npm pack --dry-run` clean, no stray files (`test/`, `tools/` are excluded by the `files` field)
53
+ - [x] `npm test` green (bridge + fake agent + CLI)
54
+ - [x] `node test/tui.js` green (real pty, rendered frames)
55
+ - [x] `LICENSE` (MIT), `README.md`, `docs/`
56
+ - [ ] version policy: `2.5.2` tracks the ArenaCode client it ports; use `2.5.3`/`2.6.0` afterwards, and keep the
57
+ client number in `src/config.js` (`VERSION`) in sync
58
+
59
+ ## After publishing
60
+
61
+ ```bash
62
+ npm view arenacode version dist.tarball
63
+ npm install -g arenacode
64
+ arenacode doctor
65
+ ```