Skip to content

Releases: kjx-talesofai/dsh-plugin-tailscale-remote

v0.2.5 — 把自己的残留拿回来

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 17 Oct 20:47

不用再"先关插件再退出 DSH"了。

DSH 每次启动的 Web 端口都是新的(51361 → 53083 → …)。插件在 443 上建一条 serve 映射指向当前端口;强制退出时这条映射留在机器上,指向一个已经没人监听的端口。下次启动,新端口和它对不上,插件于是拒绝覆盖 —— 而那条映射其实是它自己上次留下的。

两条修正

1. 用"活着吗"区分残留和别人的映射

两者的形状一模一样,只有一处不同:目标端口还有没有人在听。

  • 活着 → 还是拒绝(不抢别人的)
  • 已经不在了 → 拿回来,日志记一行 taking back serve port 443: … is no longer listening

2. 优雅退出时主动拆掉

dispose 里 serve --https=<port> off,仅当那条映射的根 handler 仍指向我们自己的端口。强制杀进程跑不到这里,所以第 1 条仍然必要。

你现在的状态

我已经把那条 stale 的 443 映射单独拆掉了(serve --https=443 off,没动电台的 8443)。现在 tailscale serve status 只剩:

https://jiaxin-mbpm2.taila3698f.ts.net:8443 → 127.0.0.1:8840

装 0.2.5 重启之后,直接把开关打开即可。

v0.2.4 — 补上 DSH 版本约束

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 17 Oct 18:39

这是一个真问题,不是文档问题。

这个包原来没有向 harness 声明任何 DSH 版本要求:

  • 顶层 engines 只有 node
  • peer 只有 @deepseek-ai/cordis

harness 的规则是:顶层 engines.dsh 存在就只由它决定;不存在才去查 peerDependencies 里 @deepseek-ai/dsh* 的每条范围。两者都没有 = 没有约束。所以 DSH 0.3 出来时,这个包会照常装上、然后在运行时坏掉,而 README 一直写着"需要 ≥ 0.2.0-rc.1"。

(dsh.engines 那块在 bundle manifest 里,是另一个字段,兼容性检查不读它。)

现在:

"engines": { "node": ">=22", "dsh": ">=0.2.0-rc.1 <0.3.0-0" }

顺带把依赖面在 0.2.1-rc.2 上重核了:cordis 4.0.5、schemastery 3.18.5、客户端模块 0.2.1-rc.2,都还在接受范围内。手机走 tailnet 那一趟没有重跑,README 里现在这么写。

安装:github:kjx-talesofai/dsh-plugin-tailscale-remote#v0.2.4

v0.2.3 — a working installer, and a guard against the next broken path

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 10 Oct 16:14

Fixed

scripts/install.mjs crashed on the first line. It shipped in files and was documented in the README, but imported ../bundle/scripts/install.mjs — a file deleted when the package moved to the repository root — so anyone who ran the documented command hit a MODULE_NOT_FOUND instead.

It is now a thin, honest wrapper over the supported path:

node scripts/install.mjs [--profile desktop] [--spec <spec>] [--revert] [--dry-run]

