@mrrisega/dsh-remote 0.7.0-beta.5 → 0.7.0-beta.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrisega/dsh-remote",
|
|
3
|
-
"version": "0.7.0-beta.
|
|
3
|
+
"version": "0.7.0-beta.6",
|
|
4
4
|
"description": "手机远程控制 DeepSeek Harness · Remote control DeepSeek Harness (dsh web) from any phone browser — 100% 全功能 App 级体验:发消息、看工具执行、审批权限、改设置、管凭据,含特权操作,免内网穿透。一条命令安装 npx @mrrisega/dsh-remote。Mobile remote control for DSH, self-host or SaaS, no server needed on LAN.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -834,22 +834,34 @@ window.__ModuleLoader__.load({
|
|
|
834
834
|
* 其中 **带 aria-keyshortcuts / 文案或 aria-label 含「设置」** 的优先(语言无关 + 精确)。
|
|
835
835
|
*/
|
|
836
836
|
function settingsTriggerCandidates() {
|
|
837
|
-
var
|
|
837
|
+
var ranked = [];
|
|
838
838
|
try {
|
|
839
|
+
/* 排名而不是「谁先扫到谁」(2026-10-11 业主实测「点入口弹出的是 token 用量小弹窗」):
|
|
840
|
+
① 官方设置槽位里的按钮 = 确定是设置触发器(sidebar.settings / settings.launcher)
|
|
841
|
+
② 带快捷键或文案含「设置」的弹层触发器
|
|
842
|
+
③ 其它弹层触发器(最后兜底)
|
|
843
|
+
并且**排除我们自己所在的底部槽**(sidebar.footer.action)—— token 用量、其它插件按钮
|
|
844
|
+
都在那一块,它们同样带 aria-haspopup=dialog,正是「点错弹窗」的来源。
|
|
845
|
+
只用 closest 判容器、不做后代选择器:任何 DOM 实现(含测试替身)都支持。 */
|
|
839
846
|
var cands = document.querySelectorAll('button[aria-haspopup="dialog"], [aria-haspopup="dialog"]');
|
|
840
|
-
var rest = [];
|
|
841
847
|
for (var i = 0; i < cands.length; i++) {
|
|
842
848
|
var el = cands[i];
|
|
843
849
|
if (!el || el.id === NAV_ENTRY_ID) continue; // 排除我们自己的入口
|
|
844
850
|
if (navInDialog(el)) continue; // 弹层内的(关闭键/栏目)一律不要
|
|
851
|
+
try { if (el.closest && el.closest('[data-slot="sidebar.footer.action"]')) continue; } catch (e0) { /* 忽略 */ }
|
|
845
852
|
if (NAV_BAD_RE.test(String(el.className || ""))) continue;
|
|
853
|
+
var inSettingsSlot = false;
|
|
854
|
+
try {
|
|
855
|
+
inSettingsSlot = !!(el.closest && el.closest('[data-slot="sidebar.settings"], [data-slot="settings.launcher"]'));
|
|
856
|
+
} catch (e1) { inSettingsSlot = false; }
|
|
846
857
|
var label = String(navAttrOf(el) || navCleanText(el) || "");
|
|
847
858
|
var hasShortcut = !!(el.getAttribute && el.getAttribute("aria-keyshortcuts"));
|
|
848
|
-
|
|
849
|
-
|
|
859
|
+
var rank = inSettingsSlot ? 0 : ((hasShortcut || /设置|Settings/i.test(label)) ? 1 : 2);
|
|
860
|
+
ranked.push({ el: el, rank: rank, order: i });
|
|
850
861
|
}
|
|
851
|
-
return
|
|
852
|
-
|
|
862
|
+
ranked.sort(function (a, b) { return (a.rank - b.rank) || (a.order - b.order); });
|
|
863
|
+
return ranked.map(function (x) { return x.el; });
|
|
864
|
+
} catch (e) { return ranked.map(function (x) { return x.el; }); }
|
|
853
865
|
}
|
|
854
866
|
/** 官方「设置」触发器(首选候选,保持旧名字给测试与调用方用)。 */
|
|
855
867
|
function pickSettingsTrigger() {
|
|
@@ -2718,7 +2730,11 @@ window.__ModuleLoader__.load({
|
|
|
2718
2730
|
// 在那边**做不到也不该做**(强行退出会弹原生致命错误)。面板据此:
|
|
2719
2731
|
// ① 不自动倒计时重启;② 保留提醒但改文案,引导"退出并重开桌面应用"。
|
|
2720
2732
|
// 旧版 host 的响应里没有该字段 → false,行为与改动前完全一致。
|
|
2721
|
-
|
|
2733
|
+
// 🔴 2026-10-11 修:node 半把该字段下在 **connect** 里(`connect.desktopHost`),
|
|
2734
|
+
// 这里以前读顶层 `st.desktopHost` → **永远 false** → 桌面端被当成 Web 版:
|
|
2735
|
+
// 面板照样摆「立即重启」按钮、照样走倒计时自动重启那条路(业主实测"装插件时窗口被突兀关掉")。
|
|
2736
|
+
// 兼容两种位置,桌面端的"不自动重启/只提示用户自己重启"这才真正生效。
|
|
2737
|
+
var desktopHost = !!(st && (st.desktopHost || (st.connect && st.connect.desktopHost)));
|
|
2722
2738
|
// kind === "refresh" = 插件文件在运行中被改写(装完/更新完),只需刷新页面;
|
|
2723
2739
|
// 其余情况(极为罕见)才提重启 dsh web。
|
|
2724
2740
|
var isRefreshOnly = restartPending && restartInfo.kind === "refresh";
|
|
@@ -6626,6 +6626,21 @@ function registerRoutes(ctx, relayDir) {
|
|
|
6626
6626
|
handler: async (_req, res) => {
|
|
6627
6627
|
// 首次安装/在线更新后重启 DeepSeek harness:插件本体与浏览器半在进程启动时装载,
|
|
6628
6628
|
// 不重启则面板入口与 /dsh-remote/* 路由都不会出现。只重启承载本插件的进程。
|
|
6629
|
+
//
|
|
6630
|
+
// 🔴 2026-10-11 业主口径:**桌面端绝不代为重启**。
|
|
6631
|
+
// "自动重启会很突兀地把窗口给关掉,关没了……我们应该提示用户自己重启,不要由我们来重启。"
|
|
6632
|
+
// 桌面端 Host 由 Electron 托管,重启 = 强杀承载窗口的进程(还会弹原生致命错误)。
|
|
6633
|
+
// 这里做成**硬拒绝**(不只是前端藏按钮):任何调用方都拿不到"被重启"的结果,
|
|
6634
|
+
// 只会收到一句明确的"请手动重启",前端据此展示提示。
|
|
6635
|
+
if (IS_ELECTRON_HOST) {
|
|
6636
|
+
sendJson(res, 200, {
|
|
6637
|
+
...(await composeStatus(relayDir)),
|
|
6638
|
+
ok: false,
|
|
6639
|
+
status: "manual-restart-required",
|
|
6640
|
+
detail: "桌面端不代为重启:请手动退出并重新打开 DeepSeek Harness(自动重启会突兀地关掉窗口)",
|
|
6641
|
+
});
|
|
6642
|
+
return;
|
|
6643
|
+
}
|
|
6629
6644
|
const r = await restartHarness(relayDir);
|
|
6630
6645
|
const payload = { ...(await composeStatus(relayDir)), ...r };
|
|
6631
6646
|
if (!r.ok) payload.error = r.detail || r.status || "重启失败";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-web",
|
|
3
|
-
"version": "0.7.0-beta.
|
|
3
|
+
"version": "0.7.0-beta.6",
|
|
4
4
|
"description": "公网远程控制 DeepSeek Harness(dsh web):安装即得专属加密地址,人在外面也能用手机访问电脑上的 dsh——无需同一局域网/WiFi、无需公网 IP 与内网穿透,全程加密;手机端 100% 还原电脑体验(对话/工具/审批/设置)。技术用户可选自建服务,流量走自己的服务器。Remote control DeepSeek Harness (dsh web) from anywhere over the public internet — install, get an encrypted URL, use it from your phone on any network (dual-half cordis plugin: Settings panel + same-origin /dsh-remote routes). (2026-09 由 dsh-remote-ui 更名 / renamed from dsh-remote-ui; dsh-remote 的 dsh web 插件半,不是纯 UI/皮肤插件)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -100,7 +100,7 @@ function makeDocument(roots) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
function loadNavHelpers(doc) {
|
|
103
|
-
const src = navHelpersSource() + "\nreturn { pickSettingsTrigger, pickNavByToken, navInDialog };";
|
|
103
|
+
const src = navHelpersSource() + "\nreturn { pickSettingsTrigger, settingsTriggerCandidates, pickNavByToken, navInDialog };";
|
|
104
104
|
// eslint-disable-next-line no-new-func
|
|
105
105
|
return new Function("document", "window", src)(doc, {});
|
|
106
106
|
}
|
|
@@ -135,6 +135,30 @@ test("★ 事故锁:官方「设置」触发器只认语义锚点,且必须
|
|
|
135
135
|
assert.equal(pickSettingsTrigger(), trigger, "必须命中侧栏那颗 aria-haspopup=dialog 的齿轮按钮");
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
test("★ 2026-10-11 业主实测:点入口弹出的是 token 用量弹窗 —— 官方设置槽位必须优先于底部槽邻居", () => {
|
|
139
|
+
// 现场:入口挂在 sidebar.footer.action,而同一底部区域的「token 本轮用量」按钮
|
|
140
|
+
// 也带 aria-haspopup="dialog"(甚至带快捷键)→ 旧实现"谁先扫到谁"就点中了它。
|
|
141
|
+
// 现在:① 官方设置槽位里的按钮排第一;② 底部槽(我们所在那块)里的候选直接排除。
|
|
142
|
+
const sidebar = new El("div", { cls: "sidebarRoot" });
|
|
143
|
+
const footSlot = new El("div", { attrs: { "data-slot": "sidebar.footer.action" } });
|
|
144
|
+
const tokenBtn = new El("button", { cls: "tokTrigger", attrs: { "aria-haspopup": "dialog", "aria-keyshortcuts": "Alt+Meta+T", "aria-label": "本轮用量" } });
|
|
145
|
+
const entry = new El("button", { id: "dru-nav-remote", cls: "dru-foot-entry", text: "📱 远程访问" });
|
|
146
|
+
footSlot.append(tokenBtn, entry);
|
|
147
|
+
const settingsArea = new El("div", { cls: "settingsArea" });
|
|
148
|
+
const settingsSlot = new El("div", { attrs: { "data-slot": "sidebar.settings" } });
|
|
149
|
+
const trigger = new El("button", { cls: "gearTrigger", attrs: { "aria-haspopup": "dialog", "aria-label": "设置" } });
|
|
150
|
+
settingsSlot.append(trigger);
|
|
151
|
+
settingsArea.append(settingsSlot);
|
|
152
|
+
sidebar.append(footSlot, settingsArea);
|
|
153
|
+
|
|
154
|
+
const doc = makeDocument([sidebar]);
|
|
155
|
+
const { pickSettingsTrigger, settingsTriggerCandidates } = loadNavHelpers(doc);
|
|
156
|
+
const cands = settingsTriggerCandidates();
|
|
157
|
+
assert.ok(!cands.includes(tokenBtn), "★ 底部槽里的 token 用量按钮必须被排除(点它会弹错窗)");
|
|
158
|
+
assert.ok(!cands.includes(entry), "我们自己的入口不能成为候选(否则点自己→再点自己)");
|
|
159
|
+
assert.equal(pickSettingsTrigger(), trigger, "★ 必须选中官方设置槽位里的齿轮,而不是 token 用量按钮");
|
|
160
|
+
});
|
|
161
|
+
|
|
138
162
|
test("★ 事故锁:弹层里的「通用设置」永远不能成为落点", () => {
|
|
139
163
|
const { doc, general } = desktopFixture();
|
|
140
164
|
const { pickSettingsTrigger, pickNavByToken } = loadNavHelpers(doc);
|