From c22793d5f6aac55252fe251c21da65f412235de6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 3 Jul 2026 13:34:11 -0400 Subject: [PATCH] feat(cli): validate explicit server before starting the tui --- packages/cli/src/commands/handlers/default.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/handlers/default.ts b/packages/cli/src/commands/handlers/default.ts index ca4c6469b4..addafa634f 100644 --- a/packages/cli/src/commands/handlers/default.ts +++ b/packages/cli/src/commands/handlers/default.ts @@ -19,10 +19,26 @@ export default Runtime.handler(Commands, (input) => const transport = yield* Effect.gen(function* () { if (server !== undefined) { const password = process.env["OPENCODE_PASSWORD"] - return { + const explicit = { url: server, headers: password ? { authorization: "Basic " + btoa("opencode:" + password) } : undefined, } satisfies Service.Transport + // Fail loudly before entering the TUI: an explicit server that is + // unreachable or rejects auth should not present as reconnect churn. + const response = yield* Effect.tryPromise(() => + fetch(new URL("/api/health", server), { headers: explicit.headers, signal: AbortSignal.timeout(5_000) }), + ).pipe(Effect.mapError((cause) => new Error(`Could not reach server at ${server}`, { cause }))) + if (response.status === 401) + return yield* Effect.fail( + new Error( + password + ? `Server at ${server} rejected the password` + : `Server at ${server} requires a password; set OPENCODE_PASSWORD`, + ), + ) + if (!response.ok) + return yield* Effect.fail(new Error(`Server at ${server} responded with status ${response.status}`)) + return explicit } if (input.standalone) return yield* Standalone.transport() const options = yield* ServiceConfig.options()