dsh-plugin-mobile-link 0.7.0 → 0.8.1
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/README.md +27 -12
- package/cordis.patch.yml +5 -0
- package/lib/cli.js +11 -13
- package/lib/client.js +1453 -91
- package/lib/dlp.js +15 -0
- package/lib/enroll.js +249 -35
- package/lib/handoff.js +1 -1
- package/lib/hello.js +1 -1
- package/lib/history/fold-summary.js +85 -32
- package/lib/history/fold.js +11 -1
- package/lib/history/question-record.js +186 -0
- package/lib/i18n.js +177 -0
- package/lib/image-originals.js +121 -0
- package/lib/image-variants.js +315 -0
- package/lib/index.js +59 -2
- package/lib/journal.js +2 -0
- package/lib/link.js +274 -17
- package/lib/router.js +70 -8
- package/lib/routes.js +147 -0
- package/lib/session-digest.js +24 -10
- package/lib/session-scope.js +89 -0
- package/lib/state.js +3 -3
- package/lib/status.js +42 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,14 +24,14 @@ plugins/mobile-link/
|
|
|
24
24
|
├── package.json # type: module, dsh.bundle.patch, bin: dsh-mobile-link
|
|
25
25
|
├── cordis.patch.yml # bundle patch: the loader row + config reference
|
|
26
26
|
├── lib/
|
|
27
|
-
│ ├── index.js # cordis plugin: starts the agent, registers
|
|
27
|
+
│ ├── index.js # cordis plugin: starts the agent, registers 5 routes
|
|
28
28
|
│ ├── link.js # the DLP agent transport: identity, socket, heartbeat, reconnect
|
|
29
29
|
│ ├── router.js # frames ⇄ local DSH mux streams, per-device `$events`
|
|
30
30
|
│ ├── dlp.js # DLP v1 frame codec (shared with the relay)
|
|
31
31
|
│ ├── files.js # phone → computer file transfers (`_link/file*` methods)
|
|
32
|
-
│ ├── enroll.js # invite
|
|
32
|
+
│ ├── enroll.js # open enrollment (no invite code), writes ~/.dsh/mobile-link/agent.json
|
|
33
33
|
│ ├── journal.js # lifecycle journal: why a relay link was dropped, after the fact
|
|
34
|
-
│ ├── routes.js #
|
|
34
|
+
│ ├── routes.js # status, enroll, pair-code, pair-code/regenerate, devices/revoke
|
|
35
35
|
│ ├── qr.js # dependency-free QR SVG
|
|
36
36
|
│ └── cli.js # dsh-mobile-link: enroll / mint-pair-code
|
|
37
37
|
└── test/ # node --test, 100+ tests (no DSH, no network)
|
|
@@ -74,29 +74,43 @@ dsh plugin --profile web add ./plugins/mobile-link
|
|
|
74
74
|
|
|
75
75
|
## Use
|
|
76
76
|
|
|
77
|
-
1. Register this computer with a relay (once).
|
|
77
|
+
1. Register this computer with a relay (once). Open the **移动端连接** panel at the
|
|
78
|
+
bottom of the DSH sidebar: the relay address is prefilled (editable) from the
|
|
79
|
+
installation default; click **登记**. The connector derives the computer name
|
|
80
|
+
automatically, writes `~/.dsh/mobile-link/agent.json` and reconnects in place —
|
|
81
|
+
no terminal and no DSH restart. Open enrollment is the default; the relay
|
|
82
|
+
rate-limits by source. If an installation has no panel (headless, CI), the CLI
|
|
83
|
+
still works:
|
|
78
84
|
|
|
79
85
|
```bash
|
|
80
|
-
dsh-mobile-link enroll --
|
|
86
|
+
dsh-mobile-link enroll --relay wss://<你的站点>/dsh-link
|
|
81
87
|
```
|
|
82
88
|
|
|
83
89
|
…or register it on the relay host with `dsh-relay admin agent-register
|
|
84
90
|
--write-config ~/.dsh/mobile-link/agent.json`.
|
|
85
91
|
|
|
86
|
-
2.
|
|
92
|
+
2. The same panel shows the pairing code and its QR once the relay link is up.
|
|
93
|
+
The CLI can still mint one directly:
|
|
87
94
|
|
|
88
95
|
```bash
|
|
89
96
|
dsh-mobile-link --mint-pair-code # prints JSON: code + relay + agentId
|
|
90
97
|
```
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
The code is persistent: it stays valid until you click 重新生成. Each paired device
|
|
100
|
+
has a quiet 撤销 action; revoking one device leaves the code and other devices alone.
|
|
94
101
|
|
|
95
|
-
3. Scan it in the iOS app.
|
|
96
|
-
|
|
102
|
+
3. Scan it in the iOS app. The code can be claimed again for another phone, a reinstall
|
|
103
|
+
or a new device; only 重新生成 revokes it (already-paired devices stay connected).
|
|
104
|
+
After that the phone reconnects on its own — nothing else has to be run by hand.
|
|
97
105
|
|
|
98
106
|
`GET /mobile-link/status` reports whether the link is up, which devices are
|
|
99
|
-
attached, and whether this computer is registered yet. Its `
|
|
107
|
+
attached, and whether this computer is registered yet. Its `enroll` block also
|
|
108
|
+
carries `panel` (this connector can enroll from the DSH panel), `relayPrefill`
|
|
109
|
+
and `relayLocked` (the address to prefill and whether it came from config).
|
|
110
|
+
`POST /mobile-link/enroll` performs that enrollment (open enrollment; no invite code); it returns the agent id and
|
|
111
|
+
name, never `agentSecret`, and is behind the same DSH request fence as pairing. `GET /mobile-link/devices` lists the full paired roster (including offline) for the panel, and
|
|
112
|
+
`POST /mobile-link/devices/revoke` revokes one paired device by `deviceId` through the relay; both are behind
|
|
113
|
+
the fence and authenticated with the agent secret. Its `journal` block carries
|
|
100
114
|
the current process's newest lifecycle events (link opened/closed with the reason,
|
|
101
115
|
device attached/detached); the counters in it reset when the plugin hot-reloads, so
|
|
102
116
|
the full history — across restarts — is the JSONL file at `journal.path`.
|
|
@@ -109,7 +123,8 @@ plugin's config block (`cordis.patch.yml`), not by editing this file.
|
|
|
109
123
|
| Key | Default | Meaning |
|
|
110
124
|
| --- | --- | --- |
|
|
111
125
|
| `enabled` | `true` | `false` stops the outbound link without uninstalling |
|
|
112
|
-
| `relayUrl` | from `agent.json` | Relay base URL; a bare deployment dials `<relay>/link/agent
|
|
126
|
+
| `relayUrl` | from `agent.json` | Relay base URL; a bare deployment dials `<relay>/link/agent`. Pinning a real value here outranks the enrolled identity and makes the panel field read-only |
|
|
127
|
+
| `defaultRelayUrl` | empty | Form-only default: prefills the panel's relay input, but never outranks the enrolled identity. Official installs set this so users can still edit it |
|
|
113
128
|
| `agentId` / `agentSecret` | from `agent.json` | Override the enrolled identity |
|
|
114
129
|
| `stateFile` | `~/.dsh/mobile-link/agent.json` | Where the identity lives |
|
|
115
130
|
| `journalFile` | `~/.dsh/mobile-link/link-events.jsonl` | Lifecycle journal (rotated once at 256 KiB); `false` keeps it in memory only |
|
package/cordis.patch.yml
CHANGED
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
# ws://127.0.0.1:8787). Left unset here on purpose: a value
|
|
18
18
|
# pinned in this file outranks both DSH_RELAY_URL and the
|
|
19
19
|
# address already enrolled in ~/.dsh/mobile-link/agent.json.
|
|
20
|
+
# defaultRelayUrl Form-only default for the 移动端连接 panel. It prefills the
|
|
21
|
+
# relay input but does not outrank the identity file, so a user
|
|
22
|
+
# can edit it and the edit survives a restart. Official installs
|
|
23
|
+
# set this (not `relayUrl`) to prefill the real address.
|
|
24
|
+
# Status: enroll.relayPrefill / relayLocked.
|
|
20
25
|
# agentId overrides ~/.dsh/mobile-link/agent.json
|
|
21
26
|
# agentSecret overrides ~/.dsh/mobile-link/agent.json
|
|
22
27
|
# stateFile agent identity file (default ~/.dsh/mobile-link/agent.json)
|
package/lib/cli.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* prefix-preserving URL join the live agent uses.
|
|
18
18
|
*
|
|
19
19
|
* `enroll` is the first-run path for someone else's computer: it redeems an
|
|
20
|
-
*
|
|
20
|
+
* relay address for an identity and writes `~/.dsh/mobile-link/agent.json`, then
|
|
21
21
|
* exits. Everything after that is the ordinary agent.
|
|
22
22
|
*/
|
|
23
23
|
|
|
@@ -28,6 +28,7 @@ import { MobileLinkAgent } from './link.js'
|
|
|
28
28
|
import { mintPairCode } from './pairing.js'
|
|
29
29
|
import { qrTerminal } from './qr.js'
|
|
30
30
|
import { INVITE_ENV, defaultStatePath, resolveIdentity } from './state.js'
|
|
31
|
+
import { T, setLanguage } from './i18n.js'
|
|
31
32
|
|
|
32
33
|
/** Atomic status snapshot so readers never observe a half-written file. */
|
|
33
34
|
async function writeJsonAtomic(path, value) {
|
|
@@ -60,14 +61,16 @@ function parseArgs(argv) {
|
|
|
60
61
|
|
|
61
62
|
async function main() {
|
|
62
63
|
const args = parseArgs(process.argv.slice(2))
|
|
64
|
+
if (args.lang) setLanguage(args.lang)
|
|
65
|
+
else if (process.env.DSH_LANG) setLanguage(process.env.DSH_LANG)
|
|
63
66
|
if (args.help) {
|
|
64
|
-
process.stdout.write(`usage: cli.js enroll
|
|
65
|
-
|
|
66
|
-
The invite may also arrive as ${INVITE_ENV}. Exits non-zero on failure.
|
|
67
|
+
process.stdout.write(`usage: cli.js enroll [--relay <ws url>] [--name <label>]
|
|
68
|
+
Register this computer with open enrollment in ${defaultStatePath()}. Exits non-zero on failure.
|
|
67
69
|
|
|
68
70
|
cli.js --relay <ws url> --agent-id <id> --agent-secret <secret>
|
|
69
71
|
[--state-file <path>] [--dsh-url <url>]
|
|
70
72
|
[--status-file <path>] [--heartbeat-ms <n>] [--log-level debug|info|warn]
|
|
73
|
+
[--lang en|zh-Hans|zh-Hant|ja|ko|es|pt-BR|fr|de]
|
|
71
74
|
|
|
72
75
|
Note: run this inside a DSH host (plugin) when you can. A standalone process has no
|
|
73
76
|
in-process context, so on the official desktop app it cannot learn the local address by
|
|
@@ -101,21 +104,16 @@ async function main() {
|
|
|
101
104
|
// 反正人已经坐在终端前、手机就在手边,索性把配对码也申请出来、画成终端二维码:
|
|
102
105
|
// 扫这一屏等于在 App 里填「中转服务器 + 配对码」,少一次开页面找端口的往返。
|
|
103
106
|
// 申请失败不算失败——登记本身已经成功,退回到"去界面里的连接入口取"的说法。
|
|
104
|
-
process.stdout.write('
|
|
105
|
-
+ ' 1. 重启 DSH,让连接器用上这个身份;\n'
|
|
106
|
-
+ ' 2. 手机 App 的「扫码配对」扫下面的二维码(与 DSH 界面左下角「移动端连接」里那张等价)。\n')
|
|
107
|
+
process.stdout.write(`${T('cli.registered')}\n ${T('cli.step1')}\n ${T('cli.step2')}\n`)
|
|
107
108
|
try {
|
|
108
109
|
const identity = await resolveIdentity({ stateFile: settled.stateFile })
|
|
109
110
|
const minted = await mintPairCode({ identity, ttlMs: 10 * 60 * 1000 })
|
|
110
111
|
const minutes = Math.round((minted.expiresAt - Date.now()) / 60000)
|
|
111
112
|
process.stdout.write('\n' + qrTerminal(minted.qrPayload) + '\n')
|
|
112
|
-
process.stdout.write(
|
|
113
|
-
process.stdout.write('
|
|
114
|
-
+ '连接器还会把带令牌地址写进 ~/.dsh/mobile-link/endpoint.json。\n')
|
|
113
|
+
process.stdout.write(`${T('cli.code', { code: minted.code, minutes })}\n`)
|
|
114
|
+
process.stdout.write(`${T('cli.hint')}\n`)
|
|
115
115
|
} catch (error) {
|
|
116
|
-
process.stdout.write('
|
|
117
|
-
+ '重启 DSH 后,在这台电脑的 DSH 界面左下角点「移动端连接」,'
|
|
118
|
-
+ '用手机 App 的「扫码配对」扫里面的二维码。\n')
|
|
116
|
+
process.stdout.write(`${T('cli.prefetchFailed', { error: error?.message ?? error })}\n${T('cli.prefetchHint')}\n`)
|
|
119
117
|
}
|
|
120
118
|
return 0
|
|
121
119
|
}
|