mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] windows polish + bump 1.0.25: publish-win.ps1 mirror of publish.sh + run.ps1 windows dev launcher, build-app-win.ps1 warns 8s if Mac latest-mac.yml missing in
v<version> release before -Publish, python-env cache skip via OPENSWARM_REBUILD_PYTHON, pip stderr wrapped to suppress PS5.1 NativeCommandError; analytics APP_VERSION auto-derived from electron/package.json (single source of truth); OnboardingModal renders Connected state for already-active providers; gitignore fix for backend/.venv. Bumps 1.0.25 - first version that ships Mac and Windows together.
This commit is contained in:
@@ -27,6 +27,8 @@ backend/npm-servers/*/node_modules/
|
||||
frontend/dist/
|
||||
# Bundled uv binaries (downloaded during build)
|
||||
backend/uv-bin/
|
||||
# Backend Python venv (created by run.ps1 / backend/run.sh)
|
||||
backend/.venv/
|
||||
.account-factory
|
||||
openswarm-cloud
|
||||
.openswarm-cloud
|
||||
|
||||
@@ -15,7 +15,23 @@ from backend.apps.analytics.collector import init as init_collector, shutdown as
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
APP_VERSION = "1.0.24"
|
||||
def _read_app_version() -> str:
|
||||
"""Read app version from electron/package.json so we never have to bump
|
||||
it in two places. Falls back to a literal if the file isn't reachable
|
||||
(e.g. unusual layouts in tests)."""
|
||||
import json
|
||||
try:
|
||||
_here = os.path.dirname(os.path.abspath(__file__))
|
||||
# backend/apps/analytics/ -> backend/apps/ -> backend/ -> repo root
|
||||
_repo = os.path.dirname(os.path.dirname(os.path.dirname(_here)))
|
||||
_pkg = os.path.join(_repo, "electron", "package.json")
|
||||
with open(_pkg, encoding="utf-8") as _f:
|
||||
return json.load(_f).get("version", "unknown")
|
||||
except (OSError, ValueError, KeyError):
|
||||
return "unknown"
|
||||
|
||||
|
||||
APP_VERSION = _read_app_version()
|
||||
|
||||
_heartbeat_task: asyncio.Task | None = None
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openswarm",
|
||||
"version": "1.0.24",
|
||||
"version": "1.0.25",
|
||||
"description": "OpenSwarm — AI Agent Orchestrator",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -858,28 +858,31 @@ const OnboardingModal: React.FC = () => {
|
||||
Use your existing subscription
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75, mb: 2.5 }}>
|
||||
{SUBSCRIPTION_PROVIDERS.map((p) => (
|
||||
{SUBSCRIPTION_PROVIDERS.map((p) => {
|
||||
const isConnected = connectedProviders.has(p.id);
|
||||
return (
|
||||
<Box
|
||||
key={p.id}
|
||||
onClick={() => !p.preview && !connecting && nineRouterReady && handleConnect(p.id)}
|
||||
onClick={() => !p.preview && !connecting && nineRouterReady && !isConnected && handleConnect(p.id)}
|
||||
sx={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}`,
|
||||
cursor: p.preview || !nineRouterReady ? 'default' : connecting ? 'wait' : 'pointer',
|
||||
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${isConnected ? c.accent.primary : c.border.subtle}`,
|
||||
cursor: p.preview || !nineRouterReady || isConnected ? 'default' : connecting ? 'wait' : 'pointer',
|
||||
opacity: p.preview ? 0.5 : !nineRouterReady ? 0.6 : 1,
|
||||
transition: 'border-color 0.15s, background 0.15s',
|
||||
...(!p.preview && nineRouterReady && { '&:hover': { borderColor: c.border.medium, bgcolor: `${c.accent.primary}05` } }),
|
||||
...(!p.preview && nineRouterReady && !isConnected && { '&:hover': { borderColor: c.border.medium, bgcolor: `${c.accent.primary}05` } }),
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography sx={{ fontSize: '0.92rem', fontWeight: 600, color: c.text.primary }}>{p.name}</Typography>
|
||||
<Typography sx={{ fontSize: '0.75rem', color: c.text.muted }}>{p.desc}</Typography>
|
||||
</Box>
|
||||
<Typography sx={{ fontSize: '0.78rem', color: p.preview ? c.text.ghost : connecting === p.id ? c.accent.primary : !nineRouterReady ? c.text.ghost : c.text.tertiary, fontStyle: p.preview ? 'italic' : 'normal' }}>
|
||||
{p.preview ? 'Coming soon' : connecting === p.id ? 'Connecting...' : !nineRouterReady && nineRouterReady !== false ? 'Starting...' : 'Connect \u2192'}
|
||||
<Typography sx={{ fontSize: '0.78rem', color: p.preview ? c.text.ghost : isConnected ? c.accent.primary : connecting === p.id ? c.accent.primary : !nineRouterReady ? c.text.ghost : c.text.tertiary, fontStyle: p.preview ? 'italic' : 'normal' }}>
|
||||
{p.preview ? 'Coming soon' : isConnected ? 'Connected' : connecting === p.id ? 'Connecting...' : !nineRouterReady && nineRouterReady !== false ? 'Starting...' : 'Connect \u2192'}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
|
||||
{/* API key option */}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
# Windows mirror of publish.sh.
|
||||
# Builds + signs + publishes the Windows installer to the GitHub Release
|
||||
# matching electron/package.json's version.
|
||||
#
|
||||
# Usage:
|
||||
# pwsh publish-win.ps1 (or) powershell -File publish-win.ps1
|
||||
#
|
||||
# Prereqs:
|
||||
# - .env.windows populated with AZURE_* secrets + GH_TOKEN
|
||||
# - For a clean release flow: run `bash publish.sh` on a Mac first so
|
||||
# the v<version> release exists with Mac assets. The build script will
|
||||
# warn if the Mac release isn't found.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
$BuildScript = Join-Path $ScriptDir 'scripts\build-app-win.ps1'
|
||||
|
||||
if (-not (Test-Path $BuildScript)) {
|
||||
Write-Error "Cannot find $BuildScript"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Publishing Windows release..."
|
||||
& $BuildScript -Publish
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,192 @@
|
||||
# Windows dev launcher - mirror of bash run.sh.
|
||||
# Spins up backend (uvicorn --reload) + frontend (webpack dev server) + electron
|
||||
# in one terminal. Hot-reload for Python and React. Ctrl+C to stop all three.
|
||||
#
|
||||
# Prereqs: Python 3.12+, Node 20+, npm. Everything else is installed on demand.
|
||||
#
|
||||
# Usage:
|
||||
# powershell -ExecutionPolicy Bypass -File run.ps1
|
||||
# (or) pwsh run.ps1 if PowerShell 7 is installed.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ScriptDir = Split-Path -Parent $PSCommandPath
|
||||
|
||||
# --- Locate Python ---
|
||||
$python = $null
|
||||
foreach ($name in @('python', 'python3', 'py')) {
|
||||
$cmd = Get-Command $name -ErrorAction SilentlyContinue
|
||||
if ($cmd) { $python = $cmd.Source; break }
|
||||
}
|
||||
if (-not $python) {
|
||||
throw "Python 3.12+ not found on PATH. Install from https://www.python.org/downloads/"
|
||||
}
|
||||
|
||||
# --- Bundled uv/uvx (matches what build-app-win.ps1 does so MCP discovery works in dev) ---
|
||||
$UvBinDir = Join-Path $ScriptDir 'backend\uv-bin'
|
||||
if (-not (Test-Path (Join-Path $UvBinDir 'uvx.exe'))) {
|
||||
Write-Host "[setup] Downloading uv/uvx for Windows..."
|
||||
New-Item -ItemType Directory -Force -Path $UvBinDir | Out-Null
|
||||
$UvUrl = 'https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-pc-windows-msvc.zip'
|
||||
$TmpZip = Join-Path $env:TEMP "uv-win-$([guid]::NewGuid()).zip"
|
||||
$TmpExtract = Join-Path $env:TEMP "uv-win-extract-$([guid]::NewGuid())"
|
||||
try {
|
||||
Invoke-WebRequest -Uri $UvUrl -OutFile $TmpZip -UseBasicParsing
|
||||
Expand-Archive -Path $TmpZip -DestinationPath $TmpExtract -Force
|
||||
Get-ChildItem $TmpExtract -Recurse -Filter 'uv.exe' | Select-Object -First 1 | ForEach-Object { Copy-Item $_.FullName (Join-Path $UvBinDir 'uv.exe') -Force }
|
||||
Get-ChildItem $TmpExtract -Recurse -Filter 'uvx.exe' | Select-Object -First 1 | ForEach-Object { Copy-Item $_.FullName (Join-Path $UvBinDir 'uvx.exe') -Force }
|
||||
} finally {
|
||||
Remove-Item -Force $TmpZip -ErrorAction SilentlyContinue
|
||||
Remove-Item -Recurse -Force $TmpExtract -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# --- Backend venv + deps ---
|
||||
$Venv = Join-Path $ScriptDir 'backend\.venv'
|
||||
$VenvPy = Join-Path $Venv 'Scripts\python.exe'
|
||||
if (-not (Test-Path $VenvPy)) {
|
||||
Write-Host "[setup] Creating Python venv at $Venv ..."
|
||||
& $python -m venv $Venv
|
||||
if ($LASTEXITCODE -ne 0) { throw "venv creation failed" }
|
||||
}
|
||||
Write-Host "[setup] Installing backend deps (idempotent, fast if already up to date)..."
|
||||
# Suppress PowerShell 5.1's native-stderr-as-error wrapping for these idempotent
|
||||
# pip calls (deprecation warnings on stderr would otherwise terminate the script
|
||||
# under $ErrorActionPreference=Stop). We still check $LASTEXITCODE for real failures.
|
||||
$prevEAP = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'Continue'
|
||||
try {
|
||||
& $VenvPy -m pip install --quiet --upgrade pip *>&1 | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed" }
|
||||
& $VenvPy -m pip install --quiet -r (Join-Path $ScriptDir 'backend\requirements.txt')
|
||||
if ($LASTEXITCODE -ne 0) { throw "pip install backend reqs failed" }
|
||||
& $VenvPy -m pip install --quiet -e (Join-Path $ScriptDir 'debugger')
|
||||
if ($LASTEXITCODE -ne 0) { throw "pip install debugger failed" }
|
||||
} finally {
|
||||
$ErrorActionPreference = $prevEAP
|
||||
}
|
||||
|
||||
# --- Frontend deps ---
|
||||
$FrontendDir = Join-Path $ScriptDir 'frontend'
|
||||
if (-not (Test-Path (Join-Path $FrontendDir 'node_modules'))) {
|
||||
Write-Host "[setup] Installing frontend deps..."
|
||||
Push-Location $FrontendDir
|
||||
try { & npm install } finally { Pop-Location }
|
||||
}
|
||||
|
||||
# --- Electron deps ---
|
||||
$ElectronDir = Join-Path $ScriptDir 'electron'
|
||||
if (-not (Test-Path (Join-Path $ElectronDir 'node_modules'))) {
|
||||
Write-Host "[setup] Installing electron deps..."
|
||||
Push-Location $ElectronDir
|
||||
try { & npm install } finally { Pop-Location }
|
||||
}
|
||||
|
||||
# --- Process tracking + cleanup ---
|
||||
$script:childPids = New-Object System.Collections.ArrayList
|
||||
|
||||
function Stop-Tree($processId, $label) {
|
||||
if (-not $processId) { return }
|
||||
try {
|
||||
& taskkill /PID $processId /T /F 2>$null | Out-Null
|
||||
Write-Host " killed $label (pid $processId)" -ForegroundColor DarkGray
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Cleanup-All {
|
||||
Write-Host ""
|
||||
Write-Host "Shutting down all services..." -ForegroundColor Yellow
|
||||
foreach ($entry in $script:childPids) {
|
||||
Stop-Tree $entry.Pid $entry.Label
|
||||
}
|
||||
Write-Host "All services stopped." -ForegroundColor Green
|
||||
}
|
||||
|
||||
try {
|
||||
# --- Start backend (NoNewWindow so logs interleave into this terminal) ---
|
||||
Write-Host ""
|
||||
Write-Host "[backend] Starting uvicorn --reload on http://localhost:8324 ..." -ForegroundColor Blue
|
||||
$backend = Start-Process -PassThru -NoNewWindow `
|
||||
-FilePath $VenvPy `
|
||||
-WorkingDirectory $ScriptDir `
|
||||
-ArgumentList @(
|
||||
'-m', 'uvicorn', 'backend.main:app',
|
||||
'--host', '0.0.0.0', '--port', '8324', '--reload',
|
||||
'--reload-dir', (Join-Path $ScriptDir 'backend'),
|
||||
'--reload-exclude', '*.pyc'
|
||||
)
|
||||
[void]$script:childPids.Add(@{ Pid = $backend.Id; Label = 'backend' })
|
||||
|
||||
Write-Host "Waiting for backend (max 90s)..." -ForegroundColor Yellow
|
||||
$deadline = (Get-Date).AddSeconds(90)
|
||||
$ready = $false
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
if ($backend.HasExited) { throw "Backend exited prematurely (code $($backend.ExitCode))" }
|
||||
try {
|
||||
Invoke-WebRequest -Uri 'http://localhost:8324/api/health/check' -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop | Out-Null
|
||||
$ready = $true
|
||||
break
|
||||
} catch {}
|
||||
Start-Sleep -Milliseconds 1500
|
||||
}
|
||||
if (-not $ready) { throw "Backend did not become ready within 90s" }
|
||||
Write-Host "Backend ready." -ForegroundColor Green
|
||||
|
||||
# --- Start frontend ---
|
||||
Write-Host ""
|
||||
Write-Host "[frontend] Starting webpack dev server on http://localhost:3000 ..." -ForegroundColor Green
|
||||
$frontend = Start-Process -PassThru -NoNewWindow `
|
||||
-FilePath 'npm.cmd' `
|
||||
-WorkingDirectory $FrontendDir `
|
||||
-ArgumentList @('run', 'dev')
|
||||
[void]$script:childPids.Add(@{ Pid = $frontend.Id; Label = 'frontend' })
|
||||
|
||||
Write-Host "Waiting for frontend (max 90s)..." -ForegroundColor Yellow
|
||||
$deadline = (Get-Date).AddSeconds(90)
|
||||
$ready = $false
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
if ($frontend.HasExited) { throw "Frontend exited prematurely (code $($frontend.ExitCode))" }
|
||||
try {
|
||||
Invoke-WebRequest -Uri 'http://localhost:3000/' -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop | Out-Null
|
||||
$ready = $true
|
||||
break
|
||||
} catch {}
|
||||
Start-Sleep -Milliseconds 1500
|
||||
}
|
||||
if (-not $ready) { throw "Frontend did not become ready within 90s" }
|
||||
Write-Host "Frontend ready." -ForegroundColor Green
|
||||
|
||||
# --- Start electron in dev mode (npm run dev = cross-env ELECTRON_DEV=1 electron .) ---
|
||||
Write-Host ""
|
||||
Write-Host "[electron] Launching dev shell..." -ForegroundColor Magenta
|
||||
$electron = Start-Process -PassThru -NoNewWindow `
|
||||
-FilePath 'npm.cmd' `
|
||||
-WorkingDirectory $ElectronDir `
|
||||
-ArgumentList @('run', 'dev')
|
||||
[void]$script:childPids.Add(@{ Pid = $electron.Id; Label = 'electron' })
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "All services running. Press Ctrl+C to stop." -ForegroundColor Cyan
|
||||
Write-Host " Backend: http://localhost:8324" -ForegroundColor Blue
|
||||
Write-Host " Frontend: http://localhost:3000" -ForegroundColor Green
|
||||
Write-Host " Electron: dev shell (pid $($electron.Id))" -ForegroundColor Magenta
|
||||
Write-Host ""
|
||||
|
||||
# --- Supervise: if any dies, tear down all ---
|
||||
while ($true) {
|
||||
Start-Sleep -Seconds 3
|
||||
if ($backend.HasExited) {
|
||||
Write-Host "[backend] exited unexpectedly (code $($backend.ExitCode)). Tearing down..." -ForegroundColor Red
|
||||
break
|
||||
}
|
||||
if ($frontend.HasExited) {
|
||||
Write-Host "[frontend] exited unexpectedly (code $($frontend.ExitCode)). Tearing down..." -ForegroundColor Red
|
||||
break
|
||||
}
|
||||
if ($electron.HasExited) {
|
||||
Write-Host "[electron] exited (normal close). Tearing down..." -ForegroundColor Yellow
|
||||
break
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Cleanup-All
|
||||
}
|
||||
@@ -242,6 +242,29 @@ try {
|
||||
}
|
||||
|
||||
if ($Publish) {
|
||||
# Safety check: warn if the matching Mac release isn't on GitHub yet.
|
||||
# Mac and Windows publishes don't conflict (different asset names,
|
||||
# different latest*.yml manifests), but a Windows-only release means
|
||||
# Mac users will skip this version entirely. Better to know now than
|
||||
# explain it after the fact. Non-fatal; sleeps 8s to let the user
|
||||
# Ctrl+C if it surprises them.
|
||||
try {
|
||||
$pkgJson = Get-Content -Raw (Join-Path $ProjectRoot 'electron\package.json') | ConvertFrom-Json
|
||||
$version = $pkgJson.version
|
||||
$macYmlUrl = "https://github.com/openswarm-ai/openswarm/releases/download/v$version/latest-mac.yml"
|
||||
$null = Invoke-WebRequest -Uri $macYmlUrl -Method Head -UseBasicParsing -ErrorAction Stop -TimeoutSec 10
|
||||
Write-Host " > Mac release v$version detected on GitHub (latest-mac.yml present). OK to proceed."
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "WARNING: Mac release v$version is NOT yet published on GitHub." -ForegroundColor Yellow
|
||||
Write-Host " -> Uploading Windows assets to a release with no Mac assets means" -ForegroundColor Yellow
|
||||
Write-Host " Mac users will skip v$version entirely (electron-updater on Mac" -ForegroundColor Yellow
|
||||
Write-Host " will see no latest-mac.yml). Recommended order:" -ForegroundColor Yellow
|
||||
Write-Host " 1. bash publish.sh (on the Mac)" -ForegroundColor Yellow
|
||||
Write-Host " 2. pwsh publish-win.ps1 (here)" -ForegroundColor Yellow
|
||||
Write-Host " -> Continuing in 8s. Press Ctrl+C to abort." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 8
|
||||
}
|
||||
& npx electron-builder --win --x64 --publish always
|
||||
} else {
|
||||
& npx electron-builder --win --x64 --publish never
|
||||
|
||||
Reference in New Issue
Block a user