// @effect-diagnostics nodeBuiltinImport:offimport { expect, it } from "@effect/vitest";import * as NodeChildProcess from "node:child_process";import * as NodeFS from "node:fs";import * as NodeServices from "@effect/platform-node/NodeServices";import * as NodeOS from "node:os";import * as NodePath from "node:path";import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";import { HostProcessPlatform } from "@t3tools/shared/hostProcess";import * as Crypto from "effect/Crypto";import * as Effect from "effect/Effect";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import { HttpClient } from "effect/unstable/http";import { ChildProcessSpawner } from "effect/unstable/process";import { createProviderVersionAdvisory, enrichProviderSnapshotWithVersionAdvisory, homebrewOwnershipFromCommandPath, makeCachedProviderMaintenanceResolution, makePackageManagedProviderMaintenanceResolver, makeProviderMaintenanceCapabilities, normalizeCommandPath, npmGlobalPrefixFromCommandPath, parseHomebrewLatestVersion, ProviderVersionCache, resolveLatestProviderVersion, resolvePackageManagedProviderMaintenance, resolveProviderMaintenanceCapabilitiesEffect, type ProviderMaintenanceCapabilities,} from "./providerMaintenance.ts";import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; const driver = (value: string) => ProviderDriverKind.make(value);// These write `#!/bin/sh` stubs and evaluate them with darwin/linux path// semantics; a Windows temp path cannot be split on `:`.const windowsHost = HostProcessPlatform.defaultValue() === "win32";const makeTempDir = (name: string) => Crypto.Crypto.pipe( Effect.flatMap((crypto) => crypto.randomUUIDv4), Effect.map((id) => NodePath.join(NodeOS.tmpdir(), `${name}-${id}`)), );const isNativeTestCommandPath = (expectedPathSegment: string) => (commandPath: string): boolean => normalizeCommandPath(commandPath).includes(expectedPathSegment);const packageToolUpdate = makePackageManagedProviderMaintenanceResolver({ provider: driver("packageTool"), npmPackageName: "@example/package-tool", nativeUpdate: null,});const nativePackageToolUpdate = makePackageManagedProviderMaintenanceResolver({ provider: driver("nativePackageTool"), npmPackageName: "@example/native-package-tool", nativeUpdate: { args: ["update"], isCommandPath: isNativeTestCommandPath("/.local/bin/native-package-tool"), },});const installedPackageToolProvider: ServerProvider = { instanceId: ProviderInstanceId.make("packageTool"), driver: driver("packageTool"), enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-04-10T00:00:00.000Z", models: [], slashCommands: [], skills: [],};const manualPackageTool: ProviderMaintenanceCapabilities = { provider: driver("packageTool"), packageName: "@example/package-tool", update: null,}; function writeExecutable(path: string) { NodeFS.mkdirSync(NodePath.dirname(path), { recursive: true }); NodeFS.writeFileSync(path, "#!/bin/sh\n"); NodeFS.chmodSync(path, 0o755);} /** Symlink `<tempDir>/bin/<name>` into a package entry point, like npm/pnpm do. */function linkIntoPackage(tempDir: string, name: string, packageSegments: ReadonlyArray<string>) { const target = NodePath.join(tempDir, ...packageSegments, "bin", `${name}.js`); writeExecutable(target); const link = NodePath.join(tempDir, "bin", name); NodeFS.mkdirSync(NodePath.dirname(link), { recursive: true }); NodeFS.symlinkSync(target, link); return link;} const noSpawn = ChildProcessSpawner.make(() => Effect.die("maintenance resolution should not spawn a process here"),); function stdoutSpawner(onSpawn: (command: string, args: ReadonlyArray<string>) => string) { return ChildProcessSpawner.make((command) => { const { command: executable, args } = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; }; return Effect.succeed( ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(1), exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)), isRunning: Effect.succeed(false), kill: () => Effect.void, unref: Effect.succeed(Effect.void), stdin: Sink.drain, stdout: Stream.encodeText(Stream.make(onSpawn(executable, args))), stderr: Stream.empty, all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }), ); });} it.layer(NodeServices.layer)("providerMaintenance", (it) => { it.effect("reads cached versions through the injectable cache reference", () => resolveLatestProviderVersion(manualPackageTool).pipe( Effect.provideService( ProviderVersionCache, new Map([ [ "@example/package-tool", { expiresAt: Number.MAX_SAFE_INTEGER, version: "9.9.9", }, ], ]), ), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("cached provider version should not make an HTTP request"), ), ), Effect.map((version) => { expect(version).toBe("9.9.9"); }), ), ); it.effect("prefers the installer's own latest version over the npm registry", () => resolveLatestProviderVersion({ ...manualPackageTool, latestVersion: "1.2.0" }).pipe( Effect.provideService(ProviderVersionCache, new Map()), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("installer-reported latest should not make an HTTP request"), ), ), Effect.map((version) => { expect(version).toBe("1.2.0"); }), ), ); it.effect("does not fetch latest provider versions when update checks are disabled", () => enrichProviderSnapshotWithVersionAdvisory(installedPackageToolProvider, manualPackageTool, { enableProviderUpdateChecks: false, }).pipe( Effect.provideService(ProviderVersionCache, new Map()), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("disabled provider update checks should not make an HTTP request"), ), ), Effect.map((provider) => { expect(provider.versionAdvisory).toMatchObject({ status: "unknown", currentVersion: "1.0.0", latestVersion: null, checkedAt: "2026-04-10T00:00:00.000Z", }); }), ), ); it("marks providers with unknown current versions as unknown", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: null, latestVersion: "9.9.9", }), ).toMatchObject({ status: "unknown", currentVersion: null, latestVersion: "9.9.9", }); }); it("marks providers with unknown latest versions as unknown", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: "1.0.0", latestVersion: null, }), ).toMatchObject({ status: "unknown", currentVersion: "1.0.0", latestVersion: null, message: null, }); }); it("keeps the manual update hint when the install is behind but unowned", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: "2.1.110", latestVersion: "2.1.117", maintenanceCapabilities: manualPackageTool, }), ).toMatchObject({ status: "behind_latest", latestVersion: "2.1.117", updateCommand: null, canUpdate: false, message: "Install the update now or review provider settings.", }); }); it.effect("stays manual-only when the binary cannot be located", () => resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: "package-tool", env: { PATH: "" }, }).pipe( Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), Effect.map((capabilities) => { expect(capabilities).toEqual(manualPackageTool); }), ), ); it.effect.skipIf(!symlinksSupported)( "pins npm updates to the global prefix that owns the package", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-npm-capabilities"); const link = linkIntoPackage(tempDir, "package-tool", [ "lib", "node_modules", "@example", "package-tool", ]); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: link, env: { PATH: "" }, }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities).toEqual({ provider: driver("packageTool"), packageName: "@example/package-tool", update: { command: `npm install -g --prefix ${tempDir} --allow-scripts=@example/package-tool @example/package-tool@latest`, executable: "npm", args: [ "install", "-g", "--prefix", tempDir, "--allow-scripts=@example/package-tool", "@example/package-tool@latest", ], lockKey: `npm-global:${normalizeCommandPath(tempDir)}`, }, }); }), ); it("derives the npm prefix only from the global lib/node_modules layout", () => { expect( npmGlobalPrefixFromCommandPath( "/usr/local/lib/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBe("/usr/local"); // A copy nested inside another package is not a global install. expect( npmGlobalPrefixFromCommandPath( "/usr/local/lib/node_modules/other/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBeNull(); expect( npmGlobalPrefixFromCommandPath( "/lib/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBe("/"); // Neither is a project-local dependency. expect( npmGlobalPrefixFromCommandPath( "/work/app/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBeNull(); }); // The Codex Windows installer exposes `%LOCALAPPDATA%\\Programs\\OpenAI\\Codex\\bin` // as a junction into `%CODEX_HOME%\\packages\\standalone\\current\\bin`. Node's // realpath follows junctions, so the real path carries the standalone marker // even though the visible path does not. it.effect("recognizes a Windows standalone install through its junctioned bin dir", () => Effect.gen(function* () { const visiblePath = "C:\\Users\\Theo\\AppData\\Local\\Programs\\OpenAI\\Codex\\bin\\codex.exe"; const realPath = "C:\\Users\\Theo\\.codex\\packages\\standalone\\releases\\0.120.0-x86_64\\bin\\codex.exe"; const capabilities = yield* resolvePackageManagedProviderMaintenance( { provider: driver("codex"), npmPackageName: "@openai/codex", nativeUpdate: { args: ["update"], isCommandPath: isNativeTestCommandPath("/packages/standalone/"), }, }, { binaryPath: "codex", resolvedCommandPath: visiblePath, realCommandPath: realPath, env: {}, platform: "win32", }, ).pipe(Effect.provideService(HostProcessPlatform, "win32")); expect(capabilities.update).toMatchObject({ executable: visiblePath, args: ["update"], lockKey: "codex-native", }); }), ); it.effect("proves Windows npm ownership from the package manifest beside the shim", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-npm-windows-capabilities"); const shim = NodePath.join(tempDir, "package-tool.cmd"); NodeFS.mkdirSync(tempDir, { recursive: true }); NodeFS.writeFileSync(shim, "@echo off\r\n"); NodeFS.mkdirSync(NodePath.join(tempDir, "node_modules", "@example", "package-tool"), { recursive: true, }); NodeFS.writeFileSync( NodePath.join(tempDir, "node_modules", "@example", "package-tool", "package.json"), "{}", ); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: shim, env: { PATH: "", PATHEXT: ".COM;.EXE;.BAT;.CMD" }, }).pipe( Effect.provideService(HostProcessPlatform, "win32"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities.update).toMatchObject({ executable: "npm", args: ["install", "-g", "--prefix", tempDir, expect.any(String), expect.any(String)], }); // The same layout on POSIX is a project checkout, not a global install. const script = NodePath.join(tempDir, "package-tool"); writeExecutable(script); const posix = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: script, env: { PATH: "" }, }).pipe( Effect.provideService(HostProcessPlatform, "linux"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(posix.update).toBeNull(); }), ); it.effect.skipIf(!symlinksSupported)( "switches to pnpm updates when the real path lives in pnpm's global store", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-pnpm-capabilities"); const link = linkIntoPackage(tempDir, "package-tool", [ ".local", "share", "pnpm", "global", "5", "node_modules", "@example", "package-tool", ]); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: link, env: { PATH: "" }, }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities.update).toMatchObject({ command: "pnpm add -g @example/package-tool@latest", lockKey: "pnpm-global", }); }), ); it.effect.skipIf(windowsHost)( "switches to bun updates when the resolved binary lives in bun's global bin", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-bun-capabilities"); const bunBinDir = NodePath.join(tempDir, ".bun", "bin"); writeExecutable(NodePath.join(bunBinDir, "package-tool")); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: "package-tool", env: { PATH: bunBinDir }, }, ).pipe( Effect.provideService(HostProcessPlatform, "darwin"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities.update).toMatchObject({ command: "bun i -g @example/package-tool@latest", lockKey: "bun-global", }); }), ); it.effect.skipIf(windowsHost)("switches to native updates and runs the resolved executable", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-native-capabilities"); const nativeBinDir = NodePath.join(tempDir, ".local", "bin"); const nativePath = NodePath.join(nativeBinDir, "native-package-tool"); writeExecutable(nativePath); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, { binaryPath: "native-package-tool", env: { PATH: nativeBinDir }, }, ).pipe( Effect.provideService(HostProcessPlatform, "darwin"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities).toEqual({ provider: driver("nativePackageTool"), packageName: "@example/native-package-tool", update: { command: `${nativePath} update`, executable: nativePath, args: ["update"], lockKey: "nativePackageTool-native", }, }); }), ); // Regression for #9850: an explicit native path outside PATH, with spaces, // must be what actually gets spawned. it.effect.skipIf(windowsHost)("runs an explicit native updater outside PATH", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-native-update"); const nativePath = NodePath.join( tempDir, "with spaces", ".local", "bin", "native-package-tool", ); NodeFS.mkdirSync(NodePath.dirname(nativePath), { recursive: true }); NodeFS.writeFileSync(nativePath, "#!/bin/sh\nprintf '%s' \"$1\"\n"); NodeFS.chmodSync(nativePath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, { binaryPath: nativePath, env: { PATH: "" } }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities.update?.executable).toBe(nativePath);Lines 1–500 of 788. Load more
// @effect-diagnostics nodeBuiltinImport:off import { expect, it } from "@effect/vitest"; import * as NodeChildProcess from "node:child_process"; import * as NodeFS from "node:fs"; import * as NodeServices from "@effect/platform-node/NodeServices"; import * as NodeOS from "node:os"; import * as NodePath from "node:path"; import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { HttpClient } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; import { createProviderVersionAdvisory, enrichProviderSnapshotWithVersionAdvisory, homebrewOwnershipFromCommandPath, makeCachedProviderMaintenanceResolution, makePackageManagedProviderMaintenanceResolver, makeProviderMaintenanceCapabilities, normalizeCommandPath, npmGlobalPrefixFromCommandPath, parseHomebrewLatestVersion, ProviderVersionCache, resolveLatestProviderVersion, resolvePackageManagedProviderMaintenance, resolveProviderMaintenanceCapabilitiesEffect, type ProviderMaintenanceCapabilities, } from "./providerMaintenance.ts"; import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; const driver = (value: string) => ProviderDriverKind.make(value); // These write `#!/bin/sh` stubs and evaluate them with darwin/linux path // semantics; a Windows temp path cannot be split on `:`. const windowsHost = HostProcessPlatform.defaultValue() === "win32"; const makeTempDir = (name: string) => Crypto.Crypto.pipe( Effect.flatMap((crypto) => crypto.randomUUIDv4), Effect.map((id) => NodePath.join(NodeOS.tmpdir(), `${name}-${id}`)), ); const isNativeTestCommandPath = (expectedPathSegment: string) => (commandPath: string): boolean => normalizeCommandPath(commandPath).includes(expectedPathSegment); const packageToolUpdate = makePackageManagedProviderMaintenanceResolver({ provider: driver("packageTool"), npmPackageName: "@example/package-tool", nativeUpdate: null, }); const nativePackageToolUpdate = makePackageManagedProviderMaintenanceResolver({ provider: driver("nativePackageTool"), npmPackageName: "@example/native-package-tool", nativeUpdate: { args: ["update"], isCommandPath: isNativeTestCommandPath("/.local/bin/native-package-tool"), }, }); const installedPackageToolProvider: ServerProvider = { instanceId: ProviderInstanceId.make("packageTool"), driver: driver("packageTool"), enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-04-10T00:00:00.000Z", models: [], slashCommands: [], skills: [], }; const manualPackageTool: ProviderMaintenanceCapabilities = { provider: driver("packageTool"), packageName: "@example/package-tool", update: null, }; function writeExecutable(path: string) { NodeFS.mkdirSync(NodePath.dirname(path), { recursive: true }); NodeFS.writeFileSync(path, "#!/bin/sh\n"); NodeFS.chmodSync(path, 0o755); } /** Symlink `<tempDir>/bin/<name>` into a package entry point, like npm/pnpm do. */ function linkIntoPackage(tempDir: string, name: string, packageSegments: ReadonlyArray<string>) { const target = NodePath.join(tempDir, ...packageSegments, "bin", `${name}.js`); writeExecutable(target); const link = NodePath.join(tempDir, "bin", name); NodeFS.mkdirSync(NodePath.dirname(link), { recursive: true }); NodeFS.symlinkSync(target, link); return link; } const noSpawn = ChildProcessSpawner.make(() => Effect.die("maintenance resolution should not spawn a process here"), ); function stdoutSpawner(onSpawn: (command: string, args: ReadonlyArray<string>) => string) { return ChildProcessSpawner.make((command) => { const { command: executable, args } = command as unknown as { readonly command: string; readonly args: ReadonlyArray<string>; }; return Effect.succeed( ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(1), exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)), isRunning: Effect.succeed(false), kill: () => Effect.void, unref: Effect.succeed(Effect.void), stdin: Sink.drain, stdout: Stream.encodeText(Stream.make(onSpawn(executable, args))), stderr: Stream.empty, all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }), ); }); } it.layer(NodeServices.layer)("providerMaintenance", (it) => { it.effect("reads cached versions through the injectable cache reference", () => resolveLatestProviderVersion(manualPackageTool).pipe( Effect.provideService( ProviderVersionCache, new Map([ [ "@example/package-tool", { expiresAt: Number.MAX_SAFE_INTEGER, version: "9.9.9", }, ], ]), ), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("cached provider version should not make an HTTP request"), ), ), Effect.map((version) => { expect(version).toBe("9.9.9"); }), ), ); it.effect("prefers the installer's own latest version over the npm registry", () => resolveLatestProviderVersion({ ...manualPackageTool, latestVersion: "1.2.0" }).pipe( Effect.provideService(ProviderVersionCache, new Map()), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("installer-reported latest should not make an HTTP request"), ), ), Effect.map((version) => { expect(version).toBe("1.2.0"); }), ), ); it.effect("does not fetch latest provider versions when update checks are disabled", () => enrichProviderSnapshotWithVersionAdvisory(installedPackageToolProvider, manualPackageTool, { enableProviderUpdateChecks: false, }).pipe( Effect.provideService(ProviderVersionCache, new Map()), Effect.provideService( HttpClient.HttpClient, HttpClient.make(() => Effect.die("disabled provider update checks should not make an HTTP request"), ), ), Effect.map((provider) => { expect(provider.versionAdvisory).toMatchObject({ status: "unknown", currentVersion: "1.0.0", latestVersion: null, checkedAt: "2026-04-10T00:00:00.000Z", }); }), ), ); it("marks providers with unknown current versions as unknown", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: null, latestVersion: "9.9.9", }), ).toMatchObject({ status: "unknown", currentVersion: null, latestVersion: "9.9.9", }); }); it("marks providers with unknown latest versions as unknown", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: "1.0.0", latestVersion: null, }), ).toMatchObject({ status: "unknown", currentVersion: "1.0.0", latestVersion: null, message: null, }); }); it("keeps the manual update hint when the install is behind but unowned", () => { expect( createProviderVersionAdvisory({ driver: driver("packageTool"), currentVersion: "2.1.110", latestVersion: "2.1.117", maintenanceCapabilities: manualPackageTool, }), ).toMatchObject({ status: "behind_latest", latestVersion: "2.1.117", updateCommand: null, canUpdate: false, message: "Install the update now or review provider settings.", }); }); it.effect("stays manual-only when the binary cannot be located", () => resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: "package-tool", env: { PATH: "" }, }).pipe( Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), Effect.map((capabilities) => { expect(capabilities).toEqual(manualPackageTool); }), ), ); it.effect.skipIf(!symlinksSupported)( "pins npm updates to the global prefix that owns the package", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-npm-capabilities"); const link = linkIntoPackage(tempDir, "package-tool", [ "lib", "node_modules", "@example", "package-tool", ]); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: link, env: { PATH: "" }, }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities).toEqual({ provider: driver("packageTool"), packageName: "@example/package-tool", update: { command: `npm install -g --prefix ${tempDir} --allow-scripts=@example/package-tool @example/package-tool@latest`, executable: "npm", args: [ "install", "-g", "--prefix", tempDir, "--allow-scripts=@example/package-tool", "@example/package-tool@latest", ], lockKey: `npm-global:${normalizeCommandPath(tempDir)}`, }, }); }), ); it("derives the npm prefix only from the global lib/node_modules layout", () => { expect( npmGlobalPrefixFromCommandPath( "/usr/local/lib/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBe("/usr/local"); // A copy nested inside another package is not a global install. expect( npmGlobalPrefixFromCommandPath( "/usr/local/lib/node_modules/other/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBeNull(); expect( npmGlobalPrefixFromCommandPath( "/lib/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBe("/"); // Neither is a project-local dependency. expect( npmGlobalPrefixFromCommandPath( "/work/app/node_modules/@openai/codex/bin/codex.js", "@openai/codex", ), ).toBeNull(); }); // The Codex Windows installer exposes `%LOCALAPPDATA%\\Programs\\OpenAI\\Codex\\bin` // as a junction into `%CODEX_HOME%\\packages\\standalone\\current\\bin`. Node's // realpath follows junctions, so the real path carries the standalone marker // even though the visible path does not. it.effect("recognizes a Windows standalone install through its junctioned bin dir", () => Effect.gen(function* () { const visiblePath = "C:\\Users\\Theo\\AppData\\Local\\Programs\\OpenAI\\Codex\\bin\\codex.exe"; const realPath = "C:\\Users\\Theo\\.codex\\packages\\standalone\\releases\\0.120.0-x86_64\\bin\\codex.exe"; const capabilities = yield* resolvePackageManagedProviderMaintenance( { provider: driver("codex"), npmPackageName: "@openai/codex", nativeUpdate: { args: ["update"], isCommandPath: isNativeTestCommandPath("/packages/standalone/"), }, }, { binaryPath: "codex", resolvedCommandPath: visiblePath, realCommandPath: realPath, env: {}, platform: "win32", }, ).pipe(Effect.provideService(HostProcessPlatform, "win32")); expect(capabilities.update).toMatchObject({ executable: visiblePath, args: ["update"], lockKey: "codex-native", }); }), ); it.effect("proves Windows npm ownership from the package manifest beside the shim", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-npm-windows-capabilities"); const shim = NodePath.join(tempDir, "package-tool.cmd"); NodeFS.mkdirSync(tempDir, { recursive: true }); NodeFS.writeFileSync(shim, "@echo off\r\n"); NodeFS.mkdirSync(NodePath.join(tempDir, "node_modules", "@example", "package-tool"), { recursive: true, }); NodeFS.writeFileSync( NodePath.join(tempDir, "node_modules", "@example", "package-tool", "package.json"), "{}", ); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: shim, env: { PATH: "", PATHEXT: ".COM;.EXE;.BAT;.CMD" }, }).pipe( Effect.provideService(HostProcessPlatform, "win32"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities.update).toMatchObject({ executable: "npm", args: ["install", "-g", "--prefix", tempDir, expect.any(String), expect.any(String)], }); // The same layout on POSIX is a project checkout, not a global install. const script = NodePath.join(tempDir, "package-tool"); writeExecutable(script); const posix = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: script, env: { PATH: "" }, }).pipe( Effect.provideService(HostProcessPlatform, "linux"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(posix.update).toBeNull(); }), ); it.effect.skipIf(!symlinksSupported)( "switches to pnpm updates when the real path lives in pnpm's global store", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-pnpm-capabilities"); const link = linkIntoPackage(tempDir, "package-tool", [ ".local", "share", "pnpm", "global", "5", "node_modules", "@example", "package-tool", ]); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: link, env: { PATH: "" }, }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities.update).toMatchObject({ command: "pnpm add -g @example/package-tool@latest", lockKey: "pnpm-global", }); }), ); it.effect.skipIf(windowsHost)( "switches to bun updates when the resolved binary lives in bun's global bin", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-bun-capabilities"); const bunBinDir = NodePath.join(tempDir, ".bun", "bin"); writeExecutable(NodePath.join(bunBinDir, "package-tool")); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, { binaryPath: "package-tool", env: { PATH: bunBinDir }, }, ).pipe( Effect.provideService(HostProcessPlatform, "darwin"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities.update).toMatchObject({ command: "bun i -g @example/package-tool@latest", lockKey: "bun-global", }); }), ); it.effect.skipIf(windowsHost)("switches to native updates and runs the resolved executable", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-native-capabilities"); const nativeBinDir = NodePath.join(tempDir, ".local", "bin"); const nativePath = NodePath.join(nativeBinDir, "native-package-tool"); writeExecutable(nativePath); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, { binaryPath: "native-package-tool", env: { PATH: nativeBinDir }, }, ).pipe( Effect.provideService(HostProcessPlatform, "darwin"), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), ); expect(capabilities).toEqual({ provider: driver("nativePackageTool"), packageName: "@example/native-package-tool", update: { command: `${nativePath} update`, executable: nativePath, args: ["update"], lockKey: "nativePackageTool-native", }, }); }), ); // Regression for #9850: an explicit native path outside PATH, with spaces, // must be what actually gets spawned. it.effect.skipIf(windowsHost)("runs an explicit native updater outside PATH", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-native-update"); const nativePath = NodePath.join( tempDir, "with spaces", ".local", "bin", "native-package-tool", ); NodeFS.mkdirSync(NodePath.dirname(nativePath), { recursive: true }); NodeFS.writeFileSync(nativePath, "#!/bin/sh\nprintf '%s' \"$1\"\n"); NodeFS.chmodSync(nativePath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, { binaryPath: nativePath, env: { PATH: "" } }, ).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn)); expect(capabilities.update?.executable).toBe(nativePath);