virlen-remote 0.2.0 → 0.3.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 CHANGED
@@ -3,6 +3,22 @@
3
3
  本文件记录对外可见的变更(协议 / 导出面 / 行为)。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
4
4
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
5
5
 
6
+ ## [0.3.0] - 2026-10-01
7
+
8
+ ### Added
9
+
10
+ - **配对链接**(`buildPairingUrl` / `PAIRING_URL_BASE` / `PAIRING_URL_PARAM`):二维码内容改为
11
+ `https://virlen.cn/mobile?t=<配对数据>`。这样**系统相机 / 微信 / 任意浏览器**扫码就能直接打开
12
+ 手机端并自动配对,无需先装 App 再扫码;App 内的扫码器则从 URL 里取回 `t`。
13
+ - `parsePairingPayload` **新增识 URL**:输入是 URL 时自动取 `?t=` 再解析。三种输入都吃:
14
+ URL / `vrp1:` 混淆串 / 旧版明文 JSON。
15
+
16
+ ### Notes
17
+
18
+ - 向后兼容:新版能解旧码与旧串;**旧版解不了新码**(消费方需同步升级)。
19
+ - `PAIRING_URL_BASE` 是部署地址,`buildPairingUrl` 的第二个参数 `base` 可覆盖(自建 / 换域名)。
20
+ - 无破坏性变更;0.2.0 的导出面保持不变。
21
+
6
22
  ## [0.2.0] - 2026-09-30
7
23
 
8
24
  ### Changed
@@ -94,3 +110,4 @@
94
110
  [0.1.1]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.1
95
111
  [0.1.2]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.2
