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.
@@ -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)
@@ -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
- $before = @(Get-WebPid)
43
- Start-Dsh -OpenBrowser:$false
44
- $after = @(Get-WebPid)
45
- if ($after.Count -gt 0) {
46
- $how = 'started'
47
- if ($before.Count -gt 0) { $how = 'already running' }
48
- Write-BootLog ('boot ok ({0}), web pid: {1}' -f $how, ($after -join ','))
49
- } else {
50
- Write-BootLog ('boot FAILED: port {0} not listening after start (see web-menu.err.log)' -f $PORT)
51
- exit 1
52
- }
53
- } catch {
54
- Write-BootLog ('boot FAILED: ' + $_.Exception.Message)
55
- exit 1
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
+ }