@linxin666/dsh-remote-web-ui 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.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: 2007aecc144d421951a3aebd9d94708746e1f549
6
- README.zh.md: 85a76a5df45f3cb2d4ec0a477e65affd78c98795
5
+ README.md: edd272ef9b98700efc6ff465c5cb0dcc2e2d9648
6
+ README.zh.md: 53f1f72524ad3072ba2526108f7fc650403a5e0a
package/README.md CHANGED
@@ -65,7 +65,12 @@ that probes and runs the update.
65
65
  `/api/pair` endpoints: loopback, the advertised LAN literals, or the
66
66
  configured public base URL. Through the `/remote` desktop channel all
67
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
68
+ provider's live model catalog is unavailable or unknown. A provider whose
69
+ resolved `models` list is absent or empty keeps inheriting its installed
70
+ catalog until an unknown custom model must materialize that catalog;
71
+ existing model overrides are preserved and translated into the resulting
72
+ entries. Malformed or conflicting model profiles are refused instead of
73
+ being destructively rewritten. Stop or device
69
74
  revocation disables it immediately; the
70
75
  generic `settings.*`, `credentials.*`, and `llm.discoverModels` RPC methods
71
76
  remain loopback-only.
package/README.zh.md CHANGED
@@ -13,7 +13,7 @@
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
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。
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 的实时模型目录不可用或未知时,采纳会被拒绝。解析后的 `models` 列表缺失或为空时,provider 会继续继承已安装目录;只有加入未知的自定义模型时才会实体化完整目录,现有模型 override 会保留并转换到对应条目。格式错误或相互冲突的模型配置会被拒绝,不会被破坏性重写。停止或撤销设备会立即禁用它;通用 `settings.*`、`credentials.*` 和 `llm.discoverModels` RPC 方法仍然仅限 loopback。
17
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` 关掉(或关闭插件)也会清掉通道短暂激活期间弹出的提示。
18
18
  - **姿态探测**:插件用伪造 Host 头(公网 base 与每个 LAN base)探测 SDK 的 `/api` 围栏。403 是默认姿态(围栏关闭,远程访问走配对)。任何非 403 结果——`--trusted-host`,或 `--host 0.0.0.0` 下 SDK 的局域网自动信任——都会以 CRITICAL 日志与配对面板红色横幅呈现,因此 SDK 的 `/api` 信任姿态是被看见的,而不是被假设的。探测失败会丢掉进行中的目标缓存,相同 origin 会在下次触发时重试。
19
19
  - **实时状态**:桌面面板经 SSE 流实时镜像配对状态(等待 → 已连接 → 已断开),并展示已授权设备名单。
package/lib/client.js CHANGED
@@ -4114,10 +4114,16 @@ window.__ModuleLoader__.load({
4114
4114
  * @param ctx - client root context.
4115
4115
  */
4116
4116
  function apply(ctx) {
4117
- ctx.effect(() => ctx.locale.register(NS, {
4118
- zh,
4119
- en
4120
- }), "remote-web-ui: dictionaries");
4117
+ ctx.effect(() => {
4118
+ try {
4119
+ return ctx.locale.register(NS, {
4120
+ zh,
4121
+ en
4122
+ });
4123
+ } catch {
4124
+ return () => {};
4125
+ }
4126
+ }, "remote-web-ui: dictionaries");
4121
4127
  const t = ctx.locale.bind(NS);
4122
4128
  const settingsScope = (ctx.get("webUiSettings") ?? ctx.settingsScope).bind({ namespace: REMOTE_WEB_UI_NS });
4123
4129
  const enabled = () => {
@@ -4127,10 +4133,12 @@ window.__ModuleLoader__.load({
4127
4133
  ctx.slots.inject("sidebar.remote", () => {
4128
4134
  let disposeEntry;
4129
4135
  const syncEntry = () => {
4130
- if (enabled() && disposeEntry === void 0) disposeEntry = ctx.slots.register({
4131
- name: "sidebar.remote",
4132
- locale: NS
4133
- }, RemoteEntry);
4136
+ if (enabled() && disposeEntry === void 0) try {
4137
+ disposeEntry = ctx.slots.register({
4138
+ name: "sidebar.remote",
4139
+ locale: NS
4140
+ }, RemoteEntry);
4141
+ } catch {}
4134
4142
  else if (!enabled() && disposeEntry !== void 0) {
4135
4143
  disposeEntry();
4136
4144
  disposeEntry = void 0;
@@ -4146,11 +4154,13 @@ window.__ModuleLoader__.load({
4146
4154
  ctx.slots.inject("sidebar.footer.action", () => {
4147
4155
  let disposeEntry;
4148
4156
  const syncEntry = () => {
4149
- if (enabled() && disposeEntry === void 0) disposeEntry = ctx.slots.register({
4150
- name: "sidebar.footer.action",
4151
- id: "remote-web-ui",
4152
- locale: NS
4153
- }, FooterRemoteEntry);
4157
+ if (enabled() && disposeEntry === void 0) try {
4158
+ disposeEntry = ctx.slots.register({
4159
+ name: "sidebar.footer.action",
4160
+ id: "remote-web-ui",
4161
+ locale: NS
4162
+ }, FooterRemoteEntry);
4163
+ } catch {}
4154
4164
  else if (!enabled() && disposeEntry !== void 0) {
4155
4165
  disposeEntry();
4156
4166
  disposeEntry = void 0;
@@ -4165,17 +4175,21 @@ window.__ModuleLoader__.load({
4165
4175
  });
4166
4176
  const remoteSettings = new RemoteSettingsCardController(settingsScope);
4167
4177
  ctx.slots.inject("web-ui.plugin.item", () => {
4168
- const unregister = ctx.slots.register({
4169
- name: "web-ui.plugin.item",
4170
- id: "remote-web-ui",
4171
- order: 90,
4172
- locale: NS,
4173
- inject: () => remoteSettings.inject()
4174
- }, RemoteSettingsCard);
4175
- return () => {
4176
- remoteSettings.dispose();
4177
- unregister();
4178
- };
4178
+ try {
4179
+ const unregister = ctx.slots.register({
4180
+ name: "web-ui.plugin.item",
4181
+ id: "remote-web-ui",
4182
+ order: 90,
4183
+ locale: NS,
4184
+ inject: () => remoteSettings.inject()
4185
+ }, RemoteSettingsCard);
4186
+ return () => {
4187
+ remoteSettings.dispose();
4188
+ unregister();
4189
+ };
4190
+ } catch {
4191
+ return () => {};
4192
+ }
4179
4193
  });
4180
4194
  let disposeRuntime;
4181
4195
  const syncRuntime = () => {