diff --git a/coordinator/cmd/coordinator/main.go b/coordinator/cmd/coordinator/main.go index 68f5f45..6c13e78 100644 --- a/coordinator/cmd/coordinator/main.go +++ b/coordinator/cmd/coordinator/main.go @@ -30,16 +30,19 @@ func main() { switch args[0] { case "setup": if err := runSetup(args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "setup:", err) os.Exit(1) } return case "serve": if err := runServe(args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "serve:", err) os.Exit(1) } return case "agent": if err := runAgent(args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "agent:", err) os.Exit(1) } return diff --git a/coordinator/internal/userservice/embedded.go b/coordinator/internal/userservice/embedded.go index bc58f9a..6c7c983 100644 --- a/coordinator/internal/userservice/embedded.go +++ b/coordinator/internal/userservice/embedded.go @@ -77,10 +77,10 @@ func Serve(ctx context.Context, cfg Config) (string, func() error, error) { handler := usershttp.NewServer(cfg.Log, uc, issuer) handler = http.TimeoutHandler(handler, 15*time.Second, `{"error":"request timeout"}`) - // A fixed loopback port lets the coordinator's proxy reuse its config - // unchanged; collisions are unlikely (no other process binds 18081 on a - // fresh machine) and fail loudly. - listener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:18081") + // Bind an ephemeral loopback port so a second serve instance can never + // collide with the first; the coordinator's proxy uses the returned + // address and needs no fixed-port assumption. + listener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:0") if err != nil { _ = db.Close() return "", nil, fmt.Errorf("listen for embedded userservice: %w", err) diff --git a/install.ps1 b/install.ps1 index 8eb5de7..ee27a6b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -9,6 +9,7 @@ $ErrorActionPreference = "Stop" $Repo = "emil28092005/SciMesh" +$Component = if ($env:SCIMESH_COMPONENT) { $env:SCIMESH_COMPONENT } else { "coordinator" } $Version = if ($env:SCIMESH_VERSION) { $env:SCIMESH_VERSION } else { "latest" } $InstallDir = if ($env:SCIMESH_INSTALL_DIR) { $env:SCIMESH_INSTALL_DIR @@ -16,6 +17,12 @@ $InstallDir = if ($env:SCIMESH_INSTALL_DIR) { Join-Path $env:LOCALAPPDATA "SciMesh" } +switch ($Component) { + "coordinator" { $Binary = "coordinator" } + "worker" { $Binary = "worker-agent" } + default { throw "unknown component: $Component (use 'coordinator' or 'worker')" } +} + $Arch = switch ($env:PROCESSOR_ARCHITECTURE) { "AMD64" { "amd64" } "ARM64" { "arm64" } @@ -39,20 +46,35 @@ if ($Version -eq "latest") { } } -$Url = "https://github.com/$Repo/releases/download/$Version/coordinator-windows-$Arch.exe" +$Url = "https://github.com/$Repo/releases/download/$Version/$Binary-windows-$Arch.exe" New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null -$Target = Join-Path $InstallDir "coordinator.exe" +$Target = Join-Path $InstallDir "$Binary.exe" Write-Host "Downloading $Url" Invoke-WebRequest -Uri $Url -OutFile "$Target.tmp" Move-Item -Force "$Target.tmp" $Target Write-Host "" -Write-Host "SciMesh installed: $Target" +Write-Host "SciMesh $Component installed: $Target" & $Target --version Write-Host "" -Write-Host "Start the platform (one command, everything embedded):" -Write-Host " $Target serve --open" -Write-Host "" -Write-Host "Your data lives in ~\.scimesh. The admin login is printed on first start." +if ($Component -eq "coordinator") { + Write-Host "Start the platform (one command, everything embedded):" + Write-Host " $Target serve --open" + Write-Host "" + Write-Host "Your data lives in ~\.scimesh. The admin login is printed on first start." +} else { + Write-Host "The worker needs Python 3 with the scimesh package, then a coordinator" + Write-Host "to connect to. Run it with environment variables:" + Write-Host "" + Write-Host " set COORDINATOR_URL=http://COORDINATOR_HOST:8080" + Write-Host " set WORKER_AUTH_TOKEN=" + Write-Host " set WORK_DIR=%USERPROFILE%\scimesh-worker" + Write-Host " $Target" + Write-Host "" + Write-Host "For a coordinator started with 'coordinator serve', the worker token is" + Write-Host "in ~\.scimesh\worker.token on that machine. Set SCIMESH_PIP_PACKAGE to" + Write-Host "install scimesh into a managed venv, or install it yourself:" + Write-Host " pip install scimesh" +} diff --git a/install.sh b/install.sh index 232cf7d..a7af65d 100644 --- a/install.sh +++ b/install.sh @@ -1,19 +1,28 @@ #!/usr/bin/env bash -# SciMesh installer: downloads the coordinator binary for this OS/architecture -# from the latest GitHub release and installs it locally. One command, no -# picking from a list of files: +# SciMesh installer: downloads a binary for this OS/architecture from the +# newest GitHub release and installs it locally. One command, no picking from +# a list of files: # # curl -fsSL https://raw.githubusercontent.com/emil28092005/SciMesh/main/install.sh | bash +# curl -fsSL https://raw.githubusercontent.com/emil28092005/SciMesh/main/install.sh | bash -s worker # -# Installed to ~/.local/bin/coordinator (Linux/macOS). Then run: -# -# coordinator serve --open +# The first form installs the coordinator (the whole platform in one binary: +# databases, userservice, local workers). The second installs a standalone +# worker agent that joins an existing coordinator. Installed to ~/.local/bin +# (Linux/macOS) or %LOCALAPPDATA%\SciMesh (Windows). set -eu REPO="emil28092005/SciMesh" +COMPONENT="${1:-coordinator}" VERSION="${SCIMESH_VERSION:-latest}" INSTALL_DIR="${SCIMESH_INSTALL_DIR:-$HOME/.local/bin}" +case "$COMPONENT" in + coordinator) BINARY="coordinator" ;; + worker) BINARY="worker-agent" ;; + *) echo "unknown component: $COMPONENT (use 'coordinator' or 'worker')" >&2; exit 1 ;; +esac + case "$(uname -s)" in Linux) OS="linux" ;; Darwin) OS="darwin" ;; @@ -41,16 +50,16 @@ if [ "$VERSION" = "latest" ]; then fi mkdir -p "$INSTALL_DIR" -TARGET="$INSTALL_DIR/coordinator" +TARGET="$INSTALL_DIR/$BINARY" -URL="https://github.com/${REPO}/releases/download/${VERSION}/coordinator-${OS}-${ARCH}" +URL="https://github.com/${REPO}/releases/download/${VERSION}/${BINARY}-${OS}-${ARCH}" echo "Downloading $URL" curl -fsSL -o "$TARGET.tmp" "$URL" chmod +x "$TARGET.tmp" mv "$TARGET.tmp" "$TARGET" echo -echo "SciMesh installed: $TARGET" +echo "SciMesh $COMPONENT installed: $TARGET" INSTALLED_VERSION=$("$TARGET" --version 2>/dev/null | awk '{print $2}') "$TARGET" --version if [ -n "$INSTALLED_VERSION" ] && [ "$INSTALLED_VERSION" != "${VERSION#v}" ]; then @@ -60,8 +69,25 @@ if [ -n "$INSTALLED_VERSION" ] && [ "$INSTALLED_VERSION" != "${VERSION#v}" ]; th echo "minutes, or pin the version explicitly:" echo " SCIMESH_VERSION=${VERSION} bash <(curl -fsSL https://raw.githubusercontent.com/${REPO}/main/install.sh)" fi -echo -echo "Start the platform (one command, everything embedded):" -echo " $TARGET serve --open" -echo -echo "Your data lives in ~/.scimesh. The admin login is printed on first start." + +if [ "$COMPONENT" = "coordinator" ]; then + echo + echo "Start the platform (one command, everything embedded):" + echo " $TARGET serve --open" + echo + echo "Your data lives in ~/.scimesh. The admin login is printed on first start." +else + echo + echo "The worker needs Python 3 with the scimesh package, then a coordinator" + echo "to connect to. Run it with environment variables:" + echo + echo " export COORDINATOR_URL=http://COORDINATOR_HOST:8080" + echo " export WORKER_AUTH_TOKEN=" + echo " export WORK_DIR=~/scimesh-worker" + echo " $TARGET" + echo + echo "For a coordinator started with 'coordinator serve', the worker token is" + echo "in ~/.scimesh/worker.token on that machine. Set SCIMESH_PIP_PACKAGE to" + echo "install scimesh into a managed venv, or install it yourself:" + echo " pip install scimesh" +fi diff --git a/mkdocs/index.md b/mkdocs/index.md index b56eebb..f37f587 100644 --- a/mkdocs/index.md +++ b/mkdocs/index.md @@ -90,11 +90,21 @@ curl -L -o coordinator https://github.com/emil28092005/SciMesh/releases/latest/d chmod +x coordinator ``` -- **worker-agent** runs anywhere with Python: it spawns - `python -m scimesh.worker.task`, so the machine needs the `scimesh` - package in a venv (`pip install scimesh`) and the usual environment: - `COORDINATOR_URL`, `WORKER_AUTH_TOKEN`, `WORK_DIR`. The same binary can run - it via `coordinator agent`. +- **worker-agent** is installed separately and joins an existing coordinator: + + ```bash + curl -fsSL https://raw.githubusercontent.com/emil28092005/SciMesh/main/install.sh | bash -s worker + export COORDINATOR_URL=http://COORDINATOR_HOST:8080 + export WORKER_AUTH_TOKEN= + export WORK_DIR=~/scimesh-worker + worker-agent + ``` + + It spawns `python -m scimesh.worker.task`, so the machine needs Python 3 + with the `scimesh` package (`pip install scimesh`, or let the managed venv + do it via `SCIMESH_PIP_PACKAGE`). For a `coordinator serve` instance, the + worker token is in `~/.scimesh/worker.token`. On Windows set + `SCIMESH_COMPONENT=worker` for `install.ps1`. - **coordinator** needs no external services at all in its default mode: `coordinator serve` embeds SQLite (both databases), the userservice, and local workers. The `SCIMESH_DB=postgres` engine remains for cluster