dsh-moli 1.1.17 → 1.1.18

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 CHANGED
@@ -577,47 +577,6 @@ On the versions themselves: npm allows a version to be unpublished only briefly
577
577
  individually, so 1.1.11 and 1.1.12 remain installable but are marked deprecated and point here.
578
578
  `npm install dsh-moli` resolves to 1.1.13.
579
579
 
580
- ## Releasing
581
-
582
- Publishing goes through GitHub Actions' OIDC identity, so **no npm token exists to expire**:
583
-
584
- ```bash
585
- npm version patch # or minor / major
586
- git push && git tag v1.1.6 && git push origin v1.1.6
587
- ```
588
-
589
- `.github/workflows/publish.yml` checks that the tag matches `package.json`, runs the offline suites,
590
- and publishes. One-time setup on npmjs.com: **package → Settings → Trusted Publisher → GitHub
591
- Actions**, with user `27painter`, repository `dsh-moli`, workflow filename `publish.yml`, and the
592
- `npm publish` action allowed.
593
-
594
- **Do not publish with a stored token.** This machine was configured with a classic token that bypasses
595
- 2FA, and npm is retiring those; when it was revoked, the publish failed with
596
-
597
- ```
598
- E404 Not Found - PUT https://registry.npmjs.org/dsh-moli
599
- The requested resource 'dsh-moli@1.1.5' could not be found or you do not have permission
600
- ```
601
-
602
- which reads as a missing package and is actually an authorisation failure. Run
603
- `node scripts/preflight-publish.mjs` before any local publish: it reports whether the credential can
604
- authenticate and names that misleading 404 when it cannot.
605
-
606
- ### Deprecating a version: do it on the website
607
-
608
- Publishing is scripted; deprecating deliberately is not. Use the package's page on npmjs.com —
609
- **package → Settings → Deprecate** — and name the versions and the reason.
610
-
611
- `npm deprecate dsh-moli@1.1.11 "…"` produces the same misleading failure as above when the local
612
- credential is dead, and there is a second reason not to reach for a token here. npm restricted
613
- 2FA-bypass granular tokens from package-management operations on 31 July 2026, so the kind of token
614
- that used to make `deprecate` scriptable is being withdrawn, while `publish` has a supported
615
- token-free path through OIDC. A release process that needs a token only for the rare act of
616
- deprecating is a process that will be broken the next time it is needed, which is exactly what
617
- happened with 1.1.11 and 1.1.12.
618
-
619
- The 1.0.0 and 1.0.1 deprecations were done this way, from the website.
620
-
621
580
  ## License
622
581
 
623
582
  MIT. See [LICENSE](LICENSE).
package/README.zh.md CHANGED
@@ -521,43 +521,6 @@ Chromium 后端、原始 CDP 逃生舱、按调用的 CDP 端点、持久化浏
521
521
  关于这两个版本本身:npm 只允许在很短的时间窗口内撤回发布,且无法单独撤回某一个版本,因此 1.1.11 与
522
522
  1.1.12 仍然可安装,但已被标记为废弃并指向这里。`npm install dsh-moli` 会安装到 1.1.13。
523
523
 
524
- ## 发布
525
-
526
- 发布走 GitHub Actions 的 OIDC 身份,**本机不存任何 npm token,因此没有会失效的凭据**:
527
-
528
- ```bash
529
- npm version patch # 或 minor / major
530
- git push && git tag v1.1.6 && git push origin v1.1.6
531
- ```
532
-
533
- `.github/workflows/publish.yml` 会先校验 tag 与 `package.json` 版本一致、跑离线测试套件,然后发布。
534
- npmjs.com 上需要一次性配置:**package → Settings → Trusted Publisher → GitHub Actions**,用户
535
- `27painter`、仓库 `dsh-moli`、workflow 文件名 `publish.yml`,并允许 `npm publish` 动作。
536
-
537
- **不要再用本机存储的 token 发布。** 这台机器原先配的是一个绕过 2FA 的 classic token,npm 正在淘汰这类
538
- token;它被吊销后,发布会报:
539
-
540
- ```
541
- E404 Not Found - PUT https://registry.npmjs.org/dsh-moli
542
- The requested resource 'dsh-moli@1.1.5' could not be found or you do not have permission
543
- ```
544
-
545
- **这看起来像"包不存在",实际是认证失败。** 任何本地发布之前先跑
546
- `node scripts/preflight-publish.mjs`:它会告诉你凭据能否认证,不能时明确点出这个误导性的 404。
547
-
548
- ### 废弃某个版本:去网站操作,别用命令行
549
-
550
- 发布是脚本化的,**废弃则刻意不做成脚本**。用 npmjs.com 上的包页面——**package → Settings →
551
- Deprecate**——选版本、写理由。
552
-
553
- `npm deprecate dsh-moli@1.1.11 "…"` 在本机凭据失效时会给出上面那个同样误导的失败,而且这里还有第二个
554
- 理由不要依赖 token:npm 在 **2026 年 7 月 31 日**起禁止 2FA-bypass 类型的 granular token 执行**包管理**
555
- 操作,也就是说"以前能让 `deprecate` 脚本化"的那类凭据正在被取消;而 `publish` 有受支持的、免 token 的
556
- OIDC 路径。**一个只在"偶尔废弃版本"时才需要 token 的流程,注定在下次需要它时是坏的**——1.1.11 与 1.1.12
557
- 遇到的正是这种情况。
558
-
559
- 1.0.0 与 1.0.1 的废弃就是通过网站完成的。
560
-
561
524
  ## 许可
