dsh-moli 1.0.2 → 1.0.4

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.
Files changed (4) hide show
  1. package/README.md +45 -3
  2. package/README.zh.md +51 -29
  3. package/index.js +69 -48
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -24,15 +24,57 @@ straight from the repository works too, and needs GitHub to be reachable:
24
24
  dsh plugin --profile web add github:27painter/dsh-moli
25
25
  ```
26
26
 
27
- Either way, restart DSH, then check the engine and fetch a page:
27
+ > [!IMPORTANT]
28
+ > **The npm package contains the plugin only — it does not bundle Moli.** It is 26 kB of JavaScript;
29
+ > the `moli.exe` engine is a separate ~101 MB Rust binary with its own release process, so this
30
+ > plugin installs alongside Moli rather than shipping it.
31
+ >
32
+ > **There is no npm package for Moli itself.** The names `moli` and `moli-cli` on the registry belong
33
+ > to unrelated projects, and `@lexmount/moli` does not exist. Moli is distributed as prebuilt
34
+ > binaries on [GitHub Releases](https://github.com/lexmount/moli/releases/latest) — do not install
35
+ > the engine by guessing a package name.
36
+
37
+ Either plugin route ends the same way. Restart DSH, then check the engine and fetch a page:
28
38
 
29
39
  ```
30
40
  moli_probe
31
41
  moli_fetch url=https://example.com dump=markdown
32
42
  ```
33
43
 
34
- Moli itself is a separate download. `moli_probe` reports whether it is present, and
35
- `moli_probe action=install` can fetch it. See [Installation](#installation).
44
+ ### Getting the Moli engine
45
+
46
+ `moli_probe` reports whether it is present. If it is not, pick one of:
47
+
48
+ **1. Let the plugin install it** — several download routes, and a SHA256 of what actually ran:
49
+
50
+ ```
51
+ moli_probe action=install
52
+ ```
53
+
54
+ **2. Official one-line installer** (Moli's own documented command):
55
+
56
+ ```powershell
57
+ # Windows
58
+ powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
59
+ ```
60
+
61
+ ```sh
62
+ # Linux / macOS
63
+ curl --proto '=https' --tlsv1.2 -fsSL https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh
64
+ ```
65
+
66
+ **3. Direct binary, if you would rather not run an installer:**
67
+
68
+ ```powershell
69
+ # pick the asset for your architecture from the releases page
70
+ curl.exe -L -o moli.zip https://github.com/lexmount/moli/releases/latest/download/moli-x86_64-pc-windows-msvc.zip
71
+ Expand-Archive moli.zip -DestinationPath "$env:LOCALAPPDATA\Moli" -Force
72
+ ```
73
+
74
+ Those `releases/latest/download/...` URLs always resolve to the newest release, so they stay valid
75
+ without pinning a version. See [Verifying the download](#verifying-the-download) to check what you
76
+ got against GitHub's published digest, and [Manual install of Moli](#manual-install-of-moli) for the
77
+ same commands in context.
36
78
 
37
79
  ## What it provides
38
80
 
package/README.zh.md CHANGED
@@ -19,6 +19,14 @@ dsh plugin --profile web add dsh-moli
19
19
  dsh plugin --profile web add github:27painter/dsh-moli
20
20
  ```
21
21
 
