Files
叫我小杨同学 ca7cd47a3d fix: 收录 DSH NEXT 识别与单测(#174 #175)
#171/#173 已在 1.1.80;#174 按官方 asar 路径认 DSH NEXT,不把任意 app.asar 当桌面。
2026-10-28 00:03:24 +08:00

140 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import path from "node:path";
import fs from "node:fs";
/** 当前已接线的宿主面。gui / tui 先能识别,适配仍回退 web。 */
export const SURFACES = Object.freeze(["web", "desktop", "gui", "tui"]);
function norm(value) {
return String(value || "").trim().toLowerCase();
}
function slash(value) {
return String(value || "").replace(/\\/g, "/").toLowerCase();
}
function exeName(execPath) {
return path.basename(String(execPath || "")).toLowerCase();
}
export function pathLooksDesktop(p) {
const n = slash(p);
if (!n) return false;
// 安装目录可能叫 "DeepSeek Harness" / "deepseek-harness" / "DeepseekHarnessDesktop"。
// 源码仓库也常带 deepseek-harness 名字,所以还要带 resources/、exe、.app/ 才算桌面安装。
const harnessDir =
n.includes("/deepseek harness/") ||
n.includes("/deepseek-harness/") ||
n.includes("/deepseekharness") ||
/\/deepseek[\s._-]*harness/.test(n);
return (
n.includes("/dsh desktop/") ||
n.includes("/dsh desktop.app/") ||
n.includes("dsh-desktop") ||
n.includes("dsh-plugin-desktop") ||
n.includes("dsh-desktop-next") ||
n.includes("/dsh next/") ||
n.includes("/dsh next.app/") ||
n.includes("dsh-next") ||
n.includes("/deepseek bot/") ||
n.includes("/deepseek-bot/") ||
n.includes("/ds-bot/") ||
n.includes("/deepseek harness.app/") ||
(harnessDir && (n.includes("/resources/") || n.includes("/contents/resources/") || n.endsWith("/resources"))) ||
/\/dsh desktop\.exe$/i.test(n) ||
/\/dsh next\.exe$/i.test(n) ||
/\/deepseek[\s._-]*harness\.exe$/i.test(n) ||
n.endsWith("/host-process-entry.js") ||
n.endsWith("/desktop-cli.js")
);
}
function resourcesLookLikeDesktopInstall(resourcesPath) {
if (!resourcesPath) return false;
if (pathLooksDesktop(resourcesPath)) return true;
const n = slash(resourcesPath);
if (!(n.endsWith("/resources") || n.includes("/contents/resources"))) return false;
try {
const asar = path.join(resourcesPath, "app.asar");
if (fs.existsSync(asar) && fs.statSync(asar).isFile()) return true;
if (fs.existsSync(path.join(resourcesPath, "app", "dsh"))) return true;
if (fs.existsSync(path.join(resourcesPath, "app.asar.unpacked", "dsh"))) return true;
} catch {
/* ignore */
}
return false;
}
/** 社区 Electron(DS Bot / DeepSeek Bot):profile 常是 ds-bot,DSH_SURFACE 还可能被写成 web。 */
export function isCommunityElectronHost(input = {}) {
const env = input.env ?? process.env;
const profile = norm(env.DSH_PROFILE);
if (profile === "ds-bot" || profile === "dsbot" || profile === "deepseek-bot") return true;
if (profile === "dsh-next" || profile === "dshnext" || profile === "dsh next") return true;
const execPath = input.execPath ?? process.execPath;
const name = exeName(execPath);
if (/^deepseek[\s._-]*bot(\.exe)?$/.test(name) || /^ds[\s._-]*bot(\.exe)?$/.test(name)) return true;
if (/^dsh[\s._-]*next(\.exe)?$/.test(name)) return true;
const p = slash(execPath);
if (p.includes("/deepseek bot") || p.includes("/deepseek-bot") || p.includes("/ds-bot/") || p.endsWith("/ds-bot")) return true;
if (p.includes("/dsh next") || p.includes("/dsh-next")) return true;
const resourcesPath = input.resourcesPath ?? process.resourcesPath;
const electron = (input.versions ?? process.versions)?.electron;
if (electron && resourcesLookLikeDesktopInstall(resourcesPath)) return true;
return false;
}
export function isDesktopSurface(input = {}) {
const env = input.env ?? process.env;
if (isCommunityElectronHost(input)) return true;
const forced = norm(env.DSH_SURFACE);
if (forced === "desktop") return true;
if (forced && SURFACES.includes(forced)) return false;
const execPath = input.execPath ?? process.execPath;
const name = exeName(execPath);
if (
name === "dsh desktop.exe" ||
name === "dsh desktop" ||
name === "dsh next.exe" ||
name === "dsh next" ||
name === "deepseek harness.exe" ||
name === "deepseek-harness.exe" ||
name === "deepseek harness" ||
name === "deepseek-harness" ||
/^deepseek[\s._-]*harness(\.exe)?$/.test(name) ||
/^dsh[\s._-]*next(\.exe)?$/.test(name)
) return true;
if (pathLooksDesktop(execPath)) return true;
const resourcesPath = input.resourcesPath ?? process.resourcesPath;
if (resourcesLookLikeDesktopInstall(resourcesPath)) return true;
const argv = input.argv ?? process.argv;
if ((Array.isArray(argv) ? argv : []).some((arg) => pathLooksDesktop(arg))) return true;
const parentPort = input.parentPort !== undefined ? input.parentPort : process.parentPort;
if (parentPort && (pathLooksDesktop(execPath) || resourcesLookLikeDesktopInstall(resourcesPath))) return true;
if (env.ELECTRON_RUN_AS_NODE && (pathLooksDesktop(execPath) || resourcesLookLikeDesktopInstall(resourcesPath))) return true;
return false;
}
function forcedSurface(env) {
const forced = norm(env.DSH_SURFACE);
return SURFACES.includes(forced) ? forced : "";
}
export function detectSurface(input = {}) {
const env = input.env ?? process.env;
if (isCommunityElectronHost(input)) return "desktop";
const forced = forcedSurface(env);
if (forced) return forced;
if (isDesktopSurface(input)) return "desktop";
return "web";
}
/** gui / tui 尚未单独适配,先走 web 重启与探测。 */
export function adapterFor(kind) {
return kind === "desktop" ? "desktop" : "web";
}