dsh-plugin-rea 1.0.6 → 2.0.0
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/CHANGELOG.md +124 -0
- package/README.md +182 -72
- package/README.zh-CN.md +185 -85
- package/assets/make-screenshots.ps1 +14 -8
- package/assets/screenshot-1.png +0 -0
- package/bin/rea-shim.mjs +356 -0
- package/check-updates.ps1 +68 -0
- package/cordis.patch.yml +39 -31
- package/docs/bundle-design.md +186 -0
- package/manifest/bundle.manifest.json +399 -0
- package/package.json +22 -3
- package/scripts/audit-manifest.mjs +445 -0
- package/scripts/rea-bundle-bootstrap.ps1 +192 -0
- package/scripts/setup.mjs +1453 -0
- package/scripts/setup.test.mjs +418 -0
- package/scripts/verify-branch.ps1 +131 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,130 @@
|
|
|
2
2
|
|
|
3
3
|
本插件遵循语义化版本。版本号变化说明「配置组合」这层的变化,与 REA 自身的版本无关。
|
|
4
4
|
|
|
5
|
+
## 2.0.0 — 2026-10-15
|
|
6
|
+
|
|
7
|
+
**一体包线的首个正式版**(此前的 `2.0.0-bundle.1` / `2.0.0-bundle.2` 是它的两个预发布)。
|
|
8
|
+
`main` 现在就是这一条;1.x 的纯配置适配器保留在
|
|
9
|
+
[`v1` 分支](https://github.com/czcdr/dsh-plugin-rea/tree/v1),npm 上仍可装 `dsh-plugin-rea@1.0.6`。
|
|
10
|
+
|
|
11
|
+
### 🔴 从 1.x 升级:先跑一次供给,再重启
|
|
12
|
+
|
|
13
|
+
1.x 的行指向 PATH 上的 `rea mcp`;2.x 的行指向 `<DSH_HOME>/plugins/rea-bundle/bin/rea-shim.mjs`,
|
|
14
|
+
而**那个文件只有 `setup.mjs` 会创建**。所以从 1.x 升上来后直接重启会:
|
|
15
|
+
|
|
16
|
+
- 让该行以 `MODULE_NOT_FOUND` 退出(harness 本身不受影响 —— `failOnStartupError: false` 是刻意的),
|
|
17
|
+
于是 `mcp__rea__*` 工具消失,而且**不会得到任何修复提示**:会打提示的 shim 本身还不存在;
|
|
18
|
+
- 正确做法是在插件目录跑一次 `node scripts/setup.mjs`(或 `scripts\rea-bundle-bootstrap.ps1 -Reuse`),
|
|
19
|
+
再重启 dsh,工具就回来了。此后每一次「状态缺失」都由 shim 自己发现并在后台补供给。
|
|
20
|
+
|
|
21
|
+
(如果你在 profile 的 `cordis.patch.yml` 里按 id 覆盖过 `mcp-rea`(例如钉 `cmd.exe /c rea mcp`),
|
|
22
|
+
那条覆盖会继续生效,升级不影响你。)
|
|
23
|
+
|
|
24
|
+
### 三个致命缺陷(按旧版说明走的用户每一步都会撞上)
|
|
25
|
+
|
|
26
|
+
- **MCP 行里的 `!!js` 标量没有加引号。** 裸标量同时含 `? ` 与 `: ` 时,YAML 把它读成**显式键值对**,
|
|
27
|
+
loader 拿到的是 mapping 而不是表达式节点。用 harness 自己的方言实测(`JSON_SCHEMA` + `app.asar` 里的
|
|
28
|
+
`!!js` 类型定义),`args[0]` 变成 `{"[object Object]": "..."}` —— 这一行**从来没有可能启动**。
|
|
29
|
+
现改为双引号标量,内部 JS 字符串用单引号。
|
|
30
|
+
- **`DSH_HOME` 的兜底少了一层 `.dsh`。** 行回退到裸 home,而 shim、`setup.mjs`、`bootstrap.ps1` 都回退到
|
|
31
|
+
`<home>/.dsh`。全归档扫描确认 **DSH 从不设置 `DSH_HOME`**(宿主进程内 0 处写入,只在 shell 工具子进程的
|
|
32
|
+
执行覆盖层注入),所以这是**默认路径**而不是边缘情况:行永远指向供给不会写的位置。
|
|
33
|
+
- **镜像副本缺 manifest。** 镜像后的 `setup.mjs` 用它自己所在的位置推导 plugin root,于是 `loadManifest()`
|
|
34
|
+
去读 `<bundle>/manifest/bundle.manifest.json` —— 一个只有镜像步骤才能创建的文件。由 shim 触发的后台供给
|
|
35
|
+
会以裸 ENOENT 崩溃;`stdio: "ignore"` 让它完全不可见,而它已写下的 lock 又让重试被抑制六小时。
|
|
36
|
+
|
|
37
|
+
### 🟡 一并修掉的
|
|
38
|
+
|
|
39
|
+
- **shim 改用自身位置定位 bundle。** MCP 子进程的环境会剥掉所有 `DSH_*`(大小写不敏感),所以自定义
|
|
40
|
+
`DSH_HOME` 时旧 shim 会去 `~/.dsh` 找 `bundle.json`、报"未供给",然后**在错位置再供给一次 800 MB**。
|
|
41
|
+
`bundleRoot()` 现在返回 `<shim 所在目录>/..`,与 `DSH_HOME` 是否可见无关。
|
|
42
|
+
- **新增镜像记录 `mirror.json`。** 真源运行会写下插件 checkout 的位置;镜像副本据此回到真源,插件升级后
|
|
43
|
+
不会永远用旧 manifest 供给。记录失效(目录被移动/删除)时优雅回退到本地副本。
|
|
44
|
+
- **空值环境变量。** `REA_BUNDLE_ROOT=""` / `DSH_HOME=""` 原先会把 root 算成 `""` 或相对路径 —— 前者
|
|
45
|
+
`mkdir ''` 崩在任何日志之前,后者把 bundle 装进 cwd。现在统一 trim 后回退;`--root`/`--cache` 一律
|
|
46
|
+
`path.resolve`(相对 root 会让 `bundle.json` 记下相对路径,shim 随即误判组件"已消失"并重新供给)。
|
|
47
|
+
- **`fail()` 的修复命令**不再指向"存在但必然崩溃"的镜像副本;没有可信副本时给出明确指引。
|
|
48
|
+
- **`check-updates.ps1`** 增加「镜像 manifest 是否就位」检查,指向上面那条升级路径。
|
|
49
|
+
- **shim 的两份副本各自解析出正确的 bundle root。** 上面那条"用自身位置"只在**镜像副本**上成立:
|
|
50
|
+
插件 checkout 里的那份是插件而不是 bundle,于是 `npm run bundle:status` 会在已供给的机器上报
|
|
51
|
+
"未供给",而裸跑 `node bin/rea-shim.mjs mcp` 会把约 800 MB 下载进**插件目录**。现在用
|
|
52
|
+
`cordis.patch.yml`(只有 checkout 会带)区分两者:镜像副本用自身位置(MCP 子进程里读不到
|
|
53
|
+
`DSH_HOME`),checkout 副本按与 setup 相同的规则解析。setup 结尾打印的 `status:` 命令也改为指向
|
|
54
|
+
镜像副本,这样它对自定义 `--root` 同样成立。
|
|
55
|
+
- **裸跑 shim 不再启动任何东西。** `package.json` 把这个文件暴露为 `rea-bundle-status` bin,而 npm 的 bin
|
|
56
|
+
转发只能原样传参、**无法追加 flag**,于是"裸跑一个叫 status 的命令"会掉进启动路径:在已供给的机器上
|
|
57
|
+
启动 REA,在未供给的机器上**开始约 800 MB 下载** —— 与这个名字承诺的正好相反。现在无参数时打印用法并
|
|
58
|
+
退出(真实退出码 0),MCP 行不受影响,因为它始终显式传 `mcp`。
|
|
59
|
+
- **`--dry-run` 在裸机上误报失败并退出 1。** 计划项返回 `{ planned: true }`(没有 `path`),而只有真实
|
|
60
|
+
`path` 才被计入 `context.resolved`,于是每个依赖前序组件的计划项都被判成"依赖不可用":在**裸机** —— 恰恰
|
|
61
|
+
是 README 建议先跑 `--dry-run` 的那台机器 —— 整条链报 4 条 FAIL 且退出码为 1。现在计划项同样满足下游的
|
|
62
|
+
`requires`。(本机 `--reuse` 会 adopt 成功,所以这个缺陷只在干净机器上可见。)
|
|
63
|
+
- **`release.yml` 的 npm 自更新**由 `npm@latest` 钉到 `npm@^11`:发布当天不该让流水线获得新行为。
|
|
64
|
+
- **`check-updates.ps1` 的 engines 判定**:不认识的 pattern(`22.x`、`>=22` 之类)此前返回"不满足",误导
|
|
65
|
+
用户去升级一个本来就满足的 Node;现在返回"无法解析"。三条实测:`22.x` → 无法解析、`>=99.0.0` → 不满足、
|
|
66
|
+
`^24.11.0` → 满足。
|
|
67
|
+
- **README 的「先装插件也可以」改为诚实描述。** 发布的行指向的镜像 shim **由 setup 创建**,所以在第一次
|
|
68
|
+
供给之前该行以 `MODULE_NOT_FOUND` 退出 —— 没有任何东西能启动后台供给。首次必须手动跑一次
|
|
69
|
+
`setup.mjs`,此后才由 shim 自动补供给。
|
|
70
|
+
|
|
71
|
+
### 🔴 真机验证(Windows 11 x64,Electron 宿主 0.2.0-rc.2)当场抓到的
|
|
72
|
+
|
|
73
|
+
- **bootstrap 的便携 Node 路径必崩。** `$nodeFolder` 被连续剥了两次扩展名:`node-v24.21.0-win-x64.zip`
|
|
74
|
+
→ `node-v24.21`,而官方 zip 解出来的目录是 `node-v24.21.0-win-x64`。于是在**没有任何 Node 的机器**上
|
|
75
|
+
—— 也就是这个脚本唯一的读者 —— 它会先成功下载(35 MiB)、校验通过、解压完成,然后抛
|
|
76
|
+
`the Node zip did not contain node-v24.21`。本机有 Node,永远走不到这段分支,所以单测、审计、WSL 与
|
|
77
|
+
沙箱端到端全都看不见它;真机复现(清空 PATH 模拟无 Node)一次即中。
|
|
78
|
+
现在只剥一次,且解压后**接受任何真的带 `node.exe` 的目录**:已经付过下载代价的机器不该因为一次命名
|
|
79
|
+
漂移而卡住。`-DryRun` 也会去认已经解压好的便携 Node(只认、不下载),这正是下面那条回归检查能
|
|
80
|
+
**零下载**判定它的原因。
|
|
81
|
+
|
|
82
|
+
### 🟡 真机验证顺带修掉的(判据都来自仓库自身)
|
|
83
|
+
|
|
84
|
+
- **manifest 的 `optional: true` 被忽略。** `androidPlatformTools` 明写可选("没有设备时这些工具只是报告
|
|
85
|
+
没有设备"),而 `setup.mjs` 里 `optional` 出现 **0 次**:dl.google.com 不可达时整轮 exit 1 —— 其余组件
|
|
86
|
+
都供给好了,却因为 adb 失败。现在可选组件的失败**照实报告**(`optional component(s) unavailable`),但
|
|
87
|
+
不计入退出码、也不写进 `bundle.json`(shim 因此不会导出 `REA_ADB_PATH`);被别的组件 `requires` 的组件
|
|
88
|
+
永不被当作可选,免得"依赖失败却退出 0"。
|
|
89
|
+
- **`--reuse` 采纳 Node 只看"文件存在"。** manifest 的 `probe.why` 明写"reuse 路径必须在这里拒绝",但旧代码
|
|
90
|
+
只做 `fileExists`:PATH 上第一个是 Node 20 的机器会把它记进 `bundle.json`,失败推迟到 REA 启动才以难读的
|
|
91
|
+
形式出现。现在每个候选(`REA_BUNDLE_NODE` 与**每一个** PATH 命中)都要先过 manifest 的版本闸门;跑不起来
|
|
92
|
+
的候选(Windows 上 `.cmd`/`.bat` 包装器即 EINVAL)同样拒绝,并把原因打出来。
|
|
93
|
+
- **README:apktool 在 Windows 上实际不可用**(能力矩阵新增一行)——上游 `execFile` 不加 shell 直接起该命令,
|
|
94
|
+
而 Windows 不能这样执行 `.bat`/`.cmd`。setup 的 apktool 探针走 `java -jar`(绕过包装器)所以报 `[ok]`,
|
|
95
|
+
落差只在运行时暴露。
|
|
96
|
+
- **README:Electron 宿主不能用 `dsh plugin --profile desktop …`**(CLI 硬编码拒绝 `desktop` 这个 profile 名),
|
|
97
|
+
改为指向应用内插件管理器。
|
|
98
|
+
|
|
99
|
+
### 回归防护
|
|
100
|
+
|
|
101
|
+
- `audit-manifest.mjs` 新增六项,全部做过反向验证(**故意写坏 6/6 都被抓住**):`!!js` 裸标量 mapping
|
|
102
|
+
检测、`DSH_HOME` 三态(未设/空/已设)求值一致性、README 与 patch 表达式不得漂移、镜像清单必须含
|
|
103
|
+
manifest、镜像清单**不得**携带 `cordis.patch.yml`(它是 shim 区分两份副本的判据,被镜像进去会让镜像
|
|
104
|
+
副本退回 `DSH_HOME` 规则)、**两份 README 结构平行**(节/子节数量,双语靠手工同步)。另外把
|
|
105
|
+
「`DSH_HOME`/`USERPROFILE`/`HOME` 全空 → 根路径」这个**已知无害行为**钉成 warn(此前无覆盖):该情形下
|
|
106
|
+
该行报 `MODULE_NOT_FOUND`,不写文件、不下载,harness 照常。
|
|
107
|
+
- `validate.yml`:YAML 步骤注册 `!!js` 标量构造器(原先 `safe_load` 直接拒绝这个仓库要发布的行 ——
|
|
108
|
+
**CI 一直是红的**);行契约步骤断言 `args[0]` 是标量、不是 mapping;新增 `bundle:test` 与 `bundle:audit` 两步。
|
|
109
|
+
- `verify-branch.ps1`:dry-run 断言改为**不带 `--reuse`**(旧写法在本机 adopt 成功时正好掩盖了上面那个
|
|
110
|
+
裸机缺陷),并新增"不得出现依赖不可用"一条;它现在也检查**自己**的 BOM,因为它是被编辑得最频繁的脚本。
|
|
111
|
+
- `verify-branch.ps1` 新增第 8 节:**空 PATH + 官方 zip 布局**下,bootstrap 必须认出已解压的便携 Node、
|
|
112
|
+
且不得计划下载(`-DryRun`,零下载、零副作用)。反向验证:把该脚本 checkout 回 HEAD 版(双重剥离)
|
|
113
|
+
→ 两条断言立刻变红(`2 CHECK(S) FAILED`/`plan: download and extract ...`)。
|
|
114
|
+
- 单元测试新增四条**真的去 spawn shim**的用例(镜像布局、checkout 布局、`REA_BUNDLE_ROOT` 覆盖、裸跑只
|
|
115
|
+
打印用法),并逐条做过反向验证:把 shim 改回"一律用自身位置"时 checkout 那条会红;去掉裸跑守卫时裸跑
|
|
116
|
+
那条会红(后者自带桩保护,即使回退也只会跑到桩、不会下载)。
|
|
117
|
+
- 单元测试里的 POSIX 退化断言加平台守卫(`EXE` 为空时 `endsWith("java")` 恒真,在 Linux 上必红;
|
|
118
|
+
WSL 实测修前 12/13、修后 17/17)。
|
|
119
|
+
- 单元测试新增四条(共 **21** 条;Windows 21/21、WSL 真 Linux 21/21):`--reuse` 仍采纳兼容 Node、拒绝
|
|
120
|
+
manifest 版本闸门不认的候选(用**改了正则的合成 manifest** 判定,跨平台确定,不依赖机器上恰好有什么)、
|
|
121
|
+
拒绝跑不起来的候选、以及"可选组件失败不致命"。反向验证:把可选记账改回旧行为 → 那条立刻变红;把复用
|
|
122
|
+
检查改回旧行为 → 另两条立刻变红;两次都确认了源码按字节复原。
|
|
123
|
+
|
|
124
|
+
### 升级说明
|
|
125
|
+
|
|
126
|
+
已部署过 `2.0.0-bundle.1` 的机器**无法自愈**(旧镜像既缺 manifest 也没有真源记录):请从**插件目录**
|
|
127
|
+
重跑一次 `node scripts/setup.mjs`。
|
|
128
|
+
|
|
5
129
|
## 1.0.6 — 2026-10-13
|
|
6
130
|
|
|
7
131
|
文档结构调整,无行为变更。
|
package/README.md
CHANGED
|
@@ -6,25 +6,35 @@
|
|
|
6
6
|
|
|
7
7
|
Brings the upstream **[REA — Reverse Engineer Anything](https://github.com/morluto/rea)** MCP server into
|
|
8
8
|
**DeepSeek Harness** (dsh), so its tools show up in a session as `mcp__rea__*`.
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
> **This is the 2.x line, and `main` is it.** The all-in-one variant: it provisions the runtime (Node, the REA
|
|
13
|
+
> CLI, a JDK, Ghidra) and then wires the row itself. The original configuration-only adapter — 1.x, which
|
|
14
|
+
> assumed you had installed Node, `rea-agents` and Ghidra yourself — is preserved on the
|
|
15
|
+
> [`v1` branch](https://github.com/czcdr/dsh-plugin-rea/tree/v1) and on npm as `dsh-plugin-rea@1.0.6`.
|
|
16
|
+
> The two lines are compared in [Two lines: 1.x and 2.x](#two-lines-1x-and-2x).
|
|
10
17
|
|
|
11
18
|
## What this is
|
|
12
19
|
|
|
13
|
-
A **
|
|
14
|
-
one row of the official MCP client plugin (`@deepseek-ai/dsh-mcp-client`)
|
|
15
|
-
|
|
20
|
+
A **Cordis bundle with a provisioning layer**. `package.json` declares `dsh.bundle.patch`, and that patch
|
|
21
|
+
inserts one row of the official MCP client plugin (`@deepseek-ai/dsh-mcp-client`). The row launches
|
|
22
|
+
`bin/rea-shim.mjs` with the running Node executable; the shim reads the machine-local state that
|
|
23
|
+
`scripts/setup.mjs` wrote and starts the REA CLI it provisioned. There is no analysis code here, and the
|
|
24
|
+
published patch contains no machine path.
|
|
16
25
|
|
|
17
26
|
> **Third-party adapter.** This repository is not part of REA, ships none of REA's code, and is not
|
|
18
|
-
> maintained by REA's authors. It composes one configuration row.
|
|
27
|
+
> maintained by REA's authors. It composes one configuration row and fetches upstream's own releases.
|
|
19
28
|
> Upstream: [github.com/morluto/rea](https://github.com/morluto/rea) (MIT) ·
|
|
20
29
|
> [docs](https://github.com/morluto/rea#what-you-can-analyze) ·
|
|
21
|
-
> [中文文档](https://github.com/morluto/rea/blob/main/README_zh.md#可以分析什么) ·
|
|
22
30
|
> [rea.tools](https://rea.tools/) · npm package `rea-agents`.
|
|
23
31
|
> REA's own problems go to its tracker; **this plugin's** problems go to
|
|
24
32
|
> [this repository's issues](https://github.com/czcdr/dsh-plugin-rea/issues).
|
|
25
33
|
|
|
26
|
-
**It adds exactly
|
|
27
|
-
engine environment (`GHIDRA_INSTALL_DIR`,
|
|
34
|
+
**It adds exactly three things:** the tool registration under the `mcp__rea__*` names, the per-machine
|
|
35
|
+
engine environment (`GHIDRA_INSTALL_DIR`, `JAVA_HOME`, `REA_ADB_PATH`, `REA_APKTOOL_COMMAND`,
|
|
36
|
+
`REA_JADX_MCP_JAR`, `REA_BROWSER_EXECUTABLE`, …) scoped to that MCP child instead of the whole harness, and
|
|
37
|
+
the provisioning that puts those components on disk.
|
|
28
38
|
|
|
29
39
|
## What it can and cannot do
|
|
30
40
|
|
|
@@ -43,73 +53,120 @@ The analysis is upstream's; this section only restates upstream's own contract s
|
|
|
43
53
|
re-implementation, security review, migration reference. To obtain actual source code, use the legitimate
|
|
44
54
|
route — the upstream repository, vendor authorisation, or the vendor's own SDK.
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
**What provisioning cannot buy you.** These are upstream host gates, not settings this plugin forgot:
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
| Component | Version |
|
|
58
|
+
| Capability | Gate |
|
|
51
59
|
|---|---|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
| Hopper provider | macOS only |
|
|
61
|
+
| IDA provider | needs your own IDA and `REA_IDA_MCP_CONFIG`; REA never launches IDA |
|
|
62
|
+
| firmware `binwalk` / `unblob` | Linux only |
|
|
63
|
+
| Wakaru JS recovery, pwntools ELF/recorded-crash paths | Linux x64 only |
|
|
64
|
+
| pwndbg / gdb core context | Linux, macOS |
|
|
65
|
+
| native mitmproxy captures | Linux (`REA_MITMDUMP_COMMAND`); HAR needs no engine |
|
|
66
|
+
| Apple toolchain (Mach-O, plist, asset catalog, LLDB) | macOS only |
|
|
67
|
+
| `capture_process_scenario` | Linux/macOS PTY |
|
|
68
|
+
| 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`/`.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 |
|
|
69
|
+
|
|
70
|
+
## Two lines: 1.x and 2.x
|
|
71
|
+
|
|
72
|
+
| | **2.x** — this line, `main` | **1.x** — [`v1`](https://github.com/czcdr/dsh-plugin-rea/tree/v1) |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| 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 |
|
|
75
|
+
| What you need first | nothing for the runtime — provisioning fetches what is missing (with `--reuse` it adopts an existing Ghidra/JDK instead) | Node.js on REA's engines, `npm i -g rea-agents`, and Ghidra 12.1.x for native analysis |
|
|
76
|
+
| First run | run `scripts/setup.mjs` (or the bootstrap) once, then restart dsh | nothing to run |
|
|
77
|
+
| The row launches | the running Node executable plus `<DSH_HOME>/plugins/rea-bundle/bin/rea-shim.mjs`, which reads that machine's state | `cmd.exe /c rea mcp` on Windows, `rea mcp` on macOS/Linux |
|
|
78
|
+
| Machine state | `<DSH_HOME>/plugins/rea-bundle/` — `bundle.json`, `vendor/`, `cache/` | your own REA and Ghidra installs |
|
|
79
|
+
| Install | `dsh plugin --profile <profile> add dsh-plugin-rea` (npm `latest` ≥ 2.0.0) | `… add dsh-plugin-rea@1.0.6`, or `github:czcdr/dsh-plugin-rea#v1.0.6` |
|
|
80
|
+
|
|
81
|
+
Both lines ship the **same patch shape** — one `insert` row of `@deepseek-ai/dsh-mcp-client` — and offer the
|
|
82
|
+
same `mcp__rea__*` surface; they differ in who prepares the runtime. Moving from 1.x to 2.x takes two
|
|
83
|
+
commands: see [Upgrading from 1.x](#upgrading-from-1x).
|
|
61
84
|
|
|
62
85
|
## Install
|
|
63
86
|
|
|
64
|
-
**
|
|
87
|
+
**Nothing to install first.** On a bare Windows x64 machine:
|
|
65
88
|
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
Static JavaScript/Electron and .NET analysis need no engine at all.
|
|
89
|
+
```
|
|
90
|
+
powershell -ExecutionPolicy Bypass -File scripts\rea-bundle-bootstrap.ps1 -Reuse
|
|
91
|
+
```
|
|
70
92
|
|
|
71
|
-
|
|
93
|
+
`-Reuse` adopts a Ghidra/JDK you already have; omit it to download both (about 775 MB from the official
|
|
94
|
+
releases, checksum-verified). Then install the plugin for your profile:
|
|
72
95
|
|
|
73
96
|
```
|
|
74
|
-
dsh plugin --profile <profile> add github:czcdr/dsh-plugin-rea
|
|
75
|
-
# or, from npm:
|
|
76
97
|
dsh plugin --profile <profile> add dsh-plugin-rea
|
|
98
|
+
# or pin this release instead of tracking npm latest:
|
|
99
|
+
dsh plugin --profile <profile> add github:czcdr/dsh-plugin-rea#v2.0.0
|
|
77
100
|
```
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
|
|
102
|
+
> **On an Electron host (the DeepSeek Harness desktop app) the profile is owned by the application.**
|
|
103
|
+
> `dsh plugin --profile desktop …` is refused by the CLI (`profile "desktop" is managed exclusively by
|
|
104
|
+
> the Electron application`) — install or update through the in-app plugin manager instead. The CLI form
|
|
105
|
+
> above is for a profile you created yourself with the CLI under another name.
|
|
81
106
|
|
|
82
|
-
|
|
107
|
+
If Node is already installed, you can skip the bootstrap and run provisioning directly:
|
|
83
108
|
|
|
84
|
-
|
|
109
|
+
```
|
|
110
|
+
node scripts/setup.mjs --reuse # or --dry-run to see the plan first
|
|
111
|
+
```
|
|
85
112
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
Installing the plugin **first** also works, but it takes two steps. The row resolves to
|
|
114
|
+
`<DSH_HOME>/plugins/rea-bundle/bin/rea-shim.mjs`, and that file is created by setup — so until the first
|
|
115
|
+
provisioning run the row exits with `MODULE_NOT_FOUND`, and nothing is there to start a background
|
|
116
|
+
provisioning (the harness itself is unaffected: `failOnStartupError: false` is deliberate). Run
|
|
117
|
+
`node scripts/setup.mjs` once; from then on the shim is in place, and every later missing state is picked up
|
|
118
|
+
by the shim itself, which provisions in the background.
|
|
90
119
|
|
|
91
|
-
|
|
92
|
-
either:
|
|
120
|
+
## Configure — nothing
|
|
93
121
|
|
|
94
|
-
|
|
95
|
-
2. pin it for the MCP child only — see
|
|
96
|
-
[`examples/cordis.patch.with-env.yml`](examples/cordis.patch.with-env.yml).
|
|
122
|
+
The shipped row carries **no machine path**:
|
|
97
123
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
124
|
+
```yaml
|
|
125
|
+
- insert:
|
|
126
|
+
- id: mcp-rea
|
|
127
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
128
|
+
config:
|
|
129
|
+
serverName: rea
|
|
130
|
+
transport: stdio
|
|
131
|
+
command: !!js process.execPath
|
|
132
|
+
args:
|
|
133
|
+
- !!js "(process.env.DSH_HOME && process.env.DSH_HOME.trim() !== '' ? process.env.DSH_HOME : (process.env.USERPROFILE || process.env.HOME || '') + '/.dsh') + '/plugins/rea-bundle/bin/rea-shim.mjs'"
|
|
134
|
+
- mcp
|
|
135
|
+
env:
|
|
136
|
+
ELECTRON_RUN_AS_NODE: '1'
|
|
137
|
+
failOnStartupError: false
|
|
138
|
+
```
|
|
101
139
|
|
|
102
|
-
|
|
103
|
-
|
|
140
|
+
Three details in that row are load-bearing, and all three were bugs first:
|
|
141
|
+
|
|
142
|
+
- **The `!!js` scalar is quoted.** A bare scalar containing `? ` and `: ` is not one scalar: YAML reads it as an explicit key/value 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.
|
|
143
|
+
- **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.
|
|
144
|
+
- **The `DSH_HOME` fallback is `<home>/.dsh`, not `<home>`.** The shim, `setup.mjs` and `bootstrap.ps1` all resolve an unset `DSH_HOME` to `<home>/.dsh/...`; 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.
|
|
145
|
+
- **No `path`/`require` in a `!!js` expression**: `ctx` is the loader scope, so the DSH_HOME fallback is written as plain concatenation.
|
|
146
|
+
|
|
147
|
+
`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.
|
|
148
|
+
|
|
149
|
+
`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.
|
|
150
|
+
|
|
151
|
+
`command` is the running Node executable and `args[0]` is computed from `DSH_HOME`, so the same patch works
|
|
152
|
+
on Windows, macOS and Linux without a PATH entry. Everything machine-specific lives in
|
|
153
|
+
`<DSH_HOME>/plugins/rea-bundle/bundle.json`, written by setup and readable with:
|
|
104
154
|
|
|
105
|
-
```yaml
|
|
106
|
-
- id: mcp-rea
|
|
107
|
-
config:
|
|
108
|
-
env:
|
|
109
|
-
GHIDRA_INSTALL_DIR: '<your Ghidra 12.1.x directory>'
|
|
110
155
|
```
|
|
156
|
+
node bin/rea-shim.mjs --shim-status # resolved paths, versions, verification state
|
|
157
|
+
node bin/rea-shim.mjs --shim-print-env # the environment the MCP child will see
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Both commands answer with the same bundle whether you run them from the plugin directory (the checkout copy)
|
|
161
|
+
or from `<DSH_HOME>/plugins/rea-bundle` (the mirrored copy): the checkout resolves the location by the same
|
|
162
|
+
rule setup uses, while the mirror uses its own position — it cannot rely on `DSH_HOME`, which the harness
|
|
163
|
+
strips from an MCP child's environment.
|
|
164
|
+
|
|
165
|
+
Setting `REA_BUNDLE_AUTO_PROVISION=0` disables the first-run background provisioning.
|
|
111
166
|
|
|
112
|
-
|
|
167
|
+
To pin a different launch command on this machine (for example your own globally installed `rea`), override
|
|
168
|
+
the row from the profile's own `cordis.patch.yml` — that file is machine-local and never published. Only one
|
|
169
|
+
row may use `serverName: rea` in a profile.
|
|
113
170
|
|
|
114
171
|
## Verify
|
|
115
172
|
|
|
@@ -117,30 +174,83 @@ Ask for a `mcp__rea__*` call — `binary_session` reports the engine checks
|
|
|
117
174
|
(`configuration` / `installation` / `headless` / `native_decompiler` / `java`), all of which should be
|
|
118
175
|
`ok: true`. Then `open_binary` a PE, `binary_overview`, and `procedure_pseudo_code`.
|
|
119
176
|
|
|
177
|
+
Provisioning itself ends with two probes: `rea doctor --json` (reported, with unmet checks named) and the
|
|
178
|
+
exact shim launch the MCP row will use.
|
|
179
|
+
|
|
180
|
+
## Verified against
|
|
181
|
+
|
|
182
|
+
Author's machine, 2026-10-13 / 14:
|
|
183
|
+
|
|
184
|
+
| Component | Version |
|
|
185
|
+
|---|---|
|
|
186
|
+
| REA (`rea-agents`) | **6.4.0** (170 tools) |
|
|
187
|
+
| DeepSeek Harness | desktop runtime **0.2.0-rc.2**, with its bundled dsh CLI 0.1.7-rc.2 |
|
|
188
|
+
| `@deepseek-ai/dsh-mcp-client` (row provider) | bundled by the harness |
|
|
189
|
+
| Ghidra | 12.1.4 |
|
|
190
|
+
| JDK | Temurin 21 (provisioned) / Microsoft OpenJDK 25.0.4 (adopted) |
|
|
191
|
+
| Node.js | 24.21.0 (REA engines: `^22.19.0 \|\| ^24.11.0 \|\| >=26.0.0`) |
|
|
192
|
+
| Host | Windows 11 x64 (build 26200) |
|
|
193
|
+
|
|
194
|
+
Exercised live: a clean `setup.mjs --reuse` run reached `setup complete`; an MCP `initialize` handshake
|
|
195
|
+
**through the shim with no `rea` on PATH** answered `serverInfo {name: rea, version: 6.4.0}`; and the whole
|
|
196
|
+
chain ran under the real desktop host — its own Electron binary launched
|
|
197
|
+
`<DSH_HOME>/plugins/rea-bundle/bin/rea-shim.mjs mcp`, the shim started the **vendored** REA, and a live tool
|
|
198
|
+
call reported `path_source: environment` for a path only that machine's `bundle.json` knows.
|
|
199
|
+
|
|
200
|
+
Not exercised end to end here, and therefore not claimed: the ~775 MB JDK + Ghidra download branch (planned
|
|
201
|
+
by `--dry-run`, but this machine adopted its existing installs with `--reuse`), and first runs on
|
|
202
|
+
macOS/Linux.
|
|
203
|
+
|
|
120
204
|
## Scope and caveats
|
|
121
205
|
|
|
122
|
-
REA exposes 170
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
206
|
+
- REA exposes 170 tools at 6.4.0, so every session's tool list grows.
|
|
207
|
+
- Provisioning is honest about checksums: where a vendor publishes one it is pinned and verified; where none
|
|
208
|
+
exists (jadx-headless-mcp, the apktool wrapper, Android platform-tools, the rolling "latest" URLs) the
|
|
209
|
+
hash is **measured once and recorded**, and later drift is reported rather than silently accepted.
|
|
210
|
+
`bundle.json` lists which components are in that state under `unverifiedChecksums`.
|
|
211
|
+
- Downloads are slow on some networks (measured 0.35–0.54 MiB/s from the GitHub release CDN here, so Ghidra
|
|
212
|
+
alone takes ~25–45 min). Fetching is resumable: the cache is kept and re-verified, and a killed run
|
|
213
|
+
continues instead of restarting.
|
|
214
|
+
- Some REA tools launch processes, write files and access the network — DSH's permission policy applies.
|
|
215
|
+
Analyse only artifacts you are authorized to inspect.
|
|
216
|
+
- `scripts/setup.mjs` runs `npm install` for the REA CLI with `--ignore-scripts`: the published tarball
|
|
217
|
+
carries no `node_modules`, so a dependency closure must be resolved, and package lifecycle scripts are
|
|
218
|
+
skipped on purpose during provisioning.
|
|
126
219
|
|
|
127
|
-
## Updating —
|
|
220
|
+
## Updating — three independent things
|
|
128
221
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- **DeepSeek Harness**: a harness upgrade may change the MCP client's config schema — inspect the row with
|
|
137
|
-
`Config.listConfigs name=@deepseek-ai/dsh-mcp-client` and adjust the field names.
|
|
222
|
+
### Upgrading from 1.x
|
|
223
|
+
|
|
224
|
+
Install 2.x, then run provisioning **once** before restarting dsh:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
node scripts/setup.mjs # or: powershell -File scripts\rea-bundle-bootstrap.ps1 -Reuse
|
|
228
|
+
```
|
|
138
229
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
230
|
+
Why it matters: the 1.x row launched `rea mcp` from PATH, while the 2.x row launches
|
|
231
|
+
`<DSH_HOME>/plugins/rea-bundle/bin/rea-shim.mjs` — a file only `setup.mjs` creates. Upgrade and restart
|
|
232
|
+
without provisioning and that row exits with `MODULE_NOT_FOUND`: the harness stays up
|
|
233
|
+
(`failOnStartupError: false` is deliberate), but the `mcp__rea__*` tools are gone and you get **no
|
|
234
|
+
remediation message**, because the shim that prints one does not exist yet. After the setup run they are
|
|
235
|
+
back on the next start.
|
|
236
|
+
|
|
237
|
+
- **This plugin** never updates itself. Installed from a market? Update it there. Installed from a `file:`
|
|
238
|
+
checkout? Overwrite the directory and reinstall.
|
|
239
|
+
- **Upgrading from `2.0.0-bundle.1`**: that release mirrored the shim and `setup.mjs` but not the manifest,
|
|
240
|
+
and recorded nothing about the plugin checkout, so a bundle it provisioned cannot repair itself — its
|
|
241
|
+
background provisioning dies on the missing manifest, invisibly, and its own lock suppresses retries for
|
|
242
|
+
six hours. Re-run `node scripts/setup.mjs` **from the plugin directory** once; it re-mirrors all three
|
|
243
|
+
files and writes the record that lets later runs find the checkout. `check-updates.ps1` reports this state.
|
|
244
|
+
- **Provisioned components**: re-run `node scripts/setup.mjs`; it re-verifies what is on disk and only
|
|
245
|
+
fetches what is missing or wrong. Pins live in `manifest/bundle.manifest.json`.
|
|
246
|
+
- **Upstream REA / Ghidra**: update the pins in the manifest, then re-run setup. REA version-gates Ghidra
|
|
247
|
+
(12.1.x today), so update REA first.
|
|
248
|
+
|
|
249
|
+
`check-updates.ps1` (Windows PowerShell) reports the state of all of these at once.
|
|
142
250
|
|
|
143
251
|
## License
|
|
144
252
|
|
|
145
|
-
MIT (this adapter). REA is MIT and owned by its authors; Ghidra is Apache-2.0 and owned by the NSA
|
|
146
|
-
|
|
253
|
+
MIT (this adapter). REA is MIT and owned by its authors; Ghidra is Apache-2.0 and owned by the NSA; Temurin
|
|
254
|
+
is GPLv2+CE; Node.js is MIT; apktool, jadx-headless-mcp and Android platform-tools keep their own licences.
|
|
255
|
+
**No upstream code or binary is redistributed in this package** — provisioning fetches each component from
|
|
256
|
+
its official source.
|