dsh-moli 1.1.12 → 1.1.13
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/README.md +46 -125
- package/README.zh.md +41 -111
- package/index.js +5 -299
- package/lib/moli.js +62 -1
- package/package.json +1 -1
- package/lib/actions.js +0 -460
- package/lib/session.js +0 -632
package/README.md
CHANGED
|
@@ -209,9 +209,9 @@ An unresolvable bundle is **skipped silently** apart from one stderr line
|
|
|
209
209
|
|
|
210
210
|
## Usage
|
|
211
211
|
|
|
212
|
-
`moli_fetch` parameters: `url` (required), `dump`, `writeTo`, `eval`, `
|
|
213
|
-
`delayMs`, `timeoutMs`, `maxChars`, `headers`, `userAgent`, `layout`, `scrollbars`,
|
|
214
|
-
`noCss`, `resources`, `withFrames`, `insecureTls`, `profileDir`, `cookieFile`.
|
|
212
|
+
`moli_fetch` parameters: `url` (required), `dump`, `writeTo`, `eval`, `scroll`, `wait`,
|
|
213
|
+
`waitSelector`, `delayMs`, `timeoutMs`, `maxChars`, `headers`, `userAgent`, `layout`, `scrollbars`,
|
|
214
|
+
`noJs`, `noCss`, `resources`, `withFrames`, `insecureTls`, `profileDir`, `cookieFile`, `proxy`.
|
|
215
215
|
|
|
216
216
|
```
|
|
217
217
|
moli_fetch url=https://example.com dump=markdown # token-efficient text
|
|
@@ -219,105 +219,30 @@ moli_fetch url=https://example.com dump=semantic_tree_text # nodes with backe
|
|
|
219
219
|
moli_fetch url=https://example.com eval="document.title" # one computed value
|
|
220
220
|
moli_fetch url=https://example.com dump=screenshot writeTo=/abs/path/a.png
|
|
221
221
|
moli_fetch url=https://example.com dump=pdf writeTo=/abs/path/a.pdf
|
|
222
|
+
moli_fetch url=https://example.com dump=markdown scroll=800 # trigger lazy loading first
|
|
222
223
|
```
|
|
223
224
|
|
|
224
225
|
Screenshot and PDF dumps need `writeTo`; the bytes are written verbatim rather than printed.
|
|
225
226
|
|
|
226
|
-
### `
|
|
227
|
+
### `scroll` — reach what only appears on a scroll
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
`
|
|
231
|
-
`
|
|
232
|
-
stop it.
|
|
229
|
+
The engine serializes the whole document, so a plain fetch already contains everything the DOM holds.
|
|
230
|
+
What it cannot contain is content that does not exist yet: a list that loads on the first `scroll`
|
|
231
|
+
event, an `IntersectionObserver` that fires on entry, an image swapped in when it nears the viewport.
|
|
232
|
+
`scroll` runs the scroll before the capture, so those handlers fire first.
|
|
233
233
|
|
|
234
234
|
```
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
moli_session action=type selector="#q" text="moli" submit=true
|
|
239
|
-
moli_session action=scroll dy=1200
|
|
240
|
-
moli_session action=waitFor selector=".results"
|
|
241
|
-
moli_session action=eval expression="document.title"
|
|
242
|
-
moli_session action=screenshot writeTo=/abs/path/a.png
|
|
243
|
-
moli_session action=status # is it running, and on what page
|
|
244
|
-
moli_session action=close # stop it and free the port
|
|
235
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=600
|
|
236
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=bottom
|
|
237
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=-400 # back up
|
|
245
238
|
```
|
|
246
239
|
|
|
247
|
-
|
|
248
|
-
|
|
240
|
+
Measured on a page whose `scroll` handler fills a slot: without `scroll` the dump says
|
|
241
|
+
`STILL-EMPTY`, with `scroll=1000` it says `LAZY-CONTENT-1000`. It also moves the screenshot, so a
|
|
242
|
+
scrolled `dump=screenshot` shows that part of the page.
|
|
249
243
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
`moli_session` can drive either engine, and you choose which — there is **no automatic fallback**,
|
|
253
|
-
because switching engines when one click fails would make the timing and the behaviour depend on which
|
|
254
|
-
operation happened to fail, and you could not tell which browser answered.
|
|
255
|
-
|
|
256
|
-
```yaml
|
|
257
|
-
config:
|
|
258
|
-
sessionBrowser: moli # moli (default) or chromium
|
|
259
|
-
sessionBrowserPath: '' # optional path to chrome.exe / msedge.exe / brave
|
|
260
|
-
sessionPort: 9222
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Why the choice exists.** Moli's element geometry is document-absolute and does not follow scrolling,
|
|
264
|
-
and its hit test is bounded by the viewport height. Measured on Moli 1.1.16 and Chrome 155, same page,
|
|
265
|
-
same element:
|
|
266
|
-
|
|
267
|
-
| | `rect.y` at scrollY 0 / 400 / 800 | click a control below the first viewport |
|
|
268
|
-
|---|---|---|
|
|
269
|
-
| **moli** | 1008 / 1008 / 1008 — never moves | **impossible**; synthetic input cannot reach it |
|
|
270
|
-
| **chromium** | 1008 / 608 / 208 — follows the scroll | **works**; the browser scrolls to it |
|
|
271
|
-
|
|
272
|
-
So moli is the good default — it is the engine this plugin wraps, it is lighter, and it is already
|
|
273
|
-
installed — and a real Chromium is what you switch to when a task needs an element further down a long
|
|
274
|
-
page. When moli refuses, it says so, names the element, and points at both routes:
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
moli_session action=click selector="#far-down"
|
|
278
|
-
→ "#far-down" is below the viewport (y=2548) and this engine cannot send input there
|
|
279
|
-
|
|
280
|
-
# either click from inside the page (reaches it, but skips hover and focus behaviour)
|
|
281
|
-
moli_session action=eval expression="document.querySelector('#far-down').click()"
|
|
282
|
-
|
|
283
|
-
# or use a real browser for that session
|
|
284
|
-
config: sessionBrowser: chromium
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
With `chromium` the plugin starts the browser it finds (Chrome, Edge or Brave, or `sessionBrowserPath`)
|
|
288
|
-
in `--headless=new` mode with its own profile, so it does not touch your normal browsing profile.
|
|
289
|
-
|
|
290
|
-
### Driving a browser you already have open
|
|
291
|
-
|
|
292
|
-
Pass `cdpEndpoint` to attach to a CDP port instead of starting anything. This is the only way to inherit
|
|
293
|
-
your own cookies and logins: start your browser with `--remote-debugging-port=9222`, then
|
|
294
|
-
|
|
295
|
-
```
|
|
296
|
-
moli_session action=open url=https://example.com cdpEndpoint=http://127.0.0.1:9222
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
The endpoint applies to that call only. A browser this plugin did not start is **never stopped by
|
|
300
|
-
`close`**, and `status` reports `external: true` so you can tell.
|
|
301
|
-
|
|
302
|
-
### `cdp` — the raw protocol escape hatch
|
|
303
|
-
|
|
304
|
-
Everything the named actions do is an abstraction chosen in advance. `action=cdp` is not:
|
|
305
|
-
|
|
306
|
-
```
|
|
307
|
-
moli_session action=cdp method=Browser.getVersion
|
|
308
|
-
moli_session action=cdp method=Network.setExtraHTTPHeaders params={"headers":{"X-Thing":"1"}}
|
|
309
|
-
moli_session action=cdp method=DOM.setFileInputFiles params={"files":["/abs/a.png"],"nodeId":42}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
It reaches what the wrapper does not: other targets, cross-origin frames, file inputs, extra headers,
|
|
313
|
-
network interception. `params` may be an object or a JSON string.
|
|
314
|
-
|
|
315
|
-
The session needs **Node 21 or newer** for the built-in `WebSocket`; on Node 20 the tool answers with
|
|
316
|
-
that reason instead of failing obscurely. `moli_fetch` and `moli_probe` still work on 20.12.
|
|
317
|
-
|
|
318
|
-
`moli_session` also takes `port` (default 9222). If that port is already served by a `moli serve` you
|
|
319
|
-
started yourself, the tool connects to it rather than failing to bind — and `close` then leaves it
|
|
320
|
-
alone, because it is not ours to stop.
|
|
244
|
+
It runs as part of the engine's own pre-capture evaluation and therefore composes with `eval`: the
|
|
245
|
+
scroll happens first, and the expression still returns its own value.
|
|
321
246
|
|
|
322
247
|
## Relationship to Moli's own agent skills
|
|
323
248
|
|
|
@@ -406,26 +331,12 @@ Override any field under the plugin's `config` entry in the profile patch layer.
|
|
|
406
331
|
maxChars: 200000 # inline text truncation
|
|
407
332
|
proxy: 'http://127.0.0.1:7890' # forward proxy for moli, downloads, and the installer's engine download
|
|
408
333
|
insecureTls: true # see the TLS notes below
|
|
409
|
-
sessionBrowser: moli # or chromium, for the session tool
|
|
410
|
-
sessionBrowserPath: '' # explicit chrome.exe / msedge.exe / brave
|
|
411
|
-
sessionPort: 9222 # CDP port for moli_session
|
|
412
|
-
blockPrivateNetworks: false # refuse private addresses (see the note below)
|
|
413
|
-
blockCidrs: [] # extra CIDRs to refuse
|
|
414
334
|
```
|
|
415
335
|
|
|
416
336
|
The plugin-level `proxy` above is the default; `moli_fetch` also takes a `proxy` argument, so a single
|
|
417
337
|
call can route through a different proxy without changing the profile. A loopback URL always bypasses
|
|
418
338
|
whichever proxy is in effect.
|
|
419
339
|
|
|
420
|
-
`blockPrivateNetworks` is **off by default, matching the engine**: `moli fetch http://10.0.0.1/` reaches
|
|
421
|
-
a private address, so the plugin does not silently narrow what the moli you installed already does.
|
|
422
|
-
Turn it on, or list `blockCidrs`, when the agent is following links on pages you do not control and
|
|
423
|
-
you want the engine to refuse private destinations. Two caveats worth knowing before you rely on it:
|
|
424
|
-
moli documents the policy as applying to **directly connected** targets, so a configured `proxy`
|
|
425
|
-
routes around it, and only `blockCidrs` (which the proxy must enforce) covers that path. The sharpest
|
|
426
|
-
target this protects is the cloud metadata address `169.254.169.254`, which hands out instance
|
|
427
|
-
credentials on a VM.
|
|
428
|
-
|
|
429
340
|
Without `exePath`, the plugin probes `exePath` → `MOLI_BIN` → `%LOCALAPPDATA%\Moli\bin\moli.exe` →
|
|
430
341
|
`%USERPROFILE%\.local\bin\moli.exe`, then scans inside `%LOCALAPPDATA%\Moli` for a `moli.exe`, so an
|
|
431
342
|
installation under a versioned subdirectory is still found.
|
|
@@ -488,25 +399,20 @@ Four suites live in [`verify/`](verify/), and all are runnable against a working
|
|
|
488
399
|
`dump` discards its value, an expression that throws is classified as such instead of being blamed
|
|
489
400
|
on the installation, truncation keeps the tail as well as the head, `proxy` is a per-call parameter,
|
|
490
401
|
and `moli_probe` states which process its `httpsOk` describes.
|
|
491
|
-
- `
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
pointing at port 9 for the rest of the process.
|
|
506
|
-
|
|
507
|
-
`.github/workflows/verify.yml` runs the first two on Linux, Windows and macOS (they need no engine and
|
|
508
|
-
no network) and attempts the engine-dependent suites on Linux and Windows, reporting a missing engine
|
|
509
|
-
as a skip rather than a failure.
|
|
402
|
+
- `scroll-verify.mjs` checks the expression builder without an engine (pixels, negatives, `top`,
|
|
403
|
+
`bottom`, nonsense rejected, the caller's `eval` value preserved) and then, when an engine is
|
|
404
|
+
present, fetches a page whose `scroll` handler fills a slot and asserts the content appears with
|
|
405
|
+
`scroll` and the placeholder appears without it — a property of the output, not of an internal call.
|
|
406
|
+
|
|
407
|
+
`.github/workflows/verify.yml` runs `schema-verify`, `empty-verify` and `scroll-verify` on Linux,
|
|
408
|
+
Windows and macOS (they need no engine and no network) and attempts the engine-dependent suites on
|
|
409
|
+
Linux and Windows, reporting a missing engine as a skip rather than a failure.
|
|
410
|
+
|
|
411
|
+
`report-verify.mjs` is deliberately **not** in CI. Three of its checks fetch `example.com` and one
|
|
412
|
+
fetches a specific forum thread, so it needs the open internet — and on a host whose traffic goes
|
|
413
|
+
through a proxy, that proxy too. Useful where a network is available, but a suite that goes red
|
|
414
|
+
because a website changed is worse than no suite, so it stays a local check. If you run it and see
|
|
415
|
+
failures in sections 4–6 while sections 1–3 pass, suspect the network before the code.
|
|
510
416
|
|
|
511
417
|
Beyond those, `lib/moli.js` and the tool layer were exercised end to end against Moli 1.1.16 on
|
|
512
418
|
Windows: registration, probe, Markdown and JSON dumps, `eval` (including a tight-timeout regression
|
|
@@ -520,6 +426,21 @@ Confirmed at runtime too: a JavaScript-rendered page (GitHub search) returned 14
|
|
|
520
426
|
Markdown containing 30 real repository matches when fetched through a local proxy. A throwaway
|
|
521
427
|
profile with the plugin installed composes its config with no `JsonSchemaError` and no skipped bundle.
|
|
522
428
|
|
|
429
|
+
## Version note: 1.1.11 and 1.1.12 are deprecated
|
|
430
|
+
|
|
431
|
+
**Use 1.1.13 or later.** Two releases between them carried an interactive-browser experiment —
|
|
432
|
+
`moli_session`, a second Chromium backend, a raw-CDP escape hatch, per-call CDP endpoints, persistent
|
|
433
|
+
browser profiles — roughly 2,300 lines of machinery reaching well past what this plugin is for. It has
|
|
434
|
+
been removed and the code is back to the 1.1.10 shape plus one focused addition.
|
|
435
|
+
|
|
436
|
+
The useful finding from that work is kept in [`/lib/moli.js`](lib/moli.js) and appears above as the
|
|
437
|
+
`scroll` parameter. Everything else it needed was already in `moli fetch`'s own `--eval`, so the
|
|
438
|
+
experiment bought nothing that a parameter could not.
|
|
439
|
+
|
|
440
|
+
On the versions themselves: npm allows a version to be unpublished only briefly and never
|
|
441
|
+
individually, so 1.1.11 and 1.1.12 remain installable but are marked deprecated and point here.
|
|
442
|
+
`npm install dsh-moli` resolves to 1.1.13.
|
|
443
|
+
|
|
523
444
|
## Releasing
|
|
524
445
|
|
|
525
446
|
Publishing goes through GitHub Actions' OIDC identity, so **no npm token exists to expire**:
|
package/README.zh.md
CHANGED
|
@@ -195,9 +195,9 @@ profile 插件需要两半都齐:依赖边(`dependencies`)+ `dsh.profile.b
|
|
|
195
195
|
|
|
196
196
|
## 用法
|
|
197
197
|
|
|
198
|
-
`moli_fetch` 参数:`url`(必填)、`dump`、`writeTo`、`eval`、`wait`、`waitSelector
|
|
199
|
-
`timeoutMs`、`maxChars`、`headers`、`userAgent`、`layout`、`scrollbars`、`noJs
|
|
200
|
-
`resources`、`withFrames`、`insecureTls`、`profileDir`、`cookieFile`。
|
|
198
|
+
`moli_fetch` 参数:`url`(必填)、`dump`、`writeTo`、`eval`、`scroll`、`wait`、`waitSelector`、
|
|
199
|
+
`delayMs`、`timeoutMs`、`maxChars`、`headers`、`userAgent`、`layout`、`scrollbars`、`noJs`、
|
|
200
|
+
`noCss`、`resources`、`withFrames`、`insecureTls`、`profileDir`、`cookieFile`、`proxy`。
|
|
201
201
|
|
|
202
202
|
```
|
|
203
203
|
moli_fetch url=https://example.com dump=markdown # 省 token 的正文
|
|
@@ -205,99 +205,29 @@ moli_fetch url=https://example.com dump=semantic_tree_text # 带节点 id 的
|
|
|
205
205
|
moli_fetch url=https://example.com eval="document.title" # 取一个值
|
|
206
206
|
moli_fetch url=https://example.com dump=screenshot writeTo=/绝对路径/a.png
|
|
207
207
|
moli_fetch url=https://example.com dump=pdf writeTo=/绝对路径/a.pdf
|
|
208
|
+
moli_fetch url=https://example.com dump=markdown scroll=800 # 先触发懒加载再抓取
|
|
208
209
|
```
|
|
209
210
|
|
|
210
211
|
截图与 PDF 必须给 `writeTo`;字节是原样落盘的,不会打印到输出里。
|
|
211
212
|
|
|
212
|
-
### `
|
|
213
|
+
### `scroll` —— 拿到"只有滚动后才出现"的内容
|
|
213
214
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
引擎会序列化整个文档,所以普通抓取**已经包含 DOM 里的一切**。它拿不到的是**尚未存在的内容**:首次
|
|
216
|
+
`scroll` 事件才加载的列表、进入视口才触发的 `IntersectionObserver`、靠近视口才换上的图片。`scroll` 会在
|
|
217
|
+
抓取前置入一次滚动,让这些处理器先跑起来。
|
|
217
218
|
|
|
218
219
|
```
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
moli_session action=type selector="#q" text="moli" submit=true
|
|
223
|
-
moli_session action=scroll dy=1200
|
|
224
|
-
moli_session action=waitFor selector=".results"
|
|
225
|
-
moli_session action=eval expression="document.title"
|
|
226
|
-
moli_session action=screenshot writeTo=/绝对路径/a.png
|
|
227
|
-
moli_session action=status # 是否在运行、当前在哪一页
|
|
228
|
-
moli_session action=close # 停止并释放端口
|
|
220
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=600
|
|
221
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=bottom
|
|
222
|
+
moli_fetch url=https://example.com/feed dump=markdown scroll=-400 # 往回滚
|
|
229
223
|
```
|
|
230
224
|
|
|
231
|
-
|
|
232
|
-
|
|
225
|
+
在一个"滚动处理器填充某个槽位"的页面上实测:不带 `scroll` 时 dump 是 `STILL-EMPTY`,带
|
|
226
|
+
`scroll=1000` 时是 `LAZY-CONTENT-1000`。它同样会改变截图,所以带滚动的 `dump=screenshot` 拍到的是页面的
|
|
227
|
+
那一段。
|
|
233
228
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
`moli_session` 可以驱动两种引擎,由你选择——**没有自动降级**。因为"某个点击失败就换引擎"会让工具的耗时和
|
|
237
|
-
行为取决于哪一步恰好失败,而且你无法知道最后是哪个浏览器在回答。
|
|
238
|
-
|
|
239
|
-
```yaml
|
|
240
|
-
config:
|
|
241
|
-
sessionBrowser: moli # moli(默认)或 chromium
|
|
242
|
-
sessionBrowserPath: '' # 可选,指定 chrome.exe / msedge.exe / brave
|
|
243
|
-
sessionPort: 9222
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**为什么需要这个选择。** moli 的元素几何是**文档绝对坐标且不随滚动更新**,命中测试又受视口高度限制。在
|
|
247
|
-
Moli 1.1.16 与 Chrome 155 上,同一页面、同一元素实测:
|
|
248
|
-
|
|
249
|
-
| | `rect.y` 在 scrollY 0 / 400 / 800 | 点击首个视口以下的控件 |
|
|
250
|
-
|---|---|---|
|
|
251
|
-
| **moli** | 1008 / 1008 / 1008 —— 纹丝不动 | **做不到**,合成输入触达不了 |
|
|
252
|
-
| **chromium** | 1008 / 608 / 208 —— 跟随滚动 | **可以**,浏览器会滚动到它 |
|
|
253
|
-
|
|
254
|
-
所以 moli 是合适的默认值——它是本插件包装的引擎、更轻、而且你已经装了;而当任务需要长页面下方的元素时,
|
|
255
|
-
就换成真实 Chromium。moli 拒绝时会把原因、元素和两条出路都讲清楚:
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
moli_session action=click selector="#far-down"
|
|
259
|
-
→ "#far-down" is below the viewport (y=2548) and this engine cannot send input there
|
|
260
|
-
|
|
261
|
-
# 要么在页面内部点击(能触达,但会跳过 hover 与 focus 行为)
|
|
262
|
-
moli_session action=eval expression="document.querySelector('#far-down').click()"
|
|
263
|
-
|
|
264
|
-
# 要么这个会话改用真实浏览器
|
|
265
|
-
config: sessionBrowser: chromium
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
选 `chromium` 时,插件会在 `--headless=new` 模式下用**独立 profile** 启动它找到的浏览器(Chrome、Edge 或
|
|
269
|
-
Brave,也可用 `sessionBrowserPath` 指定),不会碰你日常使用的浏览器配置。
|
|
270
|
-
|
|
271
|
-
### 驱动你自己已经开着的浏览器
|
|
272
|
-
|
|
273
|
-
传 `cdpEndpoint` 即可连接到已有的 CDP 端口,什么都不启动。这是**继承你自己 cookie 与登录态的唯一途径**:
|
|
274
|
-
用 `--remote-debugging-port=9222` 启动你的浏览器,然后
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
moli_session action=open url=https://example.com cdpEndpoint=http://127.0.0.1:9222
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
该端点**只对当次调用生效**。不是本插件启动的浏览器**永远不会被 `close` 关掉**,并且 `status` 会报
|
|
281
|
-
`external: true` 让你能分辨。
|
|
282
|
-
|
|
283
|
-
### `cdp` —— 原始协议逃生舱
|
|
284
|
-
|
|
285
|
-
具名动作全都是"预先替你决定好的抽象",而 `action=cdp` 不是:
|
|
286
|
-
|
|
287
|
-
```
|
|
288
|
-
moli_session action=cdp method=Browser.getVersion
|
|
289
|
-
moli_session action=cdp method=Network.setExtraHTTPHeaders params={"headers":{"X-Thing":"1"}}
|
|
290
|
-
moli_session action=cdp method=DOM.setFileInputFiles params={"files":["/绝对/a.png"],"nodeId":42}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
它能做到包装层做不到的事:切换 target、跨域 iframe、文件上传、额外的请求头、网络拦截。`params` 可以是对象
|
|
294
|
-
或 JSON 字符串。
|
|
295
|
-
|
|
296
|
-
本工具需要 **Node 21 或更新版本**(内置 `WebSocket`);在 Node 20 上会直接说明原因,而不是给出费解的失败。
|
|
297
|
-
`moli_fetch` 与 `moli_probe` 仍支持 20.12。
|
|
298
|
-
|
|
299
|
-
`moli_session` 也接受 `port`(默认 9222)。如果该端口已经由你自己启动的 `moli serve` 占用,工具会连接它
|
|
300
|
-
而不是绑定失败——此时 `close` **不会**停掉它,因为那不是我们的进程。
|
|
229
|
+
它作为引擎自身"抓取前求值"的一部分执行,因此与 `eval` 可组合:**先滚动,表达式再读取页面,并且仍然返回
|
|
230
|
+
它自己的值**。
|
|
301
231
|
|
|
302
232
|
## 与 Moli 官方 agent skill 的关系
|
|
303
233
|
|
|
@@ -374,22 +304,11 @@ cookie 驱动的查询参数。那次实测同时覆盖了代理下载路由与
|
|
|
374
304
|
maxChars: 200000 # 内联文本截断阈值
|
|
375
305
|
proxy: 'http://127.0.0.1:7890' # 转给本地正向代理(moli、下载、安装器内部引擎下载都走它)
|
|
376
306
|
insecureTls: true # 见下方 TLS 说明
|
|
377
|
-
sessionBrowser: moli # 或 chromium,供 session 工具使用
|
|
378
|
-
sessionBrowserPath: '' # 显式指定 chrome.exe / msedge.exe / brave
|
|
379
|
-
sessionPort: 9222 # moli_session 的 CDP 端口
|
|
380
|
-
blockPrivateNetworks: false # 拒绝私网地址(见下方说明)
|
|
381
|
-
blockCidrs: [] # 额外拒绝的 CIDR
|
|
382
307
|
```
|
|
383
308
|
|
|
384
309
|
上面配置里的 `proxy` 是默认值;`moli_fetch` 也能接收 `proxy` 参数,因此单次调用可以走另一个代理而不必改
|
|
385
310
|
profile。无论生效的是哪个代理,loopback 地址一律绕过它。
|
|
386
311
|
|
|
387
|
-
`blockPrivateNetworks` **默认关闭,与引擎一致**:`moli fetch http://10.0.0.1/` 本来就能访问私网地址,插件
|
|
388
|
-
不会悄悄收窄你已安装的 moli 的能力。当 agent 会在你无法控制的页面上跟着链接走、而你希望引擎拒绝私网目标时,
|
|
389
|
-
再打开它或配置 `blockCidrs`。依赖它之前有两点需要知道:moli 文档写明该策略只作用于**直连**目标,因此配置了
|
|
390
|
-
`proxy` 就会绕过它,只有 `blockCidrs`(由代理执行)能覆盖那条路径。这个开关保护的最锐利目标是云元数据地址
|
|
391
|
-
`169.254.169.254`——在虚拟机上它会交出实例凭据。
|
|
392
|
-
|
|
393
312
|
未设置 `exePath` 时探测顺序:`exePath` → 环境变量 `MOLI_BIN` → `%LOCALAPPDATA%\Moli\bin\moli.exe`
|
|
394
313
|
→ `%USERPROFILE%\.local\bin\moli.exe`,随后还会在 `%LOCALAPPDATA%\Moli` 内递归查找 `moli.exe`,
|
|
395
314
|
所以装在带版本号的子目录里也能找到。
|
|
@@ -436,20 +355,18 @@ profile。无论生效的是哪个代理,loopback 地址一律绕过它。
|
|
|
436
355
|
- `report-verify.mjs` 守住一份使用体验报告里的发现:`eval` 与 `dump` 同用会丢弃表达式值(已在参数描述里
|
|
437
356
|
写明)、表达式抛异常时被正确归类而不是甩锅给"安装有问题"、截断同时保留头尾、`proxy` 是可按调用传入的参数、
|
|
438
357
|
以及 `moli_probe` 会说明它的 `httpsOk` 描述的是哪个进程。
|
|
439
|
-
- `
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
`.github/workflows/verify.yml` 在 Linux、Windows、macOS 上跑前两个(无需引擎、无需网络),在 Linux 与 Windows
|
|
452
|
-
上尝试依赖引擎的几套;若 runner 上没有引擎,则报告为跳过而不是失败。
|
|
358
|
+
- `scroll-verify.mjs` 在无引擎时校验表达式构造(像素值、负数、`top`、`bottom`、无效输入被拒、调用方的 `eval`
|
|
359
|
+
值被保留),有引擎时抓取一个"滚动处理器填充槽位"的页面,断言带 `scroll` 时内容出现、不带时只有占位符——
|
|
360
|
+
断言的是**输出**的性质,而不是内部调用。
|
|
361
|
+
|
|
362
|
+
`.github/workflows/verify.yml` 在 Linux、Windows、macOS 上跑 `schema-verify`、`empty-verify` 与
|
|
363
|
+
`scroll-verify`(无需引擎、无需网络),在 Linux 与 Windows 上尝试依赖引擎的几套;若 runner 上没有引擎,
|
|
364
|
+
则报告为跳过而不是失败。
|
|
365
|
+
|
|
366
|
+
`report-verify.mjs` **故意不放进 CI**:其中三项会抓 `example.com`,还有一项抓一个具体的论坛帖子,因此它
|
|
367
|
+
需要能上外网——如果这台机器的流量走代理,还需要那个代理可用。有网络时它是有用的,但**一个因为网站改版就变
|
|
368
|
+
红的测试套件,比没有套件更糟**,所以它只作为本机检查保留。如果你跑它时发现第 1–3 节通过而第 4–6 节失败,
|
|
369
|
+
**先怀疑网络,再怀疑代码**。
|
|
453
370
|
|
|
454
371
|
此外,`lib/moli.js` 与工具层在 Windows 上对 Moli 1.1.16 做了端到端验证:注册、probe、Markdown 与 JSON
|
|
455
372
|
dump、`eval`(含针对 `-t` 参数位置的紧超时回归防护)、PNG 与 PDF 产物签名、截断、全部拒绝路径(二元 dump
|
|
@@ -460,6 +377,19 @@ dump、`eval`(含针对 `-t` 参数位置的紧超时回归防护)、PNG 与
|
|
|
460
377
|
30 处真实结果匹配。另有一个装了本插件的一次性 profile,组合其配置时既无 `JsonSchemaError` 也无被跳过的
|
|
461
378
|
bundle。
|
|
462
379
|
|
|
380
|
+
## 版本说明:1.1.11 与 1.1.12 已废弃
|
|
381
|
+
|
|
382
|
+
**请使用 1.1.13 或更高版本。** 这两个版本之间夹着一次"交互式浏览器"实验——`moli_session`、第二个
|
|
383
|
+
Chromium 后端、原始 CDP 逃生舱、按调用的 CDP 端点、持久化浏览器 profile——约 2300 行机制,远远超出了这个
|
|
384
|
+
插件的定位。它已被移除,代码回到 1.1.10 的形态,只保留一处聚焦的新增。
|
|
385
|
+
|
|
386
|
+
那次工作里唯一有价值的发现保留在 [`/lib/moli.js`](lib/moli.js),并以 `scroll` 参数的形式出现在上文。**它
|
|
387
|
+
本来需要的其他一切,`moli fetch` 自带的 `--eval` 早就有了**——所以那次实验没有换来任何"一个参数换不来的
|
|
388
|
+
东西"。
|
|
389
|
+
|
|
390
|
+
关于这两个版本本身:npm 只允许在很短的时间窗口内撤回发布,且无法单独撤回某一个版本,因此 1.1.11 与
|
|
391
|
+
1.1.12 仍然可安装,但已被标记为废弃并指向这里。`npm install dsh-moli` 会安装到 1.1.13。
|
|
392
|
+
|
|
463
393
|
## 发布
|
|
464
394
|
|
|
465
395
|
发布走 GitHub Actions 的 OIDC 身份,**本机不存任何 npm token,因此没有会失效的凭据**:
|