562
525
 
563
526
  MIT,见 [LICENSE](LICENSE)。
package/index.js CHANGED
@@ -189,6 +189,7 @@ const FETCH_OUTPUT_SCHEMA = {
189
189
  sandboxTlsBlock: { type: 'boolean' },
190
190
  unreachable: { type: 'boolean' },
191
191
  possiblyUnreachable: { type: 'boolean' },
192
+ conditionTimedOut: { type: 'boolean' },
192
193
  note: { type: 'string' },
193
194
  },
194
195
  };
@@ -232,6 +233,10 @@ function renderFetch(_args, value) {
232
233
  if (value.sandboxTlsBlock === true) lines.push('Cause: the DSH sandbox blocks Schannel client credentials (HTTPS only).');
233
234
  if (value.unreachable === true) lines.push('Cause: the host did not answer; a local forward proxy is the usual fix on this network.');
234
235
  else if (value.possiblyUnreachable === true) lines.push('Cause: the host did not answer in time; it may be blocked, or the page may simply be slow.');
236
+ // A wait that never resolved is the one failure that is definitely not the
237
+ // network: the page arrived and was parsed. Saying so up front keeps the caller
238
+ // from spending their time on a proxy that was never the problem.
239
+ else if (value.conditionTimedOut === true) lines.push('Cause: the page loaded, but the wait condition never became true — this is not a network problem.');
235
240
  if (typeof value.hint === 'string' && value.hint !== '') lines.push(`Next: ${value.hint}`);
236
241
  // An HTTP error keeps its body under `failOnHttpError`. Reporting the failure
237
242
  // without the body would throw away the error page, which is the part worth
package/lib/moli.js CHANGED
@@ -298,22 +298,44 @@ export function isSandboxTlsFailure(message) {
298
298
  }
299
299
 
300
300
  /**
301
- * Classify a transport failure as "the host never answered".
301
+ * Classify a transport failure: did the host never answer, or did a wait condition
302
+ * simply never become true?
302
303
  *
303
304
  * A blocked host fails at connect time (`Failed to connect`, reset, refused) or,
304
305
  * when the socket never opens, as a readiness timeout with nothing to show. A
305
306
  * readiness timeout can also mean a genuinely slow page, so that weaker case is
306
307
  * reported separately as {@link possiblyUnreachable}.
307
308
  *
309
+ * **But not every readiness timeout is about the network.** The engine reports the
310
+ * condition it was waiting for, and three of them mean the page was already there:
311
+ *
312
+ * ... while waiting for a selector
313
+ * ... while waiting for a script to become truthy
314
+ * ... while waiting for a subresource response
315
+ *
316
+ * In those cases the document was fetched and parsed; what did not happen is the
317
+ * selector appearing, the script becoming true, or the matching response arriving.
318
+ * Telling the caller to retry or configure a proxy then sends them to look at their
319
+ * network for a problem that is in their selector -- measured: `waitSelector` on an
320
+ * element that is not on the page used to report "the host did not answer in time".
321
+ * A timeout "while waiting for response headers" is the opposite case and stays
322
+ * classified as possibly unreachable, because that one really is the server.
323
+ *
308
324
  * @param message - condensed stderr text.
309
- * @returns whether a connect-time failure was reported, and whether any timeout was.
325
+ * @returns `unreachable` (connect-time), `possiblyUnreachable` (network timeout), and
326
+ * `conditionTimedOut` (a wait that the page itself never satisfied).
310
327
  */
311
328
  export function isUnreachableFailure(message) {
312
329
  const text = String(message);
313
330
  const connectFailure = /Failed to connect|Could not connect|Connection was reset|Connection refused/iu.test(text);
331
+ const anyTimeout = /Timeout was reached|readiness timed out/iu.test(text);
332
+ const conditionTimedOut = /readiness timed out .*?while waiting for (?:a selector|a script|a subresource response)/iu.test(text);
314
333
  return {
315
334
  unreachable: connectFailure,
316
- possiblyUnreachable: connectFailure || /Timeout was reached|readiness timed out/iu.test(text),
335
+ // A condition timeout is not evidence about the network: the page arrived. Only
336
+ // the remaining timeouts are worth mentioning as a reachability possibility.
337
+ possiblyUnreachable: connectFailure || (anyTimeout && !conditionTimedOut),
338
+ conditionTimedOut,
317
339
  };
318
340
  }
319
341
 
@@ -609,7 +631,7 @@ export function runFetch(options) {
609
631
  // the socket never opens, as a readiness timeout with nothing to show. A
610
632
  // readiness timeout can also mean a genuinely slow page, so it is reported
611
633
  // as *possibly* unreachable and worded as a suggestion rather than a verdict.
612
- const { unreachable, possiblyUnreachable } = isUnreachableFailure(stderr);
634
+ const { unreachable, possiblyUnreachable, conditionTimedOut } = isUnreachableFailure(stderr);
613
635
  // The engine reports a thrown expression as
614
636
  // `Reason: JavaScript evaluation failed: TypeError: ... at <anonymous>:1:6`
615
637
  // which is a bug in the call, not an install or network problem. Falling
@@ -620,15 +642,27 @@ export function runFetch(options) {
620
642
  // status 1" leaves the caller to guess whether an image exists; the useful fact
621
643
  // is that it does not, and that a text dump would have captured this page.
622
644
  const binaryFailure = isBinary && httpStatusFrom(stderr) !== undefined;
645
+ // Name the wait that never resolved. "Retry or set a proxy" is actively
646
+ // misleading here: the page arrived, so the network is fine, and the caller
647
+ // would spend their time on the one part of the setup that is working.
648
+ const conditionHint = args.waitSelector !== undefined
649
+ ? `The selector ${JSON.stringify(args.waitSelector)} never matched. The page loaded, so this is not a network problem: check the spelling, and confirm the element is there with \`selector\` (it reports the match count, where a wait only times out) or \`dump\`.`
650
+ : args.waitScript !== undefined
651
+ ? 'The wait expression never became truthy. The page loaded, so this is not a network problem: check the expression against the page with `eval`, and remember it is polled rather than evaluated once.'
652
+ : (args.waitResponseUrl !== undefined || args.waitResponseJson !== undefined)
653
+ ? 'No response matching that condition arrived. The page loaded, so this is not a network problem: the request may never be made, or its payload may differ from what you matched on. Use `dump` with a short timeout to see what the page does, or an `eval` that inspects the fetch.'
654
+ : 'The wait condition never became true. The page loaded, so this is not a network problem: check the condition.';
623
655
  const hint = evalFailure
624
656
  ? 'The eval expression threw. Check that the page has reached the state you expect (`wait` or `waitSelector`), that the selector matches an element, and that the expression is valid JavaScript. The engine error names the type and position.'
625
657
  : binaryFailure
626
658
  ? 'The engine could not render this page, so no file was written. An HTTP error still produces a body for a text dump (markdown, html or json), so ask for one of those if you need to see what the server returned.'
627
659
  : sandboxTls
628
660
  ? 'The DSH file sandbox blocks Schannel client credentials, so moli cannot do TLS at low integrity. Retry the same call with sandbox_permissions "danger-full-access" (with a justification), use an http:// URL, or pass proxy to reach a local forward proxy.'
629
- : possiblyUnreachable
630
- ? 'The host did not answer in time. Reachability to some hosts varies on this network, so retrying may succeed; if a local forward proxy is available, setting the plugin config field `proxy` is the other option. A certificate problem would report a schannel error instead of a timeout.'
631
- : 'Run moli_probe to confirm the install, then retry; use waitSelector or eval for a page that renders late.';
661
+ : conditionTimedOut
662
+ ? conditionHint
663
+ : possiblyUnreachable
664
+ ? 'The host did not answer in time. Reachability to some hosts varies on this network, so retrying may succeed; if a local forward proxy is available, setting the plugin config field `proxy` is the other option. A certificate problem would report a schannel error instead of a timeout.'
665
+ : 'Run moli_probe to confirm the install, then retry; use waitSelector or eval for a page that renders late.';
632
666
  const failureStatus = httpStatusFrom(stderr);
633
667
  return {
634
668
  ok: false,
@@ -638,6 +672,7 @@ export function runFetch(options) {
638
672
  sandboxTlsBlock: sandboxTls,
639
673
  unreachable,
640
674
  possiblyUnreachable,
675
+ ...(conditionTimedOut ? { conditionTimedOut: true } : {}),
641
676
  ...(evalFailure ? { evalFailed: true } : {}),
642
677
  hint,
643
678
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-moli",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
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",