@morlay/dsh-desktop-shell 0.2.9 → 0.2.10-alpha.0
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/dist/{appconfig-CSZh3JG7.mjs → appconfig-Ci991oVH.mjs} +1 -2
- package/dist/appconfig.d.mts +1 -3
- package/dist/appconfig.mjs +1 -1
- package/dist/{dshhome-DAJ9dkn2.mjs → dshhome-wAhMMZji.mjs} +1 -2
- package/dist/dshhome.d.mts +1 -3
- package/dist/dshhome.mjs +1 -1
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +47 -52
- package/dist/inline-packages.d.mts +1 -3
- package/dist/inline-packages.mjs +1 -2
- package/dist/{official-BmgBsdhq.mjs → official-NWEpqAc6.mjs} +1 -2
- package/dist/official.d.mts +1 -3
- package/dist/official.mjs +1 -1
- package/dist/preload-app.cjs +3 -7
- package/dist/{profile-project-BgQILDbu.mjs → profile-project-DsOmRRGB.mjs} +1 -2
- package/dist/profile-project.d.mts +1 -3
- package/dist/profile-project.mjs +1 -1
- package/dist/{seed-Bxr0BsCk.mjs → seed-Z7k1Wt9H.mjs} +2 -3
- package/dist/seed.d.mts +1 -3
- package/dist/seed.mjs +1 -1
- package/package.json +8 -8
- package/src/__tests__/ipc.spec.ts +10 -5
- package/src/directory-picker.ts +2 -2
- package/src/index.ts +3 -3
- package/src/ipc.ts +17 -4
- package/src/shortcuts.ts +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
|
|
3
|
+
// src/appconfig.ts
|
|
4
4
|
const PROFILE_NAME = "desktop";
|
|
5
5
|
const PROFILE_PATCH_NAME = "cordis.patch.yml";
|
|
6
6
|
function isRecord(value) {
|
|
@@ -35,5 +35,4 @@ async function loadAppConfig(exeDir) {
|
|
|
35
35
|
async function writeAppConfig(exeDir, config) {
|
|
36
36
|
await writeFile(join(exeDir, "appconfig.json"), `${JSON.stringify(config, void 0, 2)}\n`, { mode: 384 });
|
|
37
37
|
}
|
|
38
|
-
//#endregion
|
|
39
38
|
export { writeAppConfig as i, PROFILE_PATCH_NAME as n, loadAppConfig as r, PROFILE_NAME as t };
|
package/dist/appconfig.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/appconfig.d.ts
|
|
2
1
|
export declare const PROFILE_NAME = "desktop";
|
|
3
2
|
export declare const PROFILE_PATCH_NAME = "cordis.patch.yml";
|
|
4
3
|
export interface AppWindowConfig {
|
|
@@ -17,5 +16,4 @@ export interface AppConfig {
|
|
|
17
16
|
readonly window: AppWindowConfig;
|
|
18
17
|
}
|
|
19
18
|
export declare function loadAppConfig(exeDir: string): Promise<AppConfig>;
|
|
20
|
-
export declare function writeAppConfig(exeDir: string, config: AppConfig): Promise<void>;
|
|
21
|
-
//#endregion
|
|
19
|
+
export declare function writeAppConfig(exeDir: string, config: AppConfig): Promise<void>;
|
package/dist/appconfig.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as PROFILE_NAME, n as PROFILE_PATCH_NAME, r as loadAppConfig, i as writeAppConfig } from "./appconfig-
|
|
1
|
+
export { t as PROFILE_NAME, n as PROFILE_PATCH_NAME, r as loadAppConfig, i as writeAppConfig } from "./appconfig-Ci991oVH.mjs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAbsolute, join, resolve } from "node:path";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
|
|
3
|
+
// src/dshhome.ts
|
|
4
4
|
function xdgDataHome() {
|
|
5
5
|
if (process.platform === "darwin") return join(homedir(), "Library", "Application Support");
|
|
6
6
|
const configured = process.env.XDG_DATA_HOME;
|
|
@@ -18,5 +18,4 @@ function resolveDshHome(config) {
|
|
|
18
18
|
if (override !== void 0 && override.trim() !== "") return resolve(override);
|
|
19
19
|
return resolveConfiguredHome(config.name, config.dataDir);
|
|
20
20
|
}
|
|
21
|
-
//#endregion
|
|
22
21
|
export { resolveDshHome as n, xdgDataHome as r, resolveConfiguredHome as t };
|
package/dist/dshhome.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { AppConfig } from "./appconfig.mjs";
|
|
2
|
-
//#region src/dshhome.d.ts
|
|
3
2
|
export declare function xdgDataHome(): string;
|
|
4
3
|
export declare function resolveConfiguredHome(name: string, spec: string): string | undefined;
|
|
5
|
-
export declare function resolveDshHome(config: AppConfig): string | undefined;
|
|
6
|
-
//#endregion
|
|
4
|
+
export declare function resolveDshHome(config: AppConfig): string | undefined;
|
package/dist/dshhome.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as resolveConfiguredHome, n as resolveDshHome, r as xdgDataHome } from "./dshhome-
|
|
1
|
+
export { t as resolveConfiguredHome, n as resolveDshHome, r as xdgDataHome } from "./dshhome-wAhMMZji.mjs";
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as DESKTOP_HOST_PACKAGE } from "./official-
|
|
2
|
-
import { r as loadAppConfig, t as PROFILE_NAME } from "./appconfig-
|
|
3
|
-
import { n as resolveDshHome } from "./dshhome-
|
|
4
|
-
import { a as ensureSeedProfile, i as SEED_RUNTIME_DIR_NAME } from "./seed-
|
|
1
|
+
import { t as DESKTOP_HOST_PACKAGE } from "./official-NWEpqAc6.mjs";
|
|
2
|
+
import { r as loadAppConfig, t as PROFILE_NAME } from "./appconfig-Ci991oVH.mjs";
|
|
3
|
+
import { n as resolveDshHome } from "./dshhome-wAhMMZji.mjs";
|
|
4
|
+
import { a as ensureSeedProfile, i as SEED_RUNTIME_DIR_NAME } from "./seed-Z7k1Wt9H.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { access, appendFile, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
7
7
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -10,7 +10,7 @@ import "rolldown";
|
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
11
|
import { once } from "node:events";
|
|
12
12
|
import { randomBytes } from "node:crypto";
|
|
13
|
-
|
|
13
|
+
// src/electron.ts
|
|
14
14
|
/**
|
|
15
15
|
* `electron` 模块的**唯一入口**(壳自己的其它文件都从这里取)。
|
|
16
16
|
*
|
|
@@ -31,13 +31,11 @@ const nativeTheme = electron.nativeTheme;
|
|
|
31
31
|
const protocol = electron.protocol;
|
|
32
32
|
const Menu = electron.Menu;
|
|
33
33
|
const MenuItem = electron.MenuItem;
|
|
34
|
-
|
|
35
|
-
//#region ../dsh-desktop-host/src/preload-source.ts
|
|
34
|
+
// ../dsh-desktop-host/src/preload-source.ts
|
|
36
35
|
/** 壳取 preload 的路径(`GET` 它得到可执行的 CJS 字节)。 */
|
|
37
36
|
const PRELOAD_SOURCE_PATH = "/@dsh/preload.cjs";
|
|
38
37
|
join("dist", "preload.cjs");
|
|
39
|
-
|
|
40
|
-
//#region src/ipc.ts
|
|
38
|
+
// src/ipc.ts
|
|
41
39
|
const DESKTOP_IPC = {
|
|
42
40
|
directoryPick: "dsh-desktop:directory-pick",
|
|
43
41
|
nativeThemeSet: "dsh-desktop:native-theme-set",
|
|
@@ -61,20 +59,33 @@ function desktopScheme(name) {
|
|
|
61
59
|
if (!/^[a-z][a-z0-9+.-]*$/u.test(bare)) throw new Error(`dsh desktop: application name "${name}" does not yield a usable URL scheme (${bare})`);
|
|
62
60
|
return bare;
|
|
63
61
|
}
|
|
64
|
-
|
|
62
|
+
/**
|
|
63
|
+
* 校验一次 IPC 来自**本应用的页面**:senderFrame 的 origin 必须在 `allowedOrigins` 里。
|
|
64
|
+
*
|
|
65
|
+
* 页面从宿主 webServer 的 origin(`http://127.0.0.1:<port>`)载入——早先那条自定义协议
|
|
66
|
+
* `scheme://app/` 已经不再承载页面(它没有 origin,settings 的 describe 流拿不到答复)。所以这里比的是
|
|
67
|
+
* origin,而不是 scheme + hostname:后者会让所有真实页面都被判成 unowned。
|
|
68
|
+
* @param event - Electron 的 invoke 事件。
|
|
69
|
+
* @param allowedOrigins - 允许的页面 origin(应用自己的宿主 origin)。
|
|
70
|
+
*/
|
|
71
|
+
function assertDesktopSender(event, allowedOrigins) {
|
|
65
72
|
const senderFrame = event.senderFrame;
|
|
66
73
|
if (senderFrame === null) throw new Error("dsh desktop: rejected IPC without a sender frame");
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
let origin;
|
|
75
|
+
try {
|
|
76
|
+
origin = new URL(senderFrame.url).origin;
|
|
77
|
+
} catch {
|
|
78
|
+
throw new Error("dsh desktop: rejected IPC with an unreadable sender URL");
|
|
79
|
+
}
|
|
80
|
+
if (!allowedOrigins.includes(origin)) throw new Error("dsh desktop: rejected IPC from an unowned renderer");
|
|
69
81
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
function installDesktopDirectoryPicker(getWindow, scheme) {
|
|
82
|
+
// src/directory-picker.ts
|
|
83
|
+
function installDesktopDirectoryPicker(getWindow, getOrigin) {
|
|
73
84
|
const pending = /* @__PURE__ */ new WeakMap();
|
|
74
85
|
ipcMain.handle(DESKTOP_IPC.directoryPick, async (event) => {
|
|
75
86
|
const window = getWindow();
|
|
76
87
|
if (window === void 0 || window.isDestroyed() || event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame) throw new Error("dsh desktop: rejected directory picker from an unowned renderer");
|
|
77
|
-
assertDesktopSender(event,
|
|
88
|
+
assertDesktopSender(event, [getOrigin()]);
|
|
78
89
|
const existing = pending.get(window);
|
|
79
90
|
if (existing !== void 0) return existing;
|
|
80
91
|
if (window.isMinimized()) window.restore();
|
|
@@ -87,12 +98,10 @@ function installDesktopDirectoryPicker(getWindow, scheme) {
|
|
|
87
98
|
return result;
|
|
88
99
|
});
|
|
89
100
|
}
|
|
90
|
-
|
|
91
|
-
//#region ../dsh-desktop-host/src/paths.ts
|
|
101
|
+
// ../dsh-desktop-host/src/paths.ts
|
|
92
102
|
/** 应用 origin 下托管调试窗口前端产物的前缀。 */
|
|
93
103
|
const DEVTOOLS_ASSETS_PREFIX = "/node-devtools";
|
|
94
|
-
|
|
95
|
-
//#region src/developer-endpoint.ts
|
|
104
|
+
// src/developer-endpoint.ts
|
|
96
105
|
/** DevTools 前端在托管前缀下的入口路径(与上游底部面板用的是同一份产物)。 */
|
|
97
106
|
const DEVTOOLS_ENTRY = "devtools_app.html";
|
|
98
107
|
/**
|
|
@@ -110,8 +119,7 @@ function nodeInspectorDevtoolsUrl(scheme, endpoint) {
|
|
|
110
119
|
if (!/^[\u0021-\u007e]+$/u.test(socket) || socket.includes("&") || socket.includes("#")) throw new Error(`dsh desktop: the inspector endpoint is not usable in a query: ${endpoint}`);
|
|
111
120
|
return `${scheme}://app${DEVTOOLS_ASSETS_PREFIX}/${DEVTOOLS_ENTRY}?ws=${socket}&panel=console&disableLocaleInfoBar=true`;
|
|
112
121
|
}
|
|
113
|
-
|
|
114
|
-
//#region src/developer-menu.ts
|
|
122
|
+
// src/developer-menu.ts
|
|
115
123
|
function reportFailure(options, title, detail) {
|
|
116
124
|
console.error(`[dsh-shell] ${title}: ${detail}`);
|
|
117
125
|
const parent = options.mainWindow();
|
|
@@ -408,8 +416,7 @@ function isDesktopHostEvent(value) {
|
|
|
408
416
|
if (value.type === "inspected") return (value.url === null || typeof value.url === "string") && (value.message === void 0 || typeof value.message === "string");
|
|
409
417
|
return false;
|
|
410
418
|
}
|
|
411
|
-
|
|
412
|
-
//#region src/host-process.ts
|
|
419
|
+
// src/host-process.ts
|
|
413
420
|
function errorOf(reason, fallback) {
|
|
414
421
|
return reason instanceof Error ? reason : new Error(fallback);
|
|
415
422
|
}
|
|
@@ -802,8 +809,7 @@ var DesktopHostProcess = class {
|
|
|
802
809
|
this.responsePipe?.resume();
|
|
803
810
|
}
|
|
804
811
|
};
|
|
805
|
-
|
|
806
|
-
//#region ../../../vendor/deepseek-harness/packages/client/shortcuts/src/binding.ts
|
|
812
|
+
// ../../../vendor/deepseek-harness/packages/client/shortcuts/src/binding.ts
|
|
807
813
|
const keyNames = {
|
|
808
814
|
Slash: "/",
|
|
809
815
|
Comma: ",",
|
|
@@ -889,8 +895,7 @@ function isWebBindingAllowed(binding, platform) {
|
|
|
889
895
|
}
|
|
890
896
|
].some((candidate) => bindingKey(binding) === bindingKey(normalizeBinding(candidate, platform)));
|
|
891
897
|
}
|
|
892
|
-
|
|
893
|
-
//#region ../../../vendor/deepseek-harness/packages/util/values/src/partial-json.ts
|
|
898
|
+
// ../../../vendor/deepseek-harness/packages/util/values/src/partial-json.ts
|
|
894
899
|
/**
|
|
895
900
|
* Lazily scanned view of one JSON object's top-level fields, built from text
|
|
896
901
|
* that may still be streaming or from an already parsed object. Nothing is
|
|
@@ -1513,8 +1518,7 @@ function isHex(c) {
|
|
|
1513
1518
|
this.#current = null;
|
|
1514
1519
|
}
|
|
1515
1520
|
});
|
|
1516
|
-
|
|
1517
|
-
//#region ../../../vendor/deepseek-harness/packages/util/values/src/index.ts
|
|
1521
|
+
// ../../../vendor/deepseek-harness/packages/util/values/src/index.ts
|
|
1518
1522
|
/**
|
|
1519
1523
|
* Mark an unreachable closed-union branch.
|
|
1520
1524
|
* @param value - impossible value; an unhandled typed variant fails at the call site.
|
|
@@ -1525,8 +1529,7 @@ function assertNever(value, context) {
|
|
|
1525
1529
|
const rendered = JSON.stringify(value) ?? String(value);
|
|
1526
1530
|
throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
|
|
1527
1531
|
}
|
|
1528
|
-
|
|
1529
|
-
//#region ../../../vendor/deepseek-harness/packages/client/shortcuts/src/configuration.ts
|
|
1532
|
+
// ../../../vendor/deepseek-harness/packages/client/shortcuts/src/configuration.ts
|
|
1530
1533
|
/** Validated preference documents and deterministic conflict resolution, without browser dependencies. */
|
|
1531
1534
|
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1532
1535
|
const commandPattern = /^[a-z][a-zA-Z0-9-]*(?:\.[a-zA-Z][a-zA-Z0-9-]*)+$/u;
|
|
@@ -1773,8 +1776,7 @@ function parseShortcutDefinitions(value) {
|
|
|
1773
1776
|
}
|
|
1774
1777
|
return definitions;
|
|
1775
1778
|
}
|
|
1776
|
-
|
|
1777
|
-
//#region ../../../vendor/deepseek-harness/packages/util/crypto/src/index.ts
|
|
1779
|
+
// ../../../vendor/deepseek-harness/packages/util/crypto/src/index.ts
|
|
1778
1780
|
/**
|
|
1779
1781
|
* Random v4 UUID, minted from `crypto.getRandomValues`.
|
|
1780
1782
|
* @returns the UUID string.
|
|
@@ -1786,8 +1788,7 @@ function randomUUID() {
|
|
|
1786
1788
|
}).join("");
|
|
1787
1789
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
1788
1790
|
}
|
|
1789
|
-
|
|
1790
|
-
//#region ../../../vendor/deepseek-harness/packages/client/shortcuts/src/persistence.ts
|
|
1791
|
+
// ../../../vendor/deepseek-harness/packages/client/shortcuts/src/persistence.ts
|
|
1791
1792
|
/** Serialized preference transactions; storage owners publish only accepted writes or read diagnostics. */
|
|
1792
1793
|
/**
|
|
1793
1794
|
* Create a disabled initial snapshot for asynchronous adapter startup.
|
|
@@ -1935,8 +1936,7 @@ var ShortcutPersistence = class {
|
|
|
1935
1936
|
return this.snapshot;
|
|
1936
1937
|
}
|
|
1937
1938
|
};
|
|
1938
|
-
|
|
1939
|
-
//#region ../../../vendor/deepseek-harness/packages/util/atomic-write/src/index.ts
|
|
1939
|
+
// ../../../vendor/deepseek-harness/packages/util/atomic-write/src/index.ts
|
|
1940
1940
|
/**
|
|
1941
1941
|
* Zero-dependency atomic file replacement and writer coordination.
|
|
1942
1942
|
* `writeFileAtomic` writes a random-suffix sibling with exclusive create and
|
|
@@ -2010,8 +2010,7 @@ async function writeFileAtomic(filename, content, options) {
|
|
|
2010
2010
|
throw error;
|
|
2011
2011
|
}
|
|
2012
2012
|
}
|
|
2013
|
-
|
|
2014
|
-
//#region src/keybindings.ts
|
|
2013
|
+
// src/keybindings.ts
|
|
2015
2014
|
function desktopKeybindings(userData, platform, publish) {
|
|
2016
2015
|
const path = join(userData, "keybindings.json");
|
|
2017
2016
|
return new ShortcutPersistence({
|
|
@@ -2029,9 +2028,8 @@ function desktopKeybindings(userData, platform, publish) {
|
|
|
2029
2028
|
})
|
|
2030
2029
|
}, "desktop", platform, false, publish);
|
|
2031
2030
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
function installDesktopShortcuts(getWindow, userData, platform, scheme) {
|
|
2031
|
+
// src/shortcuts.ts
|
|
2032
|
+
function installDesktopShortcuts(getWindow, userData, platform, getOrigin) {
|
|
2035
2033
|
let definitions = [];
|
|
2036
2034
|
let recording = false;
|
|
2037
2035
|
let revision;
|
|
@@ -2045,7 +2043,7 @@ function installDesktopShortcuts(getWindow, userData, platform, scheme) {
|
|
|
2045
2043
|
};
|
|
2046
2044
|
const persistence = desktopKeybindings(userData, platform, publish);
|
|
2047
2045
|
const windowOf = (event) => {
|
|
2048
|
-
assertDesktopSender(event,
|
|
2046
|
+
assertDesktopSender(event, [getOrigin()]);
|
|
2049
2047
|
const window = getWindow();
|
|
2050
2048
|
if (window === void 0 || window.isDestroyed() || event.sender !== window.webContents) throw new Error("desktop shortcuts: rejected sender");
|
|
2051
2049
|
return window;
|
|
@@ -2207,8 +2205,7 @@ function installDesktopShortcuts(getWindow, userData, platform, scheme) {
|
|
|
2207
2205
|
}
|
|
2208
2206
|
};
|
|
2209
2207
|
}
|
|
2210
|
-
|
|
2211
|
-
//#region src/shell-env.ts
|
|
2208
|
+
// src/shell-env.ts
|
|
2212
2209
|
function rcFileFor(shell) {
|
|
2213
2210
|
switch (basename(shell)) {
|
|
2214
2211
|
case "bash": return "~/.bashrc";
|
|
@@ -2234,8 +2231,7 @@ function shellWrappedSpawn(command, args, options) {
|
|
|
2234
2231
|
detached: process.platform !== "win32"
|
|
2235
2232
|
});
|
|
2236
2233
|
}
|
|
2237
|
-
|
|
2238
|
-
//#region src/index.ts
|
|
2234
|
+
// src/index.ts
|
|
2239
2235
|
let focusPrimaryWindow = () => {};
|
|
2240
2236
|
/** app 目录(dev 是装配目录、打包是 `Resources/app`):由引导写进全局。 */
|
|
2241
2237
|
function shellRoot() {
|
|
@@ -2493,7 +2489,7 @@ async function main() {
|
|
|
2493
2489
|
if (!app.isPackaged) shellLog(`${request.method} ${url.pathname} → ${String(response.status)}`);
|
|
2494
2490
|
return response;
|
|
2495
2491
|
});
|
|
2496
|
-
installDesktopDirectoryPicker(() => mainWindow,
|
|
2492
|
+
installDesktopDirectoryPicker(() => mainWindow, () => hostOrigin);
|
|
2497
2493
|
let nextStreamId = 1;
|
|
2498
2494
|
const streamEncoder = new TextEncoder();
|
|
2499
2495
|
const activeStreams = /* @__PURE__ */ new Map();
|
|
@@ -2508,7 +2504,7 @@ async function main() {
|
|
|
2508
2504
|
}
|
|
2509
2505
|
};
|
|
2510
2506
|
ipcMain.handle(DESKTOP_IPC.streamOpen, async (event, endpoint, payload) => {
|
|
2511
|
-
assertDesktopSender(event,
|
|
2507
|
+
assertDesktopSender(event, [hostOrigin]);
|
|
2512
2508
|
if (typeof endpoint !== "string") throw new Error("dsh desktop: stream endpoint must be text");
|
|
2513
2509
|
const active = host;
|
|
2514
2510
|
if (active === void 0) throw new Error("dsh desktop: Host is unavailable");
|
|
@@ -2593,7 +2589,7 @@ async function main() {
|
|
|
2593
2589
|
if (mainWindow === void 0 || event.sender !== mainWindow.webContents) return;
|
|
2594
2590
|
if (source === "light" || source === "dark" || source === "system") nativeTheme.themeSource = source;
|
|
2595
2591
|
});
|
|
2596
|
-
const shortcuts = installDesktopShortcuts(() => mainWindow, app.getPath("userData"), process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux",
|
|
2592
|
+
const shortcuts = installDesktopShortcuts(() => mainWindow, app.getPath("userData"), process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux", () => hostOrigin);
|
|
2597
2593
|
app.on("will-quit", () => {
|
|
2598
2594
|
shortcuts.dispose();
|
|
2599
2595
|
});
|
|
@@ -2693,5 +2689,4 @@ if ((() => {
|
|
|
2693
2689
|
dialog.showErrorBox("dsh desktop startup failed", message);
|
|
2694
2690
|
app.exit(1);
|
|
2695
2691
|
});
|
|
2696
|
-
//#endregion
|
|
2697
2692
|
export {};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/inline-packages.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* 壳产物里要**内联**的包(而不是留在产物外)。
|
|
4
3
|
*
|
|
@@ -10,5 +9,4 @@
|
|
|
10
9
|
* 这份名单是**一个事实**:壳自己的构建([`tsdown.config.ts`](../../tsdown.config.ts))与 `@local/dshdev` 的
|
|
11
10
|
* 装配(`prepareShell`)都用它——两边不一致的话,装配出来的产物就会缺依赖。
|
|
12
11
|
*/
|
|
13
|
-
export declare const SHELL_INLINE_PACKAGES: readonly string[];
|
|
14
|
-
//#endregion
|
|
12
|
+
export declare const SHELL_INLINE_PACKAGES: readonly string[];
|
package/dist/inline-packages.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/inline-packages.ts
|
|
2
2
|
/**
|
|
3
3
|
* 壳产物里要**内联**的包(而不是留在产物外)。
|
|
4
4
|
*
|
|
@@ -17,5 +17,4 @@ const SHELL_INLINE_PACKAGES = [
|
|
|
17
17
|
"@deepseek-ai/dsh-util-values",
|
|
18
18
|
"@deepseek-ai/dsh-util-crypto"
|
|
19
19
|
];
|
|
20
|
-
//#endregion
|
|
21
20
|
export { SHELL_INLINE_PACKAGES };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/official.ts
|
|
2
2
|
const DESKTOP_HOST_PACKAGE = "@morlay/dsh-desktop-host";
|
|
3
3
|
const OFFICIAL_PROFILE_BUNDLES = ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app"];
|
|
4
|
-
//#endregion
|
|
5
4
|
export { OFFICIAL_PROFILE_BUNDLES as n, DESKTOP_HOST_PACKAGE as t };
|
package/dist/official.d.mts
CHANGED
package/dist/official.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as DESKTOP_HOST_PACKAGE, n as OFFICIAL_PROFILE_BUNDLES } from "./official-
|
|
1
|
+
export { t as DESKTOP_HOST_PACKAGE, n as OFFICIAL_PROFILE_BUNDLES } from "./official-NWEpqAc6.mjs";
|
package/dist/preload-app.cjs
CHANGED
|
@@ -32,8 +32,7 @@ function syncNativeTheme(platform, root, send) {
|
|
|
32
32
|
if (root.readyState === "loading") root.defaultView?.addEventListener("DOMContentLoaded", observe, { once: true });
|
|
33
33
|
else observe();
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
//#region src/ipc.ts
|
|
35
|
+
// src/ipc.ts
|
|
37
36
|
/** 壳把宿主回环的 ws 端口经 preload 递进页面(`additionalArguments`,与 scheme 同一手法)。 */
|
|
38
37
|
const DESKTOP_WS_PORT_ARGUMENT = "--dsh-desktop-ws-port";
|
|
39
38
|
const DESKTOP_IPC = {
|
|
@@ -54,8 +53,7 @@ const DESKTOP_IPC = {
|
|
|
54
53
|
shortcutsChanged: "dsh-desktop:shortcuts-changed"
|
|
55
54
|
};
|
|
56
55
|
const DESKTOP_SCHEME_ARGUMENT = "--dsh-desktop-scheme";
|
|
57
|
-
|
|
58
|
-
//#region src/stream-uplink.ts
|
|
56
|
+
// src/stream-uplink.ts
|
|
59
57
|
function createStreamUplink(send, end) {
|
|
60
58
|
let streamId;
|
|
61
59
|
let closed = false;
|
|
@@ -80,8 +78,7 @@ function createStreamUplink(send, end) {
|
|
|
80
78
|
}
|
|
81
79
|
};
|
|
82
80
|
}
|
|
83
|
-
|
|
84
|
-
//#region src/preload-app.ts
|
|
81
|
+
// src/preload-app.ts
|
|
85
82
|
const scheme = process.argv.find((argument) => argument.startsWith(`${DESKTOP_SCHEME_ARGUMENT}=`))?.slice(21);
|
|
86
83
|
const wsPort = process.argv.find((argument) => argument.startsWith(`${DESKTOP_WS_PORT_ARGUMENT}=`))?.slice(22);
|
|
87
84
|
if (scheme !== void 0 && scheme !== "") {
|
|
@@ -170,4 +167,3 @@ electron.contextBridge.exposeInMainWorld("dshDesktop", {
|
|
|
170
167
|
syncNativeTheme(process.platform, document, (source) => {
|
|
171
168
|
electron.ipcRenderer.send(DESKTOP_IPC.nativeThemeSet, source);
|
|
172
169
|
});
|
|
173
|
-
//#endregion
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
|
|
4
|
+
// src/profile-project.ts
|
|
5
5
|
const PROFILE_WORKSPACE_NAME = "pnpm-workspace.yaml";
|
|
6
6
|
const PROFILE_RUNTIME_REPORT_NAME = "desktop-runtime-packages.json";
|
|
7
7
|
const PROFILE_VENDOR_DIR_NAME = "vendor";
|
|
@@ -67,5 +67,4 @@ function installProfile(options) {
|
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
-
//#endregion
|
|
71
70
|
export { runtimeOverrides as a, installProfile as i, PROFILE_VENDOR_DIR_NAME as n, workspaceWithOverrides as o, PROFILE_WORKSPACE_NAME as r, PROFILE_RUNTIME_REPORT_NAME as t };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
//#region src/profile-project.d.ts
|
|
3
2
|
export declare const PROFILE_WORKSPACE_NAME = "pnpm-workspace.yaml";
|
|
4
3
|
export declare const PROFILE_RUNTIME_REPORT_NAME = "desktop-runtime-packages.json";
|
|
5
4
|
export declare const PROFILE_VENDOR_DIR_NAME = "vendor";
|
|
@@ -16,5 +15,4 @@ export interface ProfileInstallOptions {
|
|
|
16
15
|
readonly profileDir: string;
|
|
17
16
|
readonly spawn?: ProfileInstallSpawn;
|
|
18
17
|
}
|
|
19
|
-
export declare function installProfile(options: ProfileInstallOptions): Promise<void>;
|
|
20
|
-
//#endregion
|
|
18
|
+
export declare function installProfile(options: ProfileInstallOptions): Promise<void>;
|
package/dist/profile-project.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as PROFILE_RUNTIME_REPORT_NAME, n as PROFILE_VENDOR_DIR_NAME, r as PROFILE_WORKSPACE_NAME, i as installProfile, a as runtimeOverrides, o as workspaceWithOverrides } from "./profile-project-
|
|
1
|
+
export { t as PROFILE_RUNTIME_REPORT_NAME, n as PROFILE_VENDOR_DIR_NAME, r as PROFILE_WORKSPACE_NAME, i as installProfile, a as runtimeOverrides, o as workspaceWithOverrides } from "./profile-project-DsOmRRGB.mjs";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { n as PROFILE_PATCH_NAME, t as PROFILE_NAME } from "./appconfig-
|
|
1
|
+
import { n as PROFILE_PATCH_NAME, t as PROFILE_NAME } from "./appconfig-Ci991oVH.mjs";
|
|
2
2
|
import { access, chmod, copyFile, lstat, mkdir, readFile, readdir, readlink, rm, symlink, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
-
|
|
5
|
+
// src/seed.ts
|
|
6
6
|
const SEED_DIR_NAME = "dsh-home";
|
|
7
7
|
const SEED_HASH_NAME = ".seed-hash";
|
|
8
8
|
const SEED_RUNTIME_DIR_NAME = "runtime";
|
|
@@ -137,5 +137,4 @@ async function copySeed(source, target) {
|
|
|
137
137
|
};
|
|
138
138
|
await visit(source, "");
|
|
139
139
|
}
|
|
140
|
-
//#endregion
|
|
141
140
|
export { ensureSeedProfile as a, SEED_RUNTIME_DIR_NAME as i, SEED_DIR_NAME as n, SEED_HASH_NAME as r, PROFILE_MODULES_ARCHIVE_NAME as t };
|
package/dist/seed.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/seed.d.ts
|
|
2
1
|
export declare const SEED_DIR_NAME = "dsh-home";
|
|
3
2
|
export declare const SEED_HASH_NAME = ".seed-hash";
|
|
4
3
|
export declare const SEED_RUNTIME_DIR_NAME = "runtime";
|
|
@@ -17,5 +16,4 @@ export type SeedOutcome = "seeded" | "up-to-date" | "no-seed";
|
|
|
17
16
|
* @returns 种了(`seeded`)、已是最新(`up-to-date`)、或随包根本没有这份种(`no-seed`)。
|
|
18
17
|
* @throws 归档解压失败、或解压后没有依赖面时。
|
|
19
18
|
*/
|
|
20
|
-
export declare function ensureSeedProfile(seedDir: string, home: string): Promise<SeedOutcome>;
|
|
21
|
-
//#endregion
|
|
19
|
+
export declare function ensureSeedProfile(seedDir: string, home: string): Promise<SeedOutcome>;
|
package/dist/seed.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as PROFILE_MODULES_ARCHIVE_NAME, n as SEED_DIR_NAME, r as SEED_HASH_NAME, i as SEED_RUNTIME_DIR_NAME, a as ensureSeedProfile } from "./seed-
|
|
1
|
+
export { t as PROFILE_MODULES_ARCHIVE_NAME, n as SEED_DIR_NAME, r as SEED_HASH_NAME, i as SEED_RUNTIME_DIR_NAME, a as ensureSeedProfile } from "./seed-Z7k1Wt9H.mjs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/dsh-desktop-shell",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10-alpha.0",
|
|
4
4
|
"description": "Electron shell for dsh desktop apps: serves the app's own <name>:// scheme, boots the desktop profile through the @morlay/dsh-desktop-host child over byte pipes, and installs the profile seed on first launch. The CLI that packages a workspace around it is @local/dshdev.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"desktop",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"./locale/*.json": "./locale/*.json"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@deepseek-ai/dsh-util-crypto": "^0.2.
|
|
58
|
-
"@deepseek-ai/dsh-util-values": "^0.2.
|
|
59
|
-
"@morlay/dsh-desktop-host": "0.2.
|
|
57
|
+
"@deepseek-ai/dsh-util-crypto": "^0.2.2-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-util-values": "^0.2.2-alpha.2",
|
|
59
|
+
"@morlay/dsh-desktop-host": "0.2.10-alpha.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@local/devkit": "0.1.0",
|
|
@@ -65,12 +65,12 @@
|
|
|
65
65
|
"electron": "44.7.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@deepseek-ai/dsh-app-boot": "^0.2.
|
|
69
|
-
"@deepseek-ai/dsh-atomic-write": "^0.2.
|
|
70
|
-
"@deepseek-ai/dsh-client-shortcuts": "^0.2.
|
|
68
|
+
"@deepseek-ai/dsh-app-boot": "^0.2.2-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-atomic-write": "^0.2.2-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-client-shortcuts": "^0.2.2-alpha.2"
|
|
71
71
|
},
|
|
72
72
|
"engines": {
|
|
73
|
-
"dsh": ">= 0.2.
|
|
73
|
+
"dsh": ">= 0.2.2-alpha.2 < 0.2.3",
|
|
74
74
|
"node": ">=24.11.0"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
@@ -22,13 +22,18 @@ describe("桌面标识取自 app 名", () => {
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
it("
|
|
26
|
-
const
|
|
27
|
-
expect(() => assertDesktopSender(sender(`${
|
|
28
|
-
|
|
25
|
+
it("发送者校验比的是页面 origin(页面从宿主 origin 载入,不是自定义协议)", () => {
|
|
26
|
+
const origin = "http://127.0.0.1:51234";
|
|
27
|
+
expect(() => assertDesktopSender(sender(`${origin}/?token=x`), [origin])).not.toThrow();
|
|
28
|
+
// 自定义协议那条老路:页面早已不从它载入,不能再被当成本应用。
|
|
29
|
+
expect(() => assertDesktopSender(sender("dsh-custom-next://app/"), [origin])).toThrow(
|
|
29
30
|
/unowned renderer/u,
|
|
30
31
|
);
|
|
31
|
-
|
|
32
|
+
// 别的 origin(另一个本地端口、外部站点)同样拒绝。
|
|
33
|
+
expect(() => assertDesktopSender(sender("http://127.0.0.1:59999/"), [origin])).toThrow(
|
|
34
|
+
/unowned renderer/u,
|
|
35
|
+
);
|
|
36
|
+
expect(() => assertDesktopSender(sender("https://example.com/"), [origin])).toThrow(
|
|
32
37
|
/unowned renderer/u,
|
|
33
38
|
);
|
|
34
39
|
});
|
package/src/directory-picker.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { DESKTOP_IPC, assertDesktopSender } from "./ipc.ts";
|
|
|
4
4
|
|
|
5
5
|
export function installDesktopDirectoryPicker(
|
|
6
6
|
getWindow: () => BrowserWindowType | undefined,
|
|
7
|
-
|
|
7
|
+
getOrigin: () => string,
|
|
8
8
|
): void {
|
|
9
9
|
const pending = new WeakMap<BrowserWindowType, Promise<string | null>>();
|
|
10
10
|
ipcMain.handle(DESKTOP_IPC.directoryPick, async (event) => {
|
|
@@ -17,7 +17,7 @@ export function installDesktopDirectoryPicker(
|
|
|
17
17
|
) {
|
|
18
18
|
throw new Error("dsh desktop: rejected directory picker from an unowned renderer");
|
|
19
19
|
}
|
|
20
|
-
assertDesktopSender(event,
|
|
20
|
+
assertDesktopSender(event, [getOrigin()]);
|
|
21
21
|
const existing = pending.get(window);
|
|
22
22
|
if (existing !== undefined) return existing;
|
|
23
23
|
if (window.isMinimized()) window.restore();
|
package/src/index.ts
CHANGED
|
@@ -457,7 +457,7 @@ async function main(): Promise<void> {
|
|
|
457
457
|
return response;
|
|
458
458
|
});
|
|
459
459
|
|
|
460
|
-
installDesktopDirectoryPicker(() => mainWindow,
|
|
460
|
+
installDesktopDirectoryPicker(() => mainWindow, () => hostOrigin);
|
|
461
461
|
|
|
462
462
|
// 桌面流载体:主进程自己构造 Request(body 是流——首行定 endpoint/payload,后续行是上行项),
|
|
463
463
|
// 把响应体逐块推回页面——页面 fetch 到自定义协议的 POST body 在 Chromium 上不可靠。
|
|
@@ -481,7 +481,7 @@ async function main(): Promise<void> {
|
|
|
481
481
|
}
|
|
482
482
|
};
|
|
483
483
|
ipcMain.handle(DESKTOP_IPC.streamOpen, async (event, endpoint: unknown, payload: unknown) => {
|
|
484
|
-
assertDesktopSender(event,
|
|
484
|
+
assertDesktopSender(event, [hostOrigin]);
|
|
485
485
|
if (typeof endpoint !== "string") throw new Error("dsh desktop: stream endpoint must be text");
|
|
486
486
|
const active = host;
|
|
487
487
|
if (active === undefined) throw new Error("dsh desktop: Host is unavailable");
|
|
@@ -571,7 +571,7 @@ async function main(): Promise<void> {
|
|
|
571
571
|
() => mainWindow,
|
|
572
572
|
app.getPath("userData"),
|
|
573
573
|
process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux",
|
|
574
|
-
|
|
574
|
+
() => hostOrigin,
|
|
575
575
|
);
|
|
576
576
|
app.on("will-quit", () => {
|
|
577
577
|
shortcuts.dispose();
|
package/src/ipc.ts
CHANGED
|
@@ -44,15 +44,28 @@ export function desktopScheme(name: string): string {
|
|
|
44
44
|
return bare;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* 校验一次 IPC 来自**本应用的页面**:senderFrame 的 origin 必须在 `allowedOrigins` 里。
|
|
49
|
+
*
|
|
50
|
+
* 页面从宿主 webServer 的 origin(`http://127.0.0.1:<port>`)载入——早先那条自定义协议
|
|
51
|
+
* `scheme://app/` 已经不再承载页面(它没有 origin,settings 的 describe 流拿不到答复)。所以这里比的是
|
|
52
|
+
* origin,而不是 scheme + hostname:后者会让所有真实页面都被判成 unowned。
|
|
53
|
+
* @param event - Electron 的 invoke 事件。
|
|
54
|
+
* @param allowedOrigins - 允许的页面 origin(应用自己的宿主 origin)。
|
|
55
|
+
*/
|
|
47
56
|
export function assertDesktopSender(
|
|
48
57
|
event: IpcMainInvokeEvent,
|
|
49
|
-
|
|
50
|
-
hostnames: readonly string[],
|
|
58
|
+
allowedOrigins: readonly string[],
|
|
51
59
|
): void {
|
|
52
60
|
const senderFrame = event.senderFrame;
|
|
53
61
|
if (senderFrame === null) throw new Error("dsh desktop: rejected IPC without a sender frame");
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
let origin: string;
|
|
63
|
+
try {
|
|
64
|
+
origin = new URL(senderFrame.url).origin;
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error("dsh desktop: rejected IPC with an unreadable sender URL");
|
|
67
|
+
}
|
|
68
|
+
if (!allowedOrigins.includes(origin)) {
|
|
56
69
|
throw new Error("dsh desktop: rejected IPC from an unowned renderer");
|
|
57
70
|
}
|
|
58
71
|
}
|
package/src/shortcuts.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function installDesktopShortcuts(
|
|
|
33
33
|
getWindow: () => BrowserWindowType | undefined,
|
|
34
34
|
userData: string,
|
|
35
35
|
platform: ShortcutPlatform,
|
|
36
|
-
|
|
36
|
+
getOrigin: () => string,
|
|
37
37
|
): DesktopShortcuts {
|
|
38
38
|
let definitions: readonly ShortcutDefinition[] = [];
|
|
39
39
|
let recording = false;
|
|
@@ -63,7 +63,7 @@ export function installDesktopShortcuts(
|
|
|
63
63
|
const persistence = desktopKeybindings(userData, platform, publish);
|
|
64
64
|
|
|
65
65
|
const windowOf = (event: Electron.IpcMainInvokeEvent): BrowserWindowType => {
|
|
66
|
-
assertDesktopSender(event,
|
|
66
|
+
assertDesktopSender(event, [getOrigin()]);
|
|
67
67
|
const window = getWindow();
|
|
68
68
|
if (window === undefined || window.isDestroyed() || event.sender !== window.webContents)
|
|
69
69
|
throw new Error("desktop shortcuts: rejected sender");
|