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 +21 -0
- package/README.md +235 -2
- package/README.zh.md +230 -0
- package/cordis.patch.yml +10 -0
- package/index.js +319 -0
- package/lib/install.js +318 -0
- package/lib/moli.js +460 -0
- package/package.json +51 -4
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
|
-
#
|
|
1
|
+
# dsh-moli
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
或背书关系。
|
package/cordis.patch.yml
ADDED
|
@@ -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
|