7 Commits
Author SHA1 Message Date
czcdr 1f5a9925c7 fix: ten review findings — dead audit check, silent zero-arg exit, lock without mutual exclusion (2.0.11)
audit-manifest — item 13 tested a string with `Number.isFinite`, which never
coerces, so the branch was always false and "does the probe accept the next
patch/minor" could not fail. Verified end to end with a probe of `^6\.`: the
audit now reports "accepts 6.7.1 (the next patch)" and "accepts 6.8.0".

rea-shim — a zero-argument start printed usage and exited 0, while both launch
paths still carried an unreachable `argv.length > 0 ? argv : ["mcp"]` fallback.
With `failOnStartupError: false`, that exit 0 leaves REA silently absent from
every tool list. The two cases are now told apart by whether a terminal is
attached: usage + 0 for a person, usage + stderr + EX_USAGE(64) for a harness
that lost its arguments. Both dead fallbacks removed.

setup.mjs — three:
  * provisioning cleared the destination BEFORE extracting, so a missing
    extractor or a full disk destroyed a working install and replaced it with
    nothing; the tree is cleared only for the rename now.
  * a `.part` holding the whole body could not be rescued when the manifest
    declared no size — the Range request got 416 on every retry. A pinned
    sha256 is now enough evidence to finish the rename.
  * `setup.lock` was written only when a run ENDED, so it recorded an outcome
    and excluded nothing: the shim's detached run and a hand-run setup could
    write the same `.part` and the same staging directory. It is claimed with
    O_EXCL before the first byte moves; a live holder is reported with its pid
    and log path and the run exits 75, while a dead pid or a `failed` lock is
    taken over.

check-updates.ps1 — a missing `application.version` line was reported as a
version mismatch ("requires 12.1.x, currently "), sending the reader after a
newer Ghidra instead of a reinstall.

rea-bundle-bootstrap.ps1 — the engines check compared only major/minor, so
`^24.11.1` accepted 24.11.0; all three components compare now.

rea-shim PATH dedupe — Windows compared path spellings case-sensitively and
could prepend the vendored Node twice.

cordis.patch.yml — the home fallback skipped HOMEDRIVE+HOMEPATH, so a service
account resolved `/.dsh/...`. It now mirrors `os.homedir()`: USERPROFILE →
HOMEDRIVE+HOMEPATH → HOME. Both READMEs follow, because the audit compares the
documented expression with the shipped one.

setup.mjs header — the TOFU sentence promised "enforced afterwards" while an
`unverified` checksum is trusted on a cache hit. The comment now states what the
code does and names the gap in `bundle.json` instead of implying verification.

Tests 85 -> 88: extraction order, the audit's item-13 branch, and a live lock
blocking a second run; the bare-invocation case asserts EX_USAGE plus the
`--shim-help` path that must stay 0.
2026-10-23 23:03:39 +08:00
czcdr 1438672f9a feat: show a one-shot setup guide after install and every upgrade (2.0.11)
The setup step is mandatory but invisible: a market install or update replaces
the package and stops there, and a skipped setup fails silently in both
directions - no tools on a first install, the old REA after an upgrade. The
guide names the actual state instead of asking whether it was shown before.

- bin/rea-guide.mjs: computeGuideState compares the REA version recorded in
  plugins/rea-bundle/bundle.json with the manifest pin and reports
  missing / stale / unknown / unreadable / ok. Same pin rules as
  scripts/pin-match.ps1 (dotted pin exact, bare major as a family), and it
  reads UTF-16 records the way the shim and setup.mjs now do.
- index.mjs: serves GET /dsh-plugin-rea/guide-state, mounted only through
  ctx.inject(['webServer']), so headless and ACP hosts load the plugin exactly
  as before. Read-only: no provisioning, no spawning, no writes.
- client.js: shell.overlay card shown once per plugin version, with the exact
  command for this install; "Later" and "I ran it" record the version in
  localStorage, so the next upgrade re-arms it and the same version stays quiet.
