返回 DeepSeek-Reasonix
packageGrants.ts
根目录 / desktop / electron / src / main / packageGrants.ts
1 import { execFileSync } from "node:child_process";
2
3 const SHOWN_PATHS = 5;
4
5 export interface GrantReport {
6 stripped: string[];
7 refused: string[];
8 }
9
10 export interface GrantTarget {
11 platform: NodeJS.Platform;
12 packaged: boolean;
13 execPath: string;
14 }
15
16 type Run = (file: string, args: string[], options: { encoding: "utf8"; timeout: number; windowsHide: boolean; stdio: ["ignore", "pipe", "pipe"] }) => string;
17
18 // Chromium's sandboxed children exit while loading a DLL whose access entries
19 // grant one Windows app package, so the window never paints. The service
20 // removes those grants from the tree this executable runs from, and it has to
21 // happen before Chromium starts its first child, which is why this is sync.
22 export function stripPackageGrants(binary: string, target: GrantTarget, log: (line: string) => void, run: Run = execFileSync as unknown as Run): GrantReport | null {
23 if (target.platform !== "win32" || !target.packaged) return null;
24 try {
25 const raw = run(binary, ["-strip-package-grants", "-app", target.execPath], {
26 encoding: "utf8",
27 timeout: 15000,
28 windowsHide: true,
29 stdio: ["ignore", "pipe", "pipe"],
30 });
31 return readGrantReport(raw);
32 } catch (error) {
33 const detail = error as { stderr?: unknown; message?: unknown };
34 log(`strip-package-grants: ${String(detail.stderr ?? "").trim() || String(detail.message ?? error)}`);
35 return null;
36 }
37 }
38
39 export function readGrantReport(raw: string): GrantReport {
40 const body = JSON.parse(raw) as { stripped?: unknown; refused?: unknown };
41 if (!Array.isArray(body?.stripped) || !Array.isArray(body?.refused)) {
42 throw new Error("the grant report carried no lists");
43 }
44 const stripped = body.stripped.filter((p): p is string => typeof p === "string");
45 const refused = body.refused.map((r) => (r as { path?: unknown })?.path).filter((p): p is string => typeof p === "string");
46 return { stripped, refused };
47 }
48
49 // What to tell someone whose window died before it painted, when the service
50 // already knows why: grants it found and could not remove. Any other cause is
51 // one this shell does not know, and it says nothing rather than guess.
52 export function unpaintedWindowCause(report: GrantReport | null, locale: string): { title: string; detail: string } | null {
53 if (!report?.refused.length) return null;
54 const shown = report.refused.slice(0, SHOWN_PATHS).join("\n");
55 const more = report.refused.length - SHOWN_PATHS;
56 if (locale.toLowerCase().startsWith("zh")) {
57 return {
58 title: "Reasonix 无法打开窗口",
59 detail:
60 "Reasonix 加载的文件上有授予某个 Windows 应用包(AppContainer)的访问项,Chromium 的沙箱进程加载这类文件时会退出。" +
61 "Reasonix 尝试移除它们,以下位置被拒绝:\n\n" + shown + (more > 0 ? `\n……另有 ${more} 处` : "") +
62 "\n\n以管理员身份运行一次 Reasonix,它会自行移除这些访问项。",
63 };
64 }
65 return {
66 title: "Reasonix could not open its window",
67 detail:
68 "Files Reasonix loads carry access entries granting a specific Windows app package (AppContainer), " +
69 "and Chromium's sandboxed processes exit while loading such a file. Reasonix tried to remove them and was refused for:\n\n" +
70 shown + (more > 0 ? `\n…and ${more} more` : "") +
71 "\n\nRun Reasonix once as administrator and it will remove them itself.",
72 };
73 }
74
74 lines TYPESCRIPT