From f77578ebd1a1933a37405068aa63b1a5ce7b652c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E4=BD=93=E8=BF=90=E8=BD=AC?= <30560683+534A4D21@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:17:33 +0800 Subject: [PATCH] feat(windows): optionally expose CLI service shim --- docs/windows-server.md | 4 ++++ release-capabilities.json | 1 + scripts/bootstrap-windows.ps1 | 7 +++++++ scripts/install-windows-server.ps1 | 30 ++++++++++++++++++++++++++++++ scripts/uninstall-windows.ps1 | 11 +++++++++++ 5 files changed, 53 insertions(+) diff --git a/docs/windows-server.md b/docs/windows-server.md index cfdfa2c..3ffc34d 100644 --- a/docs/windows-server.md +++ b/docs/windows-server.md @@ -64,6 +64,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\install-windows-server.ps1 ` -Port 7420 ` -Password "change-me" ` -CreateStartupTask ` + -CreateCliShim ` -EnsureCodexLogin ` -OpenFirewall ` -InstallCloudflared ` @@ -75,6 +76,9 @@ The script writes: - a stable JSON config file at `%USERPROFILE%\.cx-codex\config.json` - a launcher at `%USERPROFILE%\.local\bin\cx-codex-start.cmd` +- optionally, a CLI shim at `%USERPROFILE%\.local\bin\cx-codex.cmd` when `-CreateCliShim` is specified + +With the optional shim, the installed CLI service commands are available as `cx-codex service status`, `cx-codex service restart`, and the other commands shown by `cx-codex service --help`. The default installation does not create this command or install an npm global package. By default the installer writes `tunnel: false` and `open: false`, which is usually the right choice for Windows server usage. diff --git a/release-capabilities.json b/release-capabilities.json index 24c3554..3f6f9ea 100644 --- a/release-capabilities.json +++ b/release-capabilities.json @@ -4,6 +4,7 @@ "features": { "remoteQuick": true, "jsonOutput": true, + "cliShim": true, "stableJsonContract": true, "windowsUninstall": true } diff --git a/scripts/bootstrap-windows.ps1 b/scripts/bootstrap-windows.ps1 index 74d74f2..4c8ece8 100644 --- a/scripts/bootstrap-windows.ps1 +++ b/scripts/bootstrap-windows.ps1 @@ -14,6 +14,7 @@ param( [switch]$RemoteQuick, [switch]$JsonOutput, [switch]$SkipOpenPairing, + [switch]$CreateCliShim, [switch]$SkipStartupTask, [switch]$SkipWatchdogTask, [switch]$SkipFirewall, @@ -748,6 +749,9 @@ function Assert-RepositoryCapabilities { if ($JsonOutput -and -not [bool]$manifest.features.jsonOutput) { throw "Selected CX-Codex source does not support JsonOutput." } + if ($CreateCliShim -and -not [bool]$manifest.features.cliShim) { + throw "Selected CX-Codex source does not support CreateCliShim." + } } function Get-VerifiedReleaseArchive { @@ -915,6 +919,9 @@ $invokeArgs = @( if (-not [string]::IsNullOrWhiteSpace($runtime.NpmCli)) { $invokeArgs += @("-NpmCliPath", $runtime.NpmCli) } +if ($CreateCliShim) { + $invokeArgs += "-CreateCliShim" +} if ($NoPassword) { $invokeArgs += "-NoPassword" diff --git a/scripts/install-windows-server.ps1 b/scripts/install-windows-server.ps1 index dd77b31..87f4419 100644 --- a/scripts/install-windows-server.ps1 +++ b/scripts/install-windows-server.ps1 @@ -10,6 +10,7 @@ param( [switch]$OpenBrowser, [string]$ConfigPath = "$env:USERPROFILE\.cx-codex\config.json", [string]$LauncherPath = "$env:USERPROFILE\.local\bin\cx-codex-start.cmd", + [switch]$CreateCliShim, [string]$NodeCommand = "", [string]$NpmCommand = "", [string]$NpmCliPath = "", @@ -466,6 +467,27 @@ cd /d "$RepoRoot" Set-Content -LiteralPath $TargetLauncherPath -Value $launcherContent -Encoding ASCII } +function Create-CliShimFile { + param( + [string]$TargetShimPath, + [string]$NodePath, + [string]$RepoRoot + ) + + $shimDir = Split-Path -Parent $TargetShimPath + if (-not [string]::IsNullOrWhiteSpace($shimDir)) { + New-Item -ItemType Directory -Path $shimDir -Force | Out-Null + } + + $shimContent = @" +@echo off +setlocal +"$NodePath" "$RepoRoot\dist-cli\index.js" %* +"@ + + Set-Content -LiteralPath $TargetShimPath -Value $shimContent -Encoding ASCII +} + function Create-ManagementShortcuts { param([int]$TargetPort) @@ -1086,6 +1108,10 @@ $configTempPath = "$ConfigPath.tmp-$PID" ) Move-Item -LiteralPath $configTempPath -Destination $ConfigPath -Force Create-LauncherFile -TargetLauncherPath $LauncherPath -NodePath $nodeExecutable -RepoRoot $repoRoot -TargetConfigPath $ConfigPath +$cliShimPath = Join-Path (Split-Path -Parent $LauncherPath) "cx-codex.cmd" +if ($CreateCliShim) { + Create-CliShimFile -TargetShimPath $cliShimPath -NodePath $nodeExecutable -RepoRoot $repoRoot +} $managementShortcutPaths = @(Create-ManagementShortcuts -TargetPort $Port) if ($CreateStartupTask) { @@ -1198,6 +1224,9 @@ if (-not $JsonOutput) { Write-InstallerMessage "Install complete." Write-InstallerMessage "Config: $ConfigPath" Write-InstallerMessage "Launcher: $LauncherPath" + if ($CreateCliShim) { + Write-InstallerMessage "CLI shim: $cliShimPath" + } foreach ($shortcutPath in $managementShortcutPaths) { Write-InstallerMessage "Manage: $shortcutPath" } @@ -1270,6 +1299,7 @@ if ($JsonOutput) { [ordered]@{ health = $false; auth = $false; websocketAuth = $false } } configPath = $ConfigPath + cliShimPath = if ($CreateCliShim) { $cliShimPath } else { "" } managementUrl = "http://127.0.0.1:$Port/local-setup" managementShortcuts = @($managementShortcutPaths) logsPath = $logDir diff --git a/scripts/uninstall-windows.ps1 b/scripts/uninstall-windows.ps1 index 28c9dab..76ea366 100644 --- a/scripts/uninstall-windows.ps1 +++ b/scripts/uninstall-windows.ps1 @@ -236,6 +236,7 @@ $resolvedInstallDir = Assert-SafeManagedDirectory -Path $InstallDir -Label "inst $resolvedStateDir = Assert-SafeManagedDirectory -Path $StateDir -Label "state" $resolvedManagedBinDir = Assert-SafeManagedDirectory -Path $ManagedBinDir -Label "managed bin" $resolvedLauncherPath = Get-FullPath -Path $LauncherPath -Label "launcher" +$resolvedCliShimPath = Join-Path $resolvedManagedBinDir "cx-codex.cmd" $resolvedConfigPath = Join-Path $resolvedStateDir "config.json" $resolvedTaskName = if ([string]::IsNullOrWhiteSpace($TaskName)) { "CodexUI-$Port" } else { $TaskName } $resolvedWatchdogTaskName = if ([string]::IsNullOrWhiteSpace($WatchdogTaskName)) { "CodexUI-$Port-Watchdog" } else { $WatchdogTaskName } @@ -393,6 +394,16 @@ if ($firewallCommand) { $script:UninstallStage = "remove_program_files" Remove-ManagedItem -Path $resolvedServerPidPath -Label "server PID marker" Remove-ManagedItem -Path $resolvedLauncherPath -Label "launcher" +if (Test-Path -LiteralPath $resolvedCliShimPath) { + $shimText = Get-Content -Raw -Encoding ASCII -LiteralPath $resolvedCliShimPath + $managedIndexPath = Join-Path $resolvedInstallDir "dist-cli\index.js" + if ($shimText -like "*$managedIndexPath*") { + Remove-ManagedItem -Path $resolvedCliShimPath -Label "CLI shim" + } else { + $script:PreservedItems.Add("cli-shim:$resolvedCliShimPath") | Out-Null + Write-UninstallWarning -Code "CLI_SHIM_PRESERVED" -Message "Preserved CLI shim because it does not target the managed CX-Codex installation." + } +} foreach ($managementShortcutPath in $managementShortcutPaths) { if ( (Test-Path -LiteralPath $managementShortcutPath) -and