22
+ > [!IMPORTANT]
23
+ > **npm 包只有插件本身,不含 Moli 本体。** 它是 26 kB 的 JavaScript;`moli.exe` 引擎是另一个约
24
+ > 101 MB 的 Rust 二进制、有自己的发布流程。所以本插件是**装在 Moli 旁边**,不是把 Moli 打包进来。
25
+ >
26
+ > **Moli 本体没有 npm 包。** registry 上的 `moli` 和 `moli-cli` 属于无关项目,`@lexmount/moli`
27
+ > 并不存在。Moli 以预编译二进制形式发布在
28
+ > [GitHub Releases](https://github.com/lexmount/moli/releases/latest)——**不要靠猜包名去装引擎**。
29
+
22
30
  两种方式都要重启 DSH,然后自检并抓一个页面:
23
31
 
24
32
  ```
@@ -26,8 +34,38 @@ moli_probe
26
34
  moli_fetch url=https://example.com dump=markdown
27
35
  ```
28
36
 
29
- Moli 本体是另外下载的。`moli_probe` 会报告它是否在场,`moli_probe action=install` 可以替你拉取。
30
- 详见[安装](#安装)。
37
+ ### 获取 Moli 引擎
38
+
39
+ `moli_probe` 会报告它是否在场。如果不在,三种方式任选:
40
+
41
+ **1. 让插件替你装** —— 多路径下载,并报告实际执行字节的 SHA256:
42
+
43
+ ```
44
+ moli_probe action=install
45
+ ```
46
+
47
+ **2. 官方一行安装**(Moli 自己的文档命令):
48
+
49
+ ```powershell
50
+ # Windows
51
+ powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
52
+ ```
53
+
54
+ ```sh
55
+ # Linux / macOS
56
+ curl --proto '=https' --tlsv1.2 -fsSL https://github.com/lexmount/moli/releases/latest/download/moli-installer.sh | sh
57
+ ```
58
+
59
+ **3. 直接下二进制**(不想跑安装器的话):
60
+
61
+ ```powershell
62
+ # 从 releases 页挑与你架构匹配的资产
63
+ curl.exe -L -o moli.zip https://github.com/lexmount/moli/releases/latest/download/moli-x86_64-pc-windows-msvc.zip
64
+ Expand-Archive moli.zip -DestinationPath "$env:LOCALAPPDATA\Moli" -Force
65
+ ```
66
+
67
+ 这些 `releases/latest/download/...` 链接始终指向最新发布,所以不用钉版本号也不会失效。想核对下载内容,
68
+ 见[校验下载](#校验下载);同样的命令放在上下文里,见[手动安装 Moli](#手动安装-moli)。
31
69
 
32
70
  ## 与 Moli 官方 agent skill 的关系
33
71
 
@@ -108,6 +146,15 @@ moli_probe action=install
108
146
  profile 插件需要两半都齐:依赖边 + `dsh.profile.bundles` 条目。缺一个 loader 就看不到它,而无法解析的
109
147
  bundle 会被**静默跳过**,只在 stderr 留一行 `skipping profile bundle …`。工具迟迟不出现时先找这行。
110
148
 
149
+ 从本地目录安装时,完整的四步是:
150
+
151
+ 1. 把本目录放到任意位置,例如 `D:\dsh_plugins\dsh-moli`。
152
+ 2. 在该 profile 的 `package.json` 中加入:
153
+ - `dependencies`: `"dsh-moli": "link:D:/dsh_plugins/dsh-moli"`
154
+ - `dsh.profile.bundles`: `"dsh-moli"`
155
+ 3. 在 profile 目录执行 `pnpm install`(生成 `node_modules/dsh-moli`)。
156
+ 4. 重启 DSH。
157
+
111
158
  ## 手动安装 Moli
112
159
 
113
160
  自动路径都失败时(受限网络可能把 release 主机整个挡掉),自己装。下面是 Moli 官方文档里的命令。
@@ -192,33 +239,8 @@ moli-x86_64-pc-windows-msvc.zip 43.8 MB sha256:5247309837b6eaf6e73bfa18f2a8d8d
192
239
  ```
193
240
 
194
241
  未设置 `exePath` 时探测顺序:`exePath` → 环境变量 `MOLI_BIN` → `%LOCALAPPDATA%\Moli\bin\moli.exe`
195
- → `%USERPROFILE%\.local\bin\moli.exe`。
196
-
197
- ## 安装
198
-
199
- 先装 Moli 本体——本插件只负责驱动它,不分发它。Windows 上:
200
-
201
- ```powershell
202
- powershell -ExecutionPolicy ByPass -c "irm https://github.com/lexmount/moli/releases/latest/download/moli-installer.ps1 | iex"
203
- ```
204
-
205
- (Linux/macOS 用对应的 `moli-installer.sh`,见 [Moli README](https://github.com/lexmount/moli#quick-start)。)
206
-
207
- 然后插件自身必须**同时**是 profile 依赖与已声明的 bundle,缺一个 loader 就看不到它:
208
-
209
- 1. 把本目录放到任意位置,例如 `D:\dsh_plugins\dsh-moli`。
210
- 2. 在该 profile 的 `package.json` 中加入:
211
- - `dependencies`: `"dsh-moli": "link:D:/dsh_plugins/dsh-moli"`
212
- - `dsh.profile.bundles`: `"dsh-moli"`
213
- 3. 在 profile 目录执行 `pnpm install`(生成 `node_modules/dsh-moli`)。
214
- 4. 重启 DSH。
215
-
216
- 注意:
217
-
218
- - `dsh plugin --profile desktop …` 会被拒绝:desktop profile 归 Electron 应用所有。直接改它的
219
- `package.json`,或用应用内插件页(侧边栏 →**插件**→**添加插件**),那条路径同样走 pnpm。
220
- - bundle 无法解析或未通过兼容性检查时,loader 会**静默跳过**,只在 stderr 留一行
221
- `skipping profile bundle …`。工具迟迟不出现时先找这行。
242
+ → `%USERPROFILE%\.local\bin\moli.exe`,随后还会在 `%LOCALAPPDATA%\Moli` 内递归查找 `moli.exe`,
243
+ 所以装在带版本号的子目录里也能找到。
222
244
 
223
245
  ## 已知限制
224
246
 
package/index.js CHANGED
@@ -30,49 +30,64 @@ const DEFAULTS = {
30
30
  /**
31
31
  * Parameter schema of `moli_fetch`.
32
32
  *
33
- * The two schema layers follow DIFFERENT rules, which is easy to get wrong:
34
- * - A parameter spec uses the author DSL, where a property-level `required: true`
35
- * is correct. `parameterSchemaSpecToJsonSchema` hoists it into an object-level
36
- * `required` array and strips the flag.
37
- * - A raw output schema is validated as-is, so `required` there must already be an
38
- * object-level array of names. A property-level `required: true` makes the
39
- * registry reject the whole tool with `JsonSchemaError`.
33
+ * Both schema layers must be RAW JSON Schema here — object root, with `required`
34
+ * as an object-level array — because this plugin registers plain definitions with
35
+ * `ctx.tools.register` instead of going through `defineTool`.
36
+ *
37
+ * That choice is forced: `defineTool` lives in `@deepseek-ai/dsh-tools`, a bare
38
+ * specifier, and a profile plugin is loaded from its real path on disk, so the
39
+ * specifier does not resolve. `schemaOf` then projects `definition.parameters`
40
+ * verbatim to the provider — it is NOT converted — so a parameter left in the
41
+ * author DSL reaches the model without a root `type`, and the provider rejects the
42
+ * request with `schema must be a JSON Schema of 'type: "object"', got 'type: null'`.
43
+ *
44
+ * The two entry points disagree, which is why one shape cannot serve both:
45
+ * - a raw `output.schema` must use an object-level `required` array; a
46
+ * property-level `required: true` fails `assertSupportedJsonSchema` at registration
47
+ * - `defineTool` is the opposite: its DSL rejects an object-level `required` and
48
+ * expects `required: true` on each property instead
49
+ * Registering raw means the raw rules apply to parameters and outputs alike.
40
50
  */
41
51
  const FETCH_PARAMETERS = {
42
- url: { type: 'string', required: true, description: 'Absolute http:// or https:// URL to fetch.' },
43
- dump: {
44
- type: 'string',
45
- enum: ['html', 'markdown', 'json', 'semantic_tree', 'semantic_tree_text', 'screenshot', 'screenshot_full', 'pdf'],
46
- description: 'Output format. Omit for rendered HTML. `markdown` and `semantic_tree_text` are the token-efficient choices for reading a page; `json` carries status and final_url; screenshot/pdf need writeTo.',
47
- },
48
- writeTo: {
49
- type: 'string',
50
- description: 'Absolute path to save the output. Required for screenshot/pdf; optional for text dumps larger than maxChars.',
51
- },
52
- eval: {
53
- type: 'string',
54
- description: 'JavaScript expression evaluated once the page is ready; its value is returned as text or JSON instead of the page.',
55
- },
56
- wait: {
57
- type: 'string',
58
- enum: ['domcontentloaded', 'load', 'networkidle', 'domstable', 'done'],
59
- description: 'Readiness stage to wait for. Defaults to `done`.',
52
+ type: 'object',
53
+ additionalProperties: false,
54
+ required: ['url'],
55
+ properties: {
56
+ url: { type: 'string', description: 'Absolute http:// or https:// URL to fetch.' },
57
+ dump: {
58
+ type: 'string',
59
+ enum: ['html', 'markdown', 'json', 'semantic_tree', 'semantic_tree_text', 'screenshot', 'screenshot_full', 'pdf'],
60
+ description: 'Output format. Omit for rendered HTML. `markdown` and `semantic_tree_text` are the token-efficient choices for reading a page; `json` carries status and final_url; screenshot/pdf need writeTo.',
61
+ },
62
+ writeTo: {
63
+ type: 'string',
64
+ description: 'Absolute path to save the output. Required for screenshot/pdf; optional for text dumps larger than maxChars.',
65
+ },
66
+ eval: {
67
+ type: 'string',
68
+ description: 'JavaScript expression evaluated once the page is ready; its value is returned as text or JSON instead of the page.',
69
+ },
70
+ wait: {
71
+ type: 'string',
72
+ enum: ['domcontentloaded', 'load', 'networkidle', 'domstable', 'done'],
73
+ description: 'Readiness stage to wait for. Defaults to `done`.',
74
+ },
75
+ waitSelector: { type: 'string', description: 'Wait until this CSS selector matches.' },
76
+ delayMs: { type: 'integer', description: 'Extra milliseconds to wait after readiness.' },
77
+ timeoutMs: { type: 'integer', description: 'Total readiness deadline in milliseconds. Defaults to the plugin configuration.' },
78
+ maxChars: { type: 'integer', description: 'Truncate inline text output to this many characters. Defaults to the plugin configuration.' },
79
+ headers: { type: 'array', items: { type: 'string' }, description: 'Extra request headers, each as "Name: value".' },
80
+ userAgent: { type: 'string', description: 'Override the User-Agent.' },
81
+ layout: { type: 'boolean', description: 'Force the real layout renderer on. Implied by screenshot and pdf.' },
82
+ scrollbars: { type: 'boolean', description: 'Show native scrollbars in rendered output.' },
83
+ noJs: { type: 'boolean', description: 'Disable page-authored JavaScript.' },
84
+ noCss: { type: 'boolean', description: 'Disable page-authored CSS.' },
85
+ resources: { type: 'boolean', description: 'Also fetch images, fonts, audio, video, and text tracks.' },
86
+ withFrames: { type: 'boolean', description: 'Include subframes in the output.' },
87
+ insecureTls: { type: 'boolean', description: 'Skip TLS host verification. Use only for a known-broken local endpoint.' },
88
+ profileDir: { type: 'string', description: 'Moli profile directory carrying imported cookies or storage.' },
89
+ cookieFile: { type: 'string', description: 'Netscape/curl cookie-jar file to send.' },
60
90
  },
61
- waitSelector: { type: 'string', description: 'Wait until this CSS selector matches.' },
62
- delayMs: { type: 'integer', description: 'Extra milliseconds to wait after readiness.' },
63
- timeoutMs: { type: 'integer', description: 'Total readiness deadline in milliseconds. Defaults to the plugin configuration.' },
64
- maxChars: { type: 'integer', description: 'Truncate inline text output to this many characters. Defaults to the plugin configuration.' },
65
- headers: { type: 'array', items: { type: 'string' }, description: 'Extra request headers, each as "Name: value".' },
66
- userAgent: { type: 'string', description: 'Override the User-Agent.' },
67
- layout: { type: 'boolean', description: 'Force the real layout renderer on. Implied by screenshot and pdf.' },
68
- scrollbars: { type: 'boolean', description: 'Show native scrollbars in rendered output.' },
69
- noJs: { type: 'boolean', description: 'Disable page-authored JavaScript.' },
70
- noCss: { type: 'boolean', description: 'Disable page-authored CSS.' },
71
- resources: { type: 'boolean', description: 'Also fetch images, fonts, audio, video, and text tracks.' },
72
- withFrames: { type: 'boolean', description: 'Include subframes in the output.' },
73
- insecureTls: { type: 'boolean', description: 'Skip TLS host verification. Use only for a known-broken local endpoint.' },
74
- profileDir: { type: 'string', description: 'Moli profile directory carrying imported cookies or storage.' },
75
- cookieFile: { type: 'string', description: 'Netscape/curl cookie-jar file to send.' },
76
91
  };
77
92
 
78
93
  /**
@@ -266,14 +281,20 @@ export function apply(ctx, config) {
266
281
  + ' reports the executable and the SHA256 of the installer it ran. Use "install" only when moli_fetch'
267
282
  + ' reports that moli.exe is missing; it downloads and executes third-party code.',
268
283
  parameters: {
269
- action: {
270
- type: 'string',
271
- enum: ['probe', 'install'],
272
- description: '`probe` (default) only inspects; `install` downloads and runs the official release installer.',
273
- },
274
- expectedSha256: {
275
- type: 'string',
276
- description: 'With action "install": abort unless the downloaded installer matches this SHA256.',
284
+ type: 'object',
285
+ additionalProperties: false,
286
+ // No required entries: every parameter is optional, and each execution path
287
+ // validates what it actually needs.
288
+ properties: {
289
+ action: {
290
+ type: 'string',
291
+ enum: ['probe', 'install'],
292
+ description: '`probe` (default) only inspects; `install` downloads and runs the official release installer.',
293
+ },
294
+ expectedSha256: {
295
+ type: 'string',
296
+ description: 'With action "install": abort unless the downloaded installer matches this SHA256.',
297
+ },
277
298
  },
278
299
  },
279
300
  output: { schema: PROBE_OUTPUT_SCHEMA, render: renderProbe },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-moli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "type": "module",
5
5
  "description": "DeepSeek Harness tools for the local Moli headless browser: rendered fetches, markdown, semantic trees, screenshots, and PDFs.",
6
6
  "license": "MIT",