@mrrisega/dsh-remote 0.4.4 → 0.4.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/dsh-setup.mjs +92 -25
- package/package.json +1 -1
- package/packages/dsh-remote-ui/lib/index.js +13 -8
package/dsh-setup.mjs
CHANGED
|
@@ -658,6 +658,91 @@ function declarePluginDep(pkgFile) {
|
|
|
658
658
|
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + "\n");
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
+
/** 把 dsh-remote-ui 加入 dsh.profile.bundles(幂等)。返回是否发生变更。 */
|
|
662
|
+
function ensureBundleEntry(pkgFile) {
|
|
663
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
|
|
664
|
+
const bundles = pkg.dsh && pkg.dsh.profile && Array.isArray(pkg.dsh.profile.bundles)
|
|
665
|
+
? pkg.dsh.profile.bundles
|
|
666
|
+
: null;
|
|
667
|
+
if (bundles && bundles.includes("dsh-remote-ui")) return false;
|
|
668
|
+
pkg.dsh = pkg.dsh || {};
|
|
669
|
+
pkg.dsh.profile = pkg.dsh.profile || {};
|
|
670
|
+
pkg.dsh.profile.bundles = bundles || [];
|
|
671
|
+
pkg.dsh.profile.bundles.push("dsh-remote-ui");
|
|
672
|
+
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + "\n");
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** 从 dsh.profile.bundles 移除 dsh-remote-ui(幂等)。返回是否发生变更。 */
|
|
677
|
+
function removeBundleEntry(pkgFile) {
|
|
678
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
|
|
679
|
+
const bundles = pkg.dsh && pkg.dsh.profile && Array.isArray(pkg.dsh.profile.bundles)
|
|
680
|
+
? pkg.dsh.profile.bundles
|
|
681
|
+
: null;
|
|
682
|
+
if (!bundles || !bundles.includes("dsh-remote-ui")) return false;
|
|
683
|
+
pkg.dsh.profile.bundles = bundles.filter((b) => b !== "dsh-remote-ui");
|
|
684
|
+
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + "\n");
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* 插件安装(pluginCmd 非卸载分支)收敛策略 —— 2026-09-06「重复 ID 崩溃」根治:
|
|
690
|
+
* dsh-remote-ui 是带 dsh.bundle.patch 的 bundle:package.json 的 dsh.profile.bundles
|
|
691
|
+
* 声明它后,加载器会自动应用插件自带的 cordis.patch.yml(节点半+浏览器半的唯一激活点)。
|
|
692
|
+
* 若用户级 cordis.patch.yml 再手工 insert 同一个 id,dsh web 启动即报“重复 ID”崩溃。
|
|
693
|
+
* 因此本命令【绝不写用户 include】,只负责:让插件以 bundle 形态可解析,
|
|
694
|
+
* 并清理历史遗留的 include 块。市场形态(github:/npm 依赖)则完全交由市场管理,只清理 include。
|
|
695
|
+
*/
|
|
696
|
+
function convergePluginActivation(profileDir, pkgFile, patchFile, pluginDir, patch) {
|
|
697
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
|
|
698
|
+
const dep = pkg.dependencies && pkg.dependencies["dsh-remote-ui"];
|
|
699
|
+
const inBundles = !!(pkg.dsh && pkg.dsh.profile && Array.isArray(pkg.dsh.profile.bundles)
|
|
700
|
+
&& pkg.dsh.profile.bundles.includes("dsh-remote-ui"));
|
|
701
|
+
const marketManaged = dep && !String(dep).startsWith("file:"); // github:/npm: 等由市场/包管理器管源码
|
|
702
|
+
const managedByUs = !dep || String(dep).startsWith("file:"); // 无依赖或 file: 拷贝 → 我们管
|
|
703
|
+
|
|
704
|
+
const stripInclude = (reason) => {
|
|
705
|
+
const newPatch = stripPluginEntries(patch);
|
|
706
|
+
if (newPatch !== patch) {
|
|
707
|
+
fs.writeFileSync(patchFile, newPatch);
|
|
708
|
+
console.log(`✅ 已移除 ${patchFile} 中的冗余 include(${reason};激活统一走插件自带 bundle patch,避免重复 ID 崩溃)`);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
if (marketManaged && inBundles) {
|
|
713
|
+
// 插件市场安装形态:依赖与源码归市场管,我们只清历史 include(若旧版曾写过)
|
|
714
|
+
stripInclude("插件市场安装形态无需用户 include");
|
|
715
|
+
console.log("ℹ 插件市场安装形态(bundles+dependency):已保持市场管理的源码不变。");
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (managedByUs) {
|
|
720
|
+
const pluginLocalDir = copyPluginIntoProfile(profileDir, pluginDir);
|
|
721
|
+
const entryFile = path.join(pluginLocalDir, "lib", "index.js");
|
|
722
|
+
if (!fs.existsSync(entryFile)) {
|
|
723
|
+
console.error(`❌ 插件入口缺失:${entryFile}(本包不完整?请用官方源重装:npx --registry=https://registry.npmjs.org @mrrisega/dsh-remote@latest)`);
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
console.log(`✅ 插件已拷贝到 ${pluginLocalDir}`);
|
|
727
|
+
declarePluginDep(pkgFile); // file: 依赖(包管理器 install 不误删)
|
|
728
|
+
const addedBundle = ensureBundleEntry(pkgFile); // bundles 声明 → 插件自带 patch 自动激活
|
|
729
|
+
const linked = ensurePluginLinked(profileDir, pluginLocalDir); // 自建 node_modules 链接
|
|
730
|
+
stripInclude("bundle patch 已是唯一激活点"); // 清历史 include
|
|
731
|
+
console.log(addedBundle
|
|
732
|
+
? "✅ 已加入 dsh.profile.bundles(dsh-remote-ui 自带 patch 自动生效)"
|
|
733
|
+
: "ℹ dsh.profile.bundles 已含 dsh-remote-ui");
|
|
734
|
+
console.log(linked
|
|
735
|
+
? `✅ 已建立 node_modules/dsh-remote-ui → ${pluginLocalDir}(免包管理器即可解析)`
|
|
736
|
+
: "✅ node_modules/dsh-remote-ui 已就绪");
|
|
737
|
+
console.log(`✅ 插件安装完成(bundle 形态,无用户 include)。配置目录: ${CONFIG_DIR}`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// 异常形态:有非 file: 依赖但不在 bundles(无法靠 bundle patch 激活)
|
|
742
|
+
console.log(`ℹ 检测到依赖 dsh-remote-ui(${dep}) 但未声明在 dsh.profile.bundles——插件不会激活。`);
|
|
743
|
+
console.log(" 请在 dsh 插件市场重新添加该插件,或先执行 `dsh-remote plugin --uninstall` 再一键安装。");
|
|
744
|
+
}
|
|
745
|
+
|
|
661
746
|
async function pluginCmd(argv) {
|
|
662
747
|
const uninstall = hasFlag(argv, "--uninstall");
|
|
663
748
|
const profileIdx = argv.indexOf("--profile");
|
|
@@ -696,35 +781,17 @@ async function pluginCmd(argv) {
|
|
|
696
781
|
if (pkg.dependencies) delete pkg.dependencies["dsh-remote-ui"];
|
|
697
782
|
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + "\n");
|
|
698
783
|
} catch { /* ignore */ }
|
|
784
|
+
// 同步移除 bundles 声明,避免“bundles 引用已删除包 → dsh web 启动报错”
|
|
785
|
+
try {
|
|
786
|
+
if (removeBundleEntry(pkgFile)) console.log("✅ 已从 dsh.profile.bundles 移除 dsh-remote-ui");
|
|
787
|
+
} catch { /* ignore */ }
|
|
699
788
|
console.log("✅ 卸载完成。重启 dsh web 生效。");
|
|
700
789
|
return;
|
|
701
790
|
}
|
|
702
791
|
|
|
703
|
-
//
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const entryFile = path.join(pluginLocalDir, "lib", "index.js");
|
|
707
|
-
if (!fs.existsSync(entryFile)) {
|
|
708
|
-
console.error(`❌ 插件入口缺失:${entryFile}(本包不完整?请用官方源重装:npx --registry=https://registry.npmjs.org @mrrisega/dsh-remote@latest)`);
|
|
709
|
-
process.exit(1);
|
|
710
|
-
}
|
|
711
|
-
console.log(`✅ 插件已拷贝到 ${pluginLocalDir}`);
|
|
712
|
-
|
|
713
|
-
// 先清掉旧条目(含旧版无标记条目),再写入带标记的新块,保证不重复。
|
|
714
|
-
// 关键:先清除默认的 [] 空文档占位行,否则拼接出的 YAML 非法,dsh web 启动即崩。
|
|
715
|
-
const stripped = stripPluginEntries(patch);
|
|
716
|
-
const base = normalizePatchBase(stripped);
|
|
717
|
-
const block = pluginBlock(CONFIG_DIR);
|
|
718
|
-
fs.writeFileSync(patchFile, (base ? base + "\n" : "") + block + "\n");
|
|
719
|
-
console.log(`✅ 已写入 ${patchFile}(裸名 include:dsh-remote-ui → 节点半 + 浏览器半均生效)`);
|
|
720
|
-
|
|
721
|
-
declarePluginDep(pkgFile); // package.json 声明 file: 依赖(后端各类 install 不误删)
|
|
722
|
-
const linked = ensurePluginLinked(profileDir, pluginLocalDir); // 自建 node_modules 链接
|
|
723
|
-
console.log(linked
|
|
724
|
-
? `✅ 已建立 node_modules/dsh-remote-ui → ${pluginLocalDir}(免包管理器即可解析)`
|
|
725
|
-
: "✅ node_modules/dsh-remote-ui 已就绪");
|
|
726
|
-
|
|
727
|
-
console.log(`✅ 插件安装完成。配置目录: ${CONFIG_DIR}`);
|
|
792
|
+
// 安装:收敛到“恰好一处激活”(bundle patch 唯一激活点),绝不与市场/历史 include 并存
|
|
793
|
+
convergePluginActivation(profileDir, pkgFile, patchFile, pluginDir, patch);
|
|
794
|
+
|
|
728
795
|
console.log(" 打开 dsh web → 设置 → 「远程控制」,注册/登录手机号即可(无需任何命令)。");
|
|
729
796
|
console.log(" 若从 DeepSeek App/插件市场 安装:请完全退出并重开 App 让插件生效。");
|
|
730
797
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrisega/dsh-remote",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -362,7 +362,9 @@ function appendLogLine(relayDir, name, line) {
|
|
|
362
362
|
|
|
363
363
|
/** 插件市场只装了 UI 插件;若桌面缺 dsh-remote 运行环境(dsh-setup.mjs=bridge/自启动),
|
|
364
364
|
* 由插件在后台自动执行一次 `npx @mrrisega/dsh-remote` 补齐,用户无需手动跑命令。
|
|
365
|
-
* 已有环境(包括手动 npx 装过)直接跳过。返回 true=已就绪。
|
|
365
|
+
* 已有环境(包括手动 npx 装过)直接跳过。返回 true=已就绪。
|
|
366
|
+
* 注意:默认优先官方源——镜像(npmmirror)可能滞后于刚发布的版本,装到旧版会把
|
|
367
|
+
* 已被 0.4.5 移除的“用户 include”重新写回 profile(历史上造成 dsh web 重复 ID 崩溃)。 */
|
|
366
368
|
function ensureRuntime(relayDir) {
|
|
367
369
|
if (existsSync(join(relayDir, "dsh-setup.mjs"))) return true;
|
|
368
370
|
const marker = join(relayDir, PROVISION_MARKER);
|
|
@@ -372,7 +374,7 @@ function ensureRuntime(relayDir) {
|
|
|
372
374
|
const log = join(relayDir, AUTO_INSTALL_LOG);
|
|
373
375
|
const child = spawn(npxCommand(), ["--yes", "@mrrisega/dsh-remote"], {
|
|
374
376
|
detached: true,
|
|
375
|
-
env: spawnEnv(),
|
|
377
|
+
env: spawnEnv({ npm_config_registry: "https://registry.npmjs.org" }),
|
|
376
378
|
stdio: ["ignore", openSync(log, "a"), openSync(log, "a")]
|
|
377
379
|
});
|
|
378
380
|
writeMarker(marker, child.pid); // 记 pid:宿主重启后可立即清理死进程残留
|
|
@@ -711,7 +713,7 @@ async function proxyFeedback(relayDir, req, res, pathname) {
|
|
|
711
713
|
// ---------- 自管理:版本 / 在线更新 / 彻底卸载(面板内“版本与更新”卡片) ----------
|
|
712
714
|
|
|
713
715
|
/** 插件自身发布版本(与 dsh-remote 根包同步递增)。 */
|
|
714
|
-
const PLUGIN_VERSION = "0.4.
|
|
716
|
+
const PLUGIN_VERSION = "0.4.6";
|
|
715
717
|
const UPDATE_LOG = ".dsh-update.log";
|
|
716
718
|
const UPDATE_MARKER = ".dsh-update-running";
|
|
717
719
|
|
|
@@ -740,10 +742,12 @@ function spawnUpdater(relayDir, extraEnv) {
|
|
|
740
742
|
}
|
|
741
743
|
|
|
742
744
|
/**
|
|
743
|
-
* 后台执行在线一键更新:npx @mrrisega/dsh-remote@latest(幂等自愈:补运行环境/更新 bridge
|
|
745
|
+
* 后台执行在线一键更新:npx @mrrisega/dsh-remote@latest(幂等自愈:补运行环境/更新 bridge/收敛 include)。
|
|
744
746
|
* 稳健性:
|
|
745
747
|
* - npx 用绝对路径 + PATH 补全解析(App 拉起的 dsh web PATH 最小化时不再 ENOENT 静默失败);
|
|
746
|
-
* -
|
|
748
|
+
* - 【官方源优先】镜像(npmmirror)滞后时会把旧版(如 0.4.4)当成最新安装,旧 pluginCmd 会把
|
|
749
|
+
* 已被移除的“用户 include”重新写回 profile → dsh web 重启重复 ID 崩溃。故先试官方源,
|
|
750
|
+
* 失败(国内网络)才回退用户默认镜像源;
|
|
747
751
|
* - marker 记录 pid,子进程退出/出错即清理;宿主重启后由 sweepStaleMarkers 立即清掉死进程残留。
|
|
748
752
|
*/
|
|
749
753
|
function runOnlineUpdate(relayDir) {
|
|
@@ -756,16 +760,17 @@ function runOnlineUpdate(relayDir) {
|
|
|
756
760
|
let retried = false;
|
|
757
761
|
const clear = () => { try { rmSync(marker, { force: true }); } catch { /* ignore */ } };
|
|
758
762
|
const run = () => {
|
|
759
|
-
|
|
763
|
+
// 第一次:官方 npm 源;失败(exit≠0/网络)才回退用户默认源(通常为国内镜像)
|
|
764
|
+
const child = spawnUpdater(relayDir, retried ? {} : { npm_config_registry: "https://registry.npmjs.org" });
|
|
760
765
|
writeMarker(marker, child.pid);
|
|
761
766
|
child.on("exit", (code) => {
|
|
762
767
|
if (!retried && code !== 0) {
|
|
763
768
|
retried = true;
|
|
764
|
-
appendLogLine(relayDir, UPDATE_LOG, `[update]
|
|
769
|
+
appendLogLine(relayDir, UPDATE_LOG, `[update] 官方源安装失败(exit=${code}),回退默认源(npmmirror 等)重试…`);
|
|
765
770
|
run();
|
|
766
771
|
return;
|
|
767
772
|
}
|
|
768
|
-
appendLogLine(relayDir, UPDATE_LOG, `[update] npx 退出 code=${code ?? "?"}
|
|
773
|
+
appendLogLine(relayDir, UPDATE_LOG, `[update] npx 退出 code=${code ?? "?"}(${retried ? "默认源" : "官方源"})`);
|
|
769
774
|
clear();
|
|
770
775
|
});
|
|
771
776
|
child.on("error", (e) => {
|