diff --git a/AGENTS.md b/AGENTS.md index b10e1a8..e365f74 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,6 +80,7 @@ IDs must remain synchronized across the catalog, default navigation, icon map, a - Supported deployments are Docker and Node.js 22.12.0+ with `web-tools.ps1` on Windows Server. A Windows single EXE is an optional future deliverable and is not currently built or supported. - Keep production port changes synchronized across `server/index.mjs`, `web-tools.ps1`, `Dockerfile`, `docker-compose.yml`, `.env.example`, deployment tests, and both READMEs where applicable. - The Windows update flow requires a clean `main` checkout and uses `git pull --ff-only`. Preserve external `C:\ProgramData\WebTools\data` state. +- Running `web-tools.ps1` without an action is an interactive user entry point and must keep the start/update/update-without-start/exit menu. Scheduled tasks and automation must pass an explicit `start` or `update` action so they never wait for input. - New catalog data is loaded by the Node process at startup. Rebuilding `dist` alone is insufficient after catalog changes; restart the server and refresh already-open browser pages. - If a single EXE is later authorized, use Node.js SEA with embedded read-only web/catalog assets and an external writable data directory. Add per-architecture builds, asset-read abstraction, Windows smoke tests, replacement/update instructions, and optional Authenticode signing. Derive EXE version metadata from `package.json`; do not create another manually maintained version source. Do not commit generated executables. diff --git a/CHANGELOG.md b/CHANGELOG.md index c44aa7b..fd347c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - 固件 Hash 在普通 HTTP 局域网访问时增加本地 SHA-256/SHA-512 回退,不再依赖 `SubtleCrypto` 必须可用。 - 生产服务在监听端口前检查 `dist/index.html`,缺少构建产物时立即给出构建命令,不再启动后按请求重复返回 500。 - 修复固件 Hash 和安全随机数模式选择框宽度过大,以及 801-1040 像素中间宽度下 CRC 参数区可能横向溢出的问题。 +- 修复 Windows 脚本无参数运行时直接启动旧 `dist`、容易被误认为已完成更新的问题;现在交互启动会明确选择启动、更新并启动、仅更新或退出。 ### Documentation diff --git a/README.md b/README.md index 372fa06..ff9ff87 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\web-tools.ps1 start The script listens on `0.0.0.0:9000` by default and stores navigation settings in `C:\ProgramData\WebTools\data\navigation.json`. It accepts `-ListenAddress`, `-Port`, and `-DataDir` overrides. It does not create Windows Firewall rules. +Running `web-tools.ps1` without an action opens an interactive menu: start the existing build, update/rebuild/start, update/rebuild without starting, or exit. Explicit `start` and `update` arguments remain available for shortcuts, scheduled tasks, and automation. When launching interactively after a repository update, choose option `2`; option `1` intentionally starts the existing `dist` without rebuilding it. + To update a clean checkout on the `main` branch, stop the foreground process with `Ctrl+C`, then run: ```powershell diff --git a/README.zh-CN.md b/README.zh-CN.md index 9acfca8..7ef57db 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -160,6 +160,17 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\web-tools.ps1 start ` -DataDir C:\ProgramData\WebTools\data ``` +不带操作参数运行 `web-tools.ps1` 时会显示交互菜单: + +```text +[1] Start current build (no update) +[2] Update from Git, rebuild, and start +[3] Update from Git and rebuild only +[0] Exit +``` + +日常双击或使用“使用 PowerShell 运行”打开脚本后,需要更新代码时选择 `2`;`1` 只启动磁盘上已有的 `dist`,不会拉取代码或重新构建。计划任务、快捷方式和自动化仍应显式传入 `start`、`update` 或 `update -NoStart`,不会进入菜单等待输入。 + 脚本不会自动修改 Windows 防火墙。需要从局域网访问默认端口时,可由管理员仅对专用网络配置入站规则: ```powershell diff --git a/tests/deploymentConfig.test.mjs b/tests/deploymentConfig.test.mjs index 18dcee9..933611c 100644 --- a/tests/deploymentConfig.test.mjs +++ b/tests/deploymentConfig.test.mjs @@ -50,6 +50,17 @@ describe('deployment configuration', () => { expect(script).toContain("@('ci', '--include=dev')") }) + it('offers an interactive Windows action menu without changing explicit automation actions', async () => { + const script = await readProjectFile('web-tools.ps1') + + expect(script).toContain('function Select-InteractiveAction') + expect(script).toContain("[2] Update from Git, rebuild, and start") + expect(script).toContain("[3] Update from Git and rebuild only") + expect(script).toContain("$PSBoundParameters.ContainsKey('Action')") + expect(script).toContain("[ValidateSet('start', 'update')]") + expect(script).not.toContain("[string]$Action = 'start'") + }) + it('provides standard entry points for a new AI session', async () => { const [entryReadme, agentGuide, completeReadme] = await Promise.all([ readProjectFile('README.md'), diff --git a/web-tools.ps1 b/web-tools.ps1 index 4f80d97..ad23f1a 100644 --- a/web-tools.ps1 +++ b/web-tools.ps1 @@ -4,7 +4,7 @@ param( [Parameter(Position = 0)] [ValidateSet('start', 'update')] - [string]$Action = 'start', + [string]$Action, [ValidateNotNullOrEmpty()] [string]$ListenAddress = '0.0.0.0', @@ -62,6 +62,26 @@ function Invoke-NativeCommand { } } +function Select-InteractiveAction { + Write-Host '' + Write-Host 'Web Tools' + Write-Host ' [1] Start current build (no update)' + Write-Host ' [2] Update from Git, rebuild, and start' + Write-Host ' [3] Update from Git and rebuild only' + Write-Host ' [0] Exit' + + while ($true) { + $selection = (Read-Host 'Select an action (0-3)').Trim() + switch ($selection) { + '1' { return 'start' } + '2' { return 'update' } + '3' { return 'update-no-start' } + '0' { return 'exit' } + default { Write-Warning 'Invalid selection. Enter 0, 1, 2, or 3.' } + } + } +} + function Assert-ProductionBuild { if (-not (Test-Path -LiteralPath $serverEntry -PathType Leaf)) { throw "Server entry was not found: $serverEntry" @@ -166,6 +186,20 @@ function Start-WebTools { } } +if (-not $PSBoundParameters.ContainsKey('Action')) { + $interactiveAction = Select-InteractiveAction + if ($interactiveAction -eq 'exit') { + return + } + if ($interactiveAction -eq 'update-no-start') { + $Action = 'update' + $NoStart = $true + } + else { + $Action = $interactiveAction + } +} + if ($Action -eq 'start' -and $NoStart) { throw "-NoStart can only be used with the 'update' action." }