import { afterEach, describe, it, assert } from "@effect/vitest";import { ProviderDriverKind, ProviderInstanceId, type ServerProvider, type ServerProviderUpdateState,} from "@t3tools/contracts";import { ServerProviderUpdateError } from "@t3tools/contracts";import { Cause, Effect, Exit, Fiber, Layer, Ref, Schema, Sink, Stream } from "effect";import { HttpClient, HttpClientResponse } from "effect/unstable/http";import { ChildProcessSpawner } from "effect/unstable/process"; import { ProviderRegistry, type ProviderRegistryShape } from "./Services/ProviderRegistry.ts";import * as ProviderMaintenanceRunner from "./providerMaintenanceRunner.ts";import { clearLatestProviderVersionCacheForTests, makeProviderMaintenanceCapabilities, type ProviderMaintenanceCapabilities,} from "./providerMaintenance.ts"; const CODEX_DRIVER = ProviderDriverKind.make("codex");const CURSOR_DRIVER = ProviderDriverKind.make("cursor");const OPENCODE_DRIVER = ProviderDriverKind.make("opencode");const CODEX_INSTANCE_ID = ProviderInstanceId.make("codex");const CURSOR_INSTANCE_ID = ProviderInstanceId.make("cursor");const OPENCODE_INSTANCE_ID = ProviderInstanceId.make("opencode");const encoder = new TextEncoder(); afterEach(() => { clearLatestProviderVersionCacheForTests();}); function lifecycleFor(provider: ProviderDriverKind): ProviderMaintenanceCapabilities { if (provider === CURSOR_DRIVER) { return makeProviderMaintenanceCapabilities({ provider, packageName: null, updateExecutable: "agent", updateArgs: ["update"], updateLockKey: "cursor-agent", }); } return makeProviderMaintenanceCapabilities({ provider, packageName: provider === OPENCODE_DRIVER ? "opencode-ai" : "@openai/codex", updateExecutable: "npm", updateArgs: provider === OPENCODE_DRIVER ? ["install", "-g", "opencode-ai@latest"] : ["install", "-g", "@openai/codex@latest"], updateLockKey: "npm-global", });} const baseProvider: ServerProvider = { instanceId: CODEX_INSTANCE_ID, driver: CODEX_DRIVER, enabled: true, installed: true, version: null, status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-04-10T00:00:00.000Z", models: [], slashCommands: [], skills: [],}; const baseCursorProvider: ServerProvider = { ...baseProvider, instanceId: CURSOR_INSTANCE_ID, driver: CURSOR_DRIVER,}; const baseOpenCodeProvider: ServerProvider = { ...baseProvider, instanceId: OPENCODE_INSTANCE_ID, driver: OPENCODE_DRIVER,}; const latestVersionHttpClient = (version: string) => Layer.succeed( HttpClient.HttpClient, HttpClient.make((request) => Effect.succeed( HttpClientResponse.fromWeb( request, Response.json({ version }, { headers: { "content-type": "application/json" } }), ), ), ), ); function mockHandle(result: { readonly stdout?: string; readonly stderr?: string; readonly code?: number; readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>;}) { return ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(1), exitCode: result.exitCode ?? Effect.succeed(ChildProcessSpawner.ExitCode(result.code ?? 0)), isRunning: Effect.succeed(false), kill: () => Effect.void, unref: Effect.succeed(Effect.void), stdin: Sink.drain, stdout: Stream.make(encoder.encode(result.stdout ?? "")), stderr: Stream.make(encoder.encode(result.stderr ?? "")), all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, });} function mockSpawnerLayer( handler: ( command: string, args: ReadonlyArray<string>, ) => { readonly stdout?: string; readonly stderr?: string; readonly code?: number; readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>; },) { return Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => { const childProcess = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; }; return Effect.succeed(mockHandle(handler(childProcess.command, childProcess.args))); }), );} function makeRegistry( initialProviders: ServerProvider | ReadonlyArray<ServerProvider> = baseProvider,) { return Effect.gen(function* () { const providersRef = yield* Ref.make<ReadonlyArray<ServerProvider>>( Array.isArray(initialProviders) ? initialProviders : [initialProviders], ); const updateStatesRef = yield* Ref.make<ReadonlyArray<ServerProviderUpdateState>>([]); const setProviderMaintenanceActionState = Effect.fn( "providerMaintenanceRunner.test.setProviderMaintenanceActionState", )(function* (input: { readonly instanceId: ProviderInstanceId; readonly action: "update"; readonly state: ServerProviderUpdateState | null; }) { const updateState = input.state; if (updateState) { yield* Ref.update(updateStatesRef, (states) => [...states, updateState]); } return yield* Ref.updateAndGet(providersRef, (providers) => providers.map((candidate) => { if (candidate.instanceId !== input.instanceId) { return candidate; } if (!updateState) { const { updateState: _updateState, ...providerWithoutUpdateState } = candidate; return providerWithoutUpdateState; } return { ...candidate, updateState, }; }), ); }); const registry: ProviderRegistryShape = { getProviders: Ref.get(providersRef), refresh: () => Ref.get(providersRef), refreshInstance: () => Ref.get(providersRef), getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) => Effect.succeed(lifecycleFor(provider)), setProviderMaintenanceActionState, streamChanges: Stream.empty, }; return { registry, updateStatesRef, }; });} const makeTestRunner = (registry: ProviderRegistryShape) => Effect.service(ProviderMaintenanceRunner.ProviderMaintenanceRunner).pipe( Effect.provide( ProviderMaintenanceRunner.layer.pipe( Layer.provide(Layer.succeed(ProviderRegistry, registry)), ), ), ); describe("providerMaintenanceRunner", () => { it.effect("runs the allowlisted provider update command and records success", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry, updateStatesRef } = yield* makeRegistry(baseCursorProvider); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CURSOR_DRIVER); assert.deepStrictEqual(calls, [ { command: "agent", args: ["update"], }, ]); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.deepStrictEqual( (yield* Ref.get(updateStatesRef)).map((state) => state.status), ["queued", "running", "succeeded"], ); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect("uses the resolved provider capabilities when choosing the update executable", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry({ ...baseProvider, versionAdvisory: { status: "behind_latest", currentVersion: "2.0.14", latestVersion: "2.1.123", updateCommand: "bun i -g @anthropic-ai/claude-code@latest", canUpdate: true, checkedAt: "2026-04-30T12:00:00.000Z", message: "Update available.", }, }); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: () => Effect.succeed( makeProviderMaintenanceCapabilities({ provider: CODEX_DRIVER, packageName: "@openai/codex", updateExecutable: "bun", updateArgs: ["i", "-g", "@openai/codex@latest"], updateLockKey: "bun-global", }), ), }); yield* updater.updateProvider(CODEX_DRIVER); assert.deepStrictEqual(calls, [ { command: "bun", args: ["i", "-g", "@openai/codex@latest"], }, ]); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect( "runs update commands through Effect ChildProcess when no test runner is injected", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry(baseProvider); const runner = yield* makeTestRunner(registry); const result = yield* runner.updateProvider(CODEX_DRIVER); assert.deepStrictEqual(calls, [ { command: "npm", args: ["install", "-g", "@openai/codex@latest"], }, ]); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }, ); it.effect("updates a single provider instance without touching sibling instances", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const personalInstanceId = ProviderInstanceId.make("codex_personal"); const workInstanceId = ProviderInstanceId.make("codex_work"); const refreshedInstanceIds: Array<ProviderInstanceId> = []; const { registry } = yield* makeRegistry([ { ...baseProvider, instanceId: personalInstanceId, version: "0.124.0-alpha.3", }, { ...baseProvider, instanceId: workInstanceId, version: "0.124.0-alpha.3", }, ]); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: (instanceId, provider) => Effect.succeed( makeProviderMaintenanceCapabilities({ provider, packageName: "@openai/codex-instance-test", updateExecutable: "vp", updateArgs: ["i", "-g", "@openai/codex"], updateLockKey: "vite-plus-global", }), ).pipe( Effect.tap(() => Effect.sync(() => assert.strictEqual(instanceId, personalInstanceId))), ), refreshInstance: (instanceId) => registry.refreshInstance(instanceId).pipe( Effect.tap(() => Effect.sync(() => { refreshedInstanceIds.push(instanceId); }), ), ), }); const result = yield* updater.updateProvider({ provider: CODEX_DRIVER, instanceId: personalInstanceId, }); assert.deepStrictEqual(calls, [ { command: "vp", args: ["i", "-g", "@openai/codex"], }, ]); assert.deepStrictEqual(refreshedInstanceIds, [personalInstanceId]); assert.strictEqual(result.providers[0]?.instanceId, personalInstanceId); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.strictEqual(result.providers[1]?.instanceId, workInstanceId); assert.strictEqual(result.providers[1]?.updateState, undefined); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.124.0-alpha.3"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect("records command failure output in provider update state", () => Effect.gen(function* () { const { registry } = yield* makeRegistry(); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CODEX_DRIVER); const updateState = result.providers[0]?.updateState; assert.strictEqual(updateState?.status, "failed"); assert.strictEqual(updateState?.message, "Update command exited with code 1."); assert.include(updateState?.output ?? "", "permission denied"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer(() => ({ stderr: "permission denied", code: 1 })), ), ), ), ); it.effect( "marks successful commands as unchanged when the refreshed provider is still outdated", () => Effect.gen(function* () { const { registry } = yield* makeRegistry({ ...baseProvider, installed: true, version: "0.1.0", }); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CODEX_DRIVER); assert.strictEqual(result.providers[0]?.updateState?.status, "unchanged"); assert.include(result.providers[0]?.updateState?.message ?? "", "still detects"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("9.9.9"), mockSpawnerLayer(() => ({ stdout: "updated" })), ), ), ), ); it.effect("prevents concurrent updates for the same provider", () => { const startedLatch: { resolve: () => void } = { resolve: () => {} }; const releaseLatch: { resolve: () => void } = { resolve: () => {} }; const started = new Promise<void>((resolve) => { startedLatch.resolve = resolve; }); const release = new Promise<void>((resolve) => { releaseLatch.resolve = resolve; }); return Effect.gen(function* () { const { registry } = yield* makeRegistry(); const updater = yield* makeTestRunner(registry); const first = yield* updater.updateProvider(CODEX_DRIVER).pipe(Effect.forkScoped); yield* Effect.promise(() => started); const second = yield* updater.updateProvider(CODEX_DRIVER).pipe(Effect.exit); assert.strictEqual(Exit.isFailure(second), true); if (Exit.isFailure(second)) { const error = Cause.squash(second.cause); assert.strictEqual(Schema.is(ServerProviderUpdateError)(error), true); if (Schema.is(ServerProviderUpdateError)(error)) { assert.include(error.reason, "already running"); } } releaseLatch.resolve(); yield* Fiber.join(first); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer(() => { startedLatch.resolve(); return { stdout: "updated", exitCode: Effect.promise(() => release).pipe( Effect.as(ChildProcessSpawner.ExitCode(0)), ), }; }), ), ), ); }); it.effect("serializes different providers that share the same update lock key", () => { const firstStartedLatch: { resolve: () => void } = { resolve: () => {} }; const releaseFirstLatch: { resolve: () => void } = { resolve: () => {} }; const firstStarted = new Promise<void>((resolve) => { firstStartedLatch.resolve = resolve; }); const releaseFirst = new Promise<void>((resolve) => { releaseFirstLatch.resolve = resolve; }); const calls: Array<string> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry([baseProvider, baseOpenCodeProvider]); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) => Effect.succeed( makeProviderMaintenanceCapabilities({ provider, packageName: provider === OPENCODE_DRIVER ? "opencode-ai" : "@openai/codex", updateExecutable: "npm", updateArgs: provider === OPENCODE_DRIVER ? ["install", "-g", "opencode-ai@latest"] : ["install", "-g", "@openai/codex@latest"], updateLockKey: "npm-global",Lines 1–500 of 640. Load more
import { afterEach, describe, it, assert } from "@effect/vitest"; import { ProviderDriverKind, ProviderInstanceId, type ServerProvider, type ServerProviderUpdateState, } from "@t3tools/contracts"; import { ServerProviderUpdateError } from "@t3tools/contracts"; import { Cause, Effect, Exit, Fiber, Layer, Ref, Schema, Sink, Stream } from "effect"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; import { ProviderRegistry, type ProviderRegistryShape } from "./Services/ProviderRegistry.ts"; import * as ProviderMaintenanceRunner from "./providerMaintenanceRunner.ts"; import { clearLatestProviderVersionCacheForTests, makeProviderMaintenanceCapabilities, type ProviderMaintenanceCapabilities, } from "./providerMaintenance.ts"; const CODEX_DRIVER = ProviderDriverKind.make("codex"); const CURSOR_DRIVER = ProviderDriverKind.make("cursor"); const OPENCODE_DRIVER = ProviderDriverKind.make("opencode"); const CODEX_INSTANCE_ID = ProviderInstanceId.make("codex"); const CURSOR_INSTANCE_ID = ProviderInstanceId.make("cursor"); const OPENCODE_INSTANCE_ID = ProviderInstanceId.make("opencode"); const encoder = new TextEncoder(); afterEach(() => { clearLatestProviderVersionCacheForTests(); }); function lifecycleFor(provider: ProviderDriverKind): ProviderMaintenanceCapabilities { if (provider === CURSOR_DRIVER) { return makeProviderMaintenanceCapabilities({ provider, packageName: null, updateExecutable: "agent", updateArgs: ["update"], updateLockKey: "cursor-agent", }); } return makeProviderMaintenanceCapabilities({ provider, packageName: provider === OPENCODE_DRIVER ? "opencode-ai" : "@openai/codex", updateExecutable: "npm", updateArgs: provider === OPENCODE_DRIVER ? ["install", "-g", "opencode-ai@latest"] : ["install", "-g", "@openai/codex@latest"], updateLockKey: "npm-global", }); } const baseProvider: ServerProvider = { instanceId: CODEX_INSTANCE_ID, driver: CODEX_DRIVER, enabled: true, installed: true, version: null, status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-04-10T00:00:00.000Z", models: [], slashCommands: [], skills: [], }; const baseCursorProvider: ServerProvider = { ...baseProvider, instanceId: CURSOR_INSTANCE_ID, driver: CURSOR_DRIVER, }; const baseOpenCodeProvider: ServerProvider = { ...baseProvider, instanceId: OPENCODE_INSTANCE_ID, driver: OPENCODE_DRIVER, }; const latestVersionHttpClient = (version: string) => Layer.succeed( HttpClient.HttpClient, HttpClient.make((request) => Effect.succeed( HttpClientResponse.fromWeb( request, Response.json({ version }, { headers: { "content-type": "application/json" } }), ), ), ), ); function mockHandle(result: { readonly stdout?: string; readonly stderr?: string; readonly code?: number; readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>; }) { return ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(1), exitCode: result.exitCode ?? Effect.succeed(ChildProcessSpawner.ExitCode(result.code ?? 0)), isRunning: Effect.succeed(false), kill: () => Effect.void, unref: Effect.succeed(Effect.void), stdin: Sink.drain, stdout: Stream.make(encoder.encode(result.stdout ?? "")), stderr: Stream.make(encoder.encode(result.stderr ?? "")), all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }); } function mockSpawnerLayer( handler: ( command: string, args: ReadonlyArray<string>, ) => { readonly stdout?: string; readonly stderr?: string; readonly code?: number; readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>; }, ) { return Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => { const childProcess = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; }; return Effect.succeed(mockHandle(handler(childProcess.command, childProcess.args))); }), ); } function makeRegistry( initialProviders: ServerProvider | ReadonlyArray<ServerProvider> = baseProvider, ) { return Effect.gen(function* () { const providersRef = yield* Ref.make<ReadonlyArray<ServerProvider>>( Array.isArray(initialProviders) ? initialProviders : [initialProviders], ); const updateStatesRef = yield* Ref.make<ReadonlyArray<ServerProviderUpdateState>>([]); const setProviderMaintenanceActionState = Effect.fn( "providerMaintenanceRunner.test.setProviderMaintenanceActionState", )(function* (input: { readonly instanceId: ProviderInstanceId; readonly action: "update"; readonly state: ServerProviderUpdateState | null; }) { const updateState = input.state; if (updateState) { yield* Ref.update(updateStatesRef, (states) => [...states, updateState]); } return yield* Ref.updateAndGet(providersRef, (providers) => providers.map((candidate) => { if (candidate.instanceId !== input.instanceId) { return candidate; } if (!updateState) { const { updateState: _updateState, ...providerWithoutUpdateState } = candidate; return providerWithoutUpdateState; } return { ...candidate, updateState, }; }), ); }); const registry: ProviderRegistryShape = { getProviders: Ref.get(providersRef), refresh: () => Ref.get(providersRef), refreshInstance: () => Ref.get(providersRef), getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) => Effect.succeed(lifecycleFor(provider)), setProviderMaintenanceActionState, streamChanges: Stream.empty, }; return { registry, updateStatesRef, }; }); } const makeTestRunner = (registry: ProviderRegistryShape) => Effect.service(ProviderMaintenanceRunner.ProviderMaintenanceRunner).pipe( Effect.provide( ProviderMaintenanceRunner.layer.pipe( Layer.provide(Layer.succeed(ProviderRegistry, registry)), ), ), ); describe("providerMaintenanceRunner", () => { it.effect("runs the allowlisted provider update command and records success", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry, updateStatesRef } = yield* makeRegistry(baseCursorProvider); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CURSOR_DRIVER); assert.deepStrictEqual(calls, [ { command: "agent", args: ["update"], }, ]); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.deepStrictEqual( (yield* Ref.get(updateStatesRef)).map((state) => state.status), ["queued", "running", "succeeded"], ); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect("uses the resolved provider capabilities when choosing the update executable", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry({ ...baseProvider, versionAdvisory: { status: "behind_latest", currentVersion: "2.0.14", latestVersion: "2.1.123", updateCommand: "bun i -g @anthropic-ai/claude-code@latest", canUpdate: true, checkedAt: "2026-04-30T12:00:00.000Z", message: "Update available.", }, }); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: () => Effect.succeed( makeProviderMaintenanceCapabilities({ provider: CODEX_DRIVER, packageName: "@openai/codex", updateExecutable: "bun", updateArgs: ["i", "-g", "@openai/codex@latest"], updateLockKey: "bun-global", }), ), }); yield* updater.updateProvider(CODEX_DRIVER); assert.deepStrictEqual(calls, [ { command: "bun", args: ["i", "-g", "@openai/codex@latest"], }, ]); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect( "runs update commands through Effect ChildProcess when no test runner is injected", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry(baseProvider); const runner = yield* makeTestRunner(registry); const result = yield* runner.updateProvider(CODEX_DRIVER); assert.deepStrictEqual(calls, [ { command: "npm", args: ["install", "-g", "@openai/codex@latest"], }, ]); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }, ); it.effect("updates a single provider instance without touching sibling instances", () => { const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; return Effect.gen(function* () { const personalInstanceId = ProviderInstanceId.make("codex_personal"); const workInstanceId = ProviderInstanceId.make("codex_work"); const refreshedInstanceIds: Array<ProviderInstanceId> = []; const { registry } = yield* makeRegistry([ { ...baseProvider, instanceId: personalInstanceId, version: "0.124.0-alpha.3", }, { ...baseProvider, instanceId: workInstanceId, version: "0.124.0-alpha.3", }, ]); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: (instanceId, provider) => Effect.succeed( makeProviderMaintenanceCapabilities({ provider, packageName: "@openai/codex-instance-test", updateExecutable: "vp", updateArgs: ["i", "-g", "@openai/codex"], updateLockKey: "vite-plus-global", }), ).pipe( Effect.tap(() => Effect.sync(() => assert.strictEqual(instanceId, personalInstanceId))), ), refreshInstance: (instanceId) => registry.refreshInstance(instanceId).pipe( Effect.tap(() => Effect.sync(() => { refreshedInstanceIds.push(instanceId); }), ), ), }); const result = yield* updater.updateProvider({ provider: CODEX_DRIVER, instanceId: personalInstanceId, }); assert.deepStrictEqual(calls, [ { command: "vp", args: ["i", "-g", "@openai/codex"], }, ]); assert.deepStrictEqual(refreshedInstanceIds, [personalInstanceId]); assert.strictEqual(result.providers[0]?.instanceId, personalInstanceId); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.strictEqual(result.providers[1]?.instanceId, workInstanceId); assert.strictEqual(result.providers[1]?.updateState, undefined); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.124.0-alpha.3"), mockSpawnerLayer((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; }), ), ), ); }); it.effect("records command failure output in provider update state", () => Effect.gen(function* () { const { registry } = yield* makeRegistry(); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CODEX_DRIVER); const updateState = result.providers[0]?.updateState; assert.strictEqual(updateState?.status, "failed"); assert.strictEqual(updateState?.message, "Update command exited with code 1."); assert.include(updateState?.output ?? "", "permission denied"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer(() => ({ stderr: "permission denied", code: 1 })), ), ), ), ); it.effect( "marks successful commands as unchanged when the refreshed provider is still outdated", () => Effect.gen(function* () { const { registry } = yield* makeRegistry({ ...baseProvider, installed: true, version: "0.1.0", }); const updater = yield* makeTestRunner(registry); const result = yield* updater.updateProvider(CODEX_DRIVER); assert.strictEqual(result.providers[0]?.updateState?.status, "unchanged"); assert.include(result.providers[0]?.updateState?.message ?? "", "still detects"); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("9.9.9"), mockSpawnerLayer(() => ({ stdout: "updated" })), ), ), ), ); it.effect("prevents concurrent updates for the same provider", () => { const startedLatch: { resolve: () => void } = { resolve: () => {} }; const releaseLatch: { resolve: () => void } = { resolve: () => {} }; const started = new Promise<void>((resolve) => { startedLatch.resolve = resolve; }); const release = new Promise<void>((resolve) => { releaseLatch.resolve = resolve; }); return Effect.gen(function* () { const { registry } = yield* makeRegistry(); const updater = yield* makeTestRunner(registry); const first = yield* updater.updateProvider(CODEX_DRIVER).pipe(Effect.forkScoped); yield* Effect.promise(() => started); const second = yield* updater.updateProvider(CODEX_DRIVER).pipe(Effect.exit); assert.strictEqual(Exit.isFailure(second), true); if (Exit.isFailure(second)) { const error = Cause.squash(second.cause); assert.strictEqual(Schema.is(ServerProviderUpdateError)(error), true); if (Schema.is(ServerProviderUpdateError)(error)) { assert.include(error.reason, "already running"); } } releaseLatch.resolve(); yield* Fiber.join(first); }).pipe( Effect.provide( Layer.mergeAll( latestVersionHttpClient("0.0.0"), mockSpawnerLayer(() => { startedLatch.resolve(); return { stdout: "updated", exitCode: Effect.promise(() => release).pipe( Effect.as(ChildProcessSpawner.ExitCode(0)), ), }; }), ), ), ); }); it.effect("serializes different providers that share the same update lock key", () => { const firstStartedLatch: { resolve: () => void } = { resolve: () => {} }; const releaseFirstLatch: { resolve: () => void } = { resolve: () => {} }; const firstStarted = new Promise<void>((resolve) => { firstStartedLatch.resolve = resolve; }); const releaseFirst = new Promise<void>((resolve) => { releaseFirstLatch.resolve = resolve; }); const calls: Array<string> = []; return Effect.gen(function* () { const { registry } = yield* makeRegistry([baseProvider, baseOpenCodeProvider]); const updater = yield* makeTestRunner({ ...registry, getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) => Effect.succeed( makeProviderMaintenanceCapabilities({ provider, packageName: provider === OPENCODE_DRIVER ? "opencode-ai" : "@openai/codex", updateExecutable: "npm", updateArgs: provider === OPENCODE_DRIVER ? ["install", "-g", "opencode-ai@latest"] : ["install", "-g", "@openai/codex@latest"], updateLockKey: "npm-global",