dsh-windows-tray 1.0.13 → 1.0.14
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.md +79 -44
- package/bin/dsh-tray.js +37 -0
- package/docs/system-tray-guide.md +36 -42
- package/install.ps1 +48 -17
- package/launcher/dsh-actions.ps1 +46 -7
- package/launcher/dsh-boot.ps1 +57 -56
- package/launcher/dsh-lib.ps1 +312 -27
- package/launcher/dsh-menu.ps1 +7 -4
- package/launcher/dsh-tray.ps1 +174 -12
- package/launcher/dsh-update.ps1 +43 -8
- package/package.json +5 -5
- package/docs/tray-dev-archive.md +0 -59
package/launcher/dsh-actions.ps1
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# dsh-actions.ps1 —— 托盘菜单动作执行器 (由 dsh-tray.ps1 以独立隐藏进程拉起)
|
|
2
|
-
# 用法: pwsh -NoProfile -File dsh-actions.ps1 -Action start|stop|restart|tui-start|tui-stop
|
|
2
|
+
# 用法: pwsh -NoProfile -File dsh-actions.ps1 -Action start|stop|restart|tui-start|tui-stop|backup
|
|
3
3
|
# 动作在独立进程执行, 托盘 UI 线程保持响应; 日志写入 ~/.dsh/logs/dsh-tray-actions.log
|
|
4
4
|
param(
|
|
5
5
|
[Parameter(Mandatory = $true)]
|
|
6
|
-
[ValidateSet('start', 'stop', 'restart', 'tui-start', 'tui-stop')]
|
|
6
|
+
[ValidateSet('start', 'stop', 'restart', 'tui-start', 'tui-stop', 'backup')]
|
|
7
7
|
[string]$Action
|
|
8
8
|
)
|
|
9
9
|
$ErrorActionPreference = 'Stop'
|
|
@@ -20,29 +20,68 @@ try {
|
|
|
20
20
|
switch ($Action) {
|
|
21
21
|
'start' {
|
|
22
22
|
Log '启动 DSH (来自系统托盘)'
|
|
23
|
-
Start-Dsh -OpenBrowser:$false
|
|
23
|
+
if (-not (Start-Dsh -OpenBrowser:$false)) { Log '启动动作失败:12 秒内端口未监听(详见 web-menu.err.log)'; exit 1 }
|
|
24
24
|
Log '启动动作完成'
|
|
25
25
|
}
|
|
26
26
|
'stop' {
|
|
27
27
|
Log '停止 DSH (来自系统托盘)'
|
|
28
|
-
Stop-Dsh
|
|
28
|
+
if (-not (Stop-Dsh)) { Log '停止动作失败:端口未释放'; exit 1 }
|
|
29
29
|
Log '停止动作完成'
|
|
30
30
|
}
|
|
31
31
|
'restart' {
|
|
32
32
|
Log '重启 DSH (来自系统托盘)'
|
|
33
|
-
Restart-Dsh
|
|
33
|
+
if (-not (Restart-Dsh)) { Log '重启动作失败:12 秒内端口未监听(详见 web-menu.err.log)'; exit 1 }
|
|
34
34
|
Log '重启动作完成'
|
|
35
35
|
}
|
|
36
36
|
'tui-start' {
|
|
37
37
|
Log '启动 TUI (来自系统托盘)'
|
|
38
|
-
Start-Tui
|
|
38
|
+
if (-not (Start-Tui)) { Log 'TUI 启动动作失败:主进程 6 秒内未出现(看 TUI 窗口报错)'; exit 1 }
|
|
39
39
|
Log 'TUI 启动动作完成'
|
|
40
40
|
}
|
|
41
41
|
'tui-stop' {
|
|
42
42
|
Log '停止 TUI (来自系统托盘)'
|
|
43
|
-
Stop-Tui
|
|
43
|
+
if (-not (Stop-Tui)) { Log 'TUI 停止动作失败:进程未完全退出'; exit 1 }
|
|
44
44
|
Log 'TUI 停止动作完成'
|
|
45
45
|
}
|
|
46
|
+
'backup' {
|
|
47
|
+
# M2: 人工触发一次备份(用户裁定:只做可见性 + 人工触发,不做空窗告警/自动补跑)
|
|
48
|
+
Log '立即备份(来自系统托盘)'
|
|
49
|
+
# 功能开关准入(**必须在 $needInit 之前**,否则 OFF 档会先跑 init 建库;方案定稿 §4-4)
|
|
50
|
+
# exit 4 = 约定的"未启用"码(区别于 exit 3 = 已启用但缺引擎);已实测:库零变化
|
|
51
|
+
if (-not $script:BackupEnabled) {
|
|
52
|
+
Log '备份未启用(本机默认关闭;启用:plugins\dsh-windows-tray\install.ps1 -EnableBackup)'
|
|
53
|
+
exit 4
|
|
54
|
+
}
|
|
55
|
+
# 引擎在 **DSH_HOME**\tools(install.ps1 部署位),不是安装根 ~\.dsh\tools——非默认数据目录机器
|
|
56
|
+
# (DSH_HOME=D:\dsh 等)写死 USERPROFILE 会"备份引擎不存在"(2026-09-22 LJS 实测)
|
|
57
|
+
$dshHome = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $env:USERPROFILE '.dsh' }
|
|
58
|
+
$tool = Join-Path $dshHome 'tools\dsh-backup.mjs'
|
|
59
|
+
# 引擎不在位 ⇒ **明说"未就绪 + 怎么补"**,不许裸失败。托盘不耦合引擎(本 npm 包只含 UI/动作),
|
|
60
|
+
# 别人只装本包时走这条;exit=3 = 约定的"未就绪"码,托盘据此显示"未就绪"而不是"失败"。
|
|
61
|
+
if (-not (Test-Path -LiteralPath $tool)) {
|
|
62
|
+
Log "备份未就绪:缺引擎 $tool(两步部署第二步 = personal-docs\dsh\config\install.ps1)"
|
|
63
|
+
Log '备份未就绪:详见托盘 README「前置依赖」'
|
|
64
|
+
exit 3
|
|
65
|
+
}
|
|
66
|
+
# 首跑自动建库(兑现菜单文案"首次「立即备份」会自动挑盘建库";2026-09-22 LJS 实测:action 直接调
|
|
67
|
+
# backup ⇒ 无库机器 exit=1"先跑 init")。库未就绪(未配置 / key-salt 缺失)⇒ 先 init:引擎自动探测
|
|
68
|
+
# 盘位、生成口令文件、记入 backup-store.txt;多库/空间不足时引擎**报错让人决定**(不猜),经日志可见。
|
|
69
|
+
$needInit = (-not $BACKUP_STORE) -or -not (Test-Path -LiteralPath (Join-Path $BACKUP_STORE 'key-salt'))
|
|
70
|
+
if ($needInit) {
|
|
71
|
+
Log '备份库未就绪 ⇒ 先 init(引擎自动挑盘建库)'
|
|
72
|
+
$iout = (& $NODE $tool init 2>&1 | Out-String).Trim()
|
|
73
|
+
Log ("init: exit={0} :: {1}" -f $LASTEXITCODE, ($iout -replace "`r?`n", ' | '))
|
|
74
|
+
if ($LASTEXITCODE -ne 0) { throw "备份库 init 退出码 $LASTEXITCODE" }
|
|
75
|
+
}
|
|
76
|
+
foreach ($obj in @('data', 'runtime', 'vault')) {
|
|
77
|
+
# --strict-empty:扫描到 0 文件也算失败(证伪 F-04:托盘这条路径按退出码判结果,不该拿 0 冒充"对象是空的")
|
|
78
|
+
$out = (& $NODE $tool backup $obj --strict-empty 2>&1 | Out-String).Trim()
|
|
79
|
+
$code = $LASTEXITCODE
|
|
80
|
+
Log ("backup {0}: exit={1} :: {2}" -f $obj, $code, ($out -replace "`r?`n", ' | '))
|
|
81
|
+
if ($code -ne 0) { throw "backup $obj 退出码 $code" }
|
|
82
|
+
}
|
|
83
|
+
Log '备份动作完成'
|
|
84
|
+
}
|
|
46
85
|
}
|
|
47
86
|
} catch {
|
|
48
87
|
Log ("动作 {0} 失败: {1}" -f $Action, $_.Exception.Message)
|
package/launcher/dsh-boot.ps1
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
|
-
# dsh-boot.ps1 -- DSH (DeepSeek Harness) logon autostart: bring up dsh web silently.
|
|
2
|
-
#
|
|
3
|
-
# Used by the Startup shortcut "DeepSeek Harness (dsh).lnk":
|
|
4
|
-
# pwsh.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File <this file>
|
|
5
|
-
#
|
|
6
|
-
# Why this file exists instead of calling dsh-menu.ps1 -Cmd start:
|
|
7
|
-
# dsh-menu.ps1 starts node WITHOUT redirecting stdout, so the dsh 0.1.2+ launch token
|
|
8
|
-
# never lands in logs\dsh-windows-tray\web-menu.out.log. The tray parses that file to
|
|
9
|
-
# open an ALREADY authorized Web UI tab; without it the tray falls back to the naked
|
|
10
|
-
# URL and the user gets the "authentication required" page after every boot.
|
|
11
|
-
# Routing through dsh-lib.ps1's Start-Dsh = the exact path the tray menu uses
|
|
12
|
-
# (hidden node, correct working dir, redirected logs, idempotent tray ensure).
|
|
13
|
-
#
|
|
14
|
-
# ASCII-ONLY ON PURPOSE: this file must stay parseable by Windows PowerShell 5.1 as well
|
|
15
|
-
# as pwsh 7. A no-BOM UTF-8 file with non-ASCII text is silently mis-parsed by 5.1
|
|
16
|
-
# (ParserError) -- that failure mode already cost one machine its autostart
|
|
17
|
-
# (see personal-docs dsh/docs/install-ledger.md, 2026-09-10 entry). Do not add non-ASCII here.
|
|
18
|
-
#
|
|
19
|
-
# Contract / boundaries:
|
|
20
|
-
# - exits 0 when web is up (started here or already running); exits 1 on any failure
|
|
21
|
-
# - every outcome is appended to dsh-boot.log (in the tray log dir once dsh-lib loads,
|
|
22
|
-
# else %TEMP%) -- hidden window means the log is the only diagnostic channel
|
|
23
|
-
# - idempotent: safe to run next to the tray's own autostart shortcut (dsh-lib reuses
|
|
24
|
-
# the existing listener; dsh-tray.ps1 is mutex-guarded)
|
|
25
|
-
|
|
26
|
-
$ErrorActionPreference = 'Stop'
|
|
27
|
-
$script:LogPath = Join-Path $env:TEMP 'dsh-boot.log'
|
|
28
|
-
|
|
29
|
-
function Write-BootLog([string]$Message) {
|
|
30
|
-
try {
|
|
31
|
-
Add-Content -LiteralPath $script:LogPath -Value ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message) -ErrorAction SilentlyContinue
|
|
32
|
-
} catch { }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
$lib = Join-Path $PSScriptRoot 'dsh-lib.ps1'
|
|
37
|
-
if (-not (Test-Path -LiteralPath $lib)) { throw "dsh-lib.ps1 not found next to dsh-boot.ps1: $lib" }
|
|
38
|
-
. $lib
|
|
39
|
-
if ($LOG_DIR) { $script:LogPath = Join-Path $LOG_DIR 'dsh-boot.log' }
|
|
40
|
-
|
|
41
|
-
Write-BootLog ('boot begin (pid {0})' -f $PID)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
# dsh-boot.ps1 -- DSH (DeepSeek Harness) logon autostart: bring up dsh web silently.
|
|
2
|
+
#
|
|
3
|
+
# Used by the Startup shortcut "DeepSeek Harness (dsh).lnk":
|
|
4
|
+
# pwsh.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File <this file>
|
|
5
|
+
#
|
|
6
|
+
# Why this file exists instead of calling dsh-menu.ps1 -Cmd start:
|
|
7
|
+
# dsh-menu.ps1 starts node WITHOUT redirecting stdout, so the dsh 0.1.2+ launch token
|
|
8
|
+
# never lands in logs\dsh-windows-tray\web-menu.out.log. The tray parses that file to
|
|
9
|
+
# open an ALREADY authorized Web UI tab; without it the tray falls back to the naked
|
|
10
|
+
# URL and the user gets the "authentication required" page after every boot.
|
|
11
|
+
# Routing through dsh-lib.ps1's Start-Dsh = the exact path the tray menu uses
|
|
12
|
+
# (hidden node, correct working dir, redirected logs, idempotent tray ensure).
|
|
13
|
+
#
|
|
14
|
+
# ASCII-ONLY ON PURPOSE: this file must stay parseable by Windows PowerShell 5.1 as well
|
|
15
|
+
# as pwsh 7. A no-BOM UTF-8 file with non-ASCII text is silently mis-parsed by 5.1
|
|
16
|
+
# (ParserError) -- that failure mode already cost one machine its autostart
|
|
17
|
+
# (see personal-docs dsh/docs/install-ledger.md, 2026-09-10 entry). Do not add non-ASCII here.
|
|
18
|
+
#
|
|
19
|
+
# Contract / boundaries:
|
|
20
|
+
# - exits 0 when web is up (started here or already running); exits 1 on any failure
|
|
21
|
+
# - every outcome is appended to dsh-boot.log (in the tray log dir once dsh-lib loads,
|
|
22
|
+
# else %TEMP%) -- hidden window means the log is the only diagnostic channel
|
|
23
|
+
# - idempotent: safe to run next to the tray's own autostart shortcut (dsh-lib reuses
|
|
24
|
+
# the existing listener; dsh-tray.ps1 is mutex-guarded)
|
|
25
|
+
|
|
26
|
+
$ErrorActionPreference = 'Stop'
|
|
27
|
+
$script:LogPath = Join-Path $env:TEMP 'dsh-boot.log'
|
|
28
|
+
|
|
29
|
+
function Write-BootLog([string]$Message) {
|
|
30
|
+
try {
|
|
31
|
+
Add-Content -LiteralPath $script:LogPath -Value ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message) -ErrorAction SilentlyContinue
|
|
32
|
+
} catch { }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
$lib = Join-Path $PSScriptRoot 'dsh-lib.ps1'
|
|
37
|
+
if (-not (Test-Path -LiteralPath $lib)) { throw "dsh-lib.ps1 not found next to dsh-boot.ps1: $lib" }
|
|
38
|
+
. $lib
|
|
39
|
+
if ($LOG_DIR) { $script:LogPath = Join-Path $LOG_DIR 'dsh-boot.log' }
|
|
40
|
+
|
|
41
|
+
Write-BootLog ('boot begin (pid {0})' -f $PID)
|
|
42
|
+
# R2-P2#1: listening port != dsh web; verify identity (bin.js) before calling "already running"
|
|
43
|
+
$before = @(Get-WebPid | Where-Object { Test-WebIdentity $_ })
|
|
44
|
+
Start-Dsh -OpenBrowser:$false
|
|
45
|
+
$after = @(Get-WebPid | Where-Object { Test-WebIdentity $_ })
|
|
46
|
+
if ($after.Count -gt 0) {
|
|
47
|
+
$how = 'started'
|
|
48
|
+
if ($before.Count -gt 0) { $how = 'already running' }
|
|
49
|
+
Write-BootLog ('boot ok ({0}), web pid: {1}' -f $how, ($after -join ','))
|
|
50
|
+
} else {
|
|
51
|
+
Write-BootLog ('boot FAILED: port {0} not listening after start (see web-menu.err.log)' -f $PORT)
|
|
52
|
+
exit 1
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
Write-BootLog ('boot FAILED: ' + $_.Exception.Message)
|
|
56
|
+
exit 1
|
|
57
|
+
}
|