@linxin666/dsh-remote-web-ui 0.2.8 → 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/LICENSE +202 -28
- package/README.i18n.yaml +2 -2
- package/README.md +51 -15
- package/README.zh.md +9 -4
- package/lib/client.js +65 -34
- package/lib/client.js.map +1 -1
- package/lib/index.js +667 -77
- package/lib/mobile.js +874 -64
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +35 -5
- package/lib/types/client/remote-channel.d.ts +4 -5
- package/lib/types/client/remote-channel.d.ts.map +1 -1
- package/lib/types/client/remote-channel.js +13 -28
- package/lib/types/http.d.ts +37 -8
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/http.js +76 -11
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +40 -5
- package/lib/types/mobile/api.d.ts +57 -0
- package/lib/types/mobile/api.d.ts.map +1 -1
- package/lib/types/mobile/api.js +29 -0
- package/lib/types/mobile/markdown.d.ts +7 -1
- package/lib/types/mobile/markdown.d.ts.map +1 -1
- package/lib/types/mobile/markdown.js +16 -7
- package/lib/types/mobile/markdown.test.js +8 -0
- package/lib/types/mobile/mobile-styles.d.ts +1 -1
- package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
- package/lib/types/mobile/mobile-styles.js +325 -6
- package/lib/types/mobile/views/ChatView.d.ts +10 -0
- package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
- package/lib/types/mobile/views/ChatView.js +205 -8
- package/lib/types/mobile/views/ChatView.stream.test.d.ts +2 -0
- package/lib/types/mobile/views/ChatView.stream.test.d.ts.map +1 -0
- package/lib/types/mobile/views/ChatView.stream.test.js +205 -0
- package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -1
- package/lib/types/mobile/views/WorkspaceView.js +46 -5
- package/lib/types/mobile/views/WorkspaceView.test.js +129 -2
- package/lib/types/mobile-api.d.ts +9 -3
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +61 -16
- package/lib/types/mobile-pending.d.ts +38 -0
- package/lib/types/mobile-pending.d.ts.map +1 -0
- package/lib/types/mobile-pending.js +59 -0
- package/lib/types/paired-model-catalog.d.ts +17 -0
- package/lib/types/paired-model-catalog.d.ts.map +1 -0
- package/lib/types/paired-model-catalog.js +252 -0
- package/lib/types/pairing.d.ts +13 -4
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +13 -5
- package/lib/types/remote-api.d.ts +13 -4
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +27 -17
- package/lib/types/remote-channel-boot.d.ts +26 -0
- package/lib/types/remote-channel-boot.d.ts.map +1 -0
- package/lib/types/remote-channel-boot.js +128 -0
- package/lib/types/remote-channel-rules.d.ts +40 -0
- package/lib/types/remote-channel-rules.d.ts.map +1 -0
- package/lib/types/remote-channel-rules.js +32 -0
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +12 -23
- package/lib/types/uuid-polyfill.d.ts +10 -0
- package/lib/types/uuid-polyfill.d.ts.map +1 -0
- package/lib/types/uuid-polyfill.js +27 -0
- package/package.json +1 -1
- package/src/client/index.ts +41 -5
- package/src/client/remote-channel.ts +18 -28
- package/src/http.ts +87 -12
- package/src/index.ts +42 -6
- package/src/mobile/api.ts +91 -0
- package/src/mobile/markdown.test.ts +9 -0
- package/src/mobile/markdown.ts +17 -7
- package/src/mobile/mobile-styles.ts +326 -6
- package/src/mobile/views/ChatView.stream.test.tsx +231 -0
- package/src/mobile/views/ChatView.tsx +328 -10
- package/src/mobile/views/WorkspaceView.test.tsx +163 -2
- package/src/mobile/views/WorkspaceView.tsx +152 -26
- package/src/mobile-api.ts +59 -17
- package/src/mobile-pending.ts +88 -0
- package/src/paired-model-catalog.ts +224 -0
- package/src/pairing.ts +13 -5
- package/src/remote-api.ts +34 -18
- package/src/remote-channel-boot.ts +131 -0
- package/src/remote-channel-rules.ts +62 -0
- package/src/routes.ts +13 -24
- package/src/uuid-polyfill.ts +28 -0
package/LICENSE
CHANGED
|
@@ -1,28 +1,202 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
1.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 2007aecc144d421951a3aebd9d94708746e1f549
|
|
6
|
+
README.zh.md: 85a76a5df45f3cb2d4ec0a477e65affd78c98795
|
package/README.md
CHANGED
|
@@ -44,7 +44,8 @@ that probes and runs the update.
|
|
|
44
44
|
their next request. Pairing is this plugin's access control for its own
|
|
45
45
|
remote channels: `/m/api` for the phone and `/remote` for a desktop
|
|
46
46
|
browser opened at a non-loopback origin. Unpaired `/remote` callers
|
|
47
|
-
are refused before the request body is read
|
|
47
|
+
are refused before the request body is read while `requirePairingForLan` is
|
|
48
|
+
on (default). Loopback (127.0.0.1) keeps using
|
|
48
49
|
`/api` directly. The default remote-desktop path does not use
|
|
49
50
|
`--trusted-host`: the connection plugin's `/api` fence stays closed for
|
|
50
51
|
public and LAN hosts, and the paired PC rides `/remote` instead.
|
|
@@ -52,18 +53,40 @@ that probes and runs the update.
|
|
|
52
53
|
itself — pairing does not gate `/api` (no plugin can; the fence is the
|
|
53
54
|
SDK's own seam). The posture probe below reports that stance when `/api`
|
|
54
55
|
is reachable without pairing.
|
|
56
|
+
- **Paired model catalog**: authenticated paired devices can use `GET
|
|
57
|
+
/api/pair/model-catalog`, `POST /api/pair/model-catalog/discover`, and
|
|
58
|
+
`POST /api/pair/model-catalog/upsert` to inspect and adopt models for an
|
|
59
|
+
existing active `llm-pi-ai` provider only. The capability fixes the
|
|
60
|
+
provider settings address internally and cannot create providers or read or
|
|
61
|
+
change credentials, general settings, endpoints, headers, or arbitrary
|
|
62
|
+
configuration. The exact routes are served ahead of the connection
|
|
63
|
+
plugin's `/api` prefix, so a paired LAN or tunnel client may call them
|
|
64
|
+
directly, and they carry the same Host/Origin trust fence as the sibling
|
|
65
|
+
`/api/pair` endpoints: loopback, the advertised LAN literals, or the
|
|
66
|
+
configured public base URL. Through the `/remote` desktop channel all
|
|
67
|
+
`/api/pair/*` paths stay loopback-only. Adoption is refused while the
|
|
68
|
+
provider's live model catalog is unavailable or unknown. Stop or device
|
|
69
|
+
revocation disables it immediately; the
|
|
70
|
+
generic `settings.*`, `credentials.*`, and `llm.discoverModels` RPC methods
|
|
71
|
+
remain loopback-only.
|
|
55
72
|
- **Remote desktop channel**: with `requirePairingForLan` on (default), a
|
|
56
73
|
desktop Web GUI opened at the LAN URL or through the tunnel transparently
|
|
57
|
-
rides `/remote` — the same UI, gated by the same pairing cookie.
|
|
74
|
+
rides `/remote` — the same UI, gated by the same pairing cookie. The
|
|
75
|
+
rewrite installs twice-removed from the race it fixes: the host inlines a
|
|
76
|
+
small classic script right after the opening `<head>` tag
|
|
77
|
+
(`webserver/index-inject`), so fetch/WebSocket/EventSource and resource
|
|
78
|
+
`src` rewrites are active before any boot entry (the connection plugin
|
|
79
|
+
opens its event streams first); the browser half then adopts the installed
|
|
80
|
+
seat instead of patching twice. Loopback origins skip the patch entirely. Browser
|
|
58
81
|
requests under `/api`, `/sidebar`, `/git`, and `/pet`, including the known
|
|
59
82
|
event, terminal, and SSH WebSockets, are re-issued to the local web server
|
|
60
83
|
without forwarding the remote Origin or pairing cookie; the authenticated
|
|
61
84
|
proxy supplies its own same-origin browser marker for sibling route fences. The
|
|
62
85
|
SDK's loopback-only privileged methods (native dialogs, the settings and
|
|
63
|
-
credentials plane) stay unreachable from a paired remote desktop
|
|
64
|
-
`/api/pair
|
|
65
|
-
`/api/
|
|
66
|
-
endpoints stay loopback-only. Unpaired desktop browsers get a persistent
|
|
86
|
+
credentials plane) stay unreachable from a paired remote desktop;
|
|
87
|
+
`/api/pair/*` (including the paired model-catalog routes),
|
|
88
|
+
`/api/update/*`, `/api/plugin-manager/*`, `/api/dsh-desktop-launcher/*`
|
|
89
|
+
and `/api/dsh-web-ui-settings/*` control endpoints stay loopback-only. Unpaired desktop browsers get a persistent
|
|
67
90
|
full-page block instead of data (the page keys off the `unpaired` error
|
|
68
91
|
code, not every 403). The block retires once a gated call succeeds or the
|
|
69
92
|
channel itself is torn down — turning `requirePairingForLan` off (or the
|
|
@@ -222,9 +245,16 @@ need **no harness source changes** — the phone's RPC calls ride the
|
|
|
222
245
|
plugin's `/m/api` proxy (which delegates to the host ApiProxy service and
|
|
223
246
|
pages `session.list` itself), so the tunneled Host never has to enter the
|
|
224
247
|
connection plugin's trust fence. The phone is gated by its paired-device
|
|
225
|
-
cookie and an explicit method allowlist
|
|
226
|
-
|
|
227
|
-
|
|
248
|
+
cookie and an explicit method allowlist. The allowlist constrains the
|
|
249
|
+
`/m/api` proxy alone: the paired-device cookie itself also passes the global
|
|
250
|
+
api/gate, so a paired device is a full-control credential for the host `/api`
|
|
251
|
+
surface — settings/credentials/host-action domains stay unreachable only
|
|
252
|
+
because the SDK pins those privileged methods to loopback. Pairing is full
|
|
253
|
+
device trust; the `/m/api` proxy's model
|
|
254
|
+
reads/writes are limited to the advisory `session.models` /
|
|
255
|
+
`session.selectModel` pair. Separately, the exact paired-only model-catalog
|
|
256
|
+
routes may adopt models for an existing eligible `llm-pi-ai` provider; they
|
|
257
|
+
cannot create providers or access credentials or general settings. Agent preset
|
|
228
258
|
access to read-only `agentPreset.list`, creation to `session.create`
|
|
229
259
|
(workspace id plus an optional id from that roster — the phone never names a
|
|
230
260
|
working directory of its own), and the permission picker only ever sends the
|
|
@@ -244,12 +274,14 @@ over Server-Sent Events on `/m/api/events.mux`. The canonical `/m/` page owns a
|
|
|
244
274
|
- The `/m/` worker uses network-first static-shell fallback and waits for current pages to close before an updated worker activates. It bypasses `/m/api`, `/api`, SSE, and every write request.
|
|
245
275
|
- Installing this plugin routes fenced non-loopback desktop traffic onto the
|
|
246
276
|
gated `/remote` channel (see `requirePairingForLan` in `src/index.ts`).
|
|
247
|
-
|
|
248
|
-
remote device — the unpaired state shows a
|
|
249
|
-
data; loopback (127.0.0.1) is unaffected
|
|
250
|
-
`requirePairingForLan: false` in the profile patch to
|
|
251
|
-
plain `/api`
|
|
252
|
-
|
|
277
|
+
While the flag is on (default), a desktop browser opened via the LAN URL or
|
|
278
|
+
the tunnel must pair like any remote device — the unpaired state shows a
|
|
279
|
+
persistent blocking page instead of data; loopback (127.0.0.1) is unaffected
|
|
280
|
+
and keeps `/api`. Set `requirePairingForLan: false` in the profile patch to
|
|
281
|
+
keep the desktop on plain `/api` and have a leftover `/remote` rewrite
|
|
282
|
+
proxy unpaired calls (only useful with the open-LAN stance; loopback-only
|
|
283
|
+
paths stay denied) while keeping tokens/status/revocation. Note the
|
|
284
|
+
underlying `/api` route itself is the
|
|
253
285
|
SDK's: on a `--host 0.0.0.0` bind the SDK auto-trusts LAN literals, so a
|
|
254
286
|
LAN client bypassing the UI can still reach `/api` directly — the posture
|
|
255
287
|
probe reports that stance on the panel.
|
|
@@ -455,6 +487,10 @@ accept → UI. Only `publicBaseUrl` (plugin config) names the tunneled host;
|
|
|
455
487
|
`--trusted-host` is not part of this pairing flow. The desktop panel still
|
|
456
488
|
opens at `http://127.0.0.1`.
|
|
457
489
|
|
|
490
|
+
## Security model
|
|
491
|
+
|
|
492
|
+
- Mobile unary routes and the mux event stream require a live paired-device session. A missing or revoked session receives HTTP 403 with a JSON rejection carrying `error.code: "unpaired"`; the browser's `EventSource` API exposes only the stream failure, not that response body.
|
|
493
|
+
|
|
458
494
|
## Known Limitations and Deferred Work
|
|
459
495
|
|
|
460
496
|
- **Revocation is per-request**: a paired phone whose request is already in
|
package/README.zh.md
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
- **面板**:「远程访问」标题、「通过手机或另一台电脑配对,远程访问当前工作区」副标题、「设备配对」卡片(含状态区「等待设备连接」+ 状态徽标)、大号二维码、各带复制按钮的手机链接(`/m/?pair=...`)与电脑链接(`/?pair=...`)、停止 / 刷新二维码操作,以及已授权设备列表(根据 User-Agent 推断的设备名称、在线/离线、最近活动时间、单台取消配对)。界面不渲染作为会话凭据的设备 id 或原始 User-Agent。两条链接共用一枚一次性令牌,任意设备配对成功后另一条立即失效。
|
|
13
13
|
- **手机侧**:扫码将手机与一次性、限时令牌绑定,并落地到 **`/m/` 独立移动端界面**——一款专为小屏设计的轻客户端(见[截图](#截图)),而不是把桌面 UI 塞进手机。该页面可安装为 PWA;每个已安装应用使用自己的已配对设备 cookie,存储隔离的移动端 Web App 需要在该上下文打开新的二维码链接,或粘贴桌面端新复制的链接完成配对。链接携带 `workspace` 参数,手机落地到桌面正在查看的同一工作区。
|
|
14
14
|
- **PC 侧(远程桌面配对)**:同一份二维码链接和配对令牌也能配对 PC 浏览器——把手机侧配对流扩展到桌面 Web GUI。从面板复制链接,在另一台电脑的浏览器打开(局域网 URL 或隧道 URL 均可);接受往返后,完整 Web GUI 在那台设备上运行——工作区、会话、聊天、切模型——受围栏保护的同源流量走门控的 `/remote` 通道,而不是直接调用仅 loopback 的 host 路由。手机进 `/m/`,PC 进桌面 UI:一枚令牌、一套配对流、两种界面。未配对的 PC 只看到不渲染底层数据的完整阻断页,其中提供电脑配对步骤与「重新检测」操作。
|
|
15
|
-
- **安全**:一个有效的一次性令牌(刷新会使旧链接失效;已接受的令牌不可复用;令牌会过期)。停止会撤销每一台已配对设备与当前令牌——已配对设备在下一次请求时被切断。配对是本插件对自己远程通道的访问控制:手机走 `/m/api`,非 loopback origin 打开的桌面浏览器走 `/remote
|
|
16
|
-
-
|
|
15
|
+
- **安全**:一个有效的一次性令牌(刷新会使旧链接失效;已接受的令牌不可复用;令牌会过期)。停止会撤销每一台已配对设备与当前令牌——已配对设备在下一次请求时被切断。配对是本插件对自己远程通道的访问控制:手机走 `/m/api`,非 loopback origin 打开的桌面浏览器走 `/remote`。未配对调用方在 `requirePairingForLan` 开启(默认)时会在读取请求体之前就被 `/remote` 拒绝。loopback(127.0.0.1)继续直接使用 `/api`。默认的远程桌面路径不使用 `--trusted-host`:连接插件的 `/api` 围栏对公网和局域网主机保持关闭,已配对 PC 改走 `/remote`。`--trusted-host` 是 SDK 的另一种用法,会让该主机被信任访问 `/api` 本身——配对不门控 `/api`(任何插件都无法做到;围栏是 SDK 自己的接缝)。当 `/api` 无需配对即可到达时,下方姿态探测会报告该姿态。
|
|
16
|
+
- **已配对模型目录**:已认证的已配对设备可使用 `GET /api/pair/model-catalog`、`POST /api/pair/model-catalog/discover` 和 `POST /api/pair/model-catalog/upsert`,仅查看并采纳一个现有、活跃的 `llm-pi-ai` provider 的模型。该能力在内部固定 provider 的 settings 地址,不能创建 provider,也不能读取或修改凭据、通用 settings、端点、header 或任意配置。这三条精确路由在 connection 插件的 `/api` 前缀之前匹配,已配对的局域网或隧道客户端可直接调用,并套用与 `/api/pair` 兄弟端点相同的 Host/Origin 信任围栏:仅接受 loopback、广告的局域网字面量或配置的公网 base URL。经 `/remote` 桌面通道访问时,所有 `/api/pair/*` 路径仍保持仅 loopback。当 provider 的实时模型目录不可用或未知时,采纳会被拒绝。停止或撤销设备会立即禁用它;通用 `settings.*`、`credentials.*` 和 `llm.discoverModels` RPC 方法仍然仅限 loopback。
|
|
17
|
+
- **远程桌面通道**:`requirePairingForLan` 开启(默认)时,经局域网 URL 或隧道打开的桌面 Web GUI 透明地改走 `/remote`——同一套 UI,同一枚配对 cookie 门控。改写补丁在解析期即生效:宿主把一段内联 classic script 注入到 `<head>` 开标签之后(`webserver/index-inject`),使 fetch/WebSocket/EventSource 与资源 `src` 改写在任何启动条目执行前就绪(连接插件最先打开事件流);浏览器半边随后接管已安装的 seat 而非重复打补丁。loopback 来源完全跳过该补丁。浏览器中 `/api`、`/sidebar`、`/git`、`/pet` 下的请求,以及已知的事件、终端与 SSH WebSocket,会在不转发远程 Origin 或配对 cookie 的前提下重新发给本机 Web Server;认证反代会为兄弟路由围栏提供自己生成的同源浏览器标记。SDK 的 loopback 专属特权方法(原生对话框、settings 与 credentials 面)对已配对远程桌面保持不可达;`/api/pair/*`(包括三条模型目录路由)、`/api/update/*`、`/api/plugin-manager/*`、`/api/dsh-desktop-launcher/*` 与 `/api/dsh-web-ui-settings/*` 控制端点保持仅 loopback。未配对的桌面浏览器看到持久的全页阻断界面而不是数据(阻断页按错误码 `unpaired` 触发,不是任意 403)。阻断页在任一门控调用成功后,或通道本身被拆除时退出——把 `requirePairingForLan` 关掉(或关闭插件)也会清掉通道短暂激活期间弹出的提示。
|
|
17
18
|
- **姿态探测**:插件用伪造 Host 头(公网 base 与每个 LAN base)探测 SDK 的 `/api` 围栏。403 是默认姿态(围栏关闭,远程访问走配对)。任何非 403 结果——`--trusted-host`,或 `--host 0.0.0.0` 下 SDK 的局域网自动信任——都会以 CRITICAL 日志与配对面板红色横幅呈现,因此 SDK 的 `/api` 信任姿态是被看见的,而不是被假设的。探测失败会丢掉进行中的目标缓存,相同 origin 会在下次触发时重试。
|
|
18
19
|
- **实时状态**:桌面面板经 SSE 流实时镜像配对状态(等待 → 已连接 → 已断开),并展示已授权设备名单。
|
|
19
20
|
- **远程更新**:侧边栏加载后,底部的下载触发按钮(手机图标左侧)会静默探测 npm registry 上已安装的 `@linxin666/dsh-*` 全家桶版本;发现可自动更新的新版本时,按钮显示圆点并提供“发现新版本,检查更新”提示。点击按钮打开更新面板;未安装聚合包时,检查和更新覆盖 profile 中 registry 管理的全部 `@linxin666/*` 直接依赖,本地 link / file 开发依赖会被跳过。当存在较新版本时,面板展示版本对比并等待确认——点击「开始更新」后执行更新(在所属 dsh profile 内 `pnpm update --latest`;pnpm 缺失时依次回退 `corepack pnpm`、`npx --yes pnpm`,Windows 上经 `cmd.exe` 执行以解析 npm 安装的 `.cmd` shim;由仅 loopback 的 `/api/update/status` + `/api/update/run` 端点驱动)并请求重启 dsh web 以生效。pnpm 绿色退出后还会对照 registry 复核已装版本:绿色退出但版本纹丝不动(例如 pnpm 的 `minimumReleaseAge` 门禁静默跳过同日发布的新版本)会报告为「未更新成功」并附配置指引,而不是误报成功。锚点自身是本地 link 安装(开发模式)时,只报告 npm 状态而不更新。
|
|
@@ -71,13 +72,13 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-remote-web-ui
|
|
|
71
72
|
6. 桌面徽标实时翻到 已连接;手机离开时回落到离线/断开。
|
|
72
73
|
7. 刷新二维码 使旧链接失效并铸一枚新的。停止 撤销移动端访问:已配对设备下一次请求 403,包括其实时流。
|
|
73
74
|
|
|
74
|
-
该移动端界面完全自包含在本插件内:`/m/` 页面及其数据通道(`/m/api`)由插件自己的路由伺服,**无需任何 harness 源码改动**——手机的 RPC 调用走插件的 `/m/api` 代理(它委托给 host 的 ApiProxy 服务并自己分页 `session.list`),因此被隧道化的 Host 永远不必进入连接插件的信任围栏。手机受其已配对设备 cookie
|
|
75
|
+
该移动端界面完全自包含在本插件内:`/m/` 页面及其数据通道(`/m/api`)由插件自己的路由伺服,**无需任何 harness 源码改动**——手机的 RPC 调用走插件的 `/m/api` 代理(它委托给 host 的 ApiProxy 服务并自己分页 `session.list`),因此被隧道化的 Host 永远不必进入连接插件的信任围栏。手机受其已配对设备 cookie 与 `/m/api` 显式方法白名单门控(白名单只约束 `/m/api` 代理本身:该 cookie 同时通过全局 api/gate,对宿主 `/api` 面是全量控制凭证——settings/credentials/host-action 域仅因 SDK 把这些特权方法钉为仅 loopback 才不可达。配对即对设备的完全信任;`/m/api` 代理的模型读写限制于建议性的 `session.models` / `session.selectModel` 对;另外,精确的已配对模型目录路由可为一个现有、合格的 `llm-pi-ai` provider 采纳模型,但不能创建 provider,也不能访问凭据或通用 settings;Agent 预设访问限制于只读 `agentPreset.list`,创建限制于 `session.create`(工作区 id 加清单中的可选 id——手机绝不自命名工作目录),权限选择器只通过已放行的 `session.prompt` 发送模式无关的 `/permission` 命令);实时流在 `/m/api/events.mux` 上经 Server-Sent Events 送达。规范的 `/m/` 页面拥有同 scope 的 manifest 与 Service Worker;其缓存仅包含静态壳和离线页,绝不包含移动端 API 响应、会话数据或命令。
|
|
75
76
|
|
|
76
77
|
### 行为说明
|
|
77
78
|
|
|
78
79
|
- 移动端输入框默认 Enter 发送(Shift+Enter 换行)。在插件设置卡片(或 profile patch)把 `mobileEnterToSend` 设为 false 后,普通 Enter 改为插入换行,只有「发送」按钮会发送;手机打开聊天时经自己的 `/m/api` 偏好方法读取该开关。在支持 `field-sizing: content` 的浏览器上,输入框随草稿自动增高,最高 120px 封顶(两种模式一致)。
|
|
79
80
|
- `/m/` Worker 对静态壳使用 network-first 回退,并等待当前页面关闭后才激活更新。它旁路 `/m/api`、`/api`、SSE 与所有写请求。
|
|
80
|
-
- 安装本插件后,非 loopback 桌面中受围栏保护的流量会改走门控的 `/remote` 通道(见 `src/index.ts` 的 `requirePairingForLan
|
|
81
|
+
- 安装本插件后,非 loopback 桌面中受围栏保护的流量会改走门控的 `/remote` 通道(见 `src/index.ts` 的 `requirePairingForLan`)。在该开关开启(默认)时,经局域网 URL 或隧道打开的桌面浏览器必须像任何远程设备一样配对——未配对状态显示完整阻断页而不是数据;loopback(127.0.0.1)不受影响,继续使用原始路径。把 profile patch 里 `requirePairingForLan` 设为 false 可让桌面继续走普通 `/api`,残留的 `/remote` 改写也会放行未配对调用(仅在开放局域网姿态下有意义;loopback-only 路径仍被拒绝),同时保留令牌/状态/撤销。注意 `/api` 路由本体属于 SDK:`--host 0.0.0.0` 绑定时 SDK 自动信任局域网字面量,绕过 UI 的局域网客户端仍能直接访问 `/api`——姿态探测会在面板上报告该姿态。
|
|
81
82
|
- `/api` 之外的兄弟 host 路由(`/pet/*`、`/git/*`、右侧面板的 `/aionui-panel/*`)可查询本插件的 `remoteWebUiPairing` 服务:有效的已配对设备 cookie 是放行路径,`stop()` 仍会切断它们;未安装本插件时该服务不存在。
|
|
82
83
|
- 二维码链接基于机器的非内部 IPv4 字面量构建;多宿主主机(Wi-Fi + 有线,或代理/VPN 虚拟适配器)会显示单选器供你发布手机实际可达的网络。第一个字面量是默认值。设 `publicBaseUrl` 后,单选器在顶部额外加一项 公网地址——默认二维码改用公网 base,选中局域网字面量会重新铸一枚网内链接。
|
|
83
84
|
- 配置的 `publicBaseUrl` 本身满足可达绑定需求:`dsh web` 绑定 `127.0.0.1`(不带 `--host 0.0.0.0`)仍能经隧道铸出可用的公网二维码链接。
|
|
@@ -168,6 +169,10 @@ pnpm run build
|
|
|
168
169
|
|
|
169
170
|
公网路径是经隧道的同一往返(见「通过互联网远程访问」):loopback mint → 手机或 PC 打开公网二维码 URL → accept → UI。只有 `publicBaseUrl`(插件配置)命名隧道主机;`--trusted-host` 不属于这条配对流。桌面面板仍在 `http://127.0.0.1` 打开。
|
|
170
171
|
|
|
172
|
+
## 安全模型
|
|
173
|
+
|
|
174
|
+
- 移动端普通接口与 mux 事件流都要求有效的已配对设备会话。会话缺失或已撤销时返回 HTTP 403,并使用带 `error.code: "unpaired"` 的 JSON 拒绝信封;浏览器 `EventSource` API 只暴露事件流失败,不暴露该响应体。
|
|
175
|
+
|
|
171
176
|
## 已知限制与待办
|
|
172
177
|
|
|
173
178
|
- **撤销是逐请求的**:已配对手机请求已在 停止 落地时在途,完成该请求;下一个 403。
|
package/lib/client.js
CHANGED
|
@@ -3781,6 +3781,38 @@ window.__ModuleLoader__.load({
|
|
|
3781
3781
|
page.replaceState(`${url.pathname}${url.search}${url.hash}`);
|
|
3782
3782
|
}
|
|
3783
3783
|
//#endregion
|
|
3784
|
+
//#region src/remote-channel-rules.ts
|
|
3785
|
+
/**
|
|
3786
|
+
* The remote-channel rewrite contract as pure data (issue #987): both the
|
|
3787
|
+
* browser patch (client/remote-channel.ts) and the parse-time boot patch
|
|
3788
|
+
* (remote-channel-boot.ts, inlined into index.html by the host) decide from
|
|
3789
|
+
* these tables, so the two can never drift apart.
|
|
3790
|
+
* @module @linxin666/dsh-remote-web-ui/remote-channel-rules
|
|
3791
|
+
*/
|
|
3792
|
+
/** The gated mirror prefix (must match src/remote-methods.ts). */
|
|
3793
|
+
const REMOTE_PREFIX = "/remote";
|
|
3794
|
+
/** The live rule set. */
|
|
3795
|
+
const REMOTE_CHANNEL_RULES = {
|
|
3796
|
+
remotePrefix: REMOTE_PREFIX,
|
|
3797
|
+
apiPrefix: "/api/",
|
|
3798
|
+
pairPrefix: "/api/pair/",
|
|
3799
|
+
updatePrefix: "/api/update/",
|
|
3800
|
+
desktopLauncherPrefix: "/api/dsh-desktop-launcher",
|
|
3801
|
+
settingsBridgePrefix: "/api/dsh-web-ui-settings",
|
|
3802
|
+
sidebarPrefix: "/sidebar/",
|
|
3803
|
+
gitPrefix: "/git/",
|
|
3804
|
+
petPrefix: "/pet/",
|
|
3805
|
+
wsPaths: [
|
|
3806
|
+
"/api/events.mux",
|
|
3807
|
+
"/api/events.host",
|
|
3808
|
+
"/sidebar/ws/terminal",
|
|
3809
|
+
"/sidebar/ws/agent-terminals",
|
|
3810
|
+
"/api/dsh-ssh/terminal"
|
|
3811
|
+
]
|
|
3812
|
+
};
|
|
3813
|
+
/** The window global the boot patch publishes its seat under. */
|
|
3814
|
+
const REMOTE_CHANNEL_BOOT_GLOBAL = "__DSH_REMOTE_CHANNEL_BOOT__";
|
|
3815
|
+
//#endregion
|
|
3784
3816
|
//#region src/client/remote-channel.ts
|
|
3785
3817
|
/**
|
|
3786
3818
|
* The remote desktop channel — browser half. On a non-loopback origin (LAN
|
|
@@ -3807,23 +3839,7 @@ window.__ModuleLoader__.load({
|
|
|
3807
3839
|
* Pure helpers are exported for unit tests; `installRemoteChannel` patches
|
|
3808
3840
|
* the given window and returns their restore.
|
|
3809
3841
|
*/
|
|
3810
|
-
|
|
3811
|
-
const REMOTE_PREFIX = "/remote";
|
|
3812
|
-
const API_PREFIX = "/api/";
|
|
3813
|
-
const PAIR_PREFIX = "/api/pair/";
|
|
3814
|
-
const UPDATE_PREFIX = "/api/update/";
|
|
3815
|
-
const DESKTOP_LAUNCHER_PREFIX = "/api/dsh-desktop-launcher";
|
|
3816
|
-
const SETTINGS_BRIDGE_PREFIX = "/api/dsh-web-ui-settings";
|
|
3817
|
-
const SIDEBAR_PREFIX = "/sidebar/";
|
|
3818
|
-
const GIT_PREFIX = "/git/";
|
|
3819
|
-
const PET_PREFIX = "/pet/";
|
|
3820
|
-
const WS_PATHS = /* @__PURE__ */ new Set([
|
|
3821
|
-
"/api/events.mux",
|
|
3822
|
-
"/api/events.host",
|
|
3823
|
-
"/sidebar/ws/terminal",
|
|
3824
|
-
"/sidebar/ws/agent-terminals",
|
|
3825
|
-
"/api/dsh-ssh/terminal"
|
|
3826
|
-
]);
|
|
3842
|
+
const RULES = REMOTE_CHANNEL_RULES;
|
|
3827
3843
|
/** Decide whether a remote desktop channel is required from local or host policy. */
|
|
3828
3844
|
function remoteChannelRequired(hostname, snapshot, hostPairingPolicy) {
|
|
3829
3845
|
if (isLoopbackHostname(hostname)) return false;
|
|
@@ -3847,14 +3863,14 @@ window.__ModuleLoader__.load({
|
|
|
3847
3863
|
* @param pathname - the request URL pathname.
|
|
3848
3864
|
*/
|
|
3849
3865
|
function shouldRewriteFetchPath(pathname) {
|
|
3850
|
-
if (pathname.startsWith(
|
|
3851
|
-
if (pathname.startsWith(
|
|
3852
|
-
if (pathname ===
|
|
3853
|
-
if (pathname ===
|
|
3854
|
-
if (pathname.startsWith(
|
|
3855
|
-
if (pathname.startsWith(
|
|
3856
|
-
if (pathname.startsWith(
|
|
3857
|
-
if (pathname.startsWith(
|
|
3866
|
+
if (pathname.startsWith(RULES.pairPrefix)) return false;
|
|
3867
|
+
if (pathname.startsWith(RULES.updatePrefix)) return false;
|
|
3868
|
+
if (pathname === RULES.desktopLauncherPrefix || pathname.startsWith(`${RULES.desktopLauncherPrefix}/`)) return false;
|
|
3869
|
+
if (pathname === RULES.settingsBridgePrefix || pathname.startsWith(`${RULES.settingsBridgePrefix}/`)) return false;
|
|
3870
|
+
if (pathname.startsWith(RULES.apiPrefix)) return true;
|
|
3871
|
+
if (pathname.startsWith(RULES.sidebarPrefix) || pathname === "/sidebar") return true;
|
|
3872
|
+
if (pathname.startsWith(RULES.gitPrefix) || pathname === "/git") return true;
|
|
3873
|
+
if (pathname.startsWith(RULES.petPrefix) || pathname === "/pet") return true;
|
|
3858
3874
|
return false;
|
|
3859
3875
|
}
|
|
3860
3876
|
/**
|
|
@@ -3862,7 +3878,7 @@ window.__ModuleLoader__.load({
|
|
|
3862
3878
|
* @param pathname - the WebSocket URL pathname.
|
|
3863
3879
|
*/
|
|
3864
3880
|
function shouldRewriteWsPath(pathname) {
|
|
3865
|
-
return
|
|
3881
|
+
return RULES.wsPaths.includes(pathname);
|
|
3866
3882
|
}
|
|
3867
3883
|
/** The gated twin of one fenced path (`/remote` + original pathname). */
|
|
3868
3884
|
function rewritePath(pathname) {
|
|
@@ -4216,19 +4232,34 @@ window.__ModuleLoader__.load({
|
|
|
4216
4232
|
showFenceNotice();
|
|
4217
4233
|
};
|
|
4218
4234
|
const channelActive = () => remoteChannelRequired(window.location.hostname, settingsScope.getSnapshot(), hostPairingPolicy);
|
|
4235
|
+
const bootSeat = () => window[REMOTE_CHANNEL_BOOT_GLOBAL];
|
|
4219
4236
|
const syncChannel = () => {
|
|
4220
4237
|
const transition = channelTransition(channelActive(), disposeChannel !== void 0);
|
|
4221
|
-
if (transition === "install")
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4238
|
+
if (transition === "install") {
|
|
4239
|
+
const seat = bootSeat();
|
|
4240
|
+
if (seat !== void 0) {
|
|
4241
|
+
seat.onUnpaired = handleUnpaired;
|
|
4242
|
+
seat.onPaired = hideFenceNotice;
|
|
4243
|
+
if (seat.pendingUnpaired) {
|
|
4244
|
+
seat.pendingUnpaired = false;
|
|
4245
|
+
handleUnpaired();
|
|
4246
|
+
}
|
|
4247
|
+
disposeChannel = ctx.effect(() => () => {
|
|
4248
|
+
seat.onUnpaired = null;
|
|
4249
|
+
seat.onPaired = null;
|
|
4250
|
+
}, "remote-web-ui: remote desktop channel (boot patch)");
|
|
4251
|
+
} else disposeChannel = ctx.effect(() => {
|
|
4252
|
+
return installRemoteChannel(window, {
|
|
4253
|
+
onUnpaired: handleUnpaired,
|
|
4254
|
+
onPaired: hideFenceNotice
|
|
4255
|
+
});
|
|
4256
|
+
}, "remote-web-ui: remote desktop channel");
|
|
4257
|
+
} else if (transition === "retire" && disposeChannel !== void 0) {
|
|
4228
4258
|
disposeChannel();
|
|
4229
4259
|
disposeChannel = void 0;
|
|
4260
|
+
bootSeat()?.restore();
|
|
4230
4261
|
hideFenceNotice();
|
|
4231
|
-
}
|
|
4262
|
+
} else if (transition === "none" && !channelActive()) bootSeat()?.restore();
|
|
4232
4263
|
};
|
|
4233
4264
|
settingsScope.subscribe(syncChannel);
|
|
4234
4265
|
syncChannel();
|