diff --git a/.macroscope/check-run-agents/effect-service-conventions.md b/.macroscope/check-run-agents/effect-service-conventions.md index 1bbd8192cb5..d474c41d2fe 100644 --- a/.macroscope/check-run-agents/effect-service-conventions.md +++ b/.macroscope/check-run-agents/effect-service-conventions.md @@ -27,7 +27,7 @@ Review changed TypeScript and directly affected call sites for the conventions b - Import Effect library modules from their subpaths as namespaces, for example `import * as Effect from "effect/Effect"` and `import * as Layer from "effect/Layer"`. Flag consolidated named imports from `"effect"` in touched Effect service code. - At a service boundary, import the local service module as a namespace and use its public module shape: `WorkspacePaths.WorkspacePaths`, `WorkspacePaths.make`, and `WorkspacePaths.layer`. Flag aliases such as `import { layer as workspacePathsLayer }` that erase the module namespace. -- Namespace imports are not a blanket rule. Keep named imports for whole packages such as `@t3tools/contracts` and `electron`, and for modules used only for a pure helper, error, schema, config value, or standalone type. Do not request `import type * as Contracts`. +- Namespace imports are not a blanket rule. Keep named imports for whole packages such as `@t3tools/contracts`, and for modules used only for a pure helper, error, schema, config value, or standalone type. Do not request `import type * as Contracts`. - A package subpath that is itself a service module may use a namespace import when callers access its service/tag, `make`, or `layer` members. - When a barrel exposes an entire service module, prefer `export * as TokenStore from "./tokenStore.ts"` so consumers can use `TokenStore.TokenStore` and `TokenStore.layer`. Do not individually rename `make` and `layer` exports to simulate a namespace. diff --git a/apps/desktop/scripts/build-preview-annotation-css.mjs b/apps/desktop/scripts/build-preview-annotation-css.mjs index c45f81268a6..a5dbdcfbe69 100644 --- a/apps/desktop/scripts/build-preview-annotation-css.mjs +++ b/apps/desktop/scripts/build-preview-annotation-css.mjs @@ -1,23 +1,23 @@ -import { readFile, writeFile } from "node:fs/promises"; -import { createRequire } from "node:module"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeModule from "node:module"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; import { compile } from "tailwindcss"; -const directory = dirname(fileURLToPath(import.meta.url)); -const appRoot = join(directory, ".."); -const sourcePath = join(appRoot, "src", "preview", "Annotation.css"); -const preloadPath = join(appRoot, "src", "preview", "PickPreload.ts"); -const outputPath = join(appRoot, "src", "preview", "AnnotationStyles.generated.ts"); -const require = createRequire(import.meta.url); -const tailwindRoot = dirname(require.resolve("tailwindcss/package.json")); +const directory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const appRoot = NodePath.join(directory, ".."); +const sourcePath = NodePath.join(appRoot, "src", "preview", "Annotation.css"); +const preloadPath = NodePath.join(appRoot, "src", "preview", "PickPreload.ts"); +const outputPath = NodePath.join(appRoot, "src", "preview", "AnnotationStyles.generated.ts"); +const require = NodeModule.createRequire(import.meta.url); +const tailwindRoot = NodePath.dirname(require.resolve("tailwindcss/package.json")); const [annotationSource, preloadSource, themeSource, preflightSource] = await Promise.all([ - readFile(sourcePath, "utf8"), - readFile(preloadPath, "utf8"), - readFile(join(tailwindRoot, "theme.css"), "utf8"), - readFile(join(tailwindRoot, "preflight.css"), "utf8"), + NodeFSP.readFile(sourcePath, "utf8"), + NodeFSP.readFile(preloadPath, "utf8"), + NodeFSP.readFile(NodePath.join(tailwindRoot, "theme.css"), "utf8"), + NodeFSP.readFile(NodePath.join(tailwindRoot, "preflight.css"), "utf8"), ]); const candidates = new Set( @@ -37,4 +37,4 @@ const encodedCss = `'${css .replaceAll("\n", "\\n")}'`; const moduleSource = `// Generated by scripts/build-preview-annotation-css.mjs. Do not edit.\nexport const previewAnnotationStyles =\n ${encodedCss};\n`; -await writeFile(outputPath, moduleSource); +await NodeFSP.writeFile(outputPath, moduleSource); diff --git a/apps/desktop/scripts/dev-electron.mjs b/apps/desktop/scripts/dev-electron.mjs index 58ccfe90eb9..c28d5ec358b 100644 --- a/apps/desktop/scripts/dev-electron.mjs +++ b/apps/desktop/scripts/dev-electron.mjs @@ -1,7 +1,7 @@ -import { spawn, spawnSync } from "node:child_process"; -import { watch } from "node:fs"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; import * as NodeOS from "node:os"; -import { join } from "node:path"; +import * as NodePath from "node:path"; import { desktopDir, @@ -64,7 +64,7 @@ function killChildTreeByPid(pid, signal) { return; } - spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" }); + NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" }); } function cleanupStaleDevApps() { @@ -72,7 +72,9 @@ function cleanupStaleDevApps() { return; } - spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], { stdio: "ignore" }); + NodeChildProcess.spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], { + stdio: "ignore", + }); } function startApp() { @@ -87,7 +89,7 @@ function startApp() { ? electronArgs : [...electronArgs, `--t3code-dev-root=${desktopDir}`, "dist-electron/main.cjs"]; const electronCommand = resolveElectronLaunchCommand(launchArgs); - const app = spawn(electronCommand.electronPath, electronCommand.args, { + const app = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, { cwd: desktopDir, env: childEnv, stdio: "inherit", @@ -180,8 +182,8 @@ function scheduleRestart() { function startWatchers() { for (const { directory, files } of watchedDirectories) { - const watcher = watch( - join(desktopDir, directory), + const watcher = NodeFS.watch( + NodePath.join(desktopDir, directory), { persistent: true }, (_eventType, filename) => { if (typeof filename !== "string" || !files.has(filename)) { @@ -202,7 +204,9 @@ function killChildTree(signal) { } // Kill direct children as a final fallback in case normal shutdown leaves stragglers. - spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], { stdio: "ignore" }); + NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], { + stdio: "ignore", + }); } async function shutdown(exitCode) { diff --git a/apps/desktop/scripts/electron-launcher.mjs b/apps/desktop/scripts/electron-launcher.mjs index 73d778fb48b..69df02fb80d 100644 --- a/apps/desktop/scripts/electron-launcher.mjs +++ b/apps/desktop/scripts/electron-launcher.mjs @@ -1,29 +1,18 @@ // This file mostly exists because we want dev mode to say "T3 Code (Dev)" instead of "electron" -import { spawnSync } from "node:child_process"; -import { - copyFileSync, - chmodSync, - cpSync, - existsSync, - mkdirSync, - mkdtempSync, - readFileSync, - rmSync, - statSync, - writeFileSync, -} from "node:fs"; -import { createRequire } from "node:module"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeModule from "node:module"; import * as NodeOS from "node:os"; -import { basename, dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; import { ensureElectronRuntime } from "./ensure-electron-runtime.mjs"; const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); -const __dirname = dirname(fileURLToPath(import.meta.url)); -export const desktopDir = resolve(__dirname, ".."); -const repoRoot = resolve(desktopDir, "..", ".."); -const devBundleIdSuffix = basename(repoRoot) +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +export const desktopDir = NodePath.resolve(__dirname, ".."); +const repoRoot = NodePath.resolve(desktopDir, "..", ".."); +const devBundleIdSuffix = NodePath.basename(repoRoot) .toLowerCase() .replaceAll(/[^a-z0-9]+/g, ""); export const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; @@ -32,22 +21,35 @@ export const APP_BUNDLE_ID = isDevelopment : "com.t3tools.t3code"; const APP_PROTOCOL_SCHEMES = isDevelopment ? ["t3code-dev"] : ["t3code"]; const LAUNCHER_VERSION = 12; -const defaultIconPath = join(desktopDir, "resources", "icon.icns"); -const developmentMacIconPngPath = join(repoRoot, "assets", "dev", "blueprint-macos-1024.png"); +const defaultIconPath = NodePath.join(desktopDir, "resources", "icon.icns"); +const developmentMacIconPngPath = NodePath.join( + repoRoot, + "assets", + "dev", + "blueprint-macos-1024.png", +); // oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone launcher script has no Effect runtime. const hostPlatform = NodeOS.platform(); function setPlistString(plistPath, key, value) { - const replaceResult = spawnSync("plutil", ["-replace", key, "-string", value, plistPath], { - encoding: "utf8", - }); + const replaceResult = NodeChildProcess.spawnSync( + "plutil", + ["-replace", key, "-string", value, plistPath], + { + encoding: "utf8", + }, + ); if (replaceResult.status === 0) { return; } - const insertResult = spawnSync("plutil", ["-insert", key, "-string", value, plistPath], { - encoding: "utf8", - }); + const insertResult = NodeChildProcess.spawnSync( + "plutil", + ["-insert", key, "-string", value, plistPath], + { + encoding: "utf8", + }, + ); if (insertResult.status === 0) { return; } @@ -58,16 +60,24 @@ function setPlistString(plistPath, key, value) { function setPlistJson(plistPath, key, value) { const serialized = JSON.stringify(value); - const replaceResult = spawnSync("plutil", ["-replace", key, "-json", serialized, plistPath], { - encoding: "utf8", - }); + const replaceResult = NodeChildProcess.spawnSync( + "plutil", + ["-replace", key, "-json", serialized, plistPath], + { + encoding: "utf8", + }, + ); if (replaceResult.status === 0) { return; } - const insertResult = spawnSync("plutil", ["-insert", key, "-json", serialized, plistPath], { - encoding: "utf8", - }); + const insertResult = NodeChildProcess.spawnSync( + "plutil", + ["-insert", key, "-json", serialized, plistPath], + { + encoding: "utf8", + }, + ); if (insertResult.status === 0) { return; } @@ -77,7 +87,7 @@ function setPlistJson(plistPath, key, value) { } function runChecked(command, args) { - const result = spawnSync(command, args, { encoding: "utf8" }); + const result = NodeChildProcess.spawnSync(command, args, { encoding: "utf8" }); if (result.status === 0) { return; } @@ -91,7 +101,7 @@ function shellSingleQuote(value) { } function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) { - const mainEntryPath = join(desktopDir, "dist-electron", "main.cjs"); + const mainEntryPath = NodePath.join(desktopDir, "dist-electron", "main.cjs"); const envEntries = [ ["VITE_DEV_SERVER_URL", process.env.VITE_DEV_SERVER_URL], ["T3CODE_PORT", process.env.T3CODE_PORT], @@ -101,7 +111,7 @@ function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) { ["T3CODE_OTLP_EXPORT_INTERVAL_MS", process.env.T3CODE_OTLP_EXPORT_INTERVAL_MS], ["T3CODE_DESKTOP_APP_USER_MODEL_ID", APP_BUNDLE_ID], ].filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0); - writeFileSync( + NodeFS.writeFileSync( targetBinaryPath, [ "#!/bin/sh", @@ -110,7 +120,7 @@ function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) { "", ].join("\n"), ); - chmodSync(targetBinaryPath, 0o755); + NodeFS.chmodSync(targetBinaryPath, 0o755); } function registerMacLauncherBundle(appBundlePath) { @@ -140,21 +150,24 @@ function registerMacLauncherBundle(appBundlePath) { } function ensureDevelopmentIconIcns(runtimeDir) { - const generatedIconPath = join(runtimeDir, "icon-dev.icns"); - mkdirSync(runtimeDir, { recursive: true }); + const generatedIconPath = NodePath.join(runtimeDir, "icon-dev.icns"); + NodeFS.mkdirSync(runtimeDir, { recursive: true }); - if (!existsSync(developmentMacIconPngPath)) { + if (!NodeFS.existsSync(developmentMacIconPngPath)) { return defaultIconPath; } - const sourceMtimeMs = statSync(developmentMacIconPngPath).mtimeMs; - if (existsSync(generatedIconPath) && statSync(generatedIconPath).mtimeMs >= sourceMtimeMs) { + const sourceMtimeMs = NodeFS.statSync(developmentMacIconPngPath).mtimeMs; + if ( + NodeFS.existsSync(generatedIconPath) && + NodeFS.statSync(generatedIconPath).mtimeMs >= sourceMtimeMs + ) { return generatedIconPath; } - const iconsetRoot = mkdtempSync(join(runtimeDir, "dev-iconset-")); - const iconsetDir = join(iconsetRoot, "icon.iconset"); - mkdirSync(iconsetDir, { recursive: true }); + const iconsetRoot = NodeFS.mkdtempSync(NodePath.join(runtimeDir, "dev-iconset-")); + const iconsetDir = NodePath.join(iconsetRoot, "icon.iconset"); + NodeFS.mkdirSync(iconsetDir, { recursive: true }); try { for (const size of [16, 32, 128, 256, 512]) { @@ -164,7 +177,7 @@ function ensureDevelopmentIconIcns(runtimeDir) { String(size), developmentMacIconPngPath, "--out", - join(iconsetDir, `icon_${size}x${size}.png`), + NodePath.join(iconsetDir, `icon_${size}x${size}.png`), ]); const retinaSize = size * 2; @@ -174,7 +187,7 @@ function ensureDevelopmentIconIcns(runtimeDir) { String(retinaSize), developmentMacIconPngPath, "--out", - join(iconsetDir, `icon_${size}x${size}@2x.png`), + NodePath.join(iconsetDir, `icon_${size}x${size}@2x.png`), ]); } @@ -187,12 +200,12 @@ function ensureDevelopmentIconIcns(runtimeDir) { ); return defaultIconPath; } finally { - rmSync(iconsetRoot, { recursive: true, force: true }); + NodeFS.rmSync(iconsetRoot, { recursive: true, force: true }); } } function patchMainBundleInfoPlist(appBundlePath, iconPath) { - const infoPlistPath = join(appBundlePath, "Contents", "Info.plist"); + const infoPlistPath = NodePath.join(appBundlePath, "Contents", "Info.plist"); setPlistString(infoPlistPath, "CFBundleDisplayName", APP_DISPLAY_NAME); setPlistString(infoPlistPath, "CFBundleName", APP_DISPLAY_NAME); setPlistString(infoPlistPath, "CFBundleIdentifier", APP_BUNDLE_ID); @@ -204,9 +217,9 @@ function patchMainBundleInfoPlist(appBundlePath, iconPath) { }, ]); - const resourcesDir = join(appBundlePath, "Contents", "Resources"); - copyFileSync(iconPath, join(resourcesDir, "icon.icns")); - copyFileSync(iconPath, join(resourcesDir, "electron.icns")); + const resourcesDir = NodePath.join(appBundlePath, "Contents", "Resources"); + NodeFS.copyFileSync(iconPath, NodePath.join(resourcesDir, "icon.icns")); + NodeFS.copyFileSync(iconPath, NodePath.join(resourcesDir, "electron.icns")); } function patchHelperBundleInfoPlists(appBundlePath) { @@ -218,7 +231,7 @@ function patchHelperBundleInfoPlists(appBundlePath) { ]; for (const [bundleName, bundleIdentifierSuffix, bundleDisplayName] of helperBundleNames) { - const infoPlistPath = join( + const infoPlistPath = NodePath.join( appBundlePath, "Contents", "Frameworks", @@ -226,7 +239,7 @@ function patchHelperBundleInfoPlists(appBundlePath) { "Contents", "Info.plist", ); - if (!existsSync(infoPlistPath)) { + if (!NodeFS.existsSync(infoPlistPath)) { continue; } @@ -242,34 +255,34 @@ function patchHelperBundleInfoPlists(appBundlePath) { function readJson(path) { try { - return JSON.parse(readFileSync(path, "utf8")); + return JSON.parse(NodeFS.readFileSync(path, "utf8")); } catch { return null; } } function buildMacLauncher(electronBinaryPath) { - const sourceAppBundlePath = resolve(dirname(electronBinaryPath), "../.."); - const runtimeDir = join(desktopDir, ".electron-runtime"); - const targetAppBundlePath = join(runtimeDir, `${APP_DISPLAY_NAME}.app`); - const targetBinaryPath = join(targetAppBundlePath, "Contents", "MacOS", "Electron"); + const sourceAppBundlePath = NodePath.resolve(NodePath.dirname(electronBinaryPath), "../.."); + const runtimeDir = NodePath.join(desktopDir, ".electron-runtime"); + const targetAppBundlePath = NodePath.join(runtimeDir, `${APP_DISPLAY_NAME}.app`); + const targetBinaryPath = NodePath.join(targetAppBundlePath, "Contents", "MacOS", "Electron"); const iconPath = isDevelopment ? ensureDevelopmentIconIcns(runtimeDir) : defaultIconPath; - const metadataPath = join(runtimeDir, "metadata.json"); + const metadataPath = NodePath.join(runtimeDir, "metadata.json"); - mkdirSync(runtimeDir, { recursive: true }); + NodeFS.mkdirSync(runtimeDir, { recursive: true }); const expectedMetadata = { launcherVersion: LAUNCHER_VERSION, sourceAppBundlePath, - sourceAppMtimeMs: statSync(sourceAppBundlePath).mtimeMs, - iconMtimeMs: statSync(iconPath).mtimeMs, + sourceAppMtimeMs: NodeFS.statSync(sourceAppBundlePath).mtimeMs, + iconMtimeMs: NodeFS.statSync(iconPath).mtimeMs, appBundleId: APP_BUNDLE_ID, appProtocolSchemes: APP_PROTOCOL_SCHEMES, }; const currentMetadata = readJson(metadataPath); if ( - existsSync(targetBinaryPath) && + NodeFS.existsSync(targetBinaryPath) && currentMetadata && JSON.stringify(currentMetadata) === JSON.stringify(expectedMetadata) ) { @@ -277,18 +290,21 @@ function buildMacLauncher(electronBinaryPath) { return targetBinaryPath; } - rmSync(targetAppBundlePath, { recursive: true, force: true }); + NodeFS.rmSync(targetAppBundlePath, { recursive: true, force: true }); // verbatimSymlinks keeps the framework's relative symlinks intact // (e.g. Resources -> Versions/Current/Resources). Without it cpSync // rewrites them to absolute paths into node_modules, which escape the // bundle and crash sandboxed helper processes (icudtl.dat not found). - cpSync(sourceAppBundlePath, targetAppBundlePath, { recursive: true, verbatimSymlinks: true }); + NodeFS.cpSync(sourceAppBundlePath, targetAppBundlePath, { + recursive: true, + verbatimSymlinks: true, + }); patchMainBundleInfoPlist(targetAppBundlePath, iconPath); patchHelperBundleInfoPlists(targetAppBundlePath); if (isDevelopment) { writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath); } - writeFileSync(metadataPath, `${JSON.stringify(expectedMetadata, null, 2)}\n`); + NodeFS.writeFileSync(metadataPath, `${JSON.stringify(expectedMetadata, null, 2)}\n`); registerMacLauncherBundle(targetAppBundlePath); return targetBinaryPath; @@ -299,9 +315,9 @@ function isLinuxSetuidSandboxConfigured(electronBinaryPath) { return true; } - const sandboxPath = join(dirname(electronBinaryPath), "chrome-sandbox"); + const sandboxPath = NodePath.join(NodePath.dirname(electronBinaryPath), "chrome-sandbox"); try { - const sandboxStat = statSync(sandboxPath); + const sandboxStat = NodeFS.statSync(sandboxPath); return sandboxStat.uid === 0 && (sandboxStat.mode & 0o4777) === 0o4755; } catch { return false; @@ -322,7 +338,7 @@ function resolveLinuxSandboxArgs(electronBinaryPath) { export function resolveElectronPath() { ensureElectronRuntime(); - const require = createRequire(import.meta.url); + const require = NodeModule.createRequire(import.meta.url); const electronBinaryPath = require("electron"); if (hostPlatform !== "darwin") { @@ -345,11 +361,11 @@ export function resolveDevProtocolClient() { return null; } - const require = createRequire(import.meta.url); + const require = NodeModule.createRequire(import.meta.url); const electronBinaryPath = require("electron"); const launcherBinaryPath = buildMacLauncher(electronBinaryPath); return { - appBundlePath: resolve(launcherBinaryPath, "..", "..", ".."), + appBundlePath: NodePath.resolve(launcherBinaryPath, "..", "..", ".."), appBundleId: APP_BUNDLE_ID, }; } diff --git a/apps/desktop/scripts/ensure-electron-runtime.mjs b/apps/desktop/scripts/ensure-electron-runtime.mjs index 0a13506d341..c37838ab183 100644 --- a/apps/desktop/scripts/ensure-electron-runtime.mjs +++ b/apps/desktop/scripts/ensure-electron-runtime.mjs @@ -1,14 +1,14 @@ -import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; -import { createRequire } from "node:module"; -import { arch, platform, tmpdir } from "node:os"; -import { dirname, join } from "node:path"; -import { spawnSync } from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeModule from "node:module"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import * as NodeChildProcess from "node:child_process"; -const require = createRequire(import.meta.url); +const require = NodeModule.createRequire(import.meta.url); // oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone repair script has no Effect runtime. -const hostPlatform = platform(); +const hostPlatform = NodeOS.platform(); // oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone repair script has no Effect runtime. -const hostArch = arch(); +const hostArch = NodeOS.arch(); function getPlatformPath() { switch (hostPlatform) { @@ -27,26 +27,28 @@ function getPlatformPath() { function ensureExecutable(filePath) { if (hostPlatform !== "win32") { - chmodSync(filePath, 0o755); + NodeFS.chmodSync(filePath, 0o755); } } function repairPathFile(electronDir, platformPath) { - const pathFile = join(electronDir, "path.txt"); - const currentPath = existsSync(pathFile) ? readFileSync(pathFile, "utf8") : undefined; + const pathFile = NodePath.join(electronDir, "path.txt"); + const currentPath = NodeFS.existsSync(pathFile) + ? NodeFS.readFileSync(pathFile, "utf8") + : undefined; if (currentPath !== platformPath) { - writeFileSync(pathFile, platformPath); + NodeFS.writeFileSync(pathFile, platformPath); } } function getRequiredRuntimePaths(electronDir, platformPath) { - const paths = [join(electronDir, "dist", platformPath)]; + const paths = [NodePath.join(electronDir, "dist", platformPath)]; if (hostPlatform === "darwin") { paths.push( - join(electronDir, "dist", "Electron.app", "Contents", "Info.plist"), - join( + NodePath.join(electronDir, "dist", "Electron.app", "Contents", "Info.plist"), + NodePath.join( electronDir, "dist", "Electron.app", @@ -66,7 +68,7 @@ function isMachO(filePath) { return true; } - const result = spawnSync("file", ["-b", filePath], { + const result = NodeChildProcess.spawnSync("file", ["-b", filePath], { encoding: "utf8", }); @@ -75,7 +77,7 @@ function isMachO(filePath) { function missingRuntimePaths(electronDir, platformPath) { return getRequiredRuntimePaths(electronDir, platformPath).filter((runtimePath) => { - return !existsSync(runtimePath); + return !NodeFS.existsSync(runtimePath); }); } @@ -85,8 +87,8 @@ function invalidRuntimePaths(electronDir, platformPath) { } return [ - join(electronDir, "dist", platformPath), - join( + NodePath.join(electronDir, "dist", platformPath), + NodePath.join( electronDir, "dist", "Electron.app", @@ -95,11 +97,11 @@ function invalidRuntimePaths(electronDir, platformPath) { "Electron Framework.framework", "Electron Framework", ), - ].filter((runtimePath) => existsSync(runtimePath) && !isMachO(runtimePath)); + ].filter((runtimePath) => NodeFS.existsSync(runtimePath) && !isMachO(runtimePath)); } function runChecked(command, args) { - const result = spawnSync(command, args, { + const result = NodeChildProcess.spawnSync(command, args, { encoding: "utf8", stdio: "inherit", }); @@ -114,8 +116,8 @@ function runChecked(command, args) { } function installElectronRuntime(electronDir, version) { - const tempDir = mkdtempSync(join(tmpdir(), "t3-electron-")); - const zipPath = join(tempDir, `electron-v${version}-${hostPlatform}-${hostArch}.zip`); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-electron-")); + const zipPath = NodePath.join(tempDir, `electron-v${version}-${hostPlatform}-${hostArch}.zip`); try { runChecked("curl", [ @@ -125,34 +127,34 @@ function installElectronRuntime(electronDir, version) { zipPath, ]); if (hostPlatform === "darwin") { - runChecked("ditto", ["-x", "-k", zipPath, join(electronDir, "dist")]); + runChecked("ditto", ["-x", "-k", zipPath, NodePath.join(electronDir, "dist")]); } else { runChecked("python3", [ "-c", "import os, sys, zipfile; os.makedirs(sys.argv[2], exist_ok=True); zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])", zipPath, - join(electronDir, "dist"), + NodePath.join(electronDir, "dist"), ]); } } finally { - rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } } export function ensureElectronRuntime() { const electronPackageJsonPath = require.resolve("electron/package.json"); - const electronPackageJson = JSON.parse(readFileSync(electronPackageJsonPath, "utf8")); - const electronDir = dirname(electronPackageJsonPath); + const electronPackageJson = JSON.parse(NodeFS.readFileSync(electronPackageJsonPath, "utf8")); + const electronDir = NodePath.dirname(electronPackageJsonPath); const platformPath = getPlatformPath(); - const electronPath = join(electronDir, "dist", platformPath); + const electronPath = NodePath.join(electronDir, "dist", platformPath); const missingBeforeInstall = missingRuntimePaths(electronDir, platformPath); const invalidBeforeInstall = invalidRuntimePaths(electronDir, platformPath); if (missingBeforeInstall.length > 0 || invalidBeforeInstall.length > 0) { - if (existsSync(join(electronDir, "dist"))) { - rmSync(join(electronDir, "dist"), { recursive: true, force: true }); + if (NodeFS.existsSync(NodePath.join(electronDir, "dist"))) { + NodeFS.rmSync(NodePath.join(electronDir, "dist"), { recursive: true, force: true }); } - rmSync(join(electronDir, "path.txt"), { force: true }); + NodeFS.rmSync(NodePath.join(electronDir, "path.txt"), { force: true }); installElectronRuntime(electronDir, electronPackageJson.version); } diff --git a/apps/desktop/scripts/smoke-test.mjs b/apps/desktop/scripts/smoke-test.mjs index 48a2e168a2b..fea5f0a120e 100644 --- a/apps/desktop/scripts/smoke-test.mjs +++ b/apps/desktop/scripts/smoke-test.mjs @@ -1,16 +1,16 @@ -import { spawn } from "node:child_process"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import * as NodeChildProcess from "node:child_process"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; import { resolveElectronLaunchCommand } from "./electron-launcher.mjs"; -const __dirname = dirname(fileURLToPath(import.meta.url)); -const desktopDir = resolve(__dirname, ".."); -const mainJs = resolve(desktopDir, "dist-electron/main.cjs"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const desktopDir = NodePath.resolve(__dirname, ".."); +const mainJs = NodePath.resolve(desktopDir, "dist-electron/main.cjs"); console.log("\nLaunching Electron smoke test..."); const electronCommand = resolveElectronLaunchCommand([mainJs]); -const child = spawn(electronCommand.electronPath, electronCommand.args, { +const child = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, diff --git a/apps/desktop/scripts/start-electron.mjs b/apps/desktop/scripts/start-electron.mjs index d959b4ab1f0..ecabd81fb40 100644 --- a/apps/desktop/scripts/start-electron.mjs +++ b/apps/desktop/scripts/start-electron.mjs @@ -1,4 +1,4 @@ -import { spawn } from "node:child_process"; +import * as NodeChildProcess from "node:child_process"; import { desktopDir, resolveElectronLaunchCommand } from "./electron-launcher.mjs"; @@ -6,7 +6,7 @@ const childEnv = { ...process.env }; delete childEnv.ELECTRON_RUN_AS_NODE; const electronCommand = resolveElectronLaunchCommand(["dist-electron/main.cjs"]); -const child = spawn(electronCommand.electronPath, electronCommand.args, { +const child = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, { stdio: "inherit", cwd: desktopDir, env: childEnv, diff --git a/apps/desktop/scripts/wait-for-resources.mjs b/apps/desktop/scripts/wait-for-resources.mjs index 2b0a60c5d98..00455f4db72 100644 --- a/apps/desktop/scripts/wait-for-resources.mjs +++ b/apps/desktop/scripts/wait-for-resources.mjs @@ -1,13 +1,13 @@ -import * as FileSystem from "node:fs/promises"; -import * as Net from "node:net"; -import * as Path from "node:path"; -import * as Timers from "node:timers/promises"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeNet from "node:net"; +import * as NodePath from "node:path"; +import * as NodeTimersPromises from "node:timers/promises"; const defaultTcpHosts = ["127.0.0.1", "localhost", "::1"]; async function fileExists(filePath) { try { - await FileSystem.access(filePath); + await NodeFSP.access(filePath); return true; } catch { return false; @@ -16,7 +16,7 @@ async function fileExists(filePath) { function tcpPortIsReady({ host, port, connectTimeoutMs = 500 }) { return new Promise((resolveReady) => { - const socket = Net.createConnection({ host, port }); + const socket = NodeNet.createConnection({ host, port }); let settled = false; const finish = (ready) => { @@ -47,7 +47,7 @@ async function resolvePendingResources({ baseDir, files, tcpPort, tcpHosts, conn const pendingFiles = []; for (const relativeFilePath of files) { - const ready = await fileExists(Path.resolve(baseDir, relativeFilePath)); + const ready = await fileExists(NodePath.resolve(baseDir, relativeFilePath)); if (!ready) { pendingFiles.push(relativeFilePath); } @@ -114,6 +114,6 @@ export async function waitForResources({ ); } - await Timers.setTimeout(intervalMs); + await NodeTimersPromises.setTimeout(intervalMs); } } diff --git a/apps/desktop/src/backend/DesktopNetworkInterfaces.ts b/apps/desktop/src/backend/DesktopNetworkInterfaces.ts index ad8c9eb8b14..79b6b824c8a 100644 --- a/apps/desktop/src/backend/DesktopNetworkInterfaces.ts +++ b/apps/desktop/src/backend/DesktopNetworkInterfaces.ts @@ -1,4 +1,4 @@ -import { networkInterfaces } from "node:os"; +import * as NodeOS from "node:os"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; @@ -27,7 +27,7 @@ export class DesktopNetworkInterfaces extends Context.Service< export const make = (): DesktopNetworkInterfaces["Service"] => DesktopNetworkInterfaces.of({ - read: Effect.sync(() => networkInterfaces()), + read: Effect.sync(() => NodeOS.networkInterfaces()), }); export const layer = Layer.succeed(DesktopNetworkInterfaces, make()); diff --git a/apps/desktop/src/ipc/methods/preview.ts b/apps/desktop/src/ipc/methods/preview.ts index 99bede9045d..1994c270024 100644 --- a/apps/desktop/src/ipc/methods/preview.ts +++ b/apps/desktop/src/ipc/methods/preview.ts @@ -22,7 +22,7 @@ import { import { BrowserWindow } from "electron"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; -import { pathToFileURL } from "node:url"; +import * as NodeURL from "node:url"; import * as PreviewManager from "../../preview/Manager.ts"; import { PREVIEW_WEBVIEW_PREFERENCES } from "../../preview/WebviewPreferences.ts"; @@ -196,7 +196,7 @@ export const getPreviewConfig = DesktopIpc.makeIpcMethod({ return { partition: yield* manager.getBrowserPartition(environmentId), webPreferences: PREVIEW_WEBVIEW_PREFERENCES, - preloadUrl: pathToFileURL(`${__dirname}/preview-pick-preload.cjs`).href, + preloadUrl: NodeURL.pathToFileURL(`${__dirname}/preview-pick-preload.cjs`).href, }; }), }); diff --git a/apps/desktop/src/preview/PlaywrightInjectedRuntime.ts b/apps/desktop/src/preview/PlaywrightInjectedRuntime.ts index 1a4dce14f87..e940ce55906 100644 --- a/apps/desktop/src/preview/PlaywrightInjectedRuntime.ts +++ b/apps/desktop/src/preview/PlaywrightInjectedRuntime.ts @@ -1,14 +1,14 @@ // @effect-diagnostics nodeBuiltinImport:off - Extracts Playwright's installed Node bundle for browser injection. -import { readFile } from "node:fs/promises"; -import { createRequire } from "node:module"; -import { dirname, join } from "node:path"; -import { runInNewContext } from "node:vm"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeModule from "node:module"; +import * as NodePath from "node:path"; +import * as NodeVM from "node:vm"; import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; -const require = createRequire(import.meta.url); +const require = NodeModule.createRequire(import.meta.url); const encodeUnknownJson = Schema.encodeUnknownEffect(Schema.UnknownFromJsonString); export class PlaywrightInjectedRuntimeError extends Data.TaggedError( @@ -32,7 +32,11 @@ export const playwrightInjectedRuntimeSource = Effect.fn("PlaywrightInjectedRunt catch: (cause) => fail("resolvePackage", cause), }); const coreBundle = yield* Effect.tryPromise({ - try: () => readFile(join(dirname(packageJsonPath), "lib/coreBundle.js"), "utf8"), + try: () => + NodeFSP.readFile( + NodePath.join(NodePath.dirname(packageJsonPath), "lib/coreBundle.js"), + "utf8", + ), catch: (cause) => fail("readCoreBundle", cause), }); const marker = "source3 = "; @@ -53,7 +57,7 @@ export const playwrightInjectedRuntimeSource = Effect.fn("PlaywrightInjectedRunt } const literal = coreBundle.slice(literalStart, literalEnd); const source = yield* Effect.try({ - try: () => runInNewContext(literal, Object.create(null), { timeout: 1_000 }), + try: () => NodeVM.runInNewContext(literal, Object.create(null), { timeout: 1_000 }), catch: (cause) => fail("evaluateSourceLiteral", cause), }); if (typeof source !== "string" || source.length < 100_000) { diff --git a/apps/mobile/modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs b/apps/mobile/modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs index 87f17c28e0f..2c2cc43bc65 100644 --- a/apps/mobile/modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs +++ b/apps/mobile/modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs @@ -1,16 +1,19 @@ -import { execFileSync } from "node:child_process"; -import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; import { getBuiltInSpriteSheet } from "@pierre/trees"; -const scriptDirectory = dirname(fileURLToPath(import.meta.url)); -const moduleDirectory = resolve(scriptDirectory, ".."); -const repositoryRoot = resolve(moduleDirectory, "../../../.."); -const outputDirectory = join(moduleDirectory, "assets/file-icons"); -const generatedModulePath = join(moduleDirectory, "src/markdownFileIcons.generated.ts"); -const webIconSource = readFileSync(join(repositoryRoot, "apps/web/src/pierre-icons.ts"), "utf8"); +const scriptDirectory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const moduleDirectory = NodePath.resolve(scriptDirectory, ".."); +const repositoryRoot = NodePath.resolve(moduleDirectory, "../../../.."); +const outputDirectory = NodePath.join(moduleDirectory, "assets/file-icons"); +const generatedModulePath = NodePath.join(moduleDirectory, "src/markdownFileIcons.generated.ts"); +const webIconSource = NodeFS.readFileSync( + NodePath.join(repositoryRoot, "apps/web/src/pierre-icons.ts"), + "utf8", +); const customSprite = webIconSource.match(/const T3_FILE_ICON_SPRITE = `([\s\S]*?)`;/)?.[1]; if (!customSprite) { @@ -95,20 +98,20 @@ function symbolFromSprite(sprite, id) { } function renderIcon(token, symbol, color) { - const svgPath = join(outputDirectory, `.pierre-${token}.svg`); - const pngPath = join(outputDirectory, `pierre_${token}.png`); - writeFileSync( + const svgPath = NodePath.join(outputDirectory, `.pierre-${token}.svg`); + const pngPath = NodePath.join(outputDirectory, `pierre_${token}.png`); + NodeFS.writeFileSync( svgPath, `${symbol.body}`, ); - execFileSync("sips", ["-s", "format", "png", svgPath, "--out", pngPath], { + NodeChildProcess.execFileSync("sips", ["-s", "format", "png", svgPath, "--out", pngPath], { stdio: "ignore", }); - rmSync(svgPath); + NodeFS.rmSync(svgPath); } -rmSync(outputDirectory, { recursive: true, force: true }); -mkdirSync(outputDirectory, { recursive: true }); +NodeFS.rmSync(outputDirectory, { recursive: true, force: true }); +NodeFS.mkdirSync(outputDirectory, { recursive: true }); const builtInSprite = getBuiltInSpriteSheet("complete"); const builtInTokens = [...builtInSprite.matchAll(/ ` ${token}: require("../assets/file-icons/pierre_${token}.png"),`) .join("\n")}\n} as const satisfies Readonly>;\n`; -writeFileSync(generatedModulePath, generatedSource); +NodeFS.writeFileSync(generatedModulePath, generatedSource); diff --git a/apps/server/integration/OrchestrationEngineHarness.integration.ts b/apps/server/integration/OrchestrationEngineHarness.integration.ts index 292b267e124..ebc4f984b86 100644 --- a/apps/server/integration/OrchestrationEngineHarness.integration.ts +++ b/apps/server/integration/OrchestrationEngineHarness.integration.ts @@ -1,5 +1,5 @@ // @effect-diagnostics nodeBuiltinImport:off -import { execFileSync } from "node:child_process"; +import * as NodeChildProcess from "node:child_process"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { @@ -82,7 +82,7 @@ import * as AgentAwarenessRelay from "../src/relay/AgentAwarenessRelay.ts"; const decodeCodexSettings = Schema.decodeEffect(CodexSettings); function runGit(cwd: string, args: ReadonlyArray) { - return execFileSync("git", args, { + return NodeChildProcess.execFileSync("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", diff --git a/apps/server/integration/orchestrationEngine.integration.test.ts b/apps/server/integration/orchestrationEngine.integration.test.ts index e79897c740e..ccfb9c46742 100644 --- a/apps/server/integration/orchestrationEngine.integration.test.ts +++ b/apps/server/integration/orchestrationEngine.integration.test.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; import { ApprovalRequestId, @@ -409,7 +409,7 @@ it.live("runs multi-turn file edits and persists checkpoint diffs", () => ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8"); }), }); @@ -456,7 +456,7 @@ it.live("runs multi-turn file edits and persists checkpoint diffs", () => ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8"); }), }); @@ -752,7 +752,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8"); }), }); yield* startTurn({ @@ -811,7 +811,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8"); }), }); yield* startTurn({ @@ -869,7 +869,10 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git ), true, ); - assert.equal(fs.readFileSync(path.join(harness.workspaceDir, "README.md"), "utf8"), "v2\n"); + assert.equal( + NodeFS.readFileSync(NodePath.join(harness.workspaceDir, "README.md"), "utf8"), + "v2\n", + ); assert.equal( gitRefExists(harness.workspaceDir, checkpointRefForThreadTurn(THREAD_ID, 2)), false, @@ -1332,7 +1335,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state", ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8"); }), }); @@ -1390,7 +1393,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state", ], mutateWorkspace: ({ cwd }) => Effect.sync(() => { - fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8"); }), }); diff --git a/apps/server/scripts/acp-mock-agent.ts b/apps/server/scripts/acp-mock-agent.ts index 2b5da74eef0..0d89775844d 100644 --- a/apps/server/scripts/acp-mock-agent.ts +++ b/apps/server/scripts/acp-mock-agent.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node // @effect-diagnostics nodeBuiltinImport:off -import { appendFileSync } from "node:fs"; +import * as NodeFS from "node:fs"; import * as Effect from "effect/Effect"; @@ -42,7 +42,7 @@ function logExit(reason: string): void { if (!exitLogPath) { return; } - appendFileSync(exitLogPath, `${reason}\n`, "utf8"); + NodeFS.appendFileSync(exitLogPath, `${reason}\n`, "utf8"); } process.once("SIGTERM", () => { @@ -693,7 +693,7 @@ const program = Effect.gen(function* () { } const payload = event.payload; return Effect.sync(() => { - appendFileSync( + NodeFS.appendFileSync( requestLogPath, payload.endsWith("\n") ? payload : `${payload}\n`, "utf8", diff --git a/apps/server/scripts/cursor-acp-model-mismatch-probe.ts b/apps/server/scripts/cursor-acp-model-mismatch-probe.ts index 31f2ef6f1f7..b36c2b2d496 100644 --- a/apps/server/scripts/cursor-acp-model-mismatch-probe.ts +++ b/apps/server/scripts/cursor-acp-model-mismatch-probe.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; -import process from "node:process"; -import readline from "node:readline"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeProcess from "node:process"; +import * as NodeReadline from "node:readline"; import * as NodeTimers from "node:timers"; import { resolveSpawnCommand } from "@t3tools/shared/shell"; import * as Effect from "effect/Effect"; @@ -56,19 +56,19 @@ type PendingRequest = { reject: (error: Error) => void; }; -const targetCwd = process.argv[2] ?? process.cwd(); -const targetModel = process.argv[3] ?? "gpt-5.4"; -const promptText = process.argv[4] ?? "helo"; -const targetReasoning = process.env.CURSOR_REASONING ?? ""; -const targetContext = process.env.CURSOR_CONTEXT ?? ""; -const targetFast = process.env.CURSOR_FAST ?? ""; -const agentBin = process.env.CURSOR_AGENT_BIN ?? "agent"; -const promptWaitMs = Number(process.env.CURSOR_PROMPT_WAIT_MS ?? "4000"); -const requestTimeoutMs = Number(process.env.CURSOR_REQUEST_TIMEOUT_MS ?? "20000"); +const targetCwd = NodeProcess.argv[2] ?? NodeProcess.cwd(); +const targetModel = NodeProcess.argv[3] ?? "gpt-5.4"; +const promptText = NodeProcess.argv[4] ?? "helo"; +const targetReasoning = NodeProcess.env.CURSOR_REASONING ?? ""; +const targetContext = NodeProcess.env.CURSOR_CONTEXT ?? ""; +const targetFast = NodeProcess.env.CURSOR_FAST ?? ""; +const agentBin = NodeProcess.env.CURSOR_AGENT_BIN ?? "agent"; +const promptWaitMs = Number(NodeProcess.env.CURSOR_PROMPT_WAIT_MS ?? "4000"); +const requestTimeoutMs = Number(NodeProcess.env.CURSOR_REQUEST_TIMEOUT_MS ?? "20000"); function logSection(title: string, value: unknown) { - process.stdout.write(`\n=== ${title} ===\n`); - process.stdout.write(`${JSON.stringify(value, null, 2)}\n`); + NodeProcess.stdout.write(`\n=== ${title} ===\n`); + NodeProcess.stdout.write(`${JSON.stringify(value, null, 2)}\n`); } function fail(message: string): never { @@ -124,18 +124,18 @@ function sleep(ms: number) { } class JsonRpcChild { - readonly child: ChildProcessWithoutNullStreams; + readonly child: NodeChildProcess.ChildProcessWithoutNullStreams; readonly pending = new Map(); nextId = 1; closed = false; constructor(bin: string, args: string[], cwd: string) { const spawnCommand = Effect.runSync(resolveSpawnCommand(bin, args)); - this.child = spawn(spawnCommand.command, spawnCommand.args, { + this.child = NodeChildProcess.spawn(spawnCommand.command, spawnCommand.args, { cwd, shell: spawnCommand.shell, stdio: ["pipe", "pipe", "pipe"], - env: process.env, + env: NodeProcess.env, }); this.child.on("exit", (code, signal) => { @@ -155,14 +155,14 @@ class JsonRpcChild { this.pending.clear(); }); - const stdout = readline.createInterface({ input: this.child.stdout }); + const stdout = NodeReadline.createInterface({ input: this.child.stdout }); stdout.on("line", (line) => { void this.handleStdoutLine(line); }); - const stderr = readline.createInterface({ input: this.child.stderr }); + const stderr = NodeReadline.createInterface({ input: this.child.stderr }); stderr.on("line", (line) => { - process.stdout.write(`[stderr] ${line}\n`); + NodeProcess.stdout.write(`[stderr] ${line}\n`); }); } @@ -175,7 +175,7 @@ class JsonRpcChild { headers: [], ...message, }); - process.stdout.write(`>>> ${payload}\n`); + NodeProcess.stdout.write(`>>> ${payload}\n`); this.child.stdin.write(`${payload}\n`); } @@ -240,13 +240,13 @@ class JsonRpcChild { return; } - process.stdout.write(`<<< ${line}\n`); + NodeProcess.stdout.write(`<<< ${line}\n`); let message: JsonRpcMessage; try { message = JSON.parse(line) as JsonRpcMessage; } catch (error) { - process.stdout.write(`[parse-error] ${(error as Error).message}\n`); + NodeProcess.stdout.write(`[parse-error] ${(error as Error).message}\n`); return; } @@ -435,7 +435,7 @@ async function main() { } void main().catch((error: unknown) => { - process.stderr.write( + NodeProcess.stderr.write( `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`, ); process.exitCode = 1; diff --git a/apps/server/src/attachmentPaths.ts b/apps/server/src/attachmentPaths.ts index dc7db435426..a5216f76b98 100644 --- a/apps/server/src/attachmentPaths.ts +++ b/apps/server/src/attachmentPaths.ts @@ -1,5 +1,5 @@ // @effect-diagnostics nodeBuiltinImport:off -import NodePath from "node:path"; +import * as NodePath from "node:path"; export function normalizeAttachmentRelativePath(rawRelativePath: string): string | null { const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, ""); diff --git a/apps/server/src/attachmentStore.test.ts b/apps/server/src/attachmentStore.test.ts index 7703902105a..e21d9cf62cf 100644 --- a/apps/server/src/attachmentStore.test.ts +++ b/apps/server/src/attachmentStore.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { describe, expect, it } from "vite-plus/test"; @@ -45,11 +45,13 @@ describe("attachmentStore", () => { }); it("resolves attachment path by id using the extension that exists on disk", () => { - const attachmentsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-attachment-store-")); + const attachmentsDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"), + ); try { const attachmentId = "thread-1-attachment"; - const pngPath = path.join(attachmentsDir, `${attachmentId}.png`); - fs.writeFileSync(pngPath, Buffer.from("hello")); + const pngPath = NodePath.join(attachmentsDir, `${attachmentId}.png`); + NodeFS.writeFileSync(pngPath, Buffer.from("hello")); const resolved = resolveAttachmentPathById({ attachmentsDir, @@ -57,12 +59,14 @@ describe("attachmentStore", () => { }); expect(resolved).toBe(pngPath); } finally { - fs.rmSync(attachmentsDir, { recursive: true, force: true }); + NodeFS.rmSync(attachmentsDir, { recursive: true, force: true }); } }); it("returns null when no attachment file exists for the id", () => { - const attachmentsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-attachment-store-")); + const attachmentsDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"), + ); try { const resolved = resolveAttachmentPathById({ attachmentsDir, @@ -70,7 +74,7 @@ describe("attachmentStore", () => { }); expect(resolved).toBeNull(); } finally { - fs.rmSync(attachmentsDir, { recursive: true, force: true }); + NodeFS.rmSync(attachmentsDir, { recursive: true, force: true }); } }); }); diff --git a/apps/server/src/attachmentStore.ts b/apps/server/src/attachmentStore.ts index 1e8dd93f603..3d5b531db21 100644 --- a/apps/server/src/attachmentStore.ts +++ b/apps/server/src/attachmentStore.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import { randomUUID } from "node:crypto"; -import { existsSync } from "node:fs"; +import * as NodeCrypto from "node:crypto"; +import * as NodeFS from "node:fs"; import type { ChatAttachment } from "@t3tools/contracts"; @@ -39,7 +39,7 @@ export function createAttachmentId(threadId: string): string | null { if (!threadSegment) { return null; } - return `${threadSegment}-${randomUUID()}`; + return `${threadSegment}-${NodeCrypto.randomUUID()}`; } export function parseThreadSegmentFromAttachmentId(attachmentId: string): string | null { @@ -89,7 +89,7 @@ export function resolveAttachmentPathById(input: { attachmentsDir: input.attachmentsDir, relativePath: `${normalizedId}${extension}`, }); - if (maybePath && existsSync(maybePath)) { + if (maybePath && NodeFS.existsSync(maybePath)) { return maybePath; } } diff --git a/apps/server/src/auth/utils.ts b/apps/server/src/auth/utils.ts index 7260ac7c54d..39f04988ac5 100644 --- a/apps/server/src/auth/utils.ts +++ b/apps/server/src/auth/utils.ts @@ -4,7 +4,7 @@ import type { AuthClientPresentationMetadata, } from "@t3tools/contracts"; import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; -import * as Crypto from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import * as Encoding from "effect/Encoding"; import * as Result from "effect/Result"; @@ -32,7 +32,7 @@ export function base64UrlDecodeUtf8(input: string): string { } export function signPayload(payload: string, secret: Uint8Array): string { - return Crypto.createHmac("sha256", Buffer.from(secret)).update(payload).digest("base64url"); + return NodeCrypto.createHmac("sha256", Buffer.from(secret)).update(payload).digest("base64url"); } export function timingSafeEqualBase64Url(left: string, right: string): boolean { @@ -41,7 +41,7 @@ export function timingSafeEqualBase64Url(left: string, right: string): boolean { if (leftBuffer.length !== rightBuffer.length) { return false; } - return Crypto.timingSafeEqual(leftBuffer, rightBuffer); + return NodeCrypto.timingSafeEqual(leftBuffer, rightBuffer); } function normalizeNonEmptyString(value: string | null | undefined): string | undefined { diff --git a/apps/server/src/bin.test.ts b/apps/server/src/bin.test.ts index d71bc83f94e..5c713ff2be7 100644 --- a/apps/server/src/bin.test.ts +++ b/apps/server/src/bin.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off - CLI integration exercises Node HTTP and filesystem boundaries. -import { createServer } from "node:http"; -import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; +import * as NodeHttp from "node:http"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -124,7 +124,7 @@ const withLiveProjectCliServer = (baseDir: string, run: () => Effect.Ef ), Layer.provideMerge(makeProjectPersistenceLayer(config)), Layer.provideMerge( - NodeHttpServer.layer(createServer, { + NodeHttpServer.layer(NodeHttp.createServer, { host: "127.0.0.1", port: 0, }), @@ -197,7 +197,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("reports fresh headless connect state without requiring local configuration", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-status-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-status-test-"), + ); const { output } = yield* captureStdout( runConnectCli(["connect", "status", "--base-dir", baseDir, "--json"]), ); @@ -220,7 +222,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("reports actionable human-readable headless connect state", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-status-human-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-status-human-test-"), + ); const { output } = yield* captureStdout( runConnectCli(["connect", "status", "--base-dir", baseDir]), ); @@ -234,11 +238,13 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("logs in to headless connect without enabling access", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-login-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-login-test-"), + ); const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined); - mkdirSync(secretsDir, { recursive: true }); - writeFileSync( - join(secretsDir, "cloud-cli-oauth-token.bin"), + NodeFS.mkdirSync(secretsDir, { recursive: true }); + NodeFS.writeFileSync( + NodePath.join(secretsDir, "cloud-cli-oauth-token.bin"), // @effect-diagnostics-next-line preferSchemaOverJson:off - Test fixture matches the persisted CLI token representation. JSON.stringify({ accessToken: "access-token", @@ -267,7 +273,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("disables headless connect without a running server", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-unlink-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-unlink-test-"), + ); const { output } = yield* captureStdout( runConnectCli(["connect", "unlink", "--base-dir", baseDir]), ); @@ -278,24 +286,28 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("logs out of headless connect and removes the stored CLI authorization", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-logout-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-logout-test-"), + ); const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined); - const tokenPath = join(secretsDir, "cloud-cli-oauth-token.bin"); - mkdirSync(secretsDir, { recursive: true }); - writeFileSync(tokenPath, "invalid persisted token"); + const tokenPath = NodePath.join(secretsDir, "cloud-cli-oauth-token.bin"); + NodeFS.mkdirSync(secretsDir, { recursive: true }); + NodeFS.writeFileSync(tokenPath, "invalid persisted token"); const { output } = yield* captureStdout( runConnectCli(["connect", "logout", "--base-dir", baseDir]), ); assert.equal(output, "Signed out of T3 Connect locally."); - assert.isFalse(existsSync(tokenPath)); + assert.isFalse(NodeFS.existsSync(tokenPath)); }), ); it.effect("executes auth pairing subcommands and redacts secrets from list output", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-auth-pairing-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-auth-pairing-test-"), + ); const createdOutput = yield* captureStdout( runCli(["auth", "pairing", "create", "--base-dir", baseDir, "--json"]), @@ -325,7 +337,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("executes auth session subcommands and redacts secrets from list output", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-auth-session-test-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-auth-session-test-"), + ); const issuedOutput = yield* captureStdout( runCli(["auth", "session", "issue", "--base-dir", baseDir, "--json"]), @@ -400,8 +414,12 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("adds, renames, and removes projects offline through the orchestration engine", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-projects-offline-test-")); - const workspaceRoot = mkdtempSync(join(tmpdir(), "t3-cli-projects-workspace-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-offline-test-"), + ); + const workspaceRoot = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-workspace-"), + ); yield* runCliWithRuntime([ "project", @@ -444,8 +462,12 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("routes project commands through a running server when runtime state is present", () => Effect.gen(function* () { - const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-projects-live-test-")); - const workspaceRoot = mkdtempSync(join(tmpdir(), "t3-cli-projects-live-workspace-")); + const baseDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-live-test-"), + ); + const workspaceRoot = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-live-workspace-"), + ); yield* withLiveProjectCliServer(baseDir, () => Effect.gen(function* () { @@ -472,8 +494,8 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { it.effect("rejects dev-url on project commands", () => Effect.gen(function* () { - const workspaceRoot = mkdtempSync( - join(tmpdir(), "t3-cli-projects-unknown-option-workspace-"), + const workspaceRoot = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-unknown-option-workspace-"), ); const error = yield* runCliWithRuntime([ "project", diff --git a/apps/server/src/bootstrap.test.ts b/apps/server/src/bootstrap.test.ts index a3bbcc66d34..84cf85c3213 100644 --- a/apps/server/src/bootstrap.test.ts +++ b/apps/server/src/bootstrap.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as NFS from "node:fs"; -import * as path from "node:path"; -import { execFileSync, spawn } from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeChildProcess from "node:child_process"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import * as FileSystem from "effect/FileSystem"; @@ -53,8 +53,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => { ); const fd = yield* Effect.acquireRelease( - Effect.sync(() => NFS.openSync(filePath, "r")), - (fd) => Effect.sync(() => NFS.closeSync(fd)), + Effect.sync(() => NodeFS.openSync(filePath, "r")), + (fd) => Effect.sync(() => NodeFS.closeSync(fd)), ); const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 }); @@ -78,7 +78,7 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => { // so the stream owns the fd lifecycle and closes it asynchronously on end. // Attempting to also close it synchronously in a finalizer races with the // stream's async close and produces an uncaught EBADF. - const fd = NFS.openSync(filePath, "r"); + const fd = NodeFS.openSync(filePath, "r"); openSyncInterceptor.failPath = `/proc/self/fd/${fd}`; try { @@ -96,8 +96,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => { it.effect("returns none when the fd is unavailable", () => Effect.gen(function* () { - const fd = NFS.openSync("/dev/null", "r"); - NFS.closeSync(fd); + const fd = NodeFS.openSync("/dev/null", "r"); + NodeFS.closeSync(fd); const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 }); assertNone(payload); @@ -108,13 +108,13 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => { Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-bootstrap-" }); - const fifoPath = path.join(tempDir, "bootstrap.pipe"); + const fifoPath = NodePath.join(tempDir, "bootstrap.pipe"); - yield* Effect.sync(() => execFileSync("mkfifo", [fifoPath])); + yield* Effect.sync(() => NodeChildProcess.execFileSync("mkfifo", [fifoPath])); const _writer = yield* Effect.acquireRelease( Effect.sync(() => - spawn("sh", ["-c", 'exec 3>"$1"; sleep 60', "sh", fifoPath], { + NodeChildProcess.spawn("sh", ["-c", 'exec 3>"$1"; sleep 60', "sh", fifoPath], { stdio: ["ignore", "ignore", "ignore"], }), ), @@ -125,8 +125,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => { ); const fd = yield* Effect.acquireRelease( - Effect.sync(() => NFS.openSync(fifoPath, "r")), - (fd) => Effect.sync(() => NFS.closeSync(fd)), + Effect.sync(() => NodeFS.openSync(fifoPath, "r")), + (fd) => Effect.sync(() => NodeFS.closeSync(fd)), ); const fiber = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { diff --git a/apps/server/src/bootstrap.ts b/apps/server/src/bootstrap.ts index 83d1d337888..1114ad8af90 100644 --- a/apps/server/src/bootstrap.ts +++ b/apps/server/src/bootstrap.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as NFS from "node:fs"; -import * as Net from "node:net"; -import * as readline from "node:readline"; -import type { Readable } from "node:stream"; +import * as NodeFS from "node:fs"; +import * as NodeNet from "node:net"; +import * as NodeReadline from "node:readline"; +import type * as NodeStream from "node:stream"; import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; @@ -33,7 +33,7 @@ export const readBootstrapEnvelope = Effect.fn("readBootstrapEnvelope")(function const timeoutMs = options?.timeoutMs ?? 1000; return yield* Effect.callback, BootstrapError>((resume) => { - const input = readline.createInterface({ + const input = NodeReadline.createInterface({ input: stream, crlfDelay: Infinity, }); @@ -96,7 +96,7 @@ const isUnavailableBootstrapFdError = Predicate.compose( const isFdReady = (fd: number) => Effect.try({ - try: () => NFS.fstatSync(fd), + try: () => NodeFS.fstatSync(fd), catch: (error) => new BootstrapError({ message: "Failed to stat bootstrap fd.", @@ -113,7 +113,7 @@ const isFdReady = (fd: number) => const makeBootstrapInputStream = (fd: number) => Effect.gen(function* () { const platform = yield* HostProcessPlatform; - return yield* Effect.try({ + return yield* Effect.try({ try: () => { const fdPath = resolveFdPath(fd, platform); if (fdPath === undefined) { @@ -122,8 +122,8 @@ const makeBootstrapInputStream = (fd: number) => let streamFd: number | undefined; try { - streamFd = NFS.openSync(fdPath, "r"); - return NFS.createReadStream("", { + streamFd = NodeFS.openSync(fdPath, "r"); + return NodeFS.createReadStream("", { fd: streamFd, encoding: "utf8", autoClose: true, @@ -131,7 +131,7 @@ const makeBootstrapInputStream = (fd: number) => } catch (error) { if (isBootstrapFdPathDuplicationError(error)) { if (streamFd !== undefined) { - NFS.closeSync(streamFd); + NodeFS.closeSync(streamFd); } return makeDirectBootstrapStream(fd); } @@ -146,15 +146,15 @@ const makeBootstrapInputStream = (fd: number) => }); }); -const makeDirectBootstrapStream = (fd: number): Readable => { +const makeDirectBootstrapStream = (fd: number): NodeStream.Readable => { try { - return NFS.createReadStream("", { + return NodeFS.createReadStream("", { fd, encoding: "utf8", autoClose: true, }); } catch { - const stream = new Net.Socket({ + const stream = new NodeNet.Socket({ fd, readable: true, writable: false, diff --git a/apps/server/src/checkpointing/CheckpointStore.test.ts b/apps/server/src/checkpointing/CheckpointStore.test.ts index d796bdfc4c1..5a60012108b 100644 --- a/apps/server/src/checkpointing/CheckpointStore.test.ts +++ b/apps/server/src/checkpointing/CheckpointStore.test.ts @@ -1,5 +1,5 @@ // @effect-diagnostics nodeBuiltinImport:off -import path from "node:path"; +import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -80,7 +80,7 @@ function initRepoWithCommit( yield* git(cwd, ["init"]); yield* git(cwd, ["config", "user.email", "test@test.com"]); yield* git(cwd, ["config", "user.name", "Test"]); - yield* writeTextFile(path.join(cwd, "README.md"), "# test\n"); + yield* writeTextFile(NodePath.join(cwd, "README.md"), "# test\n"); yield* git(cwd, ["add", "."]); yield* git(cwd, ["commit", "-m", "initial commit"]); }); @@ -107,7 +107,7 @@ it.layer(TestLayer)("CheckpointStore.layer", (it) => { cwd: tmp, checkpointRef: fromCheckpointRef, }); - yield* writeTextFile(path.join(tmp, "README.md"), buildLargeText()); + yield* writeTextFile(NodePath.join(tmp, "README.md"), buildLargeText()); yield* checkpointStore.captureCheckpoint({ cwd: tmp, checkpointRef: toCheckpointRef, @@ -135,7 +135,7 @@ it.layer(TestLayer)("CheckpointStore.layer", (it) => { const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0); const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1); - const componentPath = path.join(tmp, "Component.tsx"); + const componentPath = NodePath.join(tmp, "Component.tsx"); yield* writeTextFile( componentPath, [ diff --git a/apps/server/src/cloud/CliTokenManager.ts b/apps/server/src/cloud/CliTokenManager.ts index f2ad5e621ec..00709370b26 100644 --- a/apps/server/src/cloud/CliTokenManager.ts +++ b/apps/server/src/cloud/CliTokenManager.ts @@ -1,5 +1,5 @@ // @effect-diagnostics nodeBuiltinImport:off - The CLI loopback OAuth callback is a Node HTTP boundary. -import { createServer } from "node:http"; +import * as NodeHttp from "node:http"; import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import * as Clock from "effect/Clock"; @@ -206,7 +206,7 @@ export const make = Effect.gen(function* () { disableLogger: true, }).pipe( Layer.provide( - NodeHttpServer.layer(createServer, { + NodeHttpServer.layer(NodeHttp.createServer, { host: "127.0.0.1", port: 34338, disablePreemptiveShutdown: true, diff --git a/apps/server/src/cloud/http.ts b/apps/server/src/cloud/http.ts index 64c87f3487c..71be9f376d8 100644 --- a/apps/server/src/cloud/http.ts +++ b/apps/server/src/cloud/http.ts @@ -1,4 +1,4 @@ -import { createPublicKey } from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import { AuthRelayReadScope, AuthRelayWriteScope, @@ -152,7 +152,7 @@ function validateCloudMintPublicKey( publicKey: string, ): Effect.Effect { return Effect.try({ - try: () => createPublicKey(publicKey.replace(/\\n/g, "\n")), + try: () => NodeCrypto.createPublicKey(publicKey.replace(/\\n/g, "\n")), catch: () => new EnvironmentHttpBadRequestError({ message: "Cloud mint public key must be a valid Ed25519 public key.", diff --git a/apps/server/src/environment/ServerEnvironment.test.ts b/apps/server/src/environment/ServerEnvironment.test.ts index 3b0cef13bf9..665447589eb 100644 --- a/apps/server/src/environment/ServerEnvironment.test.ts +++ b/apps/server/src/environment/ServerEnvironment.test.ts @@ -1,5 +1,5 @@ // @effect-diagnostics nodeBuiltinImport:off -import { dirname } from "node:path"; +import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; @@ -76,7 +76,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { }); const serverConfig = yield* makeServerConfig(baseDir); const environmentIdPath = serverConfig.environmentIdPath; - yield* fileSystem.makeDirectory(dirname(environmentIdPath), { recursive: true }); + yield* fileSystem.makeDirectory(NodePath.dirname(environmentIdPath), { recursive: true }); yield* fileSystem.writeFileString(environmentIdPath, "persisted-environment-id\n"); const writeAttempts: string[] = []; const failingFileSystemLayer = FileSystem.layerNoop({ diff --git a/apps/server/src/git/GitManager.test.ts b/apps/server/src/git/GitManager.test.ts index 3ff9a42390e..81b82d7de30 100644 --- a/apps/server/src/git/GitManager.test.ts +++ b/apps/server/src/git/GitManager.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import path from "node:path"; -import { spawnSync } from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeChildProcess from "node:child_process"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -164,7 +164,7 @@ function normalizeFakePullRequestSummary(raw: unknown): GitHubCli.GitHubPullRequ } function runGitSyncForFakeGh(cwd: string, args: readonly string[]): void { - const result = spawnSync("git", args, { + const result = NodeChildProcess.spawnSync("git", args, { cwd, encoding: "utf8", }); @@ -254,7 +254,7 @@ function initRepo( yield* runGit(cwd, ["init", "--initial-branch=main"]); yield* runGit(cwd, ["config", "user.email", "test@example.com"]); yield* runGit(cwd, ["config", "user.name", "Test User"]); - yield* fs.writeFileString(path.join(cwd, "README.md"), "hello\n"); + yield* fs.writeFileString(NodePath.join(cwd, "README.md"), "hello\n"); yield* runGit(cwd, ["add", "README.md"]); yield* runGit(cwd, ["commit", "-m", "Initial commit"]); }); @@ -459,7 +459,7 @@ function createGitHubCliWithFakeGh(scenario: FakeGhScenario = {}): { try: () => { const headBranch = scenario.pullRequest?.headRefName; if (headBranch) { - const existingBranch = spawnSync( + const existingBranch = NodeChildProcess.spawnSync( "git", ["show-ref", "--verify", "--quiet", `refs/heads/${headBranch}`], { @@ -916,7 +916,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { it.effect("status returns an explicit non-repo result for deleted directories", () => Effect.gen(function* () { const rootDir = yield* makeTempDir("t3code-git-manager-missing-dir-"); - const cwd = path.join(rootDir, "deleted-repo"); + const cwd = NodePath.join(rootDir, "deleted-repo"); yield* makeDirectory(cwd); yield* removePath(cwd); const { manager } = yield* makeManager(); @@ -1026,7 +1026,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const forkDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "statemachine"]); - fs.writeFileSync(path.join(repoDir, "fork-pr.txt"), "fork pr\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "fork-pr.txt"), "fork pr\n"); yield* runGit(repoDir, ["add", "fork-pr.txt"]); yield* runGit(repoDir, ["commit", "-m", "Fork PR branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "statemachine"]); @@ -1348,7 +1348,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "README.md"), "hello\nworld\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\nworld\n"); const { manager } = yield* makeManager(); const result = yield* runStackedAction(manager, { @@ -1383,7 +1383,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "README.md"), "hello\ncustom\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\ncustom\n"); let generatedCount = 0; const { manager } = yield* makeManager({ @@ -1426,8 +1426,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "a.txt"), "file a\n"); - fs.writeFileSync(path.join(repoDir, "b.txt"), "file b\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "a.txt"), "file a\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "b.txt"), "file b\n"); const { manager } = yield* makeManager(); const result = yield* runStackedAction(manager, { @@ -1454,7 +1454,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); - fs.writeFileSync(path.join(repoDir, "README.md"), "hello\nfeature-branch\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\nfeature-branch\n"); let generatedCount = 0; const { manager } = yield* makeManager({ @@ -1514,7 +1514,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "README.md"), "hello\ncustom-feature\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\ncustom-feature\n"); let generatedCount = 0; const { manager } = yield* makeManager({ @@ -1597,7 +1597,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/stacked-flow"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n"); const { manager } = yield* makeManager(); const result = yield* runStackedAction(manager, { @@ -1626,7 +1626,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/no-upstream-pr"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n"); const { manager, ghCalls } = yield* makeManager({ ghScenario: { @@ -1697,7 +1697,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/push-only"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "push-only.txt"), "push only\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "push-only.txt"), "push only\n"); yield* runGit(repoDir, ["add", "push-only.txt"]); yield* runGit(repoDir, ["commit", "-m", "Push only branch"]); @@ -1725,11 +1725,11 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/push-dirty"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "push-dirty.txt"), "push dirty\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "push-dirty.txt"), "push dirty\n"); yield* runGit(repoDir, ["add", "push-dirty.txt"]); yield* runGit(repoDir, ["commit", "-m", "Push dirty branch"]); - fs.mkdirSync(path.join(repoDir, ".vercel")); - fs.writeFileSync(path.join(repoDir, ".vercel", "project.json"), "{}\n"); + NodeFS.mkdirSync(NodePath.join(repoDir, ".vercel")); + NodeFS.writeFileSync(NodePath.join(repoDir, ".vercel", "project.json"), "{}\n"); const { manager } = yield* makeManager(); const result = yield* runStackedAction(manager, { @@ -1760,7 +1760,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/create-pr-only"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "create-pr-only.txt"), "create pr\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "create-pr-only.txt"), "create pr\n"); yield* runGit(repoDir, ["add", "create-pr-only.txt"]); yield* runGit(repoDir, ["commit", "-m", "Create PR only branch"]); @@ -1805,7 +1805,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); yield* runGit(repoDir, ["checkout", "-b", "feature/provider-fallback"]); - fs.writeFileSync(path.join(repoDir, "provider-fallback.txt"), "fallback\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "provider-fallback.txt"), "fallback\n"); yield* runGit(repoDir, ["add", "provider-fallback.txt"]); yield* runGit(repoDir, ["commit", "-m", "Provider fallback"]); const remoteDir = yield* createBareRemote(); @@ -1982,7 +1982,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "main"]); yield* runGit(repoDir, ["branch", "-D", "effect-atom"]); yield* runGit(repoDir, ["checkout", "--track", "my-org/upstream/effect-atom"]); - fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n"); yield* runGit(repoDir, ["add", "changes.txt"]); yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); @@ -2200,7 +2200,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature-create-pr"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n"); yield* runGit(repoDir, ["add", "changes.txt"]); yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature-create-pr"]); @@ -2252,7 +2252,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(peerDir, ["clone", remoteDir, "."]); yield* runGit(peerDir, ["config", "user.email", "peer@example.com"]); yield* runGit(peerDir, ["config", "user.name", "Peer User"]); - fs.writeFileSync(path.join(peerDir, "remote.txt"), "remote\n"); + NodeFS.writeFileSync(NodePath.join(peerDir, "remote.txt"), "remote\n"); yield* runGit(peerDir, ["add", "remote.txt"]); yield* runGit(peerDir, ["commit", "-m", "Remote base commit"]); yield* runGit(peerDir, ["push", "origin", "main"]); @@ -2265,7 +2265,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { "feature/remote-base", "origin/main", ]); - fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n"); yield* runGit(repoDir, ["add", "feature.txt"]); yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/remote-base"]); @@ -2304,7 +2304,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/no-fork-match"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n"); yield* runGit(repoDir, ["add", "changes.txt"]); yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/no-fork-match"]); @@ -2376,7 +2376,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const forkDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "statemachine"]); - fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n"); yield* runGit(repoDir, ["add", "changes.txt"]); yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "statemachine"]); @@ -2556,7 +2556,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local"]); - fs.writeFileSync(path.join(repoDir, "local.txt"), "local\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "local.txt"), "local\n"); yield* runGit(repoDir, ["add", "local.txt"]); yield* runGit(repoDir, ["commit", "-m", "Local PR branch"]); @@ -2597,7 +2597,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-upstream"]); - fs.writeFileSync(path.join(repoDir, "upstream.txt"), "upstream\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "upstream.txt"), "upstream\n"); yield* runGit(repoDir, ["add", "upstream.txt"]); yield* runGit(repoDir, ["commit", "-m", "Local upstream PR branch"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-local-upstream"]); @@ -2655,7 +2655,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-no-head-repo"]); - fs.writeFileSync(path.join(repoDir, "no-head-repo.txt"), "upstream\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "no-head-repo.txt"), "upstream\n"); yield* runGit(repoDir, ["add", "no-head-repo.txt"]); yield* runGit(repoDir, ["commit", "-m", "Local PR branch without repo metadata"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-local-no-head-repo"]); @@ -2702,7 +2702,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-worktree"]); - fs.writeFileSync(path.join(repoDir, "worktree.txt"), "worktree\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "worktree.txt"), "worktree\n"); yield* runGit(repoDir, ["add", "worktree.txt"]); yield* runGit(repoDir, ["commit", "-m", "PR worktree branch"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-worktree"]); @@ -2730,7 +2730,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.branch).toBe("feature/pr-worktree"); expect(result.worktreePath).not.toBeNull(); - expect(fs.existsSync(result.worktreePath as string)).toBe(true); + expect(NodeFS.existsSync(result.worktreePath as string)).toBe(true); const worktreeBranch = (yield* runGit(result.worktreePath as string, [ "branch", "--show-current", @@ -2747,7 +2747,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-worktree-setup"]); - fs.writeFileSync(path.join(repoDir, "setup.txt"), "setup\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "setup.txt"), "setup\n"); yield* runGit(repoDir, ["add", "setup.txt"]); yield* runGit(repoDir, ["commit", "-m", "PR worktree setup branch"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-worktree-setup"]); @@ -2802,7 +2802,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-fork"]); - fs.writeFileSync(path.join(repoDir, "fork.txt"), "fork\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "fork.txt"), "fork\n"); yield* runGit(repoDir, ["add", "fork.txt"]); yield* runGit(repoDir, ["commit", "-m", "Fork PR branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-fork"]); @@ -2864,7 +2864,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-fork"]); - fs.writeFileSync(path.join(repoDir, "local-fork.txt"), "local fork\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "local-fork.txt"), "local fork\n"); yield* runGit(repoDir, ["add", "local-fork.txt"]); yield* runGit(repoDir, ["commit", "-m", "Local fork PR branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-local-fork"]); @@ -2917,7 +2917,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "binbandit-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "fix/git-action-default-without-origin"]); - fs.writeFileSync(path.join(repoDir, "derived-fork.txt"), "derived fork\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "derived-fork.txt"), "derived fork\n"); yield* runGit(repoDir, ["add", "derived-fork.txt"]); yield* runGit(repoDir, ["commit", "-m", "Derived fork PR branch"]); yield* runGit(repoDir, [ @@ -2969,11 +2969,15 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-existing-worktree"]); - fs.writeFileSync(path.join(repoDir, "existing.txt"), "existing\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "existing.txt"), "existing\n"); yield* runGit(repoDir, ["add", "existing.txt"]); yield* runGit(repoDir, ["commit", "-m", "Existing worktree branch"]); yield* runGit(repoDir, ["checkout", "main"]); - const worktreePath = path.join(repoDir, "..", `pr-existing-${path.basename(repoDir)}`); + const worktreePath = NodePath.join( + repoDir, + "..", + `pr-existing-${NodePath.basename(repoDir)}`, + ); yield* runGit(repoDir, ["worktree", "add", worktreePath, "feature/pr-existing-worktree"]); const setupCalls: ProjectSetupScriptRunner.ProjectSetupScriptRunnerInput[] = []; @@ -3004,8 +3008,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { threadId: asThreadId("thread-pr-existing-worktree"), }); - expect(result.worktreePath && fs.realpathSync.native(result.worktreePath)).toBe( - fs.realpathSync.native(worktreePath), + expect(result.worktreePath && NodeFS.realpathSync.native(result.worktreePath)).toBe( + NodeFS.realpathSync.native(worktreePath), ); expect(result.branch).toBe("feature/pr-existing-worktree"); expect(setupCalls).toHaveLength(0); @@ -3024,7 +3028,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "fork-main-source"]); - fs.writeFileSync(path.join(repoDir, "fork-main.txt"), "fork main\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "fork-main.txt"), "fork main\n"); yield* runGit(repoDir, ["add", "fork-main.txt"]); yield* runGit(repoDir, ["commit", "-m", "Fork main branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "fork-main-source:main"]); @@ -3084,7 +3088,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "fork-main-source"]); - fs.writeFileSync(path.join(repoDir, "fork-main-second.txt"), "fork main second\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "fork-main-second.txt"), "fork main second\n"); yield* runGit(repoDir, ["add", "fork-main-second.txt"]); yield* runGit(repoDir, ["commit", "-m", "Fork main second branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "fork-main-source:main"]); @@ -3142,12 +3146,16 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-reused-fork"]); - fs.writeFileSync(path.join(repoDir, "reused-fork.txt"), "reused fork\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "reused-fork.txt"), "reused fork\n"); yield* runGit(repoDir, ["add", "reused-fork.txt"]); yield* runGit(repoDir, ["commit", "-m", "Reused fork PR branch"]); yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-reused-fork"]); yield* runGit(repoDir, ["checkout", "main"]); - const worktreePath = path.join(repoDir, "..", `pr-reused-fork-${path.basename(repoDir)}`); + const worktreePath = NodePath.join( + repoDir, + "..", + `pr-reused-fork-${NodePath.basename(repoDir)}`, + ); yield* runGit(repoDir, ["worktree", "add", worktreePath, "feature/pr-reused-fork"]); yield* runGit(worktreePath, ["branch", "--unset-upstream"], true); @@ -3179,8 +3187,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { mode: "worktree", }); - expect(result.worktreePath && fs.realpathSync.native(result.worktreePath)).toBe( - fs.realpathSync.native(worktreePath), + expect(result.worktreePath && NodeFS.realpathSync.native(result.worktreePath)).toBe( + NodeFS.realpathSync.native(worktreePath), ); expect( (yield* runGit(worktreePath, ["rev-parse", "--abbrev-ref", "@{upstream}"])).stdout.trim(), @@ -3196,7 +3204,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); yield* runGit(repoDir, ["push", "-u", "origin", "main"]); yield* runGit(repoDir, ["checkout", "-b", "feature/pr-setup-failure"]); - fs.writeFileSync(path.join(repoDir, "setup-failure.txt"), "setup failure\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "setup-failure.txt"), "setup failure\n"); yield* runGit(repoDir, ["add", "setup-failure.txt"]); yield* runGit(repoDir, ["commit", "-m", "PR setup failure branch"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-setup-failure"]); @@ -3236,7 +3244,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.branch).toBe("feature/pr-setup-failure"); expect(result.worktreePath).not.toBeNull(); - expect(fs.existsSync(result.worktreePath as string)).toBe(true); + expect(NodeFS.existsSync(result.worktreePath as string)).toBe(true); }), ); @@ -3276,9 +3284,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "hooked.txt"), "hooked\n"); - fs.writeFileSync( - path.join(repoDir, ".git", "hooks", "pre-commit"), + NodeFS.writeFileSync(NodePath.join(repoDir, "hooked.txt"), "hooked\n"); + NodeFS.writeFileSync( + NodePath.join(repoDir, ".git", "hooks", "pre-commit"), '#!/bin/sh\necho "hook: start" >&2\nsleep 0.05\necho "hook: end" >&2\n', { mode: 0o755 }, ); @@ -3339,9 +3347,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); - fs.writeFileSync(path.join(repoDir, "hook-failure.txt"), "broken\n"); - fs.writeFileSync( - path.join(repoDir, ".git", "hooks", "pre-commit"), + NodeFS.writeFileSync(NodePath.join(repoDir, "hook-failure.txt"), "broken\n"); + NodeFS.writeFileSync( + NodePath.join(repoDir, ".git", "hooks", "pre-commit"), '#!/bin/sh\necho "hook: fail" >&2\nexit 1\n', { mode: 0o755 }, ); @@ -3392,7 +3400,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["checkout", "-b", "feature/pr-only-follow-up"]); const remoteDir = yield* createBareRemote(); yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); - fs.writeFileSync(path.join(repoDir, "pr-only.txt"), "pr only\n"); + NodeFS.writeFileSync(NodePath.join(repoDir, "pr-only.txt"), "pr only\n"); yield* runGit(repoDir, ["add", "pr-only.txt"]); yield* runGit(repoDir, ["commit", "-m", "PR only branch"]); yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-only-follow-up"]); diff --git a/apps/server/src/git/Utils.ts b/apps/server/src/git/Utils.ts index b414daaa0a4..e4a703f4454 100644 --- a/apps/server/src/git/Utils.ts +++ b/apps/server/src/git/Utils.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import { existsSync } from "node:fs"; -import { join } from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; export function isGitRepository(cwd: string): boolean { - return existsSync(join(cwd, ".git")); + return NodeFS.existsSync(NodePath.join(cwd, ".git")); } diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts index 07c543264f7..707c87c43c9 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import { execFileSync } from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import * as NodeChildProcess from "node:child_process"; import { ProviderDriverKind, @@ -198,7 +198,7 @@ async function waitForEvent( } function runGit(cwd: string, args: ReadonlyArray) { - return execFileSync("git", args, { + return NodeChildProcess.execFileSync("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", @@ -206,11 +206,11 @@ function runGit(cwd: string, args: ReadonlyArray) { } function createGitRepository() { - const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "t3-checkpoint-handler-")); + const cwd = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-checkpoint-handler-")); runGit(cwd, ["init", "--initial-branch=main"]); runGit(cwd, ["config", "user.email", "test@example.com"]); runGit(cwd, ["config", "user.name", "Test User"]); - fs.writeFileSync(path.join(cwd, "README.md"), "v1\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v1\n", "utf8"); runGit(cwd, ["add", "."]); runGit(cwd, ["commit", "-m", "Initial"]); return cwd; @@ -267,7 +267,7 @@ describe("CheckpointReactor", () => { while (tempDirs.length > 0) { const dir = tempDirs.pop(); if (dir) { - fs.rmSync(dir, { recursive: true, force: true }); + NodeFS.rmSync(dir, { recursive: true, force: true }); } } }); @@ -395,14 +395,14 @@ describe("CheckpointReactor", () => { checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0), }), ); - fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8"); await runtime.runPromise( checkpointStore.captureCheckpoint({ cwd, checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1), }), ); - fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8"); await runtime.runPromise( checkpointStore.captureCheckpoint({ cwd, @@ -456,7 +456,7 @@ describe("CheckpointReactor", () => { checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0), ); - fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8"); harness.provider.emit({ type: "turn.completed", eventId: EventId.make("evt-turn-completed-1"), @@ -554,7 +554,7 @@ describe("CheckpointReactor", () => { checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0), ); - fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8"); harness.provider.emit({ type: "turn.completed", @@ -628,7 +628,7 @@ describe("CheckpointReactor", () => { checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0), ); - fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8"); harness.provider.emit({ type: "turn.completed", eventId: EventId.make("evt-turn-completed-claude-1"), @@ -761,7 +761,7 @@ describe("CheckpointReactor", () => { }), ); - fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8"); + NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8"); harness.provider.emit({ type: "turn.completed", eventId: EventId.make("evt-turn-completed-missing-provider-cwd"), @@ -829,8 +829,8 @@ describe("CheckpointReactor", () => { }); it("continues processing runtime events after a single checkpoint runtime failure", async () => { - const nonRepositorySessionCwd = fs.mkdtempSync( - path.join(os.tmpdir(), "t3-checkpoint-runtime-non-repo-"), + const nonRepositorySessionCwd = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-checkpoint-runtime-non-repo-"), ); tempDirs.push(nonRepositorySessionCwd); @@ -963,7 +963,7 @@ describe("CheckpointReactor", () => { threadId: ThreadId.make("thread-1"), numTurns: 1, }); - expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n"); + expect(NodeFS.readFileSync(NodePath.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n"); expect( gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2)), ).toBe(false); diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts index 8041bc66dd3..ce464565dc5 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { ModelSelection, @@ -107,11 +107,11 @@ describe("ProviderCommandReactor", () => { } runtime = null; for (const stateDir of createdStateDirs) { - fs.rmSync(stateDir, { recursive: true, force: true }); + NodeFS.rmSync(stateDir, { recursive: true, force: true }); } createdStateDirs.clear(); for (const baseDir of createdBaseDirs) { - fs.rmSync(baseDir, { recursive: true, force: true }); + NodeFS.rmSync(baseDir, { recursive: true, force: true }); } createdBaseDirs.clear(); }); @@ -147,7 +147,8 @@ describe("ProviderCommandReactor", () => { readonly requiresNewThreadForModelChange?: boolean; }) { const now = "2026-01-01T00:00:00.000Z"; - const baseDir = input?.baseDir ?? fs.mkdtempSync(path.join(os.tmpdir(), "t3code-reactor-")); + const baseDir = + input?.baseDir ?? NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-reactor-")); createdBaseDirs.add(baseDir); const { stateDir } = deriveServerPathsSync(baseDir, undefined); createdStateDirs.add(stateDir); diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts index 2aaa7ea9a33..001ba388949 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { OrchestrationReadModel, @@ -198,7 +198,7 @@ describe("ProviderRuntimeIngestion", () => { const tempDirs: string[] = []; function makeTempDir(prefix: string): string { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix)); + const dir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), prefix)); tempDirs.push(dir); return dir; } @@ -213,13 +213,13 @@ describe("ProviderRuntimeIngestion", () => { } runtime = null; for (const dir of tempDirs.splice(0)) { - fs.rmSync(dir, { recursive: true, force: true }); + NodeFS.rmSync(dir, { recursive: true, force: true }); } }); async function createHarness(options?: { serverSettings?: Partial }) { const workspaceRoot = makeTempDir("t3-provider-project-"); - fs.mkdirSync(path.join(workspaceRoot, ".git")); + NodeFS.mkdirSync(NodePath.join(workspaceRoot, ".git")); const provider = createProviderServiceHarness(); const orchestrationLayer = OrchestrationEngineLive.pipe( Layer.provide(OrchestrationProjectionSnapshotQueryLive), diff --git a/apps/server/src/pathExpansion.test.ts b/apps/server/src/pathExpansion.test.ts index a6f004d4e6f..cc7c85786da 100644 --- a/apps/server/src/pathExpansion.test.ts +++ b/apps/server/src/pathExpansion.test.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import { homedir } from "node:os"; -import { join } from "node:path"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { describe, expect, it } from "vite-plus/test"; import { expandHomePath } from "./pathExpansion.ts"; @@ -17,15 +17,15 @@ describe("expandHomePath", () => { }); it("expands a lone tilde to the home directory", () => { - expect(expandHomePath("~")).toBe(homedir()); + expect(expandHomePath("~")).toBe(NodeOS.homedir()); }); it("expands ~/ to a subpath of the home directory", () => { - expect(expandHomePath("~/.codex-work")).toBe(join(homedir(), ".codex-work")); + expect(expandHomePath("~/.codex-work")).toBe(NodePath.join(NodeOS.homedir(), ".codex-work")); }); it("expands a Windows-style ~\\ prefix", () => { - expect(expandHomePath("~\\.codex")).toBe(join(homedir(), ".codex")); + expect(expandHomePath("~\\.codex")).toBe(NodePath.join(NodeOS.homedir(), ".codex")); }); it("does not expand ~user paths", () => { diff --git a/apps/server/src/pathExpansion.ts b/apps/server/src/pathExpansion.ts index 170d83c54d0..bacdaece0b1 100644 --- a/apps/server/src/pathExpansion.ts +++ b/apps/server/src/pathExpansion.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import { homedir } from "node:os"; -import { join } from "node:path"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; /** * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the @@ -16,9 +16,9 @@ import { join } from "node:path"; */ export function expandHomePath(value: string): string { if (!value) return value; - if (value === "~") return homedir(); + if (value === "~") return NodeOS.homedir(); if (value.startsWith("~/") || value.startsWith("~\\")) { - return join(homedir(), value.slice(2)); + return NodePath.join(NodeOS.homedir(), value.slice(2)); } return value; } diff --git a/apps/server/src/persistence/NodeSqliteClient.ts b/apps/server/src/persistence/NodeSqliteClient.ts index fd49edf0529..f3d03e1c695 100644 --- a/apps/server/src/persistence/NodeSqliteClient.ts +++ b/apps/server/src/persistence/NodeSqliteClient.ts @@ -4,7 +4,7 @@ * * @module SqliteClient */ -import { DatabaseSync, type StatementSync } from "node:sqlite"; +import * as NodeSqlite from "node:sqlite"; import * as Cache from "effect/Cache"; import * as Config from "effect/Config"; @@ -69,7 +69,7 @@ const checkNodeSqliteCompat = () => { const makeWithDatabase = Effect.fn("makeWithDatabase")(function* ( options: SqliteClientConfig, - openDatabase: () => DatabaseSync, + openDatabase: () => NodeSqlite.DatabaseSync, ): Effect.fn.Return { yield* checkNodeSqliteCompat(); @@ -86,8 +86,8 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* ( Effect.sync(() => db.close()), ); - const statementReaderCache = new WeakMap(); - const hasRows = (statement: StatementSync): boolean => { + const statementReaderCache = new WeakMap(); + const hasRows = (statement: NodeSqlite.StatementSync): boolean => { const cached = statementReaderCache.get(statement); if (cached !== undefined) { return cached; @@ -113,7 +113,11 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* ( }), }); - const runStatement = (statement: StatementSync, params: ReadonlyArray, raw: boolean) => + const runStatement = ( + statement: NodeSqlite.StatementSync, + params: ReadonlyArray, + raw: boolean, + ) => Effect.withFiber, SqlError>((fiber) => { statement.setReadBigInts(Boolean(Context.get(fiber.context, Client.SafeIntegers))); try { @@ -220,7 +224,7 @@ const make = ( makeWithDatabase( options, () => - new DatabaseSync(options.filename, { + new NodeSqlite.DatabaseSync(options.filename, { readOnly: options.readonly ?? false, allowExtension: options.allowExtension ?? false, }), @@ -236,7 +240,7 @@ const makeMemory = ( readonly: false, }, () => { - const database = new DatabaseSync(":memory:", { + const database = new NodeSqlite.DatabaseSync(":memory:", { allowExtension: config.allowExtension ?? false, }); return database; diff --git a/apps/server/src/preview/PortScanner.test.ts b/apps/server/src/preview/PortScanner.test.ts index 9216c696008..6c48f6d5c8b 100644 --- a/apps/server/src/preview/PortScanner.test.ts +++ b/apps/server/src/preview/PortScanner.test.ts @@ -1,4 +1,4 @@ -import * as net from "node:net"; +import * as NodeNet from "node:net"; import { it as effectIt } from "@effect/vitest"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; @@ -17,9 +17,9 @@ const TestPortDiscoveryLive = PortScanner.layer.pipe( ), ); -const openServer = (port: number): Effect.Effect => +const openServer = (port: number): Effect.Effect => Effect.callback((resume) => { - const server = net.createServer(); + const server = NodeNet.createServer(); server.once("error", () => { resume(Effect.succeed(null)); }); @@ -31,7 +31,7 @@ const openServer = (port: number): Effect.Effect => }); }); -const closeServer = (server: net.Server): Effect.Effect => +const closeServer = (server: NodeNet.Server): Effect.Effect => Effect.callback((resume) => { server.close(() => resume(Effect.void)); }); diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index 916c9d077dd..4d22a2c1f8d 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import type { @@ -395,7 +395,7 @@ describe("ClaudeAdapterLive", () => { }); const createInput = harness.getLastCreateQueryInput(); - assert.equal(createInput?.options.env?.HOME, path.join(os.homedir(), ".claude-work")); + assert.equal(createInput?.options.env?.HOME, NodePath.join(NodeOS.homedir(), ".claude-work")); }).pipe( Effect.provideService(Random.Random, makeDeterministicRandomService()), Effect.provide(harness.layer), @@ -649,7 +649,7 @@ describe("ClaudeAdapterLive", () => { }); it.effect("embeds image attachments in Claude user messages", () => { - const baseDir = mkdtempSync(path.join(os.tmpdir(), "claude-attachments-")); + const baseDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "claude-attachments-")); const harness = makeHarness({ cwd: "/tmp/project-claude-attachments", baseDir, @@ -657,7 +657,7 @@ describe("ClaudeAdapterLive", () => { return Effect.gen(function* () { yield* Effect.addFinalizer(() => Effect.sync(() => - rmSync(baseDir, { + NodeFS.rmSync(baseDir, { recursive: true, force: true, }), @@ -674,9 +674,9 @@ describe("ClaudeAdapterLive", () => { mimeType: "image/png", sizeBytes: 4, }; - const attachmentPath = path.join(attachmentsDir, attachmentRelativePath(attachment)); - mkdirSync(path.dirname(attachmentPath), { recursive: true }); - writeFileSync(attachmentPath, Uint8Array.from([1, 2, 3, 4])); + const attachmentPath = NodePath.join(attachmentsDir, attachmentRelativePath(attachment)); + NodeFS.mkdirSync(NodePath.dirname(attachmentPath), { recursive: true }); + NodeFS.writeFileSync(attachmentPath, Uint8Array.from([1, 2, 3, 4])); const session = yield* adapter.startSession({ threadId: THREAD_ID, diff --git a/apps/server/src/provider/Layers/CodexAdapter.test.ts b/apps/server/src/provider/Layers/CodexAdapter.test.ts index 7fef85c42e0..515a7c6fcbb 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.test.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import assert from "node:assert/strict"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeAssert from "node:assert/strict"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { ApprovalRequestId, CodexSettings, @@ -250,8 +250,8 @@ validationLayer("CodexAdapterLive validation", (it) => { }) .pipe(Effect.result); - assert.equal(result._tag, "Failure"); - assert.deepStrictEqual( + NodeAssert.equal(result._tag, "Failure"); + NodeAssert.deepStrictEqual( result.failure, new ProviderAdapterValidationError({ provider: ProviderDriverKind.make("codex"), @@ -259,7 +259,7 @@ validationLayer("CodexAdapterLive validation", (it) => { issue: "Expected provider 'codex' but received 'claudeAgent'.", }), ); - assert.equal(validationRuntimeFactory.factory.mock.calls.length, 0); + NodeAssert.equal(validationRuntimeFactory.factory.mock.calls.length, 0); }), ); it.effect("maps codex model options before starting a session", () => @@ -276,7 +276,7 @@ validationLayer("CodexAdapterLive validation", (it) => { runtimeMode: "full-access", }); - assert.deepStrictEqual(validationRuntimeFactory.factory.mock.calls[0]?.[0], { + NodeAssert.deepStrictEqual(validationRuntimeFactory.factory.mock.calls[0]?.[0], { binaryPath: "codex", cwd: process.cwd(), model: "gpt-5.3-codex", @@ -319,10 +319,10 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { }) .pipe(Effect.result); - assert.equal(result._tag, "Failure"); - assert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError"); - assert.equal(result.failure.provider, "codex"); - assert.equal(result.failure.threadId, "sess-missing"); + NodeAssert.equal(result._tag, "Failure"); + NodeAssert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError"); + NodeAssert.equal(result.failure.provider, "codex"); + NodeAssert.equal(result.failure.threadId, "sess-missing"); }), ); @@ -335,7 +335,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { runtimeMode: "full-access", }); const runtime = sessionRuntimeFactory.lastRuntime; - assert.ok(runtime); + NodeAssert.ok(runtime); runtime.sendTurnImpl.mockClear(); yield* Effect.ignore( @@ -350,7 +350,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { }), ); - assert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { + NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { input: "hello", model: "gpt-5.3-codex", effort: "high", @@ -386,7 +386,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { runtimeMode: "full-access", }); const runtime = customRuntimeFactory.lastRuntime; - assert.ok(runtime); + NodeAssert.ok(runtime); runtime.sendTurnImpl.mockClear(); yield* Effect.ignore( @@ -405,7 +405,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { }), ); - assert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { + NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { input: "hello", model: "gpt-5.3-codex", effort: "high", @@ -442,7 +442,7 @@ function startLifecycleRuntime() { runtimeMode: "full-access", }); const runtime = lifecycleRuntimeFactory.lastRuntime; - assert.ok(runtime); + NodeAssert.ok(runtime); return { adapter, runtime }; }); } @@ -477,17 +477,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "item.completed"); + NodeAssert.equal(firstEvent.value.type, "item.completed"); if (firstEvent.value.type !== "item.completed") { return; } - assert.equal(firstEvent.value.itemId, "msg_1"); - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal(firstEvent.value.payload.itemType, "assistant_message"); + NodeAssert.equal(firstEvent.value.itemId, "msg_1"); + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal(firstEvent.value.payload.itemType, "assistant_message"); }), ); @@ -524,13 +524,13 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { }); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some" || firstEvent.value.type !== "item.completed") { return; } - assert.equal(firstEvent.value.payload.itemType, "mcp_tool_call"); - assert.equal(firstEvent.value.payload.title, "t3-code · preview_status"); - assert.deepStrictEqual(firstEvent.value.payload.data, { + NodeAssert.equal(firstEvent.value.payload.itemType, "mcp_tool_call"); + NodeAssert.equal(firstEvent.value.payload.title, "t3-code · preview_status"); + NodeAssert.deepStrictEqual(firstEvent.value.payload.data, { completedAtMs: 1_778_000_000_000, threadId: "thread-1", turnId: "turn-1", @@ -578,16 +578,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "turn.proposed.completed"); + NodeAssert.equal(firstEvent.value.type, "turn.proposed.completed"); if (firstEvent.value.type !== "turn.proposed.completed") { return; } - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two"); + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two"); }), ); @@ -615,16 +615,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "turn.proposed.delta"); + NodeAssert.equal(firstEvent.value.type, "turn.proposed.delta"); if (firstEvent.value.type !== "turn.proposed.delta") { return; } - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal(firstEvent.value.payload.delta, "## Final plan"); + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal(firstEvent.value.payload.delta, "## Final plan"); }), ); @@ -646,16 +646,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "session.exited"); + NodeAssert.equal(firstEvent.value.type, "session.exited"); if (firstEvent.value.type !== "session.exited") { return; } - assert.equal(firstEvent.value.threadId, "thread-1"); - assert.equal(firstEvent.value.payload.reason, "Session stopped"); + NodeAssert.equal(firstEvent.value.threadId, "thread-1"); + NodeAssert.equal(firstEvent.value.payload.reason, "Session stopped"); }), ); @@ -684,16 +684,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "runtime.warning"); + NodeAssert.equal(firstEvent.value.type, "runtime.warning"); if (firstEvent.value.type !== "runtime.warning") { return; } - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5"); + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5"); }), ); @@ -715,16 +715,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "runtime.warning"); + NodeAssert.equal(firstEvent.value.type, "runtime.warning"); if (firstEvent.value.type !== "runtime.warning") { return; } - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal( + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal( firstEvent.value.payload.message, "The filename or extension is too long. (os error 206)", ); @@ -752,16 +752,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "thread.realtime.started"); + NodeAssert.equal(firstEvent.value.type, "thread.realtime.started"); if (firstEvent.value.type !== "thread.realtime.started") { return; } - assert.equal(firstEvent.value.threadId, "thread-1"); - assert.equal(firstEvent.value.payload.realtimeSessionId, "realtime-session-1"); + NodeAssert.equal(firstEvent.value.threadId, "thread-1"); + NodeAssert.equal(firstEvent.value.payload.realtimeSessionId, "realtime-session-1"); }), ); @@ -784,17 +784,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "runtime.error"); + NodeAssert.equal(firstEvent.value.type, "runtime.error"); if (firstEvent.value.type !== "runtime.error") { return; } - assert.equal(firstEvent.value.turnId, "turn-1"); - assert.equal(firstEvent.value.payload.class, "provider_error"); - assert.equal( + NodeAssert.equal(firstEvent.value.turnId, "turn-1"); + NodeAssert.equal(firstEvent.value.payload.class, "provider_error"); + NodeAssert.equal( firstEvent.value.payload.message, "2026-03-31T18:14:06.833399Z ERROR codex_api::endpoint::responses_websocket: failed to connect to websocket: HTTP error: 503 Service Unavailable, url: wss://chatgpt.com/backend-api/codex/responses", ); @@ -824,15 +824,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "request.resolved"); + NodeAssert.equal(firstEvent.value.type, "request.resolved"); if (firstEvent.value.type !== "request.resolved") { return; } - assert.equal(firstEvent.value.payload.requestType, "command_execution_approval"); + NodeAssert.equal(firstEvent.value.payload.requestType, "command_execution_approval"); }), ); @@ -859,15 +859,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "request.resolved"); + NodeAssert.equal(firstEvent.value.type, "request.resolved"); if (firstEvent.value.type !== "request.resolved") { return; } - assert.equal(firstEvent.value.payload.requestType, "file_read_approval"); + NodeAssert.equal(firstEvent.value.payload.requestType, "file_read_approval"); }), ); @@ -895,15 +895,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "user-input.resolved"); + NodeAssert.equal(firstEvent.value.type, "user-input.resolved"); if (firstEvent.value.type !== "user-input.resolved") { return; } - assert.deepEqual(firstEvent.value.payload.answers, { + NodeAssert.deepEqual(firstEvent.value.payload.answers, { scope: [], }); }), @@ -934,20 +934,20 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { yield* runtime.emit(event); const events = Array.from(yield* Fiber.join(eventsFiber)); - assert.equal(events.length, 2); + NodeAssert.equal(events.length, 2); const firstEvent = events[0]; const secondEvent = events[1]; - assert.equal(firstEvent?.type, "session.state.changed"); + NodeAssert.equal(firstEvent?.type, "session.state.changed"); if (firstEvent?.type === "session.state.changed") { - assert.equal(firstEvent.payload.state, "error"); - assert.equal(firstEvent.payload.reason, "Sandbox setup failed"); + NodeAssert.equal(firstEvent.payload.state, "error"); + NodeAssert.equal(firstEvent.payload.reason, "Sandbox setup failed"); } - assert.equal(secondEvent?.type, "runtime.warning"); + NodeAssert.equal(secondEvent?.type, "runtime.warning"); if (secondEvent?.type === "runtime.warning") { - assert.equal(secondEvent.payload.message, "Sandbox setup failed"); + NodeAssert.equal(secondEvent.payload.message, "Sandbox setup failed"); } }), ); @@ -1006,17 +1006,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { } satisfies ProviderEvent); const events = Array.from(yield* Fiber.join(eventsFiber)); - assert.equal(events[0]?.type, "user-input.requested"); + NodeAssert.equal(events[0]?.type, "user-input.requested"); if (events[0]?.type === "user-input.requested") { - assert.equal(events[0].requestId, "req-user-input-1"); - assert.equal(events[0].payload.questions[0]?.id, "sandbox_mode"); - assert.equal(events[0].payload.questions[0]?.multiSelect, false); + NodeAssert.equal(events[0].requestId, "req-user-input-1"); + NodeAssert.equal(events[0].payload.questions[0]?.id, "sandbox_mode"); + NodeAssert.equal(events[0].payload.questions[0]?.multiSelect, false); } - assert.equal(events[1]?.type, "user-input.resolved"); + NodeAssert.equal(events[1]?.type, "user-input.resolved"); if (events[1]?.type === "user-input.resolved") { - assert.equal(events[1].requestId, "req-user-input-1"); - assert.deepEqual(events[1].payload.answers, { + NodeAssert.equal(events[1].requestId, "req-user-input-1"); + NodeAssert.deepEqual(events[1].payload.answers, { sandbox_mode: "workspace-write", }); } @@ -1060,16 +1060,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => { } satisfies ProviderEvent); const firstEvent = yield* Fiber.join(firstEventFiber); - assert.equal(firstEvent._tag, "Some"); + NodeAssert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } - assert.equal(firstEvent.value.type, "thread.token-usage.updated"); + NodeAssert.equal(firstEvent.value.type, "thread.token-usage.updated"); if (firstEvent.value.type !== "thread.token-usage.updated") { return; } - assert.deepEqual(firstEvent.value.payload.usage, { + NodeAssert.deepEqual(firstEvent.value.payload.usage, { usedTokens: 126, totalProcessedTokens: 11_839, maxTokens: 258_400, @@ -1119,15 +1119,15 @@ scopedLifecycleLayer("CodexAdapterLive scoped lifecycle", (it) => { }); const runtime = scopedLifecycleRuntimeFactory.lastRuntime; - assert.ok(runtime); + NodeAssert.ok(runtime); yield* adapter.stopSession(asThreadId("thread-stop")); - assert.equal(runtime.closeImpl.mock.calls.length, 1); - assert.deepStrictEqual(scopedLifecycleRuntimeFactory.releasedThreadIds, [ + NodeAssert.equal(runtime.closeImpl.mock.calls.length, 1); + NodeAssert.deepStrictEqual(scopedLifecycleRuntimeFactory.releasedThreadIds, [ asThreadId("thread-stop"), ]); - assert.equal(yield* adapter.hasSession(asThreadId("thread-stop")), false); + NodeAssert.equal(yield* adapter.hasSession(asThreadId("thread-stop")), false); }), ); }); @@ -1164,20 +1164,22 @@ scopedFailureLayer("CodexAdapterLive scoped startup failure", (it) => { }) .pipe(Effect.result); - assert.equal(result._tag, "Failure"); - assert.equal(result.failure._tag, "ProviderAdapterProcessError"); - assert.deepStrictEqual(scopedFailureRuntimeFactory.releasedThreadIds, [ + NodeAssert.equal(result._tag, "Failure"); + NodeAssert.equal(result.failure._tag, "ProviderAdapterProcessError"); + NodeAssert.deepStrictEqual(scopedFailureRuntimeFactory.releasedThreadIds, [ asThreadId("thread-fail"), ]); - assert.equal(yield* adapter.hasSession(asThreadId("thread-fail")), false); + NodeAssert.equal(yield* adapter.hasSession(asThreadId("thread-fail")), false); }), ); }); it.effect("flushes managed native logs when the adapter layer shuts down", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-codex-adapter-native-log-")); - const basePath = path.join(tempDir, "provider-native.ndjson"); + const tempDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-codex-adapter-native-log-"), + ); + const basePath = NodePath.join(tempDir, "provider-native.ndjson"); const runtimeFactory = makeRuntimeFactory(); const scope = yield* Scope.make("sequential"); let scopeClosed = false; @@ -1208,7 +1210,7 @@ it.effect("flushes managed native logs when the adapter layer shuts down", () => }); const runtime = runtimeFactory.lastRuntime; - assert.ok(runtime); + NodeAssert.ok(runtime); const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); yield* runtime.emit({ @@ -1225,15 +1227,15 @@ it.effect("flushes managed native logs when the adapter layer shuts down", () => yield* Scope.close(scope, Exit.void); scopeClosed = true; - const threadLogPath = path.join(tempDir, "thread-logger.log"); - assert.equal(fs.existsSync(threadLogPath), true); - const contents = fs.readFileSync(threadLogPath, "utf8"); - assert.match(contents, /NTIVE: .*"message":"native flush test"/); + const threadLogPath = NodePath.join(tempDir, "thread-logger.log"); + NodeAssert.equal(NodeFS.existsSync(threadLogPath), true); + const contents = NodeFS.readFileSync(threadLogPath, "utf8"); + NodeAssert.match(contents, /NTIVE: .*"message":"native flush test"/); } finally { if (!scopeClosed) { yield* Scope.close(scope, Exit.void); } - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } }), ); diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts index 2d303039856..06b7dd99bd4 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts @@ -1,4 +1,4 @@ -import assert from "node:assert/strict"; +import * as NodeAssert from "node:assert/strict"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; @@ -55,7 +55,7 @@ describe("buildTurnStartParams", () => { }), ); - assert.deepStrictEqual(params, { + NodeAssert.deepStrictEqual(params, { threadId: "provider-thread-1", approvalPolicy: "never", sandboxPolicy: { @@ -97,7 +97,7 @@ describe("buildTurnStartParams", () => { }), ); - assert.deepStrictEqual(params, { + NodeAssert.deepStrictEqual(params, { threadId: "provider-thread-1", approvalPolicy: "on-request", sandboxPolicy: { @@ -134,7 +134,7 @@ describe("buildTurnStartParams", () => { }), ); - assert.deepStrictEqual(params, { + NodeAssert.deepStrictEqual(params, { threadId: "provider-thread-1", approvalPolicy: "untrusted", sandboxPolicy: { @@ -156,19 +156,19 @@ describe("T3 browser developer instructions", () => { CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS, CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS, ]) { - assert.match(instructions, /t3-code/); - assert.match(instructions, /preview_status/); - assert.match(instructions, /preview_open/); - assert.match(instructions, /Do not switch to global browser skills/); + NodeAssert.match(instructions, /t3-code/); + NodeAssert.match(instructions, /preview_status/); + NodeAssert.match(instructions, /preview_open/); + NodeAssert.match(instructions, /Do not switch to global browser skills/); } }); }); describe("hasConfiguredMcpServer", () => { it("detects inline Codex MCP configuration arguments", () => { - assert.equal(hasConfiguredMcpServer(undefined), false); - assert.equal(hasConfiguredMcpServer(["--model", "gpt-5.4"]), false); - assert.equal( + NodeAssert.equal(hasConfiguredMcpServer(undefined), false); + NodeAssert.equal(hasConfiguredMcpServer(["--model", "gpt-5.4"]), false); + NodeAssert.equal( hasConfiguredMcpServer(["-c", 'mcp_servers.t3-code.url="http://127.0.0.1/mcp"']), true, ); @@ -177,7 +177,7 @@ describe("hasConfiguredMcpServer", () => { describe("isRecoverableThreadResumeError", () => { it("matches missing thread errors", () => { - assert.equal( + NodeAssert.equal( isRecoverableThreadResumeError( new CodexErrors.CodexAppServerRequestError({ code: -32603, @@ -189,7 +189,7 @@ describe("isRecoverableThreadResumeError", () => { }); it("ignores non-recoverable resume errors", () => { - assert.equal( + NodeAssert.equal( isRecoverableThreadResumeError( new CodexErrors.CodexAppServerRequestError({ code: -32603, @@ -201,7 +201,7 @@ describe("isRecoverableThreadResumeError", () => { }); it("ignores unrelated missing-resource errors that do not mention threads", () => { - assert.equal( + NodeAssert.equal( isRecoverableThreadResumeError( new CodexErrors.CodexAppServerRequestError({ code: -32603, @@ -210,7 +210,7 @@ describe("isRecoverableThreadResumeError", () => { ), false, ); - assert.equal( + NodeAssert.equal( isRecoverableThreadResumeError( new CodexErrors.CodexAppServerRequestError({ code: -32603, @@ -256,8 +256,8 @@ describe("openCodexThread", () => { }), ); - assert.equal(opened.thread.id, "fresh-thread"); - assert.deepStrictEqual( + NodeAssert.equal(opened.thread.id, "fresh-thread"); + NodeAssert.deepStrictEqual( calls.map((call) => call.method), ["thread/resume", "thread/start"], ); @@ -283,7 +283,7 @@ describe("openCodexThread", () => { }, }; - await assert.rejects( + await NodeAssert.rejects( Effect.runPromise( openCodexThread({ client, diff --git a/apps/server/src/provider/Layers/CursorAdapter.test.ts b/apps/server/src/provider/Layers/CursorAdapter.test.ts index c71c6964459..9795e5a0680 100644 --- a/apps/server/src/provider/Layers/CursorAdapter.test.ts +++ b/apps/server/src/provider/Layers/CursorAdapter.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as path from "node:path"; -import * as os from "node:os"; -import { chmod, mkdtemp, readFile, writeFile } from "node:fs/promises"; -import { fileURLToPath } from "node:url"; +import * as NodePath from "node:path"; +import * as NodeOS from "node:os"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; @@ -36,8 +36,8 @@ class CursorAdapter extends Context.Service() "t3/provider/Layers/CursorAdapter.test/CursorAdapter", ) {} -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts"); const mockAgentCommand = "node"; const mockAgentArgs = [mockAgentPath] as const; @@ -45,8 +45,8 @@ async function makeMockAgentWrapper( extraEnv?: Record, options?: { initialDelaySeconds?: number }, ) { - const dir = await mkdtemp(path.join(os.tmpdir(), "cursor-acp-mock-")); - const wrapperPath = path.join(dir, "fake-agent.sh"); + const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-mock-")); + const wrapperPath = NodePath.join(dir, "fake-agent.sh"); const envExports = Object.entries(extraEnv ?? {}) .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) .join("\n"); @@ -55,8 +55,8 @@ ${envExports} ${options?.initialDelaySeconds ? `sleep ${JSON.stringify(String(options.initialDelaySeconds))}` : ""} exec ${JSON.stringify(mockAgentCommand)} ${mockAgentArgs.map((arg) => JSON.stringify(arg)).join(" ")} "$@" `; - await writeFile(wrapperPath, script, "utf8"); - await chmod(wrapperPath, 0o755); + await NodeFSP.writeFile(wrapperPath, script, "utf8"); + await NodeFSP.chmod(wrapperPath, 0o755); return wrapperPath; } @@ -65,8 +65,8 @@ async function makeProbeWrapper( argvLogPath: string, extraEnv?: Record, ) { - const dir = await mkdtemp(path.join(os.tmpdir(), "cursor-acp-probe-")); - const wrapperPath = path.join(dir, "fake-agent.sh"); + const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-probe-")); + const wrapperPath = NodePath.join(dir, "fake-agent.sh"); const envExports = Object.entries(extraEnv ?? {}) .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) .join("\n"); @@ -77,13 +77,13 @@ export T3_ACP_REQUEST_LOG_PATH=${JSON.stringify(requestLogPath)} ${envExports} exec ${JSON.stringify(mockAgentCommand)} ${mockAgentArgs.map((arg) => JSON.stringify(arg)).join(" ")} "$@" `; - await writeFile(wrapperPath, script, "utf8"); - await chmod(wrapperPath, 0o755); + await NodeFSP.writeFile(wrapperPath, script, "utf8"); + await NodeFSP.chmod(wrapperPath, 0o755); return wrapperPath; } async function readArgvLog(filePath: string) { - const raw = await readFile(filePath, "utf8"); + const raw = await NodeFSP.readFile(filePath, "utf8"); return raw .split("\n") .map((line) => line.trim()) @@ -92,7 +92,7 @@ async function readArgvLog(filePath: string) { } async function readJsonLines(filePath: string) { - const raw = await readFile(filePath, "utf8"); + const raw = await NodeFSP.readFile(filePath, "utf8"); return raw .split("\n") .map((line) => line.trim()) @@ -103,7 +103,7 @@ async function readJsonLines(filePath: string) { async function waitForFileContent(filePath: string, attempts = 40) { for (let attempt = 0; attempt < attempts; attempt += 1) { try { - const raw = await readFile(filePath, "utf8"); + const raw = await NodeFSP.readFile(filePath, "utf8"); if (raw.trim().length > 0) { return raw; } @@ -315,9 +315,9 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const settings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-stop-session-close"); const tempDir = yield* Effect.promise(() => - mkdtemp(path.join(os.tmpdir(), "cursor-adapter-exit-log-")), + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-adapter-exit-log-")), ); - const exitLogPath = path.join(tempDir, "exit.log"); + const exitLogPath = NodePath.join(tempDir, "exit.log"); const wrapperPath = yield* Effect.promise(() => makeMockAgentWrapper({ @@ -349,9 +349,9 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const settings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-concurrent-start-session"); const tempDir = yield* Effect.promise(() => - mkdtemp(path.join(os.tmpdir(), "cursor-adapter-concurrent-exit-log-")), + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-adapter-concurrent-exit-log-")), ); - const exitLogPath = path.join(tempDir, "exit.log"); + const exitLogPath = NodePath.join(tempDir, "exit.log"); const wrapperPath = yield* Effect.promise(() => makeMockAgentWrapper( @@ -414,10 +414,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-plan-mode-probe"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath), ); @@ -470,10 +472,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-initial-config-probe"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath), ); @@ -713,10 +717,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const runtimeEvents: Array = []; const settledEventTypes = new Set(); const settledEventsReady = yield* Deferred.make(); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath, { T3_ACP_EMIT_TOOL_CALLS: "1" }), ); @@ -931,10 +937,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-cancel-probe"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath, { T3_ACP_EMIT_TOOL_CALLS: "1" }), ); @@ -1192,10 +1200,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-model-switch"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath), ); @@ -1255,10 +1265,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-fast-mode-reset"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath), ); @@ -1339,10 +1351,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { const adapter = yield* CursorAdapter; const serverSettings = yield* ServerSettingsService; const threadId = ThreadId.make("cursor-fast-mode-custom-instance"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); - const argvLogPath = path.join(tempDir, "argv.txt"); - yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8")); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); + const argvLogPath = NodePath.join(tempDir, "argv.txt"); + yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8")); const wrapperPath = yield* Effect.promise(() => makeProbeWrapper(requestLogPath, argvLogPath), ); diff --git a/apps/server/src/provider/Layers/CursorProvider.ts b/apps/server/src/provider/Layers/CursorProvider.ts index c7358edd55d..94faac60647 100644 --- a/apps/server/src/provider/Layers/CursorProvider.ts +++ b/apps/server/src/provider/Layers/CursorProvider.ts @@ -1,4 +1,4 @@ -import * as NodeOs from "node:os"; +import * as NodeOS from "node:os"; import type { CursorSettings, ModelCapabilities, @@ -746,7 +746,7 @@ function isCursorAboutJsonFormatUnsupported(result: CommandResult): boolean { const readCursorCliConfigChannel = Effect.fn("readCursorCliConfigChannel")(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const configPath = path.join(NodeOs.homedir(), ".cursor", "cli-config.json"); + const configPath = path.join(NodeOS.homedir(), ".cursor", "cli-config.json"); const raw = yield* fileSystem.readFileString(configPath).pipe(Effect.orElseSucceed(() => "")); return parseCursorCliConfigChannel(raw); }); diff --git a/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts b/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts index 0b1f99d3c11..f2c317a9127 100644 --- a/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts +++ b/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { ThreadId } from "@t3tools/contracts"; import { assert, describe, it } from "@effect/vitest"; @@ -31,8 +31,8 @@ function parseLogLine(line: string) { describe("EventNdjsonLogger", () => { it.effect("writes effect-style lines to thread-scoped files", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); - const basePath = path.join(tempDir, "provider-native.ndjson"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-")); + const basePath = NodePath.join(tempDir, "provider-native.ndjson"); try { const logger = yield* makeEventNdjsonLogger(basePath, { stream: "native" }); @@ -51,13 +51,13 @@ describe("EventNdjsonLogger", () => { ); yield* logger.close(); - const threadOnePath = path.join(tempDir, "thread-1.log"); - const threadTwoPath = path.join(tempDir, "thread-2.log"); - assert.equal(fs.existsSync(threadOnePath), true); - assert.equal(fs.existsSync(threadTwoPath), true); + const threadOnePath = NodePath.join(tempDir, "thread-1.log"); + const threadTwoPath = NodePath.join(tempDir, "thread-2.log"); + assert.equal(NodeFS.existsSync(threadOnePath), true); + assert.equal(NodeFS.existsSync(threadTwoPath), true); - const first = parseLogLine(fs.readFileSync(threadOnePath, "utf8").trim()); - const second = parseLogLine(fs.readFileSync(threadTwoPath, "utf8").trim()); + const first = parseLogLine(NodeFS.readFileSync(threadOnePath, "utf8").trim()); + const second = parseLogLine(NodeFS.readFileSync(threadTwoPath, "utf8").trim()); assert.equal(Number.isNaN(Date.parse(first.observedAt)), false); assert.equal(first.stream, "NTIVE"); @@ -70,7 +70,7 @@ describe("EventNdjsonLogger", () => { '{"type":"turn.completed","threadId":"provider-thread-2","id":"evt-2"}', ); } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } }), ); @@ -79,8 +79,8 @@ describe("EventNdjsonLogger", () => { "falls back to a global segment when orchestration thread id is missing or invalid", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); - const basePath = path.join(tempDir, "provider-canonical.ndjson"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-")); + const basePath = NodePath.join(tempDir, "provider-canonical.ndjson"); try { const logger = yield* makeEventNdjsonLogger(basePath, { stream: "orchestration" }); @@ -93,10 +93,9 @@ describe("EventNdjsonLogger", () => { yield* logger.write({ id: "evt-invalid-thread" }, "!!!" as unknown as ThreadId); yield* logger.close(); - const globalPath = path.join(tempDir, "_global.log"); - assert.equal(fs.existsSync(globalPath), true); - const lines = fs - .readFileSync(globalPath, "utf8") + const globalPath = NodePath.join(tempDir, "_global.log"); + assert.equal(NodeFS.existsSync(globalPath), true); + const lines = NodeFS.readFileSync(globalPath, "utf8") .trim() .split("\n") .map((line) => parseLogLine(line)); @@ -108,15 +107,15 @@ describe("EventNdjsonLogger", () => { assert.equal(lines[1]?.stream, "CANON"); assert.equal(lines[1]?.payload, '{"id":"evt-invalid-thread"}'); } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } }), ); it.effect("serializes concurrent first writes for the same segment", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); - const basePath = path.join(tempDir, "provider-canonical.ndjson"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-")); + const basePath = NodePath.join(tempDir, "provider-canonical.ndjson"); try { const logger = yield* makeEventNdjsonLogger(basePath, { @@ -137,10 +136,9 @@ describe("EventNdjsonLogger", () => { ); yield* logger.close(); - const globalPath = path.join(tempDir, "_global.log"); - assert.equal(fs.existsSync(globalPath), true); - const lines = fs - .readFileSync(globalPath, "utf8") + const globalPath = NodePath.join(tempDir, "_global.log"); + assert.equal(NodeFS.existsSync(globalPath), true); + const lines = NodeFS.readFileSync(globalPath, "utf8") .trim() .split("\n") .map((line) => parseLogLine(line)); @@ -151,15 +149,15 @@ describe("EventNdjsonLogger", () => { '{"id":"evt-concurrent-2"}', ]); } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } }), ); it.effect("rotates per-thread files when max size is exceeded", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); - const basePath = path.join(tempDir, "provider-native.ndjson"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-")); + const basePath = NodePath.join(tempDir, "provider-native.ndjson"); try { const logger = yield* makeEventNdjsonLogger(basePath, { @@ -185,8 +183,7 @@ describe("EventNdjsonLogger", () => { yield* logger.close(); const fileStem = "thread-rotate.log"; - const matchingFiles = fs - .readdirSync(tempDir) + const matchingFiles = NodeFS.readdirSync(tempDir) .filter((entry) => entry === fileStem || entry.startsWith(`${fileStem}.`)) .toSorted(); @@ -203,7 +200,7 @@ describe("EventNdjsonLogger", () => { false, ); } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); } }), ); diff --git a/apps/server/src/provider/Layers/EventNdjsonLogger.ts b/apps/server/src/provider/Layers/EventNdjsonLogger.ts index 04377ad520c..c934abbfe3d 100644 --- a/apps/server/src/provider/Layers/EventNdjsonLogger.ts +++ b/apps/server/src/provider/Layers/EventNdjsonLogger.ts @@ -6,8 +6,8 @@ * single effect-style text line in a thread-scoped file. Failures are * downgraded to warnings so provider runtime behavior is unaffected. */ -import fs from "node:fs"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; import type { ThreadId } from "@t3tools/contracts"; import { RotatingFileSink } from "@t3tools/shared/logging"; @@ -178,7 +178,7 @@ export const makeEventNdjsonLogger = Effect.fn("makeEventNdjsonLogger")(function const directoryReady = yield* Effect.sync(() => { try { - fs.mkdirSync(path.dirname(filePath), { recursive: true }); + NodeFS.mkdirSync(NodePath.dirname(filePath), { recursive: true }); return true; } catch (error) { return { ok: false as const, error }; @@ -211,7 +211,7 @@ export const makeEventNdjsonLogger = Effect.fn("makeEventNdjsonLogger")(function } return makeThreadWriter({ - filePath: path.join(path.dirname(filePath), `${threadSegment}.log`), + filePath: NodePath.join(NodePath.dirname(filePath), `${threadSegment}.log`), maxBytes, maxFiles, batchWindowMs, diff --git a/apps/server/src/provider/Layers/GrokAdapter.test.ts b/apps/server/src/provider/Layers/GrokAdapter.test.ts index bfd5ae25755..c871e3c2fc4 100644 --- a/apps/server/src/provider/Layers/GrokAdapter.test.ts +++ b/apps/server/src/provider/Layers/GrokAdapter.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as path from "node:path"; -import * as os from "node:os"; -import { chmod, mkdtemp, readFile, writeFile } from "node:fs/promises"; -import { fileURLToPath } from "node:url"; +import * as NodePath from "node:path"; +import * as NodeOS from "node:os"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; @@ -26,13 +26,13 @@ import { ServerConfig } from "../../config.ts"; import { makeGrokAdapter } from "./GrokAdapter.ts"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts"); const mockAgentCommand = process.execPath; async function makeMockGrokWrapper(extraEnv?: Record) { - const dir = await mkdtemp(path.join(os.tmpdir(), "grok-acp-mock-")); - const wrapperPath = path.join(dir, "fake-grok.sh"); + const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-mock-")); + const wrapperPath = NodePath.join(dir, "fake-grok.sh"); const envExports = Object.entries(extraEnv ?? {}) .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`) .join("\n"); @@ -40,8 +40,8 @@ async function makeMockGrokWrapper(extraEnv?: Record) { ${envExports} exec ${JSON.stringify(mockAgentCommand)} ${JSON.stringify(mockAgentPath)} "$@" `; - await writeFile(wrapperPath, script, "utf8"); - await chmod(wrapperPath, 0o755); + await NodeFSP.writeFile(wrapperPath, script, "utf8"); + await NodeFSP.chmod(wrapperPath, 0o755); return wrapperPath; } @@ -51,7 +51,7 @@ function waitForFileContent(filePath: string, attempts = 40): Effect.Effect readFile(filePath, "utf8")).pipe( + const raw = yield* Effect.tryPromise(() => NodeFSP.readFile(filePath, "utf8")).pipe( Effect.orElseSucceed(() => ""), ); if (raw.trim().length > 0) { @@ -64,7 +64,7 @@ function waitForFileContent(filePath: string, attempts = 40): Effect.Effect line.trim()) @@ -149,9 +149,9 @@ it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => { Effect.gen(function* () { const threadId = ThreadId.make("grok-stop-session-close"); const tempDir = yield* Effect.promise(() => - mkdtemp(path.join(os.tmpdir(), "grok-adapter-exit-log-")), + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-adapter-exit-log-")), ); - const exitLogPath = path.join(tempDir, "exit.log"); + const exitLogPath = NodePath.join(tempDir, "exit.log"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ @@ -227,8 +227,10 @@ it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => { it.effect("responds to ACP approvals using provider-supplied option ids", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-custom-approval-option-id"); - const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "grok-acp-"))); - const requestLogPath = path.join(tempDir, "requests.ndjson"); + const tempDir = yield* Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-")), + ); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_REQUEST_LOG_PATH: requestLogPath, diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts index 3f483d8fd7e..d0475e25284 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts @@ -1,4 +1,4 @@ -import assert from "node:assert/strict"; +import * as NodeAssert from "node:assert/strict"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import * as Context from "effect/Context"; @@ -238,11 +238,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { runtimeMode: "full-access", }); - assert.equal(session.provider, "opencode"); - assert.equal(session.threadId, "thread-opencode"); - assert.deepEqual(runtimeMock.state.startCalls, []); - assert.deepEqual(runtimeMock.state.sessionCreateUrls, ["http://127.0.0.1:9999"]); - assert.deepEqual(runtimeMock.state.authHeaders, [ + NodeAssert.equal(session.provider, "opencode"); + NodeAssert.equal(session.threadId, "thread-opencode"); + NodeAssert.deepEqual(runtimeMock.state.startCalls, []); + NodeAssert.deepEqual(runtimeMock.state.sessionCreateUrls, ["http://127.0.0.1:9999"]); + NodeAssert.deepEqual(runtimeMock.state.authHeaders, [ `Basic ${btoa("opencode:secret-password")}`, ]); }), @@ -259,8 +259,8 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { yield* adapter.stopSession(asThreadId("thread-opencode")); - assert.deepEqual(runtimeMock.state.startCalls, []); - assert.deepEqual( + NodeAssert.deepEqual(runtimeMock.state.startCalls, []); + NodeAssert.deepEqual( runtimeMock.state.abortCalls.includes("http://127.0.0.1:9999/session"), true, ); @@ -286,7 +286,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { yield* adapter.stopSession(threadId); const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second"))); - assert.deepEqual( + NodeAssert.deepEqual( events.map((event) => event.type), ["session.started", "thread.started", "session.exited"], ); @@ -316,11 +316,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { yield* Effect.exit(adapter.stopAll()); const sessions = yield* adapter.listSessions(); - assert.deepEqual(runtimeMock.state.closeCalls, [ + NodeAssert.deepEqual(runtimeMock.state.closeCalls, [ "http://127.0.0.1:9999", "http://127.0.0.1:9999", ]); - assert.deepEqual(sessions, []); + NodeAssert.deepEqual(sessions, []); }), ); @@ -348,7 +348,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { scopeClosed = true; const exit = yield* Fiber.await(eventsFiber).pipe(Effect.timeout("1 second")); - assert.equal(Exit.hasInterrupts(exit), true); + NodeAssert.equal(Exit.hasInterrupts(exit), true); } finally { if (!scopeClosed) { yield* Scope.close(scope, Exit.void).pipe(Effect.ignore); @@ -379,19 +379,19 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { .pipe(Effect.flip); const sessions = yield* adapter.listSessions(); - assert.equal(error._tag, "ProviderAdapterRequestError"); + NodeAssert.equal(error._tag, "ProviderAdapterRequestError"); if (error._tag !== "ProviderAdapterRequestError") { throw new Error("Unexpected error type"); } - assert.equal(error.detail, "prompt failed"); - assert.equal( + NodeAssert.equal(error.detail, "prompt failed"); + NodeAssert.equal( error.message, "Provider adapter request failed (opencode) for session.promptAsync: prompt failed", ); - assert.equal(sessions.length, 1); - assert.equal(sessions[0]?.status, "ready"); - assert.equal(sessions[0]?.activeTurnId, undefined); - assert.equal(sessions[0]?.lastError, "prompt failed"); + NodeAssert.equal(sessions.length, 1); + NodeAssert.equal(sessions[0]?.status, "ready"); + NodeAssert.equal(sessions[0]?.activeTurnId, undefined); + NodeAssert.equal(sessions[0]?.lastError, "prompt failed"); }), ); @@ -424,13 +424,13 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { model: "openai/gpt-5", }, }); - assert.equal(String(steeredTurn.turnId), String(turn.turnId)); + NodeAssert.equal(String(steeredTurn.turnId), String(turn.turnId)); const sessions = yield* adapter.listSessions(); const session = sessions.find((entry) => entry.threadId === threadId); - assert.equal(session?.status, "running"); - assert.equal(String(session?.activeTurnId), String(turn.turnId)); - assert.equal(runtimeMock.state.promptCalls.length, 2); + NodeAssert.equal(session?.status, "running"); + NodeAssert.equal(String(session?.activeTurnId), String(turn.turnId)); + NodeAssert.equal(runtimeMock.state.promptCalls.length, 2); }), ); @@ -466,11 +466,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { .pipe(Effect.flip); // The original turn keeps running — only the steer prompt failed. - assert.equal(error._tag, "ProviderAdapterRequestError"); + NodeAssert.equal(error._tag, "ProviderAdapterRequestError"); const sessions = yield* adapter.listSessions(); const session = sessions.find((entry) => entry.threadId === threadId); - assert.equal(session?.status, "running"); - assert.equal(String(session?.activeTurnId), String(turn.turnId)); + NodeAssert.equal(session?.status, "running"); + NodeAssert.equal(String(session?.activeTurnId), String(turn.turnId)); }), ); @@ -508,7 +508,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { ), }); - assert.deepEqual(runtimeMock.state.promptCalls.at(-1), { + NodeAssert.deepEqual(runtimeMock.state.promptCalls.at(-1), { sessionID: "http://127.0.0.1:9999/session", model: { providerID: "anthropic", @@ -552,7 +552,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { input: "Fix it", }); - assert.deepEqual(runtimeMock.state.promptCalls.at(-1), { + NodeAssert.deepEqual(runtimeMock.state.promptCalls.at(-1), { sessionID: "http://127.0.0.1:9999/session", model: { providerID: "anthropic", @@ -596,15 +596,15 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { }) .pipe(Effect.flip); - assert.equal(error._tag, "ProviderAdapterValidationError"); + NodeAssert.equal(error._tag, "ProviderAdapterValidationError"); if (error._tag !== "ProviderAdapterValidationError") { throw new Error("Unexpected error type"); } - assert.equal( + NodeAssert.equal( error.issue, "OpenCode model selection is bound to instance 'opencode', expected 'opencode_zen'.", ); - assert.deepEqual(runtimeMock.state.promptCalls, []); + NodeAssert.deepEqual(runtimeMock.state.promptCalls, []); }).pipe(Effect.provide(adapterLayer)); }); @@ -631,10 +631,10 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { const snapshot = yield* adapter.rollbackThread(threadId, 2); - assert.deepEqual(runtimeMock.state.revertCalls, [ + NodeAssert.deepEqual(runtimeMock.state.revertCalls, [ { sessionID: "http://127.0.0.1:9999/session" }, ]); - assert.deepEqual(snapshot.turns, []); + NodeAssert.deepEqual(snapshot.turns, []); }), ); @@ -644,11 +644,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world"); const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hellolo world"); - assert.deepEqual( + NodeAssert.deepEqual( [firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit], ["Hello", "lo world", ""], ); - assert.equal(secondUpdate.latestText, "Hellolo world"); + NodeAssert.equal(secondUpdate.latestText, "Hellolo world"); }), ); @@ -721,14 +721,14 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second"))); const deltas = events.filter((event) => event.type === "content.delta"); - assert.deepEqual( + NodeAssert.deepEqual( deltas.map((event) => (event.type === "content.delta" ? event.payload.delta : "")), ["A B", "Bonus"], ); - assert.equal(events.at(-1)?.type, "item.completed"); + NodeAssert.equal(events.at(-1)?.type, "item.completed"); const completed = events.at(-1); if (completed?.type === "item.completed") { - assert.equal(completed.payload.detail, "A BBonus"); + NodeAssert.equal(completed.payload.detail, "A BBonus"); } }), ); @@ -820,27 +820,27 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { return started; }).pipe(Effect.provide(adapterLayer)); - assert.equal(session.threadId, "thread-native-log"); - assert.equal(nativeEvents.length, 1); - assert.equal( + NodeAssert.equal(session.threadId, "thread-native-log"); + NodeAssert.equal(nativeEvents.length, 1); + NodeAssert.equal( nativeEvents.some((record) => record.event?.provider === "opencode"), true, ); - assert.equal( + NodeAssert.equal( nativeEvents.some( (record) => record.event?.providerThreadId === "http://127.0.0.1:9999/session", ), true, ); - assert.equal( + NodeAssert.equal( nativeEvents.some((record) => record.event?.threadId === "thread-native-log"), true, ); - assert.equal( + NodeAssert.equal( nativeEvents.some((record) => record.event?.type === "message.updated"), true, ); - assert.equal( + NodeAssert.equal( nativeThreadIds.every((threadId) => threadId === "thread-native-log"), true, ); @@ -911,9 +911,9 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { }; }).pipe(Effect.provide(adapterLayer)); - assert.equal(sessions.length, 1); - assert.equal(sessions[0]?.threadId, "thread-native-log-failure"); - assert.deepEqual(closeCallsDuringRun, []); + NodeAssert.equal(sessions.length, 1); + NodeAssert.equal(sessions[0]?.threadId, "thread-native-log-failure"); + NodeAssert.deepEqual(closeCallsDuringRun, []); }), ); }); diff --git a/apps/server/src/provider/Layers/OpenCodeProvider.test.ts b/apps/server/src/provider/Layers/OpenCodeProvider.test.ts index eac9f0b43fb..b0e785512dc 100644 --- a/apps/server/src/provider/Layers/OpenCodeProvider.test.ts +++ b/apps/server/src/provider/Layers/OpenCodeProvider.test.ts @@ -1,4 +1,4 @@ -import assert from "node:assert/strict"; +import * as NodeAssert from "node:assert/strict"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -122,9 +122,12 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { runtimeMock.state.runVersionError = new Error("spawn opencode ENOENT"); const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd()); - assert.equal(snapshot.status, "error"); - assert.equal(snapshot.installed, false); - assert.equal(snapshot.message, "OpenCode CLI (`opencode`) is not installed or not on PATH."); + NodeAssert.equal(snapshot.status, "error"); + NodeAssert.equal(snapshot.installed, false); + NodeAssert.equal( + snapshot.message, + "OpenCode CLI (`opencode`) is not installed or not on PATH.", + ); }), ); @@ -133,9 +136,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { runtimeMock.state.runVersionError = new Error("An error occurred in Effect.tryPromise"); const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd()); - assert.equal(snapshot.status, "error"); - assert.equal(snapshot.installed, true); - assert.equal(snapshot.message, "Failed to execute OpenCode CLI health check."); + NodeAssert.equal(snapshot.status, "error"); + NodeAssert.equal(snapshot.installed, true); + NodeAssert.equal(snapshot.message, "Failed to execute OpenCode CLI health check."); }), ); @@ -174,20 +177,20 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd()); const model = snapshot.models.find((entry) => entry.slug === "openai/gpt-5.4"); - assert.ok(model); + NodeAssert.ok(model); const variantDescriptor = model.capabilities?.optionDescriptors?.find( (descriptor) => descriptor.id === "variant" && descriptor.type === "select", ); - assert.ok(variantDescriptor && variantDescriptor.type === "select"); - assert.equal( + NodeAssert.ok(variantDescriptor && variantDescriptor.type === "select"); + NodeAssert.equal( variantDescriptor.options.find((option) => option.isDefault === true)?.id, "medium", ); const agentDescriptor = model.capabilities?.optionDescriptors?.find( (descriptor) => descriptor.id === "agent" && descriptor.type === "select", ); - assert.ok(agentDescriptor && agentDescriptor.type === "select"); - assert.equal( + NodeAssert.ok(agentDescriptor && agentDescriptor.type === "select"); + NodeAssert.equal( agentDescriptor.options.find((option) => option.isDefault === true)?.id, "build", ); @@ -198,7 +201,7 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { Effect.gen(function* () { yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd()); - assert.equal(runtimeMock.state.closeCalls, 1); + NodeAssert.equal(runtimeMock.state.closeCalls, 1); }), ); }); @@ -215,9 +218,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus with configured server URL", (i process.cwd(), ); - assert.equal(snapshot.status, "error"); - assert.equal(snapshot.installed, true); - assert.equal( + NodeAssert.equal(snapshot.status, "error"); + NodeAssert.equal(snapshot.installed, true); + NodeAssert.equal( snapshot.message, "OpenCode server rejected authentication. Check the server URL and password.", ); @@ -237,9 +240,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus with configured server URL", (i process.cwd(), ); - assert.equal(snapshot.status, "error"); - assert.equal(snapshot.installed, true); - assert.equal( + NodeAssert.equal(snapshot.status, "error"); + NodeAssert.equal(snapshot.installed, true); + NodeAssert.equal( snapshot.message, "Couldn't reach the configured OpenCode server at http://127.0.0.1:9999. Check that the server is running and the URL is correct.", ); diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index fbb8acfb9e6..ccbbce1759f 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import type { ProviderApprovalDecision, @@ -644,8 +644,8 @@ it.effect("ProviderServiceLive writes canonical events to the emitting thread se it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-")); - const dbPath = path.join(tempDir, "orchestration.sqlite"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-service-")); + const dbPath = NodePath.join(tempDir, "orchestration.sqlite"); const codex = makeFakeCodexAdapter(); const registry = makeAdapterRegistryMock({ @@ -706,7 +706,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", ( }).pipe(Effect.provide(persistenceLayer)); assert.equal(legacyTableRows.length, 0); - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }).pipe(Effect.provide(NodeServices.layer)), ); @@ -714,8 +714,10 @@ it.effect( "ProviderServiceLive restores rollback routing after restart using persisted thread mapping", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-restart-")); - const dbPath = path.join(tempDir, "orchestration.sqlite"); + const tempDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-provider-service-restart-"), + ); + const dbPath = NodePath.join(tempDir, "orchestration.sqlite"); const persistenceLayer = makeSqlitePersistenceLive(dbPath); const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe( Layer.provide(persistenceLayer), @@ -834,7 +836,7 @@ it.effect( assert.equal(typeof rollbackCall?.[0], "string"); assert.equal(rollbackCall?.[1], 1); - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }).pipe(Effect.provide(NodeServices.layer)), ); @@ -1283,8 +1285,10 @@ routing.layer("ProviderServiceLive routing", (it) => { it.effect("reuses persisted resume cursor when startSession is called after a restart", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-start-")); - const dbPath = path.join(tempDir, "orchestration.sqlite"); + const tempDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-provider-service-start-"), + ); + const dbPath = NodePath.join(tempDir, "orchestration.sqlite"); const persistenceLayer = makeSqlitePersistenceLive(dbPath); const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe( Layer.provide(persistenceLayer), @@ -1379,7 +1383,7 @@ routing.layer("ProviderServiceLive routing", (it) => { assert.equal(startPayload.threadId, initial.threadId); } - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }).pipe(Effect.provide(NodeServices.layer)), ); @@ -1387,8 +1391,10 @@ routing.layer("ProviderServiceLive routing", (it) => { "reuses persisted cwd when startSession resumes a claude session without cwd input", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-cwd-")); - const dbPath = path.join(tempDir, "orchestration.sqlite"); + const tempDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3-provider-service-cwd-"), + ); + const dbPath = NodePath.join(tempDir, "orchestration.sqlite"); const persistenceLayer = makeSqlitePersistenceLive(dbPath); const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe( Layer.provide(persistenceLayer), @@ -1477,7 +1483,7 @@ routing.layer("ProviderServiceLive routing", (it) => { assert.equal(startPayload.threadId, initial.threadId); } - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }).pipe(Effect.provide(NodeServices.layer)), ); }); diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts index c5d60a69a22..079b7f10ebf 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { ProviderDriverKind, ThreadId } from "@t3tools/contracts"; @@ -229,8 +229,8 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL it("rehydrates persisted mappings across layer restart", () => Effect.gen(function* () { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-directory-")); - const dbPath = path.join(tempDir, "orchestration.sqlite"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-directory-")); + const dbPath = NodePath.join(tempDir, "orchestration.sqlite"); const directoryLayer = makeDirectoryLayer(makeSqlitePersistenceLive(dbPath)); const threadId = ThreadId.make("thread-restart"); @@ -266,6 +266,6 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL assert.equal(legacyTableRows.length, 0); }).pipe(Effect.provide(directoryLayer)); - fs.rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); })); }); diff --git a/apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts b/apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts index a2c44f0ac1b..5533a04bc83 100644 --- a/apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts +++ b/apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as path from "node:path"; -import * as os from "node:os"; -import { fileURLToPath } from "node:url"; -import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeOS from "node:os"; +import * as NodeURL from "node:url"; +import * as NodeFS from "node:fs"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -13,8 +13,8 @@ import { describe, expect } from "vite-plus/test"; import * as AcpSessionRuntime from "./AcpSessionRuntime.ts"; import type * as EffectAcpProtocol from "effect-acp/protocol"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts"); const mockAgentCommand = "node"; const mockAgentArgs = [mockAgentPath]; @@ -347,8 +347,8 @@ describe("AcpSessionRuntime", () => { }); it.effect("rejects invalid config option values before sending session/set_config_option", () => { - const tempDir = mkdtempSync(path.join(os.tmpdir(), "acp-runtime-")); - const requestLogPath = path.join(tempDir, "requests.ndjson"); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "acp-runtime-")); + const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); return Effect.gen(function* () { const runtime = yield* AcpSessionRuntime.AcpSessionRuntime; yield* runtime.start(); @@ -363,7 +363,7 @@ describe("AcpSessionRuntime", () => { expect(error.message).toContain("composer-2[fast=true]"); } - const recordedRequests = readFileSync(requestLogPath, "utf8") + const recordedRequests = NodeFS.readFileSync(requestLogPath, "utf8") .trim() .split("\n") .filter((line) => line.length > 0) @@ -392,7 +392,7 @@ describe("AcpSessionRuntime", () => { ), Effect.scoped, Effect.provide(NodeServices.layer), - Effect.ensuring(Effect.sync(() => rmSync(tempDir, { recursive: true, force: true }))), + Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))), ); }); }); diff --git a/apps/server/src/provider/opencodeRuntime.ts b/apps/server/src/provider/opencodeRuntime.ts index 365884da85d..a83c134d5bd 100644 --- a/apps/server/src/provider/opencodeRuntime.ts +++ b/apps/server/src/provider/opencodeRuntime.ts @@ -1,4 +1,4 @@ -import { pathToFileURL } from "node:url"; +import * as NodeURL from "node:url"; import type { ChatAttachment, ProviderApprovalDecision, RuntimeMode } from "@t3tools/contracts"; import { @@ -206,7 +206,7 @@ export function toOpenCodeFileParts(input: { type: "file", mime: attachment.mimeType, filename: attachment.name, - url: pathToFileURL(attachmentPath).href, + url: NodeURL.pathToFileURL(attachmentPath).href, }); } diff --git a/apps/server/src/provider/providerMaintenance.test.ts b/apps/server/src/provider/providerMaintenance.test.ts index 1018d123bb7..8937844f613 100644 --- a/apps/server/src/provider/providerMaintenance.test.ts +++ b/apps/server/src/provider/providerMaintenance.test.ts @@ -1,9 +1,9 @@ // @effect-diagnostics nodeBuiltinImport:off import { expect, it } from "@effect/vitest"; -import { chmodSync, mkdirSync, symlinkSync, writeFileSync } from "node:fs"; +import * as NodeFS from "node:fs"; import * as NodeServices from "@effect/platform-node/NodeServices"; import * as NodeOS from "node:os"; -import path from "node:path"; +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"; @@ -25,7 +25,7 @@ const driver = (value: string) => ProviderDriverKind.make(value); const makeTempDir = (name: string) => Crypto.Crypto.pipe( Effect.flatMap((crypto) => crypto.randomUUIDv4), - Effect.map((id) => path.join(NodeOS.tmpdir(), `${name}-${id}`)), + Effect.map((id) => NodePath.join(NodeOS.tmpdir(), `${name}-${id}`)), ); const isNativeTestCommandPath = (expectedPathSegment: string) => @@ -203,11 +203,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-vite-plus-capabilities"); - const vitePlusBinDir = path.join(tempDir, ".vite-plus", "bin"); - mkdirSync(vitePlusBinDir, { recursive: true }); - const packageToolPath = path.join(vitePlusBinDir, "package-tool"); - writeFileSync(packageToolPath, "#!/bin/sh\n"); - chmodSync(packageToolPath, 0o755); + const vitePlusBinDir = NodePath.join(tempDir, ".vite-plus", "bin"); + NodeFS.mkdirSync(vitePlusBinDir, { recursive: true }); + const packageToolPath = NodePath.join(vitePlusBinDir, "package-tool"); + NodeFS.writeFileSync(packageToolPath, "#!/bin/sh\n"); + NodeFS.chmodSync(packageToolPath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( packageToolUpdate, @@ -240,9 +240,9 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-bun-capabilities"); - const bunBinDir = path.join(tempDir, ".bun", "bin"); - mkdirSync(bunBinDir, { recursive: true }); - writeFileSync(path.join(bunBinDir, "native-package-tool.exe"), "MZ"); + const bunBinDir = NodePath.join(tempDir, ".bun", "bin"); + NodeFS.mkdirSync(bunBinDir, { recursive: true }); + NodeFS.writeFileSync(NodePath.join(bunBinDir, "native-package-tool.exe"), "MZ"); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, @@ -276,11 +276,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-pnpm-capabilities"); - const pnpmHomeDir = path.join(tempDir, ".local", "share", "pnpm"); - mkdirSync(pnpmHomeDir, { recursive: true }); - const scopedPackageToolPath = path.join(pnpmHomeDir, "scoped-package-tool"); - writeFileSync(scopedPackageToolPath, "#!/bin/sh\n"); - chmodSync(scopedPackageToolPath, 0o755); + const pnpmHomeDir = NodePath.join(tempDir, ".local", "share", "pnpm"); + NodeFS.mkdirSync(pnpmHomeDir, { recursive: true }); + const scopedPackageToolPath = NodePath.join(pnpmHomeDir, "scoped-package-tool"); + NodeFS.writeFileSync(scopedPackageToolPath, "#!/bin/sh\n"); + NodeFS.chmodSync(scopedPackageToolPath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( scopedPackageToolUpdate, @@ -336,11 +336,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-native-package-tool-native-capabilities"); - const nativeBinDir = path.join(tempDir, ".local", "bin"); - mkdirSync(nativeBinDir, { recursive: true }); - const nativePackageToolPath = path.join(nativeBinDir, "native-package-tool"); - writeFileSync(nativePackageToolPath, "#!/bin/sh\n"); - chmodSync(nativePackageToolPath, 0o755); + const nativeBinDir = NodePath.join(tempDir, ".local", "bin"); + NodeFS.mkdirSync(nativeBinDir, { recursive: true }); + const nativePackageToolPath = NodePath.join(nativeBinDir, "native-package-tool"); + NodeFS.writeFileSync(nativePackageToolPath, "#!/bin/sh\n"); + NodeFS.chmodSync(nativePackageToolPath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( nativePackageToolUpdate, @@ -373,11 +373,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-scoped-package-tool-native-capabilities"); - const nativeBinDir = path.join(tempDir, ".scoped-package-tool", "bin"); - mkdirSync(nativeBinDir, { recursive: true }); - const scopedPackageToolPath = path.join(nativeBinDir, "scoped-package-tool"); - writeFileSync(scopedPackageToolPath, "#!/bin/sh\n"); - chmodSync(scopedPackageToolPath, 0o755); + const nativeBinDir = NodePath.join(tempDir, ".scoped-package-tool", "bin"); + NodeFS.mkdirSync(nativeBinDir, { recursive: true }); + const scopedPackageToolPath = NodePath.join(nativeBinDir, "scoped-package-tool"); + NodeFS.writeFileSync(scopedPackageToolPath, "#!/bin/sh\n"); + NodeFS.chmodSync(scopedPackageToolPath, 0o755); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect( scopedPackageToolUpdate, @@ -454,8 +454,8 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { it.effect("keeps npm updates for binaries symlinked into npm's global node_modules tree", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-npm-capabilities"); - const binDir = path.join(tempDir, "bin"); - const packageBinDir = path.join( + const binDir = NodePath.join(tempDir, "bin"); + const packageBinDir = NodePath.join( tempDir, "lib", "node_modules", @@ -463,13 +463,13 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { "package-tool", "bin", ); - mkdirSync(binDir, { recursive: true }); - mkdirSync(packageBinDir, { recursive: true }); - const packageBinPath = path.join(packageBinDir, "package-tool.js"); - const symlinkPath = path.join(binDir, "package-tool"); - writeFileSync(packageBinPath, "#!/usr/bin/env node\n"); - chmodSync(packageBinPath, 0o755); - symlinkSync(packageBinPath, symlinkPath); + NodeFS.mkdirSync(binDir, { recursive: true }); + NodeFS.mkdirSync(packageBinDir, { recursive: true }); + const packageBinPath = NodePath.join(packageBinDir, "package-tool.js"); + const symlinkPath = NodePath.join(binDir, "package-tool"); + NodeFS.writeFileSync(packageBinPath, "#!/usr/bin/env node\n"); + NodeFS.chmodSync(packageBinPath, 0o755); + NodeFS.symlinkSync(packageBinPath, symlinkPath); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: symlinkPath, @@ -497,8 +497,8 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { it.effect("uses Effect FileSystem realPath when detecting pnpm global symlinks", () => Effect.gen(function* () { const tempDir = yield* makeTempDir("t3-pnpm-realpath-capabilities"); - const binDir = path.join(tempDir, "bin"); - const packageBinDir = path.join( + const binDir = NodePath.join(tempDir, "bin"); + const packageBinDir = NodePath.join( tempDir, ".local", "share", @@ -510,13 +510,13 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { "package-tool", "bin", ); - mkdirSync(binDir, { recursive: true }); - mkdirSync(packageBinDir, { recursive: true }); - const packageBinPath = path.join(packageBinDir, "package-tool.js"); - const symlinkPath = path.join(binDir, "package-tool"); - writeFileSync(packageBinPath, "#!/usr/bin/env node\n"); - chmodSync(packageBinPath, 0o755); - symlinkSync(packageBinPath, symlinkPath); + NodeFS.mkdirSync(binDir, { recursive: true }); + NodeFS.mkdirSync(packageBinDir, { recursive: true }); + const packageBinPath = NodePath.join(packageBinDir, "package-tool.js"); + const symlinkPath = NodePath.join(binDir, "package-tool"); + NodeFS.writeFileSync(packageBinPath, "#!/usr/bin/env node\n"); + NodeFS.chmodSync(packageBinPath, 0o755); + NodeFS.symlinkSync(packageBinPath, symlinkPath); const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, { binaryPath: symlinkPath, diff --git a/apps/server/src/relay/AgentAwarenessRelay.test.ts b/apps/server/src/relay/AgentAwarenessRelay.test.ts index eb2b2c3f2fa..e6b26efb3ff 100644 --- a/apps/server/src/relay/AgentAwarenessRelay.test.ts +++ b/apps/server/src/relay/AgentAwarenessRelay.test.ts @@ -1,4 +1,4 @@ -import { generateKeyPairSync } from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import * as NodeServices from "@effect/platform-node/NodeServices"; import type { @@ -348,7 +348,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { }); it("signs the activity publish JWT and rejects tampering", async () => { - const keyPair = generateKeyPairSync("ed25519", { + const keyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index 19988b20213..2202d30b837 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -1,7 +1,7 @@ import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import * as NodeSocket from "@effect/platform-node/NodeSocket"; import * as NodeServices from "@effect/platform-node/NodeServices"; -import { generateKeyPairSync, type KeyObject, sign } from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import { AuthAccessTokenType, @@ -950,14 +950,14 @@ const makeDpopProof = (input: { readonly iat: number; readonly accessToken?: string; readonly jti?: string; - readonly privateKey?: KeyObject; + readonly privateKey?: NodeCrypto.KeyObject; readonly publicJwk?: DpopPublicJwk; }) => { const keyPair = input.privateKey && input.publicJwk ? { privateKey: input.privateKey, publicJwk: input.publicJwk } : (() => { - const { privateKey, publicKey } = generateKeyPairSync("ec", { + const { privateKey, publicKey } = NodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256", }); return { privateKey, publicJwk: publicKey.export({ format: "jwk" }) as DpopPublicJwk }; @@ -978,7 +978,7 @@ const makeDpopProof = (input: { ...(input.accessToken ? { ath: computeDpopAccessTokenHash(input.accessToken) } : {}), }), ).toString("base64url"); - const signature = sign("sha256", Buffer.from(`${header}.${payload}`), { + const signature = NodeCrypto.sign("sha256", Buffer.from(`${header}.${payload}`), { key: keyPair.privateKey, dsaEncoding: "ieee-p1363", }).toString("base64url"); @@ -1024,7 +1024,7 @@ const makeCloudMintCredentialRequest = (input: { const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url"); const signingInput = `${header}.${encodedPayload}`; return { - proof: `${signingInput}.${sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`, + proof: `${signingInput}.${NodeCrypto.sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`, }; }; @@ -1057,7 +1057,7 @@ const makeCloudEnvironmentHealthRequest = (input: { const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url"); const signingInput = `${header}.${encodedPayload}`; return { - proof: `${signingInput}.${sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`, + proof: `${signingInput}.${NodeCrypto.sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`, }; }; @@ -2054,7 +2054,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2131,7 +2131,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2174,7 +2174,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2268,7 +2268,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, }); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2345,7 +2345,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2404,7 +2404,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2463,7 +2463,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2523,7 +2523,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2584,7 +2584,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2664,7 +2664,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, }); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2733,7 +2733,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2800,7 +2800,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2851,7 +2851,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2902,7 +2902,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -2967,7 +2967,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); @@ -3017,7 +3017,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { Effect.gen(function* () { yield* buildAppUnderTest(); - const cloudKeyPair = generateKeyPairSync("ed25519", { + const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); diff --git a/apps/server/src/terminal/NodePtyAdapter.ts b/apps/server/src/terminal/NodePtyAdapter.ts index e7b5406e7b9..7518901bfdd 100644 --- a/apps/server/src/terminal/NodePtyAdapter.ts +++ b/apps/server/src/terminal/NodePtyAdapter.ts @@ -1,4 +1,4 @@ -import { createRequire } from "node:module"; +import * as NodeModule from "node:module"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -11,7 +11,7 @@ import * as PtyAdapter from "./PtyAdapter.ts"; let didEnsureSpawnHelperExecutable = false; const resolveNodePtySpawnHelperPath = Effect.gen(function* () { - const requireForNodePty = createRequire(import.meta.url); + const requireForNodePty = NodeModule.createRequire(import.meta.url); const path = yield* Path.Path; const fs = yield* FileSystem.FileSystem; const platform = yield* HostProcessPlatform; diff --git a/apps/server/src/textGeneration/CursorTextGeneration.test.ts b/apps/server/src/textGeneration/CursorTextGeneration.test.ts index 5365d920471..2dc4720dcad 100644 --- a/apps/server/src/textGeneration/CursorTextGeneration.test.ts +++ b/apps/server/src/textGeneration/CursorTextGeneration.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as path from "node:path"; -import * as os from "node:os"; -import { fileURLToPath } from "node:url"; -import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeOS from "node:os"; +import * as NodeURL from "node:url"; +import * as NodeFS from "node:fs"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -21,8 +21,8 @@ import * as TextGeneration from "./TextGeneration.ts"; import { makeCursorTextGeneration } from "./CursorTextGeneration.ts"; const decodeCursorSettings = Schema.decodeSync(CursorSettings); -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const mockAgentPath = path.join(__dirname, "../../scripts/acp-mock-agent.ts"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const mockAgentPath = NodePath.join(__dirname, "../../scripts/acp-mock-agent.ts"); function shellSingleQuote(value: string): string { return `'${value.replaceAll("'", `'"'"'`)}'`; @@ -33,10 +33,10 @@ const CursorTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces }).pipe(Layer.provideMerge(NodeServices.layer)); function makeAcpAgentWrapper(dir: string, env: Record): string { - const binDir = path.join(dir, "bin"); - const agentPath = path.join(binDir, "agent"); - mkdirSync(binDir, { recursive: true }); - writeFileSync( + const binDir = NodePath.join(dir, "bin"); + const agentPath = NodePath.join(binDir, "agent"); + NodeFS.mkdirSync(binDir, { recursive: true }); + NodeFS.writeFileSync( agentPath, [ "#!/bin/sh", @@ -50,7 +50,7 @@ function makeAcpAgentWrapper(dir: string, env: Record): string { ].join("\n"), "utf8", ); - chmodSync(agentPath, 0o755); + NodeFS.chmodSync(agentPath, 0o755); return agentPath; } @@ -59,10 +59,10 @@ function withFakeAcpAgent( effectFn: (textGeneration: TextGeneration.TextGeneration["Service"]) => Effect.Effect, ) { return Effect.gen(function* () { - const tempDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-acp-")); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-acp-")); yield* Effect.addFinalizer(() => Effect.sync(() => { - rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }), ); const agentPath = makeAcpAgentWrapper(tempDir, env); @@ -76,7 +76,7 @@ function waitForFileContent(path: string): Effect.Effect { return Effect.gen(function* () { const deadline = (yield* Clock.currentTimeMillis) + 5_000; for (;;) { - const result = yield* Effect.exit(Effect.sync(() => readFileSync(path, "utf8"))); + const result = yield* Effect.exit(Effect.sync(() => NodeFS.readFileSync(path, "utf8"))); if (Exit.isSuccess(result)) { return result.value; } @@ -92,8 +92,10 @@ function waitForFileContent(path: string): Effect.Effect { it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => { it.effect("uses ACP model config options instead of raw CLI model ids", () => { - const requestLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-log-")); - const requestLogPath = path.join(requestLogDir, "requests.ndjson"); + const requestLogDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-log-"), + ); + const requestLogPath = NodePath.join(requestLogDir, "requests.ndjson"); return withFakeAcpAgent( { @@ -123,7 +125,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => { expect(generated.subject).toBe("Add generated commit message"); expect(generated.body).toBe("- verify cursor acp model config path"); - const requests = readFileSync(requestLogPath, "utf8") + const requests = NodeFS.readFileSync(requestLogPath, "utf8") .trim() .split("\n") .filter((line) => line.length > 0) @@ -181,7 +183,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => { ]), ); - rmSync(requestLogDir, { recursive: true, force: true }); + NodeFS.rmSync(requestLogDir, { recursive: true, force: true }); }), ); }); @@ -235,8 +237,10 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => { ); it.effect("closes the ACP child process after text generation completes", () => { - const exitLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-exit-log-")); - const exitLogPath = path.join(exitLogDir, "exit.log"); + const exitLogDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-exit-log-"), + ); + const exitLogPath = NodePath.join(exitLogDir, "exit.log"); return withFakeAcpAgent( { @@ -265,7 +269,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => { const exitLog = yield* waitForFileContent(exitLogPath); expect(exitLog).toContain("exit:0"); - rmSync(exitLogDir, { recursive: true, force: true }); + NodeFS.rmSync(exitLogDir, { recursive: true, force: true }); }), ); }); diff --git a/apps/server/src/textGeneration/GrokTextGeneration.test.ts b/apps/server/src/textGeneration/GrokTextGeneration.test.ts index 5df012cca85..85127b519b9 100644 --- a/apps/server/src/textGeneration/GrokTextGeneration.test.ts +++ b/apps/server/src/textGeneration/GrokTextGeneration.test.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off -import * as path from "node:path"; -import * as os from "node:os"; -import { fileURLToPath } from "node:url"; -import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeOS from "node:os"; +import * as NodeURL from "node:url"; +import * as NodeFS from "node:fs"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; @@ -18,8 +18,8 @@ import * as TextGeneration from "./TextGeneration.ts"; import { makeGrokTextGeneration } from "./GrokTextGeneration.ts"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const mockAgentPath = path.join(__dirname, "../../scripts/acp-mock-agent.ts"); +const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); +const mockAgentPath = NodePath.join(__dirname, "../../scripts/acp-mock-agent.ts"); function shellSingleQuote(value: string): string { return `'${value.replaceAll("'", `'"'"'`)}'`; @@ -30,10 +30,10 @@ const GrokTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(process. }).pipe(Layer.provideMerge(NodeServices.layer)); function makeAcpGrokWrapper(dir: string, env: Record): string { - const binDir = path.join(dir, "bin"); - const grokPath = path.join(binDir, "grok"); - mkdirSync(binDir, { recursive: true }); - writeFileSync( + const binDir = NodePath.join(dir, "bin"); + const grokPath = NodePath.join(binDir, "grok"); + NodeFS.mkdirSync(binDir, { recursive: true }); + NodeFS.writeFileSync( grokPath, [ "#!/bin/sh", @@ -47,7 +47,7 @@ function makeAcpGrokWrapper(dir: string, env: Record): string { ].join("\n"), "utf8", ); - chmodSync(grokPath, 0o755); + NodeFS.chmodSync(grokPath, 0o755); return grokPath; } @@ -56,10 +56,10 @@ function withFakeAcpGrok( effectFn: (textGeneration: TextGeneration.TextGeneration["Service"]) => Effect.Effect, ) { return Effect.gen(function* () { - const tempDir = mkdtempSync(path.join(os.tmpdir(), "t3code-grok-text-acp-")); + const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-acp-")); yield* Effect.addFinalizer(() => Effect.sync(() => { - rmSync(tempDir, { recursive: true, force: true }); + NodeFS.rmSync(tempDir, { recursive: true, force: true }); }), ); const binaryPath = makeAcpGrokWrapper(tempDir, env); @@ -72,7 +72,7 @@ function withFakeAcpGrok( function readJsonRpcRequests( filePath: string, ): ReadonlyArray<{ readonly method?: string; readonly params?: Record }> { - return readFileSync(filePath, "utf8") + return NodeFS.readFileSync(filePath, "utf8") .trim() .split("\n") .filter((line) => line.length > 0) @@ -81,8 +81,10 @@ function readJsonRpcRequests( it.layer(GrokTextGenerationTestLayer)("GrokTextGeneration", (it) => { it.effect("uses ACP with disabled tool capabilities and forwards the requested model id", () => { - const requestLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-grok-text-log-")); - const requestLogPath = path.join(requestLogDir, "requests.ndjson"); + const requestLogDir = NodeFS.mkdtempSync( + NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-log-"), + ); + const requestLogPath = NodePath.join(requestLogDir, "requests.ndjson"); return withFakeAcpGrok( { diff --git a/apps/server/src/vcs/GitVcsDriver.ts b/apps/server/src/vcs/GitVcsDriver.ts index e0c19bd3428..55aa8f38835 100644 --- a/apps/server/src/vcs/GitVcsDriver.ts +++ b/apps/server/src/vcs/GitVcsDriver.ts @@ -1,4 +1,4 @@ -import { randomUUID } from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import * as Context from "effect/Context"; import * as DateTime from "effect/DateTime"; @@ -651,7 +651,10 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* ( captureCheckpoint: Effect.fn("GitVcsDriver.checkpoints.captureCheckpoint")(function* (input) { const operation = "GitVcsDriver.checkpoints.captureCheckpoint"; const gitCommonDir = yield* resolveGitCommonDir(input.cwd); - const tempIndexPath = path.join(gitCommonDir, `t3-checkpoint-index-${randomUUID()}`); + const tempIndexPath = path.join( + gitCommonDir, + `t3-checkpoint-index-${NodeCrypto.randomUUID()}`, + ); const commitEnv: NodeJS.ProcessEnv = { ...process.env, GIT_INDEX_FILE: tempIndexPath, diff --git a/apps/server/src/workspace/WorkspaceEntries.test.ts b/apps/server/src/workspace/WorkspaceEntries.test.ts index 7d6005f030d..a08350ed959 100644 --- a/apps/server/src/workspace/WorkspaceEntries.test.ts +++ b/apps/server/src/workspace/WorkspaceEntries.test.ts @@ -1,13 +1,14 @@ // @effect-diagnostics nodeBuiltinImport:off -import fsPromises from "node:fs/promises"; +import * as NodeFSP from "node:fs/promises"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { FileFinder } from "@ff-labs/fff-node"; -import { it, afterEach, describe, expect, vi } from "@effect/vitest"; +import { it, afterEach, describe, expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as PlatformError from "effect/PlatformError"; +import { vi } from "vite-plus/test"; import * as ServerConfig from "../config.ts"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; @@ -15,6 +16,11 @@ import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as WorkspaceEntries from "./WorkspaceEntries.ts"; import * as WorkspacePaths from "./WorkspacePaths.ts"; +vi.mock("node:fs/promises", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, readdir: vi.fn(actual.readdir) }; +}); + const TestLayer = Layer.empty.pipe( Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), Layer.provideMerge(WorkspacePaths.layer), @@ -376,7 +382,7 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceEntries", (it) => { const cwd = yield* makeTempDir({ prefix: "t3code-workspace-browse-eacces-" }); const denied = Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" }); - vi.spyOn(fsPromises, "readdir").mockRejectedValueOnce(denied); + vi.mocked(NodeFSP.readdir).mockRejectedValueOnce(denied); const result = yield* workspaceEntries.browse({ partialPath: yield* appendSeparator(cwd), diff --git a/apps/server/src/workspace/WorkspaceEntries.ts b/apps/server/src/workspace/WorkspaceEntries.ts index 398b3d951b3..cdb26a38bc7 100644 --- a/apps/server/src/workspace/WorkspaceEntries.ts +++ b/apps/server/src/workspace/WorkspaceEntries.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import { readdir } from "node:fs/promises"; -import { homedir } from "node:os"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeOS from "node:os"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; @@ -98,10 +98,10 @@ export class WorkspaceEntries extends Context.Service< function expandHomePath(input: string, path: Path.Path): string { if (input === "~") { - return homedir(); + return NodeOS.homedir(); } if (input.startsWith("~/") || input.startsWith("~\\")) { - return path.join(homedir(), input.slice(2)); + return path.join(NodeOS.homedir(), input.slice(2)); } return input; } @@ -176,7 +176,7 @@ export const make = Effect.gen(function* () { const prefix = endsWithSeparator ? "" : path.basename(resolvedInputPath); const dirents = yield* Effect.tryPromise({ - try: () => readdir(parentPath, { withFileTypes: true }), + try: () => NodeFSP.readdir(parentPath, { withFileTypes: true }), catch: (cause) => new WorkspaceEntriesReadDirectoryError({ cwd: input.cwd, diff --git a/apps/server/src/workspace/WorkspaceFileSystem.ts b/apps/server/src/workspace/WorkspaceFileSystem.ts index 48e02c89cae..8cd176db3dd 100644 --- a/apps/server/src/workspace/WorkspaceFileSystem.ts +++ b/apps/server/src/workspace/WorkspaceFileSystem.ts @@ -7,7 +7,7 @@ * * @module WorkspaceFileSystem */ -import { open, realpath } from "node:fs/promises"; +import * as NodeFSP from "node:fs/promises"; import type { ProjectReadFileInput, @@ -89,8 +89,8 @@ export const make = Effect.gen(function* () { return yield* Effect.tryPromise({ try: async () => { const [realWorkspaceRoot, realTargetPath] = await Promise.all([ - realpath(input.cwd), - realpath(target.absolutePath), + NodeFSP.realpath(input.cwd), + NodeFSP.realpath(target.absolutePath), ]); const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath); if ( @@ -101,7 +101,7 @@ export const make = Effect.gen(function* () { throw new Error("Workspace file path resolves outside the project root."); } - const handle = await open(realTargetPath, "r"); + const handle = await NodeFSP.open(realTargetPath, "r"); try { const stat = await handle.stat(); if (!stat.isFile()) { diff --git a/apps/server/src/workspace/WorkspacePaths.ts b/apps/server/src/workspace/WorkspacePaths.ts index 8b6b685524b..85e3db561c4 100644 --- a/apps/server/src/workspace/WorkspacePaths.ts +++ b/apps/server/src/workspace/WorkspacePaths.ts @@ -6,7 +6,7 @@ * * @module WorkspacePaths */ -import { homedir } from "node:os"; +import * as NodeOS from "node:os"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; @@ -105,10 +105,10 @@ function toPosixRelativePath(input: string): string { function expandHomePath(input: string, path: Path.Path): string { if (input === "~") { - return homedir(); + return NodeOS.homedir(); } if (input.startsWith("~/") || input.startsWith("~\\")) { - return path.join(homedir(), input.slice(2)); + return path.join(NodeOS.homedir(), input.slice(2)); } return input; } diff --git a/oxlint-plugin-t3code/index.ts b/oxlint-plugin-t3code/index.ts index b8db9e16a36..400785be043 100644 --- a/oxlint-plugin-t3code/index.ts +++ b/oxlint-plugin-t3code/index.ts @@ -1,5 +1,6 @@ import { definePlugin } from "@oxlint/plugins"; +import namespaceNodeImports from "./rules/namespace-node-imports.ts"; import noGlobalProcessRuntime from "./rules/no-global-process-runtime.ts"; import noInlineSchemaCompile from "./rules/no-inline-schema-compile.ts"; import noManualEffectRuntimeInTests from "./rules/no-manual-effect-runtime-in-tests.ts"; @@ -9,6 +10,7 @@ export default definePlugin({ name: "t3code", }, rules: { + "namespace-node-imports": namespaceNodeImports, "no-global-process-runtime": noGlobalProcessRuntime, "no-inline-schema-compile": noInlineSchemaCompile, "no-manual-effect-runtime-in-tests": noManualEffectRuntimeInTests, diff --git a/oxlint-plugin-t3code/rules/namespace-node-imports.test.ts b/oxlint-plugin-t3code/rules/namespace-node-imports.test.ts new file mode 100644 index 00000000000..c097264ba8e --- /dev/null +++ b/oxlint-plugin-t3code/rules/namespace-node-imports.test.ts @@ -0,0 +1,63 @@ +import { assert, describe } from "@effect/vitest"; + +import { createOxlintRuleHarness } from "../test/utils.ts"; + +const rule = createOxlintRuleHarness("t3code/namespace-node-imports"); + +describe("t3code/namespace-node-imports", () => { + rule.valid( + "allows canonical Node namespaces", + ` + import * as NodeFS from "node:fs"; + import * as NodeFSP from "node:fs/promises"; + import * as NodeAssert from "node:assert/strict"; + import * as NodeChildProcess from "node:child_process"; + import * as NodeTimersPromises from "node:timers/promises"; + import type * as NodeStream from "node:stream"; + + NodeAssert.ok(NodeChildProcess.spawn && NodeTimersPromises.setTimeout); + export const read = NodeFS.readFileSync; + export const readAsync = NodeFSP.readFile; + export type Input = NodeStream.Readable; + `, + ); + + rule.valid( + "does not apply to non-Node packages", + ` + import { BrowserWindow } from "electron"; + `, + ); + + rule.invalid( + "reports named imports", + ` + import { readFile } from "node:fs/promises"; + `, + (output) => { + assert.match(output, /namespace named NodeFSP/); + }, + ); + + rule.invalid( + "reports default imports", + ` + import path from "node:path"; + `, + (output) => { + assert.match(output, /namespace named NodePath/); + }, + ); + + rule.invalid( + "reports non-canonical namespace aliases", + ` + import * as Crypto from "node:crypto"; + import * as NodeOs from "node:os"; + `, + (output) => { + assert.match(output, /namespace named NodeCrypto/); + assert.match(output, /namespace named NodeOS/); + }, + ); +}); diff --git a/oxlint-plugin-t3code/rules/namespace-node-imports.ts b/oxlint-plugin-t3code/rules/namespace-node-imports.ts new file mode 100644 index 00000000000..07d73dcf6b6 --- /dev/null +++ b/oxlint-plugin-t3code/rules/namespace-node-imports.ts @@ -0,0 +1,76 @@ +import { defineRule } from "@oxlint/plugins"; + +const NODE_MODULE_ALIASES = new Map([ + ["assert/strict", "Assert"], + ["fs/promises", "FSP"], +]); + +const NODE_SEGMENT_ALIASES = new Map([ + ["fs", "FS"], + ["os", "OS"], + ["url", "URL"], + ["vm", "VM"], +]); + +const toPascalCase = (value: string) => + value + .split(/[_-]/u) + .filter((segment) => segment.length > 0) + .map((segment) => segment[0]?.toUpperCase() + segment.slice(1)) + .join(""); + +const expectedNamespaceAlias = (source: string) => { + const moduleName = source.slice("node:".length); + const knownAlias = NODE_MODULE_ALIASES.get(moduleName); + if (knownAlias !== undefined) return `Node${knownAlias}`; + + return `Node${moduleName + .split("/") + .map((segment) => NODE_SEGMENT_ALIASES.get(segment) ?? toPascalCase(segment)) + .join("")}`; +}; + +const literalStringValue = (node: unknown): string | undefined => { + if (typeof node !== "object" || node === null) return undefined; + if (!("type" in node) || node.type !== "Literal") return undefined; + if (!("value" in node) || typeof node.value !== "string") return undefined; + return node.value; +}; + +const identifierName = (node: unknown): string | undefined => { + if (typeof node !== "object" || node === null) return undefined; + if (!("type" in node) || node.type !== "Identifier") return undefined; + if (!("name" in node) || typeof node.name !== "string") return undefined; + return node.name; +}; + +export default defineRule({ + meta: { + type: "problem", + docs: { + description: "Require canonical namespace imports for Node.js built-in modules.", + }, + }, + create(context) { + return { + ImportDeclaration(node) { + const source = literalStringValue(node.source); + if (source === undefined || !source.startsWith("node:")) return; + + const expectedAlias = expectedNamespaceAlias(source); + const namespaceImport = + node.specifiers.length === 1 && node.specifiers[0]?.type === "ImportNamespaceSpecifier" + ? node.specifiers[0] + : undefined; + const actualAlias = identifierName(namespaceImport?.local); + + if (actualAlias === expectedAlias) return; + + context.report({ + node, + message: `Import ${source} as a namespace named ${expectedAlias}.`, + }); + }, + }; + }, +}); diff --git a/packages/shared/src/logging.ts b/packages/shared/src/logging.ts index 8e1d1019e1d..e19d5cd0efa 100644 --- a/packages/shared/src/logging.ts +++ b/packages/shared/src/logging.ts @@ -1,6 +1,6 @@ // @effect-diagnostics nodeBuiltinImport:off -import fs from "node:fs"; -import path from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; export interface RotatingFileSinkOptions { readonly filePath: string; @@ -29,7 +29,7 @@ export class RotatingFileSink { this.maxFiles = options.maxFiles; this.throwOnError = options.throwOnError ?? false; - fs.mkdirSync(path.dirname(this.filePath), { recursive: true }); + NodeFS.mkdirSync(NodePath.dirname(this.filePath), { recursive: true }); this.pruneOverflowBackups(); this.currentSize = this.readCurrentSize(); } @@ -43,7 +43,7 @@ export class RotatingFileSink { this.rotate(); } - fs.appendFileSync(this.filePath, buffer); + NodeFS.appendFileSync(this.filePath, buffer); this.currentSize += buffer.length; if (this.currentSize > this.maxBytes) { @@ -60,20 +60,20 @@ export class RotatingFileSink { private rotate(): void { try { const oldest = this.withSuffix(this.maxFiles); - if (fs.existsSync(oldest)) { - fs.rmSync(oldest, { force: true }); + if (NodeFS.existsSync(oldest)) { + NodeFS.rmSync(oldest, { force: true }); } for (let index = this.maxFiles - 1; index >= 1; index -= 1) { const source = this.withSuffix(index); const target = this.withSuffix(index + 1); - if (fs.existsSync(source)) { - fs.renameSync(source, target); + if (NodeFS.existsSync(source)) { + NodeFS.renameSync(source, target); } } - if (fs.existsSync(this.filePath)) { - fs.renameSync(this.filePath, this.withSuffix(1)); + if (NodeFS.existsSync(this.filePath)) { + NodeFS.renameSync(this.filePath, this.withSuffix(1)); } this.currentSize = 0; @@ -87,13 +87,13 @@ export class RotatingFileSink { private pruneOverflowBackups(): void { try { - const dir = path.dirname(this.filePath); - const baseName = path.basename(this.filePath); - for (const entry of fs.readdirSync(dir)) { + const dir = NodePath.dirname(this.filePath); + const baseName = NodePath.basename(this.filePath); + for (const entry of NodeFS.readdirSync(dir)) { if (!entry.startsWith(`${baseName}.`)) continue; const suffix = Number(entry.slice(baseName.length + 1)); if (!Number.isInteger(suffix) || suffix <= this.maxFiles) continue; - fs.rmSync(path.join(dir, entry), { force: true }); + NodeFS.rmSync(NodePath.join(dir, entry), { force: true }); } } catch { if (this.throwOnError) { @@ -104,7 +104,7 @@ export class RotatingFileSink { private readCurrentSize(): number { try { - return fs.statSync(this.filePath).size; + return NodeFS.statSync(this.filePath).size; } catch { return 0; } diff --git a/packages/shared/src/shell.ts b/packages/shared/src/shell.ts index 5eab78b83d5..cf2f2417ff4 100644 --- a/packages/shared/src/shell.ts +++ b/packages/shared/src/shell.ts @@ -1,8 +1,8 @@ // @effect-diagnostics nodeBuiltinImport:off import * as NodeOS from "node:os"; import * as NodePath from "node:path"; -import { execFileSync } from "node:child_process"; -import { accessSync, constants as fileSystemConstants, statSync } from "node:fs"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -26,7 +26,7 @@ type ExecFileSyncLike = ( function canExecuteFile(filePath: string): boolean { try { - accessSync(filePath, fileSystemConstants.X_OK); + NodeFS.accessSync(filePath, NodeFS.constants.X_OK); return true; } catch { return false; @@ -108,7 +108,7 @@ function resolveSpawnExecutableWithNode( ); const isExecutable = (candidate: string) => { try { - if (!statSync(candidate).isFile()) return false; + if (!NodeFS.statSync(candidate).isFile()) return false; if (platform === "win32") { return windowsPathExtensions.includes(path.extname(candidate).toUpperCase()); } @@ -192,13 +192,13 @@ export function extractPathFromShellOutput(output: string): string | null { export function readPathFromLoginShell( shell: string, - execFile: ExecFileSyncLike = execFileSync, + execFile: ExecFileSyncLike = NodeChildProcess.execFileSync, ): string | undefined { return readEnvironmentFromLoginShell(shell, ["PATH"], execFile).PATH; } export function readPathFromLaunchctl( - execFile: ExecFileSyncLike = execFileSync, + execFile: ExecFileSyncLike = NodeChildProcess.execFileSync, ): string | undefined { try { return trimNonEmpty( @@ -305,7 +305,7 @@ export type ShellEnvironmentReader = ( export const readEnvironmentFromLoginShell: ShellEnvironmentReader = ( shell, names, - execFile = execFileSync, + execFile = NodeChildProcess.execFileSync, ) => { if (names.length === 0) { return {}; @@ -371,7 +371,7 @@ export function readEnvironmentFromWindowsShell( const execFile: ExecFileSyncLike = typeof optionsOrExecFile === "function" ? optionsOrExecFile - : (maybeExecFile ?? (execFileSync as ExecFileSyncLike)); + : (maybeExecFile ?? (NodeChildProcess.execFileSync as ExecFileSyncLike)); const command = buildWindowsEnvironmentCaptureCommand(names); const args = [ "-NoLogo", diff --git a/packages/ssh/src/command.ts b/packages/ssh/src/command.ts index aa48a1b357e..10927b43089 100644 --- a/packages/ssh/src/command.ts +++ b/packages/ssh/src/command.ts @@ -1,4 +1,4 @@ -import * as Crypto from "node:crypto"; +import * as NodeCrypto from "node:crypto"; import type { DesktopSshEnvironmentTarget, DesktopUpdateChannel } from "@t3tools/contracts"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; @@ -74,7 +74,10 @@ export function targetConnectionKey(target: DesktopSshEnvironmentTarget): string } export function remoteStateKey(target: DesktopSshEnvironmentTarget): string { - return Crypto.createHash("sha256").update(targetConnectionKey(target)).digest("hex").slice(0, 16); + return NodeCrypto.createHash("sha256") + .update(targetConnectionKey(target)) + .digest("hex") + .slice(0, 16); } export function buildSshHostSpec(target: DesktopSshEnvironmentTarget): string { diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts index 8aa95c3e68d..2d708057e38 100644 --- a/scripts/build-desktop-artifact.ts +++ b/scripts/build-desktop-artifact.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { createRequire } from "node:module"; +import * as NodeModule from "node:module"; import { fromYaml } from "@t3tools/shared/schemaYaml"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; @@ -482,7 +482,7 @@ const stageClerkPasskeyNativeBinaries = Effect.fn("stageClerkPasskeyNativeBinari path.join(stageAppDir, "node_modules", "@clerk", "electron-passkeys", "index.js"), ); const packageDir = path.dirname(packageEntryPath); - const packageRequire = createRequire(packageEntryPath); + const packageRequire = NodeModule.createRequire(packageEntryPath); for (const artifact of resolveClerkPasskeyNativeArtifacts(platform, arch)) { const sourcePath = yield* Effect.try({ diff --git a/scripts/lib/public-config.test.ts b/scripts/lib/public-config.test.ts index 6f6e8315664..62d383484cf 100644 --- a/scripts/lib/public-config.test.ts +++ b/scripts/lib/public-config.test.ts @@ -1,7 +1,7 @@ // @effect-diagnostics nodeBuiltinImport:off - Tests exercise root env file precedence directly. -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; import { afterEach, describe, expect, it } from "vite-plus/test"; import { loadRepoEnv, resolvePublicConfig } from "./public-config.ts"; @@ -10,7 +10,7 @@ const temporaryDirectories: string[] = []; afterEach(() => { for (const directory of temporaryDirectories.splice(0)) { - rmSync(directory, { recursive: true, force: true }); + NodeFS.rmSync(directory, { recursive: true, force: true }); } }); @@ -43,12 +43,12 @@ describe("loadRepoEnv", () => { it("applies process, root local, and root precedence in that order", () => { const repoRoot = makeTemporaryDirectory(); - writeFileSync( - join(repoRoot, ".env"), + NodeFS.writeFileSync( + NodePath.join(repoRoot, ".env"), "T3CODE_CLERK_PUBLISHABLE_KEY=pk_root\nT3CODE_CLERK_JWT_TEMPLATE=template_root\nT3CODE_CLERK_CLI_OAUTH_CLIENT_ID=oauth_root\nT3CODE_RELAY_URL=https://root.example.test\n", ); - writeFileSync( - join(repoRoot, ".env.local"), + NodeFS.writeFileSync( + NodePath.join(repoRoot, ".env.local"), "T3CODE_CLERK_PUBLISHABLE_KEY=pk_local\nT3CODE_CLERK_JWT_TEMPLATE=template_local\nT3CODE_CLERK_CLI_OAUTH_CLIENT_ID=oauth_local\nT3CODE_RELAY_URL=https://local.example.test\n", ); @@ -148,7 +148,7 @@ describe("loadRepoEnv", () => { }); function makeTemporaryDirectory() { - const directory = mkdtempSync(join(tmpdir(), "t3code-public-config-")); + const directory = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-public-config-")); temporaryDirectories.push(directory); return directory; } diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index 2fe67164666..45d2dd436b6 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -1,21 +1,13 @@ // @effect-diagnostics nodeBuiltinImport:off -import { execFileSync } from "node:child_process"; -import { - cpSync, - existsSync, - mkdirSync, - mkdtempSync, - readFileSync, - rmSync, - writeFileSync, -} from "node:fs"; -import { tmpdir } from "node:os"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; import * as Console from "effect/Console"; import * as Effect from "effect/Effect"; -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const repoRoot = NodePath.resolve(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), ".."); const workspaceFiles = [ "package.json", @@ -44,26 +36,26 @@ const workspaceFiles = [ function copyWorkspaceManifestFixture(targetRoot: string): void { for (const relativePath of workspaceFiles) { - const sourcePath = resolve(repoRoot, relativePath); - const destinationPath = resolve(targetRoot, relativePath); - mkdirSync(dirname(destinationPath), { recursive: true }); - cpSync(sourcePath, destinationPath); + const sourcePath = NodePath.resolve(repoRoot, relativePath); + const destinationPath = NodePath.resolve(targetRoot, relativePath); + NodeFS.mkdirSync(NodePath.dirname(destinationPath), { recursive: true }); + NodeFS.cpSync(sourcePath, destinationPath); } - const patchesDirectory = resolve(repoRoot, "patches"); - if (existsSync(patchesDirectory)) { - cpSync(patchesDirectory, resolve(targetRoot, "patches"), { recursive: true }); + const patchesDirectory = NodePath.resolve(repoRoot, "patches"); + if (NodeFS.existsSync(patchesDirectory)) { + NodeFS.cpSync(patchesDirectory, NodePath.resolve(targetRoot, "patches"), { recursive: true }); } } function writeMacManifestFixtures(targetRoot: string): { arm64Path: string; x64Path: string } { - const assetDirectory = resolve(targetRoot, "release-assets"); - mkdirSync(assetDirectory, { recursive: true }); + const assetDirectory = NodePath.resolve(targetRoot, "release-assets"); + NodeFS.mkdirSync(assetDirectory, { recursive: true }); - const arm64Path = resolve(assetDirectory, "latest-mac.yml"); - const x64Path = resolve(assetDirectory, "latest-mac-x64.yml"); + const arm64Path = NodePath.resolve(assetDirectory, "latest-mac.yml"); + const x64Path = NodePath.resolve(assetDirectory, "latest-mac-x64.yml"); - writeFileSync( + NodeFS.writeFileSync( arm64Path, `version: 9.9.9-smoke.0 files: @@ -79,7 +71,7 @@ releaseDate: '2026-03-08T10:32:14.587Z' `, ); - writeFileSync( + NodeFS.writeFileSync( x64Path, `version: 9.9.9-smoke.0 files: @@ -102,13 +94,13 @@ function writeWindowsManifestFixtures( targetRoot: string, channel: string, ): { arm64Path: string; x64Path: string } { - const assetDirectory = resolve(targetRoot, "release-assets"); - mkdirSync(assetDirectory, { recursive: true }); + const assetDirectory = NodePath.resolve(targetRoot, "release-assets"); + NodeFS.mkdirSync(assetDirectory, { recursive: true }); - const arm64Path = resolve(assetDirectory, `${channel}-win-arm64.yml`); - const x64Path = resolve(assetDirectory, `${channel}-win-x64.yml`); + const arm64Path = NodePath.resolve(assetDirectory, `${channel}-win-arm64.yml`); + const x64Path = NodePath.resolve(assetDirectory, `${channel}-win-x64.yml`); - writeFileSync( + NodeFS.writeFileSync( arm64Path, `version: 9.9.9-smoke.0 files: @@ -124,7 +116,7 @@ releaseDate: '2026-03-08T10:32:14.587Z' `, ); - writeFileSync( + NodeFS.writeFileSync( x64Path, `version: 9.9.9-smoke.0 files: @@ -147,11 +139,11 @@ function writeWindowsBuilderDebugFixtures(targetRoot: string): { arm64Path: string; x64Path: string; } { - const assetDirectory = resolve(targetRoot, "release-assets"); - mkdirSync(assetDirectory, { recursive: true }); + const assetDirectory = NodePath.resolve(targetRoot, "release-assets"); + NodeFS.mkdirSync(assetDirectory, { recursive: true }); - const arm64Path = resolve(assetDirectory, "builder-debug-win-arm64.yml"); - const x64Path = resolve(assetDirectory, "builder-debug-win-x64.yml"); + const arm64Path = NodePath.resolve(assetDirectory, "builder-debug-win-arm64.yml"); + const x64Path = NodePath.resolve(assetDirectory, "builder-debug-win-x64.yml"); const debugFixture = `arm64: firstOrDefaultFilePatterns: - '**/*' @@ -160,8 +152,8 @@ nsis: !include "example.nsh" `; - writeFileSync(arm64Path, debugFixture); - writeFileSync(x64Path, debugFixture); + NodeFS.writeFileSync(arm64Path, debugFixture); + NodeFS.writeFileSync(x64Path, debugFixture); return { arm64Path, x64Path }; } @@ -172,13 +164,13 @@ function assertContains(haystack: string, needle: string, message: string): void } function assertExists(path: string, message: string): void { - if (!existsSync(path)) { + if (!NodeFS.existsSync(path)) { throw new Error(message); } } function assertPackageVersion(path: string, version: string): void { - const packageJson = JSON.parse(readFileSync(path, "utf8")) as { + const packageJson = JSON.parse(NodeFS.readFileSync(path, "utf8")) as { readonly version?: unknown; }; @@ -188,20 +180,20 @@ function assertPackageVersion(path: string, version: string): void { } function assertMissing(path: string, message: string): void { - if (existsSync(path)) { + if (NodeFS.existsSync(path)) { throw new Error(message); } } -const tempRoot = mkdtempSync(join(tmpdir(), "t3-release-smoke-")); +const tempRoot = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-release-smoke-")); try { copyWorkspaceManifestFixture(tempRoot); - execFileSync( + NodeChildProcess.execFileSync( process.execPath, [ - resolve(repoRoot, "scripts/update-release-package-versions.ts"), + NodePath.resolve(repoRoot, "scripts/update-release-package-versions.ts"), "9.9.9-smoke.0", "--root", tempRoot, @@ -212,14 +204,14 @@ try { }, ); - rmSync(resolve(tempRoot, "pnpm-lock.yaml"), { force: true }); + NodeFS.rmSync(NodePath.resolve(tempRoot, "pnpm-lock.yaml"), { force: true }); - execFileSync("vp", ["install", "--lockfile-only", "--ignore-scripts"], { + NodeChildProcess.execFileSync("vp", ["install", "--lockfile-only", "--ignore-scripts"], { cwd: tempRoot, stdio: "inherit", }); - const lockfile = readFileSync(resolve(tempRoot, "pnpm-lock.yaml"), "utf8"); + const lockfile = NodeFS.readFileSync(NodePath.resolve(tempRoot, "pnpm-lock.yaml"), "utf8"); assertContains(lockfile, "lockfileVersion:", "Expected pnpm-lock.yaml to be regenerated."); for (const relativePath of [ @@ -228,13 +220,13 @@ try { "apps/web/package.json", "packages/contracts/package.json", ]) { - assertPackageVersion(resolve(tempRoot, relativePath), "9.9.9-smoke.0"); + assertPackageVersion(NodePath.resolve(tempRoot, relativePath), "9.9.9-smoke.0"); } - const nightlyReleaseMetadata = execFileSync( + const nightlyReleaseMetadata = NodeChildProcess.execFileSync( process.execPath, [ - resolve(repoRoot, "scripts/resolve-nightly-release.ts"), + NodePath.resolve(repoRoot, "scripts/resolve-nightly-release.ts"), "--date", "20260413", "--run-number", @@ -266,10 +258,10 @@ try { ); const { arm64Path, x64Path } = writeMacManifestFixtures(tempRoot); - execFileSync( + NodeChildProcess.execFileSync( process.execPath, [ - resolve(repoRoot, "scripts/merge-update-manifests.ts"), + NodePath.resolve(repoRoot, "scripts/merge-update-manifests.ts"), "--platform", "mac", arm64Path, @@ -281,7 +273,7 @@ try { }, ); - const mergedManifest = readFileSync(arm64Path, "utf8"); + const mergedManifest = NodeFS.readFileSync(arm64Path, "utf8"); assertContains( mergedManifest, "T3-Code-9.9.9-smoke.0-arm64.zip", @@ -297,21 +289,21 @@ try { tempRoot, "latest", ); - const mergedWindowsManifestPath = resolve(tempRoot, "release-assets/latest.yml"); + const mergedWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/latest.yml"); const { arm64Path: nightlyWinArm64Path, x64Path: nightlyWinX64Path } = writeWindowsManifestFixtures(tempRoot, "nightly"); - const mergedNightlyWindowsManifestPath = resolve(tempRoot, "release-assets/nightly.yml"); + const mergedNightlyWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/nightly.yml"); const { arm64Path: previewWinArm64Path, x64Path: previewWinX64Path } = writeWindowsManifestFixtures(tempRoot, "preview"); - const mergedPreviewWindowsManifestPath = resolve(tempRoot, "release-assets/preview.yml"); + const mergedPreviewWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/preview.yml"); const { arm64Path: winDebugArm64Path, x64Path: winDebugX64Path } = writeWindowsBuilderDebugFixtures(tempRoot); - execFileSync( + NodeChildProcess.execFileSync( "bash", [ "-lc", ` - release_assets_dir=${JSON.stringify(resolve(tempRoot, "release-assets"))} + release_assets_dir=${JSON.stringify(NodePath.resolve(tempRoot, "release-assets"))} shopt -s nullglob found_windows_manifest=false for x64_manifest in "$release_assets_dir"/*-win-x64.yml; do @@ -327,7 +319,7 @@ try { fi found_windows_manifest=true - ${JSON.stringify(process.execPath)} ${JSON.stringify(resolve(repoRoot, "scripts/merge-update-manifests.ts"))} --platform win \ + ${JSON.stringify(process.execPath)} ${JSON.stringify(NodePath.resolve(repoRoot, "scripts/merge-update-manifests.ts"))} --platform win \ "$arm64_manifest" \ "$x64_manifest" \ "$output_manifest" @@ -346,7 +338,7 @@ try { }, ); - const mergedWindowsManifest = readFileSync(mergedWindowsManifestPath, "utf8"); + const mergedWindowsManifest = NodeFS.readFileSync(mergedWindowsManifestPath, "utf8"); assertContains( mergedWindowsManifest, "T3-Code-9.9.9-smoke.0-arm64.exe", @@ -357,7 +349,10 @@ try { "T3-Code-9.9.9-smoke.0-x64.exe", "Merged Windows manifest is missing the x64 asset.", ); - const mergedNightlyWindowsManifest = readFileSync(mergedNightlyWindowsManifestPath, "utf8"); + const mergedNightlyWindowsManifest = NodeFS.readFileSync( + mergedNightlyWindowsManifestPath, + "utf8", + ); assertContains( mergedNightlyWindowsManifest, "T3-Code-9.9.9-smoke.0-arm64.exe", @@ -368,7 +363,10 @@ try { "T3-Code-9.9.9-smoke.0-x64.exe", "Merged nightly Windows manifest is missing the x64 asset.", ); - const mergedPreviewWindowsManifest = readFileSync(mergedPreviewWindowsManifestPath, "utf8"); + const mergedPreviewWindowsManifest = NodeFS.readFileSync( + mergedPreviewWindowsManifestPath, + "utf8", + ); assertContains( mergedPreviewWindowsManifest, "T3-Code-9.9.9-smoke.0-arm64.exe", @@ -411,5 +409,5 @@ try { Effect.runSync(Console.log("Release smoke checks passed.")); } finally { - rmSync(tempRoot, { recursive: true, force: true }); + NodeFS.rmSync(tempRoot, { recursive: true, force: true }); } diff --git a/vite.config.ts b/vite.config.ts index 1a8029b1656..967521100a0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,11 @@ import "vite-plus/test/config"; import { defineConfig } from "vite-plus"; -import { fileURLToPath } from "node:url"; +import * as NodeURL from "node:url"; export default defineConfig({ resolve: { alias: { - "~": fileURLToPath(new URL("./apps/web/src", import.meta.url)), + "~": NodeURL.fileURLToPath(new URL("./apps/web/src", import.meta.url)), }, }, test: { @@ -111,6 +111,7 @@ export default defineConfig({ "t3code/no-global-process-runtime": "error", "t3code/no-inline-schema-compile": "warn", "t3code/no-manual-effect-runtime-in-tests": "error", + "t3code/namespace-node-imports": "error", }, options: { // Revisit once Oxlint's tsgolint path can integrate with @effect/tsgo diagnostics.