diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index d25df16d1d..6521ecb064 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -59,6 +59,21 @@ const Handlers = Runtime.handlers(Commands, { Effect.gen(function* () { yield* Heap.listen + const runFork = Effect.runForkWith(yield* Effect.context()) + const uncaughtException = (cause: Error, origin: "uncaughtException" | "unhandledRejection") => { + runFork(Effect.logError("uncaught exception", { cause, origin })) + } + const unhandledRejection = (cause: unknown) => { + runFork(Effect.logError("unhandled rejection", { cause })) + } + process.on("uncaughtException", uncaughtException) + process.on("unhandledRejection", unhandledRejection) + yield* Effect.addFinalizer(() => + Effect.sync(() => { + process.off("uncaughtException", uncaughtException) + process.off("unhandledRejection", unhandledRejection) + }), + ) yield* Effect.logInfo("cli starting", { version: OPENCODE_VERSION, channel: OPENCODE_CHANNEL, @@ -67,6 +82,12 @@ Effect.gen(function* () { }) return yield* Runtime.run(Commands, Handlers, { version: OPENCODE_VERSION }) }).pipe( + Effect.catchCause((cause) => + Effect.logError("cli process failed", { + cause, + args: process.argv.slice(2), + }).pipe(Effect.andThen(Effect.failCause(cause))), + ), Effect.annotateLogs({ role: "cli" }), Effect.provide(Config.layer), Effect.provide(Updater.layer), diff --git a/packages/cli/src/server-process.ts b/packages/cli/src/server-process.ts index ba3a38d5ad..aeedf4a3bf 100644 --- a/packages/cli/src/server-process.ts +++ b/packages/cli/src/server-process.ts @@ -180,27 +180,38 @@ const register = Effect.fnUntraced(function* ( password, } const encoded = yield* encodeInfo(info) - const current = fs.readFileString(file).pipe( - Effect.flatMap(decodeInfo), - Effect.orElseSucceed(() => undefined), - ) - const owns = (found: Info | undefined) => - found?.id === info.id && + const current = fs.readFileString(file).pipe(Effect.flatMap(decodeInfo)) + const owns = (found: Info) => + found.id === info.id && found.version === info.version && found.url === info.url && found.pid === info.pid && found.password === info.password yield* fs.writeFileString(temp, encoded, { mode: 0o600 }).pipe(Effect.andThen(fs.rename(temp, file))) yield* current.pipe( - Effect.filterOrFail(owns), - Effect.repeat(Schedule.spaced("5 seconds")), - Effect.tapError(() => - Effect.logWarning("managed service registration lost; shutting down", { + Effect.catchCause((cause) => + Effect.logWarning("managed service registration check failed; shutting down", { + cause, serviceID: id, servicePID: process.pid, registration: file, - }), + }).pipe(Effect.andThen(Effect.failCause(cause))), ), + Effect.tap((found) => + owns(found) + ? Effect.void + : Effect.logWarning("managed service registration replaced; shutting down", { + serviceID: id, + servicePID: process.pid, + registration: file, + observedServiceID: found.id, + observedServicePID: found.pid, + observedVersion: found.version, + observedURL: found.url, + }), + ), + Effect.filterOrFail(owns), + Effect.repeat(Schedule.spaced("5 seconds")), Effect.ignore, Effect.andThen(shutdown), Effect.forkScoped,