| author | |
| committer | |
| log | cebbe6ff193c9598bf77c4f673a933db34ed7bcb |
| tree | a892f6b3c787bd17d3a47c3f32ed177043bc5a37 |
| parent | e4a84a348e59d59e03bca15baa627aed99aec790 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: codex <codex@users.noreply.github.com>6 files changed, 113 insertions(+), 4 deletions(-)
packages/effect-acp/src/_internal/stdio.test.ts created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | import { assert, describe, it } from "@effect/vitest"; | |
| 2 | import * as Effect from "effect/Effect"; | |
| 3 | import * as PlatformError from "effect/PlatformError"; | |
| 4 | import { ChildProcessSpawner } from "effect/unstable/process"; | |
| 5 | ||
| 6 | import * as AcpError from "../errors.ts"; | |
| 7 | import { makeTerminationError } from "./stdio.ts"; | |
| 8 | ||
| 9 | describe("ACP child process termination", () => { | |
| 10 | it.effect("retains the process identifier with the exit code", () => | |
| 11 | Effect.gen(function* () { | |
| 12 | const error = yield* makeTerminationError({ | |
| 13 | pid: ChildProcessSpawner.ProcessId(41), | |
| 14 | exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(7)), | |
| 15 | }); | |
| 16 | ||
| 17 | assert.instanceOf(error, AcpError.AcpProcessExitedError); | |
| 18 | assert.equal(error.pid, 41); | |
| 19 | assert.equal(error.code, 7); | |
| 20 | assert.equal(error.message, "ACP process exited with code 7"); | |
| 21 | }), | |
| 22 | ); | |
| 23 | ||
| 24 | it.effect("retains the process identifier and exact exit-status cause", () => | |
| 25 | Effect.gen(function* () { | |
| 26 | const rootCause = new Error("private process diagnostics"); | |
| 27 | const cause = PlatformError.systemError({ | |
| 28 | _tag: "Unknown", | |
| 29 | module: "ChildProcess", | |
| 30 | method: "exitCode", | |
| 31 | cause: rootCause, | |
| 32 | }); | |
| 33 | const error = yield* makeTerminationError({ | |
| 34 | pid: ChildProcessSpawner.ProcessId(42), | |
| 35 | exitCode: Effect.fail(cause), | |
| 36 | }); | |
| 37 | ||
| 38 | assert.instanceOf(error, AcpError.AcpTransportError); | |
| 39 | assert.equal(error.pid, 42); | |
| 40 | assert.strictEqual(error.cause, cause); | |
| 41 | assert.equal(error.message, "ACP transport operation read-process-exit-status failed."); | |
| 42 | assert.notInclude(error.message, rootCause.message); | |
| 43 | }), | |
| 44 | ); | |
| 45 | }); |
packages/effect-acp/src/_internal/stdio.ts+8-2| ... | ... | @@ -44,14 +44,20 @@ export const makeInMemoryStdio = Effect.fn("makeInMemoryStdio")(function* () { |
| 44 | 44 | }; |
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | type ChildProcessTerminationHandle = Pick< | |
| 48 | ChildProcessSpawner.ChildProcessHandle, | |
| 49 | "exitCode" | "pid" | |
| 50 | >; | |
| 51 | ||
| 47 | 52 | export const makeTerminationError = ( |
| 48 | handle: ChildProcessSpawner.ChildProcessHandle, | |
| 53 | handle: ChildProcessTerminationHandle, | |
| 49 | 54 | ): Effect.Effect<AcpError.AcpError> => |
| 50 | 55 | Effect.match(handle.exitCode, { |
| 51 | 56 | onFailure: (cause) => |
| 52 | 57 | new AcpError.AcpTransportError({ |
| 53 | 58 | operation: "read-process-exit-status", |
| 59 | pid: handle.pid, | |
| 54 | 60 | cause, |
| 55 | 61 | }), |
| 56 | onSuccess: (code) => new AcpError.AcpProcessExitedError({ code }), | |
| 62 | onSuccess: (code) => new AcpError.AcpProcessExitedError({ code, pid: handle.pid }), | |
| 57 | 63 | }); |
packages/effect-acp/src/errors.ts+2| ... | ... | @@ -91,6 +91,7 @@ export class AcpProcessExitedError extends Schema.TaggedErrorClass<AcpProcessExi |
| 91 | 91 | "AcpProcessExitedError", |
| 92 | 92 | { |
| 93 | 93 | code: Schema.optional(Schema.Number), |
| 94 | pid: Schema.optionalKey(Schema.Int), | |
| 94 | 95 | cause: Schema.optional(Schema.Defect()), |
| 95 | 96 | }, |
| 96 | 97 | ) { |
| ... | ... | @@ -160,6 +161,7 @@ export class AcpTransportError extends Schema.TaggedErrorClass<AcpTransportError |
| 160 | 161 | ), |
| 161 | 162 | method: Schema.optional(Schema.String), |
| 162 | 163 | detail: Schema.optional(Schema.String), |
| 164 | pid: Schema.optionalKey(Schema.Int), | |
| 163 | 165 | cause: Schema.Defect(), |
| 164 | 166 | }, |
| 165 | 167 | ) { |
packages/effect-codex-app-server/src/_internal/stdio.test.ts created+48| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | import { assert, describe, it } from "@effect/vitest"; | |
| 2 | import * as Effect from "effect/Effect"; | |
| 3 | import * as PlatformError from "effect/PlatformError"; | |
| 4 | import { ChildProcessSpawner } from "effect/unstable/process"; | |
| 5 | ||
| 6 | import * as CodexError from "../errors.ts"; | |
| 7 | import { makeTerminationError } from "./stdio.ts"; | |
| 8 | ||
| 9 | describe("Codex App Server child process termination", () => { | |
| 10 | it.effect("retains the process identifier with the exit code", () => | |
| 11 | Effect.gen(function* () { | |
| 12 | const error = yield* makeTerminationError({ | |
| 13 | pid: ChildProcessSpawner.ProcessId(51), | |
| 14 | exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(9)), | |
| 15 | }); | |
| 16 | ||
| 17 | assert.instanceOf(error, CodexError.CodexAppServerProcessExitedError); | |
| 18 | assert.equal(error.pid, 51); | |
| 19 | assert.equal(error.code, 9); | |
| 20 | assert.equal(error.message, "Codex App Server process exited with code 9"); | |
| 21 | }), | |
| 22 | ); | |
| 23 | ||
| 24 | it.effect("retains the process identifier and exact exit-status cause", () => | |
| 25 | Effect.gen(function* () { | |
| 26 | const rootCause = new Error("private process diagnostics"); | |
| 27 | const cause = PlatformError.systemError({ | |
| 28 | _tag: "Unknown", | |
| 29 | module: "ChildProcess", | |
| 30 | method: "exitCode", | |
| 31 | cause: rootCause, | |
| 32 | }); | |
| 33 | const error = yield* makeTerminationError({ | |
| 34 | pid: ChildProcessSpawner.ProcessId(52), | |
| 35 | exitCode: Effect.fail(cause), | |
| 36 | }); | |
| 37 | ||
| 38 | assert.instanceOf(error, CodexError.CodexAppServerTransportError); | |
| 39 | assert.equal(error.pid, 52); | |
| 40 | assert.strictEqual(error.cause, cause); | |
| 41 | assert.equal( | |
| 42 | error.message, | |
| 43 | "Codex App Server transport operation 'read-process-exit-status' failed.", | |
| 44 | ); | |
| 45 | assert.notInclude(error.message, rootCause.message); | |
| 46 | }), | |
| 47 | ); | |
| 48 | }); |
packages/effect-codex-app-server/src/_internal/stdio.ts+8-2| ... | ... | @@ -44,14 +44,20 @@ export const makeInMemoryStdio = Effect.fn("makeInMemoryStdio")(function* () { |
| 44 | 44 | }; |
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | type ChildProcessTerminationHandle = Pick< | |
| 48 | ChildProcessSpawner.ChildProcessHandle, | |
| 49 | "exitCode" | "pid" | |
| 50 | >; | |
| 51 | ||
| 47 | 52 | export const makeTerminationError = ( |
| 48 | handle: ChildProcessSpawner.ChildProcessHandle, | |
| 53 | handle: ChildProcessTerminationHandle, | |
| 49 | 54 | ): Effect.Effect<CodexError.CodexAppServerError> => |
| 50 | 55 | Effect.match(handle.exitCode, { |
| 51 | 56 | onFailure: (cause) => |
| 52 | 57 | new CodexError.CodexAppServerTransportError({ |
| 53 | 58 | operation: "read-process-exit-status", |
| 59 | pid: handle.pid, | |
| 54 | 60 | cause, |
| 55 | 61 | }), |
| 56 | onSuccess: (code) => new CodexError.CodexAppServerProcessExitedError({ code }), | |
| 62 | onSuccess: (code) => new CodexError.CodexAppServerProcessExitedError({ code, pid: handle.pid }), | |
| 57 | 63 | }); |
packages/effect-codex-app-server/src/errors.ts+2| ... | ... | @@ -146,6 +146,7 @@ export class CodexAppServerProcessExitedError extends Schema.TaggedErrorClass<Co |
| 146 | 146 | "CodexAppServerProcessExitedError", |
| 147 | 147 | { |
| 148 | 148 | code: Schema.optional(Schema.Number), |
| 149 | pid: Schema.optionalKey(Schema.Int), | |
| 149 | 150 | cause: Schema.optional(Schema.Defect()), |
| 150 | 151 | }, |
| 151 | 152 | ) { |
| ... | ... | @@ -233,6 +234,7 @@ export class CodexAppServerTransportError extends Schema.TaggedErrorClass<CodexA |
| 233 | 234 | "CodexAppServerTransportError", |
| 234 | 235 | { |
| 235 | 236 | operation: CodexAppServerTransportOperation, |
| 237 | pid: Schema.optionalKey(Schema.Int), | |
| 236 | 238 | cause: Schema.Defect(), |
| 237 | 239 | }, |
| 238 | 240 | ) { |