[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-105439":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":20,"hasPages":20,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":33,"readmeContent":34,"aiSummary":10,"trendingCount":15,"starSnapshotCount":15,"syncStatus":35,"lastSyncTime":36,"discoverSource":37},105439,"dsh-plugin-rea","czcdr\u002Fdsh-plugin-rea","czcdr","第三方适配插件：把上游 REA（github.com\u002Fmorluto\u002Frea）的 MCP 服务接入 DeepSeek Harness，提供 mcp__rea__* 工具。2.x 会自动准备 Node、REA CLI、JDK 与 Ghidra 并把那一行接好；逆向分析能力全部由上游 REA 提供。","https:\u002F\u002Fgithub.com\u002Fczcdr\u002Fdsh-plugin-rea",null,"JavaScript",56,13,51,0,1,3,41.72,"MIT License",false,"main",[23,24,25,26,27,28,29,30,31,32],"binary-analysis","cordis-plugin","deepseek-harness","dsh","dsh-plugin","ghidra","mcp","mcp-server","rea","reverse-engineering","2026-11-01 00:16:46","# dsh-plugin-rea\n\n[![npm version](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002Fdsh-plugin-rea?style=flat-square&color=cb3837)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fdsh-plugin-rea)\n\n**English** · [简体中文](README.zh-CN.md)\n\n> **Which version is this? 2.x — and `main` is it.** 2.x provisions the runtime itself (Node, the REA CLI, a\n> JDK, Ghidra) and then wires the row: install the plugin, run `scripts\u002Fsetup.mjs` once, restart dsh.\n> **1.x** is the older configuration-only adapter — it expects `rea` on PATH and Ghidra installed by you —\n> kept on the [`v1` branch](https:\u002F\u002Fgithub.com\u002Fczcdr\u002Fdsh-plugin-rea\u002Ftree\u002Fv1) and on npm as `dsh-plugin-rea@1.0.6`.\n> Full comparison: [Two lines: 1.x and 2.x](#two-lines-1x-and-2x) · upgrading: [Upgrading from 1.x](#upgrading-from-1x).\n\n> REA **6.8.0** — this package's pin — still accepts Ghidra **12.1.x** only (12.2 is `unsupported`, and the whole\n> Ghidra decompile\u002Fanalysis group disappears) and the jadx bridge **0.7.1** only (0.8.0 fails on its first call with\n> `Expected engine 0.7.1`, before any APK is loaded). Both gates were re-checked against 6.8.0 on 2026-10-26 and\n> neither one moved. JDK **21.0.13** is out upstream, but Adoptium publishes no Windows build of it, so there is\n> nothing to point at here — and it drops only three of the four eMudhra anchors. The trade-off is therefore \"all\n> tools green, CVE still open\": Ghidra 12.1.4's CVE-2026-100504 \u002F 100505 \u002F 100503 \u002F 113604 stay in the chain — **do\n> not analyse untrusted samples with this environment** — and the JDK still trusts the eMudhra anchors. Evidence,\n> mitigations and revisit triggers: [docs\u002Fupdate-review-2026-10-24.md](docs\u002Fupdate-review-2026-10-24.md) and\n> [docs\u002Fupdate-review-2026-10-26.md](docs\u002Fupdate-review-2026-10-26.md).\n>\n> **Ghidra 12.2 itself works** — measured 2026-10-26: same 9 segments, same 341 procedures, byte-identical `entry`\n> pseudocode as 12.1.4, through REA's own bridge. What blocks it is REA's version-equality check, not the bridge.\n> If you want the CVE-fixed engine anyway, `node scripts\u002Fghidra-gate-override.mjs --enable --version 12.2` rewrites\n> that one constant in the vendored copy, records what it changed in `\u003Cbundle>\u002Fghidra-gate-override.json`, and\n> `--disable` restores it; re-running setup reverts it too. It needs **JDK 25+** (`application.java.min=25`).\n\n> **You must run `node scripts\u002Fsetup.mjs` yourself — once after installing, and once after every plugin\n> upgrade.** Nothing runs it for you: a market install or update replaces the package and stops there.\n> Since **2.0.8** a market upgrade keeps the *row* working (the mirrored launcher re-execs the new one), but it\n> does **not** move the bundle to the new pins — REA and the engines stay at the versions recorded by the last\n> setup run. Skip it on a first install and the row exits `MODULE_NOT_FOUND` with no tools and no message;\n> skip it after an upgrade and you keep running the old REA silently (`check-updates.ps1` reports the drift).\n> If you had enabled the Ghidra gate override (see §9 of the 2026-10-26 review), that setup run re-provisions the\n> bundled REA and the patch goes with it — re-enable it afterwards, or accept the pinned Ghidra again.\n> The full rule set is under [Updating](#updating--three-independent-things).\n\n> **A first install and every plugin upgrade also show a one-time guide in the Web GUI.** It says whether the\n> runtime is missing or behind this package's pin, shows the exact `node \"\u003Cinstall path>\u002Fscripts\u002Fsetup.mjs\"`\n> command for that install, and remembers the dismissal per plugin version. It is a notice only: it never runs\n> setup for you and never edits the bundle. Run it once, restart dsh, and the guide stays away until the next\n> plugin version.\n\nBrings the upstream **[REA — Reverse Engineer Anything](https:\u002F\u002Fgithub.com\u002Fmorluto\u002Frea)** MCP server into\n**DeepSeek Harness** (dsh), so its tools show up in a session as `mcp__rea__*`.\n\n![dsh-plugin-rea: the session calls the official MCP client row, which launches the bundle's shim; the shim starts the provisioned REA and the Evidence comes back](assets\u002Fscreenshot-1.png)\n\n## What this is\n\nA **Cordis bundle with a provisioning layer**. `package.json` declares `dsh.bundle.patch`, and that patch\ninserts one row of the official MCP client plugin (`@deepseek-ai\u002Fdsh-mcp-client`). The row launches\n`bin\u002Frea-shim.mjs` with the running Node executable; the shim reads the machine-local state that\n`scripts\u002Fsetup.mjs` wrote and starts the REA CLI it provisioned. There is no analysis code here, and the\npublished patch contains no machine path.\n\n> **Third-party adapter.** This repository is not part of REA, ships none of REA's code, and is not\n> maintained by REA's authors. It composes one configuration row and fetches upstream's own releases.\n> Upstream: [github.com\u002Fmorluto\u002Frea](https:\u002F\u002Fgithub.com\u002Fmorluto\u002Frea) (MIT) ·\n> [docs](https:\u002F\u002Fgithub.com\u002Fmorluto\u002Frea#what-you-can-analyze) ·\n> [rea.tools](https:\u002F\u002Frea.tools\u002F) · npm package `rea-agents`.\n> REA's own problems go to its tracker; **this plugin's** problems go to\n> [this repository's issues](https:\u002F\u002Fgithub.com\u002Fczcdr\u002Fdsh-plugin-rea\u002Fissues).\n\n**It adds exactly three things:** the tool registration under the `mcp__rea__*` names, the per-machine\nengine environment (`GHIDRA_INSTALL_DIR`, `JAVA_HOME`, `REA_ADB_PATH`, `REA_APKTOOL_COMMAND`,\n`REA_JADX_MCP_JAR`, `REA_BROWSER_EXECUTABLE`, …) scoped to that MCP child instead of the whole harness, and\nthe provisioning that puts those components on disk.\n\n## What it can and cannot do\n\nThe analysis is upstream's; this section only restates upstream's own contract so expectations are right.\n\n- **It cannot recover complete source code.** For native binaries upstream returns *pseudocode and\n  assembly*, for Android APKs *decompiled methods*, for JEB units *pseudocode*, and for JavaScript\u002FElectron\n  *modules and their relations*. Upstream's tool contract states the pseudocode \"is decompiler output, not\n  original source and not text-equivalent\" to another provider's output.\n- Not recoverable in practice: original identifier names, types, comments, macros and template expansion,\n  build scripts and assets. Optimisation (inlining, reordering), symbol stripping, obfuscation, packing and\n  VM protection only make the output worse.\n- Every result carries **confidence and limitations** (`observed` \u002F `derived` \u002F `inferred`, plus an explicit\n  limitations list). Read them as evidence, never as recovered source.\n- Good for: understanding behaviour, locating an implementation, interoperability and compatible\n  re-implementation, security review, migration reference. To obtain actual source code, use the legitimate\n  route — the upstream repository, vendor authorisation, or the vendor's own SDK.\n\n**What provisioning cannot buy you.** These are upstream host gates, not settings this plugin forgot:\n\n| Capability | Gate |\n|---|---|\n| Hopper provider | macOS only |\n| IDA provider | needs your own IDA and `REA_IDA_MCP_CONFIG`; REA never launches IDA |\n| firmware `binwalk` \u002F `unblob` | Linux only |\n| Wakaru JS recovery, pwntools ELF\u002Frecorded-crash paths | Linux x64 only |\n| pwndbg \u002F gdb core context | Linux, macOS |\n| native mitmproxy captures | Linux (`REA_MITMDUMP_COMMAND`); HAR needs no engine |\n| Apple toolchain (Mach-O, plist, asset catalog, LLDB) | macOS only |\n| `capture_process_scenario` | Linux\u002FmacOS PTY |\n| apktool (Android resource decoding) | available only when `REA_APKTOOL_COMMAND` points at something Node can **execute directly**: upstream `execFile`s that command without a shell, and Windows cannot execute a `.bat`\u002F`.cmd` that way (EINVAL). This bundle wires the official `apktool.bat` as upstream documents, so on Windows the launcher reports no version and the capability stays unavailable; JADX (a jar) is unaffected |\n\n## Two lines: 1.x and 2.x\n\n| | **2.x** — this line, `main` | **1.x** — [`v1`](https:\u002F\u002Fgithub.com\u002Fczcdr\u002Fdsh-plugin-rea\u002Ftree\u002Fv1) |\n|---|---|---|\n| What it is | the adapter **plus provisioning**: it prepares Node, the REA CLI, a JDK and Ghidra, then wires the row | configuration-only adapter: one MCP row pointing at a `rea` you installed yourself |\n| What you need first | nothing for the runtime — provisioning fetches what is missing (with `--reuse` it adopts an existing Ghidra\u002FJDK instead) | Node.js on REA's engines, `npm i -g rea-agents`, and Ghidra 12.1.x for native analysis |\n| First run | run `scripts\u002Fsetup.mjs` (or the bootstrap) once, then restart dsh | nothing to run |\n| The row launches | the running Node executable plus `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs`, which reads that machine's state | `cmd.exe \u002Fc rea mcp` on Windows, `rea mcp` on macOS\u002FLinux |\n| Machine state | `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002F` — `bundle.json`, `vendor\u002F`, `cache\u002F` | your own REA and Ghidra installs |\n| Install | `dsh plugin --profile \u003Cprofile> add dsh-plugin-rea` (npm `latest` ≥ 2.0.0) | `… add dsh-plugin-rea@1.0.6`, or `github:czcdr\u002Fdsh-plugin-rea#v1.0.6` |\n\nBoth lines ship the **same patch shape** — one `insert` row of `@deepseek-ai\u002Fdsh-mcp-client` — and offer the\nsame `mcp__rea__*` surface; they differ in who prepares the runtime. Moving from 1.x to 2.x takes two\ncommands: see [Upgrading from 1.x](#upgrading-from-1x).\n\nUpstream also registers itself with dsh now (`rea setup --client dsh`); that route is essentially the\n1.x model maintained upstream — configuration only, your own Node\u002FREA\u002FGhidra. This bundle is the\nzero-dependency route. Pick one of the three, and never keep two `serverName: rea` rows at once.\n\n## Install\n\n**Nothing to install first.** On a bare Windows x64 machine:\n\n```\npowershell -ExecutionPolicy Bypass -File scripts\\rea-bundle-bootstrap.ps1 -Reuse\n```\n\n`-Reuse` adopts a Ghidra\u002FJDK you already have; omit it to download both (about 775 MB from the official\nreleases, checksum-verified). Then install the plugin for your profile:\n\n```\ndsh plugin --profile \u003Cprofile> add dsh-plugin-rea\n# or pin this release instead of tracking npm latest:\ndsh plugin --profile \u003Cprofile> add github:czcdr\u002Fdsh-plugin-rea#v2.0.13\n```\n\n> **On an Electron host (the DeepSeek Harness desktop app) the profile is owned by the application.**\n> `dsh plugin --profile desktop …` is refused by the CLI (`profile \"desktop\" is managed exclusively by\n> the Electron application`) — install or update through the in-app plugin manager instead. The CLI form\n> above is for a profile you created yourself with the CLI under another name.\n\nIf Node is already installed, you can skip the bootstrap and run provisioning directly:\n\n```\nnode scripts\u002Fsetup.mjs --reuse      # or --dry-run to see the plan first\n```\n\nNode, when you provide it instead of letting setup fetch one, must satisfy REA's engines —\n`^22.23.4 || ^24.22.1 || >=26.12.1` (check with `node -v`). REA raised those floors in 6.8.0: the pinned\n24.22.1 is now the *oldest* acceptable 24.x, so a 24.11–24.21 runtime that 6.7.0 accepted is rejected here.\nA lower Node does not fail at install time; the MCP row fails later, which reads as silence.\n\nInstalling the plugin **first** also works, but it takes two steps. The row resolves to\n`\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs`, and that file is created by setup — so until the first\nprovisioning run the row exits with `MODULE_NOT_FOUND`, and nothing is there to start a background\nprovisioning (the harness itself is unaffected: `failOnStartupError: false` is deliberate). Run\n`node scripts\u002Fsetup.mjs` once; from then on the shim is in place, and every later missing state is picked up\nby the shim itself, which provisions in the background.\n\n### Which DSH versions\n\n`package.json` declares `engines.dsh: \">=0.1.7-rc.2 \u003C0.3.0-0\"`, and that declaration is the answer the plugin market\nreads: with it the card says **compatible**; without it the market can only say *unknown*. The range is not a guess —\nit is the dsh release list this plugin is checked against, and `node scripts\u002Fhost-compat.mjs` fails the build when the\ntwo disagree:\n\n| Host | Covered |\n|---|---|\n| `0.1.7-rc.2` … `0.2.1-rc.2` — every release in between, alpha and rc | yes |\n| `0.3.0` and later | no — a new minor line is a host nobody updated this row for yet |\n\nWhat was actually compared across that range is the row provider: `packages\u002Fmcp\u002Fmcp-client` at `dsh-v0.1.7-rc.2` and at\n`dsh-v0.2.1-rc.2` exposes the same config surface the shipped row uses (`serverName`, `transport`, `command`, `args`,\n`env`, `failOnStartupError`), and `dsh.bundle.patch` exists at both tags. A host outside the range can still work; it is\nsimply not claimed.\n\n### A REA you already have is used as-is\n\n2.x exists to supply what a machine is missing. If REA is already installed — `rea` on PATH, the 1.x setup — the row\nruns **that**, exactly as the 1.x line did, and nothing is provisioned:\n\n| What the machine has | What the row runs |\n|---|---|\n| `rea` on PATH, at least as new as the provisioned build | your REA (`rea mcp`), with your Node and your PATH |\n| `rea` on PATH but older than the provisioned build | the provisioned bundle — a stale global CLI would take the tool count backwards (6.4.0 ships 170 tools, 6.7.0 ships 207, 6.8.0 ships 229) |\n| no `rea` on PATH | the provisioned bundle, provisioning itself on first use |\n\n- `REA_BUNDLE_LAUNCH=auto|system|bundle` overrides the decision (`auto` is the default); `REA_BUNDLE_SYSTEM_REA=\u003Cpath>`\n  names a binary that is not on PATH.\n- The probe is the CLI's own `--version`, bounded at 8 s: a `.cmd` shim on Windows takes ~2.4 s to answer, so the check\n  runs once per start and only when a `rea` actually exists.\n- `--shim-status` prints the decision — `launch.mode`, `launch.reason`, `launch.systemRea` — and its `launch.rejected`\n  list says why a candidate was skipped (`\"exit 7\"`, `\"inside the bundle\"`, `\"timeout\"`).\n- A delegated child carries `REA_BUNDLE_DELEGATED=1`, so a wrapper that calls this shim cannot make it delegate to\n  itself forever.\n- **On Windows a forced kill of the shim does not reap the delegated REA.** There is no job object here: Task Manager\n  or a supervisor terminating the shim leaves your `rea` running with the row's pipes (a console Ctrl+C reaches the\n  whole process group and does stop it). On POSIX the child is in the same group and dies with the terminal.\n\n## Configure — nothing\n\nThe shipped row carries **no machine path**:\n\n```yaml\n- insert:\n    - id: mcp-rea\n      name: '@deepseek-ai\u002Fdsh-mcp-client'\n      config:\n        serverName: rea\n        transport: stdio\n        command: !!js process.execPath\n        args:\n          - !!js \"(process.env.DSH_HOME && process.env.DSH_HOME.trim() !== '' ? process.env.DSH_HOME : (process.env.USERPROFILE || ((process.env.HOMEDRIVE || '') + (process.env.HOMEPATH || '')) || process.env.HOME || '') + '\u002F.dsh') + '\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs'\"\n          - mcp\n        env:\n          ELECTRON_RUN_AS_NODE: '1'\n        failOnStartupError: false\n```\n\nThree details in that row are load-bearing, and all three were bugs first:\n\n- **The `!!js` scalar is quoted.** A bare scalar containing `? ` and `: ` is not one scalar: YAML reads it as an explicit key\u002Fvalue pair, so the loader receives a *mapping* where an expression belongs. Measured against the harness's own dialect (`JSON_SCHEMA` plus the `!!js` type from `app.asar`), the unquoted form made `args[0]` into `{\"[object Object]\": \"...\"}` — an object the row cannot launch with. Quoting removes the ambiguity, and the JS string literals inside then use single quotes.\n- **Forward slashes, never backslashes.** A single-quoted YAML scalar keeps `\\` literal, and JavaScript then consumes it: `'\\plugins'` is not a path but the BEL control character, `'\\rea-bundle'` becomes a carriage return and `'\\bin'` a backspace — the path is destroyed silently. Forward slashes work on Windows and on POSIX.\n- **The `DSH_HOME` fallback is `\u003Chome>\u002F.dsh`, not `\u003Chome>`.** The shim, `setup.mjs` and `bootstrap.ps1` all resolve an unset `DSH_HOME` to `\u003Chome>\u002F.dsh\u002F...`; a fallback that stopped one level higher made the row point at a file provisioning never writes — invisible on any machine where `DSH_HOME` happens to be set.\n- **The home fallback mirrors `os.homedir()`: `USERPROFILE`, then `HOMEDRIVE`+`HOMEPATH`, then `HOME`.**\n  The middle pair is what a Windows service account gets instead of `USERPROFILE`; an expression that skipped\n  it produced `\u002F.dsh\u002F...` — the root of the current drive — for exactly the accounts that cannot read that\n  path. Only when all four are empty (a container account with no home at all) does the concatenation land on\n  `\u002F.dsh\u002F...`, and `failOnStartupError: false` keeps that quiet, so the only symptom is \"no `mcp__rea__*`\n  tools\". Set `DSH_HOME` explicitly on such an account.\n- **No `path`\u002F`require` in a `!!js` expression**: `ctx` is the loader scope, so the DSH_HOME fallback is written as plain concatenation.\n\n`npm run bundle:audit` enforces the first three: it evaluates this exact expression with `DSH_HOME` unset, empty and set, and rejects a bare scalar that YAML would read as a mapping.\n\n`ELECTRON_RUN_AS_NODE` only matters if the harness itself runs on Electron; it is harmless for a plain Node host, and stating it explicitly beats relying on it surviving the child-env scrub.\n\n`command` is the running Node executable and `args[0]` is computed from `DSH_HOME`, so the same patch works\non Windows, macOS and Linux without a PATH entry. Everything machine-specific lives in\n`\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbundle.json`, written by setup and readable with:\n\n```\nnode bin\u002Frea-shim.mjs --shim-status      # resolved paths, versions, verification state\nnode bin\u002Frea-shim.mjs --shim-print-env   # the environment the MCP child will see\n```\n\nBoth commands answer with the same bundle whether you run them from the plugin directory (the checkout copy)\nor from `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle` (the mirrored copy): the checkout resolves the location by the same\nrule setup uses, while the mirror uses its own position — it cannot rely on `DSH_HOME`, which the harness\nstrips from an MCP child's environment.\n\nSetting `REA_BUNDLE_AUTO_PROVISION=0` disables the first-run background provisioning.\n\nTo pin a different launch command on this machine (for example your own globally installed `rea`), override\nthe row from the profile's own `cordis.patch.yml` — that file is machine-local and never published. Only one\nrow may use `serverName: rea` in a profile.\n\nA patch replaces the row's `config` **wholesale** — it does not deep-merge — so a partial override such as\n`config: {env: {...}}` also deletes `command`, `args` and `serverName`, and the row stops starting. Supply the\ncomplete config, or use `REA_BUNDLE_LAUNCH=system` (above), which needs no patch at all.\n\nGhidra and the JDK are not read from your environment at launch: `setup.mjs` records their absolute paths in\n`bundle.json` and the shim exports `GHIDRA_INSTALL_DIR` \u002F `JAVA_HOME` from that record — which is why a\nnon-standard install location works without touching the row. To adopt an existing install, point\n`GHIDRA_INSTALL_DIR` \u002F `JAVA_HOME` at it and run `node scripts\u002Fsetup.mjs --reuse`: the manifest's probe checks\nthe candidate and falls back to a download when it does not match, so a wrong path is reported during setup\ninstead of failing later inside a tool call.\n\n### One REA registration only\n\nUpstream REA 6.5.0 can register itself with DSH:\n\n```\nrea setup --client dsh\n```\n\nThat writes a row with `id: rea-mcp` and `serverName: rea` into `\u003CDSH_HOME>\u002Fcordis.patch.yml`. This\nbundle's row is `id: mcp-rea` with the same `serverName`, and DSH rejects the later of two entries\nthat share a server name — so which REA row survives depends on load order. **Use one, not both.**\n\nWhich one? Upstream's is the configuration-only route: it expects Node on REA's engines,\n`rea-agents` reachable, and a Ghidra\u002FJDK you provisioned yourself. This bundle is the\nzero-dependency route: it fetches Node, REA, JDK and Ghidra on first run. The patch shape and the\n`mcp__rea__*` surface are the same either way.\n\nThe shim reports the conflict on startup — `setup.mjs` records the DSH home it ran under in\n`mirror.json`, and the mirrored copy reads that when the harness has stripped `DSH_*` from its\nenvironment; `node bin\u002Frea-shim.mjs --shim-status` lists it under `duplicateRegistration`.\n\n## Verify\n\nAsk for a `mcp__rea__*` call — `binary_session` reports the engine checks\n(`configuration` \u002F `installation` \u002F `headless` \u002F `native_decompiler` \u002F `java`), all of which should be\n`ok: true`. Then `open_binary` a PE, `binary_overview`, and `procedure_pseudo_code`.\n\nProvisioning itself ends with three checks: `rea doctor --json` (reported, with unmet checks named), a\n**Ghidra smoke import** that runs `rea analyze` on the JDK's own `bin\u002Fjava` and fails the run when it\ncannot import (`--skip-smoke` omits it), and the exact shim launch the MCP row will use. The smoke step\nexists because `doctor` validates paths, versions and which Java would be chosen — it never imports a\nbinary, so a broken bridge or a mismatched native addon used to surface only as an MCP timeout.\n\n**The first analysis of each new target is slow.** Measured here: 36 s for a small PE, 64 s for a 6 MB\nDLL — every import starts a JVM and runs Ghidra's default auto-analysis. If the first\n`open_binary` → `list_segments` on a large file times out, that is this cost and not a broken engine:\ncall it again and the open target answers immediately, because it keeps its ephemeral project.\n\n## Verified against\n\nAuthor's machine, 2026-10-13 \u002F 14 \u002F 16 \u002F 19 \u002F 23 \u002F 26:\n\n| Component | Version |\n|---|---|\n| REA (`rea-agents`) | **6.8.0** (229 tools) |\n| DeepSeek Harness | desktop runtime **0.2.0-rc.2**, with its bundled dsh CLI 0.1.7-rc.2 |\n| `@deepseek-ai\u002Fdsh-mcp-client` (row provider) | bundled by the harness |\n| Ghidra | 12.1.4 |\n| JDK | Temurin 21 (provisioned) \u002F Amazon Corretto 25.0.5 (adopted by the host) |\n| Node.js | **24.22.1** adopted (REA engines: `^22.23.4 \\|\\| ^24.22.1 \\|\\| >=26.12.1`; ABI 137) |\n| Host | Windows 11 x64 (build 26200) |\n\nExercised live: a clean `setup.mjs --reuse` run reached `setup complete`; an MCP `initialize` handshake\n**through the shim with no `rea` on PATH** answered `serverInfo {name: rea, version: 6.8.0}`; and the whole\nchain ran under the real desktop host — its own Electron binary launched\n`\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs mcp`, the shim started the **vendored** REA, and a live tool\ncall reported `path_source: environment` for a path only that machine's `bundle.json` knows.\n\nNot exercised end to end here, and therefore not claimed: the ~775 MB JDK + Ghidra download branch (planned\nby `--dry-run`, but this machine adopted its existing installs with `--reuse`), and first runs on\nmacOS\u002FLinux.\n\nOn 2026-10-23 the same chain was re-verified after the host Node moved to **24.22.1**: the vendored\n`rea-windows-x64.node` and both `conpty` addons loaded under ABI 137; `binary_overview` on `conpty.node`\nreported 10 segments \u002F 1178 procedures \u002F 618 strings; `procedure_pseudo_code` returned real decompiled\noutput; and `java -version` from the vendored JDK answered `21.0.12.1`. The one thing that did **not**\nwork on the first try was the first `list_segments` — it timed out at 64 s of cold Ghidra analysis, which\nis what the smoke step and the note above are for.\n\nOn 2026-10-26 the pin moved to **REA 6.8.0** and the chain was re-verified on the same machine: `setup.mjs\n--reuse` reported `probe cli=6.8.0 tools=229` and its Ghidra smoke imported `java.exe` in 26 s; an independent\nMCP handshake over stdio answered `serverInfo {name: rea, version: 6.8.0}` with **229 tools \u002F 6 prompts** on\nprotocol `2024-11-05`; apktool still answers `3.0.3`, `adb` still reports 37.0.1 (1.0.41), and the jadx jar on\ndisk still hashes to `6e5eacf5…` — the value REA 6.8.0 requires. Executing 6.8.0's own release policy gave\n`12.1.4 → verified`, `12.1.5 → compatible`, `12.2 → unsupported`, so Ghidra stays where it was.\n\n## Scope and caveats\n\n- REA exposes 229 tools at 6.8.0, so every session's tool list grows.\n- Provisioning is honest about checksums: where a vendor publishes one it is pinned and verified; where none\n  exists (jadx-headless-mcp, the apktool wrapper, Android platform-tools, the rolling \"latest\" URLs) the\n  hash is **measured once and recorded**, and later drift is reported rather than silently accepted.\n  `bundle.json` lists which components are in that state under `unverifiedChecksums`.\n- Downloads are slow on some networks (measured 0.35–0.54 MiB\u002Fs from the GitHub release CDN here, so Ghidra\n  alone takes ~25–45 min). Fetching is resumable on both routes: a partial `.part` is continued with a Range\n  request, and a `.part` that already holds the whole file is verified and kept instead of re-fetched.\n- Some REA tools launch processes, write files and access the network — DSH's permission policy applies.\n  Analyse only artifacts you are authorized to inspect.\n- The MCP child and the engines it launches are ordinary processes running as **you**. DSH's tool\n  approvals govern what the model asks for, not what that child may read, write or send on its own;\n  treat the plugin like any third-party code you install.\n- On Linux, upstream's process-ownership checks need a **procps-compatible `ps`** on PATH; the\n  Ghidra\u002FJADX and firmware paths use it. A minimal image without it fails those tools, not the install.\n- `scripts\u002Fsetup.mjs` runs `npm install` for the REA CLI with `--ignore-scripts`: the published tarball\n  carries no `node_modules`, so a dependency closure must be resolved, and package lifecycle scripts are\n  skipped on purpose during provisioning. That step is also the **only** one that cannot use this bundle's\n  own route\u002Fproxy resolution — npm reads `HTTPS_PROXY`\u002F`HTTP_PROXY` and `NPM_CONFIG_REGISTRY` from the\n  environment, so on a network that needs a mirror, set one of those before provisioning\n  (`NPM_CONFIG_REGISTRY=https:\u002F\u002Fregistry.npmmirror.com`). With `--offline` the step is refused outright\n  instead of quietly going online.\n- On POSIX, extraction falls back when the usual tools are missing: a zip is opened with `unzip`, then\n  `python3 -m zipfile`, then the provisioned JDK's `jar`; a tarball with `tar`, then `python3`. A minimal\n  image that has none of them fails with an explicit \"no zip extractor available\" line rather than a bare\n  \"extraction failed\". This matters because Ghidra is a zip on every platform and `unzip` is not installed\n  everywhere by default.\n\n## Updating — three independent things\n\n> **Two standing rules — for this release and for every later 2.x release.**\n>\n> `setup.mjs` reconciles the **bundle**, never the **plugin**: it reads the manifest of the plugin that is\n> installed *right now*. Installed 2.0.9? setup provisions 6.5.0 even after a newer release exists. The package changes\n> only through the market (or `npm i -g dsh-plugin-rea@\u003Cversion>` \u002F a `file:` reinstall); running setup more\n> often never substitutes for that.\n>\n> **And nothing runs it for you.** A market install or update replaces the package and stops; the first\n> `setup.mjs` — and one after every upgrade — is yours to run, from the plugin directory.\n>\n> 1. **The row executes a copy of this plugin's launcher.** `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs` is a\n>    mirror made by setup, and a market upgrade replaces the package without replacing it — so the mirror can keep\n>    running old code with **no symptom at all**. Since **2.0.8** the mirror compares itself with the launcher\n>    recorded in `mirror.json` and re-execs the plugin's copy when the two differ (one hop, guarded by\n>    `REA_BUNDLE_SHIM_REEXEC`). A mirror **older than 2.0.8 cannot do this**, so upgrading from **2.0.0–2.0.7**\n>    still needs one `node scripts\u002Fsetup.mjs`. `--shim-status` reports `mirror.stale`; `check-updates.ps1` says\n>    when to re-run.\n> 2. **The mirrored `scripts\u002Fsetup.mjs` is a copy too, and it has no self-heal of its own.** Running setup from the\n>    bundle root therefore executes the version from when the bundle was created, not the one you just installed.\n>    Run it once **from the plugin directory** to refresh that copy. Since **2.0.9** the copy strips the UTF-8 BOM\n>    from `mirror.json` \u002F `bundle.json` exactly as the shim does, so a BOM-ed record no longer reads as \"no record\"\n>    on the setup side (which used to re-mirror the previous manifest).\n\n### Updating from the market\n\nUpdate it where you installed it. Updating **to 2.0.5 or later** is routine; updating an installed 2.0.1–2.0.4\n*through the in-app market* failed with this, three times on a healthy profile:\n\n> dsh-plugin-rea 更新后缺少入口文件（package.json 的 main\u002Fexports 指向的文件不存在），且未能验证恢复原版本文件\n\nThat message is not about a broken tarball. On every update the market asks whether the new build's\n`package.json` points at an entry file that exists (`hasLoadableEntry` in `dshmarket`), and rolls the build back\nwhen the answer is false. A bundle may legitimately own no entry — but only if a package it *mounts* has one, and\nthe package this bundle mounts (`@deepseek-ai\u002Fdsh-mcp-client`) ships inside the harness build rather than in\n`node_modules`, so every location that test probes comes back empty. The 1.x and 2.0.x manifests also declared\nno `main` at all, so the answer was false even though the profile was working; the rollback was then reported as\nunverifiable for the same reason. Nothing in the profile was actually damaged — the update simply did not apply.\n\n2.0.5 declares `main: .\u002Findex.mjs`. That file is a real, deliberately empty Cordis plugin: it registers nothing,\nspawns nothing, touches no state — the working row is still inserted by `cordis.patch.yml` — and it exists so the\nquestion \"can this package load?\" has an honest answer, and so `import 'dsh-plugin-rea'` resolves. On an older\nversion, update to `>=2.0.5`; if the market refuses before that version exists, install `dsh-plugin-rea@2.0.5`\n(Desktop: through the plugin manager) instead of updating in place.\n\n### Upgrading from 1.x\n\nInstall 2.x, then run provisioning **once** before restarting dsh:\n\n```\nnode scripts\u002Fsetup.mjs          # or: powershell -File scripts\\rea-bundle-bootstrap.ps1 -Reuse\n```\n\nWhy it matters: the 1.x row launched `rea mcp` from PATH, while the 2.x row launches\n`\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs` — a file only `setup.mjs` creates. Upgrade and restart\nwithout provisioning and that row exits with `MODULE_NOT_FOUND`: the harness stays up\n(`failOnStartupError: false` is deliberate), but the `mcp__rea__*` tools are gone and you get **no\nremediation message**, because the shim that prints one does not exist yet. After the setup run they are\nback on the next start.\n\n- **This plugin** never updates itself. Installed from a market? Update it there. Installed from a `file:`\n  checkout? Overwrite the directory and reinstall.\n- **The row runs a COPY of this plugin's launcher.** `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fbin\u002Frea-shim.mjs` is a mirror\n  made by setup, so a market upgrade replaces the package but not the file the row executes — until setup runs\n  again. From **2.0.8** the mirror notices that the installed plugin ships a different launcher and re-execs it\n  (one hop, guarded by `REA_BUNDLE_SHIM_REEXEC`), so upgrades carry. On an older version the mirror stays behind\n  silently; `--shim-status` now reports `mirror.stale`, and `check-updates.ps1` says to re-run setup. If you are\n  upgrading from 2.0.0–2.0.7, run `node scripts\u002Fsetup.mjs` once after the upgrade.\n- **The mirrored `scripts\u002Fsetup.mjs` is a copy too.** A market upgrade replaces the package but not that copy,\n  so a remediation command that points at `\u003CDSH_HOME>\u002Fplugins\u002Frea-bundle\u002Fscripts\u002Fsetup.mjs` runs the setup from\n  when the bundle was created — not the one you just installed. From **2.0.9** both the shim and setup strip a\n  UTF-8 BOM from `mirror.json` \u002F `bundle.json` (a BOM-ed record used to read as \"no record\" on the setup side,\n  which re-mirrored the previous manifest). Running `node scripts\u002Fsetup.mjs` once **from the plugin directory**\n  refreshes that copy too.\n- **Upgrading from `2.0.0-bundle.1`**: that release mirrored the shim and `setup.mjs` but not the manifest,\n  and recorded nothing about the plugin checkout, so a bundle it provisioned cannot repair itself — its\n  background provisioning dies on the missing manifest, invisibly, and its own lock suppresses retries for\n  six hours. Re-run `node scripts\u002Fsetup.mjs` **from the plugin directory** once; it re-mirrors all three\n  files and writes the record that lets later runs find the checkout. `check-updates.ps1` reports this state.\n- **Provisioned components**: re-run `node scripts\u002Fsetup.mjs`; it re-verifies what is on disk and only\n  fetches what is missing or wrong. Pins live in `manifest\u002Fbundle.manifest.json`.\n- **Upstream REA \u002F Ghidra**: update the pins in the manifest, then re-run setup. REA version-gates Ghidra\n  (12.1.x today), so update REA first.\n\n`check-updates.ps1` (Windows PowerShell) reports the state of all of these at once. `npm run check-updates`\nforwards to it on Windows (PowerShell 5.1, then `pwsh`); on macOS\u002FLinux it prints why the script cannot\nanswer there and names the two portable commands instead — `node bin\u002Frea-shim.mjs --shim-status` and\n`node scripts\u002Fsetup.mjs --dry-run` — exiting non-zero rather than reporting a green it did not earn.\n\n## Still prefer the configuration-only 1.x line?\n\n1.x is the adapter **without** provisioning: one row that launches `rea mcp` from PATH and expects Ghidra to be\ninstalled by you. It is the right choice when you already manage REA and Ghidra yourself, when you do not want\nthe plugin fetching anything, or when you want the row to keep pointing at your own `rea` binary:\n\n```\ndsh plugin --profile \u003Cprofile> add dsh-plugin-rea@1.0.6\n# or pin the branch instead of the version:\ndsh plugin --profile \u003Cprofile> add github:czcdr\u002Fdsh-plugin-rea#v1.0.6\n```\n\nBoth lines publish the same patch shape and the same `mcp__rea__*` surface. 1.x is maintained on the\n[`v1` branch](https:\u002F\u002Fgithub.com\u002Fczcdr\u002Fdsh-plugin-rea\u002Ftree\u002Fv1), where its own install notes live (Node on REA's\nengines, `npm i -g rea-agents`, Ghidra 12.1.x for native analysis).\n\n## License\n\nMIT (this adapter). REA is MIT and owned by its authors; Ghidra is Apache-2.0 and owned by the NSA; Temurin\nis GPLv2+CE; Node.js is MIT; apktool, jadx-headless-mcp and Android platform-tools keep their own licences.\n**No upstream code or binary is redistributed in this package** — provisioning fetches each component from\nits official source.\n",2,"2026-10-31 04:26:48","CREATED_QUERY"]