dsh-moli 0.0.0-stage → 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 27painter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,236 @@
1
- # Temporary Holding Version
1
+ # dsh-moli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Expose a locally installed [Moli](https://github.com/lexmount/moli) (`moli.exe`, a Rust headless
4
+ browser for AI agents) as **DeepSeek Harness tools**, so an agent can call it instead of shelling out.
5
+
6
+ English | [中文](README.zh.md)
7
+
8
+ ## What it provides
9
+
10
+ | Tool | Purpose |
11
+ |---|---|
12
+ | `moli_fetch` | Fetch a URL and return rendered HTML, Markdown, the semantic tree, or a JSON envelope (`status`, `final_url`); evaluate a JavaScript expression; write a PNG screenshot or a PDF |
13
+ | `moli_probe` | Report the engine path and version and whether HTTPS works; with `action: "install"`, download and install Moli first |
14
+
15
+ `moli_fetch` parameters: `url` (required), `dump`, `writeTo`, `eval`, `wait`, `waitSelector`,
16
+ `delayMs`, `timeoutMs`, `maxChars`, `headers`, `userAgent`, `layout`, `scrollbars`, `noJs`,
17
+ `noCss`, `resources`, `withFrames`, `insecureTls`, `profileDir`, `cookieFile`.
18
+
19
+ ```
20
+ moli_fetch url=https://example.com dump=markdown # token-efficient text
21
+ moli_fetch url=https://example.com dump=semantic_tree_text # nodes with backend ids
22
+ moli_fetch url=https://example.com eval="document.title" # one computed value
23
+ moli_fetch url=https://example.com dump=screenshot writeTo=/abs/path/a.png
24
+ moli_fetch url=https://example.com dump=pdf writeTo=/abs/path/a.pdf
25
+ ```
26
+
27
+ ## Relationship to Moli's own agent skills
28
+
29
+ Moli ships official agent skills in its repository — [`moli-webfetch`](https://github.com/lexmount/moli/tree/main/skills/moli-webfetch),
30
+ [`moli-websearch`](https://github.com/lexmount/moli/tree/main/skills/moli-websearch), and
31
+ [`moli-cdp-server`](https://github.com/lexmount/moli/tree/main/skills/moli-cdp-server) — and their
32
+ README's quick start is to have an agent install them. If your agent loads skills, the simplest path
33
+ is to point it at those; they are maintained upstream and cover web search and a CDP server too.
34
+
35
+ This plugin exists because it is a different trade-off, not a replacement:
36
+
37
+ - **Always-on tool schemas instead of an on-demand skill.** The tools are registered at Host startup,
38
+ so the model sees them every turn with no discovery step.
39
+ - **DSH-specific failure handling.** The three traps below (sandbox pipe capture, UTF-16 mangling of
40
+ binary output, bare-specifier resolution for a `link:`ed plugin) plus sandbox/revocation/proxy
41
+ classification are solved here, because they are DSH-on-Windows problems.
42
+ - **Smaller surface.** Only `fetch` is wrapped; the official skills also cover web search and the CDP
43
+ server, which this plugin does not.
44
+
45
+ Both can coexist: install the skills if you want upstream coverage, keep this plugin if you want the
46
+ capabilities always visible to the model.
47
+
48
+ ## Installation
49
+
50
+ The plugin is installed into a DSH profile. Pick the route that matches your machine.
51
+
52
+ ### Route A — Moli is already installed
53
+
54
+ Nothing else to fetch. The plugin finds `moli.exe` on its own and both tools work immediately.
55
+
56
+ ```powershell
57
+ dsh plugin --profile web add github:27painter/dsh-moli
58
+ ```
59
+
60
+ Restart DSH, then confirm with `moli_probe`.
61
+
62
+ ### Route B — Moli is not installed yet
63
+
64
+ Install the plugin the same way, restart, then let it fetch Moli for you:
65
+
66
+ ```powershell
67
+ dsh plugin --profile web add github:27painter/dsh-moli
68
+ # restart DSH, then call:
69
+ moli_probe action=install
70
+ ```
71
+
72
+ The install action tries several download routes in turn and reports every outcome, because a
73
+ filtered network can block the release host completely. It reports the SHA256 of the exact bytes it
74
+ ran, and `expectedSha256` turns that report into an enforced gate — see
75
+ [Verifying the download](#verifying-the-download). Installation only ever runs when a call asks for
76
+ it; it is never a side effect of using `moli_fetch`.
77
+
78
+ ### Installing into the desktop profile
79
+
80
+ `dsh plugin --profile desktop …` is refused — the desktop profile belongs to the Electron app. Either
81
+ edit its `package.json` directly (add a `link:` dependency **and** the bundle entry, then run
82
+ `pnpm install`), or use the in-app plugin page (sidebar → **Plugins** → **Add plugin**), which goes
83
+ through the same pnpm path.
84
+
85
+ A profile plugin needs both halves: a dependency edge and a `dsh.profile.bundles` entry. One without
86
+ the other leaves the plugin invisible to the loader, and an unresolvable bundle is **skipped
87
+ silently** apart from one stderr line (`skipping profile bundle …`). If the tools never appear, that
88
+ line is the first thing to look for.
89
+
90
+ ## Manual install of Moli
91
+
92
+ When the automatic routes fail, install Moli yourself. These are Moli's own documented commands.
93
+
94
+ Windows (PowerShell):
95
+
96
+ ```powershell
97
+ powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
98
+ ```
99
+
100
+ Linux / macOS:
101
+
102
+ ```sh
103
+ curl --proto '=https' --tlsv1.2 -fsSL https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh
104
+ ```
105
+
106
+ A direct Windows binary is published too, if you would rather not run an installer:
107
+
108
+ ```powershell
109
+ # pick the asset matching your architecture from the releases page
110
+ curl.exe -L -o moli.zip https://github.com/lexmount/moli/releases/latest/download/moli-x86_64-pc-windows-msvc.zip
111
+ Expand-Archive moli.zip -DestinationPath "$env:LOCALAPPDATA\Moli" -Force
112
+ ```
113
+
114
+ After any of these, point the plugin at the result if it landed somewhere unusual:
115
+
116
+ ```yaml
117
+ config:
118
+ exePath: 'C:\path\to\moli.exe'
119
+ ```
120
+
121
+ ### Verifying the download
122
+
123
+ Moli's README documents no checksum or signature, and the installer publishes no checksum file. What
124
+ does exist is GitHub's own per-asset `sha256` digest, readable from the release API:
125
+
126
+ ```powershell
127
+ (irm https://api.github.com/repos/lexmount/moli/releases/latest).assets | Select-Object name, digest
128
+ ```
129
+
130
+ For `v1.1.17` that reports, among others:
131
+
132
+ ```
133
+ moli-installer.ps1 2,020 sha256:e002f7a6c174d78ae6ed7289aca9f5d3a62ab0b1f6e588606a80c0cb5d37964d
134
+ moli-x86_64-pc-windows-msvc.zip 43.8 MB sha256:5247309837b6eaf6e73bfa18f2a8d8d90473b9e2fe5cfcaaef57d7eee9b074c4
135
+ ```
136
+
137
+ Compare it against the file you downloaded:
138
+
139
+ ```powershell
140
+ (Get-FileHash .\moli-installer.ps1 -Algorithm SHA256).Hash.ToLower()
141
+ ```
142
+
143
+ The plugin follows the same discipline: `moli_probe action=install` reports the SHA256 of the bytes it
144
+ ran, and `expectedSha256` enforces a digest you supply. It does not invent a trusted default, because
145
+ the digest is only meaningful when compared against the release page's own value.
146
+
147
+ This is a checked property, not a claim: downloading `v1.1.17`'s installer through
148
+ `downloadInstaller` reproduced `e002f7a6…964d` exactly — the same digest GitHub publishes — and
149
+ re-hashing the file from disk matched too.
150
+
151
+ ## Three design points
152
+
153
+ 1. **Output goes through file descriptors, never pipes.** Under DSH's Windows sandbox a child
154
+ process cannot open an inherited named pipe, so `spawnSync(..., { stdio: 'pipe' })` returns
155
+ `status=null` and zero bytes. Every run redirects stdout/stderr to real files and reads them back.
156
+ 2. **Binary output is copied byte for byte.** PowerShell's `>` operator re-encodes a native
157
+ program's output as UTF-16, inserting a `00` after every byte, which corrupts a screenshot.
158
+ Bytes here are written verbatim, so the PNG signature stays `89504e470d0a1a0a`.
159
+ 3. **No external dependencies.** A profile plugin is loaded from its real path on disk (a `link:`
160
+ dependency stays a symlink target), so a bare specifier such as `@deepseek-ai/schemastery`
161
+ resolves against the plugin directory instead of the profile's `node_modules` and the plugin
162
+ fails to load. This plugin imports only `node:` builtins and its own files.
163
+
164
+ ## Configuration
165
+
166
+ Override any field under the plugin's `config` entry in the profile patch layer. All are optional.
167
+
168
+ ```yaml
169
+ - insert:
170
+ - id: dsh-moli
171
+ name: dsh-moli
172
+ config:
173
+ exePath: 'C:\Users\me\AppData\Local\Moli\bin\moli.exe' # default: auto-detect
174
+ defaultTimeoutMs: 30000 # per-fetch readiness deadline
175
+ maxChars: 200000 # inline text truncation
176
+ proxy: 'http://127.0.0.1:7890' # forward proxy for moli and downloads
177
+ insecureTls: true # see TLS notes below
178
+ ```
179
+
180
+ Without `exePath`, the plugin probes `exePath` → `MOLI_BIN` → `%LOCALAPPDATA%\Moli\bin\moli.exe` →
181
+ `%USERPROFILE%\.local\bin\moli.exe`, then scans inside `%LOCALAPPDATA%\Moli` for a `moli.exe`, so an
182
+ installation under a versioned subdirectory is still found.
183
+
184
+ ## Known limitations
185
+
186
+ - **Windows-only targeting.** Developed and tested on Windows; the executable discovery, the
187
+ file-descriptor workaround, and the Schannel notes are Windows-specific. Nothing prevents a
188
+ POSIX port, but it is untested.
189
+ - **Moli itself is not bundled.** Install it separately or let `moli_probe action=install` fetch it.
190
+ - **Only `fetch` is wrapped.** `moli serve` (CDP/WebDriver server) and `moli import` (session
191
+ import) are not exposed as tools yet.
192
+ - **No per-call cancellation.** The engine runs synchronously, so an interrupted turn cannot abort
193
+ a fetch mid-flight. A call already in progress finishes and its artifact is written.
194
+ - **HTTP error status is only visible for `dump: json`.** That is the only dump format carrying the
195
+ status field. With `markdown` or `html` an error page is returned as content.
196
+ - **`writeTo` calls are serialized.** They report as unsafe to parallelize, so two artifact writes
197
+ never interleave.
198
+ - **`insecureTls` defaults to `true`.** Behind a filtering proxy the certificate-revocation
199
+ responder is often unreachable, and Schannel then fails with
200
+ `CRYPT_E_REVOCATION_OFFLINE (0x80092013)` on hosts like GitHub. The default passes moli's
201
+ `--insecure-disable-tls-host-verification`. Set `insecureTls: false` on networks where revocation
202
+ checks work, or per call with `insecureTls: false`.
203
+ - **Sandbox interaction.** Under DSH's default file sandbox, a subprocess runs at low integrity and
204
+ Schannel cannot acquire client credentials, so HTTPS fails with `SEC_E_NO_CREDENTIALS`. Plain
205
+ HTTP still works and the failure is reported structurally (`sandboxTlsBlock: true`) with the
206
+ retry path; passing `sandbox_permissions: "danger-full-access"` for that call resolves it.
207
+ - **Host reachability varies on some networks.** Observed on the development machine: `curl`-based
208
+ clients (Moli) timed out on `github.com` while `git` over HTTPS and the system browser reached it
209
+ in seconds, and `api.github.com` worked where `github.com` did not — at the same moment. Treat a
210
+ timeout as possibly transient: retry, or set `proxy`. Because a bare timeout is ambiguous, it is
211
+ reported as `possiblyUnreachable` rather than a verdict. The install action's multi-route fallback
212
+ and the manual commands above exist for exactly this case.
213
+
214
+ ## Verification
215
+
216
+ `lib/moli.js` and the tool layer were exercised end to end against Moli 1.1.16 on Windows: tool
217
+ registration, probe, Markdown and JSON dumps, `eval` (including a tight-timeout regression guard
218
+ for the `-t` argument position), PNG and PDF artifacts written with valid signatures, truncation,
219
+ every rejection path (binary dump without `writeTo`, unknown `dump`, unknown `wait` stage, missing
220
+ destination directory), failure classification (unreachable host, HTTP status, sandbox TLS block,
221
+ revocation block), and the concurrency claims. The blocked-host classifier is covered by pure
222
+ function tests using stderr text captured from real runs.
223
+
224
+ Confirmed at runtime as well: a JavaScript-rendered page (GitHub search) returned 14,560 characters
225
+ of Markdown containing 30 real repository matches when fetched through a local proxy.
226
+
227
+ The install pipeline is covered against a loopback HTTP server, which proves the mechanics without
228
+ depending on GitHub: route ordering, the SHA256 of served bytes, refusal to execute on a checksum
229
+ mismatch, actual execution when the digest matches, and the all-routes-failed diagnostic.
230
+
231
+ ## License
232
+
233
+ MIT. See [LICENSE](LICENSE).
234
+
235
+ Moli itself is MIT-licensed (Copyright (c) 2026 Moli contributors); this plugin is an independent
236
+ integration and is not affiliated with or endorsed by the Moli project.
package/README.zh.md ADDED
@@ -0,0 +1,230 @@
1
+ # dsh-moli
2
+
3
+ 把本机安装的 [Moli](https://github.com/lexmount/moli)(`moli.exe`,Rust 编写的 AI agent 无头浏览器)暴露成 **DeepSeek Harness 工具**,让 agent 直接调用,不必每次手敲命令。
4
+
5
+ [English](README.md) | 中文
6
+
7
+ ## 与 Moli 官方 agent skill 的关系
8
+
9
+ Moli 仓库自带官方 agent skill:[`moli-webfetch`](https://github.com/lexmount/moli/tree/main/skills/moli-webfetch)、
10
+ [`moli-websearch`](https://github.com/lexmount/moli/tree/main/skills/moli-websearch)、
11
+ [`moli-cdp-server`](https://github.com/lexmount/moli/tree/main/skills/moli-cdp-server),其 README 的快速开始
12
+ 就是让 agent 去安装它们。如果你的 agent 能加载 skill,最省事的路就是直接用官方那套——上游维护,而且额外覆盖网页
13
+ 搜索与 CDP 服务。
14
+
15
+ 本插件的存在是因为它是另一种取舍,不是替代品:
16
+
17
+ - **常驻工具 schema,而非按需 skill。** 工具在 Host 启动时注册,模型每回合都能看到,不需要发现步骤。
18
+ - **针对 DSH 的失败处理。** 下面那三个坑(沙箱管道捕获、二进制被 UTF-16 破坏、`link:` 插件的裸包名解析)
19
+ 以及沙箱/吊销/代理三类失败分类,都在这里解决了——因为它们本质是"DSH on Windows"的问题。
20
+ - **面更窄。** 只封装了 `fetch`;官方 skill 还覆盖网页搜索和 CDP 服务,本插件没有。
21
+
22
+ 两者可以共存:想要上游覆盖就装 skill,想要能力始终对模型可见就留这个插件。
23
+
24
+ ## 提供的能力
25
+
26
+ | 工具 | 作用 |
27
+ |---|---|
28
+ | `moli_fetch` | 抓取 URL 并返回渲染后的 HTML / Markdown / 语义树 / JSON(含 `status` 与 `final_url`);执行 JS 表达式取值;写出 PNG 截图或 PDF |
29
+ | `moli_probe` | 报告引擎路径与版本,以及调用进程能否走 HTTPS |
30
+
31
+ `moli_fetch` 参数:`url`(必填)、`dump`、`writeTo`、`eval`、`wait`、`waitSelector`、`delayMs`、
32
+ `timeoutMs`、`maxChars`、`headers`、`userAgent`、`layout`、`scrollbars`、`noJs`、`noCss`、
33
+ `resources`、`withFrames`、`insecureTls`、`profileDir`、`cookieFile`。
34
+
35
+ ```
36
+ moli_fetch url=https://example.com dump=markdown # 省 token 的正文
37
+ moli_fetch url=https://example.com dump=semantic_tree_text # 带节点 id 的结构
38
+ moli_fetch url=https://example.com eval="document.title" # 取一个值
39
+ moli_fetch url=https://example.com dump=screenshot writeTo=/绝对路径/a.png
40
+ moli_fetch url=https://example.com dump=pdf writeTo=/绝对路径/a.pdf
41
+ ```
42
+
43
+ ## 安装
44
+
45
+ 插件装进某个 DSH profile。按你的机器情况选一条路。
46
+
47
+ ### 路线 A —— 已经装了 Moli
48
+
49
+ 不需要再装别的东西。插件自己会找到 `moli.exe`,两个工具立即可用。
50
+
51
+ ```powershell
52
+ dsh plugin --profile web add github:27painter/dsh-moli
53
+ ```
54
+
55
+ 重启 DSH,然后用 `moli_probe` 确认。
56
+
57
+ ### 路线 B —— 还没装 Moli
58
+
59
+ 同样方式装插件、重启,然后让它替你把 Moli 拉下来:
60
+
61
+ ```powershell
62
+ dsh plugin --profile web add github:27painter/dsh-moli
63
+ # 重启 DSH,然后调用:
64
+ moli_probe action=install
65
+ ```
66
+
67
+ 安装动作会**依次尝试多条下载路径**并逐条报告结果——因为受限网络可能把 release 主机整个挡掉。它会报告
68
+ 实际执行的字节的 SHA256,而 `expectedSha256` 能把这份报告变成强制校验门(见
69
+ [校验下载](#校验下载))。安装**只在调用明确要求时**发生,使用 `moli_fetch` 永远不会触发它。
70
+
71
+ ### 装进 desktop profile
72
+
73
+ `dsh plugin --profile desktop …` 会被拒绝——desktop profile 归 Electron 应用所有。直接改它的
74
+ `package.json`(加 `link:` 依赖**和** bundle 条目,再跑 `pnpm install`),或用应用内插件页
75
+ (侧边栏 →**插件**→**添加插件**),那条路径同样走 pnpm。
76
+
77
+ profile 插件需要两半都齐:依赖边 + `dsh.profile.bundles` 条目。缺一个 loader 就看不到它,而无法解析的
78
+ bundle 会被**静默跳过**,只在 stderr 留一行 `skipping profile bundle …`。工具迟迟不出现时先找这行。
79
+
80
+ ## 手动安装 Moli
81
+
82
+ 自动路径都失败时(受限网络可能把 release 主机整个挡掉),自己装。下面是 Moli 官方文档里的命令。
83
+
84
+ Windows(PowerShell):
85
+
86
+ ```powershell
87
+ powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
88
+ ```
89
+
90
+ Linux / macOS:
91
+
92
+ ```sh
93
+ curl --proto '=https' --tlsv1.2 -fsSL https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh
94
+ ```
95
+
96
+ 官方也发布 Windows 直接二进制,不想跑安装器就用它:
97
+
98
+ ```powershell
99
+ # 从 releases 页挑与你的架构匹配的资产
100
+ curl.exe -L -o moli.zip https://github.com/lexmount/moli/releases/latest/download/moli-x86_64-pc-windows-msvc.zip
101
+ Expand-Archive moli.zip -DestinationPath "$env:LOCALAPPDATA\Moli" -Force
102
+ ```
103
+
104
+ 装完若位置不常规,把插件指过去:
105
+
106
+ ```yaml
107
+ config:
108
+ exePath: 'C:\path\to\moli.exe'
109
+ ```
110
+
111
+ ### 校验下载
112
+
113
+ Moli 的 README 没有记录任何校验和或签名,安装器也不发布校验和文件。**存在的是 GitHub 自己给每个资产的
114
+ `sha256` 摘要**,可以从 release API 读到:
115
+
116
+ ```powershell
117
+ (irm https://api.github.com/repos/lexmount/moli/releases/latest).assets | Select-Object name, digest
118
+ ```
119
+
120
+ `v1.1.17` 下这个命令会给出(节选):
121
+
122
+ ```
123
+ moli-installer.ps1 2,020 sha256:e002f7a6c174d78ae6ed7289aca9f5d3a62ab0b1f6e588606a80c0cb5d37964d
124
+ moli-x86_64-pc-windows-msvc.zip 43.8 MB sha256:5247309837b6eaf6e73bfa18f2a8d8d90473b9e2fe5cfcaaef57d7eee9b074c4
125
+ ```
126
+
127
+ 与你下载的文件比对:
128
+
129
+ ```powershell
130
+ (Get-FileHash .\moli-installer.ps1 -Algorithm SHA256).Hash.ToLower()
131
+ ```
132
+
133
+ 插件遵循同样的纪律:`moli_probe action=install` 会报告它实际执行的那份字节的 SHA256,而
134
+ `expectedSha256` 能强制校验你提供的摘要。它**不会**编造一个可信默认值——因为上游根本没有校验和可比。
135
+
136
+ ## 三个设计要点
137
+
138
+ 1. **输出走文件描述符,不走管道。** DSH 的 Windows 沙箱禁止子进程打开继承的命名管道,
139
+ `spawnSync(..., { stdio: 'pipe' })` 会得到 `status=null` 与 0 字节。本项目始终把 stdout/stderr
140
+ 重定向到临时文件再读回。
141
+ 2. **二进制按原始字节落盘。** PowerShell 的 `>` 会把原生程序输出按 UTF-16 重编码(每字节后插
142
+ `00`),截图因此损坏。这里直接写字节,PNG 签名保持 `89504e470d0a1a0a`。
143
+ 3. **零外部依赖。** profile 插件按磁盘真实路径加载(`link:` 依赖仍是符号链接目标),任何裸包名
144
+ 导入(例如 `@deepseek-ai/schemastery`)都会解析到插件目录而非 profile 的 `node_modules`,导致
145
+ 插件加载失败。本插件只导入 `node:` 内置模块和自己的文件。
146
+
147
+ ## 配置
148
+
149
+ 在 profile 的补丁层里覆盖 `config`,全部可选:
150
+
151
+ ```yaml
152
+ - insert:
153
+ - id: dsh-moli
154
+ name: dsh-moli
155
+ config:
156
+ exePath: 'C:\Users\me\AppData\Local\Moli\bin\moli.exe' # 默认自动探测
157
+ defaultTimeoutMs: 30000 # 单次抓取就绪超时
158
+ maxChars: 200000 # 内联文本截断阈值
159
+ proxy: 'http://127.0.0.1:7890' # 转给本地正向代理
160
+ insecureTls: true # 见下方 TLS 说明
161
+ ```
162
+
163
+ 未设置 `exePath` 时探测顺序:`exePath` → 环境变量 `MOLI_BIN` → `%LOCALAPPDATA%\Moli\bin\moli.exe`
164
+ → `%USERPROFILE%\.local\bin\moli.exe`。
165
+
166
+ ## 安装
167
+
168
+ 先装 Moli 本体——本插件只负责驱动它,不分发它。Windows 上:
169
+
170
+ ```powershell
171
+ powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
172
+ ```
173
+
174
+ (Linux/macOS 用对应的 `moli-installer.sh`,见 [Moli README](https://github.com/lexmount/moli#quick-start)。)
175
+
176
+ 然后插件自身必须**同时**是 profile 依赖与已声明的 bundle,缺一个 loader 就看不到它:
177
+
178
+ 1. 把本目录放到任意位置,例如 `D:\dsh_plugins\dsh-moli`。
179
+ 2. 在该 profile 的 `package.json` 中加入:
180
+ - `dependencies`: `"dsh-moli": "link:D:/dsh_plugins/dsh-moli"`
181
+ - `dsh.profile.bundles`: `"dsh-moli"`
182
+ 3. 在 profile 目录执行 `pnpm install`(生成 `node_modules/dsh-moli`)。
183
+ 4. 重启 DSH。
184
+
185
+ 注意:
186
+
187
+ - `dsh plugin --profile desktop …` 会被拒绝:desktop profile 归 Electron 应用所有。直接改它的
188
+ `package.json`,或用应用内插件页(侧边栏 →**插件**→**添加插件**),那条路径同样走 pnpm。
189
+ - bundle 无法解析或未通过兼容性检查时,loader 会**静默跳过**,只在 stderr 留一行
190
+ `skipping profile bundle …`。工具迟迟不出现时先找这行。
191
+
192
+ ## 已知限制
193
+
194
+ - **面向 Windows。** 开发与验证都在 Windows 上;可执行文件探测、文件描述符绕坑、Schannel 说明都
195
+ 与 Windows 相关。移植到 POSIX 没有障碍,但未经验证。
196
+ - **不包含 Moli 本体。** 需自行安装,插件只负责定位和驱动它。
197
+ - **只封装了 `fetch`。** `moli serve`(CDP/WebDriver 服务器)与 `moli import`(会话导入)尚未做成工具。
198
+ - **不支持单次取消。** 引擎是同步执行的,被中断的回合无法中途终止抓取;已在进行的调用会跑完并
199
+ 写出产物。
200
+ - **HTTP 错误码只在 `dump: json` 下可见。** 那是唯一携带状态字段的格式;用 `markdown` 或 `html`
201
+ 时错误页会作为内容返回。
202
+ - **`writeTo` 调用被串行化。** 它们声明为不可并行,因此两次产物写入不会交错。
203
+ - **`insecureTls` 默认为 `true`。** 在过滤代理后面,证书吊销响应者常常不可达,Schannel 于是在
204
+ GitHub 这类站点上报 `CRYPT_E_REVOCATION_OFFLINE (0x80092013)`。默认传 moli 的
205
+ `--insecure-disable-tls-host-verification`;在吊销检查可用的网络里改成 `insecureTls: false`,
206
+ 或按次传 `insecureTls: false`。
207
+ - **与沙箱的交互。** DSH 默认文件沙箱下子进程是低完整性,Schannel 拿不到客户端凭证,HTTPS 会报
208
+ `SEC_E_NO_CREDENTIALS`。纯 HTTP 仍可用,且失败会被结构化上报(`sandboxTlsBlock: true`)并给出
209
+ 重试方式;该次调用带 `sandbox_permissions: "danger-full-access"` 即可通过。
210
+ - **部分网络下主机可达性会波动。** 开发机上观察到的现象:同一时刻,基于 curl 的客户端(moli)访问
211
+ `github.com` 超时,而 `git` over HTTPS 与系统浏览器几秒内就能通;`api.github.com` 正常而
212
+ `github.com` 不通。所以超时可能是暂时性的——重试,或设置 `proxy`。正因裸超时含义不唯一,它被
213
+ 上报为 `possiblyUnreachable` 而不当作定论。
214
+
215
+ ## 验证情况
216
+
217
+ 在 Windows 上对 Moli 1.1.16 做了端到端验证:工具注册、probe、Markdown 与 JSON dump、`eval`
218
+ (含针对 `-t` 参数位置的紧超时回归防护)、PNG 与 PDF 产物签名、截断、全部拒绝路径(二元 dump 缺
219
+ `writeTo`、非法 `dump`、非法 `wait`、目标目录不存在)、失败分类(不可达主机、HTTP 状态、沙箱 TLS
220
+ 阻断、吊销阻断)以及并发声明。被阻断主机的分类器用真实运行捕获的 stderr 文本做纯函数测试。
221
+
222
+ 运行时也确认过:走本地代理抓取 JavaScript 渲染页面(GitHub 搜索)返回 14,560 字符 Markdown,
223
+ 其中包含 30 处真实结果匹配。
224
+
225
+ ## 许可
226
+
227
+ MIT,见 [LICENSE](LICENSE)。
228
+
229
+ Moli 本体也是 MIT(Copyright (c) 2026 Moli contributors);本插件是独立的集成项目,与 Moli 项目无从属
230
+ 或背书关系。
@@ -0,0 +1,10 @@
1
+ - insert:
2
+ - id: dsh-moli
3
+ name: dsh-moli
4
+ config:
5
+ defaultTimeoutMs: 30000
6
+ maxChars: 200000
7
+ # This network's certificate-revocation responder is unreachable, so some
8
+ # HTTPS hosts (GitHub in particular) fail with CRYPT_E_REVOCATION_OFFLINE.
9
+ # true passes moli's no-verify flag; set false where revocation works.
10
+ insecureTls: true