dsh-qr-share 0.1.0

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 ADDED
@@ -0,0 +1,54 @@
1
+ # Changelog
2
+
3
+ All notable changes to **dsh-qr-share** are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] — 2026-08-29
11
+
12
+ ### Added
13
+
14
+ - **Host half** (`lib/index.js`) — registers `/_qr/share` HTTP route via
15
+ `ctx.webServer.register`. The route is gated by `connection.requestRejection`
16
+ (same trust fence as `/api`): 403 if Host is not in `trustedHosts`,
17
+ 401 if the browser cookie is missing/expired, 200 only when both pass.
18
+ On success it returns `{ ready, token, tokenTtlMs }` so the client can
19
+ compose `${window.location.origin}/?token=${token}` without any
20
+ environment variable coupling.
21
+ - **Client half** (`lib/client.js` + `lib/client-registry.js`) — registers a
22
+ `sidebar.footer.action` slot occupant. The button stays mounted but
23
+ hidden until the host reports `ready: true`; clicking it opens a dialog
24
+ showing the QR code (`qrcode` lib) and the resolved URL as plain text
25
+ fallback.
26
+ - **Locale dictionaries** — `betterSidebar` namespace, `zh` and `en` translations
27
+ for button title and dialog copy.
28
+ - **Two client bundles** — official profile channel (`lib/client.js`,
29
+ id `dsh-qr-share`) and plugin-registry channel (`lib/client-registry.js`,
30
+ id `dsh-external/dsh-qr-share`); both ship the same source compiled with
31
+ only the registered id and output filename differing.
32
+ - **Type-only `Context` augmentations** — `declare module '@deepseek-ai/cordis'`
33
+ carries the minimal slices (`locale`, `slots`, `connection`, `webServer`)
34
+ we use; no runtime value import of cordis, the runtime instance is
35
+ supplied by the DSH core.
36
+ - **`cordis.patch.yml`** — single `insert` row that the official
37
+ `dsh plugin --profile <name> add dsh-qr-share@<version>` reconciles
38
+ into `dsh.profile.bundles` automatically.
39
+
40
+ ### Security
41
+
42
+ - The `/_qr/share` route only returns the launch token after passing the
43
+ same `connection.requestRejection` gate as `/api` — identical 401/403
44
+ status codes, identical minimal text body shape, no JSON leak about
45
+ which check tripped.
46
+ - The launch token is process-stable (one per `dsh web` activation) and
47
+ becomes useless after the first exchange (the cookie is bound to the
48
+ authority and HttpOnly + SameSite=Strict).
49
+ - The client uses `credentials: 'same-origin'` and `cache: 'no-store'`,
50
+ preventing cross-site frames from reaching the route and stopping
51
+ intermediary caches from storing the token.
52
+
53
+ [Unreleased]: https://github.com/xiaoguomeiyitian/dsh-qr-share/compare/v0.1.0...HEAD
54
+ [0.1.0]: https://github.com/xiaoguomeiyitian/dsh-qr-share/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xiaoguomeiyitian
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # dsh-qr-share
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ > DSH web plugin: a sidebar-footer QR-code button that lets a phone scan
6
+ > and re-issue the current browser's authenticated launch URL, reusing the
7
+ > same `?token=…` exchange the desktop just completed.
8
+
9
+ [![license](https://img.shields.io/github/license/xiaoguomeiyitian/dsh-qr-share)](LICENSE)
10
+ [![version](https://img.shields.io/github/v/release/xiaoguomeiyitian/dsh-qr-share)](https://github.com/xiaoguomeiyitian/dsh-qr-share/releases)
11
+ [![stars](https://img.shields.io/github/stars/xiaoguomeiyitian/dsh-qr-share?style=social)](https://github.com/xiaoguomeiyitian/dsh-qr-share/stargazers)
12
+ [![dsh.so](https://www.dsh.so/badge/dsh-qr-share.svg)](https://www.dsh.so/artifact/dsh-qr-share)
13
+ ![dsh](https://img.shields.io/badge/DSH-0.1.0--rc.8%20%7C%200.1.2--alpha.1-8257D0)
14
+
15
+ ## Screenshots
16
+
17
+ > Placeholder — add a screenshot of the sidebar footer button + dialog to
18
+ > `docs/screenshot.png` and uncomment the line below. Recommended size:
19
+ > 640×400, capturing the bottom of the sidebar and the QR dialog.
20
+
21
+ <!-- ![QR button + dialog](docs/screenshot.png) -->
22
+
23
+ ## What it does
24
+
25
+ 1. After login, a QR-code icon appears in the sidebar footer (next to
26
+ the Settings trigger).
27
+ 2. Click → a dialog opens showing a QR code and the resolved URL as
28
+ plain text (fallback for failed scans).
29
+ 3. Scan with a phone → the phone opens the URL → the server's
30
+ `BrowserAuth` exchanges the token for a cookie (303 → `/`) and the
31
+ phone lands on the same session.
32
+
33
+ The QR URL is composed from `window.location.origin` so it automatically
34
+ matches whatever authority the desktop actually used (LAN IP, public
35
+ domain, reverse-proxied sub-path). **No environment variable is needed
36
+ for the QR to point at the right host** — the `DSH_PUBLIC_HOST` already
37
+ in `web-external.patch.yml` still gates the route via the trust fence.
38
+
39
+ ## Security model
40
+
41
+ - The `/_qr/share` route is gated by `connection.requestRejection` —
42
+ identical to `/api`'s gate. 403 if Host isn't trusted; 401 if the
43
+ browser cookie isn't valid. The token is only returned on 200.
44
+ - The launch token is process-stable (one per `dsh web` activation) and
45
+ becomes useless after the first exchange (the cookie is bound to the
46
+ authority and HttpOnly + SameSite=Strict).
47
+ - `SameSite=Strict` + same-origin `fetch` mean the route is unreachable
48
+ from any cross-site frame.
49
+
50
+ ## Install
51
+
52
+ ### Via the DSH plugin CLI (recommended for production)
53
+
54
+ ```bash
55
+ dsh plugin --profile <name> add dsh-qr-share@latest
56
+ ```
57
+
58
+ This adds the bundle declaration to your profile and reuses the
59
+ `cordis.patch.yml` shipped in the package — no manual patch overlay
60
+ edits needed.
61
+
62
+ ### Via plain `npm install`
63
+
64
+ ```bash
65
+ # in a project that owns a DSH profile directory
66
+ npm install dsh-qr-share
67
+ # or with pnpm
68
+ pnpm add dsh-qr-share
69
+ ```
70
+
71
+ Then add the row from `cordis.patch.yml` (shipped with the package) to
72
+ your profile's patch overlay, and ensure the package is reachable from
73
+ the profile's `node_modules` (pnpm/npm flat layout works; pnpm strict
74
+ layout needs a symlink — see `manual` install below).
75
+
76
+ ### Manual install (e.g. inside the docker-build image)
77
+
78
+ 1. Place the package at `/app/dsh-qr-share/`.
79
+ 2. Symlink it into `$DSH_HOME/profiles/node_modules/dsh-qr-share` (the
80
+ entrypoint does this for you; see `docker-build/docker-scripts/entrypoint.sh`).
81
+ 3. Add the row from `cordis.patch.yml` to your patch overlay:
82
+
83
+ ```yaml
84
+ - insert:
85
+ - id: qr-share
86
+ name: 'dsh-qr-share'
87
+ ```
88
+
89
+ ## Configuration (patch overlay)
90
+
91
+ ```yaml
92
+ - insert:
93
+ - id: qr-share
94
+ name: 'dsh-qr-share'
95
+ config:
96
+ enabled: true # default true; set false to short-circuit the route to 404
97
+ ```
98
+
99
+ ## Build
100
+
101
+ ```bash
102
+ pnpm install
103
+ pnpm build # tsc declarations + tsdown bundles
104
+ pnpm typecheck # strict check
105
+ pnpm pack # produce dsh-qr-share-<version>.tgz (used by the install commands above)
106
+ ```
107
+
108
+ Outputs:
109
+
110
+ - `lib/index.js` — Node host half (the `/_qr/share` route)
111
+ - `lib/invariant.js` — runtime guard
112
+ - `lib/client.js` — official profile channel bundle (id `dsh-qr-share`)
113
+ - `lib/client-registry.js` — plugin-registry channel bundle (id `dsh-external/dsh-qr-share`)
114
+ - `lib/types/**` — TypeScript declarations
115
+
116
+ ## Compatibility
117
+
118
+ - Node `>=20`
119
+ - DSH (DeepSeek Harness) core `>=0.1.0-rc.8` (tested against
120
+ `0.1.1-rc.2` and `0.1.2-alpha.1`)
121
+ - React `^18 || ^19` (peer, optional — the host half doesn't need React)
122
+
123
+ The host half uses `@deepseek-ai/cordis` **types only** (declaration
124
+ merging + `Context` re-export); the runtime cordis instance is supplied
125
+ by the DSH core at plugin mount time. This is why the peer dependency
126
+ on `@deepseek-ai/cordis` is intentionally absent — it would be rejected
127
+ by the DSH market manifest gate, and is not required at runtime.
128
+
129
+ ## Publishing
130
+
131
+ For maintainers — see [RELEASE-CHECKLIST.md](RELEASE-CHECKLIST.md) for
132
+ the step-by-step `npm publish` flow.
133
+
134
+ ## License
135
+
136
+ MIT — see [LICENSE](LICENSE).
137
+
138
+ ## Acknowledgements
139
+
140
+ - [`dsh-better-sidebar`](https://github.com/omdsh-dev/DSH-better-sidebar) — the
141
+ slot injection and dual client bundle (official / plugin-registry) pattern
142
+ is inspired by its `tsdown.config.ts`.
143
+ - [`qrcode`](https://www.npmjs.com/package/qrcode) — the QR code renderer.
144
+ - DSH community — for the `dsh.community` plugin manifest spec
145
+ (`0.15`) and the verification / discovery surface at
146
+ [dsh.so](https://www.dsh.so).
package/README.zh.md ADDED
@@ -0,0 +1,109 @@
1
+ # dsh-qr-share
2
+
3
+ [English](README.md) | 中文
4
+
5
+ > DeepSeek Harness 侧边栏底部 QR 码按钮插件。手机扫码即获取当前浏览器已登录的 launch URL,复用桌面已完成的 `?token=` 换 cookie 流程。
6
+
7
+ ![license](https://img.shields.io/github/license/xiaoguomeiyitian/dsh-qr-share)
8
+ ![version](https://img.shields.io/github/v/release/xiaoguomeiyitian/dsh-qr-share)
9
+ ![stars](https://img.shields.io/github/stars/xiaoguomeiyitian/dsh-qr-share?style=social)
10
+ ![dsh-so](https://www.dsh.so/badge/dsh-qr-share.svg)
11
+ ![dsh](https://img.shields.io/badge/DSH-0.1.0--rc.8%20%7C%200.1.2--alpha.1-8257D0)
12
+
13
+ ## 这是什么
14
+
15
+ DSH 运行起来后,扫码登录很烦——你用手机拍屏幕上的 `127.0.0.1:3080` 没用。`dsh-qr-share` 在侧边栏底部加一个 QR 码按钮:
16
+
17
+ 1. 登录后,QR 码图标出现在侧边栏底部(设置按钮旁)。
18
+ 2. 点击 → 弹窗显示 QR 码 + URL 明文(扫码失败时手动复制)。
19
+ 3. 手机扫码 → 浏览器打开 URL → 服务端 `BrowserAuth` 拿 token 换 cookie(303 → `/)→ 手机落到同一会话。
20
+
21
+ QR 内容用 `window.location.origin` 拼接,所以**自动跟随**桌面实际使用的 host(局域网 IP、公网域名、反向代理子路径)——**不需要任何环境变量**告诉它指向哪个 host。`web-external.patch.yml` 里那个 `DSH_PUBLIC_HOST` 只负责打开 `/_qr/share` 路由的信任围栏,不参与 URL 拼接。
22
+
23
+ ## 安全模型
24
+
25
+ - `/_qr/share` 走与 `/api` 相同的 `connection.requestRejection` 围栏:
26
+ - 403 — Host 不在 `trustedHosts`;
27
+ - 401 — 浏览器 cookie 缺失/过期;
28
+ - 200 — 仅在两条都通过时返回 token。
29
+ - 启动 token 每个 `dsh web` 进程一份(`process-stable`),第一次换 cookie 后即作废(cookie 绑定到 authority,HttpOnly + SameSite=Strict)。
30
+ - 客户端用 `credentials: 'same-origin'` + `cache: 'no-store'`,跨站 frame 无法访问该路由。
31
+
32
+ ## 安装
33
+
34
+ ### 通过 DSH CLI(推荐)
35
+
36
+ ```bash
37
+ dsh plugin --profile <name> add dsh-qr-share@latest
38
+ ```
39
+
40
+ CLI 会自动把本包声明的 `cordis.patch.yml` 加进 `dsh.profile.bundles`,不需要手改 profile。
41
+
42
+ ### 通过 `npm install`
43
+
44
+ ```bash
45
+ # 在拥有 DSH profile 目录的项目里
46
+ npm install dsh-qr-share
47
+ # 或
48
+ pnpm add dsh-qr-share
49
+ ```
50
+
51
+ 然后把 `cordis.patch.yml`(包内自带)的一行加到 profile 的 patch overlay 里,并确保 profile 的 `node_modules` 能解析到这个包。
52
+
53
+ ### 手动安装(如 docker-build 镜像)
54
+
55
+ 1. 把包放到 `/app/dsh-qr-share/`。
56
+ 2. 用 symlink 链到 `$DSH_HOME/profiles/node_modules/dsh-qr-share`(`docker-build/docker-scripts/entrypoint.sh` 已自动做)。
57
+ 3. 在 patch overlay 加:
58
+ ```yaml
59
+ - insert:
60
+ - id: qr-share
61
+ name: 'dsh-qr-share'
62
+ ```
63
+
64
+ ## 配置(patch overlay)
65
+
66
+ ```yaml
67
+ - insert:
68
+ - id: qr-share
69
+ name: 'dsh-qr-share'
70
+ config:
71
+ enabled: true # 默认 true;设为 false 时路由 short-circuit 到 404
72
+ ```
73
+
74
+ ## 构建
75
+
76
+ ```bash
77
+ pnpm install
78
+ pnpm build # tsc 声明 + tsdown bundle
79
+ pnpm typecheck # 严格检查
80
+ pnpm pack # 产出 dsh-qr-share-<version>.tgz
81
+ ```
82
+
83
+ 产物:
84
+
85
+ - `lib/index.js` — Node 宿主半(`/_qr/share` 路由)
86
+ - `lib/invariant.js` — 运行时守卫
87
+ - `lib/client.js` — 官方 profile 通道 bundle(id `dsh-qr-share`)
88
+ - `lib/client-registry.js` — plugin-registry 通道 bundle(id `dsh-external/dsh-qr-share`)
89
+ - `lib/types/**` — TypeScript 声明
90
+
91
+ ## 兼容性
92
+
93
+ - Node `>=20`
94
+ - DSH core `>=0.1.0-rc.8`(已在 `0.1.1-rc.2` 与 `0.1.2-alpha.1` 实测)
95
+ - React `^18 || ^19`(peer,可选;宿主半不依赖 React)
96
+
97
+ 宿主半只**类型层面**使用 `@deepseek-ai/cordis`(声明合并 + `Context` re-export),运行时 cordis 实例由 DSH core 在挂载时注入——所以 `peerDependencies` 里**故意没有** `@deepseek-ai/cordis`:它会被 DSH 市场 manifest 闸门按名硬拒,运行时也不需要。
98
+
99
+ ## 发布
100
+
101
+ 维护者请参考 [RELEASE-CHECKLIST.md](RELEASE-CHECKLIST.md) 走 `npm publish` 流程。
102
+
103
+ ## 许可证
104
+
105
+ MIT — 见 [LICENSE](LICENSE)。
106
+
107
+ ## 致谢
108
+
109
+ `[dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)` 启发了本插件的 slot 注入与多 bundle 双通道模式;`qrcode` 库提供 QR 渲染。
@@ -0,0 +1,38 @@
1
+ # Third-Party Notices
2
+
3
+ This project includes or depends on the following third-party software.
4
+ The full corresponding license texts are reproduced below per their
5
+ respective terms.
6
+
7
+ ---
8
+
9
+ ## `qrcode` — MIT
10
+
11
+ > Copyright (c) 2012 Ryan Day
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in
21
+ all copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
+ THE SOFTWARE.
30
+
31
+ ---
32
+
33
+ ## `react-icons` — MIT / CC0 (icon glyphs)
34
+
35
+ `react-icons` redistributes icon glyphs from multiple upstream icon sets
36
+ (Heroicons, VS Code Icons, etc.) under MIT for the wrapper code and
37
+ CC0 1.0 Universal for the glyphs themselves. See
38
+ <https://github.com/react-icons/react-icons#license> for the full breakdown.
@@ -0,0 +1,22 @@
1
+ # dsh-qr-share bundle patch.
2
+ #
3
+ # When the plugin is installed through the official CLI:
4
+ #
5
+ # dsh plugin --profile <name> add dsh-qr-share@<version>
6
+ #
7
+ # — this declaration makes the profile reconcile `dsh.profile.bundles`
8
+ # against installed packages and append `dsh-qr-share` to the bundle
9
+ # stack automatically. The profile boot then merges THIS patch (a single
10
+ # `insert` of the plugin row) exactly like a manual cordis.patch.yml
11
+ # mount line.
12
+ #
13
+ # `name` MUST be the package specifier, not a file path: dsh-client-
14
+ # modules resolves `<name>/package.json` from the profile dir to
15
+ # discover the client half and compose window.__DSH_BOOT__.
16
+ #
17
+ # To disable the plugin without uninstalling, swap this row's `name`
18
+ # to a `!!js` expression returning a disabled placeholder (see
19
+ # dsh-better-sidebar's cordis.patch.yml for the pattern).
20
+ - insert:
21
+ - id: qr-share
22
+ name: 'dsh-qr-share'
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "dsh-external/dsh-qr-share",
3
+ "version": "0.1.0",
4
+ "main": "./lib/index.js",
5
+ "description": "Sidebar footer QR-code button: lets a phone scan and re-issue the current browser's authenticated launch URL, reusing the same ?token exchange the desktop just completed.",
6
+ "engines": {
7
+ "dsh": ">=0.0.1"
8
+ },
9
+ "contributes": {
10
+ "tools": [],
11
+ "skills": []
12
+ },
13
+ "client": {
14
+ "main": "./lib/client-registry.js"
15
+ }
16
+ }