@mrrisega/dsh-remote 0.4.4 → 0.4.5

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 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
- // 安装 = 拷贝完整插件目录 + 写裸名 include + 自建 node_modules 链接 + 声明 file: 依赖。
704
- // 全程不跑 pnpm/npm;但链接与依赖双保险,任何包管理器之后 install 也不会破坏它。
705
- const pluginLocalDir = copyPluginIntoProfile(profileDir, pluginDir);
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.4",
3
+ "version": "0.4.5",
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",
@@ -711,7 +711,7 @@ async function proxyFeedback(relayDir, req, res, pathname) {
711
711
  // ---------- 自管理:版本 / 在线更新 / 彻底卸载(面板内“版本与更新”卡片) ----------
712
712
 
713
713
  /** 插件自身发布版本(与 dsh-remote 根包同步递增)。 */
714
- const PLUGIN_VERSION = "0.4.4";
714
+ const PLUGIN_VERSION = "0.4.5";
715
715
  const UPDATE_LOG = ".dsh-update.log";
716
716
  const UPDATE_MARKER = ".dsh-update-running";
717
717