Compare commits

..

3 commits

Author SHA1 Message Date
86dfd41f44 Fix Windows install start: writable runtime logs and robust Postgres bootstrap.
Some checks failed
Release Windows / build (push) Has been cancelled
API logs go under ProgramData; first-run verifies CREATE ROLE/DATABASE and login.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 11:22:01 +08:00
ca97a2a597 Add Explorer .cmd launchers and always-on desktop First-time setup shortcuts.
Server 2012 Start Menu often hides PowerShell .lnk entries; .cmd targets are visible.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 11:03:38 +08:00
41ab4921a0 Fix Windows PowerShell parsing on Server 2012: UTF-8 BOM scripts.
Chinese packaging scripts without BOM were misread as ANSI and failed at first-run.
Document that bundled Python 3.13+ needs Server 2016+ / Win10+.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 10:53:29 +08:00
27 changed files with 148 additions and 62 deletions

View file

@ -21,6 +21,8 @@ This directory builds a Windows deliverable with:
**Offline:** Setup ships portable PostgreSQL, `.venv` (when built with `-CreateVenv`), and WinSW. First-run uses `-NonInteractive -DbMode bundled` — **no GitHub/EDB download on the target PC**. Service install uses bundled WinSW (pass `-AllowDownload` only on a build/dev machine if the binary is missing). Auto-update still needs network later, and is unchecked by default.
**OS:** Windows 10/11 or Windows Server **2016+** recommended. Packaging scripts are UTF-8 **with BOM** so Chinese UI works on Windows PowerShell 5.x. Bundled Python in current releases is **3.13+**, which does **not** support Windows Server 2012 R2 — use Server 2016+ or a newer desktop OS.
## Build (developer machine)
Prerequisites: Python 3.11+, Node 20+, PowerShell, network (to download PG binaries once).

View file

