diff --git a/packages/cli/src/services/daemon.ts b/packages/cli/src/services/daemon.ts index 98c1540388..9cf18222ea 100644 --- a/packages/cli/src/services/daemon.ts +++ b/packages/cli/src/services/daemon.ts @@ -36,10 +36,11 @@ const ServiceConfig = Schema.Struct({ hostname: Schema.optional(Schema.String), port: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(65_535))), password: Schema.optional(Schema.String), + autostart: Schema.optional(Schema.Boolean), }) export type ServiceConfig = typeof ServiceConfig.Type -const serviceConfigKeys = ["hostname", "port", "password"] as const +const serviceConfigKeys = ["hostname", "port", "password", "autostart"] as const type ServiceConfigKey = (typeof serviceConfigKeys)[number] function serviceConfigKey(key: string): ServiceConfigKey { @@ -102,6 +103,10 @@ export const layer = Layer.effect( case "password": { return yield* password() } + case "autostart": { + const autostart = (yield* config()).autostart + return autostart === undefined ? "" : String(autostart) + } } }) @@ -124,6 +129,11 @@ export const layer = Layer.effect( yield* password(value) return } + case "autostart": { + if (value !== "true" && value !== "false") throw new Error("Autostart must be true or false") + yield* writeConfig({ ...(yield* config()), autostart: value === "true" }) + return + } } }) @@ -147,6 +157,11 @@ export const layer = Layer.effect( yield* writeConfig(next) return } + case "autostart": { + const { autostart: _autostart, ...next } = yield* config() + yield* writeConfig(next) + return + } } }) @@ -166,6 +181,16 @@ export const layer = Layer.effect( return yield* Effect.fail(new Error("Registered server is not healthy")) }) + const remoteTransport = Effect.fn("cli.daemon.remoteTransport")(function* (input: ServiceConfig) { + const url = serviceURL(input) + const headers = ServerAuth.headers({ password: input.password }) + const response = yield* Effect.tryPromise(() => + createOpencodeClient({ baseUrl: url, headers }).v2.health.get({ signal: AbortSignal.timeout(2_000) }), + ) + if (response.data?.healthy === true) return { url, headers } + return yield* Effect.fail(new Error(`Server is not healthy: ${url}`)) + }) + const compatible = Effect.fnUntraced(function* () { const info = yield* healthy() if (info.version === InstallationVersion) return info @@ -230,6 +255,8 @@ export const layer = Layer.effect( }) const transport = Effect.fn("cli.daemon.transport")(function* () { + const current = yield* config() + if (current.autostart === false) return yield* remoteTransport(current) return { url: yield* start(), headers: ServerAuth.headers({ password: yield* password() }) } }) @@ -286,4 +313,11 @@ export const layer = Layer.effect( export const defaultLayer = layer +function serviceURL(config: ServiceConfig) { + const hostname = config.hostname ?? "127.0.0.1" + const result = new URL(`http://${hostname.includes(":") && !hostname.startsWith("[") ? `[${hostname}]` : hostname}`) + result.port = String(config.port ?? 4096) + return result.toString() +} + export * as Daemon from "./daemon"