@mrrisega/dsh-remote 0.4.1 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrisega/dsh-remote",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
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",
@@ -6,9 +6,9 @@
6
6
  // - 代理 relay API(captcha / register / login / public-config),直连、不走系统代理
7
7
  //
8
8
  // 不依赖任何第三方包:只使用 node 内置模块与 cordis 注入的 webServer 服务。
9
- import { readFileSync, writeFileSync, mkdirSync, existsSync, realpathSync, accessSync, chmodSync, constants as fsConstants } from "node:fs";
9
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, realpathSync, accessSync, chmodSync, openSync, rmSync, constants as fsConstants } from "node:fs";
10
10
  import { join, dirname } from "node:path";
11
- import { execSync } from "node:child_process";
11
+ import { execSync, spawn } from "node:child_process";
12
12
  import { homedir, hostname, platform } from "node:os";
13
13
 
14
14
  /** 本插件在 host 侧的服务依赖。 */
@@ -250,6 +250,37 @@ function manualStatus() {
250
250
  return { watcher, bridge };
251
251
  }
252
252
 
253
+ // ---------- 插件市场一键全功能:缺桌面运行环境时自动后台安装 dsh-remote ----------
254
+
255
+ const PROVISION_MARKER = ".dsh-setup-installing";
256
+ const AUTO_INSTALL_LOG = ".dsh-setup-install.log";
257
+
258
+ /** 插件市场只装了 UI 插件;若桌面缺 dsh-remote 运行环境(dsh-setup.mjs=bridge/自启动),
259
+ * 由插件在后台自动执行一次 `npx @mrrisega/dsh-remote` 补齐,用户无需手动跑命令。
260
+ * 已有环境(包括手动 npx 装过)直接跳过。返回 true=已就绪。 */
261
+ function ensureRuntime(relayDir) {
262
+ if (existsSync(join(relayDir, "dsh-setup.mjs"))) return true;
263
+ const marker = join(relayDir, PROVISION_MARKER);
264
+ if (existsSync(marker)) return false; // 正在安装中
265
+ try {
266
+ mkdirSync(relayDir, { recursive: true });
267
+ writeFileSync(marker, String(Date.now()), { mode: 0o600 });
268
+ const log = join(relayDir, AUTO_INSTALL_LOG);
269
+ const npx = process.platform === "win32" ? "npx.cmd" : "npx";
270
+ const child = spawn(npx, ["--yes", "@mrrisega/dsh-remote"], {
271
+ detached: true,
272
+ stdio: ["ignore", openSync(log, "a"), openSync(log, "a")]
273
+ });
274
+ child.unref();
275
+ console.log(`[dsh-remote-ui] 检测到缺少桌面运行环境,已在后台自动安装(日志: ${log}),完成后将自动启动 bridge`);
276
+ return false;
277
+ } catch (e) {
278
+ console.warn(`[dsh-remote-ui] 自动安装启动失败: ${e.message}`);
279
+ try { rmSync(marker, { force: true }); } catch { /* ignore */ }
280
+ return false;
281
+ }
282
+ }
283
+
253
284
  /** 生成 plist(与 dsh-setup.mjs writeAutostartFile 同构),返回路径。 */
254
285
  function writeAutostartFile(relayDir) {
255
286
  const plistPath = launchAgentPath();
@@ -291,6 +322,29 @@ function startBridge(relayDir) {
291
322
  return { ok: st.running, status: st.running ? "running" : "failed", pid: st.pid, detail: st.running ? void 0 : "服务未进入运行态" };
292
323
  }
293
324
 
325
+ /** 运行时自愈 watcher:账号就绪后若环境缺失则自动安装;装好/重启后自动拉起 bridge。 */
326
+ function scheduleRuntime(relayDir) {
327
+ let done = false;
328
+ const iv = setInterval(() => {
329
+ if (done) { clearInterval(iv); return; }
330
+ try {
331
+ const cfg = loadConfig(relayDir);
332
+ const hasAcct = Boolean((cfg.phone || cfg.email) && cfg.password) || Boolean(cfg.local_key);
333
+ if (!hasAcct) return;
334
+ const setupUrl = join(relayDir, "dsh-setup.mjs");
335
+ if (!existsSync(setupUrl)) {
336
+ ensureRuntime(relayDir);
337
+ return;
338
+ }
339
+ const st = launchdStatus();
340
+ if (st.running) { done = true; clearInterval(iv); return; }
341
+ startBridge(relayDir);
342
+ } catch { /* 下一轮再试 */ }
343
+ }, 12_000);
344
+ iv.unref?.();
345
+ return () => clearInterval(iv);
346
+ }
347
+
294
348
  /** 停止 bridge:launchctl bootout。 */
295
349
  function stopBridge() {
296
350
  const target = launchTarget();
@@ -814,5 +868,7 @@ export function apply(ctx, config = {}) {
814
868
  ctx.effect(() => registerRoutes(ctx, relayDir), "dsh-remote-ui: /dsh-remote routes");
815
869
  // 0.1.2-rc.1+ 浏览器会话代持:换取 Harness 会话 Cookie 供 bridge 上游携带(手机点设备不再 401 白页)
816
870
  ctx.effect(() => scheduleHarnessMint(ctx, relayDir), "dsh-remote-ui: harness browser-session mint");
871
+ // 插件市场一键全功能:缺桌面运行环境则自动安装,登录后自动拉起 bridge(不依赖用户跑 npx)
872
+ ctx.effect(() => scheduleRuntime(relayDir), "dsh-remote-ui: runtime self-provision");
817
873
  ctx.logger?.info?.(`dsh-remote-ui: /dsh-remote routes ready (relayDir=${relayDir})`);
818
874
  }