From 481260551ca4d5294128ffc451600d27cfcd74b8 Mon Sep 17 00:00:00 2001 From: Parag Sharma Date: Thu, 27 Aug 2026 18:14:42 +0530 Subject: [PATCH 1/2] chore: remove live-traces script and local Langfuse test stack --- .gitignore | 2 - AGENTS.md | 2 +- CONTRIBUTING.md | 8 +- Taskfile.yml | 28 +- scripts/langfuse/.env | 55 ---- scripts/langfuse/docker-compose.yml | 145 --------- scripts/live-traces.ps1 | 484 ---------------------------- 7 files changed, 4 insertions(+), 720 deletions(-) delete mode 100644 scripts/langfuse/.env delete mode 100644 scripts/langfuse/docker-compose.yml delete mode 100644 scripts/live-traces.ps1 diff --git a/.gitignore b/.gitignore index 6e5e8d2..17bb164 100644 --- a/.gitignore +++ b/.gitignore @@ -33,8 +33,6 @@ squad-export.json .env .env.* !.env.example -# Local-only Langfuse init keys (not production); needed for live-traces.ps1 -!scripts/langfuse/.env *.pem *.key diff --git a/AGENTS.md b/AGENTS.md index d452065..40c5575 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -157,7 +157,7 @@ go test ./... go build -o squad-oc.exe ./cmd/squad-oc ``` -Prefer Task when it is on PATH (`task --list`): `task test`, `task build`, `task ci`, `task release` (snapshot only), `task bump TAG=vX.Y.Z`, `task langfuse:up` / `task langfuse:down`, `task live:e2e` / `task live:traces`. Real publish is `task release:tag TAG=vX.Y.Z` on main then `task release:push`. Do not edit `internal/version/version.go`. Do not use this git clone as the live serve cwd. +Prefer Task when it is on PATH (`task --list`): `task test`, `task build`, `task ci`, `task release` (snapshot only), `task bump TAG=vX.Y.Z`, `task live:e2e` / `task live:models`. Real publish is `task release:tag TAG=vX.Y.Z` on main then `task release:push`. Do not edit `internal/version/version.go`. Do not use this git clone as the live serve cwd. Live OpenCode HTTP checks need a running `opencode serve` and use the `live` build tag. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a819b2e..24968c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,10 +32,8 @@ task release # GoReleaser snapshot into dist/ (no tag, no publish) task bump TAG=vX.Y.Z # pin Homebrew/Scoop/winget from dist/ (no PR) task release:tag TAG=vX.Y.Z # annotated tag on main only (no push) task release:push TAG=vX.Y.Z # push tag; GitHub release workflow publishes -task langfuse:up -task langfuse:down task live:e2e -task live:traces +task live:models ``` Do not edit `internal/version/version.go` to bump. Do not tag a feature branch. @@ -44,11 +42,9 @@ Requires **Go 1.26.6+**. Optional live checks (dummy project only, never this cl ```powershell ./scripts/live-e2e.ps1 -./scripts/live-traces.ps1 -# same as: task live:e2e / task live:traces ``` -`live-e2e.ps1` talks to `opencode serve` on `127.0.0.1:4096` (doctor + PONG). `live-traces.ps1` exercises JSONL spans and optional local Langfuse OTLP ingest. The TUI (`opencode`) is not the API. CI stays `go test ./...` without live env. `task ci` is the local mirror of `.github/workflows/ci.yml`. +`live-e2e.ps1` talks to `opencode serve` on `127.0.0.1:4096` (doctor + PONG). The TUI (`opencode`) is not the API. CI stays `go test ./...` without live env. `task ci` is the local mirror of `.github/workflows/ci.yml`. ## Branch and PR diff --git a/Taskfile.yml b/Taskfile.yml index 9095a7a..803ebca 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,10 +1,9 @@ -# https://taskfile.dev — local CI + Langfuse/live helpers. +# https://taskfile.dev — local CI, release, and live dummy helpers. # Install: go install github.com/go-task/task/v3/cmd/task@latest version: "3" vars: BINARY: '{{if eq OS "windows"}}squad-oc.exe{{else}}squad-oc{{end}}' - COMPOSE: scripts/langfuse/docker-compose.yml PWSH: '{{if eq OS "windows"}}powershell{{else}}pwsh{{end}}' tasks: @@ -111,36 +110,11 @@ tasks: desc: Local CI gate matching .github/workflows/ci.yml deps: [fmt:check, vet, lint, test:race, build, vuln, actionlint] - langfuse:up: - desc: Start local Langfuse v4 on 127.0.0.1:3000 - cmds: - - docker compose -f {{.COMPOSE}} up -d - - langfuse:down: - desc: Stop local Langfuse (keeps volumes) - cmds: - - docker compose -f {{.COMPOSE}} down - - langfuse:down:volumes: - desc: Stop local Langfuse and remove squad-oc-langfuse-* volumes - cmds: - - docker compose -f {{.COMPOSE}} down -v - live:e2e: desc: Dummy PONG check (D:\xAI\squad-oc-dummy only) cmds: - "{{.PWSH}} -File scripts/live-e2e.ps1" - live:traces: - desc: Dummy traces + optional local Langfuse OTLP - cmds: - - "{{.PWSH}} -File scripts/live-traces.ps1" - - live:traces:skip-langfuse: - desc: Dummy traces only (no Langfuse) - cmds: - - "{{.PWSH}} -File scripts/live-traces.ps1 -SkipLangfuse" - live:models: desc: Dummy per-agent model pin check cmds: diff --git a/scripts/langfuse/.env b/scripts/langfuse/.env deleted file mode 100644 index 67c53a7..0000000 --- a/scripts/langfuse/.env +++ /dev/null @@ -1,55 +0,0 @@ -# Local-only Langfuse v4 init keys for scripts/live-traces.ps1. -# NOT production. Well-known dummy credentials for 127.0.0.1 only. - -NEXTAUTH_URL=http://localhost:3000 -NEXTAUTH_SECRET=squad-oc-local-nextauth-secret -SALT=squad-oc-local-salt -# 64 hex chars (openssl rand -hex 32); local-only, not production -ENCRYPTION_KEY=a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90 - -POSTGRES_USER=postgres -POSTGRES_PASSWORD=squad-oc-local-postgres -POSTGRES_DB=postgres -DATABASE_URL=postgresql://postgres:squad-oc-local-postgres@postgres:5432/postgres - -CLICKHOUSE_USER=clickhouse -CLICKHOUSE_PASSWORD=squad-oc-local-clickhouse -CLICKHOUSE_URL=http://clickhouse:8123 -CLICKHOUSE_MIGRATION_URL=clickhouse://clickhouse:9000 -CLICKHOUSE_CLUSTER_ENABLED=false - -REDIS_HOST=redis -REDIS_PORT=6379 -REDIS_AUTH=squad-oc-local-redis - -MINIO_ROOT_USER=minio -MINIO_ROOT_PASSWORD=squad-oc-local-minio -LANGFUSE_S3_EVENT_UPLOAD_BUCKET=langfuse -LANGFUSE_S3_EVENT_UPLOAD_REGION=auto -LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=minio -LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=squad-oc-local-minio -LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=http://minio:9000 -LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE=true -LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/ -LANGFUSE_S3_MEDIA_UPLOAD_BUCKET=langfuse -LANGFUSE_S3_MEDIA_UPLOAD_REGION=auto -LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID=minio -LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=squad-oc-local-minio -LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT=http://minio:9000 -LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE=true -LANGFUSE_S3_MEDIA_UPLOAD_PREFIX=media/ - -TELEMETRY_ENABLED=false -LANGFUSE_INGESTION_QUEUE_DELAY_MS=0 -LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS=100 - -# Headless project/user (local-only, not production) -LANGFUSE_INIT_ORG_ID=squad-oc -LANGFUSE_INIT_ORG_NAME=squad-oc -LANGFUSE_INIT_PROJECT_ID=squad-oc -LANGFUSE_INIT_PROJECT_NAME=squad-oc -LANGFUSE_INIT_PROJECT_PUBLIC_KEY=pk-lf-local-squad-oc -LANGFUSE_INIT_PROJECT_SECRET_KEY=sk-lf-local-squad-oc -LANGFUSE_INIT_USER_EMAIL=local@squad-oc.test -LANGFUSE_INIT_USER_NAME=local -LANGFUSE_INIT_USER_PASSWORD=local-squad-oc diff --git a/scripts/langfuse/docker-compose.yml b/scripts/langfuse/docker-compose.yml deleted file mode 100644 index 81b623c..0000000 --- a/scripts/langfuse/docker-compose.yml +++ /dev/null @@ -1,145 +0,0 @@ -# Minimal official Langfuse v4 stack for scripts/live-traces.ps1. -# Local-only (127.0.0.1). Not production. Do not clone the Langfuse git repo. -# Images: docker.langfuse.com …:4 + clickhouse/clickhouse-server:25.12. -name: squad-oc-langfuse - -services: - langfuse-worker: - image: docker.langfuse.com/langfuse/langfuse-worker:4 - restart: unless-stopped - depends_on: &langfuse-depends-on - postgres: - condition: service_healthy - minio: - condition: service_healthy - redis: - condition: service_healthy - clickhouse: - condition: service_healthy - env_file: - - .env - environment: &langfuse-worker-env - NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000} - DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:squad-oc-local-postgres@postgres:5432/postgres} - SALT: ${SALT:-squad-oc-local-salt} - ENCRYPTION_KEY: ${ENCRYPTION_KEY:-a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90} - TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-false} - CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL:-clickhouse://clickhouse:9000} - CLICKHOUSE_URL: ${CLICKHOUSE_URL:-http://clickhouse:8123} - CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse} - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-squad-oc-local-clickhouse} - CLICKHOUSE_CLUSTER_ENABLED: ${CLICKHOUSE_CLUSTER_ENABLED:-false} - LANGFUSE_S3_EVENT_UPLOAD_BUCKET: ${LANGFUSE_S3_EVENT_UPLOAD_BUCKET:-langfuse} - LANGFUSE_S3_EVENT_UPLOAD_REGION: ${LANGFUSE_S3_EVENT_UPLOAD_REGION:-auto} - LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID:-minio} - LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY:-squad-oc-local-minio} - LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: ${LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT:-http://minio:9000} - LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE:-true} - LANGFUSE_S3_EVENT_UPLOAD_PREFIX: ${LANGFUSE_S3_EVENT_UPLOAD_PREFIX:-events/} - LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: ${LANGFUSE_S3_MEDIA_UPLOAD_BUCKET:-langfuse} - LANGFUSE_S3_MEDIA_UPLOAD_REGION: ${LANGFUSE_S3_MEDIA_UPLOAD_REGION:-auto} - LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID:-minio} - LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY:-squad-oc-local-minio} - LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT:-http://minio:9000} - LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE:-true} - LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: ${LANGFUSE_S3_MEDIA_UPLOAD_PREFIX:-media/} - LANGFUSE_INGESTION_QUEUE_DELAY_MS: ${LANGFUSE_INGESTION_QUEUE_DELAY_MS:-0} - LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS: ${LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS:-100} - REDIS_HOST: ${REDIS_HOST:-redis} - REDIS_PORT: ${REDIS_PORT:-6379} - REDIS_AUTH: ${REDIS_AUTH:-squad-oc-local-redis} - - langfuse-web: - image: docker.langfuse.com/langfuse/langfuse:4 - restart: unless-stopped - depends_on: *langfuse-depends-on - ports: - - 127.0.0.1:3000:3000 - env_file: - - .env - environment: - <<: *langfuse-worker-env - NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-squad-oc-local-nextauth-secret} - LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-squad-oc} - LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-squad-oc} - LANGFUSE_INIT_PROJECT_ID: ${LANGFUSE_INIT_PROJECT_ID:-squad-oc} - LANGFUSE_INIT_PROJECT_NAME: ${LANGFUSE_INIT_PROJECT_NAME:-squad-oc} - LANGFUSE_INIT_PROJECT_PUBLIC_KEY: ${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-pk-lf-local-squad-oc} - LANGFUSE_INIT_PROJECT_SECRET_KEY: ${LANGFUSE_INIT_PROJECT_SECRET_KEY:-sk-lf-local-squad-oc} - LANGFUSE_INIT_USER_EMAIL: ${LANGFUSE_INIT_USER_EMAIL:-local@squad-oc.test} - LANGFUSE_INIT_USER_NAME: ${LANGFUSE_INIT_USER_NAME:-local} - LANGFUSE_INIT_USER_PASSWORD: ${LANGFUSE_INIT_USER_PASSWORD:-local-squad-oc} - - clickhouse: - image: docker.io/clickhouse/clickhouse-server:25.12 - restart: unless-stopped - user: "101:101" - environment: - CLICKHOUSE_DB: default - CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse} - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-squad-oc-local-clickhouse} - volumes: - - squad-oc-langfuse-clickhouse:/var/lib/clickhouse - - squad-oc-langfuse-clickhouse-logs:/var/log/clickhouse-server - healthcheck: - test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1 - interval: 5s - timeout: 5s - retries: 10 - start_period: 1s - - minio: - image: cgr.dev/chainguard/minio - restart: unless-stopped - entrypoint: sh - command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data' - environment: - MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio} - MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-squad-oc-local-minio} - volumes: - - squad-oc-langfuse-minio:/data - healthcheck: - test: ["CMD", "mc", "ready", "local"] - interval: 1s - timeout: 5s - retries: 5 - start_period: 1s - - redis: - image: docker.io/redis:7 - restart: unless-stopped - command: > - --requirepass ${REDIS_AUTH:-squad-oc-local-redis} - --maxmemory-policy noeviction - healthcheck: - test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "squad-oc-local-redis", "ping"] - interval: 3s - timeout: 10s - retries: 10 - - postgres: - image: docker.io/postgres:17 - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 3s - timeout: 3s - retries: 10 - environment: - POSTGRES_USER: ${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-squad-oc-local-postgres} - POSTGRES_DB: ${POSTGRES_DB:-postgres} - TZ: UTC - PGTZ: UTC - volumes: - - squad-oc-langfuse-postgres:/var/lib/postgresql/data - -volumes: - squad-oc-langfuse-postgres: - name: squad-oc-langfuse-postgres - squad-oc-langfuse-clickhouse: - name: squad-oc-langfuse-clickhouse - squad-oc-langfuse-clickhouse-logs: - name: squad-oc-langfuse-clickhouse-logs - squad-oc-langfuse-minio: - name: squad-oc-langfuse-minio diff --git a/scripts/live-traces.ps1 b/scripts/live-traces.ps1 deleted file mode 100644 index b62571f..0000000 --- a/scripts/live-traces.ps1 +++ /dev/null @@ -1,484 +0,0 @@ -# cwd: repo root. Uses D:\xAI\squad-oc-dummy only (never this git repo). -# Builds squad-oc.exe, inits the dummy, starts or attaches to -# opencode serve on 127.0.0.1:4096, run -p TRACEOK, asserts traces --json/--export. -# Unless -SkipLangfuse: attach or start local Langfuse v4, second run with OTLP, poll traces API. -# Leaves serve running. Does not stop a Langfuse this script did not start. -# Does not change live-e2e.ps1 (PONG check stays put). -param([switch]$SkipLangfuse) - -$ErrorActionPreference = "Stop" - -$RepoRoot = Split-Path -Parent $PSScriptRoot -$Dummy = "D:\xAI\squad-oc-dummy" -$BaseURL = "http://127.0.0.1:4096" -$LangfuseURL = "http://127.0.0.1:3000" -$SquadModel = "opencode/big-pickle" -$Prompt = "Reply with exactly TRACEOK and nothing else." -$LangfusePrompt = "Reply with exactly LANGFUSEOK and nothing else." -$Log = Join-Path $Dummy "live-traces.log" -$MainDumpDir = "D:\xAI\squad-opencode\.playwright-mcp" -$WorktreeDumpDir = Join-Path $RepoRoot ".playwright-mcp" -$MainDump = Join-Path $MainDumpDir "dummy-traces-summary.json" -$WorktreeDump = Join-Path $WorktreeDumpDir "dummy-traces-summary.json" -$MainLangfuseDump = Join-Path $MainDumpDir "dummy-langfuse-trace.json" -$WorktreeLangfuseDump = Join-Path $WorktreeDumpDir "dummy-langfuse-trace.json" - -$Pk = "pk-lf-local-squad-oc" -$Sk = "sk-lf-local-squad-oc" - -$startedServe = $false -$startedLangfuse = $false -$serveProc = $null - -function Write-Log { - param([string]$Message) - $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-ddTHH:mm:ssK"), $Message - Write-Host $line - $parent = Split-Path -Parent $Log - if (Test-Path $parent) { - Add-Content -Path $Log -Value $line - } -} - -function Get-Port4096Listeners { - Get-NetTCPConnection -LocalPort 4096 -State Listen -ErrorAction SilentlyContinue -} - -function Assert-Port4096Safe { - $listeners = @(Get-Port4096Listeners) - if ($listeners.Count -eq 0) { - return $false - } - $ok = @("127.0.0.1", "::1", "localhost") - $bad = @($listeners | Where-Object { $ok -notcontains $_.LocalAddress }) - if ($bad.Count -gt 0) { - $addrs = ($bad | ForEach-Object { $_.LocalAddress } | Sort-Object -Unique) -join ", " - throw "port 4096 is bound on non-localhost ($addrs); refusing to steal another server" - } - return $true -} - -function Wait-ServeReady { - param([int]$Seconds = 20) - $deadline = (Get-Date).AddSeconds($Seconds) - while ((Get-Date) -lt $deadline) { - try { - $r = Invoke-WebRequest -Uri "$BaseURL/global/health" -UseBasicParsing -TimeoutSec 2 - if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 500) { - return - } - } catch { - Start-Sleep -Milliseconds 250 - } - } - throw "opencode serve at $BaseURL never became ready" -} - -function Invoke-SquadOc { - param( - [string]$Exe, - [string[]]$CommandArgs - ) - Write-Log ("squad-oc " + ($CommandArgs -join " ")) - & $Exe @CommandArgs - if ($LASTEXITCODE -ne 0) { - throw "squad-oc $($CommandArgs[0]) exited $LASTEXITCODE" - } -} - -function Get-OpencodeExe { - $cmd = Get-Command opencode -ErrorAction SilentlyContinue - if (-not $cmd) { - throw "opencode not on PATH" - } - $src = $cmd.Source - if ($src -like "*.ps1") { - $exe = Join-Path (Split-Path $src) "node_modules\opencode-ai\bin\opencode.exe" - if (Test-Path $exe) { - return $exe - } - } - return $src -} - -function Clear-OtlpEnv { - foreach ($n in @( - "OTEL_EXPORTER_OTLP_ENDPOINT", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", - "OTEL_EXPORTER_OTLP_PROTOCOL", - "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", - "OTEL_EXPORTER_OTLP_HEADERS", - "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT" - )) { - Remove-Item "Env:$n" -ErrorAction SilentlyContinue - } -} - -function Write-JsonDump { - param( - [string[]]$Paths, - [object]$Record - ) - $json = $Record | ConvertTo-Json -Depth 12 - foreach ($p in $Paths) { - $dir = Split-Path -Parent $p - if (-not (Test-Path $dir)) { - New-Item -ItemType Directory -Path $dir | Out-Null - } - Set-Content -Path $p -Value $json -Encoding utf8 - Write-Log "wrote $p" - } - return $json -} - -function Test-LangfuseHealthy { - foreach ($url in @("$LangfuseURL/api/public/health", "$LangfuseURL/")) { - try { - $r = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 3 - if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 500) { - return $true - } - } catch { - } - } - return $false -} - -function Get-LangfuseBasicB64 { - $pair = "{0}:{1}" -f $Pk, $Sk - return [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair)) -} - -function Wait-LangfuseReady { - param([int]$Seconds = 180) - $deadline = (Get-Date).AddSeconds($Seconds) - while ((Get-Date) -lt $deadline) { - if (Test-LangfuseHealthy) { - return - } - Start-Sleep -Seconds 2 - } - throw "Langfuse at $LangfuseURL never became ready" -} - -function Invoke-LangfuseGet { - param( - [string]$Url, - [hashtable]$Headers - ) - try { - $r = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 10 -Headers $Headers - return [ordered]@{ - url = $Url - status = [int]$r.StatusCode - body = $r.Content - } - } catch { - $status = 0 - $resp = $_.Exception.Response - if ($null -ne $resp -and $null -ne $resp.StatusCode) { - $status = [int]$resp.StatusCode - } - return [ordered]@{ - url = $Url - status = $status - body = $null - error = $_.Exception.Message - } - } -} - -# This-run gate: both span names. LANGFUSEOK only if the payload actually has I/O fields. -function Test-LangfuseThisRun { - param([string]$Body) - if ([string]::IsNullOrWhiteSpace($Body)) { - return $false - } - if ($Body -notlike "*squad-oc.run*" -or $Body -notlike "*gen_ai.chat*") { - return $false - } - $hasIO = ($Body -like "*gen_ai.input.messages*") -or - ($Body -like "*gen_ai.output.messages*") -or - ($Body -match '"input"\s*:') -or - ($Body -match '"output"\s*:') - if ($hasIO -and $Body -notlike "*LANGFUSEOK*") { - return $false - } - return $true -} - -function Get-LangfuseTraces { - param( - [string]$B64, - [string]$FromStartTime - ) - $headers = @{ Authorization = "Basic $B64" } - # Try legacy traces first (v4 events_only → 404). 2xx only counts if this-run markers match. - $tracesUrl = "$LangfuseURL/api/public/traces?limit=20" - $tracesGot = Invoke-LangfuseGet -Url $tracesUrl -Headers $headers - if ($tracesGot.status -ge 200 -and $tracesGot.status -lt 300 -and (Test-LangfuseThisRun $tracesGot.body)) { - return $tracesGot - } - # v4 replacement, windowed so leftover observations cannot match. - $obsUrl = "$LangfuseURL/api/public/v2/observations?limit=20" - if (-not [string]::IsNullOrWhiteSpace($FromStartTime)) { - $obsUrl = $obsUrl + "&fromStartTime=" + [uri]::EscapeDataString($FromStartTime) - } - $obsGot = Invoke-LangfuseGet -Url $obsUrl -Headers $headers - if ($obsGot.status -ge 200 -and $obsGot.status -lt 300 -and (Test-LangfuseThisRun $obsGot.body)) { - return $obsGot - } - if ($null -ne $obsGot.url) { - return $obsGot - } - return $tracesGot -} - -try { - Set-Location $RepoRoot - - if (-not (Test-Path $Dummy)) { - throw "dummy missing: $Dummy (never use this git clone; never squad-oc-dummy-v2)" - } - if (Test-Path $Log) { - Remove-Item $Log -Force - } - Write-Log "repo=$RepoRoot dummy=$Dummy skipLangfuse=$SkipLangfuse" - - Write-Log "go build -o squad-oc.exe ./cmd/squad-oc" - go build -o squad-oc.exe ./cmd/squad-oc - if ($LASTEXITCODE -ne 0) { - throw "go build failed" - } - $exe = Join-Path $RepoRoot "squad-oc.exe" - if (-not (Test-Path $exe)) { - throw "squad-oc.exe missing after build" - } - - $prev = Get-Location - Set-Location $Dummy - try { - if (-not (Test-Path (Join-Path $Dummy ".squad\config.json"))) { - Invoke-SquadOc -Exe $exe -CommandArgs @("init", "--preset", "default", "--description", "dummy") - } else { - Write-Log "already initialized" - } - - # Pin a model this OpenCode instance actually has. xai/* / anthropic/* - # 500 when those providers are not configured (opencode models lists opencode/* only). - Invoke-SquadOc -Exe $exe -CommandArgs @("cast", "--model", "squad", $SquadModel) - - $opencodeExe = Get-OpencodeExe - Write-Log "opencode=$opencodeExe" - - if (Assert-Port4096Safe) { - Write-Log "attaching to existing localhost:4096" - } else { - Write-Log "starting opencode serve --hostname 127.0.0.1 --port 4096" - $serveProc = Start-Process -FilePath $opencodeExe -ArgumentList @("serve", "--hostname", "127.0.0.1", "--port", "4096") -WorkingDirectory $Dummy -PassThru -WindowStyle Hidden - $startedServe = $true - Write-Log "started serve pid=$($serveProc.Id) (leaving it running)" - } - Wait-ServeReady - - Clear-OtlpEnv - $parent = $null - $child = $null - $spans = $null - $runOut = "" - for ($attempt = 1; $attempt -le 3; $attempt++) { - $attemptStart = (Get-Date).AddSeconds(-2) - Write-Log "run -p TRACEOK (no OTLP env) attempt $attempt" - $runOut = & $exe run -p $Prompt 2>&1 | Out-String - Write-Host $runOut - Add-Content -Path $Log -Value $runOut - if ($LASTEXITCODE -ne 0) { - Write-Log "run attempt $attempt exited $LASTEXITCODE" - continue - } - - Write-Log "traces --json --last 8" - $tracesJson = & $exe traces --json --last 8 | Out-String - if ($LASTEXITCODE -ne 0) { - throw "squad-oc traces --json exited $LASTEXITCODE" - } - Write-Host $tracesJson - Add-Content -Path $Log -Value $tracesJson - $spans = $tracesJson | ConvertFrom-Json - if ($null -eq $spans) { - throw "traces --json produced no spans" - } - $child = @( - $spans | Where-Object { - $_.name -eq "gen_ai.chat" -and - [string]$_.prompt -like "*TRACEOK*" -and - -not [string]::IsNullOrWhiteSpace([string]$_.completion) -and - ([datetime]$_.start) -ge $attemptStart - } - ) | Select-Object -Last 1 - if ($null -eq $child) { - Write-Log ("attempt {0}: gen_ai.chat missing completion (OpenCode empty/upstream flake)" -f $attempt) - continue - } - $parent = @( - $spans | Where-Object { $_.name -eq "squad-oc.run" -and $_.traceId -eq $child.traceId } - ) | Select-Object -Last 1 - if ($null -ne $parent) { - break - } - } - if ($null -eq $parent -or $null -eq $child) { - throw "traces --json missing parent squad-oc.run + child gen_ai.chat with local prompt/completion" - } - if ([string]::IsNullOrWhiteSpace([string]$child.model)) { - throw "gen_ai.chat missing model from OpenCode" - } - $inTok = 0 - $outTok = 0 - if ($null -ne $child.inputTokens) { $inTok = [int]$child.inputTokens } - if ($null -ne $child.outputTokens) { $outTok = [int]$child.outputTokens } - if ($inTok -eq 0 -and $outTok -eq 0) { - Write-Log "WARN gen_ai.chat tokens are 0 (OpenCode omitempty / empty Info)" - } - Write-Log ("PASS jsonl: parent={0} child={1} model={2} tokens={3}/{4}" -f $parent.name, $child.name, $child.model, $inTok, $outTok) - - $exportPath = Join-Path $env:TEMP ("squad-oc-traces-export-{0}.json" -f [guid]::NewGuid().ToString("n")) - Write-Log "traces --export $exportPath --last 8" - Invoke-SquadOc -Exe $exe -CommandArgs @("traces", "--export", $exportPath, "--last", "8") - $exportText = Get-Content -Raw -Path $exportPath - if ($exportText -notlike "*gen_ai.*") { - throw "traces --export missing gen_ai. metadata" - } - if ($exportText -like "*gen_ai.input.messages*") { - throw "traces --export leaked gen_ai.input.messages" - } - if ($exportText -like "*$Prompt*") { - throw "traces --export leaked prompt text" - } - Write-Log "PASS export: has gen_ai. metadata, no bodies" - - $record = [ordered]@{ - dummy = $Dummy - startedServe = $startedServe - skipLangfuse = [bool]$SkipLangfuse - prompt = $Prompt - runPreview = $runOut.Substring(0, [Math]::Min(240, $runOut.Length)) - parentName = $parent.name - childName = $child.name - model = $child.model - provider = $child.provider - inputTokens = $child.inputTokens - outputTokens = $child.outputTokens - promptSeen = $child.prompt - completion = $child.completion - exportPath = $exportPath - traces = $spans - } - Write-JsonDump -Paths @($MainDump, $WorktreeDump) -Record $record | Out-Null - - if ($SkipLangfuse) { - Write-Log "Langfuse skipped: -SkipLangfuse" - } else { - if (Test-LangfuseHealthy) { - Write-Log "attaching to existing Langfuse $LangfuseURL" - } else { - $docker = Get-Command docker -ErrorAction SilentlyContinue - if (-not $docker) { - throw "docker not on PATH (needed unless -SkipLangfuse)" - } - $composeFile = Join-Path $RepoRoot "scripts\langfuse\docker-compose.yml" - $envFile = Join-Path $RepoRoot "scripts\langfuse\.env" - if (-not (Test-Path $composeFile)) { - throw "missing $composeFile" - } - Write-Log "docker compose -f scripts/langfuse/docker-compose.yml up -d" - & docker compose --project-name squad-oc-langfuse --env-file $envFile -f $composeFile up -d - if ($LASTEXITCODE -ne 0) { - throw "docker compose up failed ($LASTEXITCODE)" - } - $startedLangfuse = $true - Write-Log "started Langfuse compose (leaving it running); waiting for web" - Wait-LangfuseReady -Seconds 180 - } - - $b64 = Get-LangfuseBasicB64 - $env:OTEL_EXPORTER_OTLP_ENDPOINT = "http://127.0.0.1:3000/api/public/otel" - $env:OTEL_EXPORTER_OTLP_PROTOCOL = "http/protobuf" - $env:OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = "true" - $env:OTEL_EXPORTER_OTLP_HEADERS = "Authorization=Basic $b64,x-langfuse-ingestion-version=4" - Remove-Item Env:OTEL_EXPORTER_OTLP_TRACES_ENDPOINT -ErrorAction SilentlyContinue - Remove-Item Env:OTEL_EXPORTER_OTLP_TRACES_PROTOCOL -ErrorAction SilentlyContinue - - $fromStart = (Get-Date).ToUniversalTime().AddSeconds(-2).ToString("yyyy-MM-ddTHH:mm:ssZ") - Write-Log "Langfuse poll window fromStartTime=$fromStart" - - $lfRunOut = "" - $lfOk = $false - for ($attempt = 1; $attempt -le 3; $attempt++) { - Write-Log "run -p LANGFUSEOK (OTLP http/protobuf + capture on) attempt $attempt" - $lfRunOut = & $exe run -p $LangfusePrompt 2>&1 | Out-String - Write-Host $lfRunOut - Add-Content -Path $Log -Value $lfRunOut - if ($LASTEXITCODE -eq 0) { - $lfOk = $true - break - } - Write-Log "langfuse run attempt $attempt exited $LASTEXITCODE" - } - if (-not $lfOk) { - throw "squad-oc run (langfuse) failed after retries" - } - - $got = $null - $deadline = (Get-Date).AddSeconds(60) - while ((Get-Date) -lt $deadline) { - $got = Get-LangfuseTraces -B64 $b64 -FromStartTime $fromStart - Write-Log ("GET {0} status={1}" -f $got.url, $got.status) - if ($got.status -ge 200 -and $got.status -lt 300 -and (Test-LangfuseThisRun $got.body)) { - Write-Log "PASS Langfuse API found this-run parent+child" - break - } - $got = $null - Start-Sleep -Seconds 2 - } - if ($null -eq $got -or -not (Test-LangfuseThisRun $got.body)) { - throw "Langfuse GET never showed this-run squad-oc.run + gen_ai.chat" - } - - $lfRecord = [ordered]@{ - dummy = $Dummy - startedLangfuse = $startedLangfuse - endpoint = $env:OTEL_EXPORTER_OTLP_ENDPOINT - protocol = $env:OTEL_EXPORTER_OTLP_PROTOCOL - fromStartTime = $fromStart - prompt = $LangfusePrompt - runPreview = $lfRunOut.Substring(0, [Math]::Min(240, $lfRunOut.Length)) - tracesStatus = $got.status - tracesUrl = $got.url - traces = $got.body - } - Write-JsonDump -Paths @($MainLangfuseDump, $WorktreeLangfuseDump) -Record $lfRecord | Out-Null - } - } finally { - Set-Location $prev - Clear-OtlpEnv - } - - if ($startedServe) { - Write-Log "leaving serve running (pid=$($serveProc.Id))" - } else { - Write-Log "left existing serve running" - } - if (-not $SkipLangfuse) { - if ($startedLangfuse) { - Write-Log "leaving Langfuse compose running (this script started it)" - } else { - Write-Log "left existing Langfuse running" - } - } - Write-Log "live-traces ok" - exit 0 -} catch { - Write-Log ("FAIL: " + $_.Exception.Message) - throw -} From ca1a735143bab5579023bb20c67760ad1351ff12 Mon Sep 17 00:00:00 2001 From: Parag Sharma Date: Thu, 27 Aug 2026 18:16:27 +0530 Subject: [PATCH 2/2] chore: remove live-e2e and live-models dummy scripts --- AGENTS.md | 2 +- CONTRIBUTING.md | 10 +- Taskfile.yml | 13 +- docs/get-started.md | 12 -- scripts/live-e2e.ps1 | 180 ------------------------- scripts/live-models.ps1 | 292 ---------------------------------------- 6 files changed, 3 insertions(+), 506 deletions(-) delete mode 100644 scripts/live-e2e.ps1 delete mode 100644 scripts/live-models.ps1 diff --git a/AGENTS.md b/AGENTS.md index 40c5575..6613572 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -157,7 +157,7 @@ go test ./... go build -o squad-oc.exe ./cmd/squad-oc ``` -Prefer Task when it is on PATH (`task --list`): `task test`, `task build`, `task ci`, `task release` (snapshot only), `task bump TAG=vX.Y.Z`, `task live:e2e` / `task live:models`. Real publish is `task release:tag TAG=vX.Y.Z` on main then `task release:push`. Do not edit `internal/version/version.go`. Do not use this git clone as the live serve cwd. +Prefer Task when it is on PATH (`task --list`): `task test`, `task build`, `task ci`, `task release` (snapshot only), `task bump TAG=vX.Y.Z`. Real publish is `task release:tag TAG=vX.Y.Z` on main then `task release:push`. Do not edit `internal/version/version.go`. Live OpenCode HTTP checks need a running `opencode serve` and use the `live` build tag. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24968c1..1d50148 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,19 +32,11 @@ task release # GoReleaser snapshot into dist/ (no tag, no publish) task bump TAG=vX.Y.Z # pin Homebrew/Scoop/winget from dist/ (no PR) task release:tag TAG=vX.Y.Z # annotated tag on main only (no push) task release:push TAG=vX.Y.Z # push tag; GitHub release workflow publishes -task live:e2e -task live:models ``` Do not edit `internal/version/version.go` to bump. Do not tag a feature branch. -Requires **Go 1.26.6+**. Optional live checks (dummy project only, never this clone as the serve cwd unless you mean to dogfood): - -```powershell -./scripts/live-e2e.ps1 -``` - -`live-e2e.ps1` talks to `opencode serve` on `127.0.0.1:4096` (doctor + PONG). The TUI (`opencode`) is not the API. CI stays `go test ./...` without live env. `task ci` is the local mirror of `.github/workflows/ci.yml`. +Requires **Go 1.26.6+**. CI is `go test ./...` (no live OpenCode). `task ci` is the local mirror of `.github/workflows/ci.yml`. Optional package test `TestLiveEnsureAPI` stays behind `SQUAD_OC_LIVE=1`. ## Branch and PR diff --git a/Taskfile.yml b/Taskfile.yml index 803ebca..6a8b581 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,10 +1,9 @@ -# https://taskfile.dev — local CI, release, and live dummy helpers. +# https://taskfile.dev — local CI and release helpers. # Install: go install github.com/go-task/task/v3/cmd/task@latest version: "3" vars: BINARY: '{{if eq OS "windows"}}squad-oc.exe{{else}}squad-oc{{end}}' - PWSH: '{{if eq OS "windows"}}powershell{{else}}pwsh{{end}}' tasks: default: @@ -110,16 +109,6 @@ tasks: desc: Local CI gate matching .github/workflows/ci.yml deps: [fmt:check, vet, lint, test:race, build, vuln, actionlint] - live:e2e: - desc: Dummy PONG check (D:\xAI\squad-oc-dummy only) - cmds: - - "{{.PWSH}} -File scripts/live-e2e.ps1" - - live:models: - desc: Dummy per-agent model pin check - cmds: - - "{{.PWSH}} -File scripts/live-models.ps1" - release:check: desc: Validate .goreleaser.yaml cmds: diff --git a/docs/get-started.md b/docs/get-started.md index 1e82c3e..7b4a8ed 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -160,18 +160,6 @@ squad-oc run --url http://127.0.0.1:5000 -p "Summarize .squad/team.md" Plain `opencode` opens the interactive UI and does **not** listen on 4096. -### Live check - -From this repo root, ground `doctor` / `run` against a dummy project (never this git tree): - -```powershell -pwsh -File scripts/live-e2e.ps1 -``` - -The script builds `squad-oc.exe`, inits `D:\xAI\squad-oc-dummy` if needed, starts `opencode serve --hostname 127.0.0.1 --port 4096` there (or attaches if that localhost port is already up), then runs `doctor` and `run -p "Reply with exactly PONG and nothing else."`. It fails if `:4096` is bound on a non-localhost address. The TUI is optional; if no window is open, skip it. - -`TestLiveEnsureAPI` stays behind `SQUAD_OC_LIVE=1`. CI stays unit-only (`go test ./...` without that env). - --- ## Also useful diff --git a/scripts/live-e2e.ps1 b/scripts/live-e2e.ps1 deleted file mode 100644 index 9a25e36..0000000 --- a/scripts/live-e2e.ps1 +++ /dev/null @@ -1,180 +0,0 @@ -# cwd: repo root. Uses D:\xAI\squad-oc-dummy only (never this git repo). -# Builds squad-oc.exe, inits the dummy, starts or attaches to -# opencode serve on 127.0.0.1:4096, then doctor + run (+ optional recast). -param([switch]$SkipTui) - -$ErrorActionPreference = "Stop" - -$RepoRoot = Split-Path -Parent $PSScriptRoot -$Dummy = "D:\xAI\squad-oc-dummy" -$BaseURL = "http://127.0.0.1:4096" -$Prompt = "Reply with exactly PONG and nothing else." -$Log = Join-Path $Dummy "live-e2e.log" - -$startedServe = $false -$serveProc = $null - -function Write-Log { - param([string]$Message) - $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-ddTHH:mm:ssK"), $Message - Write-Host $line - if (Test-Path (Split-Path -Parent $Log)) { - Add-Content -Path $Log -Value $line - } -} - -function Get-Port4096Listeners { - Get-NetTCPConnection -LocalPort 4096 -State Listen -ErrorAction SilentlyContinue -} - -function Assert-Port4096Safe { - $listeners = @(Get-Port4096Listeners) - if ($listeners.Count -eq 0) { - return $false - } - $ok = @("127.0.0.1", "::1", "localhost") - $bad = @($listeners | Where-Object { $ok -notcontains $_.LocalAddress }) - if ($bad.Count -gt 0) { - $addrs = ($bad | ForEach-Object { $_.LocalAddress } | Sort-Object -Unique) -join ", " - throw "port 4096 is bound on non-localhost ($addrs); refusing to steal another server" - } - return $true -} - -function Wait-ServeReady { - param([int]$Seconds = 20) - $deadline = (Get-Date).AddSeconds($Seconds) - while ((Get-Date) -lt $deadline) { - try { - $r = Invoke-WebRequest -Uri "$BaseURL/global/health" -UseBasicParsing -TimeoutSec 2 - if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 500) { - return - } - } catch { - Start-Sleep -Milliseconds 250 - } - } - throw "opencode serve at $BaseURL never became ready" -} - -function Invoke-SquadOc { - param( - [string]$Exe, - [string[]]$CommandArgs - ) - Write-Log ("squad-oc " + ($CommandArgs -join " ")) - & $Exe @CommandArgs - if ($LASTEXITCODE -ne 0) { - throw "squad-oc $($CommandArgs[0]) exited $LASTEXITCODE" - } -} - -try { - Set-Location $RepoRoot - - if (-not (Test-Path $Dummy)) { - New-Item -ItemType Directory -Path $Dummy | Out-Null - } - if (Test-Path $Log) { - Remove-Item $Log -Force - } - Write-Log "repo=$RepoRoot dummy=$Dummy" - - if (-not (Test-Path (Join-Path $Dummy ".git"))) { - Write-Log "git init $Dummy" - git init $Dummy | Out-Host - if ($LASTEXITCODE -ne 0) { - throw "git init failed in $Dummy" - } - } - - Write-Log "go build -o squad-oc.exe ./cmd/squad-oc" - go build -o squad-oc.exe ./cmd/squad-oc - if ($LASTEXITCODE -ne 0) { - throw "go build failed" - } - $exe = Join-Path $RepoRoot "squad-oc.exe" - if (-not (Test-Path $exe)) { - throw "squad-oc.exe missing after build" - } - - $prev = Get-Location - Set-Location $Dummy - try { - if (-not (Test-Path (Join-Path $Dummy ".squad\config.json"))) { - Invoke-SquadOc -Exe $exe -CommandArgs @("init", "--preset", "default") - } else { - Write-Log "already initialized" - } - - $opencode = Get-Command opencode -ErrorAction SilentlyContinue - if (-not $opencode) { - throw "opencode not on PATH" - } - - if (Assert-Port4096Safe) { - Write-Log "attaching to existing localhost:4096" - } else { - Write-Log "starting opencode serve --hostname 127.0.0.1 --port 4096" - $serveProc = Start-Process -FilePath $opencode.Source -ArgumentList @("serve", "--hostname", "127.0.0.1", "--port", "4096") -WorkingDirectory $Dummy -PassThru -WindowStyle Hidden - $startedServe = $true - Write-Log "started serve pid=$($serveProc.Id)" - } - Wait-ServeReady - - Invoke-SquadOc -Exe $exe -CommandArgs @("doctor") - - Write-Log "run -p PONG" - $runOut = & $exe run -p $Prompt 2>&1 | Out-String - Write-Host $runOut - Add-Content -Path $Log -Value $runOut - if ($LASTEXITCODE -ne 0) { - throw "squad-oc run exited $LASTEXITCODE" - } - if ($runOut -notmatch "PONG" -and [string]::IsNullOrWhiteSpace($runOut)) { - throw "run produced no assistant text" - } - if ($runOut -notmatch "PONG") { - Write-Log "run paraphrased (no exact PONG); accepted non-empty reply" - } else { - Write-Log "run ok (PONG)" - } - - $probeAgent = Join-Path $Dummy ".opencode\agents\liveprobe.md" - if (Test-Path $probeAgent) { - Write-Log "recast smoke: liveprobe.md already present" - } else { - Invoke-SquadOc -Exe $exe -CommandArgs @("cast", "--add", "LiveProbe", "--role", "Probe") - if (-not (Test-Path $probeAgent)) { - throw "expected .opencode/agents/liveprobe.md after cast --add LiveProbe" - } - Write-Log "recast smoke: wrote liveprobe.md" - } - - if ($SkipTui) { - Write-Log "TUI skipped: -SkipTui" - } - } finally { - Set-Location $prev - } - - Write-Log "live-e2e ok" - exit 0 -} catch { - Write-Log ("FAIL: " + $_.Exception.Message) - throw -} finally { - if ($startedServe -and $null -ne $serveProc) { - Write-Log "stopping serve pid=$($serveProc.Id)" - try { - if (-not $serveProc.HasExited) { - Stop-Process -Id $serveProc.Id -Force -ErrorAction SilentlyContinue - # children of opencode serve (node) - Get-CimInstance Win32_Process -Filter "ParentProcessId=$($serveProc.Id)" -ErrorAction SilentlyContinue | - ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } - } - } catch { - Write-Log ("stop serve: " + $_.Exception.Message) - } - } -} diff --git a/scripts/live-models.ps1 b/scripts/live-models.ps1 deleted file mode 100644 index b00fe20..0000000 --- a/scripts/live-models.ps1 +++ /dev/null @@ -1,292 +0,0 @@ -# cwd: repo root. Uses D:\xAI\squad-oc-dummy only (never this git repo). -# Builds squad-oc.exe, pins per-agent models, starts or attaches to -# opencode serve on 127.0.0.1:4096, asserts host files + GET /config. -# Leaves serve running. Does not restore dummy model pins. -# Does not change live-e2e.ps1 (PONG check stays put). - -$ErrorActionPreference = "Stop" - -$RepoRoot = Split-Path -Parent $PSScriptRoot -$Dummy = "D:\xAI\squad-oc-dummy" -$BaseURL = "http://127.0.0.1:4096" -$Log = Join-Path $Dummy "live-models.log" -$MainDumpDir = "D:\xAI\squad-opencode\.playwright-mcp" -$MainDump = Join-Path $MainDumpDir "dummy-per-agent-model-config.json" -$WorktreeDumpDir = Join-Path $RepoRoot ".playwright-mcp" -$WorktreeDump = Join-Path $WorktreeDumpDir "dummy-per-agent-model-config.json" - -$SquadModel = "opencode/big-pickle" -$LeadModel = "opencode/hy3-free" - -$startedServe = $false -$serveProc = $null - -function Write-Log { - param([string]$Message) - $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-ddTHH:mm:ssK"), $Message - Write-Host $line - $parent = Split-Path -Parent $Log - if (Test-Path $parent) { - Add-Content -Path $Log -Value $line - } -} - -function Get-Port4096Listeners { - Get-NetTCPConnection -LocalPort 4096 -State Listen -ErrorAction SilentlyContinue -} - -function Assert-Port4096Safe { - $listeners = @(Get-Port4096Listeners) - if ($listeners.Count -eq 0) { - return $false - } - $ok = @("127.0.0.1", "::1", "localhost") - $bad = @($listeners | Where-Object { $ok -notcontains $_.LocalAddress }) - if ($bad.Count -gt 0) { - $addrs = ($bad | ForEach-Object { $_.LocalAddress } | Sort-Object -Unique) -join ", " - throw "port 4096 is bound on non-localhost ($addrs); refusing to steal another server" - } - return $true -} - -function Wait-ServeReady { - param([int]$Seconds = 20) - $deadline = (Get-Date).AddSeconds($Seconds) - while ((Get-Date) -lt $deadline) { - try { - $r = Invoke-WebRequest -Uri "$BaseURL/global/health" -UseBasicParsing -TimeoutSec 2 - if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 500) { - return - } - } catch { - Start-Sleep -Milliseconds 250 - } - } - throw "opencode serve at $BaseURL never became ready" -} - -function Invoke-SquadOc { - param( - [string]$Exe, - [string[]]$CommandArgs - ) - Write-Log ("squad-oc " + ($CommandArgs -join " ")) - & $Exe @CommandArgs - if ($LASTEXITCODE -ne 0) { - throw "squad-oc $($CommandArgs[0]) exited $LASTEXITCODE" - } -} - -function Get-OpencodeExe { - $cmd = Get-Command opencode -ErrorAction SilentlyContinue - if (-not $cmd) { - throw "opencode not on PATH" - } - $src = $cmd.Source - if ($src -like "*.ps1") { - $exe = Join-Path (Split-Path $src) "node_modules\opencode-ai\bin\opencode.exe" - if (Test-Path $exe) { - return $exe - } - } - return $src -} - -function Resolve-HostAgentFile { - param([string]$RoleId) - $agents = Join-Path $Dummy ".opencode\agents" - $candidates = @(Join-Path $agents "$RoleId.md") - $office = @{ - lead = "michael" - frontend = "jim" - backend = "dwight" - tester = "pam" - } - if ($office.ContainsKey($RoleId)) { - $candidates += (Join-Path $agents ($office[$RoleId] + ".md")) - } - foreach ($p in $candidates) { - if (Test-Path $p) { - return $p - } - } - throw "host agent file not found for $RoleId (tried: $($candidates -join ', '))" -} - -function Assert-FileContains { - param( - [string]$Path, - [string]$Needle, - [string]$Label - ) - $text = Get-Content -Raw -Path $Path - if ($text -notlike "*$Needle*") { - throw "$Label missing '$Needle' in $Path" - } - Write-Log ("PASS file: {0} has '{1}'" -f $Label, $Needle) -} - -function Invoke-ServeGet { - param([string]$Url) - try { - $r = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 10 - return [ordered]@{ - url = $Url - status = [int]$r.StatusCode - body = $r.Content - } - } catch { - $status = 0 - $resp = $_.Exception.Response - if ($null -ne $resp -and $null -ne $resp.StatusCode) { - $status = [int]$resp.StatusCode - } - return [ordered]@{ - url = $Url - status = $status - body = $null - error = $_.Exception.Message - } - } -} - -try { - Set-Location $RepoRoot - - if (-not (Test-Path $Dummy)) { - throw "dummy missing: $Dummy (never use this git clone; never squad-oc-dummy-v2)" - } - if (Test-Path $Log) { - Remove-Item $Log -Force - } - Write-Log "repo=$RepoRoot dummy=$Dummy" - - Write-Log "go build -o squad-oc.exe ./cmd/squad-oc" - go build -o squad-oc.exe ./cmd/squad-oc - if ($LASTEXITCODE -ne 0) { - throw "go build failed" - } - $exe = Join-Path $RepoRoot "squad-oc.exe" - if (-not (Test-Path $exe)) { - throw "squad-oc.exe missing after build" - } - - $prev = Get-Location - Set-Location $Dummy - try { - if (-not (Test-Path (Join-Path $Dummy ".squad\config.json"))) { - Invoke-SquadOc -Exe $exe -CommandArgs @("init", "--preset", "default", "--description", "dummy") - } else { - Write-Log "already initialized" - } - - Invoke-SquadOc -Exe $exe -CommandArgs @("recast") - Invoke-SquadOc -Exe $exe -CommandArgs @("cast", "--model", "squad", $SquadModel) - Invoke-SquadOc -Exe $exe -CommandArgs @("cast", "--model", "Lead", $LeadModel) - Invoke-SquadOc -Exe $exe -CommandArgs @("cast", "--model", "Tester", "-") - Invoke-SquadOc -Exe $exe -CommandArgs @("status") - - $squadFile = Join-Path $Dummy ".opencode\agents\squad.md" - if (-not (Test-Path $squadFile)) { - throw "missing $squadFile" - } - Assert-FileContains -Path $squadFile -Needle "model: $SquadModel" -Label "squad.md" - Assert-FileContains -Path $squadFile -Needle "You are **Squad**" -Label "squad.md" - - $leadFile = Resolve-HostAgentFile -RoleId "lead" - Write-Log "Lead host file: $leadFile" - Assert-FileContains -Path $leadFile -Needle "model: $LeadModel" -Label (Split-Path $leadFile -Leaf) - - $testerFile = Resolve-HostAgentFile -RoleId "tester" - Write-Log "Tester host file: $testerFile" - Assert-FileContains -Path $testerFile -Needle "model: $SquadModel" -Label (Split-Path $testerFile -Leaf) - - $opencodeExe = Get-OpencodeExe - Write-Log "opencode=$opencodeExe" - - if (Assert-Port4096Safe) { - Write-Log "attaching to existing localhost:4096" - } else { - Write-Log "starting opencode serve --hostname 127.0.0.1 --port 4096" - $serveProc = Start-Process -FilePath $opencodeExe -ArgumentList @("serve", "--hostname", "127.0.0.1", "--port", "4096") -WorkingDirectory $Dummy -PassThru -WindowStyle Hidden - $startedServe = $true - Write-Log "started serve pid=$($serveProc.Id) (leaving it running)" - } - Wait-ServeReady - - $health = Invoke-ServeGet "$BaseURL/global/health" - Write-Log ("GET /global/health status={0}" -f $health.status) - if ($health.status -lt 200 -or $health.status -ge 500) { - throw "GET /global/health failed: $($health.error)" - } - - $dirBack = [uri]::EscapeDataString($Dummy) - $dirFwd = [uri]::EscapeDataString(($Dummy -replace '\\', '/')) - $configUrls = @( - "$BaseURL/config?directory=$dirBack", - "$BaseURL/config?directory=$dirFwd", - "$BaseURL/config", - "$BaseURL/project", - "$BaseURL/agent?directory=$dirBack", - "$BaseURL/agent?directory=$dirFwd", - "$BaseURL/agent" - ) - - $probes = @() - $chosen = $null - foreach ($url in $configUrls) { - $got = Invoke-ServeGet $url - $probes += $got - $preview = "" - if ($got.body) { - $preview = $got.body.Substring(0, [Math]::Min(80, $got.body.Length)) - } - Write-Log ("GET {0} status={1} preview={2}" -f $url, $got.status, $preview) - if ($null -eq $chosen -and $got.status -ge 200 -and $got.status -lt 300 -and $got.body) { - $chosen = $got - } - } - - $record = [ordered]@{ - dummy = $Dummy - startedServe = $startedServe - health = $health - chosenUrl = if ($chosen) { $chosen.url } else { $null } - probes = $probes - } - $json = $record | ConvertTo-Json -Depth 12 - foreach ($dir in @($MainDumpDir, $WorktreeDumpDir)) { - if (-not (Test-Path $dir)) { - New-Item -ItemType Directory -Path $dir | Out-Null - } - } - Set-Content -Path $MainDump -Value $json -Encoding utf8 - Set-Content -Path $WorktreeDump -Value $json -Encoding utf8 - Write-Log "wrote $MainDump" - Write-Log "wrote $WorktreeDump" - - $haystack = $json - if ($haystack -notlike "*$SquadModel*") { - throw "serve JSON missing '$SquadModel' (see $MainDump)" - } - if ($haystack -notlike "*$LeadModel*") { - throw "serve JSON missing '$LeadModel' (see $MainDump)" - } - Write-Log "PASS serve API: JSON contains $SquadModel and $LeadModel" - - Write-Log "TUI/Playwright skipped (serve JSON is the required artifact)" - } finally { - Set-Location $prev - } - - if ($startedServe) { - Write-Log "leaving serve running (pid=$($serveProc.Id))" - } else { - Write-Log "left existing serve running" - } - Write-Log "live-models ok" - exit 0 -} catch { - Write-Log ("FAIL: " + $_.Exception.Message) - throw -}