- cordis.patch.yml: one extra row for the package entry only (no tools, no REA).
- 5 new tests (78 total); verify-branch section 9 checks all four ends of the
  chain, so a rename cannot make the guide silently never appear.
2026-10-20 19:09:16 +08:00
czcdr 8f1ea15f2a bundle: make the one-plugin flow actually work end to end
Three defects made the published flow impossible to complete:

* The !!js scalar in the MCP row was unquoted, and a bare scalar containing
  "? " and ": " is read by YAML as an explicit key/value pair. The loader
  therefore received a mapping, so args[0] was {"[object Object]": "..."} and
  the row could never start. Reproduced against the harness's own dialect
  (JSON_SCHEMA plus the !!js type definition read out of app.asar).
* The DSH_HOME fallback stopped one level above <home>/.dsh, while the shim,
  setup.mjs and bootstrap.ps1 all resolve to <home>/.dsh. DSH never sets
  DSH_HOME — zero writes anywhere in the archive — so this was the default
  path, not an edge case: the row pointed at a file provisioning never writes.
* The mirror step copied the shim and setup.mjs but not the manifest, and the
  mirrored setup derives its plugin root from its own location. Background
  provisioning therefore died on a bare ENOENT that stdio:"ignore" hid, while
  the lock it had written suppressed retries for six hours.

Also fixed:

* The shim locates its bundle from its own position. MCP children have every
  DSH_* name stripped (case-insensitively), so with a custom DSH_HOME the old
  shim looked in ~/.dsh, reported "not provisioned" and started a second
  ~800 MB run there.
* setup records the real checkout in <bundle>/mirror.json, so a mirrored or
  later-upgraded install finds the source instead of copying the bundle onto
  itself and provisioning the previous manifest forever.
* Empty REA_BUNDLE_ROOT / DSH_HOME no longer degrade the root to "" or a
  relative path, and --root/--cache are resolved (a relative root would be
  recorded in bundle.json and make every component look deleted).
* fail() no longer recommends a mirrored copy that cannot work, and
  check-updates.ps1 reports the migration state for old bundles.

Guarded by checks that were themselves reverse-tested — 4/4 deliberate
sabotages caught: unquoted !!js mapping detection, DSH_HOME agreement across
unset/empty/set, README-versus-patch drift, and mirror-list completeness. CI
now runs bundle:test and bundle:audit, and its YAML step registers the !!js
scalar constructor: safe_load had been rejecting the very row this repository
ships, so the workflow was permanently red.

Verified: audit clean; 13/13 unit tests on Windows and on Linux (WSL); 25/25
end-to-end path checks in a sandbox; and a full JSON-RPC handshake launched
from the row's own evaluated command line (rea 6.4.0, 170 tools) with DSH_HOME
absent from the child environment.
2026-10-14 13:40:16 +08:00
czcdr 4a6d2e0a79 bundle: fix the four defects that made the one-plugin promise false
An external review of this branch found that the headline promise did not
actually work, and it was right. Each claim was checked before acting on it.

Fatal, now fixed:

- **The patch could never launch anything.** The JS string literal used
  backslashes, and a single-quoted YAML scalar keeps them literal for JavaScript
  to eat: '\plugins' is the BEL control character, '\rea-bundle' a carriage
  return, '\bin' a backspace. Evaluated, the path was
  "C:\...\.dshplugins<CR>ea-bundle<BS>in<CR>ea-shim.mjs". Forward slashes now,
  with a DSH_HOME fallback so an unset variable no longer yields
  "undefined/plugins/...".
- **Nothing created the file the patch points at.** `setup.mjs` contained no
  copy of the shim anywhere, so provisioning could finish and the row would
  still fail with MODULE_NOT_FOUND — and the documented "install the plugin
  first and it provisions in the background" could never happen, because the
  shim that would do that was never at the path the row launches. Setup now
  mirrors bin/rea-shim.mjs and scripts/setup.mjs into the bundle root, before
  the probe, which now tests the mirrored copy rather than the source tree.
