@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 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 (the default when stdout is a terminal)
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.
@@ -1,65 +1,62 @@
1
1
  # Publishing
2
2
 
3
- The package is ready: `arenacode@2.5.2`, 23 files, ~60 kB tarball, no runtime dependencies.
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
- cd arenacode-cli
7
- npm publish --dry-run # + arenacode@2.5.2
14
+ npm install -g @lardexx/arenacode # then: arenacode
15
+ npx @lardexx/arenacode # or one-shot
8
16
  ```
9
17
 
10
- ## Status of the publish attempt
18
+ ## Why the scoped name
11
19
 
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 |
20
+ The bare name is taken twice over:
18
21
 
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:
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
- 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.
26
+ npm itself suggests the escape hatch in that error: publish under a scope. Hence `@lardexx/arenacode`.
28
27
 
29
- ## Commands
28
+ ## The run that worked
30
29
 
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
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
- # B. with the tarball
37
- npm publish arenacode-2.5.2.tgz
42
+ Two lessons for next time:
38
43
 
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
- ```
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
- The `bin` stays `arenacode` in all three cases, so the installed command is the same:
48
+ ## Publishing a new version
45
49
 
46
50
  ```bash
47
- npx arenacode # or npx @lardexx/arenacode
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
- ## 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
+ `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
- ## After publishing
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",
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:')} arenacode init ${c.gray('or')} arenacode config set url https://your-bridge.tunnl.gg`);
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(' check the API key: arenacode config set key <API key>'));
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
- write('');
148
- write(` ${c.gray('try:')} arenacode ask "list the files in my folder"`);
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('usage: arenacode config set <url|key|folder|lang|mode|pc_access> <value>');
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('usage: arenacode config [show|path|set <key> <value>|clear]');
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
- const ctx = context(flags);
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
- const [command = 'chat', ...rest] = positional;
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;