@ -1,4 +1,4 @@
# Shared path helpers for Windows packaging scripts.
# Shared path helpers for Windows packaging scripts.
# Dot-source: . "$PSScriptRoot\_common.ps1"
$ErrorActionPreference = "Stop"
@ -148,6 +148,7 @@ function Get-NetxDataEnvMap {
"NETX_AUTH_MCP_TOKEN_FILE" = (ConvertTo-NetxFsPath (Join-Path $d "data\auth\mcp_token"))
"NETX_AUTH_SECRET_FILE" = (ConvertTo-NetxFsPath (Join-Path $d "data\auth\jwt_secret"))
"NETX_SCHEDULER_HEARTBEAT_PATH" = (ConvertTo-NetxFsPath (Join-Path $d "data\runtime\scheduler_heartbeat.json"))
"NETX_RUN_DIR" = (ConvertTo-NetxFsPath (Join-Path $d "data\runtime"))
"NETX_BIZ_STATE_SPOOL_DIR" = (ConvertTo-NetxFsPath (Join-Path $d "data\biz_state_spool"))
"NETX_NE_COLLECTION_DATA_DIR" = (ConvertTo-NetxFsPath (Join-Path $d "data\ne_collections"))
"NETX_NE_EXEC_JOB_DIR" = (ConvertTo-NetxFsPath (Join-Path $d "data\ne_exec_jobs"))

View file

@ -1,4 +1,4 @@
param(
param(
[string]$Version = "",
[string]$OutDir = "",
[switch]$SkipWebBuild = $false,
@ -80,6 +80,22 @@ $webOut = Join-Path $stage "web\dist"
New-Item -ItemType Directory -Path (Split-Path $webOut -Parent) -Force | Out-Null
Copy-Item -Path $dist -Destination $webOut -Recurse -Force
function Write-Utf8BomFile {
param([string]$Path)
# Windows PowerShell 4/5 (Server 2012+) mis-parses UTF-8 without BOM when scripts contain CJK.
$utf8Bom = New-Object System.Text.UTF8Encoding $true
$bytes = [IO.File]::ReadAllBytes($Path)
$hasBom = ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)
$text = if ($hasBom) {
[Text.Encoding]::UTF8.GetString($bytes, 3, $bytes.Length - 3)
} else {
[Text.Encoding]::UTF8.GetString($bytes)
}
$text = $text -replace "`r`n", "`n" -replace "`n", "`r`n"
if (-not $text.EndsWith("`r`n")) { $text += "`r`n" }
[IO.File]::WriteAllText($Path, $text, $utf8Bom)
}
$packOut = Join-Path $stage "packaging"
New-Item -ItemType Directory -Path $packOut -Force | Out-Null
Copy-Item -Path (Join-Path $PSScriptRoot "_common.ps1") -Destination $packOut -Force
@ -87,13 +103,15 @@ foreach ($name in @(
"download_postgres.ps1", "setup_first_run.ps1", "start_netx_app.ps1",
"stop_netx_app.ps1", "update_netx.ps1", "check_update.ps1",
"launch_shortcut.ps1", "netx_tray.ps1", "install_autostart.ps1", "install_service.ps1",
"service_run.ps1", "install_update_task.ps1", "sign_release.ps1",
"build_release.ps1", "publish_release.ps1", "publish_forgejo_release.ps1", "README.md", "manifest.example.json"
)) {
$src = Join-Path $PSScriptRoot $name
if (Test-Path $src) { Copy-Item $src (Join-Path $packOut $name) -Force }
}
Get-ChildItem -Path $packOut -Filter *.ps1 -File | ForEach-Object { Write-Utf8BomFile -Path $_.FullName }
Get-ChildItem -Path (Join-Path $stage "scripts") -Filter *.ps1 -File -ErrorAction SilentlyContinue |
ForEach-Object { Write-Utf8BomFile -Path $_.FullName }
Copy-Item -Path (Join-Path $PSScriptRoot "config") -Destination (Join-Path $packOut "config") -Recurse -Force
Copy-Item -Path (Join-Path $PSScriptRoot "installer") -Destination (Join-Path $packOut "installer") -Recurse -Force
$assetsSrc = Join-Path $PSScriptRoot "assets"
@ -139,6 +157,15 @@ $builtAt = (Get-Date).ToUniversalTime().ToString("o")
Set-Content -Path (Join-Path $stage ".portable") -Value "1" -Encoding ascii
# Root .cmd launchers (Explorer / Start Menu friendly; ASCII-only).
$cmdSrc = Join-Path $PSScriptRoot "cmd"
foreach ($cmdName in @("NetX-FirstRun.cmd", "NetX-Start.cmd", "NetX-Tray.cmd")) {
$c = Join-Path $cmdSrc $cmdName
if (Test-Path $c) {
Copy-Item -Path $c -Destination (Join-Path $stage $cmdName) -Force
}
}
if ($CreateVenv) {
Write-Host "==> Creating .venv in stage (requires Python 3.11+ on PATH)"
$py = (Get-Command python -ErrorAction SilentlyContinue)

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[string]$UpdateUrl = "",

View file

@ -0,0 +1,7 @@
@echo off
setlocal
set "ROOT=%~dp0"
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
cd /d "%ROOT%"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%ROOT%\packaging\launch_shortcut.ps1" -Action setup -ProgramRoot "%ROOT%" -DataRoot "%ProgramData%\NetX"
exit /b %ERRORLEVEL%

View file

@ -0,0 +1,7 @@
@echo off
setlocal
set "ROOT=%~dp0"
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
cd /d "%ROOT%"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%ROOT%\packaging\launch_shortcut.ps1" -Action start -ProgramRoot "%ROOT%" -DataRoot "%ProgramData%\NetX"
exit /b %ERRORLEVEL%

View file

@ -0,0 +1,7 @@
@echo off
setlocal
set "ROOT=%~dp0"
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
cd /d "%ROOT%"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%ROOT%\packaging\launch_shortcut.ps1" -Action tray -ProgramRoot "%ROOT%" -DataRoot "%ProgramData%\NetX" -StartOnLaunch
exit /b %ERRORLEVEL%

View file

@ -1,4 +1,4 @@
param(
param(
[string]$Version = "16.4-1",
[string]$OutDir = ""
)

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[switch]$Remove = $false
)

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[ValidateSet("winsw", "task")]

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[ValidateSet("Daily", "AtLogOn", "Hourly")]

View file

@ -1,10 +1,10 @@
; NetX Windows installer (Inno Setup 6+)
; NetX Windows installer (Inno Setup 6+)
; Compile after: packaging\build_release.ps1
; ISCC.exe packaging\installer\netx.iss
#define MyAppName "NetX"
#ifndef MyAppVersion
#define MyAppVersion "0.4.2"
#define MyAppVersion "0.4.5"
#endif
#define MyAppPublisher "NetX"
#define MyAppURL "https://github.com/hansjone/netx"
@ -34,7 +34,7 @@ WizardStyle=modern
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
PrivilegesRequired=admin
; Data lives under {commonappdata}\NetX — not overwritten by upgrades
; Data lives under {commonappdata}\NetX 鈥?not overwritten by upgrades
CloseApplications=yes
ShowLanguageDialog=yes
@ -50,20 +50,19 @@ english.StartTrayNow=Start NetX tray now
english.EnableAutostart=Start NetX tray at Windows logon
english.EnableAutoUpdate=Enable daily silent auto-update (needs network later)
english.InstallService=Install as Windows Service (uses bundled WinSW, offline)
chinesesimplified.CreateDesktopIcon=创建桌面快捷方式
chinesesimplified.SetupOptions=安装选项:
chinesesimplified.RunFirstSetup=安装后运行首次配置(内置数据库,可离线)
chinesesimplified.StartTrayNow=立即启动 NetX 托盘
chinesesimplified.EnableAutostart=开机时自动启动 NetX 托盘
chinesesimplified.EnableAutoUpdate=启用每日静默自动更新(以后需要联网)
chinesesimplified.InstallService=安装为 Windows 服务(使用已捆绑的 WinSW,可离线)
chinesesimplified.CreateDesktopIcon=鍒涘缓妗岄潰蹇嵎鏂瑰紡
chinesesimplified.SetupOptions=瀹夎閫夐」:
chinesesimplified.RunFirstSetup=瀹夎鍚庤繍琛岄娆¢厤缃紙鍐呯疆鏁版嵁搴擄紝鍙绾匡級
chinesesimplified.StartTrayNow=绔嬪嵆鍚姩 NetX 鎵樼洏
chinesesimplified.EnableAutostart=寮€鏈烘椂鑷姩鍚姩 NetX 鎵樼洏
chinesesimplified.EnableAutoUpdate=鍚敤姣忔棩闈欓粯鑷姩鏇存柊锛堜互鍚庨渶瑕佽仈缃戯級
chinesesimplified.InstallService=瀹夎涓?Windows 鏈嶅姟锛堜娇鐢ㄥ凡鎹嗙粦鐨?WinSW锛屽彲绂荤嚎锛?
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
Name: "firstrun"; Description: "{cm:RunFirstSetup}"; GroupDescription: "{cm:SetupOptions}"; Flags: checkedonce
[Files]
; Entire release stage → {app}. Exclude .portable so installed builds use ProgramData.
; Entire release stage 鈫?{app}. Exclude .portable so installed builds use ProgramData.
Source: "{#StageDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: ".portable"
[Dirs]
@ -75,16 +74,20 @@ Name: "{commonappdata}\NetX\pgdata"
Name: "{commonappdata}\NetX\backups"
[Icons]
Name: "{group}\NetX Tray"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action tray -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -StartOnLaunch"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Start NetX"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action start -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
; Prefer .cmd targets so Server 2012 / classic Start Menu shows normal program entries.
; Also pin First-time setup + Start to common desktop (always), not only optional tray icon.
Name: "{group}\NetX Tray"; Filename: "{app}\NetX-Tray.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Start NetX"; Filename: "{app}\NetX-Start.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Stop NetX"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action stop -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Check for updates"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action update -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Open NetX UI"; Filename: "http://127.0.0.1:8890/"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\First-time setup"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action setup -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\First-time setup"; Filename: "{app}\NetX-FirstRun.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Install Windows Service (admin)"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_service.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -Start"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Enable silent auto-update (daily)"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_update_task.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{group}\Enable start at logon"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_autostart.ps1"" -ProgramRoot ""{app}"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{autodesktop}\NetX"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action tray -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -StartOnLaunch"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"; Tasks: desktopicon
Name: "{commondesktop}\NetX First-time setup"; Filename: "{app}\NetX-FirstRun.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{commondesktop}\NetX Start"; Filename: "{app}\NetX-Start.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
Name: "{commondesktop}\NetX"; Filename: "{app}\NetX-Tray.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"; Tasks: desktopicon
[Run]
; First-run is NonInteractive + bundled DB so offline machines succeed without prompts or downloads.
@ -95,5 +98,6 @@ Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\p
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_service.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -Start"; WorkingDir: "{app}"; Description: "{cm:InstallService}"; Flags: postinstall skipifsilent unchecked
[UninstallDelete]
; Do NOT delete {commonappdata}\NetX — preserves DB and secrets across reinstall
; Do NOT delete {commonappdata}\NetX 鈥?preserves DB and secrets across reinstall
Type: filesandordirs; Name: "{app}"

View file

@ -1,4 +1,4 @@
param(
param(
[ValidateSet("tray", "start", "stop", "setup", "update")]
[string]$Action = "tray",
[string]$ProgramRoot = "",
@ -50,9 +50,10 @@ function Ensure-NetxEnv {
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $setup `
-ProgramRoot $prog -DataRoot $data -NonInteractive -DbMode bundled
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $envPath)) {
$cmd = Join-Path $prog "NetX-FirstRun.cmd"
throw (T `
"First-time setup failed. Run Start Menu → NetX → First-time setup.`nLog: $log" `
"首次配置失败。请运行「开始菜单 → NetX → First-time setup」。`n日志: $log")
"First-time setup failed.`nDouble-click: $cmd`nOr: Start Menu → All Programs → NetX → First-time setup`nLog: $log" `
"首次配置失败。`n请双击: $cmd`n或: 开始菜单 → 所有程序 → NetX → First-time setup`n日志: $log")
}
}

View file

@ -1,11 +1,11 @@
{
{
"channel": "stable",
"latest": "0.4.2",
"latest": "0.4.5",
"min_compatible": "0.3.0",
"notes_url": "https://github.com/hansjone/netx/releases/tag/v0.4.2",
"notes_url": "https://github.com/hansjone/netx/releases/tag/v0.4.5",
"windows": {
"url": "https://github.com/hansjone/netx/releases/download/v0.4.2/NetX-0.4.2-win64.zip",
"setup_url": "https://github.com/hansjone/netx/releases/download/v0.4.2/NetX-Setup-0.4.2.exe",
"url": "https://github.com/hansjone/netx/releases/download/v0.4.5/NetX-0.4.5-win64.zip",
"setup_url": "https://github.com/hansjone/netx/releases/download/v0.4.5/NetX-Setup-0.4.5.exe",
"sha256": "",
"size": 0
}

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[switch]$StartOnLaunch = $true,

View file

@ -1,4 +1,4 @@
param(
param(
[string]$Version = "",
[string]$ForgejoBase = "http://10.0.0.131:3000",
[string]$Owner = "hansjone",

View file

@ -1,4 +1,4 @@
param(
param(
[string]$Version = "",
[switch]$SkipBuild = $false,
[switch]$SkipInstaller = $false,

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[int]$HealthFailLimit = 6,

View file

@ -1,4 +1,4 @@
param(
param(
[ValidateSet("bundled", "external", "")]
[string]$DbMode = "",
[string]$ProgramRoot = "",
@ -153,21 +153,37 @@ if ($DbMode -eq "bundled") {
$env:PGPASSWORD = $BundledPassword
try {
$role = & $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='netx'"
if ($role -notmatch "1") {
& $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -v ON_ERROR_STOP=1 `
-c "CREATE ROLE netx LOGIN PASSWORD '$sqlPw';"
function Invoke-NetxPsql {
param([string]$Sql, [string]$Database = "postgres")
$out = & $psql -h 127.0.0.1 -p $BundledPort -U postgres -d $Database -v ON_ERROR_STOP=1 -tAc $Sql 2>&1
if ($LASTEXITCODE -ne 0) {
throw "psql_failed ($LASTEXITCODE): $Sql`n$out"
}
return (($out | Out-String).Trim())
}
$role = Invoke-NetxPsql -Sql "SELECT 1 FROM pg_roles WHERE rolname='netx'"
if ($role -eq "1") {
Invoke-NetxPsql -Sql "ALTER ROLE netx WITH LOGIN PASSWORD '$sqlPw'" | Out-Null
} else {
& $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -v ON_ERROR_STOP=1 `
-c "ALTER ROLE netx WITH LOGIN PASSWORD '$sqlPw';"
Invoke-NetxPsql -Sql "CREATE ROLE netx LOGIN PASSWORD '$sqlPw'" | Out-Null
}
$db = & $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='netx'"
if ($db -notmatch "1") {
& $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -v ON_ERROR_STOP=1 `
-c "CREATE DATABASE netx OWNER netx;"
$db = Invoke-NetxPsql -Sql "SELECT 1 FROM pg_database WHERE datname='netx'"
if ($db -ne "1") {
Invoke-NetxPsql -Sql "CREATE DATABASE netx OWNER netx" | Out-Null
}
Invoke-NetxPsql -Sql "GRANT ALL PRIVILEGES ON DATABASE netx TO netx" | Out-Null
# Verify login as app role (catches auth/hba mismatches early).
$prev = $env:PGPASSWORD
$env:PGPASSWORD = $BundledPassword
try {
$ping = & $psql -h 127.0.0.1 -p $BundledPort -U netx -d netx -v ON_ERROR_STOP=1 -tAc "SELECT 1" 2>&1
if ($LASTEXITCODE -ne 0 -or (($ping | Out-String).Trim()) -ne "1") {
throw "netx_role_login_failed: $ping"
}
} finally {
$env:PGPASSWORD = $prev
}
& $psql -h 127.0.0.1 -p $BundledPort -U postgres -d postgres -v ON_ERROR_STOP=1 `
-c "GRANT ALL PRIVILEGES ON DATABASE netx TO netx;"
} finally {
Remove-Item Env:PGPASSWORD -ErrorAction SilentlyContinue
}

View file

@ -1,4 +1,4 @@
param(
param(
[Parameter(Mandatory = $true)]
[string[]]$Files,
[string]$Thumbprint = "",

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[switch]$SkipBrowser = $false,

View file

@ -1,4 +1,4 @@
param(
param(
[string]$ProgramRoot = "",
[string]$DataRoot = "",
[switch]$KeepPostgres = $false

View file

@ -1,4 +1,4 @@
param(
param(
[Parameter(Mandatory = $true)]
[string]$PackagePath,
[string]$ProgramRoot = "",

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "netx-ops"
version = "0.4.2"
version = "0.4.5"
description = "netx operations tool: alarm-centric workflows with REST API and stdio MCP"
readme = "README.md"
requires-python = ">=3.11"
@ -43,3 +43,4 @@ netx-topology-mcp = "netx_topology_mcp.server:main"
[tool.setuptools.packages.find]
where = ["."]
include = ["netx_api*"]

View file

@ -1,4 +1,4 @@
param(
param(
[string]$PgHost = "127.0.0.1",
[int]$PgPort = 5432,
[string]$SuperUser = "postgres",

View file

@ -1,4 +1,4 @@
param(
param(
[string]$BindHost = "127.0.0.1",
[int]$Port = 8890,
[int]$WebPort = 5173,
@ -23,9 +23,16 @@ if ($Backgtound -and -not $Background) {
$projectRoot = Split-Path -Parent $PSScriptRoot
Set-Location $projectRoot
$runDir = Join-Path $PSScriptRoot ".run"
# Prefer writable data-root runtime dir (Program Files is not writable after Setup install).
if ($env:NETX_RUN_DIR) {
$runDir = $env:NETX_RUN_DIR
} elseif ($env:NETX_SCHEDULER_HEARTBEAT_PATH) {
$runDir = Split-Path -Parent ($env:NETX_SCHEDULER_HEARTBEAT_PATH -replace '/', '\')
} else {
$runDir = Join-Path $PSScriptRoot ".run"
}
if (-not (Test-Path $runDir)) {
New-Item -ItemType Directory -Path $runDir | Out-Null
New-Item -ItemType Directory -Path $runDir -Force | Out-Null
}
$pidFile = Join-Path $runDir "netx.pid"

View file

@ -1,4 +1,4 @@
param(
param(
[int]$Port = 8890,
[int]$WebPort = 5173,
# Windows often ignores graceful Stop-Process on python; default hard-kill.
@ -9,7 +9,13 @@ param(
$ErrorActionPreference = "Continue"
$useForce = if ($NoForce) { $false } else { [bool]$Force }
$runDir = Join-Path $PSScriptRoot ".run"
if ($env:NETX_RUN_DIR) {
$runDir = $env:NETX_RUN_DIR
} elseif ($env:NETX_SCHEDULER_HEARTBEAT_PATH) {
$runDir = Split-Path -Parent ($env:NETX_SCHEDULER_HEARTBEAT_PATH -replace '/', '\')
} else {
$runDir = Join-Path $PSScriptRoot ".run"
}
$pidFile = Join-Path $runDir "netx.pid"
$workerPidFile = Join-Path $runDir "worker.pid"
$webPidFile = Join-Path $runDir "web.pid"