@mrrisega/dsh-remote 0.3.6 → 0.3.8
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 +54 -63
- package/package.json +1 -1
package/dsh-setup.mjs
CHANGED
|
@@ -469,14 +469,22 @@ async function setup(argv) {
|
|
|
469
469
|
// ---------- plugin:安装/卸载 dsh web 远程控制插件 ----------
|
|
470
470
|
const PLUGIN_MARKER_START = "# >>> dsh-remote-ui (managed by dsh-remote plugin; do not edit)";
|
|
471
471
|
const PLUGIN_MARKER_END = "# <<< dsh-remote-ui";
|
|
472
|
-
/** 插件在 profile 内的固定本地目录(
|
|
472
|
+
/** 插件在 profile 内的固定本地目录(安装时整目录拷贝;include 直接指向其入口文件)。 */
|
|
473
473
|
const PLUGIN_LOCAL_DIR = "dsh-remote-ui-plugin";
|
|
474
|
+
/** 插件 node 半入口(相对 profile 目录;加载器按 '.' 开头在配置文件旁解析)。 */
|
|
475
|
+
const PLUGIN_ENTRY_REL = `./${PLUGIN_LOCAL_DIR}/lib/index.js`;
|
|
474
476
|
|
|
477
|
+
/**
|
|
478
|
+
* include 块。v0.3.7 起 name 直接用「相对文件路径」指向已拷贝的插件入口:
|
|
479
|
+
* dsh 加载器对 '.' 开头的 name 会在配置文件(profile)目录旁解析并 import 该文件,
|
|
480
|
+
* 完全不经过 pnpm/npm/node_modules —— 任何机器装完即用,包管理器成不成功都不影响。
|
|
481
|
+
* (已用隔离 HOME + 独立端口真实启动 dsh web 验证:/dsh-remote/* 路由正常注册。)
|
|
482
|
+
*/
|
|
475
483
|
function pluginBlock(relayDir) {
|
|
476
484
|
return `${PLUGIN_MARKER_START}
|
|
477
485
|
- insert:
|
|
478
486
|
- id: dsh-remote-ui
|
|
479
|
-
name: '
|
|
487
|
+
name: '${PLUGIN_ENTRY_REL}'
|
|
480
488
|
config:
|
|
481
489
|
relayDir: '${relayDir}'
|
|
482
490
|
${PLUGIN_MARKER_END}`;
|
|
@@ -529,57 +537,49 @@ function normalizePatchBase(patch) {
|
|
|
529
537
|
.trimEnd();
|
|
530
538
|
}
|
|
531
539
|
|
|
532
|
-
/** 在 profile 目录执行依赖安装(pnpm 优先,npm 兜底)。 */
|
|
533
|
-
function installProfileDeps(profileDir) {
|
|
534
|
-
const pm = sh("command -v pnpm >/dev/null 2>&1 && echo pnpm || echo npm").stdout.trim() || "npm";
|
|
535
|
-
const cmd = pm === "pnpm" ? "pnpm install" : "npm install";
|
|
536
|
-
return sh(cmd, 120000, profileDir);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
540
|
/**
|
|
540
541
|
* 把插件整目录拷贝进 profile 的固定子目录并返回该目录。
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
* - npx 缓存目录是临时的,link 目标迟早失效。
|
|
544
|
-
* 拷贝后用 file: 相对依赖即可,npm/pnpm 都能解析。
|
|
542
|
+
* 源(本包 packages/dsh-remote-ui)可能在 npx 临时缓存里,因此必须拷到 profile 内持久化,
|
|
543
|
+
* include 用相对路径直接指向拷贝出的入口,不写依赖、不跑任何包管理器。
|
|
545
544
|
*/
|
|
546
545
|
function copyPluginIntoProfile(profileDir, pluginDir) {
|
|
547
546
|
const dest = path.join(profileDir, PLUGIN_LOCAL_DIR);
|
|
548
547
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
549
548
|
fs.cpSync(pluginDir, dest, {
|
|
550
549
|
recursive: true,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
550
|
+
// 注意:插件源(npx 缓存 / npm 全局)本身就位于某个 node_modules 之下,
|
|
551
|
+
// 过滤必须基于“相对插件目录”的路径片段,绝不能整条 src 路径包含判断,
|
|
552
|
+
// 否则会把插件文件全过滤掉、拷出空目录(此前反复“插件入口缺失”的总根因)。
|
|
553
|
+
filter: (src) => {
|
|
554
|
+
if (src === pluginDir) return true;
|
|
555
|
+
const rel = path.relative(pluginDir, src);
|
|
556
|
+
return !String(rel).split(path.sep).some((seg) => seg === "node_modules" || seg === ".git" || seg === ".DS_Store");
|
|
557
|
+
}
|
|
555
558
|
});
|
|
556
559
|
return dest;
|
|
557
560
|
}
|
|
558
561
|
|
|
559
562
|
/**
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
* -
|
|
563
|
-
* - pnpm 布局的 profile 里跑 npm install 会崩(npm 读 .pnpm 报错),不能依赖包管理器。
|
|
564
|
-
* 因此这里:解析现有链接的真实目标,只要不等于本次拷贝目录就强制重建为绝对路径链接/拷贝。
|
|
565
|
-
* 返回 true 表示本次重建了链接。
|
|
563
|
+
* 清理 v0.3.0~0.3.6 旧机制的残留(避免与新 include 并存造成困惑/冲突):
|
|
564
|
+
* - package.json 里 link:/file: 的 dsh-remote-ui 依赖;
|
|
565
|
+
* - node_modules 里的 dsh-remote-ui 链接/目录。
|
|
566
566
|
*/
|
|
567
|
-
function
|
|
568
|
-
|
|
569
|
-
const nmPlugin = path.join(nmDir, "dsh-remote-ui");
|
|
570
|
-
fs.mkdirSync(nmDir, { recursive: true });
|
|
571
|
-
// 已正确指向本次拷贝目录 → 无需处理
|
|
567
|
+
function cleanupLegacyPluginState(profileDir, pkgFile) {
|
|
568
|
+
let changed = false;
|
|
572
569
|
try {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
570
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
|
|
571
|
+
if (pkg.dependencies && pkg.dependencies["dsh-remote-ui"]) {
|
|
572
|
+
delete pkg.dependencies["dsh-remote-ui"];
|
|
573
|
+
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + "\n");
|
|
574
|
+
changed = true;
|
|
575
|
+
}
|
|
576
|
+
} catch { /* 无 package.json 或损坏则跳过 */ }
|
|
576
577
|
try {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
try { return fs.realpathSync(nmPlugin) === pluginLocalDir; } catch { return false; }
|
|
578
|
+
const legacy = path.join(profileDir, "node_modules", "dsh-remote-ui");
|
|
579
|
+
fs.rmSync(legacy, { recursive: true, force: true });
|
|
580
|
+
// 顺手清理可能因旧依赖产生的空 node_modules 不影响 dsh
|
|
581
|
+
} catch { /* ignore */ }
|
|
582
|
+
return changed;
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
async function pluginCmd(argv) {
|
|
@@ -603,7 +603,6 @@ async function pluginCmd(argv) {
|
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
const patch = fs.readFileSync(patchFile, "utf8");
|
|
606
|
-
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
|
|
607
606
|
|
|
608
607
|
if (uninstall) {
|
|
609
608
|
// 移除 patch 中的插件条目(兼容旧版无标记条目)
|
|
@@ -614,25 +613,22 @@ async function pluginCmd(argv) {
|
|
|
614
613
|
} else {
|
|
615
614
|
console.log("ℹ patch 中未发现 dsh-remote-ui 条目。");
|
|
616
615
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
fs.rmSync(path.join(profileDir, PLUGIN_LOCAL_DIR), { recursive: true, force: true });
|
|
621
|
-
console.log(`✅ 已从 ${pkgFile} 移除 dsh-remote-ui 依赖与本地插件目录`);
|
|
622
|
-
}
|
|
623
|
-
console.log(" 执行依赖更新...");
|
|
624
|
-
const r = installProfileDeps(profileDir);
|
|
625
|
-
if (!r.ok) console.warn(`⚠️ 依赖更新失败:${r.stderr.trim() || r.stdout.trim()}(请手动在 ${profileDir} 执行 pnpm/npm install)`);
|
|
616
|
+
fs.rmSync(path.join(profileDir, PLUGIN_LOCAL_DIR), { recursive: true, force: true });
|
|
617
|
+
console.log(`✅ 已删除本地插件目录 ${path.join(profileDir, PLUGIN_LOCAL_DIR)}`);
|
|
618
|
+
cleanupLegacyPluginState(profileDir, pkgFile);
|
|
626
619
|
console.log("✅ 卸载完成。重启 dsh web 生效。");
|
|
627
620
|
return;
|
|
628
621
|
}
|
|
629
622
|
|
|
630
|
-
//
|
|
631
|
-
|
|
623
|
+
// 安装:整目录拷贝插件 + 写相对路径 include。不写 package.json 依赖、不跑 pnpm/npm,
|
|
624
|
+
// 不依赖 node_modules —— dsh 加载器按 name 的 './' 相对路径直接 import 入口文件。
|
|
632
625
|
const pluginLocalDir = copyPluginIntoProfile(profileDir, pluginDir);
|
|
633
|
-
|
|
634
|
-
fs.
|
|
635
|
-
|
|
626
|
+
const entryFile = path.join(pluginLocalDir, "lib", "index.js");
|
|
627
|
+
if (!fs.existsSync(entryFile)) {
|
|
628
|
+
console.error(`❌ 插件入口缺失:${entryFile}(本包不完整?)`);
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
631
|
+
console.log(`✅ 插件已拷贝到 ${pluginLocalDir}(include 直接指向其入口,无需 pnpm/npm)`);
|
|
636
632
|
|
|
637
633
|
// 先清掉旧条目(含旧版无标记条目),再写入带标记的新块,保证不重复。
|
|
638
634
|
// 关键:先清除默认的 [] 空文档占位行,否则拼接出的 YAML 非法,dsh web 启动即崩。
|
|
@@ -640,18 +636,13 @@ async function pluginCmd(argv) {
|
|
|
640
636
|
const base = normalizePatchBase(stripped);
|
|
641
637
|
const block = pluginBlock(CONFIG_DIR);
|
|
642
638
|
fs.writeFileSync(patchFile, (base ? base + "\n" : "") + block + "\n");
|
|
643
|
-
console.log(`✅ 已写入 ${patchFile}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (
|
|
647
|
-
console.
|
|
648
|
-
console.warn(" 将直接建立插件链接,不依赖包管理器,dsh web 仍可正常加载插件。");
|
|
639
|
+
console.log(`✅ 已写入 ${patchFile}(name: ${PLUGIN_ENTRY_REL})`);
|
|
640
|
+
|
|
641
|
+
// 清理 v0.3.0~0.3.6 旧机制的残留(file:/link: 依赖与 node_modules 链接)
|
|
642
|
+
if (cleanupLegacyPluginState(profileDir, pkgFile)) {
|
|
643
|
+
console.log("✅ 已清理旧版写入 package.json 的 dsh-remote-ui 依赖与 node_modules 链接");
|
|
649
644
|
}
|
|
650
|
-
|
|
651
|
-
const linked = ensurePluginLinked(profileDir, pluginLocalDir);
|
|
652
|
-
console.log(linked
|
|
653
|
-
? `✅ 插件链接已就绪:${path.join(profileDir, "node_modules", "dsh-remote-ui")} → ${pluginLocalDir}`
|
|
654
|
-
: "✅ 插件依赖已就绪");
|
|
645
|
+
|
|
655
646
|
console.log(`✅ 插件安装完成。配置目录: ${CONFIG_DIR}`);
|
|
656
647
|
console.log(" 重启 dsh web(或重开 profile)后,在「设置 → 远程控制」查看面板。");
|
|
657
648
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrisega/dsh-remote",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
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",
|