@lardexx/arenacode 2.5.3 → 2.5.5
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/README.md +16 -4
- package/docs/PUBLISHING.md +43 -46
- package/package.json +1 -1
- package/src/cli.js +53 -13
package/README.md
CHANGED
|
@@ -16,16 +16,20 @@ you ──▶ arenacode CLI ──▶ arena-bridge ──▶ agent
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install -g arenacode
|
|
20
|
-
arenacode --version # 2.5.2
|
|
19
|
+
npm install -g @lardexx/arenacode
|
|
20
|
+
arenacode --version # 2.5.2 (the bridge protocol version this client speaks)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Or without installing:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npx arenacode ask "list the files in my folder"
|
|
26
|
+
npx @lardexx/arenacode ask "list the files in my folder"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Published as [`@lardexx/arenacode`](https://www.npmjs.com/package/@lardexx/arenacode) — the bare name `arenacode`
|
|
30
|
+
is blocked by npm's similarity check against the existing `arena-code` package, so the scope is required. The
|
|
31
|
+
command is plain `arenacode` in every case.
|
|
32
|
+
|
|
29
33
|
Requires Node.js 18 or newer. No runtime dependencies.
|
|
30
34
|
|
|
31
35
|
## Terminal UI
|
|
@@ -36,7 +40,9 @@ rebuilt from opencode 1.19.5's own source (`packages/tui/src/component/prompt`,
|
|
|
36
40
|
`logo.tsx`, `theme/assets/*.json`) and checked against frames captured from the installed binary in a pty.
|
|
37
41
|
|
|
38
42
|
```bash
|
|
39
|
-
arenacode # opens the TUI
|
|
43
|
+
arenacode # opens the TUI on a terminal; on the very first run it asks for URL + key first
|
|
44
|
+
arenacode ui # same thing, explicitly
|
|
45
|
+
arenacode chat # the plain line-by-line client, if you really want one
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
Home screen — two-tone block wordmark, centred prompt, tips, folder and version in the footer:
|
|
@@ -275,8 +281,14 @@ SCENARIO=approve node test/tui.js # TUI, terminal card confirmation
|
|
|
275
281
|
SCENARIO=plan node test/tui.js # TUI, plan approval
|
|
276
282
|
node test/tui.js # TUI: drives the real pty, checks the rendered frames
|
|
277
283
|
node tools/frame2html.js /tmp/tui-frames.raw docs/tui-preview.html --match done # frame -> HTML snapshot
|
|
284
|
+
python3 tools/ansi2png.py /tmp/tui-frames.raw docs/tui-session.png --match done # frame -> PNG snapshot
|
|
285
|
+
BIN=arenacode tools/e2e-installed.sh # the *installed* package against a real bridge
|
|
278
286
|
```
|
|
279
287
|
|
|
288
|
+
`tools/e2e-installed.sh` runs whatever `arenacode` is on `PATH` (or `BIN=…`) against a fresh bridge and a fake
|
|
289
|
+
agent, types a request into the TUI through a pty, and prints the agent's report plus the final screen — the check
|
|
290
|
+
that the published tarball, not just the working tree, drives the bridge.
|
|
291
|
+
|
|
280
292
|
The integration test spins up `bridge_server.py` from a private copy, runs a fake agent that claims the task,
|
|
281
293
|
then checks that the CLI answered the `local-fs` / `local-exec` cards with the right payloads, applied the file
|
|
282
294
|
write, rendered the timeline and bound the API key to the device.
|
package/docs/PUBLISHING.md
CHANGED
|
@@ -1,65 +1,62 @@
|
|
|
1
1
|
# Publishing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Live:** [`@lardexx/arenacode@2.5.5`](https://www.npmjs.com/package/@lardexx/arenacode) — 34 files, 226 kB tarball,
|
|
4
|
+
no runtime dependencies. The command stays `arenacode`.
|
|
5
|
+
|
|
6
|
+
2.5.5 fixes `main()` defaulting the subcommand to `chat`, which sent every bare `arenacode` to the line CLI and
|
|
7
|
+
made the TUI unreachable without typing `ui`; a bare invocation now opens the interface, and on first run the
|
|
8
|
+
interface asks for URL + key instead of printing a dead-end notice.
|
|
9
|
+
|
|
10
|
+
2.5.4 made the hints launch-aware: run through npx and every message prints the command you can actually paste
|
|
11
|
+
(`npx @lardexx/arenacode init`), plus the one-line way to get the bare `arenacode` command.
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
npm install -g @lardexx/arenacode # then: arenacode
|
|
15
|
+
npx @lardexx/arenacode # or one-shot
|
|
8
16
|
```
|
|
9
17
|
|
|
10
|
-
##
|
|
18
|
+
## Why the scoped name
|
|
11
19
|
|
|
12
|
-
|
|
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 |
|
|
20
|
+
The bare name is taken twice over:
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
1. `arenacode-cli` already exists (an unrelated package).
|
|
23
|
+
2. `npm publish arenacode` is rejected even though the name is unclaimed:
|
|
24
|
+
`403 Package name too similar to existing package arena-code` — npm's similarity filter, not a permission problem.
|
|
21
25
|
|
|
22
|
-
|
|
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.
|
|
26
|
+
npm itself suggests the escape hatch in that error: publish under a scope. Hence `@lardexx/arenacode`.
|
|
28
27
|
|
|
29
|
-
##
|
|
28
|
+
## The run that worked
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
npm
|
|
34
|
-
npm publish
|
|
30
|
+
| Attempt | Result |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `npm whoami` with the first token | ✅ `lardexx` |
|
|
33
|
+
| `npm publish` (unscoped `arenacode`) | ❌ `E403 … You may not perform that action with these credentials` (read-only token) |
|
|
34
|
+
| `npm publish` with the second token | ❌ `EOTP … requires a one-time password` (write token, 2FA enforced) |
|
|
35
|
+
| `npm publish` with the third token (2FA bypass) | ❌ `403 … Package name too similar to existing package arena-code` |
|
|
36
|
+
| `npm publish --access public` as `@lardexx/arenacode` | ⚠️ `202 Accepted` → a `0.0.0-stage` **placeholder** appeared (`"stub": true`), the real version was staged |
|
|
37
|
+
| re-publish the same version | ❌ `409 Cannot publish over previously staged version "2.5.2"` |
|
|
38
|
+
| `npm stage list` / `stage approve` | ❌ "No staged versions" / `stage-id must be a valid UUID` — nothing to approve from the CLI |
|
|
39
|
+
| **bump to 2.5.3 and publish** | ✅ `+ @lardexx/arenacode@2.5.3`, `dist-tags.latest = 2.5.3` after ~2 min of async processing |
|
|
40
|
+
| 2.5.4 (npx-aware hints) | ✅ same path, again ~1–2 min of async processing before it appeared |
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
npm publish arenacode-2.5.2.tgz
|
|
42
|
+
Two lessons for next time:
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
44
|
+
* a new scoped package may land asynchronously — the registry answers `202 Accepted`, shows a `0.0.0-stage` stub and
|
|
45
|
+
needs a couple of minutes before `https://registry.npmjs.org/<pkg>` returns 200. Poll it before assuming failure;
|
|
46
|
+
* a version that was ever staged cannot be republished, so bump the version instead of retrying it.
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
## Publishing a new version
|
|
45
49
|
|
|
46
50
|
```bash
|
|
47
|
-
|
|
51
|
+
cd arenacode-cli
|
|
52
|
+
npm version patch --no-git-tag-version # 2.5.3 → 2.5.4
|
|
53
|
+
npm test && node test/tui.js # CLI + TUI suites
|
|
54
|
+
npm pack # inspect the tarball first
|
|
55
|
+
npm publish --access public # needs a Read+write, All-packages, 2FA-bypass token
|
|
48
56
|
```
|
|
49
57
|
|
|
50
|
-
|
|
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
|
+
`publishConfig.access = public` is already in `package.json`, and `files` ships only `bin/`, `src/`, `docs/`,
|
|
59
|
+
`README.md`, `LICENSE` — 34 files, ~226 kB.
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npm view arenacode version dist.tarball
|
|
63
|
-
npm install -g arenacode
|
|
64
|
-
arenacode doctor
|
|
65
|
-
```
|
|
61
|
+
The `VERSION` shown by the CLI is the **bridge protocol version** (2.5.2) and is deliberately separate from the
|
|
62
|
+
package version: the client keeps speaking the same protocol as `arena-bridge` 2.5.2.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lardexx/arenacode",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.5",
|
|
4
4
|
"description": "Terminal client for ArenaCode: talks to an arena-bridge, streams the Agent Mode timeline, and runs local commands and file operations on your PC. Node port of the ArenaCode desktop client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
package/src/cli.js
CHANGED
|
@@ -15,6 +15,21 @@ const { c, write, ask, banner, truncate, indentLines } = require('./ui');
|
|
|
15
15
|
|
|
16
16
|
const AGENT_PROMPT_FILE = path.join(__dirname, 'prompts', 'agent-prompt.md');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* How this process was started, so the hints it prints are commands the user can actually paste.
|
|
20
|
+
* `npx @lardexx/arenacode …` never puts `arenacode` on PATH, and on Windows that reads as
|
|
21
|
+
* "not recognized as an internal or external command".
|
|
22
|
+
*/
|
|
23
|
+
const SELF = (() => {
|
|
24
|
+
const agent = process.env.npm_config_user_agent || '';
|
|
25
|
+
const viaNpx = /\bnpm\/.*\bexec\b|\bnpx\b/.test(agent) || process.env.npm_lifecycle_event === 'npx';
|
|
26
|
+
if (!viaNpx) return 'arenacode';
|
|
27
|
+
return process.platform === 'win32' ? 'npx @lardexx/arenacode' : 'npx @lardexx/arenacode';
|
|
28
|
+
})();
|
|
29
|
+
const INSTALL_HINT = process.platform === 'win32'
|
|
30
|
+
? 'npm install -g @lardexx/arenacode (then reopen the terminal)'
|
|
31
|
+
: 'npm install -g @lardexx/arenacode';
|
|
32
|
+
|
|
18
33
|
const MODE_LINES = {
|
|
19
34
|
agent:
|
|
20
35
|
'[ArenaCode mode: agent]\n' +
|
|
@@ -103,7 +118,8 @@ function requireUrl(ctx) {
|
|
|
103
118
|
if (ctx.url) return true;
|
|
104
119
|
write('');
|
|
105
120
|
write(c.red(' no bridge URL configured.'));
|
|
106
|
-
write(` ${c.gray('run:')}
|
|
121
|
+
write(` ${c.gray('run:')} ${SELF} init ${c.gray('or')} ${SELF} config set url https://your-bridge.tunnl.gg`);
|
|
122
|
+
if (SELF !== 'arenacode') write(` ${c.gray('to get the bare command:')} ${INSTALL_HINT}`);
|
|
107
123
|
return false;
|
|
108
124
|
}
|
|
109
125
|
|
|
@@ -113,7 +129,7 @@ async function ensureAuth(ctx) {
|
|
|
113
129
|
const reason = (res.json && (res.json.error || res.json.hint)) || `HTTP ${res.status}`;
|
|
114
130
|
write(c.red(` auth failed: ${reason}`));
|
|
115
131
|
if (res.status === 403) write(c.gray(' this API key is bound to another PC: on the bridge run keys unbind <ID>'));
|
|
116
|
-
if (res.status === 401) write(c.gray(
|
|
132
|
+
if (res.status === 401) write(c.gray(` check the API key: ${SELF} config set key <API key>`));
|
|
117
133
|
return false;
|
|
118
134
|
}
|
|
119
135
|
|
|
@@ -121,7 +137,7 @@ async function ensureAuth(ctx) {
|
|
|
121
137
|
// commands
|
|
122
138
|
// --------------------------------------------------------------------------- //
|
|
123
139
|
|
|
124
|
-
async function cmdInit(flags) {
|
|
140
|
+
async function cmdInit(flags, { tail = true } = {}) {
|
|
125
141
|
banner(VERSION);
|
|
126
142
|
const cfg = readConfig();
|
|
127
143
|
write(c.gray(` config file: ${configPath()}`));
|
|
@@ -144,8 +160,10 @@ async function cmdInit(flags) {
|
|
|
144
160
|
const auth = await bridge.authCheck();
|
|
145
161
|
if (auth.status === 200) write(` ${c.green('✓')} API key accepted`);
|
|
146
162
|
else write(c.red(` ✗ API key rejected: ${(auth.json && auth.json.error) || auth.status}`));
|
|
147
|
-
|
|
148
|
-
|
|
163
|
+
if (tail) {
|
|
164
|
+
write('');
|
|
165
|
+
write(` ${c.gray('try:')} arenacode ask "list the files in my folder"`);
|
|
166
|
+
}
|
|
149
167
|
return 0;
|
|
150
168
|
}
|
|
151
169
|
|
|
@@ -166,7 +184,7 @@ function cmdConfig(positional, flags) {
|
|
|
166
184
|
const name = key;
|
|
167
185
|
const val = rest.join(' ');
|
|
168
186
|
if (!name || val === '') {
|
|
169
|
-
write(
|
|
187
|
+
write(`usage: ${SELF} config set <url|key|folder|lang|mode|pc_access> <value>`);
|
|
170
188
|
return 2;
|
|
171
189
|
}
|
|
172
190
|
const patch = {};
|
|
@@ -186,7 +204,7 @@ function cmdConfig(positional, flags) {
|
|
|
186
204
|
write(` ${c.green('✓')} config removed`);
|
|
187
205
|
return 0;
|
|
188
206
|
}
|
|
189
|
-
write(
|
|
207
|
+
write(`usage: ${SELF} config [show|path|set <key> <value>|clear]`);
|
|
190
208
|
return 2;
|
|
191
209
|
}
|
|
192
210
|
|
|
@@ -337,13 +355,25 @@ async function cmdAsk(positional, flags) {
|
|
|
337
355
|
|
|
338
356
|
/** Full-screen TUI (opencode-style): timeline, input box, dialogs, local cards. */
|
|
339
357
|
async function cmdUi(flags) {
|
|
340
|
-
|
|
341
|
-
if (!requireUrl(ctx)) return 1;
|
|
358
|
+
let ctx = context(flags);
|
|
342
359
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
360
|
+
if (!requireUrl(ctx)) return 1;
|
|
343
361
|
write(c.red(' the TUI needs an interactive terminal.'));
|
|
344
362
|
write(c.gray(' inside a script or a pipe use: arenacode ask "…" or arenacode chat'));
|
|
345
363
|
return 1;
|
|
346
364
|
}
|
|
365
|
+
|
|
366
|
+
// First run: `arenacode` (or `arenacode ui`) opens the setup here instead of printing two lines
|
|
367
|
+
// and quitting — the interface is what the user came for, the questions are just the way in.
|
|
368
|
+
if (!ctx.url) {
|
|
369
|
+
write('');
|
|
370
|
+
write(` ${c.bold('first run')} ${c.gray('· bridge URL and API key (Enter keeps the value in brackets)')}`);
|
|
371
|
+
write('');
|
|
372
|
+
await cmdInit(flags, { tail: false });
|
|
373
|
+
write('');
|
|
374
|
+
ctx = context(flags);
|
|
375
|
+
if (!ctx.url) return 1; // setup was abandoned: nothing to open
|
|
376
|
+
}
|
|
347
377
|
const { App } = require('./tui/app');
|
|
348
378
|
const app = new App({
|
|
349
379
|
url: ctx.url,
|
|
@@ -621,6 +651,10 @@ async function cmdHandshake(flags) {
|
|
|
621
651
|
function help() {
|
|
622
652
|
banner(VERSION);
|
|
623
653
|
write(` ${c.bold('usage')} arenacode <command> [options]`);
|
|
654
|
+
if (SELF !== 'arenacode') {
|
|
655
|
+
write(` ${c.gray('you are running it through npx — every command can be prefixed the same way,')}`);
|
|
656
|
+
write(` ${c.gray(`or install it once: ${INSTALL_HINT}`)}`);
|
|
657
|
+
}
|
|
624
658
|
write('');
|
|
625
659
|
write(` ${c.cyan('setup')}`);
|
|
626
660
|
write(` init configure URL + API key, test the connection`);
|
|
@@ -663,7 +697,10 @@ function help() {
|
|
|
663
697
|
|
|
664
698
|
async function main(argv) {
|
|
665
699
|
const { flags, positional } = parseArgs(argv);
|
|
666
|
-
|
|
700
|
+
// No default here: a bare `arenacode` must be distinguishable from `arenacode chat`, because only
|
|
701
|
+
// the bare form opens the TUI. Defaulting to 'chat' sent every plain invocation to the line CLI.
|
|
702
|
+
const [first = undefined, ...rest] = positional;
|
|
703
|
+
const command = first;
|
|
667
704
|
|
|
668
705
|
if (flags.version || command === 'version') {
|
|
669
706
|
write(VERSION);
|
|
@@ -704,13 +741,16 @@ async function main(argv) {
|
|
|
704
741
|
case 'handshake':
|
|
705
742
|
return cmdHandshake(flags);
|
|
706
743
|
default:
|
|
744
|
+
if (command === undefined) {
|
|
745
|
+
// bare invocation: the interface, when there is a terminal to draw it on
|
|
746
|
+
if (process.stdout.isTTY && process.stdin.isTTY) return cmdUi(flags);
|
|
747
|
+
help();
|
|
748
|
+
return 0;
|
|
749
|
+
}
|
|
707
750
|
if (command.startsWith('-')) {
|
|
708
751
|
// `arenacode "instruction"` convenience form
|
|
709
752
|
return cmdAsk(positional, flags);
|
|
710
753
|
}
|
|
711
|
-
if ((command === 'chat' || command === undefined) && process.stdout.isTTY && process.stdin.isTTY) {
|
|
712
|
-
return cmdUi(flags);
|
|
713
|
-
}
|
|
714
754
|
write(c.red(` unknown command: ${command}`));
|
|
715
755
|
help();
|
|
716
756
|
return 2;
|