Commit Graph
2 Commits
Author SHA1 Message Date
DevvynandClaude Sonnet 5 a0628a40e8 fix(cli-registry): address maintainer review on #380
Rebased onto current master (the one real conflict was the import line
in docker-hosts.ts Ark0N flagged; kept both), then addressed every
point from the review:

**1. Rebase.** Done — this branch now sits on current upstream/master.

**2. Agent-image special cases are data now, not an id-keyed table
outside stock.ts.** `AGENT_IMAGE_SPECIAL_CASE_IDS`/`AGENT_IMAGE_SPECIAL_CASES`
are gone. `CliDiscovery.install.agentImageLayer?: { kind: 'dedicated';
reason: string }` is a field on the registry entry itself (pi,
deepseek), `reason` is required by schema.ts, both producers
(docker-hosts.ts and cli-catalog.mjs) filter on its presence instead
of an id, and the coverage test reads it from the generated catalogue.
Also added the npm-package-name validation to the TS producer, which
only the .mjs one had — same SAFE_PACKAGE regex, duplicated
(necessarily, one side can't import the other) and now pinned
byte-identical by a new parity test.

**3. Changeset said five, it's eight.** (Not nine — see the DeepSeek
point below, which changes the true count.) Reworded to state it
structurally rather than pin a number that will go stale again.

Then the four behavior-changing findings:

- **DeepSeek was offered as a normal install option but can't actually
  drive a pane.** `npm install -g @deepseek-ai/dsh` installs the
  launcher only; DeepSeek ships no profile that can run standalone.
  The generator now emits an empty install command for any
  `launcherProfile` entry, so install.sh's menu (which requires a
  non-empty command) skips it and falls through to its docs URL hint
  instead — matching what the old hand-written code did before this
  PR replaced it.
- **wget-only hosts lost every automatic install, including the npm
  ones that never needed curl.** The menu-building loop now filters
  PER ENTRY (only a command starting with `curl ` is held back) rather
  than wiping the whole menu when DOWNLOADER != curl.
- **The DISPLAY/TRUSTED split and the catalogue refresh didn't hold up
  under review** (refresh's only real write was the label; it ran
  before the Node existence check; its own eval-detection test was
  tripped by the word "eval'd" in a comment). Dropped entirely per
  your own recommendation — embedded catalogue only, no network
  fetch, no second array. install-sh-invariants.test.ts now asserts
  the refresh/DISPLAY machinery does not exist rather than testing its
  internals.

The three take-or-leave items, applied:

- `dsh_banner_probe`'s bash 3.2 empty-array bug: `${runner[@]}` →
  `${runner[@]+"${runner[@]}"}`. Verified live in a real `bash:3.2.57`
  container with `timeout` removed from PATH — crashed before, clean
  now, full `detect_all_clis` path exercised end to end.
- `docker-agent-image-coverage.test.ts` now anchors on each layer's
  `<binary> --version` proof line instead of `Dockerfile.includes(binary)`,
  which stayed true if a layer were deleted but its comment survived.
- Doc drift: docs/docker-cases.md (four → five, and now describes the
  data field), docker/agent.Dockerfile's "other four CLIs" comment (no
  longer a magic number — CLI_NPM_PACKAGES is generated and can grow),
  CLAUDE.md's install.sh size (104KB → ~112KB) and its stale mention of
  the now-dropped refresh.

Verified: tsc clean, prettier clean, the full targeted suite (142
tests across the 8 affected files) green, and the full `npm test` gate
diffed BY TEST NAME against a clean upstream/master baseline run on
this same machine — identical 201-name failure set both sides (168
tests / 67 files, all pre-existing Windows-environment noise: symlinks,
PTY spawning, POSIX permission bits — none of it touching anything
this PR changes), zero new failures either side of the diff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R9ZSTEenc8soSu9bTi8Xru
2026-09-13 17:43:14 +08:00
DevvynandClaude Opus 5 7af4dbc0f8 feat(docker): derive the agent image's npm CLI list from the catalogue
docker/agent.Dockerfile hardcoded the four npm-published CLIs it installs, one
of the several lists that had to be kept in step with the registry by hand.

It now takes them as `ARG CLI_NPM_PACKAGES`, supplied by
scripts/build-agent-image.mjs from config/clis.stock.json, with the default set
to today's list so a bare `docker build` still produces the same image. The arg
is expanded unquoted because word splitting is what turns the list into several
arguments, which is exactly why every token is validated against
^[@A-Za-z0-9][@A-Za-z0-9/._-]*$ on the producing side; a package name carrying a
space or a metacharacter is refused rather than reaching the RUN line. Verified
by building the layer: four packages in, four arguments out, and the default
still applies with no arg.

The list is filtered on each entry's `enabled` flag — the field whose absence
was the maintainer's §3 finding, where a CLI shipping disabled still got baked
into every image. No stock entry is disabled today, so that assertion would pass
vacuously; a unit test feeds the pure helper a fabricated disabled entry so the
fix is covered now rather than the first time someone ships one.

⚠️ It reads the STOCK catalogue, never the merged registry. A user's
~/.codeman/clis.json must not change what is inside an image tagged
codeman/agent:base, or two machines holding that tag hold different images.

Four CLIs keep hand-written layers because the registry cannot describe what
makes them special: pi's --ignore-scripts, deepseek's pnpm companion and dsh-tui
profile, and the three standalone installers. Rather than extend the schema for
a Docker-only benefit, the coverage test requires each to carry a written reason
AND still be present, so an exclusion cannot quietly become an omission.

There are two producers of this command line and there have to be — the .mjs
cannot import TypeScript, and src/docker-hosts.ts builds the same argv for the
in-app auto-build — so a parity test pins them together, package list, arg pairs
and rendered argv. Their order is pinned too: a different order is a different
RUN string and so a needless cache miss between the two build paths.

docker/server.Dockerfile is deliberately NOT edited (PRs #373 and #377 both
modify it); its narrower list is asserted as a declared omission list instead, so
the divergence is reviewable without touching the file.

Also fixes the in-app hint at index.html, which the new coverage test caught
still omitting omp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015EMxQreQUZX5ZyybxAGh12
2026-09-13 17:43:13 +08:00