diff --git a/apps/server/src/wsServer.ts b/apps/server/src/wsServer.ts index daecc6f6c..b068f96c8 100644 --- a/apps/server/src/wsServer.ts +++ b/apps/server/src/wsServer.ts @@ -189,37 +189,36 @@ function testOpenclawGateway( error: "Gateway URL is empty.", }; } - try { - const parsed = new URL(gatewayUrl); - if (!["ws:", "wss:"].includes(parsed.protocol)) { - pushStep( - "URL validation", - "fail", - Date.now() - urlStart, - `Invalid protocol "${parsed.protocol}". Expected ws: or wss:.`, - ); - return { - success: false, - steps, - totalDurationMs: Date.now() - overallStart, - error: `Invalid protocol "${parsed.protocol}".`, - }; - } + const parsedUrl = URL.canParse(gatewayUrl) ? new URL(gatewayUrl) : null; + if (!parsedUrl) { + pushStep("URL validation", "fail", Date.now() - urlStart, "Malformed URL."); + return { + success: false, + steps, + totalDurationMs: Date.now() - overallStart, + error: "Malformed URL.", + }; + } + if (!["ws:", "wss:"].includes(parsedUrl.protocol)) { pushStep( "URL validation", - "pass", + "fail", Date.now() - urlStart, - `${parsed.protocol}//${parsed.host}`, + `Invalid protocol "${parsedUrl.protocol}". Expected ws: or wss:.`, ); - } catch { - pushStep("URL validation", "fail", Date.now() - urlStart, "Malformed URL."); return { success: false, steps, totalDurationMs: Date.now() - overallStart, - error: "Malformed URL.", + error: `Invalid protocol "${parsedUrl.protocol}".`, }; } + pushStep( + "URL validation", + "pass", + Date.now() - urlStart, + `${parsedUrl.protocol}//${parsedUrl.host}`, + ); // ── Step 2: WebSocket connect ─────────────────────────────────── const connectStart = Date.now(); @@ -261,31 +260,25 @@ function testOpenclawGateway( error: detail, }; } + ws = wsResult.right; + pushStep( + "WebSocket connect", + "pass", + Date.now() - connectStart, + `Connected in ${Date.now() - connectStart}ms`, + ); // ── Step 3: Authentication ────────────────────────────────────── if (input.password) { const authStart = Date.now(); - try { - const response = yield* Effect.tryPromise(() => - sendRpc(ws!, "auth.authenticate", { password: input.password }), - ); - if (response.error) { - pushStep( - "Authentication", - "fail", - Date.now() - authStart, - `RPC error ${response.error.code}: ${response.error.message}`, - ); - return { - success: false, - steps, - totalDurationMs: Date.now() - overallStart, - error: `Authentication failed: ${response.error.message}`, - }; - } - pushStep("Authentication", "pass", Date.now() - authStart, "Authenticated successfully."); - } catch (err) { - const detail = err instanceof Error ? err.message : "Authentication request failed."; + const authResult = yield* Effect.either( + Effect.tryPromise(() => sendRpc(ws!, "auth.authenticate", { password: input.password })), + ); + if (authResult._tag === "Left") { + const detail = + authResult.left instanceof Error + ? authResult.left.message + : "Authentication request failed."; pushStep("Authentication", "fail", Date.now() - authStart, detail); return { success: false, @@ -294,28 +287,18 @@ function testOpenclawGateway( error: detail, }; } - } else { - pushStep("Authentication", "skip", 0, "No password configured."); - } - - // ── Step 4: Session create (probe) ────────────────────────────── - const sessionStart = Date.now(); - try { - const response = yield* Effect.tryPromise(() => - sendRpc(ws!, "session.create", { runtimeMode: "headless" }), - ); - if (response.error) { + if (authResult.right.error) { pushStep( - "Session create", + "Authentication", "fail", - Date.now() - sessionStart, - `RPC error ${response.error.code}: ${response.error.message}`, + Date.now() - authStart, + `RPC error ${authResult.right.error.code}: ${authResult.right.error.message}`, ); return { success: false, steps, totalDurationMs: Date.now() - overallStart, - error: `Session creation failed: ${response.error.message}`, + error: `Authentication failed: ${authResult.right.error.message}`, }; } const result = (response.result ?? {}) as Record; @@ -327,20 +310,33 @@ function testOpenclawGateway( }; pushStep( "Session create", - "pass", + "fail", Date.now() - sessionStart, - sessionId ? `Session ID: ${sessionId}` : "Session created.", + `RPC error ${sessionResult.right.error.code}: ${sessionResult.right.error.message}`, ); - } catch (err) { - const detail = err instanceof Error ? err.message : "Session creation failed."; - pushStep("Session create", "fail", Date.now() - sessionStart, detail); return { success: false, steps, totalDurationMs: Date.now() - overallStart, - error: detail, + error: `Session creation failed: ${sessionResult.right.error.message}`, }; } + const result = (sessionResult.right.result ?? {}) as Record; + const sessionId = typeof result.sessionId === "string" ? result.sessionId : undefined; + const version = typeof result.version === "string" ? result.version : undefined; + serverInfo = {}; + if (version !== undefined) { + serverInfo.version = version; + } + if (sessionId !== undefined) { + serverInfo.sessionId = sessionId; + } + pushStep( + "Session create", + "pass", + Date.now() - sessionStart, + sessionId ? `Session ID: ${sessionId}` : "Session created.", + ); return { success: true, diff --git a/docs/releases/v0.19.0.md b/docs/releases/v0.19.0.md index 08e753e79..695e3268d 100644 --- a/docs/releases/v0.19.0.md +++ b/docs/releases/v0.19.0.md @@ -6,6 +6,7 @@ - Release-preflight fixes for server snapshot decoding and exact optional property checks. ## Changes + - Improved release workflow resilience around staged release documentation and optional CLI publishing. - Fixed snapshot query handling for thread `githubRef` data. - Fixed `wsServer.ts` type issues blocking strict server typecheck. @@ -15,4 +16,5 @@ See the attached assets on this GitHub Release. ## Notes +- See the attached assets on this GitHub Release. - This release includes release-process fixes landed during the rollout itself. diff --git a/docs/releases/v0.19.0/assets.md b/docs/releases/v0.19.0/assets.md index cbf8539d5..eb62a8a5d 100644 --- a/docs/releases/v0.19.0/assets.md +++ b/docs/releases/v0.19.0/assets.md @@ -1,6 +1,7 @@ # OK Code v0.19.0 Assets This release contains: + - macOS (arm64): `.dmg` and updater manifests where applicable - Linux (x64): `.AppImage` - Windows (x64): `.exe`