deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:kelai141/dsh-host-web-compat
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT 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.
Older kernels (MuMu's bundled browser, older WebViews) lack APIs the host page calls directly:
AbortSignal.any() breaks the workspace directory picker's concurrent RPC cancellation;Promise.withResolvers() breaks the host boot-ready tail script, failing the plugin tree;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.
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.
| 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).
__dshBridge plus the /api/android/dir-pick/* polling endpoints that
return real SAF paths);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);__dshThemeBridge: system light/dark into page theme variables);[dsh-boot-stall] stuck, [dsh-boot-ready] really rendered,
[dsh-boot-failed] client plugin tree failed) — see 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.
Snippets live in the POLYFILLS array; the assembly rule is POLYFILL_SCRIPT_BODY in
lib/index.js:
; 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).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.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,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。