dsh-plugin-tether 0.1.24 → 0.1.26
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/CHANGELOG.md +32 -0
- package/README.md +46 -2
- package/README.zh.md +46 -2
- package/index.js +36 -20
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录面向用户的变化。每个版本的这一节会原样作为该版本 Release 的说明。
|
|
4
4
|
|
|
5
|
+
## 0.1.26
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- **dsh 0.2.2-rc 上手机打开侧栏变成一块黑、点不动**。0.2.2-rc 起 dsh 在 <768px 自带抽屉:主框架挂 `data-sidebar-drawer`,并渲染一层 `z-index:70` 的遮罩;tether 注入的窄屏抽屉把侧栏钉成 `z-index:30`,侧栏于是被原生遮罩压到下面——深色主题下遮罩是 50% 黑,侧栏看起来就是一块黑,遮罩的 `pointer-events:auto` 又把点击全吞了。现在这段手工抽屉只在 dsh 没有原生抽屉(框架不带 `data-sidebar-drawer`)时生效,原生抽屉出现时整体让位:滑入滑出、遮罩、点外收起都交给 dsh 自己,侧栏里的「连接手机/我的电脑」按钮照常挂载;点外收起那段脚本同样只在没有原生抽屉时接管。老版本 dsh 行为不变。
|
|
10
|
+
|
|
11
|
+
### English
|
|
12
|
+
|
|
13
|
+
On dsh 0.2.2-rc and later the sidebar turned into a black block and stopped responding when opened on a phone. Since 0.2.2-rc dsh ships its own drawer below 768px: the frame carries `data-sidebar-drawer` and renders a mask at `z-index:70`, while tether's injected narrow-screen drawer pinned the sidebar to `z-index:30`, so the native mask stacked on top — in dark mode that mask is 50% black, which is exactly the black block, and its `pointer-events:auto` swallowed every tap. The hand-rolled drawer now only applies when dsh has no native drawer (the frame lacks `data-sidebar-drawer`) and steps aside when the native drawer is present, leaving the slide-in/out, the mask and tap-outside-to-close to dsh itself; the "Connect a phone / My computers" button still mounts as usual. Behaviour on older dsh versions is unchanged.
|
|
14
|
+
|
|
15
|
+
## 0.1.25
|
|
16
|
+
|
|
17
|
+
### 修复
|
|
18
|
+
|
|
19
|
+
- **窄屏下侧栏卡在 56px:标签文字渲染出来却被裁掉,侧栏开关也找不到**([#22](https://github.com/zexadev/dsh-tether/issues/22))。0.1.24 的窄屏样式用「主框架下有没有拖拽把手」判断侧栏是否展开,这个判据要靠 `:has()`。有的厂商 WebView 会把带 `:has()` 的规则整条丢掉(报告机型:华为 Mate 50 / HarmonyOS 4.2),于是「展开时侧栏 300px」那条不生效,而 dsh 自己处在展开态——标签照常渲染,被 56px 的盒子裁成每行一两个字;开关只在收起态渲染,页面上也找不到,界面就卡在这个混态。现在改读 dsh 写在主框架上的 `data-sidebar-collapsed`(收起为 `"true"`,展开时属性不存在),不再用 `:has()`;插件声明支持的五段 dsh 范围各取一版查过,这个属性每版都有。拖拽把手那条规则一并删掉,手机上它本来也拖不动。实测 480×1056:收起侧栏 57px、无遮罩、「主机」标签隐藏;按开关 301px、遮罩出现、标签显示;点内容区收回。
|
|
20
|
+
- **dsh 桌面端里「我的电脑」按钮点了没有任何反应**([#23](https://github.com/zexadev/dsh-tether/issues/23))。按钮的文案和行为原本按 `window.parent !== window` 判断「是不是在 tether 手机 App 里」,而 dsh 桌面端的插件界面同样跑在宿主的内嵌 frame 里,于是桌面端被当成手机:按钮显示「我的电脑」,点击只向父窗口发一条 `dsh-tether:open-hosts` 消息——桌面宿主没有这条消息的监听者,按钮因此是死的,也没有别的出路。现在改成 App 建窗口时往所有 frame 注入 `window.__DSH_TETHER_APP__`,谁是 App 谁挂标记;拿不到标记一律按浏览器处理,当场出示配对串。桌面端因此也能拿到配对串(换手机、加设备时用),不必再手动开浏览器访问本机地址。这条要配新版 App 才完整:老 App 没有这个标记,装上新插件后按钮会显示「连接手机」并出示配对串。
|
|
21
|
+
- **窄屏下附件缩略图被挤变形**(修 #22 时发现)。窄屏那几条规则用 `[class*="_frame"]` 选主框架,但 dsh 的会话区、附件缩略图、子代理、文档预览的局部类名也带 `_frame`,而且都长在主框架里面。附件缩略图自己是个单格网格,被 `grid-template-columns: 56px minmax(0,1fr) 0px` 改成三列之后,图片挤进 56px 的第一列;侧栏遮罩那条同理会糊在每个嵌套元素上。这几条现在限定成「带 `_frame` 且不在任何 `_frame` 里面」。
|
|
22
|
+
|
|
23
|
+
### 变更
|
|
24
|
+
|
|
25
|
+
- 声明支持 dsh `0.2.1-rc.5` 与 `0.2.2-alpha.2`。dsh 从 0.1.7 起会拒装声明范围没覆盖自己的插件,所以 0.2.2-alpha 线上原本装不上。两个版本都实跑验证过:布局包的类名与 `data-sidebar-collapsed` 和 0.2.1-rc.3 逐字相同,抽屉、设置弹窗、侧栏按钮行为一致。APK 内置的 dsh 仍是 `0.2.1-rc.2`,本次不动。
|
|
26
|
+
|
|
27
|
+
### English
|
|
28
|
+
|
|
29
|
+
On a narrow screen the sidebar could get stuck at 56px with its labels rendered but clipped and no sidebar toggle in sight ([#22](https://github.com/zexadev/dsh-tether/issues/22)). The narrow-screen styling in 0.1.24 decided whether the sidebar was expanded by looking for the drag handle under the frame, which needs `:has()`. Some vendor WebViews drop every rule containing `:has()` (reported on a Huawei Mate 50 running HarmonyOS 4.2), so the "sidebar is 300px when expanded" rule never applied while dsh itself was in the expanded state: the labels rendered and were clipped to one or two characters by the 56px box, and the toggle is only rendered while collapsed, so there was no way out of that mixed state. The expanded state is now read from `data-sidebar-collapsed`, which dsh writes on the frame itself (`"true"` when collapsed, absent when expanded), with no `:has()` involved; the attribute was checked in one version from each of the five dsh ranges the plugin declares. The drag-handle rule is gone as well — it was never draggable on a phone. Measured at 480×1056: collapsed sidebar 57px, no veil, hosts label hidden; after the toggle 301px with the veil and the label; tapping the content column collapses it again.
|
|
30
|
+
|
|
31
|
+
In the dsh desktop app the "My computers" button did nothing when clicked ([#23](https://github.com/zexadev/dsh-tether/issues/23)). The label and the behaviour were chosen by `window.parent !== window`, meant to detect "running inside the tether phone app", but the plugin UI in the dsh desktop shell also lives in an embedded frame, so the desktop was treated as a phone: the button read "My computers" and a click only posted `dsh-tether:open-hosts` to the parent window — which the desktop shell does not listen for, leaving the button dead with nothing else to fall back on. The app now injects `window.__DSH_TETHER_APP__` into every frame when it creates the window, so whoever is the app says so; without that marker the plugin treats the page as a browser and shows the pairing string. Desktop users can therefore get the pairing string (for a new phone or an extra device) without opening a browser by hand. This one needs the new app build to be complete: an older app does not set the marker, so with the new plugin its button reads "Connect a phone" and shows the pairing string.
|
|
32
|
+
|
|
33
|
+
Attachment thumbnails were squeezed out of shape on narrow screens (found while fixing #22). Those narrow-screen rules selected the frame with `[class*="_frame"]`, but the conversation area, attachment thumbnails, subagent and document-preview components all carry local class names containing `_frame` and all live inside the frame. An attachment thumbnail is a single-cell grid, so `grid-template-columns: 56px minmax(0,1fr) 0px` turned it into three columns and pushed the image into the 56px first column, and the sidebar veil would likewise be painted over every nested element. Those rules are now restricted to a `_frame` that is not inside another `_frame`.
|
|
34
|
+
|
|
35
|
+
Support is now declared for dsh `0.2.1-rc.5` and `0.2.2-alpha.2`. Since 0.1.7 dsh refuses to install a plugin whose declared range does not cover it, so the 0.2.2-alpha line could not install this plugin at all. Both versions were verified by running them: the layout package class names and `data-sidebar-collapsed` are identical to 0.2.1-rc.3, and the drawer, settings dialog and sidebar button all behave the same. The dsh bundled inside the APK stays at `0.2.1-rc.2`.
|
|
36
|
+
|
|
5
37
|
## 0.1.24
|
|
6
38
|
|
|
7
39
|
### 修复
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center"><sub>An independent community project. Not affiliated with, partnered with, authorised by, or endorsed by DeepSeek.<br>No DeepSeek employee or upstream DeepSeek Harness team member is involved in this repository.<br><a href="README.zh.md">中文</a> · English</sub></p>
|
|
10
10
|
|
|
11
|
+
<p align="center"><sub>Official repository: <a href="https://github.com/zexadev/dsh-tether">github.com/zexadev/dsh-tether</a> — releases and issues live here; forks are not maintained by this project.<br>手机远程使用电脑上的 DeepSeek Harness(dsh):跨网点对点直连、不用架服务器;Android 还能把 dsh 跑在手机本地。</sub></p>
|
|
12
|
+
|
|
11
13
|
<p align="center">
|
|
12
14
|
<img src="assets/banner.jpg" alt="Phone tethered directly to a dev machine" width="100%">
|
|
13
15
|
</p>
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
<a href="../../releases"><img src="https://img.shields.io/github/downloads/zexadev/dsh-tether/total?style=flat&label=downloads&color=4D6BFE" alt="Downloads"></a>
|
|
18
20
|
<a href="../../stargazers"><img src="https://img.shields.io/github/stars/zexadev/dsh-tether?style=flat&label=%E2%98%85&color=08C" alt="Stars"></a>
|
|
19
21
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-2EA44F?style=flat" alt="MIT"></a>
|
|
20
|
-
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1">
|
|
22
|
+
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1%20%7C%200.2.2-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1 | 0.2.2">
|
|
21
23
|
<img src="https://img.shields.io/badge/Android-4493F8?style=flat" alt="Android">
|
|
22
24
|
<img src="https://img.shields.io/badge/iOS-beta-8E8E93?style=flat" alt="iOS beta">
|
|
23
25
|
<a href="https://www.dsh.so/artifact/dsh-tether"><img src="https://www.dsh.so/badge/dsh-tether.svg" alt="dsh.so security scan"></a>
|
|
@@ -137,7 +139,7 @@ The Termux route still works if you prefer it: install dsh and this plugin insid
|
|
|
137
139
|
- The interface on the phone is DSH's own; the narrow-screen fit comes from minimal injected styles, so a dsh layout change may need a follow-up here.
|
|
138
140
|
- English and Chinese: the app follows your phone's system language, terminal output follows the computer's, and the part injected into the dsh interface follows dsh's own language setting. There is no separate language switch.
|
|
139
141
|
- Local mode exists only in the `arm64` build, never on iOS; the phone lacks bash and a full coreutils, so shell-dependent tools are incomplete. The `arm64` APK is about 83 MB because of the bundled runtime; the others are about 32 MB.
|
|
140
|
-
- Verified against dsh **`0.1.0-rc.7`**, **`0.1.0-rc.8`**, **`0.1.2`** (alpha and rc.1), **`0.1.5-alpha.1`**, **`0.1.5-rc.2`**, **`0.1.6-alpha.2`**, **`0.1.7-rc.2`**, **`0.2.0-rc.2`**, **`0.2.1-alpha.1`**, **`0.2.1-alpha.2
|
|
142
|
+
- Verified against dsh **`0.1.0-rc.7`**, **`0.1.0-rc.8`**, **`0.1.2`** (alpha and rc.1), **`0.1.5-alpha.1`**, **`0.1.5-rc.2`**, **`0.1.6-alpha.2`**, **`0.1.7-rc.2`**, **`0.2.0-rc.2`**, **`0.2.1-alpha.1`**, **`0.2.1-alpha.2`**, **`0.2.1-rc.2`**, **`0.2.1-rc.3`**, **`0.2.1-rc.5`** and **`0.2.2-alpha.2`**. dsh is in developer preview — check this line before assuming a newer dsh works. From 0.1.7 dsh refuses to install a plugin whose declared range does not cover it, so a version outside this line is a refusal at install time rather than a silent risk. The browser authentication dsh introduced in 0.1.2-alpha is handled entirely on the machine side; the phone app needs no update for it.
|
|
141
143
|
|
|
142
144
|
## What has been verified
|
|
143
145
|
|
|
@@ -147,6 +149,48 @@ Pairing (including a wrong code being refused), approval delivery, and switching
|
|
|
147
149
|
|
|
148
150
|
Local mode was verified on a Redmi running Android 16: a fresh install reaches a working interface in 9.1 seconds (runtime unpacking included) and later starts take 2.1 seconds; the node process survives 30 seconds in the background; force-stopping the app and reopening it restores local mode automatically.
|
|
149
151
|
|
|
152
|
+
## FAQ
|
|
153
|
+
|
|
154
|
+
### Do I need a public IP, a server of my own, or the same Wi-Fi?
|
|
155
|
+
|
|
156
|
+
No. The phone and the machine hole-punch to each other through iroh; there is no account and nothing to deploy. Only a failed punch falls back to a relay, and that relay carries ciphertext it cannot read. If your phone only ever sits on the same Wi-Fi as your machine, a plain LAN setup is simpler and you do not need this.
|
|
157
|
+
|
|
158
|
+
### Does it work with the phone on 4G/5G and the machine on home broadband?
|
|
159
|
+
|
|
160
|
+
Yes, and that is the path that has been verified on a real device: Wi-Fi off, 5G only, machine on home broadband, the plugin reported a direct public path and the relay was never used (see [What has been verified](#what-has-been-verified)). The relay fallback itself has not yet been triggered on a real network.
|
|
161
|
+
|
|
162
|
+
### Is there an iOS app?
|
|
163
|
+
|
|
164
|
+
A beta. Every release ships an unsigned IPA; sign it yourself with AltStore, Sideloadly or similar (a free Apple ID expires after 7 days). It is built only in CI and this project has never run it on a real device. Local mode is Android-only, because iOS forbids child processes.
|
|
165
|
+
|
|
166
|
+
### Can I use it with no computer at all?
|
|
167
|
+
|
|
168
|
+
On Android, yes. The `arm64` APK bundles Node 24 and dsh `0.2.1-rc.2`; **Hosts → Run on this phone** starts DSH on the phone itself, with no Termux, no commands and no root. That bundle is why the `arm64` APK is about 83 MB. The phone has no bash and no full coreutils, so shell-dependent tools are incomplete; chat and file read/write are unaffected.
|
|
169
|
+
|
|
170
|
+
### Can I open it as a web page (H5) or a WeChat mini program instead of installing the app?
|
|
171
|
+
|
|
172
|
+
No. The app owns an iroh endpoint (QUIC with a custom ALPN) and feeds the DSH interface into its WebView through a loopback proxy; a browser page cannot make that connection, and a mini program's `<web-view>` only loads whitelisted business domains. Doing it from a browser would need a server in the middle, which is exactly what this project avoids ([#11](https://github.com/zexadev/dsh-tether/issues/11)).
|
|
173
|
+
|
|
174
|
+
### Which dsh versions are supported?
|
|
175
|
+
|
|
176
|
+
The list under [Known limitations](#known-limitations), currently `0.1.0-rc.7` through `0.2.2-alpha.2`. From dsh 0.1.7 the install is refused outright when the declared range does not cover your dsh, so an unsupported version fails at install time instead of silently misbehaving.
|
|
177
|
+
|
|
178
|
+
### The interface sits at "Loading workspace…", or "Select workspace" reports `AbortSignal.any is not a function`
|
|
179
|
+
|
|
180
|
+
Fixed in 0.1.24. Older system WebViews (Huawei and others still ship Chromium 114) lack `Promise.withResolvers` and `AbortSignal.any`, which the dsh 0.2.1-rc frontend uses; the app now supplies both when the WebView lacks them ([#20](https://github.com/zexadev/dsh-tether/issues/20)).
|
|
181
|
+
|
|
182
|
+
### After sitting idle the app is stuck on "Reconnecting"
|
|
183
|
+
|
|
184
|
+
Fixed in 0.1.24 ([#19](https://github.com/zexadev/dsh-tether/issues/19)): a dead link was never reported and the phone-side proxy port was not released. Upgrade to 0.1.24 or later.
|
|
185
|
+
|
|
186
|
+
### How is this different from ds-harness-remote or DSH Desktop?
|
|
187
|
+
|
|
188
|
+
See the table under [The case it solves](#compared-with-similar-projects). In short: no account and no server here, direct first with a ciphertext-only relay as fallback, Android local mode, MIT; ds-harness-remote signs in to its service and offers PC / Android / Web; DSH Desktop expects the same Wi-Fi or a Tailscale network you set up.
|
|
189
|
+
|
|
190
|
+
### Is this an official DeepSeek project?
|
|
191
|
+
|
|
192
|
+
No. It is an independent community project; see [Relationship to DeepSeek Harness](#relationship-to-deepseek-harness).
|
|
193
|
+
|
|
150
194
|
## Relationship to DeepSeek Harness
|
|
151
195
|
|
|
152
196
|
<p align="center"><img src="assets/dsh-mark.svg" height="36" alt="DeepSeek Harness"></p>
|
package/README.zh.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center"><sub>独立的社区开源项目,与深度求索不存在隶属、合作、授权或背书关系。<br>本仓库无深度求索员工或 DeepSeek Harness 上游官方团队成员参与。<br>中文 · <a href="README.md">English</a></sub></p>
|
|
10
10
|
|
|
11
|
+
<p align="center"><sub>官方仓库:<a href="https://github.com/zexadev/dsh-tether">github.com/zexadev/dsh-tether</a>,发版与 issue 都在这里;各 fork 不由本仓维护。<br>DeepSeek Harness (dsh) remote access from your phone: Android / iOS client, peer-to-peer across networks, no server to set up; Android can also run dsh locally.</sub></p>
|
|
12
|
+
|
|
11
13
|
<p align="center">
|
|
12
14
|
<img src="assets/banner.jpg" alt="手机与开发机直接相连" width="100%">
|
|
13
15
|
</p>
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
<a href="../../releases"><img src="https://img.shields.io/github/downloads/zexadev/dsh-tether/total?style=flat&label=downloads&color=4D6BFE" alt="下载量"></a>
|
|
18
20
|
<a href="../../stargazers"><img src="https://img.shields.io/github/stars/zexadev/dsh-tether?style=flat&label=%E2%98%85&color=08C" alt="Stars"></a>
|
|
19
21
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-2EA44F?style=flat" alt="MIT"></a>
|
|
20
|
-
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1">
|
|
22
|
+
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1%20%7C%200.2.2-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1 | 0.2.2">
|
|
21
23
|
<img src="https://img.shields.io/badge/Android-4493F8?style=flat" alt="Android">
|
|
22
24
|
<img src="https://img.shields.io/badge/iOS-%E6%B5%8B%E8%AF%95%E7%89%88-8E8E93?style=flat" alt="iOS 测试版">
|
|
23
25
|
<a href="https://www.dsh.so/artifact/dsh-tether"><img src="https://www.dsh.so/badge/dsh-tether.svg" alt="dsh.so 安全扫描"></a>
|
|
@@ -137,7 +139,7 @@ dsh plugin --profile web add .
|
|
|
137
139
|
- 手机上的界面是 DSH 自己的,窄屏适配靠注入的最小样式完成;dsh 改版式时可能需要跟进。
|
|
138
140
|
- 中英双语:App 界面跟随手机的系统语言;电脑终端上的输出跟随电脑的系统语言;注入 dsh 界面的那部分跟随 dsh 自己的语言设置。没有单独的语言开关。
|
|
139
141
|
- 本地模式只有 `arm64` 包有,iOS 没有;手机上没有 bash 与完整 coreutils,依赖 shell 的工具用不全。arm64 包因内置运行时约 83 MB,其余包约 32 MB。
|
|
140
|
-
- 已针对 dsh **`0.1.0-rc.7`**、**`0.1.0-rc.8`**、**`0.1.2`**(alpha 与 rc.1)、**`0.1.5-alpha.1`**、**`0.1.5-rc.2`**、**`0.1.6-alpha.2`**、**`0.1.7-rc.2`**、**`0.2.0-rc.2`**、**`0.2.1-alpha.1`**、**`0.2.1-alpha.2`** 与 **`0.2.
|
|
142
|
+
- 已针对 dsh **`0.1.0-rc.7`**、**`0.1.0-rc.8`**、**`0.1.2`**(alpha 与 rc.1)、**`0.1.5-alpha.1`**、**`0.1.5-rc.2`**、**`0.1.6-alpha.2`**、**`0.1.7-rc.2`**、**`0.2.0-rc.2`**、**`0.2.1-alpha.1`**、**`0.2.1-alpha.2`**、**`0.2.1-rc.2`**、**`0.2.1-rc.3`**、**`0.2.1-rc.5`** 与 **`0.2.2-alpha.2`** 验证。dsh 处于 developer preview,换更新的 dsh 之前先看这一行。0.1.7 起 dsh 会拒绝安装声明范围没覆盖自己的插件,所以这一行之外的版本是装不上,而不是装上了埋雷。dsh 0.1.2-alpha 引入的浏览器认证完全在电脑侧处理,手机 App 无需更新。
|
|
141
143
|
|
|
142
144
|
## 验证过什么
|
|
143
145
|
|
|
@@ -154,6 +156,48 @@ dsh plugin --profile web add .
|
|
|
154
156
|
|
|
155
157
|
本地模式在 Redmi(Android 16)真机验证:全新安装后首次启动到界面出现 9.1 秒(含解压运行时),之后启动 2.1 秒;退后台 30 秒 node 进程仍在;强杀 App 后重开自动恢复到本地模式。
|
|
156
158
|
|
|
159
|
+
## 常见问题
|
|
160
|
+
|
|
161
|
+
### 需要公网 IP、自己的服务器,或者同一个 WiFi 吗?
|
|
162
|
+
|
|
163
|
+
都不需要。手机和电脑通过 iroh 互相打洞直连,不注册账号、不部署任何东西;打不通才退回 relay,relay 上过的只是它读不懂的密文。如果你的手机永远和电脑在同一个 WiFi 里,直接走局域网更简单,用不上这个。
|
|
164
|
+
|
|
165
|
+
### 手机走 4G/5G、电脑在家宽,能连上吗?
|
|
166
|
+
|
|
167
|
+
能,而且这正是真机验证过的那条路:手机关 WiFi 只走 5G、电脑在家宽,插件侧报出公网直连路径,relay 全程没用上(见[验证过什么](#验证过什么))。打洞失败退回 relay 这条路径本身,尚未在真实网络下触发过。
|
|
168
|
+
|
|
169
|
+
### 有 iOS 版吗?
|
|
170
|
+
|
|
171
|
+
有,测试版。每个 Release 都带未签名的 ipa,用 AltStore、Sideloadly 一类工具自签安装(免费 Apple ID 签出来的 7 天过期)。它只在 CI 上构建,本项目没有在真机上跑过。本地模式只有 Android 有,iOS 禁止子进程。
|
|
172
|
+
|
|
173
|
+
### 手边完全没有电脑,能用吗?
|
|
174
|
+
|
|
175
|
+
Android 可以。`arm64` 包内置了 Node 24 和 dsh `0.2.1-rc.2`,**主机 → 在本机运行**就能在手机上把 DSH 跑起来,不装 Termux、不敲命令、不用 root;arm64 包约 83 MB 就是因为这个。手机上没有 bash 和完整 coreutils,依赖 shell 的工具用不全,对话和读写文件不受影响。
|
|
176
|
+
|
|
177
|
+
### 能不装 App,用手机网页(H5)或微信小程序访问吗?
|
|
178
|
+
|
|
179
|
+
不能。App 自己带一个 iroh 端点(自定义 ALPN 的 QUIC),再经手机本地的回环代理把 DSH 界面灌进 WebView;浏览器页面建不起这种连接,小程序的 `<web-view>` 也只能加载白名单里的业务域名。要在浏览器里用,中间必须常驻一台服务器,而这正是本项目要避开的([#11](https://github.com/zexadev/dsh-tether/issues/11))。
|
|
180
|
+
|
|
181
|
+
### 支持哪些 dsh 版本?
|
|
182
|
+
|
|
183
|
+
见[已知限制](#已知限制)里那一行,目前是 `0.1.0-rc.7` 到 `0.2.2-alpha.2`。dsh 0.1.7 起,声明范围没覆盖到的 dsh 会在安装时直接拒绝,所以不支持的版本是装不上,而不是装上了乱跑。
|
|
184
|
+
|
|
185
|
+
### 界面停在「正在加载工作区…」,或点「选择工作区」报 `AbortSignal.any is not a function`
|
|
186
|
+
|
|
187
|
+
0.1.24 已修。华为等厂商随系统发的旧 WebView 还停在 Chromium 114,没有 dsh 0.2.1-rc 前端用到的 `Promise.withResolvers` 和 `AbortSignal.any`;现在 App 在 WebView 缺这两个 API 时自行补上([#20](https://github.com/zexadev/dsh-tether/issues/20))。
|
|
188
|
+
|
|
189
|
+
### 放一段时间后卡在「重新连接中」不恢复
|
|
190
|
+
|
|
191
|
+
0.1.24 已修([#19](https://github.com/zexadev/dsh-tether/issues/19)):断链没有上报、手机侧代理端口没有释放。升级到 0.1.24 及以上。
|
|
192
|
+
|
|
193
|
+
### 和 ds-harness-remote、DSH Desktop 有什么区别?
|
|
194
|
+
|
|
195
|
+
见[它解决什么](#与同类项目的区别)下面的表。一句话:这里不注册、不架服务器,直连优先、退回时 relay 只见密文,Android 有本地模式,MIT;ds-harness-remote 要登录它的服务,提供 PC / Android / Web;DSH Desktop 要同一个 WiFi 或你自己搭的 Tailscale 网络。
|
|
196
|
+
|
|
197
|
+
### 这是 DeepSeek 官方项目吗?
|
|
198
|
+
|
|
199
|
+
不是,是独立的社区项目,见[与 DeepSeek Harness 的关系](#与-deepseek-harness-的关系)。
|
|
200
|
+
|
|
157
201
|
## 与 DeepSeek Harness 的关系
|
|
158
202
|
|
|
159
203
|
<p align="center"><img src="assets/dsh-mark.svg" height="36" alt="DeepSeek Harness"></p>
|
package/index.js
CHANGED
|
@@ -490,20 +490,28 @@ const tetherCss = `
|
|
|
490
490
|
}
|
|
491
491
|
/* 侧栏展开时主框架把网格从「56px 内容」改成「280px 内容」,对话区被压到
|
|
492
492
|
140px。改成抽屉:网格保持收起时的列宽,侧栏浮到内容之上盖住一部分。
|
|
493
|
-
|
|
494
|
-
|
|
493
|
+
展开态读 dsh 自己写在 frame 上的 data-sidebar-collapsed(收起为 "true",
|
|
494
|
+
展开时该属性不在),0.1.0-rc.7 起每个版本都带。把手本身在手机上没用
|
|
495
|
+
(没法拖),隐掉。 */
|
|
495
496
|
/* 网格恒定为收起时的列宽,侧栏恒定绝对定位、只让宽度随展开态变化——
|
|
496
497
|
position 不可过渡,只有始终 absolute 才能做出滑入滑出的动画。
|
|
497
498
|
侧栏脱离文档流后剩下的子元素会整体上移一列(对话区掉进 56px 那列,
|
|
498
499
|
反而更窄),所以显式钉住列号。 */
|
|
499
|
-
|
|
500
|
+
/* 只认最外层那个 frame。dsh 里好几个组件的局部类名也带 _frame(会话区、
|
|
501
|
+
附件缩略图、子代理、文档预览),它们都长在它里面:附件缩略图自己是个
|
|
502
|
+
单格网格,被这条列模板改掉后图片会挤进 56px 的第一列。 */
|
|
503
|
+
/* dsh 0.2.2-rc 起在 <768px 自带抽屉:frame 挂 data-sidebar-drawer,自带
|
|
504
|
+
遮罩(z-index 70)与滑入动画。这段手工抽屉只服务没有原生抽屉的旧版本,
|
|
505
|
+
原生抽屉出现时整体让位——否则我们的 z-index:30 会落到原生遮罩(70)下面,
|
|
506
|
+
侧栏被遮罩压成一块黑、点也点不到。 */
|
|
507
|
+
[class*="_frame"]:not([class*="_frame"] *):not([data-sidebar-drawer]) {
|
|
500
508
|
grid-template-columns: 56px minmax(0, 1fr) 0px !important;
|
|
501
509
|
}
|
|
502
|
-
[class*="_frame"] > [class*="centerCol"] { grid-column: 2 !important; }
|
|
510
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="centerCol"] { grid-column: 2 !important; }
|
|
503
511
|
/* 右列 dsh 0.1.5 起从 detailsCol 改名 rightbarCol,两个名字都钉住 */
|
|
504
|
-
[class*="_frame"] > [class*="detailsCol"],
|
|
505
|
-
[class*="_frame"] > [class*="rightbarCol"] { grid-column: 3 !important; }
|
|
506
|
-
[class*="_frame"] > [class*="sidebarCol"] {
|
|
512
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="detailsCol"],
|
|
513
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="rightbarCol"] { grid-column: 3 !important; }
|
|
514
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="sidebarCol"] {
|
|
507
515
|
position: absolute !important;
|
|
508
516
|
top: 0;
|
|
509
517
|
bottom: 0;
|
|
@@ -512,14 +520,13 @@ const tetherCss = `
|
|
|
512
520
|
z-index: 30 !important;
|
|
513
521
|
transition: width 220ms ease, box-shadow 220ms ease;
|
|
514
522
|
}
|
|
515
|
-
|
|
516
|
-
[class*="_frame"]:has(> [class*="_handle"]) > [class*="sidebarCol"] {
|
|
523
|
+
[class*="_frame"]:not([data-sidebar-drawer]):not([data-sidebar-collapsed="true"]) > [class*="sidebarCol"] {
|
|
517
524
|
width: min(300px, 84vw) !important;
|
|
518
525
|
box-shadow: 0 10px 36px rgba(0, 0, 0, 0.24);
|
|
519
526
|
}
|
|
520
527
|
/* 抽屉下的内容压暗。指针不拦——收起由注入的脚本接管,拦了反而会在脚本
|
|
521
528
|
未生效时把整页点死。 */
|
|
522
|
-
[class*="_frame"]::after {
|
|
529
|
+
[class*="_frame"]:not([class*="_frame"] *):not([data-sidebar-drawer])::after {
|
|
523
530
|
content: '';
|
|
524
531
|
position: absolute;
|
|
525
532
|
inset: 0;
|
|
@@ -529,15 +536,15 @@ const tetherCss = `
|
|
|
529
536
|
pointer-events: none;
|
|
530
537
|
transition: opacity 220ms ease;
|
|
531
538
|
}
|
|
532
|
-
[class*="_frame"]:
|
|
533
|
-
[class*="_frame"] > [class*="_handle"] { display: none !important; }
|
|
539
|
+
[class*="_frame"]:not([class*="_frame"] *):not([data-sidebar-drawer]):not([data-sidebar-collapsed="true"])::after { opacity: 1; }
|
|
540
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="_handle"] { display: none !important; }
|
|
534
541
|
|
|
535
542
|
/* 侧栏收起时是图标条,「主机」按钮的文字跟着藏起来,和 dsh 自己的按钮一致 */
|
|
536
543
|
[data-dsh-tether="hosts-label"] { display: none; }
|
|
537
|
-
[class*="_frame"]:
|
|
544
|
+
[class*="_frame"]:not([data-sidebar-collapsed="true"]) [data-dsh-tether="hosts-label"] { display: inline; }
|
|
538
545
|
@media (prefers-reduced-motion: reduce) {
|
|
539
|
-
[class*="_frame"] > [class*="sidebarCol"],
|
|
540
|
-
[class*="_frame"]::after { transition: none; }
|
|
546
|
+
[class*="_frame"]:not([data-sidebar-drawer]) > [class*="sidebarCol"],
|
|
547
|
+
[class*="_frame"]:not([data-sidebar-drawer])::after { transition: none; }
|
|
541
548
|
}
|
|
542
549
|
|
|
543
550
|
/* 触摸时安卓 WebView 会按元素的 DOM 盒子刷一块蓝色高亮,方角、盖住圆角,
|
|
@@ -570,7 +577,10 @@ const tetherScript = `
|
|
|
570
577
|
document.addEventListener('pointerdown', function (event) {
|
|
571
578
|
if (!narrow.matches) return
|
|
572
579
|
var frame = document.querySelector('[class*="_frame"]')
|
|
573
|
-
|
|
580
|
+
// dsh 0.2.2-rc 起自带抽屉,遮罩自己会点外收起;这里只补没有原生抽屉的旧版
|
|
581
|
+
if (!frame || frame.hasAttribute('data-sidebar-drawer')) return
|
|
582
|
+
// 抽屉开着才有「点外面收起来」可言;判据和样式那边同一个
|
|
583
|
+
if (frame.getAttribute('data-sidebar-collapsed') === 'true') return
|
|
574
584
|
var drawer = frame.querySelector(':scope > [class*="sidebarCol"]')
|
|
575
585
|
if (!drawer || drawer.contains(event.target)) return
|
|
576
586
|
var toggle = document.querySelector('button[aria-label*="侧边栏"], button[aria-label*="sidebar" i]')
|
|
@@ -649,7 +659,13 @@ const tetherScript = `
|
|
|
649
659
|
// 这里放「主机」按钮,点了让外层的手机客户端打开它自己的主机页。
|
|
650
660
|
// 同一个按钮两种归宿:手机 App 里打开它自己的主机页;电脑浏览器里出示配对串,
|
|
651
661
|
// 好让用户换手机、加设备时有地方拿新配对码(否则只能去删 paired.json)。
|
|
652
|
-
|
|
662
|
+
// 判据是「App 自己挂的标记」,不是「我在不在 iframe 里」:dsh 桌面端的插件界面
|
|
663
|
+
// 同样跑在宿主的内嵌 frame 里,按 window.parent !== window 判会被当成手机 App,
|
|
664
|
+
// 按钮变「我的电脑」、点了只往父窗口发一条消息——桌面端没有这条消息的监听者,
|
|
665
|
+
// 按钮就是死的。标记拿不到一律按浏览器处理,出示配对串,至少点得动;
|
|
666
|
+
// 0.1.24 及更早的 App 没有这个标记,在 App 里也会出示配对串(它自己的顶栏有
|
|
667
|
+
// 「主机」入口,功能不丢)。
|
|
668
|
+
var inApp = !!window.__DSH_TETHER_APP__
|
|
653
669
|
|
|
654
670
|
// 已配对手机列表。白名单每条都持有访问权,而手机重装 App 会换身份密钥,
|
|
655
671
|
// 旧条目就此变成没人认识的常驻凭证——这里是撤销它们的地方。
|
|
@@ -803,7 +819,7 @@ const tetherScript = `
|
|
|
803
819
|
var button = document.createElement('button')
|
|
804
820
|
button.type = 'button'
|
|
805
821
|
button.setAttribute('data-dsh-tether', 'hosts-trigger')
|
|
806
|
-
var label =
|
|
822
|
+
var label = inApp ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
|
|
807
823
|
button.setAttribute('aria-label', label)
|
|
808
824
|
// 浏览器侧沿用原来那个 18px 图标,一字不改;抄不到「设置」时按参照行的尺寸画
|
|
809
825
|
var icon = reference === null || reference.browser
|
|
@@ -811,7 +827,7 @@ const tetherScript = `
|
|
|
811
827
|
: phoneIcon(referenceIconWidth(reference.trigger))
|
|
812
828
|
button.innerHTML = icon + '<span data-dsh-tether="hosts-label">' + label + '</span>'
|
|
813
829
|
button.addEventListener('click', function () {
|
|
814
|
-
if (
|
|
830
|
+
if (inApp) window.parent.postMessage({ type: 'dsh-tether:open-hosts' }, '*')
|
|
815
831
|
else showPairing()
|
|
816
832
|
})
|
|
817
833
|
row.append(button)
|
|
@@ -853,7 +869,7 @@ const tetherScript = `
|
|
|
853
869
|
}
|
|
854
870
|
// dsh 的语言能在设置里当场切,这个标签跟着换
|
|
855
871
|
new MutationObserver(function () {
|
|
856
|
-
var next =
|
|
872
|
+
var next = inApp ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
|
|
857
873
|
button.setAttribute('aria-label', next)
|
|
858
874
|
var span = button.querySelector('[data-dsh-tether="hosts-label"]')
|
|
859
875
|
if (span) span.textContent = next
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-tether",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "Mobile client for the DeepSeek Harness — use the dsh on your dev machine from your Android or iOS phone, remotely across networks, peer-to-peer over iroh with no server to set up (falls back to a relay that only sees ciphertext)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -48,19 +48,19 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0",
|
|
52
|
-
"@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0"
|
|
51
|
+
"@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0 || >=0.2.2-alpha.2 <0.2.3-0",
|
|
52
|
+
"@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0 || >=0.2.2-alpha.2 <0.2.3-0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"@deepseek-ai/dsh-host-webserver": { "optional": true },
|
|
56
56
|
"@deepseek-ai/dsh-user-approval": { "optional": true }
|
|
57
57
|
},
|
|
58
58
|
"optionalDependencies": {
|
|
59
|
-
"dsh-tether-host-android-arm64": "0.1.
|
|
60
|
-
"dsh-tether-host-darwin-arm64": "0.1.
|
|
61
|
-
"dsh-tether-host-darwin-x64": "0.1.
|
|
62
|
-
"dsh-tether-host-linux-arm64": "0.1.
|
|
63
|
-
"dsh-tether-host-linux-x64": "0.1.
|
|
64
|
-
"dsh-tether-host-win32-x64": "0.1.
|
|
59
|
+
"dsh-tether-host-android-arm64": "0.1.26",
|
|
60
|
+
"dsh-tether-host-darwin-arm64": "0.1.26",
|
|
61
|
+
"dsh-tether-host-darwin-x64": "0.1.26",
|
|
62
|
+
"dsh-tether-host-linux-arm64": "0.1.26",
|
|
63
|
+
"dsh-tether-host-linux-x64": "0.1.26",
|
|
64
|
+
"dsh-tether-host-win32-x64": "0.1.26"
|
|
65
65
|
}
|
|
66
66
|
}
|