96
112
  [0.2.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.2.0
113
+ [0.3.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.3.0
package/README.md CHANGED
@@ -106,21 +106,25 @@ const hello = await caller.call('host.hello', {
106
106
  ```ts
107
107
  import {
108
108
  buildPairingPayload,
109
- encodePairingPayload,
109
+ buildPairingUrl,
110
110
  parsePairingPayload,
111
111
  roomOfPayload,
112
112
  } from 'virlen-remote'
113
113
 
114
114
  const payload = buildPairingPayload({ host: deviceKey, name: '我的电脑', ticket, signal: signalUrl })
115
- const qrText = encodePairingPayload(payload) // 画进二维码 —— 输出形如 'vrp1:xxxx'(混淆后,非明文 JSON)
116
- const back = parsePairingPayload(qrText) // 手机端解析(无法识别时返回 null)
115
+ const qrText = buildPairingUrl(payload) // 画进二维码:'https://virlen.cn/mobile?t=vrp1:xxxx'
116
+ const back = parsePairingPayload(qrText) // 手机端解析(URL / 混淆串 / 旧明文 JSON 都吃;认不出返回 null)
117
117
  if (back) await connect(roomOfPayload(back), back.host, back.ticket)
118
118
  ```
119
119
 
120
- > **关于混淆**:`encodePairingPayload` 输出 `vrp1:` + `Base64URL(JSON ⊕ 固定盐)`,只是让二维码文字 /
120
+ > **配对链接**:二维码内容 = `https://virlen.cn/mobile?t=<配对数据>`,于是**系统相机 / 微信 / 任意浏览器**
121
+ > 扫码就能直接打开手机端并自动配对。手机端从 `?t=` 取回数据后同样交给 `parsePairingPayload`。
122
+ > 地址常量见 `PAIRING_URL_BASE`(自建 / 换域名给 `buildPairingUrl` 传第二个参数覆盖)。
123
+ >
124
+ > **关于混淆**:配对数据是 `vrp1:` + `Base64URL(JSON ⊕ 固定盐)`,只是让二维码文字 /
121
125
  > 界面上的排查文本**不是一眼可读**,**不是加密** —— 固定盐在源码里,拿到源码即可解;它也
122
126
  > 挡不住有人对着屏幕拍照。真正的安全边界是「一次性 ticket + 电脑端确认弹窗 + 授权凭证」。
123
- > `parsePairingPayload` **同时接受**混淆串与旧版明文 JSON(向后兼容)。前缀常量见 `PAIRING_OBFUSCATION_PREFIX`。
127
+ > `parsePairingPayload` **同时接受** URL、混淆串与旧版明文 JSON(向后兼容)。前缀常量见 `PAIRING_OBFUSCATION_PREFIX`。
124
128
 
125
129
  ## ICE / TURN 配置
126
130
 
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export type { ErrorCode, WireError, BridgeErrorOptions } from './protocol/errors
18
18
  export { newRequestId } from './protocol/ids.js';
19
19
  export { HOST_KEY_PREFIX, MOBILE_KEY_PREFIX, GRANT_PREFIX, ROOM_PREFIX, GRANT_TTL_MS, GRANT_MAX_LIFETIME_MS, randomKey, newDeviceKey, newGrantToken, isDeviceKey, roomFor, hostKeyFromRoom, issueGrant, isGrantExpired, renewGrant, checkGrant, describeGrantRemaining, } from './protocol/identity.js';
20
20
  export type { DeviceKind, GrantRecord, CredentialRejectReason, RoomStatus } from './protocol/identity.js';
21
- export { PAIRING_PAYLOAD_VERSION, PAIRING_TICKET_TTL_MS, PAIRING_OBFUSCATION_PREFIX, buildPairingPayload, encodePairingPayload, parsePairingPayload, roomOfPayload, } from './protocol/pairing.js';
21
+ export { PAIRING_PAYLOAD_VERSION, PAIRING_TICKET_TTL_MS, PAIRING_OBFUSCATION_PREFIX, PAIRING_URL_BASE, PAIRING_URL_PARAM, buildPairingPayload, encodePairingPayload, buildPairingUrl, parsePairingPayload, roomOfPayload, } from './protocol/pairing.js';
22
22
  export type { PairingPayload } from './protocol/pairing.js';
23
23
  export { normalizeChoiceAnswer, answerActionError, CHOICE_JOINER } from './protocol/answer.js';
24
24
  export type { ChoiceAnswer } from './protocol/answer.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ export { newRequestId } from './protocol/ids.js';
18
18
  // ── 设备身份与授权凭证(M6,§30)──
19
19
  export { HOST_KEY_PREFIX, MOBILE_KEY_PREFIX, GRANT_PREFIX, ROOM_PREFIX, GRANT_TTL_MS, GRANT_MAX_LIFETIME_MS, randomKey, newDeviceKey, newGrantToken, isDeviceKey, roomFor, hostKeyFromRoom, issueGrant, isGrantExpired, renewGrant, checkGrant, describeGrantRemaining, } from './protocol/identity.js';
20
20
  // ── 配对载荷(二维码内容,两端同一份)──
21
- export { PAIRING_PAYLOAD_VERSION, PAIRING_TICKET_TTL_MS, PAIRING_OBFUSCATION_PREFIX, buildPairingPayload, encodePairingPayload, parsePairingPayload, roomOfPayload, } from './protocol/pairing.js';
21
+ export { PAIRING_PAYLOAD_VERSION, PAIRING_TICKET_TTL_MS, PAIRING_OBFUSCATION_PREFIX, PAIRING_URL_BASE, PAIRING_URL_PARAM, buildPairingPayload, encodePairingPayload, buildPairingUrl, parsePairingPayload, roomOfPayload, } from './protocol/pairing.js';
22
22
  export { normalizeChoiceAnswer, answerActionError, CHOICE_JOINER } from './protocol/answer.js';
23
23
  export { negotiate, intersectCapabilities } from './protocol/hello.js';
24
24
  export { Endpoint } from './protocol/endpoint.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iBAAiB;AACjB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,yBAAyB;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,EACf,UAAU,EACV,cAAc,EACd,UAAU,EACV,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAG5B,yBAAyB;AACzB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG3F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAU9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAsClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAS/F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,mCAAmC;AACnC,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAQ9B,yCAAyC;AACzC,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iBAAiB;AACjB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,yBAAyB;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,EACf,UAAU,EACV,cAAc,EACd,UAAU,EACV,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAG5B,yBAAyB;AACzB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG3F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAU9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAsClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAS/F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,mCAAmC;AACnC,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAQ9B,yCAAyC;AACzC,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAA"}
@@ -17,6 +17,17 @@ export declare const PAIRING_TICKET_TTL_MS: number;
17
17
  * 前缀选 `vrp1:`(virlen pairing v1)—— 明文 JSON 必以 `{` 开头,不可能撞上前缀。
18
18
  */
19
19
  export declare const PAIRING_OBFUSCATION_PREFIX = "vrp1:";
20
+ /**
21
+ * 配对二维码指向的手机端地址。
22
+ *
23
+ * 二维码内容 = `https://virlen.cn/mobile?t=<配对数据>`:**系统相机 / 微信 / 任意浏览器**扫到后
24
+ * 直接打开手机端,手机端从 `?t=` 取回配对数据即可自动配对 —— 无需先装 App 再扫码。
25
+ *
26
+ * ⚠️ 这是**部署地址**(手机端 PWA 在 `/mobile/`);自建 / 换域名时由调用方传入 `base` 覆盖。
27
+ */
28
+ export declare const PAIRING_URL_BASE = "https://virlen.cn/mobile";
29
+ /** URL 里承载配对数据的查询参数名。 */
30
+ export declare const PAIRING_URL_PARAM = "t";
20
31
  export interface PairingPayload {
21
32
  v: number;
22
33
  /** 电脑设备 key(也是房间号的来源)。 */
@@ -40,9 +51,17 @@ export declare function buildPairingPayload(input: {
40
51
  /** 序列化为二维码内容(**混淆后的串**,不再是明文 JSON)。 */
41
52
  export declare function encodePairingPayload(payload: PairingPayload): string;
42
53
  /**
43
- * 解析二维码 / 配对串。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
54
+ * 构造成「可被系统相机 / 微信扫开」的配对链接(二维码内容)。
55
+ *
56
+ * @param payload 配对载荷
57
+ * @param base 手机端地址(缺省 {@link PAIRING_URL_BASE})
58
+ */
59
+ export declare function buildPairingUrl(payload: PairingPayload, base?: string): string;
60
+ /**
61
+ * 解析二维码 / 配对串 / 配对链接。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
44
62
  *
45
63
  * 宽容点(都是真机上会遇到的东西):
64
+ * - 输入可以是 `vrp1:…` 串、旧版明文 JSON,或 `…?t=<配对数据>` 的 URL;
46
65
  * - `v` 缺省按 1 处理(旧码);
47
66
  * - `name` 缺省回退 `host`;
48
67
  * - `ticket` 允许是任意非空串(前缀只作可读性,不作校验 —— 电脑端的判定才是权威)。
@@ -1 +1 @@
1
- {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/protocol/pairing.ts"],"names":[],"mappings":"AAsBA,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAgB,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,UAAU,CAAA;AAcjD,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAA;IACT,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,iDAAiD;AACjD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,cAAc,CAQjB;AAwED,uCAAuC;AACvC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAGpE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAyBvE;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/protocol/pairing.ts"],"names":[],"mappings":"AA2BA,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAgB,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,UAAU,CAAA;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,6BAA6B,CAAA;AAE1D,yBAAyB;AACzB,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAcpC,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAA;IACT,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,iDAAiD;AACjD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,cAAc,CAQjB;AAwED,uCAAuC;AACvC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAGpE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,GAAE,MAAyB,GAAG,MAAM,CAIhG;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CA2BvE;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAE7D"}
@@ -14,6 +14,11 @@
14
14
  * `vrp1:` + Base64URL(UTF-8(JSON) ⊕ 固定盐),使二维码文字 / 界面上的排查文本不再是明文 JSON;
15
15
  * `parsePairingPayload` 仍**兼容旧明文码**(无前缀即按原 JSON 解析),已生成 / 已截图的旧码不会失效。
16
16
  *
17
+ * **二维码内容是一条 URL**:`https://virlen.cn/mobile?t=<配对数据>`(见 `buildPairingUrl`)。
18
+ * 这样**系统相机 / 微信 / 任意浏览器**扫码都能直接打开手机端并自动配对,无需先装 App 再扫码;
19
+ * App 内的扫码器则用 `parsePairingPayload` 从 URL 里取回 `t`。`parsePairingPayload`
20
+ * **三种输入都吃**:URL、`vrp1:` 混淆串、旧版明文 JSON。
21
+ *
17
22
  * ⚠️ 这是**混淆,不是加密**:本包是公开 npm 包、固定盐也在源码里,拿到源码即可解;
18
23
  * 且它**挡不住有人对着屏幕拍照**(二维码本就是给人扫的)。真正的安全边界是
19
24
  * 「一次性 ticket + 电脑端确认弹窗 + 授权凭证」(见 `identity.ts`)。
@@ -38,6 +43,17 @@ export const PAIRING_TICKET_TTL_MS = 5 * 60 * 1000;
38
43
  * 前缀选 `vrp1:`(virlen pairing v1)—— 明文 JSON 必以 `{` 开头,不可能撞上前缀。
39
44
  */
40
45
  export const PAIRING_OBFUSCATION_PREFIX = 'vrp1:';
46
+ /**
47
+ * 配对二维码指向的手机端地址。
48
+ *
49
+ * 二维码内容 = `https://virlen.cn/mobile?t=<配对数据>`:**系统相机 / 微信 / 任意浏览器**扫到后
50
+ * 直接打开手机端,手机端从 `?t=` 取回配对数据即可自动配对 —— 无需先装 App 再扫码。
51
+ *
52
+ * ⚠️ 这是**部署地址**(手机端 PWA 在 `/mobile/`);自建 / 换域名时由调用方传入 `base` 覆盖。
53
+ */
54
+ export const PAIRING_URL_BASE = 'https://virlen.cn/mobile';
55
+ /** URL 里承载配对数据的查询参数名。 */
56
+ export const PAIRING_URL_PARAM = 't';
41
57
  const textEncoder = new TextEncoder();
42
58
  const textDecoder = new TextDecoder();
43
59
  /**
@@ -137,9 +153,42 @@ export function encodePairingPayload(payload) {
137
153
  return PAIRING_OBFUSCATION_PREFIX + base64UrlEncode(xorSalt(json));
138
154
  }
139
155
  /**
140
- * 解析二维码 / 配对串。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
156
+ * 构造成「可被系统相机 / 微信扫开」的配对链接(二维码内容)。
157
+ *
158
+ * @param payload 配对载荷
159
+ * @param base 手机端地址(缺省 {@link PAIRING_URL_BASE})
160
+ */
161
+ export function buildPairingUrl(payload, base = PAIRING_URL_BASE) {
162
+ const url = new URL(base);
163
+ url.searchParams.set(PAIRING_URL_PARAM, encodePairingPayload(payload));
164
+ return url.toString();
165
+ }
166
+ /**
167
+ * 从「扫到 / 粘进来的文本」里取出配对数据本体。
168
+ *
169
+ * 三种输入都吃:
170
+ * - **URL**(`https://virlen.cn/mobile?t=<配对数据>`)—— 系统相机 / 微信扫码打开的就是它;
171
+ * - `vrp1:…` 混淆串;
172
+ * - 旧版明文 JSON。
173
+ *
174
+ * 返回 `null` 表示「这里没有配对数据」(比如是别的网址、或 URL 上没带 `t`)。
175
+ */
176
+ function pairingCodeFromText(text) {
177
+ if (!/^https?:\/\//i.test(text))
178
+ return text;
179
+ try {
180
+ const code = new URL(text).searchParams.get(PAIRING_URL_PARAM);
181
+ return code && code.trim() ? code.trim() : null;
182
+ }
183
+ catch {
184
+ return null;
185
+ }
186
+ }
187
+ /**
188
+ * 解析二维码 / 配对串 / 配对链接。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
141
189
  *
142
190
  * 宽容点(都是真机上会遇到的东西):
191
+ * - 输入可以是 `vrp1:…` 串、旧版明文 JSON,或 `…?t=<配对数据>` 的 URL;
143
192
  * - `v` 缺省按 1 处理(旧码);
144
193
  * - `name` 缺省回退 `host`;
145
194
  * - `ticket` 允许是任意非空串(前缀只作可读性,不作校验 —— 电脑端的判定才是权威)。
@@ -147,9 +196,12 @@ export function encodePairingPayload(payload) {
147
196
  export function parsePairingPayload(text) {
148
197
  if (typeof text !== 'string' || !text.trim())
149
198
  return null;
150
- const trimmed = text.trim();
199
+ // 先取出配对数据本体(URL → `?t=`;否则原样)
200
+ const code = pairingCodeFromText(text.trim());
201
+ if (code === null)
202
+ return null;
151
203
  // 带前缀 → 先解混淆;否则按旧版明文 JSON 解析(向后兼容,见 PAIRING_OBFUSCATION_PREFIX)
152
- const jsonText = trimmed.startsWith(PAIRING_OBFUSCATION_PREFIX) ? decodeObfuscated(trimmed) : trimmed;
204
+ const jsonText = code.startsWith(PAIRING_OBFUSCATION_PREFIX) ? decodeObfuscated(code) : code;
153
205
  if (jsonText === null)
154
206
  return null;
155
207
  let raw;
@@ -1 +1 @@
1
- {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/protocol/pairing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/protocol/pairing.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virlen-remote",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Virlen 手机控制 · 协议栈共享包 —— 帧 / RPC / 错误 / 能力协商 / 设备身份与授权凭证 / 配对载荷 / ICE 解析 / Transport(WebRTC / 内存 / Broadcast)",
5
5
  "license": "MIT",
6
6
  "author": "WeiChens",
package/src/index.ts CHANGED
@@ -48,8 +48,11 @@ export {
48
48
  PAIRING_PAYLOAD_VERSION,
49
49
  PAIRING_TICKET_TTL_MS,
50
50
  PAIRING_OBFUSCATION_PREFIX,
51
+ PAIRING_URL_BASE,
52
+ PAIRING_URL_PARAM,
51
53
  buildPairingPayload,
52
54
  encodePairingPayload,
55
+ buildPairingUrl,
53
56
  parsePairingPayload,
54
57
  roomOfPayload,
55
58
  } from './protocol/pairing'
@@ -14,6 +14,11 @@
14
14
  * `vrp1:` + Base64URL(UTF-8(JSON) ⊕ 固定盐),使二维码文字 / 界面上的排查文本不再是明文 JSON;
15
15
  * `parsePairingPayload` 仍**兼容旧明文码**(无前缀即按原 JSON 解析),已生成 / 已截图的旧码不会失效。
16
16
  *
17
+ * **二维码内容是一条 URL**:`https://virlen.cn/mobile?t=<配对数据>`(见 `buildPairingUrl`)。
18
+ * 这样**系统相机 / 微信 / 任意浏览器**扫码都能直接打开手机端并自动配对,无需先装 App 再扫码;
19
+ * App 内的扫码器则用 `parsePairingPayload` 从 URL 里取回 `t`。`parsePairingPayload`
20
+ * **三种输入都吃**:URL、`vrp1:` 混淆串、旧版明文 JSON。
21
+ *
17
22
  * ⚠️ 这是**混淆,不是加密**:本包是公开 npm 包、固定盐也在源码里,拿到源码即可解;
18
23
  * 且它**挡不住有人对着屏幕拍照**(二维码本就是给人扫的)。真正的安全边界是
19
24
  * 「一次性 ticket + 电脑端确认弹窗 + 授权凭证」(见 `identity.ts`)。
@@ -42,6 +47,19 @@ export const PAIRING_TICKET_TTL_MS = 5 * 60 * 1000
42
47
  */
43
48
  export const PAIRING_OBFUSCATION_PREFIX = 'vrp1:'
44
49
 
50
+ /**
51
+ * 配对二维码指向的手机端地址。
52
+ *
53
+ * 二维码内容 = `https://virlen.cn/mobile?t=<配对数据>`:**系统相机 / 微信 / 任意浏览器**扫到后
54
+ * 直接打开手机端,手机端从 `?t=` 取回配对数据即可自动配对 —— 无需先装 App 再扫码。
55
+ *
56
+ * ⚠️ 这是**部署地址**(手机端 PWA 在 `/mobile/`);自建 / 换域名时由调用方传入 `base` 覆盖。
57
+ */
58
+ export const PAIRING_URL_BASE = 'https://virlen.cn/mobile'
59
+
60
+ /** URL 里承载配对数据的查询参数名。 */
61
+ export const PAIRING_URL_PARAM = 't'
62
+
45
63
  const textEncoder = new TextEncoder()
46
64
  const textDecoder = new TextDecoder()
47
65
 
@@ -161,18 +179,53 @@ export function encodePairingPayload(payload: PairingPayload): string {
161
179
  }
162
180
 
163
181
  /**
164
- * 解析二维码 / 配对串。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
182
+ * 构造成「可被系统相机 / 微信扫开」的配对链接(二维码内容)。
183
+ *
184
+ * @param payload 配对载荷
185
+ * @param base 手机端地址(缺省 {@link PAIRING_URL_BASE})
186
+ */
187
+ export function buildPairingUrl(payload: PairingPayload, base: string = PAIRING_URL_BASE): string {
188
+ const url = new URL(base)
189
+ url.searchParams.set(PAIRING_URL_PARAM, encodePairingPayload(payload))
190
+ return url.toString()
191
+ }
192
+
193
+ /**
194
+ * 从「扫到 / 粘进来的文本」里取出配对数据本体。
195
+ *
196
+ * 三种输入都吃:
197
+ * - **URL**(`https://virlen.cn/mobile?t=<配对数据>`)—— 系统相机 / 微信扫码打开的就是它;
198
+ * - `vrp1:…` 混淆串;
199
+ * - 旧版明文 JSON。
200
+ *
201
+ * 返回 `null` 表示「这里没有配对数据」(比如是别的网址、或 URL 上没带 `t`)。
202
+ */
203
+ function pairingCodeFromText(text: string): string | null {
204
+ if (!/^https?:\/\//i.test(text)) return text
205
+ try {
206
+ const code = new URL(text).searchParams.get(PAIRING_URL_PARAM)
207
+ return code && code.trim() ? code.trim() : null
208
+ } catch {
209
+ return null
210
+ }
211
+ }
212
+
213
+ /**
214
+ * 解析二维码 / 配对串 / 配对链接。无法识别返回 `null`(调用方给「不是 Virlen 配对码」的文案)。
165
215
  *
166
216
  * 宽容点(都是真机上会遇到的东西):
217
+ * - 输入可以是 `vrp1:…` 串、旧版明文 JSON,或 `…?t=<配对数据>` 的 URL;
167
218
  * - `v` 缺省按 1 处理(旧码);
168
219
  * - `name` 缺省回退 `host`;
169
220
  * - `ticket` 允许是任意非空串(前缀只作可读性,不作校验 —— 电脑端的判定才是权威)。
170
221
  */
171
222
  export function parsePairingPayload(text: string): PairingPayload | null {
172
223
  if (typeof text !== 'string' || !text.trim()) return null
173
- const trimmed = text.trim()
224
+ // 先取出配对数据本体(URL → `?t=`;否则原样)
225
+ const code = pairingCodeFromText(text.trim())
226
+ if (code === null) return null
174
227
  // 带前缀 → 先解混淆;否则按旧版明文 JSON 解析(向后兼容,见 PAIRING_OBFUSCATION_PREFIX)
175
- const jsonText = trimmed.startsWith(PAIRING_OBFUSCATION_PREFIX) ? decodeObfuscated(trimmed) : trimmed
228
+ const jsonText = code.startsWith(PAIRING_OBFUSCATION_PREFIX) ? decodeObfuscated(code) : code
176
229
  if (jsonText === null) return null
177
230
  let raw: unknown
178
231
  try {