dsh-auto-open-web 0.1.4 → 0.1.5
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/README.en.md +24 -26
- package/README.md +19 -20
- package/host-publish/DshAppWindow.deps.json +2 -2
- package/host-publish/DshAppWindow.dll +0 -0
- package/host-publish/DshAppWindow.exe +0 -0
- package/host-publish/DshAppWindow.pdb +0 -0
- package/lib/client.js +530 -264
- package/lib/index.js +34 -99
- package/package.json +9 -6
package/lib/index.js
CHANGED
|
@@ -15,20 +15,47 @@
|
|
|
15
15
|
* 不与正常浏览器页面共用进程);再失败则不打开任何东西(仅记录日志)。
|
|
16
16
|
*
|
|
17
17
|
* 配置来源:行配置(cordis.patch.yml)作为启动种子;设置页卡片(客户端 bundle,
|
|
18
|
-
* settings.plugin.item 插槽)
|
|
19
|
-
* settings
|
|
18
|
+
* settings.plugin.item 插槽)经官方 settings 域(settingsScope)读写本命名
|
|
19
|
+
* 空间,持久化到官方 settings 文档(首次保存后设置值优先于行配置)。
|
|
20
|
+
* 宿主保留的辅助路由:/auto-open-web/pick-browser(原生浏览对话框)、
|
|
21
|
+
* /auto-open-web/test-browser(浏览器拉起测试)。
|
|
20
22
|
*
|
|
21
23
|
* 端口来自 webServer 服务的真实监听值(--port 自定义、--port 0 均正确)。
|
|
22
24
|
* 打开窗口的子进程 detached + unref,不阻塞 DSH、不随 DSH 退出被终止
|
|
23
25
|
* (WebView2 宿主例外:随 DSH 退出而关闭)。
|
|
24
26
|
*/
|
|
25
27
|
import { spawn } from 'node:child_process';
|
|
26
|
-
import { existsSync,
|
|
28
|
+
import { existsSync, rmSync, writeFileSync } from 'node:fs';
|
|
29
|
+
import { createRequire } from 'node:module';
|
|
27
30
|
import { join } from 'node:path';
|
|
28
|
-
import z from '@deepseek-ai/schemastery';
|
|
29
31
|
import { dshHome, appProfileDir, testProfileDir, instanceStateFile } from './paths.js';
|
|
30
32
|
import * as platform from './platform.js';
|
|
31
33
|
|
|
34
|
+
/**
|
|
35
|
+
* schemastery 依赖策略(与 win32.js 的 koffi 同一模式):本包零 dependencies,
|
|
36
|
+
* schemastery 由 DSH 部署自带(官方核心依赖),运行时解析:
|
|
37
|
+
* 常规 import(profile 已装/手动安装)优先 → Windows 全局 npm 布局的
|
|
38
|
+
* DSH 部署副本;两者都不可用时模块加载失败(插件依赖 Config,无法降级)。
|
|
39
|
+
*/
|
|
40
|
+
let z = null;
|
|
41
|
+
try {
|
|
42
|
+
z = (await import('@deepseek-ai/schemastery')).default;
|
|
43
|
+
} catch {
|
|
44
|
+
try {
|
|
45
|
+
const globalRoot = process.env.APPDATA ? join(process.env.APPDATA, 'npm', 'node_modules') : '';
|
|
46
|
+
const dshEntry = join(globalRoot, '@deepseek-ai', 'dsh', 'package.json');
|
|
47
|
+
if (existsSync(dshEntry)) z = createRequire(dshEntry)('@deepseek-ai/schemastery');
|
|
48
|
+
} catch {
|
|
49
|
+
/* 两次解析均失败 → 下方抛错 */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (z === null || z === undefined) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
'[auto-open-web] schemastery not found (neither local nor DSH deployment); ' +
|
|
55
|
+
'this plugin cannot build its config schema. Check the DSH deployment.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
32
59
|
export const name = 'auto-open-web';
|
|
33
60
|
|
|
34
61
|
/**
|
|
@@ -53,7 +80,6 @@ export const Config = z.object({
|
|
|
53
80
|
exitOnWindowClose: z.boolean().default(false),
|
|
54
81
|
});
|
|
55
82
|
|
|
56
|
-
const CONFIG_API_PATH = '/auto-open-web/config';
|
|
57
83
|
const PICK_API_PATH = '/auto-open-web/pick-browser';
|
|
58
84
|
const TEST_API_PATH = '/auto-open-web/test-browser';
|
|
59
85
|
const ICON_SIZES = [16, 32, 48, 64, 128, 256];
|
|
@@ -311,48 +337,6 @@ async function ensureIconFile(server) {
|
|
|
311
337
|
|
|
312
338
|
// ── 插件主体 ─────────────────────────────────────────────────────────────
|
|
313
339
|
|
|
314
|
-
/**
|
|
315
|
-
* dsh 宿主版本兼容范围(与 package.json 的 peerDependencies 一致):
|
|
316
|
-
* settings.plugin.item 自 dsh 0.1.0-rc.7 起为 keyed 插槽(注册需 options.key);
|
|
317
|
-
* 0.1.4 的卡片注册同时提供 id 与 key,新旧两代 dsh 均可挂载,但低于
|
|
318
|
-
* 0.1.0-rc.7 时官方 UI 未定义该插槽的 keyed 语义,超出支持范围。
|
|
319
|
-
*/
|
|
320
|
-
const DSH_MIN_VERSION = '0.1.0-rc.7';
|
|
321
|
-
const DSH_MAX_VERSION = '0.2.0';
|
|
322
|
-
|
|
323
|
-
/** 解析 "x.y.z[-rc.N]" 为数值数组;rc 缺失视为 Infinity(正式版 > 同号 rc)。失败返回 null。 */
|
|
324
|
-
function parseVersion(text) {
|
|
325
|
-
const m = /^(\d+)\.(\d+)\.(\d+)(?:-rc\.(\d+))?/.exec(String(text).trim());
|
|
326
|
-
if (m === null) return null;
|
|
327
|
-
return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] !== undefined ? Number(m[4]) : Infinity];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function compareVersions(a, b) {
|
|
331
|
-
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2] || a[3] - b[3];
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** dsh 版本是否在支持范围内;版本未知(读不到/解析失败)返回 true(不误伤)。 */
|
|
335
|
-
export function isDshVersionSupported(versionText) {
|
|
336
|
-
const v = parseVersion(versionText);
|
|
337
|
-
const min = parseVersion(DSH_MIN_VERSION);
|
|
338
|
-
const max = parseVersion(DSH_MAX_VERSION);
|
|
339
|
-
if (v === null || min === null || max === null) return true;
|
|
340
|
-
return compareVersions(v, min) >= 0 && compareVersions(v, max) < 0;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/** 尽力读取部署的 dsh 版本(Windows 全局 npm 布局;其余环境返回 null)。 */
|
|
344
|
-
export function detectDshVersion() {
|
|
345
|
-
try {
|
|
346
|
-
const globalRoot = process.env.APPDATA ? join(process.env.APPDATA, 'npm', 'node_modules') : '';
|
|
347
|
-
const entry = join(globalRoot, '@deepseek-ai', 'dsh', 'package.json');
|
|
348
|
-
if (!existsSync(entry)) return null;
|
|
349
|
-
const version = JSON.parse(readFileSync(entry, 'utf8')).version;
|
|
350
|
-
return typeof version === 'string' && version !== '' ? version : null;
|
|
351
|
-
} catch {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
340
|
/**
|
|
357
341
|
* 窗口退出监听共享标志:"窗口关闭时退出 DSH"当前是否生效。
|
|
358
342
|
* 监听始终注册(见 openAppWindow/spawnHostWindow),行为由本标志实时决定:
|
|
@@ -374,24 +358,11 @@ export const internals = {
|
|
|
374
358
|
setExitWatcherEnabled(value) {
|
|
375
359
|
exitWatcherEnabled = value === true;
|
|
376
360
|
},
|
|
377
|
-
isDshVersionSupported,
|
|
378
|
-
detectDshVersion,
|
|
379
361
|
};
|
|
380
362
|
|
|
381
363
|
export function apply(ctx, config) {
|
|
382
364
|
removeLegacyState();
|
|
383
365
|
|
|
384
|
-
// dsh 宿主版本约束(声明性):低于支持范围时明确提示(宿主能力兼容,
|
|
385
|
-
// 卡片注册做了新旧双兼容,不硬停;日志指导用户升级 dsh)。
|
|
386
|
-
const dshVersion = detectDshVersion();
|
|
387
|
-
if (dshVersion !== null && !isDshVersionSupported(dshVersion)) {
|
|
388
|
-
console.error(
|
|
389
|
-
`[auto-open-web] DSH version ${dshVersion} is outside the supported range ` +
|
|
390
|
-
`(${DSH_MIN_VERSION} <= dsh < ${DSH_MAX_VERSION}); the settings card may not mount. ` +
|
|
391
|
-
'Please upgrade dsh to continue using this plugin.',
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
366
|
// browser 模式的专用实例随 DSH 退出:正常退出时结束其实例进程树
|
|
396
367
|
// (被强杀时由下次启动前的预清理兜底)。
|
|
397
368
|
process.on('exit', () => {
|
|
@@ -434,7 +405,9 @@ export function apply(ctx, config) {
|
|
|
434
405
|
}
|
|
435
406
|
syncExitWatcher(); // 启动时的最终生效值(settings 已合并或行配置)
|
|
436
407
|
|
|
437
|
-
// ----
|
|
408
|
+
// ---- 卡片辅助 API(浏览/测试;设置读写走官方 settings 域) ----
|
|
409
|
+
// 设置卡片经客户端 settingsScope(settings 域)读写本命名空间,不再自建
|
|
410
|
+
// config 路由;这里只保留官方通道无法覆盖的能力。
|
|
438
411
|
function sendJson(res, status, value) {
|
|
439
412
|
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' });
|
|
440
413
|
res.end(JSON.stringify(value));
|
|
@@ -445,44 +418,6 @@ export function apply(ctx, config) {
|
|
|
445
418
|
return body;
|
|
446
419
|
}
|
|
447
420
|
if (typeof server.register === 'function') {
|
|
448
|
-
server.register({
|
|
449
|
-
kind: 'exact',
|
|
450
|
-
path: CONFIG_API_PATH,
|
|
451
|
-
handler: async (req, res) => {
|
|
452
|
-
if (req.method === 'GET') {
|
|
453
|
-
sendJson(res, 200, { ok: true, ...state, writable });
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
if (req.method === 'POST') {
|
|
457
|
-
if (!writable) {
|
|
458
|
-
sendJson(res, 200, { ok: false, error: '当前配置只读(settings 不可写)' });
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
let parsed;
|
|
462
|
-
try {
|
|
463
|
-
parsed = JSON.parse(await readBody(req));
|
|
464
|
-
} catch {
|
|
465
|
-
sendJson(res, 400, { ok: false, error: 'invalid JSON body' });
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
try {
|
|
469
|
-
const next = normalizeState(parsed);
|
|
470
|
-
if (scope !== null) await scope.update(next);
|
|
471
|
-
state = next;
|
|
472
|
-
syncExitWatcher(); // 保存后即时生效,当前会话无需重启
|
|
473
|
-
sendJson(res, 200, { ok: true, ...state });
|
|
474
|
-
} catch (error) {
|
|
475
|
-
sendJson(res, 200, {
|
|
476
|
-
ok: false,
|
|
477
|
-
error: String(error && error.message !== undefined ? error.message : error),
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
res.writeHead(405);
|
|
483
|
-
res.end();
|
|
484
|
-
},
|
|
485
|
-
});
|
|
486
421
|
// 原生"浏览"对话框:选择浏览器可执行文件(平台能力;非 Windows 提示手动输入)。
|
|
487
422
|
server.register({
|
|
488
423
|
kind: 'exact',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-auto-open-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Open the DSH Web GUI in an app-style WebView2 window on profile start, with a settings card for browser paths",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"inject": [
|
|
23
23
|
"@deepseek-ai/dsh-client-connection",
|
|
24
24
|
"@deepseek-ai/dsh-api-remotes",
|
|
25
|
-
"@deepseek-ai/dsh-client-ui-settings"
|
|
25
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
26
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
27
|
+
"@deepseek-ai/dsh-client-locale"
|
|
26
28
|
]
|
|
27
29
|
}
|
|
28
30
|
},
|
|
@@ -39,15 +41,16 @@
|
|
|
39
41
|
"README.md",
|
|
40
42
|
"README.en.md"
|
|
41
43
|
],
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
44
|
-
},
|
|
45
44
|
"peerDependencies": {
|
|
46
|
-
"@deepseek-ai/dsh": ">=0.1.0-rc.7 <0.2.0"
|
|
45
|
+
"@deepseek-ai/dsh": ">=0.1.0-rc.7 <0.2.0",
|
|
46
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"@deepseek-ai/dsh": {
|
|
50
50
|
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"@deepseek-ai/schemastery": {
|
|
53
|
+
"optional": true
|
|
51
54
|
}
|
|
52
55
|
},
|
|
53
56
|
"license": "MIT"
|