返回目录
其他 插件

dsh-host-web-compat

kelai141/dsh-host-web-compat

dsh 宿主插件——经 webserver 钩子向页面注入旧内核浏览器 polyfill。

Stars
2
Forks
1
Issues
1
更新
4 天前

PROJECT TOPICS

项目标签

INSTALL REFERENCE

安装参考

未验证
dsh plugin --profile web add github:kelai141/dsh-host-web-compat

该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。

PROJECT README

README

dsh-host-web-compat

🌐 中文说明 / 中文 README

DeepSeek Harness × Android ecosystem · dsh-mobile-apk (shell APK) · dsh-shell-termux (shell) · dsh-client-ui-responsive (mobile UI)

Host plugin for DeepSeek Harness that injects legacy-browser polyfills into every served page via the webserver tapIndex hook, and bridges the shell APK's directory picker and path-open channels.

Background

Older kernels (MuMu's bundled browser, older WebViews) lack APIs the host page calls directly:

  • missing AbortSignal.any() breaks the workspace directory picker's concurrent RPC cancellation;
  • missing Promise.withResolvers() breaks the host boot-ready tail script, failing the plugin tree;
  • missing global Iterator (Chrome 122+) makes upstream 0.1.5 client bundles (documentpreview and others) throw Iterator is not defined at import time ("Failed to load plugins").

The plugin injects idempotent polyfills at the HTML level — no browser-side changes needed.

Quick start

1. Install — package into the profile's node_modules.

2. Mount — in the profile's cordis.patch.yml:

- insert:
    - id: web-compat
      name: '@dsh-android/dsh-host-web-compat'
      disabled: !!js process.platform !== 'android'

3. Restart the service; the picker works again in old kernels.

Polyfills injected

API condition note
AbortSignal.any missing Chrome 116+ / Node 20.3+
AbortSignal.timeout missing Chrome 103+ / Node 17.3+
structuredClone missing Chrome 98+ / Node 17+
Object.hasOwn missing Chrome 93+ / Safari 15.4+
Array.prototype.at missing Chrome 92+
String.prototype.replaceAll missing Chrome 85+
crypto.randomUUID missing Chrome 92+, secure context required
Promise.withResolvers missing Chrome 119+; the host boot-ready tail depends on it
global Iterator + iterator helpers missing Chrome 122+ (map/filter/take/drop/flatMap/toArray/forEach/some/every/find/reduce + Iterator.from)
Object.groupBy / Map.groupBy missing Chrome 117+
Set.prototype set methods missing Chrome 122+ (union/intersection/difference/symmetricDifference/isSubsetOf/isSupersetOf/isDisjointFrom)
Array.fromAsync missing Chrome 121+

Idempotent: skipped when already present. Every snippet shares one <script> element, so each one must end on a complete statement (see Development constraints).

Other injections

  • Directory-picker bridge (__dshBridge plus the /api/android/dir-pick/* polling endpoints that return real SAF paths);
  • open-path (window.__dshOpenPath: chat mentions and tool-row paths into the shell chooser) — 0.14 makes resolution session-scoped (relative paths resolve against the active session cwd);
  • theme bridge (__dshThemeBridge: system light/dark into page theme variables);
  • agent tool-row path recognition — 0.14 keys on DOM facts (not CSS-module class names) when clicking an absolute path in a tool row hands it to the shell chooser;
  • boot watchdog: 40s stuck diagnostics plus the three contract lines the shell APK parses by field position ([dsh-boot-stall] stuck, [dsh-boot-ready] really rendered, [dsh-boot-failed] client plugin tree failed) — see Boot diagnostics contract.

Boot diagnostics contract

The watchdog reports on the console.error channel. The shell APK matches the line prefix and then reads the remaining fields by position, so the field order below is part of the interface:

line meaning fields after the prefix
[dsh-boot-stall] dsh-boot-diag source=page-stall ... 40s without a render pageSideRuntime=<folded JSON> detail=<folded k=v>
[dsh-boot-ready] dsh-boot-diag source=page-ready ... #root rendered and the boot page is gone pageSideRuntime=<folded JSON>
[dsh-boot-failed] dsh-boot-diag source=page-plugin-fail ... client plugin tree failed detail=reason=<first line, 200 chars> takenMs=<ms> failedCount=<N> rendered=<bool>, failedIds=<comma list or ->, pageSideRuntime=<folded JSON, last>

failedIds keeps only items that look like a loader entry id (/^[@A-Za-z0-9][@A-Za-z0-9._\/-]*$/, at most 120 characters), at most 8 items, unquoted and space-free; - when none is readable. It sits before pageSideRuntime because fold truncates at 2048 characters and a truncated JSON tail cannot be parsed at all.

Readiness is rendered() && !bootPagePresent(). A failed client plugin tree renders a boot page that still gives #root children, so the old rendered()-only criterion reported it as ready: the shell stopped its stall timer and the failure stayed silent. The failure line is published from two idempotent trigger points — the MutationObserver callback (2s debounce) and every readyWatch tick, which covers a failure page that reached its final state before the observer was installed. pendingBoot() is unchanged, so a failure page deliberately does not raise the 40s overlay: it exits through [dsh-boot-failed] instead.

Development constraints

Snippets live in the POLYFILLS array; the assembly rule is POLYFILL_SCRIPT_BODY in lib/index.js:

  • Every snippet must end with ; or }. All snippets share one <script> element, and a single syntax error makes the parser reject the whole element: the page then behaves as if no polyfill existed while the served HTML still contains the snippet text (measured 2026-09-10 — the Set-methods IIFE ended with })() against the next snippet's if (, so WebView 110 reported Iterator is not defined at import time).
  • apply() parse-checks the assembled markup with new Function at load and throws on failure (loud failure beats a silent downgrade).
  • Shim the real structure, not just the name: the global Iterator must be a constructor whose .prototype IS %IteratorPrototype% (bundled code such as pdfjs patches Iterator.prototype.x directly), and iterator wrappers must inherit that same prototype or chained helpers break.

Tests

node scripts/smoke-injections.mjs   # assembly + per-script parse + page-marker assertions, no install needed
node scripts/boot-watchdog.test.mjs # watchdog behaviour: pendingBoot / ready / failure contract line

The script loads the real plugin against a stubbed cordis, runs one tapIndex transform, and parses every served script element — the only check that catches an assembly syntax error (grepping the text does not).

CLASSIFICATION EVIDENCE

分类依据

项目类型插件
功能分类其他
规则置信度低

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。