virlen-remote 0.2.0 → 0.4.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 +41 -0
- package/README.md +9 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol/api.d.ts +17 -1
- package/dist/protocol/api.d.ts.map +1 -1
- package/dist/protocol/api.js.map +1 -1
- package/dist/protocol/pairing.d.ts +20 -1
- package/dist/protocol/pairing.d.ts.map +1 -1
- package/dist/protocol/pairing.js +55 -3
- package/dist/protocol/pairing.js.map +1 -1
- package/dist/transport/transfer-tier.d.ts +60 -0
- package/dist/transport/transfer-tier.d.ts.map +1 -0
- package/dist/transport/transfer-tier.js +22 -0
- package/dist/transport/transfer-tier.js.map +1 -0
- package/package.json +2 -2
- package/src/index.ts +7 -0
- package/src/protocol/api.ts +17 -1
- package/src/protocol/pairing.ts +56 -3
- package/src/transport/transfer-tier.ts +64 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,45 @@
|
|
|
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.4.0] - 2026-10-02
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **传输档位**(`TransferTier` / `transferTierOf` / `MESSAGE_DETAIL_CAPABILITY`):把
|
|
11
|
+
「直连 / 中继」的判定再映射一层成**可执行的传输档位** —— `direct` → `full`(完整),
|
|
12
|
+
`relay` → `lean`(精简),**`unknown` → `lean`**(拍板:开局那一屏消息窗口才是全量最大的一笔,
|
|
13
|
+
宁可把「其实是直连」的链路先按精简发)。这是两端共用的唯一一份口径:电脑端据此裁剪下行正文,
|
|
14
|
+
手机端据此解释「工具输出为什么是空的」并显示当前档位。
|
|
15
|
+
- `MessageDTO.detail`(可选):正文完整性标记。当前只有 `'omitted'` = 正文被档位有意省略
|
|
16
|
+
(**不是**「这次调用没有输出」)。消费方判「有没有这个字段」,不要穷举取值。
|
|
17
|
+
- `MESSAGE_DETAIL_CAPABILITY`(`'message.detail'`):裁剪的**能力闸门**名。手机端声明它 = 能渲染
|
|
18
|
+
省略标记;电脑端列出它 = 会按档位裁剪并打标记。两边缺一就继续全量发送。
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
|
|
22
|
+
- **向后兼容**:`MessageDTO.detail` 为可选字段,旧消费方忽略它即可;但对**旧手机端**而言,
|
|
23
|
+
被省略的正文会被显示成「这次调用没有输出」—— 故裁剪**必须**由上面的能力名挡住,
|
|
24
|
+
旧手机端(未声明该能力)继续收全量,不会出现这句假话。
|
|
25
|
+
- 本轮**只加口径,不加行为**:协议表、帧格式、方法表均无破坏性变更;0.3.0 的导出面保持不变。
|
|
26
|
+
- 已知代价(如实记录):非 WebRTC 链路(同源 Broadcast / 测试注入)永远读不到候选对,
|
|
27
|
+
于是永远落在 `lean`;真机上也存在「其实是直连但首个候选对未定形」的几秒。
|
|
28
|
+
|
|
29
|
+
## [0.3.0] - 2026-10-01
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **配对链接**(`buildPairingUrl` / `PAIRING_URL_BASE` / `PAIRING_URL_PARAM`):二维码内容改为
|
|
34
|
+
`https://virlen.cn/mobile?t=<配对数据>`。这样**系统相机 / 微信 / 任意浏览器**扫码就能直接打开
|
|
35
|
+
手机端并自动配对,无需先装 App 再扫码;App 内的扫码器则从 URL 里取回 `t`。
|
|
36
|
+
- `parsePairingPayload` **新增识 URL**:输入是 URL 时自动取 `?t=` 再解析。三种输入都吃:
|
|
37
|
+
URL / `vrp1:` 混淆串 / 旧版明文 JSON。
|
|
38
|
+
|
|
39
|
+
### Notes
|
|
40
|
+
|
|
41
|
+
- 向后兼容:新版能解旧码与旧串;**旧版解不了新码**(消费方需同步升级)。
|
|
42
|
+
- `PAIRING_URL_BASE` 是部署地址,`buildPairingUrl` 的第二个参数 `base` 可覆盖(自建 / 换域名)。
|
|
43
|
+
- 无破坏性变更;0.2.0 的导出面保持不变。
|
|
44
|
+
|
|
6
45
|
## [0.2.0] - 2026-09-30
|
|
7
46
|
|
|
8
47
|
### Changed
|
|
@@ -94,3 +133,5 @@
|
|
|
94
133
|
[0.1.1]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.1
|
|
95
134
|
[0.1.2]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.2
|
|
96
135
|
[0.2.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.2.0
|
|
136
|
+
[0.3.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.3.0
|
|
137
|
+
[0.4.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.4.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
|
-
|
|
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 =
|
|
116
|
-
const back = parsePairingPayload(qrText) //
|
|
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
|
-
>
|
|
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`
|
|
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';
|
|
@@ -41,6 +41,8 @@ export { RtcTransport } from './transport/rtc.js';
|
|
|
41
41
|
export type { RtcTransportOptions } from './transport/rtc.js';
|
|
42
42
|
export { LINK_KIND_POLL_MS, classifyLinkKind, probeLinkKind, pickCandidatePair, findCandidate, LinkKindWatcher, } from './transport/link-kind.js';
|
|
43
43
|
export type { LinkKind, LinkStatsEntry, StatsReportLike, StatsProvider, } from './transport/link-kind.js';
|
|
44
|
+
export { MESSAGE_DETAIL_CAPABILITY, transferTierOf } from './transport/transfer-tier.js';
|
|
45
|
+
export type { TransferTier } from './transport/transfer-tier.js';
|
|
44
46
|
export { ICE_CONFIG_VERSION, ICE_API_PATH, ICE_CUSTOM_STORAGE_KEY, ICE_REMOTE_STORAGE_KEY, ICE_CACHE_TTL_MS, ICE_FETCH_TIMEOUT_MS, sanitizeIceServers, parseIceText, formatIceServers, describeIceSource, fetchIceServers, resolveIceServers, readCustomIceText, writeCustomIceText, } from './transport/ice.js';
|
|
45
47
|
export type { IceServerInit, IceSource, IceStoragePort, ResolvedIceServers, ParseIceResult, FetchIceOptions, ResolveIceOptions, } from './transport/ice.js';
|
|
46
48
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,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;AACnG,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC9D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAG7C,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;AAC5B,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAGtG,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,YAAY,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,WAAW,EACX,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAClF,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,eAAe,EACf,WAAW,EACX,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,aAAa,EACb,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACtD,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAEtG,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACtE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/F,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAG1D,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,MAAM,uBAAuB,CAAA;AAG9B,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;AACxB,YAAY,EACV,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,iBAAiB,CAAA"}
|
|
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';
|
|
@@ -31,6 +31,8 @@ export { SseSignalingClient, fetchRoomStatus, fetchHostOnlineMap } from './trans
|
|
|
31
31
|
export { RtcTransport } from './transport/rtc.js';
|
|
32
32
|
// ── RTC 链路类型判定(直连 / 中继,两端同一份口径)──
|
|
33
33
|
export { LINK_KIND_POLL_MS, classifyLinkKind, probeLinkKind, pickCandidatePair, findCandidate, LinkKindWatcher, } from './transport/link-kind.js';
|
|
34
|
+
// ── 传输档位(链路类型 → 发不发详细内容,两端同一份口径)──
|
|
35
|
+
export { MESSAGE_DETAIL_CAPABILITY, transferTierOf } from './transport/transfer-tier.js';
|
|
34
36
|
// ── ICE 配置(M7,§31):客户端不再内置任何 TURN 凭证 ──
|
|
35
37
|
export { ICE_CONFIG_VERSION, ICE_API_PATH, ICE_CUSTOM_STORAGE_KEY, ICE_REMOTE_STORAGE_KEY, ICE_CACHE_TTL_MS, ICE_FETCH_TIMEOUT_MS, sanitizeIceServers, parseIceText, formatIceServers, describeIceSource, fetchIceServers, resolveIceServers, readCustomIceText, writeCustomIceText, } from './transport/ice.js';
|
|
36
38
|
//# sourceMappingURL=index.js.map
|
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,oCAAoC;AACpC,OAAO,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAGrF,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"}
|
package/dist/protocol/api.d.ts
CHANGED
|
@@ -39,9 +39,25 @@ export interface SessionSummaryDTO {
|
|
|
39
39
|
export interface MessageDTO {
|
|
40
40
|
id: string;
|
|
41
41
|
role: 'user' | 'assistant' | 'tool' | 'system';
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* 纯文本投影;图片等富内容一期剥离为占位符(§7-⑦)。空串 = 无正文(手机端不渲染空气泡)。
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ 空串有**两种**来源,靠 `detail` 区分:本来就没有正文,与**正文被传输档位省略**
|
|
46
|
+
* (中继 / 类型未知时工具输出不下发,见共享包的 `TransferTier`)。把后者当成前者,
|
|
47
|
+
* 就是给用户写一句不成立的结论。
|
|
48
|
+
*/
|
|
43
49
|
text: string;
|
|
44
50
|
createdAt: number;
|
|
51
|
+
/**
|
|
52
|
+
* 正文完整性标记(**可选**,只在正文被有意省略时出现)。
|
|
53
|
+
*
|
|
54
|
+
* `'omitted'`:`text` 为空是**档位决定**,不是「这次调用没有输出」—— 工具输出确实存在,
|
|
55
|
+
* 只是这条链路(TURN 中继 / 类型未知)按精简档不下发(§33)。手机端据此显示「输出已省略」。
|
|
56
|
+
*
|
|
57
|
+
* ⚠️ 消费方**不要穷举取值**:判「有没有这个字段」,不认识的值一律按「正文不完整」处理 ——
|
|
58
|
+
* 将来增加别的完整性档(如截断)时,旧消费方仍应给出诚实的提示而不是当成完整正文。
|
|
59
|
+
*/
|
|
60
|
+
detail?: 'omitted';
|
|
45
61
|
/**
|
|
46
62
|
* 工具名(仅 `role:'tool'`,如 `read_file`)。
|
|
47
63
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAIvD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAA;IAC9C
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AA+
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AA+NA,uCAAuC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAO,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAkSrC,MAAM,UAAU,YAAY,CAAI,QAAkB;IAChD,OAAO;QACL,IAAI,CAA6B,MAAS,EAAE,MAAsB,EAAE,SAAkB;YACpF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAA4B,CAAA;QACtH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,QAAkB;IACpD,OAAO;QACL,SAAS,CAA6B,KAAQ,EAAE,QAAiC;YAC/E,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,QAAsC,CAAC,CAAA;QAC1E,CAAC;KACF,CAAA;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAI,QAAkB,EAAE,IAAoB;IAC1E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,MAA0B,CAAmD,CAAA;QAC7F,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC"}
|
|
@@ -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
|
-
*
|
|
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":"
|
|
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"}
|
package/dist/protocol/pairing.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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 =
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/protocol/pairing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC,sEAAsE;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAA;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAA;AAEjD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,0BAA0B,CAAA;AAE1D,yBAAyB;AACzB,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAA;AAEpC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;AACrC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;AAErC;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAA;AAgB5E,iDAAiD;AACjD,MAAM,UAAU,mBAAmB,CAAC,KAKnC;IACC,OAAO;QACL,CAAC,EAAE,uBAAuB;QAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAA;AACH,CAAC;AAED,0EAA0E;AAE1E,MAAM,YAAY,GAAG,kEAAkE,CAAA;AAEvF,8CAA8C;AAC9C,MAAM,UAAU,GAAe,CAAC,GAAG,EAAE;IACnC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACtF,OAAO,KAAK,CAAA;AACd,CAAC,CAAC,EAAE,CAAA;AAEJ,mCAAmC;AACnC,SAAS,OAAO,CAAC,KAAiB;IAChC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,gBAAgB,CAAA;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAE,CAAA;IACrF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,oDAAoD;AACpD,SAAS,eAAe,CAAC,KAAiB;IACxC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QACpB,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QAC3D,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QAC3D,GAAG,IAAI,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QAC5B,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC1D,IAAI,EAAE,KAAK,SAAS;YAAE,MAAK;QAC3B,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC1D,IAAI,EAAE,KAAK,SAAS;YAAE,MAAK;QAC3B,GAAG,IAAI,YAAY,CAAC,EAAE,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,0DAA0D;AAC1D,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAC7B,MAAM,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7C,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QACtB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IACpD,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;QACrB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACxB,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QACvC,IAAI,EAAE,KAAK,SAAS;YAAE,MAAK;QAC3B,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QAChD,IAAI,EAAE,KAAK,SAAS;YAAE,MAAK;QAC3B,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAA;IACpC,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC3B,CAAC;AAED,+DAA+D;AAC/D,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5E,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,OAAO,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;AAC3C,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,oBAAoB,CAAC,OAAuB;IAC1D,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IACxD,OAAO,0BAA0B,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB,EAAE,IAAI,GAAW,gBAAgB;IACtF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAA;IACtE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAAC,IAAY;IACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;QAC9D,OAAO,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IACzD,8BAA8B;IAC9B,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC9B,gEAAgE;IAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5F,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,GAAY,CAAA;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChD,MAAM,GAAG,GAAG,GAA8B,CAAA;IAC1C,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IACjE,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAA;IACrE,MAAM,OAAO,GAAmB;QAC9B,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;QACrB,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;QACzF,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;KAC1B,CAAA;IACD,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAC3F,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IACnF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,aAAa,CAAC,OAAuB;IACnD,OAAO,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAC9C,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 传输档位 —— 「这条链路上,详细内容发不发」。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么要有这一层
|
|
5
|
+
*
|
|
6
|
+
* 「直连 / 中继」此前只影响**界面上一枚胶囊**(`link-kind.ts`):用户看到「TURN 中继」,知道慢,
|
|
7
|
+
* 但慢的代价其实落在手机上 —— 中继意味着**每一个字节都要经 TURN 服务器绕一圈**,而手机那头
|
|
8
|
+
* 常常是蜂窝流量。手机上真正的大头也不是聊天正文,而是**工具输出**(`git diff` / 命令回显 /
|
|
9
|
+
* 文件片段,单条动辄数 KB,一屏历史里几十条)。
|
|
10
|
+
*
|
|
11
|
+
* 于是把链路类型**再映射一层**成可执行的档位:
|
|
12
|
+
* - `full`(完整):该发什么发什么;
|
|
13
|
+
* - `lean`(精简):只发「主要内容」;详细内容(当前 = `role:'tool'` 的输出正文)由电脑端
|
|
14
|
+
* 在**投影时**省略(`toMessageDTO`),并带上 `MessageDTO.detail = 'omitted'`,
|
|
15
|
+
* 让手机端如实显示「输出已省略」,而不是骗人的「这次调用没有输出」。
|
|
16
|
+
*
|
|
17
|
+
* ## 口径为什么也必须只有一份
|
|
18
|
+
*
|
|
19
|
+
* 档位是**两端都会读**的:电脑端据此裁剪(发送侧 —— 唯一能真正省下流量的地方),
|
|
20
|
+
* 手机端据此解释「工具输出为什么是空的」并在通讯面板上显示当前档位。两端口径一旦漂移,
|
|
21
|
+
* 用户看到的就是自相矛盾(电脑按精简发、手机写着「完整传输」)—— 与 `link-kind.ts`
|
|
22
|
+
* 当初收敛到本包是同一条理由:**这里即那份唯一实现。**
|
|
23
|
+
*
|
|
24
|
+
* ## 判定口径(用户 2026-10-02 拍板)
|
|
25
|
+
*
|
|
26
|
+
* - `direct`(P2P 直连)→ `full`:字节不经服务器,省它没有意义,信息完整优先;
|
|
27
|
+
* - `relay`(TURN 中继)→ `lean`:字节要过服务器(手机流量 + 服务器带宽),砍详细内容;
|
|
28
|
+
* - `unknown`(还没结论 / 非 WebRTC 链路)→ **也是 `lean`**:宁可把「其实一直是直连」的链路
|
|
29
|
+
* 先按精简发,也不要在开局那一屏白白走完整档 —— 而开局那一屏恰好是全量最大的一笔
|
|
30
|
+
* (手机连上就拉最近一窗消息,里面全是历史工具输出)。
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ **代价如实记在这里**:`unknown` 是常态之一 —— 非 WebRTC 链路(同源 Broadcast 联调 /
|
|
33
|
+
* 测试注入的链路)永远读不到候选对,于是永远按精简发;真机上也存在「其实是直连、但首个
|
|
34
|
+
* 候选对还没定形」的几秒。这些情形下用户看到的是「工具输出已省略」而不是崩溃或空屏,
|
|
35
|
+
* 重开会话(重拉窗口)或链路判定为直连之后新产生的数据都会是完整的。
|
|
36
|
+
* **不要**把「联调时工具输出是空的」当成 bug 去修。
|
|
37
|
+
*/
|
|
38
|
+
import type { LinkKind } from './link-kind.js';
|
|
39
|
+
/** 传输档位:`full` = 完整(含详细内容);`lean` = 精简(只发主要内容)。 */
|
|
40
|
+
export type TransferTier = 'full' | 'lean';
|
|
41
|
+
/**
|
|
42
|
+
* 两端都要声明/列出的**能力名**(手机端写进 `hello.capabilities`;电脑端写进 ACL 能力集)。
|
|
43
|
+
*
|
|
44
|
+
* 两端各表一半(能力协商本来就是「我会什么」的交集):
|
|
45
|
+
* - 手机端声明它 = 「我能渲染 `MessageDTO.detail` 的省略标记」;
|
|
46
|
+
* - 电脑端列出它 = 「我会按档位裁剪正文,并在裁掉时打标记」。
|
|
47
|
+
*
|
|
48
|
+
* **为什么裁剪必须被它挡住,而不能无条件裁**:已部署的旧手机端不认 `detail` 字段 ——
|
|
49
|
+
* 它的工具卡片会把「正文为空」显示成「这次调用没有输出」,而那是**假话**(工具有输出,
|
|
50
|
+
* 只是被省略了)。宁可对旧手机端继续全量发送,也不要在它的界面上写一句不成立的结论;
|
|
51
|
+
* 反过来,旧电脑端不认识这个能力名,新手机端也只是继续收完整正文,同样不会出问题。
|
|
52
|
+
*/
|
|
53
|
+
export declare const MESSAGE_DETAIL_CAPABILITY = "message.detail";
|
|
54
|
+
/**
|
|
55
|
+
* 链路类型 → 传输档位(**两端唯一口径**)。
|
|
56
|
+
*
|
|
57
|
+
* 纯函数:喂一个 `LinkKind` 即可,不碰 DOM、不碰 store,故可完整单测。
|
|
58
|
+
*/
|
|
59
|
+
export declare function transferTierOf(kind: LinkKind): TransferTier;
|
|
60
|
+
//# sourceMappingURL=transfer-tier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-tier.d.ts","sourceRoot":"","sources":["../../src/transport/transfer-tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AAEzD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAE3D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 两端都要声明/列出的**能力名**(手机端写进 `hello.capabilities`;电脑端写进 ACL 能力集)。
|
|
3
|
+
*
|
|
4
|
+
* 两端各表一半(能力协商本来就是「我会什么」的交集):
|
|
5
|
+
* - 手机端声明它 = 「我能渲染 `MessageDTO.detail` 的省略标记」;
|
|
6
|
+
* - 电脑端列出它 = 「我会按档位裁剪正文,并在裁掉时打标记」。
|
|
7
|
+
*
|
|
8
|
+
* **为什么裁剪必须被它挡住,而不能无条件裁**:已部署的旧手机端不认 `detail` 字段 ——
|
|
9
|
+
* 它的工具卡片会把「正文为空」显示成「这次调用没有输出」,而那是**假话**(工具有输出,
|
|
10
|
+
* 只是被省略了)。宁可对旧手机端继续全量发送,也不要在它的界面上写一句不成立的结论;
|
|
11
|
+
* 反过来,旧电脑端不认识这个能力名,新手机端也只是继续收完整正文,同样不会出问题。
|
|
12
|
+
*/
|
|
13
|
+
export const MESSAGE_DETAIL_CAPABILITY = 'message.detail';
|
|
14
|
+
/**
|
|
15
|
+
* 链路类型 → 传输档位(**两端唯一口径**)。
|
|
16
|
+
*
|
|
17
|
+
* 纯函数:喂一个 `LinkKind` 即可,不碰 DOM、不碰 store,故可完整单测。
|
|
18
|
+
*/
|
|
19
|
+
export function transferTierOf(kind) {
|
|
20
|
+
return kind === 'direct' ? 'full' : 'lean';
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=transfer-tier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-tier.js","sourceRoot":"","sources":["../../src/transport/transfer-tier.ts"],"names":[],"mappings":"AA0CA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,CAAA;AAEzD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;AAC5C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virlen-remote",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Virlen 手机控制 · 协议栈共享包 —— 帧 / RPC / 错误 / 能力协商 / 设备身份与授权凭证 / 配对载荷 / ICE 解析 / Transport(WebRTC / 内存 / Broadcast)",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Virlen 手机控制 · 协议栈共享包 —— 帧 / RPC / 错误 / 能力协商 / 设备身份与授权凭证 / 配对载荷 / ICE 解析 / 链路类型与传输档位 / Transport(WebRTC / 内存 / Broadcast)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "WeiChens",
|
|
7
7
|
"keywords": [
|
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'
|
|
@@ -144,6 +147,10 @@ export type {
|
|
|
144
147
|
StatsProvider,
|
|
145
148
|
} from './transport/link-kind'
|
|
146
149
|
|
|
150
|
+
// ── 传输档位(链路类型 → 发不发详细内容,两端同一份口径)──
|
|
151
|
+
export { MESSAGE_DETAIL_CAPABILITY, transferTierOf } from './transport/transfer-tier'
|
|
152
|
+
export type { TransferTier } from './transport/transfer-tier'
|
|
153
|
+
|
|
147
154
|
// ── ICE 配置(M7,§31):客户端不再内置任何 TURN 凭证 ──
|
|
148
155
|
export {
|
|
149
156
|
ICE_CONFIG_VERSION,
|
package/src/protocol/api.ts
CHANGED
|
@@ -45,9 +45,25 @@ export interface SessionSummaryDTO {
|
|
|
45
45
|
export interface MessageDTO {
|
|
46
46
|
id: string
|
|
47
47
|
role: 'user' | 'assistant' | 'tool' | 'system'
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* 纯文本投影;图片等富内容一期剥离为占位符(§7-⑦)。空串 = 无正文(手机端不渲染空气泡)。
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ 空串有**两种**来源,靠 `detail` 区分:本来就没有正文,与**正文被传输档位省略**
|
|
52
|
+
* (中继 / 类型未知时工具输出不下发,见共享包的 `TransferTier`)。把后者当成前者,
|
|
53
|
+
* 就是给用户写一句不成立的结论。
|
|
54
|
+
*/
|
|
49
55
|
text: string
|
|
50
56
|
createdAt: number
|
|
57
|
+
/**
|
|
58
|
+
* 正文完整性标记(**可选**,只在正文被有意省略时出现)。
|
|
59
|
+
*
|
|
60
|
+
* `'omitted'`:`text` 为空是**档位决定**,不是「这次调用没有输出」—— 工具输出确实存在,
|
|
61
|
+
* 只是这条链路(TURN 中继 / 类型未知)按精简档不下发(§33)。手机端据此显示「输出已省略」。
|
|
62
|
+
*
|
|
63
|
+
* ⚠️ 消费方**不要穷举取值**:判「有没有这个字段」,不认识的值一律按「正文不完整」处理 ——
|
|
64
|
+
* 将来增加别的完整性档(如截断)时,旧消费方仍应给出诚实的提示而不是当成完整正文。
|
|
65
|
+
*/
|
|
66
|
+
detail?: 'omitted'
|
|
51
67
|
/**
|
|
52
68
|
* 工具名(仅 `role:'tool'`,如 `read_file`)。
|
|
53
69
|
*
|
package/src/protocol/pairing.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
224
|
+
// 先取出配对数据本体(URL → `?t=`;否则原样)
|
|
225
|
+
const code = pairingCodeFromText(text.trim())
|
|
226
|
+
if (code === null) return null
|
|
174
227
|
// 带前缀 → 先解混淆;否则按旧版明文 JSON 解析(向后兼容,见 PAIRING_OBFUSCATION_PREFIX)
|
|
175
|
-
const jsonText =
|
|
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 {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 传输档位 —— 「这条链路上,详细内容发不发」。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么要有这一层
|
|
5
|
+
*
|
|
6
|
+
* 「直连 / 中继」此前只影响**界面上一枚胶囊**(`link-kind.ts`):用户看到「TURN 中继」,知道慢,
|
|
7
|
+
* 但慢的代价其实落在手机上 —— 中继意味着**每一个字节都要经 TURN 服务器绕一圈**,而手机那头
|
|
8
|
+
* 常常是蜂窝流量。手机上真正的大头也不是聊天正文,而是**工具输出**(`git diff` / 命令回显 /
|
|
9
|
+
* 文件片段,单条动辄数 KB,一屏历史里几十条)。
|
|
10
|
+
*
|
|
11
|
+
* 于是把链路类型**再映射一层**成可执行的档位:
|
|
12
|
+
* - `full`(完整):该发什么发什么;
|
|
13
|
+
* - `lean`(精简):只发「主要内容」;详细内容(当前 = `role:'tool'` 的输出正文)由电脑端
|
|
14
|
+
* 在**投影时**省略(`toMessageDTO`),并带上 `MessageDTO.detail = 'omitted'`,
|
|
15
|
+
* 让手机端如实显示「输出已省略」,而不是骗人的「这次调用没有输出」。
|
|
16
|
+
*
|
|
17
|
+
* ## 口径为什么也必须只有一份
|
|
18
|
+
*
|
|
19
|
+
* 档位是**两端都会读**的:电脑端据此裁剪(发送侧 —— 唯一能真正省下流量的地方),
|
|
20
|
+
* 手机端据此解释「工具输出为什么是空的」并在通讯面板上显示当前档位。两端口径一旦漂移,
|
|
21
|
+
* 用户看到的就是自相矛盾(电脑按精简发、手机写着「完整传输」)—— 与 `link-kind.ts`
|
|
22
|
+
* 当初收敛到本包是同一条理由:**这里即那份唯一实现。**
|
|
23
|
+
*
|
|
24
|
+
* ## 判定口径(用户 2026-10-02 拍板)
|
|
25
|
+
*
|
|
26
|
+
* - `direct`(P2P 直连)→ `full`:字节不经服务器,省它没有意义,信息完整优先;
|
|
27
|
+
* - `relay`(TURN 中继)→ `lean`:字节要过服务器(手机流量 + 服务器带宽),砍详细内容;
|
|
28
|
+
* - `unknown`(还没结论 / 非 WebRTC 链路)→ **也是 `lean`**:宁可把「其实一直是直连」的链路
|
|
29
|
+
* 先按精简发,也不要在开局那一屏白白走完整档 —— 而开局那一屏恰好是全量最大的一笔
|
|
30
|
+
* (手机连上就拉最近一窗消息,里面全是历史工具输出)。
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ **代价如实记在这里**:`unknown` 是常态之一 —— 非 WebRTC 链路(同源 Broadcast 联调 /
|
|
33
|
+
* 测试注入的链路)永远读不到候选对,于是永远按精简发;真机上也存在「其实是直连、但首个
|
|
34
|
+
* 候选对还没定形」的几秒。这些情形下用户看到的是「工具输出已省略」而不是崩溃或空屏,
|
|
35
|
+
* 重开会话(重拉窗口)或链路判定为直连之后新产生的数据都会是完整的。
|
|
36
|
+
* **不要**把「联调时工具输出是空的」当成 bug 去修。
|
|
37
|
+
*/
|
|
38
|
+
import type { LinkKind } from './link-kind'
|
|
39
|
+
|
|
40
|
+
/** 传输档位:`full` = 完整(含详细内容);`lean` = 精简(只发主要内容)。 */
|
|
41
|
+
export type TransferTier = 'full' | 'lean'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 两端都要声明/列出的**能力名**(手机端写进 `hello.capabilities`;电脑端写进 ACL 能力集)。
|
|
45
|
+
*
|
|
46
|
+
* 两端各表一半(能力协商本来就是「我会什么」的交集):
|
|
47
|
+
* - 手机端声明它 = 「我能渲染 `MessageDTO.detail` 的省略标记」;
|
|
48
|
+
* - 电脑端列出它 = 「我会按档位裁剪正文,并在裁掉时打标记」。
|
|
49
|
+
*
|
|
50
|
+
* **为什么裁剪必须被它挡住,而不能无条件裁**:已部署的旧手机端不认 `detail` 字段 ——
|
|
51
|
+
* 它的工具卡片会把「正文为空」显示成「这次调用没有输出」,而那是**假话**(工具有输出,
|
|
52
|
+
* 只是被省略了)。宁可对旧手机端继续全量发送,也不要在它的界面上写一句不成立的结论;
|
|
53
|
+
* 反过来,旧电脑端不认识这个能力名,新手机端也只是继续收完整正文,同样不会出问题。
|
|
54
|
+
*/
|
|
55
|
+
export const MESSAGE_DETAIL_CAPABILITY = 'message.detail'
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 链路类型 → 传输档位(**两端唯一口径**)。
|
|
59
|
+
*
|
|
60
|
+
* 纯函数:喂一个 `LinkKind` 即可,不碰 DOM、不碰 store,故可完整单测。
|
|
61
|
+
*/
|
|
62
|
+
export function transferTierOf(kind: LinkKind): TransferTier {
|
|
63
|
+
return kind === 'direct' ? 'full' : 'lean'
|
|
64
|
+
}
|