- **`npm` was invoked from PATH.** The entire point of this branch is the
  machine without Node, where PATH has no npm, so resolving the REA dependency
  closure died with ENOENT on exactly the target machine. It now uses the npm
  that ships inside the provisioned Node (verified: the portable Windows zip
  puts it at <node>\node_modules\npm\bin\npm-cli.js and it runs).
- **`{exe}` was not substituted on POSIX.** The `resolved !== ""` guard in
  swap() rejected the empty string, so `adb{exe}` stayed literal on Linux and
  macOS and platform-tools failed its probe. Now replaced unconditionally, and
  the test mirror matches. (The review also claimed the existing test suite
  fails on ubuntu; it does not — the mirror derives EXE from the platform.)

Also from the review, verified and fixed:

- resolveProxy() is cached; it spawned reg.exe twice per download.
- sha256File() streams instead of reading the whole file, which for the Ghidra
  archive meant a 570 MB buffer.
- The provisioning lock uses an exclusive create, so two rows starting together
  cannot both provision.
- reaAgents' probe pointed at the pre-npm-install layout, reporting a healthy
  vendored copy as unverifiable on every run.
- The node probe's regex accepted Node 20, which REA's engines reject.
- bootstrap.ps1 read its Node version and checksum from a hard-coded pair that
  duplicated the manifest's pins; it now reads the manifest, refuses a TOFU
  pin, and its ARM64 check no longer passes while downloading the x64 zip.
- check-updates.ps1 gained the bundle section bundle-design.md promised.

Two bugs I introduced while fixing the above, both caught by running things
rather than reading them:

- Editing check-updates.ps1 stripped its BOM. PowerShell 5.1 then read the file
  as ANSI, mangling the CJK comments into parse errors. Restored, and the BOM
  is now asserted. bootstrap.ps1 was ASCII-only so it still parsed, but it has
  a BOM too now so a future Chinese comment cannot silently break it.
- The node probe's own-token fallback returned the vendor/node DIRECTORY, so
  the probe spawned a folder and failed with ENOENT while the extraction was
  fine — a self-inflicted replay of the bug class this commit fixes.

New: scripts/verify-branch.ps1, which checks syntax, PowerShell parse + BOM,
the manifest audit, the unit tests, that the patch is machine-path-free and
escape-safe, the published payload list, and a dry-run of the whole chain.

Verified end to end: with DSH_HOME set, provisioning reaches `setup complete`,
the file the patch computes exists, and launching it the way the row does
answers an MCP handshake as rea 6.4.0 with 170 tools.
2026-10-13 23:24:02 +08:00
czcdr 7aeb83ce30 bundle: one-plugin provisioning from a bare machine
Ship an all-in-one variant on its own branch: the published patch stays
machine-path-free and launches a shim that reads the per-machine state, so a
fresh DSH install gets working mcp__rea__* tools without hand-installing Node,
REA, a JDK or Ghidra.

- bin/rea-shim.mjs: resolves the bundle state, exports exactly the variables
  REA reads, execs the vendored CLI. Starts detached provisioning on first run
  and explains the remediation instead of breaking the harness.
- scripts/setup.mjs: staged, idempotent, resumable provisioning (detect,
  resolve, fetch, verify, unpack, wire, probe) with a manifest of pinned
  versions and checksums; --reuse adopts existing installs, --dry-run plans.
- scripts/rea-bundle-bootstrap.ps1: gets a Node-less Windows machine to a
  provisioned bundle using only PowerShell, then hands over to setup.
- manifest/bundle.manifest.json: component pins with the checksum provenance
  each one actually has (vendor-published vs measured-on-this-machine).
- docs/bundle-design.md: design, verified facts, and the upstream host gates
  that make some capabilities impossible on Windows rather than unconfigured.

Verified end to end on Windows 11 x64: setup reaches 'setup complete', and an
MCP initialize handshake through the shim answers serverInfo rea 6.4.0 with no
REA on PATH.
2026-10-13 21:44:06 +08:00
czcdr a1faeaa178 1.0.4: portable row (cmd.exe / rea from PATH), no machine-specific path 2026-10-13 00:15:41 +08:00
czcdr 823c12b3a6 Add cordis.patch.yml 2026-10-12 22:43:02 +08:00