It shells out to dsh plugin --profile <name> add|remove, which is the only thing that knows how a profile is laid out, and then verifies the dependency and the dsh.profile.bundles entry actually landed. Its default spec is derived from its own package.json (github:<owner>/<repo>#v<version>), so it can never install a tag other than the one it shipped in. Hand-editing cordis.patch.yml is gone: the plugin manager already reconciles it, and the connection.trustedHosts override the old script wrote was taken over by the plugin's own runtime registration.

Added

  • tests/artifacts.test.mjs — a path/reference drift guard. It asserts that every path this package names exists: each literal files entry, each literal exports target, and every relative import / new URL(…, import.meta.url) under src/, scripts/ and tests/. Reintroducing the original bad import makes it fail with the exact dangling specifier, so this class of bug cannot ship again.
  • CLIENT_BUNDLE / HOST_BUNDLE env overrides on the client and host suites, so they can run against an installed copy rather than the working tree — the only way to prove what a user actually has on disk behaves as tested.
  • A regression assertion pinning the exact sentence from the report "the panel is English but the error message is Chinese", in both directions.

Note on the Chinese error text

That screenshot was a half-updated plugin, and it is worth knowing why: the browser half is read from disk per request, so a page refresh picks up a new client build without restarting DSH — but the host half loads once at boot. An old host sends no detailCode, so the new client has nothing to translate and falls back to the host's Chinese detail. Both halves now assert identically, but the host half still needs one restart.

Full Changelog: v0.2.2...v0.2.3

v0.2.2 — follows the DSH UI language

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 10 Oct 16:05

The plugin no longer speaks only Chinese. Its sidebar row, panel and help text now ride DeepSeek Harness's own locale service, so they follow Settings → General → Language and switch in place — no reload, no restart, no second language setting.

What changed

  • Browser half registers a zh/en dictionary and declares locale: tailscale-remote on both slot registrations, so the framework hands the panel its t seat and re-renders it on a locale switch. The sidebar label is re-resolved on the same subscription.
  • Host half failures now travel as a language-neutral RemoteError(code, params, message): the payload carries detailCode/detailParams for the panel to translate, while the Chinese detail stays for the log, the CLI and curl. A sessionOnly flag replaces the appended Chinese sentence.
  • Pairing pages are served before any client bundle exists, so they follow the phone browser's Accept-Language instead (English when it asks for neither).
  • Plugin list name/description localize through locale/en.json + locale/zh.json.
  • The 403 fence message no longer tells you to run a scripts/setup.sh that is not in this repo.

Tests

npm test — 11/11 preflight cases, the client suite (now rendering the panel in both languages and asserting no Chinese leaks into the English UI), and the host suite (route auth, fence registration, pairing, plus the new detailCode and Accept-Language assertions). A drift guard extracts every new RemoteError('…') code from the host source and requires a matching translation.

Full Changelog: v0.2.1...v0.2.2

v0.2.1 — host-backed settings on the remote page

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 05 Oct 16:26

Settings changed from the phone now persist.

DSH resolves a page's settings persistence as isLoopback ? "host" : "memory", where a page counts as loopback only when it is served from a loopback hostname or its transport declares ownsHost. A remote page satisfied neither, so config forms opened from the phone appeared to apply and silently did not persist.

This release injects a one-line transport bootstrap as the first child of <head> (via the webserver's tapIndex extension point, before __DSH_BOOT__). It only declares ownership: the session gate and the Host/Origin fence are untouched, and a transport already provided by a shell is left alone.

Install: github:kjx-talesofai/dsh-plugin-tailscale-remote#v0.2.1

v0.2.0 — Tailscale remote access for the DSH desktop Web UI

Choose a tag to compare

@kjx-talesofai kjx-talesofai released this 05 Oct 15:48

One switch creates a tailnet-only HTTPS mapping for the DeepSeek Harness desktop Web UI, shows a pairing QR for first login, and revokes only its own tailscale serve mapping when switched off.

Highlights

  • Panel: sidebar status dot + a minimal panel — switch, one QR code, and the fallback link.
  • No profile edit needed: the plugin registers its own tailnet authority with the harness's /api trust fence at runtime.
  • Preflight: missing Tailscale state, MagicDNS or the HTTPS certificate are reported in plain language, instead of failing later on the phone.
  • Reversible: only the mapping it created is removed; a foreign tailscale serve entry is left untouched (and a port already serving something else is never stolen).
  • Tests (npm test): preflight decision table · client smoke on the built bundle · host smoke (stub cordis context + fake tailscale) covering routes, auth semantics and the fence registration.

Install

Fill either of these into DSH's Add plugin dialog — no registry publishing involved:

github:kjx-talesofai/dsh-plugin-tailscale-remote#v0.2.0
/absolute/path/to/dsh-plugin-tailscale-remote

Requires DSH Desktop ≥ 0.2.0-rc.1 and Tailscale on both devices with MagicDNS and HTTPS Certificates enabled.

Verified

macOS (Apple Silicon) · DSH Desktop 0.2.0-rc.2 · Tailscale 1.102.4 — end to end with a real phone on the same tailnet (scan → tap → in, and the bare domain afterwards). Windows and Linux are untested.

MIT © 2026 Jiaxin Kou · hypersampling.com