authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 01:06:37-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 01:06:37-07:00
log2fa37ec183e0b99ca2ea77efbcb615f11019da75
tree5f9b229ed26c6b6fffb244af091a272d71bd6534
parentdb27502aff1add34eb2af4ff12c606e99bdc3610
signature Signed by PGP key B5690EEEBB952194

[codex] Enforce canonical Node namespace imports (#3238)

Co-authored-by: codex <codex@users.noreply.github.com>

71 files changed, 1205 insertions(+), 952 deletions(-)

.macroscope/check-run-agents/effect-service-conventions.md+1-1
......@@ -27,7 +27,7 @@ Review changed TypeScript and directly affected call sites for the conventions b
2727
2828- 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.
2929- 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.
30- 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`.
30- 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`.
3131- A package subpath that is itself a service module may use a namespace import when callers access its service/tag, `make`, or `layer` members.
3232- 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.
3333
apps/desktop/scripts/build-preview-annotation-css.mjs+16-16
......@@ -1,23 +1,23 @@
1import { readFile, writeFile } from "node:fs/promises";
2import { createRequire } from "node:module";
3import { dirname, join } from "node:path";
4import { fileURLToPath } from "node:url";
1import * as NodeFSP from "node:fs/promises";
2import * as NodeModule from "node:module";
3import * as NodePath from "node:path";
4import * as NodeURL from "node:url";
55
66import { compile } from "tailwindcss";
77
8const directory = dirname(fileURLToPath(import.meta.url));
9const appRoot = join(directory, "..");
10const sourcePath = join(appRoot, "src", "preview", "Annotation.css");
11const preloadPath = join(appRoot, "src", "preview", "PickPreload.ts");
12const outputPath = join(appRoot, "src", "preview", "AnnotationStyles.generated.ts");
13const require = createRequire(import.meta.url);
14const tailwindRoot = dirname(require.resolve("tailwindcss/package.json"));
8const directory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
9const appRoot = NodePath.join(directory, "..");
10const sourcePath = NodePath.join(appRoot, "src", "preview", "Annotation.css");
11const preloadPath = NodePath.join(appRoot, "src", "preview", "PickPreload.ts");
12const outputPath = NodePath.join(appRoot, "src", "preview", "AnnotationStyles.generated.ts");
13const require = NodeModule.createRequire(import.meta.url);
14const tailwindRoot = NodePath.dirname(require.resolve("tailwindcss/package.json"));
1515
1616const [annotationSource, preloadSource, themeSource, preflightSource] = await Promise.all([
17 readFile(sourcePath, "utf8"),
18 readFile(preloadPath, "utf8"),
19 readFile(join(tailwindRoot, "theme.css"), "utf8"),
20 readFile(join(tailwindRoot, "preflight.css"), "utf8"),
17 NodeFSP.readFile(sourcePath, "utf8"),
18 NodeFSP.readFile(preloadPath, "utf8"),
19 NodeFSP.readFile(NodePath.join(tailwindRoot, "theme.css"), "utf8"),
20 NodeFSP.readFile(NodePath.join(tailwindRoot, "preflight.css"), "utf8"),
2121]);
2222
2323const candidates = new Set(
......@@ -37,4 +37,4 @@ const encodedCss = `'${css
3737 .replaceAll("\n", "\\n")}'`;
3838const moduleSource = `// Generated by scripts/build-preview-annotation-css.mjs. Do not edit.\nexport const previewAnnotationStyles =\n ${encodedCss};\n`;
3939
40await writeFile(outputPath, moduleSource);
40await NodeFSP.writeFile(outputPath, moduleSource);
apps/desktop/scripts/dev-electron.mjs+13-9
......@@ -1,7 +1,7 @@
1import { spawn, spawnSync } from "node:child_process";
2import { watch } from "node:fs";
1import * as NodeChildProcess from "node:child_process";
2import * as NodeFS from "node:fs";
33import * as NodeOS from "node:os";
4import { join } from "node:path";
4import * as NodePath from "node:path";
55
66import {
77 desktopDir,
......@@ -64,7 +64,7 @@ function killChildTreeByPid(pid, signal) {
6464 return;
6565 }
6666
67 spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" });
67 NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(pid)], { stdio: "ignore" });
6868}
6969
7070function cleanupStaleDevApps() {
......@@ -72,7 +72,9 @@ function cleanupStaleDevApps() {
7272 return;
7373 }
7474
75 spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], { stdio: "ignore" });
75 NodeChildProcess.spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], {
76 stdio: "ignore",
77 });
7678}
7779
7880function startApp() {
......@@ -87,7 +89,7 @@ function startApp() {
8789 ? electronArgs
8890 : [...electronArgs, `--t3code-dev-root=${desktopDir}`, "dist-electron/main.cjs"];
8991 const electronCommand = resolveElectronLaunchCommand(launchArgs);
90 const app = spawn(electronCommand.electronPath, electronCommand.args, {
92 const app = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, {
9193 cwd: desktopDir,
9294 env: childEnv,
9395 stdio: "inherit",
......@@ -180,8 +182,8 @@ function scheduleRestart() {
180182
181183function startWatchers() {
182184 for (const { directory, files } of watchedDirectories) {
183 const watcher = watch(
184 join(desktopDir, directory),
185 const watcher = NodeFS.watch(
186 NodePath.join(desktopDir, directory),
185187 { persistent: true },
186188 (_eventType, filename) => {
187189 if (typeof filename !== "string" || !files.has(filename)) {
......@@ -202,7 +204,9 @@ function killChildTree(signal) {
202204 }
203205
204206 // Kill direct children as a final fallback in case normal shutdown leaves stragglers.
205 spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], { stdio: "ignore" });
207 NodeChildProcess.spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], {
208 stdio: "ignore",
209 });
206210}
207211
208212async function shutdown(exitCode) {
apps/desktop/scripts/electron-launcher.mjs+89-73
......@@ -1,29 +1,18 @@
11// This file mostly exists because we want dev mode to say "T3 Code (Dev)" instead of "electron"
22
3import { spawnSync } from "node:child_process";
4import {
5 copyFileSync,
6 chmodSync,
7 cpSync,
8 existsSync,
9 mkdirSync,
10 mkdtempSync,
11 readFileSync,
12 rmSync,
13 statSync,
14 writeFileSync,
15} from "node:fs";
16import { createRequire } from "node:module";
3import * as NodeChildProcess from "node:child_process";
4import * as NodeFS from "node:fs";
5import * as NodeModule from "node:module";
176import * as NodeOS from "node:os";
18import { basename, dirname, join, resolve } from "node:path";
19import { fileURLToPath } from "node:url";
7import * as NodePath from "node:path";
8import * as NodeURL from "node:url";
209import { ensureElectronRuntime } from "./ensure-electron-runtime.mjs";
2110
2211const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
23const __dirname = dirname(fileURLToPath(import.meta.url));
24export const desktopDir = resolve(__dirname, "..");
25const repoRoot = resolve(desktopDir, "..", "..");
26const devBundleIdSuffix = basename(repoRoot)
12const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
13export const desktopDir = NodePath.resolve(__dirname, "..");
14const repoRoot = NodePath.resolve(desktopDir, "..", "..");
15const devBundleIdSuffix = NodePath.basename(repoRoot)
2716 .toLowerCase()
2817 .replaceAll(/[^a-z0-9]+/g, "");
2918export const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
......@@ -32,22 +21,35 @@ export const APP_BUNDLE_ID = isDevelopment
3221 : "com.t3tools.t3code";
3322const APP_PROTOCOL_SCHEMES = isDevelopment ? ["t3code-dev"] : ["t3code"];
3423const LAUNCHER_VERSION = 12;
35const defaultIconPath = join(desktopDir, "resources", "icon.icns");
36const developmentMacIconPngPath = join(repoRoot, "assets", "dev", "blueprint-macos-1024.png");
24const defaultIconPath = NodePath.join(desktopDir, "resources", "icon.icns");
25const developmentMacIconPngPath = NodePath.join(
26 repoRoot,
27 "assets",
28 "dev",
29 "blueprint-macos-1024.png",
30);
3731// oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone launcher script has no Effect runtime.
3832const hostPlatform = NodeOS.platform();
3933
4034function setPlistString(plistPath, key, value) {
41 const replaceResult = spawnSync("plutil", ["-replace", key, "-string", value, plistPath], {
42 encoding: "utf8",
43 });
35 const replaceResult = NodeChildProcess.spawnSync(
36 "plutil",
37 ["-replace", key, "-string", value, plistPath],
38 {
39 encoding: "utf8",
40 },
41 );
4442 if (replaceResult.status === 0) {
4543 return;
4644 }
4745
48 const insertResult = spawnSync("plutil", ["-insert", key, "-string", value, plistPath], {
49 encoding: "utf8",
50 });
46 const insertResult = NodeChildProcess.spawnSync(
47 "plutil",
48 ["-insert", key, "-string", value, plistPath],
49 {
50 encoding: "utf8",
51 },
52 );
5153 if (insertResult.status === 0) {
5254 return;
5355 }
......@@ -58,16 +60,24 @@ function setPlistString(plistPath, key, value) {
5860
5961function setPlistJson(plistPath, key, value) {
6062 const serialized = JSON.stringify(value);
61 const replaceResult = spawnSync("plutil", ["-replace", key, "-json", serialized, plistPath], {
62 encoding: "utf8",
63 });
63 const replaceResult = NodeChildProcess.spawnSync(
64 "plutil",
65 ["-replace", key, "-json", serialized, plistPath],
66 {
67 encoding: "utf8",
68 },
69 );
6470 if (replaceResult.status === 0) {
6571 return;
6672 }
6773
68 const insertResult = spawnSync("plutil", ["-insert", key, "-json", serialized, plistPath], {
69 encoding: "utf8",
70 });
74 const insertResult = NodeChildProcess.spawnSync(
75 "plutil",
76 ["-insert", key, "-json", serialized, plistPath],
77 {
78 encoding: "utf8",
79 },
80 );
7181 if (insertResult.status === 0) {
7282 return;
7383 }
......@@ -77,7 +87,7 @@ function setPlistJson(plistPath, key, value) {
7787}
7888
7989function runChecked(command, args) {
80 const result = spawnSync(command, args, { encoding: "utf8" });
90 const result = NodeChildProcess.spawnSync(command, args, { encoding: "utf8" });
8191 if (result.status === 0) {
8292 return;
8393 }
......@@ -91,7 +101,7 @@ function shellSingleQuote(value) {
91101}
92102
93103function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) {
94 const mainEntryPath = join(desktopDir, "dist-electron", "main.cjs");
104 const mainEntryPath = NodePath.join(desktopDir, "dist-electron", "main.cjs");
95105 const envEntries = [
96106 ["VITE_DEV_SERVER_URL", process.env.VITE_DEV_SERVER_URL],
97107 ["T3CODE_PORT", process.env.T3CODE_PORT],
......@@ -101,7 +111,7 @@ function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) {
101111 ["T3CODE_OTLP_EXPORT_INTERVAL_MS", process.env.T3CODE_OTLP_EXPORT_INTERVAL_MS],
102112 ["T3CODE_DESKTOP_APP_USER_MODEL_ID", APP_BUNDLE_ID],
103113 ].filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0);
104 writeFileSync(
114 NodeFS.writeFileSync(
105115 targetBinaryPath,
106116 [
107117 "#!/bin/sh",
......@@ -110,7 +120,7 @@ function writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath) {
110120 "",
111121 ].join("\n"),
112122 );
113 chmodSync(targetBinaryPath, 0o755);
123 NodeFS.chmodSync(targetBinaryPath, 0o755);
114124}
115125
116126function registerMacLauncherBundle(appBundlePath) {
......@@ -140,21 +150,24 @@ function registerMacLauncherBundle(appBundlePath) {
140150}
141151
142152function ensureDevelopmentIconIcns(runtimeDir) {
143 const generatedIconPath = join(runtimeDir, "icon-dev.icns");
144 mkdirSync(runtimeDir, { recursive: true });
153 const generatedIconPath = NodePath.join(runtimeDir, "icon-dev.icns");
154 NodeFS.mkdirSync(runtimeDir, { recursive: true });
145155
146 if (!existsSync(developmentMacIconPngPath)) {
156 if (!NodeFS.existsSync(developmentMacIconPngPath)) {
147157 return defaultIconPath;
148158 }
149159
150 const sourceMtimeMs = statSync(developmentMacIconPngPath).mtimeMs;
151 if (existsSync(generatedIconPath) && statSync(generatedIconPath).mtimeMs >= sourceMtimeMs) {
160 const sourceMtimeMs = NodeFS.statSync(developmentMacIconPngPath).mtimeMs;
161 if (
162 NodeFS.existsSync(generatedIconPath) &&
163 NodeFS.statSync(generatedIconPath).mtimeMs >= sourceMtimeMs
164 ) {
152165 return generatedIconPath;
153166 }
154167
155 const iconsetRoot = mkdtempSync(join(runtimeDir, "dev-iconset-"));
156 const iconsetDir = join(iconsetRoot, "icon.iconset");
157 mkdirSync(iconsetDir, { recursive: true });
168 const iconsetRoot = NodeFS.mkdtempSync(NodePath.join(runtimeDir, "dev-iconset-"));
169 const iconsetDir = NodePath.join(iconsetRoot, "icon.iconset");
170 NodeFS.mkdirSync(iconsetDir, { recursive: true });
158171
159172 try {
160173 for (const size of [16, 32, 128, 256, 512]) {
......@@ -164,7 +177,7 @@ function ensureDevelopmentIconIcns(runtimeDir) {
164177 String(size),
165178 developmentMacIconPngPath,
166179 "--out",
167 join(iconsetDir, `icon_${size}x${size}.png`),
180 NodePath.join(iconsetDir, `icon_${size}x${size}.png`),
168181 ]);
169182
170183 const retinaSize = size * 2;
......@@ -174,7 +187,7 @@ function ensureDevelopmentIconIcns(runtimeDir) {
174187 String(retinaSize),
175188 developmentMacIconPngPath,
176189 "--out",
177 join(iconsetDir, `icon_${size}x${size}@2x.png`),
190 NodePath.join(iconsetDir, `icon_${size}x${size}@2x.png`),
178191 ]);
179192 }
180193
......@@ -187,12 +200,12 @@ function ensureDevelopmentIconIcns(runtimeDir) {
187200 );
188201 return defaultIconPath;
189202 } finally {
190 rmSync(iconsetRoot, { recursive: true, force: true });
203 NodeFS.rmSync(iconsetRoot, { recursive: true, force: true });
191204 }
192205}
193206
194207function patchMainBundleInfoPlist(appBundlePath, iconPath) {
195 const infoPlistPath = join(appBundlePath, "Contents", "Info.plist");
208 const infoPlistPath = NodePath.join(appBundlePath, "Contents", "Info.plist");
196209 setPlistString(infoPlistPath, "CFBundleDisplayName", APP_DISPLAY_NAME);
197210 setPlistString(infoPlistPath, "CFBundleName", APP_DISPLAY_NAME);
198211 setPlistString(infoPlistPath, "CFBundleIdentifier", APP_BUNDLE_ID);
......@@ -204,9 +217,9 @@ function patchMainBundleInfoPlist(appBundlePath, iconPath) {
204217 },
205218 ]);
206219
207 const resourcesDir = join(appBundlePath, "Contents", "Resources");
208 copyFileSync(iconPath, join(resourcesDir, "icon.icns"));
209 copyFileSync(iconPath, join(resourcesDir, "electron.icns"));
220 const resourcesDir = NodePath.join(appBundlePath, "Contents", "Resources");
221 NodeFS.copyFileSync(iconPath, NodePath.join(resourcesDir, "icon.icns"));
222 NodeFS.copyFileSync(iconPath, NodePath.join(resourcesDir, "electron.icns"));
210223}
211224
212225function patchHelperBundleInfoPlists(appBundlePath) {
......@@ -218,7 +231,7 @@ function patchHelperBundleInfoPlists(appBundlePath) {
218231 ];
219232
220233 for (const [bundleName, bundleIdentifierSuffix, bundleDisplayName] of helperBundleNames) {
221 const infoPlistPath = join(
234 const infoPlistPath = NodePath.join(
222235 appBundlePath,
223236 "Contents",
224237 "Frameworks",
......@@ -226,7 +239,7 @@ function patchHelperBundleInfoPlists(appBundlePath) {
226239 "Contents",
227240 "Info.plist",
228241 );
229 if (!existsSync(infoPlistPath)) {
242 if (!NodeFS.existsSync(infoPlistPath)) {
230243 continue;
231244 }
232245
......@@ -242,34 +255,34 @@ function patchHelperBundleInfoPlists(appBundlePath) {
242255
243256function readJson(path) {
244257 try {
245 return JSON.parse(readFileSync(path, "utf8"));
258 return JSON.parse(NodeFS.readFileSync(path, "utf8"));
246259 } catch {
247260 return null;
248261 }
249262}
250263
251264function buildMacLauncher(electronBinaryPath) {
252 const sourceAppBundlePath = resolve(dirname(electronBinaryPath), "../..");
253 const runtimeDir = join(desktopDir, ".electron-runtime");
254 const targetAppBundlePath = join(runtimeDir, `${APP_DISPLAY_NAME}.app`);
255 const targetBinaryPath = join(targetAppBundlePath, "Contents", "MacOS", "Electron");
265 const sourceAppBundlePath = NodePath.resolve(NodePath.dirname(electronBinaryPath), "../..");
266 const runtimeDir = NodePath.join(desktopDir, ".electron-runtime");
267 const targetAppBundlePath = NodePath.join(runtimeDir, `${APP_DISPLAY_NAME}.app`);
268 const targetBinaryPath = NodePath.join(targetAppBundlePath, "Contents", "MacOS", "Electron");
256269 const iconPath = isDevelopment ? ensureDevelopmentIconIcns(runtimeDir) : defaultIconPath;
257 const metadataPath = join(runtimeDir, "metadata.json");
270 const metadataPath = NodePath.join(runtimeDir, "metadata.json");
258271
259 mkdirSync(runtimeDir, { recursive: true });
272 NodeFS.mkdirSync(runtimeDir, { recursive: true });
260273
261274 const expectedMetadata = {
262275 launcherVersion: LAUNCHER_VERSION,
263276 sourceAppBundlePath,
264 sourceAppMtimeMs: statSync(sourceAppBundlePath).mtimeMs,
265 iconMtimeMs: statSync(iconPath).mtimeMs,
277 sourceAppMtimeMs: NodeFS.statSync(sourceAppBundlePath).mtimeMs,
278 iconMtimeMs: NodeFS.statSync(iconPath).mtimeMs,
266279 appBundleId: APP_BUNDLE_ID,
267280 appProtocolSchemes: APP_PROTOCOL_SCHEMES,
268281 };
269282
270283 const currentMetadata = readJson(metadataPath);
271284 if (
272 existsSync(targetBinaryPath) &&
285 NodeFS.existsSync(targetBinaryPath) &&
273286 currentMetadata &&
274287 JSON.stringify(currentMetadata) === JSON.stringify(expectedMetadata)
275288 ) {
......@@ -277,18 +290,21 @@ function buildMacLauncher(electronBinaryPath) {
277290 return targetBinaryPath;
278291 }
279292
280 rmSync(targetAppBundlePath, { recursive: true, force: true });
293 NodeFS.rmSync(targetAppBundlePath, { recursive: true, force: true });
281294 // verbatimSymlinks keeps the framework's relative symlinks intact
282295 // (e.g. Resources -> Versions/Current/Resources). Without it cpSync
283296 // rewrites them to absolute paths into node_modules, which escape the
284297 // bundle and crash sandboxed helper processes (icudtl.dat not found).
285 cpSync(sourceAppBundlePath, targetAppBundlePath, { recursive: true, verbatimSymlinks: true });
298 NodeFS.cpSync(sourceAppBundlePath, targetAppBundlePath, {
299 recursive: true,
300 verbatimSymlinks: true,
301 });
286302 patchMainBundleInfoPlist(targetAppBundlePath, iconPath);
287303 patchHelperBundleInfoPlists(targetAppBundlePath);
288304 if (isDevelopment) {
289305 writeDevelopmentLauncherScript(targetBinaryPath, electronBinaryPath);
290306 }
291 writeFileSync(metadataPath, `${JSON.stringify(expectedMetadata, null, 2)}\n`);
307 NodeFS.writeFileSync(metadataPath, `${JSON.stringify(expectedMetadata, null, 2)}\n`);
292308 registerMacLauncherBundle(targetAppBundlePath);
293309
294310 return targetBinaryPath;
......@@ -299,9 +315,9 @@ function isLinuxSetuidSandboxConfigured(electronBinaryPath) {
299315 return true;
300316 }
301317
302 const sandboxPath = join(dirname(electronBinaryPath), "chrome-sandbox");
318 const sandboxPath = NodePath.join(NodePath.dirname(electronBinaryPath), "chrome-sandbox");
303319 try {
304 const sandboxStat = statSync(sandboxPath);
320 const sandboxStat = NodeFS.statSync(sandboxPath);
305321 return sandboxStat.uid === 0 && (sandboxStat.mode & 0o4777) === 0o4755;
306322 } catch {
307323 return false;
......@@ -322,7 +338,7 @@ function resolveLinuxSandboxArgs(electronBinaryPath) {
322338export function resolveElectronPath() {
323339 ensureElectronRuntime();
324340
325 const require = createRequire(import.meta.url);
341 const require = NodeModule.createRequire(import.meta.url);
326342 const electronBinaryPath = require("electron");
327343
328344 if (hostPlatform !== "darwin") {
......@@ -345,11 +361,11 @@ export function resolveDevProtocolClient() {
345361 return null;
346362 }
347363
348 const require = createRequire(import.meta.url);
364 const require = NodeModule.createRequire(import.meta.url);
349365 const electronBinaryPath = require("electron");
350366 const launcherBinaryPath = buildMacLauncher(electronBinaryPath);
351367 return {
352 appBundlePath: resolve(launcherBinaryPath, "..", "..", ".."),
368 appBundlePath: NodePath.resolve(launcherBinaryPath, "..", "..", ".."),
353369 appBundleId: APP_BUNDLE_ID,
354370 };
355371}
apps/desktop/scripts/ensure-electron-runtime.mjs+34-32
......@@ -1,14 +1,14 @@
1import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2import { createRequire } from "node:module";
3import { arch, platform, tmpdir } from "node:os";
4import { dirname, join } from "node:path";
5import { spawnSync } from "node:child_process";
1import * as NodeFS from "node:fs";
2import * as NodeModule from "node:module";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
5import * as NodeChildProcess from "node:child_process";
66
7const require = createRequire(import.meta.url);
7const require = NodeModule.createRequire(import.meta.url);
88// oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone repair script has no Effect runtime.
9const hostPlatform = platform();
9const hostPlatform = NodeOS.platform();
1010// oxlint-disable-next-line t3code/no-global-process-runtime -- Standalone repair script has no Effect runtime.
11const hostArch = arch();
11const hostArch = NodeOS.arch();
1212
1313function getPlatformPath() {
1414 switch (hostPlatform) {
......@@ -27,26 +27,28 @@ function getPlatformPath() {
2727
2828function ensureExecutable(filePath) {
2929 if (hostPlatform !== "win32") {
30 chmodSync(filePath, 0o755);
30 NodeFS.chmodSync(filePath, 0o755);
3131 }
3232}
3333
3434function repairPathFile(electronDir, platformPath) {
35 const pathFile = join(electronDir, "path.txt");
36 const currentPath = existsSync(pathFile) ? readFileSync(pathFile, "utf8") : undefined;
35 const pathFile = NodePath.join(electronDir, "path.txt");
36 const currentPath = NodeFS.existsSync(pathFile)
37 ? NodeFS.readFileSync(pathFile, "utf8")
38 : undefined;
3739
3840 if (currentPath !== platformPath) {
39 writeFileSync(pathFile, platformPath);
41 NodeFS.writeFileSync(pathFile, platformPath);
4042 }
4143}
4244
4345function getRequiredRuntimePaths(electronDir, platformPath) {
44 const paths = [join(electronDir, "dist", platformPath)];
46 const paths = [NodePath.join(electronDir, "dist", platformPath)];
4547
4648 if (hostPlatform === "darwin") {
4749 paths.push(
48 join(electronDir, "dist", "Electron.app", "Contents", "Info.plist"),
49 join(
50 NodePath.join(electronDir, "dist", "Electron.app", "Contents", "Info.plist"),
51 NodePath.join(
5052 electronDir,
5153 "dist",
5254 "Electron.app",
......@@ -66,7 +68,7 @@ function isMachO(filePath) {
6668 return true;
6769 }
6870
69 const result = spawnSync("file", ["-b", filePath], {
71 const result = NodeChildProcess.spawnSync("file", ["-b", filePath], {
7072 encoding: "utf8",
7173 });
7274
......@@ -75,7 +77,7 @@ function isMachO(filePath) {
7577
7678function missingRuntimePaths(electronDir, platformPath) {
7779 return getRequiredRuntimePaths(electronDir, platformPath).filter((runtimePath) => {
78 return !existsSync(runtimePath);
80 return !NodeFS.existsSync(runtimePath);
7981 });
8082}
8183
......@@ -85,8 +87,8 @@ function invalidRuntimePaths(electronDir, platformPath) {
8587 }
8688
8789 return [
88 join(electronDir, "dist", platformPath),
89 join(
90 NodePath.join(electronDir, "dist", platformPath),
91 NodePath.join(
9092 electronDir,
9193 "dist",
9294 "Electron.app",
......@@ -95,11 +97,11 @@ function invalidRuntimePaths(electronDir, platformPath) {
9597 "Electron Framework.framework",
9698 "Electron Framework",
9799 ),
98 ].filter((runtimePath) => existsSync(runtimePath) && !isMachO(runtimePath));
100 ].filter((runtimePath) => NodeFS.existsSync(runtimePath) && !isMachO(runtimePath));
99101}
100102
101103function runChecked(command, args) {
102 const result = spawnSync(command, args, {
104 const result = NodeChildProcess.spawnSync(command, args, {
103105 encoding: "utf8",
104106 stdio: "inherit",
105107 });
......@@ -114,8 +116,8 @@ function runChecked(command, args) {
114116}
115117
116118function installElectronRuntime(electronDir, version) {
117 const tempDir = mkdtempSync(join(tmpdir(), "t3-electron-"));
118 const zipPath = join(tempDir, `electron-v${version}-${hostPlatform}-${hostArch}.zip`);
119 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-electron-"));
120 const zipPath = NodePath.join(tempDir, `electron-v${version}-${hostPlatform}-${hostArch}.zip`);
119121
120122 try {
121123 runChecked("curl", [
......@@ -125,34 +127,34 @@ function installElectronRuntime(electronDir, version) {
125127 zipPath,
126128 ]);
127129 if (hostPlatform === "darwin") {
128 runChecked("ditto", ["-x", "-k", zipPath, join(electronDir, "dist")]);
130 runChecked("ditto", ["-x", "-k", zipPath, NodePath.join(electronDir, "dist")]);
129131 } else {
130132 runChecked("python3", [
131133 "-c",
132134 "import os, sys, zipfile; os.makedirs(sys.argv[2], exist_ok=True); zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])",
133135 zipPath,
134 join(electronDir, "dist"),
136 NodePath.join(electronDir, "dist"),
135137 ]);
136138 }
137139 } finally {
138 rmSync(tempDir, { recursive: true, force: true });
140 NodeFS.rmSync(tempDir, { recursive: true, force: true });
139141 }
140142}
141143
142144export function ensureElectronRuntime() {
143145 const electronPackageJsonPath = require.resolve("electron/package.json");
144 const electronPackageJson = JSON.parse(readFileSync(electronPackageJsonPath, "utf8"));
145 const electronDir = dirname(electronPackageJsonPath);
146 const electronPackageJson = JSON.parse(NodeFS.readFileSync(electronPackageJsonPath, "utf8"));
147 const electronDir = NodePath.dirname(electronPackageJsonPath);
146148 const platformPath = getPlatformPath();
147 const electronPath = join(electronDir, "dist", platformPath);
149 const electronPath = NodePath.join(electronDir, "dist", platformPath);
148150 const missingBeforeInstall = missingRuntimePaths(electronDir, platformPath);
149151 const invalidBeforeInstall = invalidRuntimePaths(electronDir, platformPath);
150152
151153 if (missingBeforeInstall.length > 0 || invalidBeforeInstall.length > 0) {
152 if (existsSync(join(electronDir, "dist"))) {
153 rmSync(join(electronDir, "dist"), { recursive: true, force: true });
154 if (NodeFS.existsSync(NodePath.join(electronDir, "dist"))) {
155 NodeFS.rmSync(NodePath.join(electronDir, "dist"), { recursive: true, force: true });
154156 }
155 rmSync(join(electronDir, "path.txt"), { force: true });
157 NodeFS.rmSync(NodePath.join(electronDir, "path.txt"), { force: true });
156158 installElectronRuntime(electronDir, electronPackageJson.version);
157159 }
158160
apps/desktop/scripts/smoke-test.mjs+7-7
......@@ -1,16 +1,16 @@
1import { spawn } from "node:child_process";
2import { dirname, resolve } from "node:path";
3import { fileURLToPath } from "node:url";
1import * as NodeChildProcess from "node:child_process";
2import * as NodePath from "node:path";
3import * as NodeURL from "node:url";
44import { resolveElectronLaunchCommand } from "./electron-launcher.mjs";
55
6const __dirname = dirname(fileURLToPath(import.meta.url));
7const desktopDir = resolve(__dirname, "..");
8const mainJs = resolve(desktopDir, "dist-electron/main.cjs");
6const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
7const desktopDir = NodePath.resolve(__dirname, "..");
8const mainJs = NodePath.resolve(desktopDir, "dist-electron/main.cjs");
99
1010console.log("\nLaunching Electron smoke test...");
1111
1212const electronCommand = resolveElectronLaunchCommand([mainJs]);
13const child = spawn(electronCommand.electronPath, electronCommand.args, {
13const child = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, {
1414 stdio: ["pipe", "pipe", "pipe"],
1515 env: {
1616 ...process.env,
apps/desktop/scripts/start-electron.mjs+2-2
......@@ -1,4 +1,4 @@
1import { spawn } from "node:child_process";
1import * as NodeChildProcess from "node:child_process";
22
33import { desktopDir, resolveElectronLaunchCommand } from "./electron-launcher.mjs";
44
......@@ -6,7 +6,7 @@ const childEnv = { ...process.env };
66delete childEnv.ELECTRON_RUN_AS_NODE;
77
88const electronCommand = resolveElectronLaunchCommand(["dist-electron/main.cjs"]);
9const child = spawn(electronCommand.electronPath, electronCommand.args, {
9const child = NodeChildProcess.spawn(electronCommand.electronPath, electronCommand.args, {
1010 stdio: "inherit",
1111 cwd: desktopDir,
1212 env: childEnv,
apps/desktop/scripts/wait-for-resources.mjs+8-8
......@@ -1,13 +1,13 @@
1import * as FileSystem from "node:fs/promises";
2import * as Net from "node:net";
3import * as Path from "node:path";
4import * as Timers from "node:timers/promises";
1import * as NodeFSP from "node:fs/promises";
2import * as NodeNet from "node:net";
3import * as NodePath from "node:path";
4import * as NodeTimersPromises from "node:timers/promises";
55
66const defaultTcpHosts = ["127.0.0.1", "localhost", "::1"];
77
88async function fileExists(filePath) {
99 try {
10 await FileSystem.access(filePath);
10 await NodeFSP.access(filePath);
1111 return true;
1212 } catch {
1313 return false;
......@@ -16,7 +16,7 @@ async function fileExists(filePath) {
1616
1717function tcpPortIsReady({ host, port, connectTimeoutMs = 500 }) {
1818 return new Promise((resolveReady) => {
19 const socket = Net.createConnection({ host, port });
19 const socket = NodeNet.createConnection({ host, port });
2020 let settled = false;
2121
2222 const finish = (ready) => {
......@@ -47,7 +47,7 @@ async function resolvePendingResources({ baseDir, files, tcpPort, tcpHosts, conn
4747 const pendingFiles = [];
4848
4949 for (const relativeFilePath of files) {
50 const ready = await fileExists(Path.resolve(baseDir, relativeFilePath));
50 const ready = await fileExists(NodePath.resolve(baseDir, relativeFilePath));
5151 if (!ready) {
5252 pendingFiles.push(relativeFilePath);
5353 }
......@@ -114,6 +114,6 @@ export async function waitForResources({
114114 );
115115 }
116116
117 await Timers.setTimeout(intervalMs);
117 await NodeTimersPromises.setTimeout(intervalMs);
118118 }
119119}
apps/desktop/src/backend/DesktopNetworkInterfaces.ts+2-2
......@@ -1,4 +1,4 @@
1import { networkInterfaces } from "node:os";
1import * as NodeOS from "node:os";
22
33import * as Context from "effect/Context";
44import * as Effect from "effect/Effect";
......@@ -27,7 +27,7 @@ export class DesktopNetworkInterfaces extends Context.Service<
2727
2828export const make = (): DesktopNetworkInterfaces["Service"] =>
2929 DesktopNetworkInterfaces.of({
30 read: Effect.sync(() => networkInterfaces()),
30 read: Effect.sync(() => NodeOS.networkInterfaces()),
3131 });
3232
3333export const layer = Layer.succeed(DesktopNetworkInterfaces, make());
apps/desktop/src/ipc/methods/preview.ts+2-2
......@@ -22,7 +22,7 @@ import {
2222import { BrowserWindow } from "electron";
2323import * as Effect from "effect/Effect";
2424import * as Schema from "effect/Schema";
25import { pathToFileURL } from "node:url";
25import * as NodeURL from "node:url";
2626
2727import * as PreviewManager from "../../preview/Manager.ts";
2828import { PREVIEW_WEBVIEW_PREFERENCES } from "../../preview/WebviewPreferences.ts";
......@@ -196,7 +196,7 @@ export const getPreviewConfig = DesktopIpc.makeIpcMethod({
196196 return {
197197 partition: yield* manager.getBrowserPartition(environmentId),
198198 webPreferences: PREVIEW_WEBVIEW_PREFERENCES,
199 preloadUrl: pathToFileURL(`${__dirname}/preview-pick-preload.cjs`).href,
199 preloadUrl: NodeURL.pathToFileURL(`${__dirname}/preview-pick-preload.cjs`).href,
200200 };
201201 }),
202202});
apps/desktop/src/preview/PlaywrightInjectedRuntime.ts+11-7
......@@ -1,14 +1,14 @@
11// @effect-diagnostics nodeBuiltinImport:off - Extracts Playwright's installed Node bundle for browser injection.
2import { readFile } from "node:fs/promises";
3import { createRequire } from "node:module";
4import { dirname, join } from "node:path";
5import { runInNewContext } from "node:vm";
2import * as NodeFSP from "node:fs/promises";
3import * as NodeModule from "node:module";
4import * as NodePath from "node:path";
5import * as NodeVM from "node:vm";
66
77import * as Data from "effect/Data";
88import * as Effect from "effect/Effect";
99import * as Schema from "effect/Schema";
1010
11const require = createRequire(import.meta.url);
11const require = NodeModule.createRequire(import.meta.url);
1212const encodeUnknownJson = Schema.encodeUnknownEffect(Schema.UnknownFromJsonString);
1313
1414export class PlaywrightInjectedRuntimeError extends Data.TaggedError(
......@@ -32,7 +32,11 @@ export const playwrightInjectedRuntimeSource = Effect.fn("PlaywrightInjectedRunt
3232 catch: (cause) => fail("resolvePackage", cause),
3333 });
3434 const coreBundle = yield* Effect.tryPromise({
35 try: () => readFile(join(dirname(packageJsonPath), "lib/coreBundle.js"), "utf8"),
35 try: () =>
36 NodeFSP.readFile(
37 NodePath.join(NodePath.dirname(packageJsonPath), "lib/coreBundle.js"),
38 "utf8",
39 ),
3640 catch: (cause) => fail("readCoreBundle", cause),
3741 });
3842 const marker = "source3 = ";
......@@ -53,7 +57,7 @@ export const playwrightInjectedRuntimeSource = Effect.fn("PlaywrightInjectedRunt
5357 }
5458 const literal = coreBundle.slice(literalStart, literalEnd);
5559 const source = yield* Effect.try({
56 try: () => runInNewContext(literal, Object.create(null), { timeout: 1_000 }),
60 try: () => NodeVM.runInNewContext(literal, Object.create(null), { timeout: 1_000 }),
5761 catch: (cause) => fail("evaluateSourceLiteral", cause),
5862 });
5963 if (typeof source !== "string" || source.length < 100_000) {
apps/mobile/modules/t3-markdown-text/scripts/sync-pierre-file-icons.mjs+21-18
......@@ -1,16 +1,19 @@
1import { execFileSync } from "node:child_process";
2import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3import { dirname, join, resolve } from "node:path";
4import { fileURLToPath } from "node:url";
1import * as NodeChildProcess from "node:child_process";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeURL from "node:url";
55
66import { getBuiltInSpriteSheet } from "@pierre/trees";
77
8const scriptDirectory = dirname(fileURLToPath(import.meta.url));
9const moduleDirectory = resolve(scriptDirectory, "..");
10const repositoryRoot = resolve(moduleDirectory, "../../../..");
11const outputDirectory = join(moduleDirectory, "assets/file-icons");
12const generatedModulePath = join(moduleDirectory, "src/markdownFileIcons.generated.ts");
13const webIconSource = readFileSync(join(repositoryRoot, "apps/web/src/pierre-icons.ts"), "utf8");
8const scriptDirectory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
9const moduleDirectory = NodePath.resolve(scriptDirectory, "..");
10const repositoryRoot = NodePath.resolve(moduleDirectory, "../../../..");
11const outputDirectory = NodePath.join(moduleDirectory, "assets/file-icons");
12const generatedModulePath = NodePath.join(moduleDirectory, "src/markdownFileIcons.generated.ts");
13const webIconSource = NodeFS.readFileSync(
14 NodePath.join(repositoryRoot, "apps/web/src/pierre-icons.ts"),
15 "utf8",
16);
1417const customSprite = webIconSource.match(/const T3_FILE_ICON_SPRITE = `([\s\S]*?)`;/)?.[1];
1518
1619if (!customSprite) {
......@@ -95,20 +98,20 @@ function symbolFromSprite(sprite, id) {
9598}
9699
97100function renderIcon(token, symbol, color) {
98 const svgPath = join(outputDirectory, `.pierre-${token}.svg`);
99 const pngPath = join(outputDirectory, `pierre_${token}.png`);
100 writeFileSync(
101 const svgPath = NodePath.join(outputDirectory, `.pierre-${token}.svg`);
102 const pngPath = NodePath.join(outputDirectory, `pierre_${token}.png`);
103 NodeFS.writeFileSync(
101104 svgPath,
102105 `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="${symbol.viewBox}" style="color:${color}">${symbol.body}</svg>`,
103106 );
104 execFileSync("sips", ["-s", "format", "png", svgPath, "--out", pngPath], {
107 NodeChildProcess.execFileSync("sips", ["-s", "format", "png", svgPath, "--out", pngPath], {
105108 stdio: "ignore",
106109 });
107 rmSync(svgPath);
110 NodeFS.rmSync(svgPath);
108111}
109112
110rmSync(outputDirectory, { recursive: true, force: true });
111mkdirSync(outputDirectory, { recursive: true });
113NodeFS.rmSync(outputDirectory, { recursive: true, force: true });
114NodeFS.mkdirSync(outputDirectory, { recursive: true });
112115
113116const builtInSprite = getBuiltInSpriteSheet("complete");
114117const builtInTokens = [...builtInSprite.matchAll(/<symbol id="file-tree-builtin-([^"]+)"/g)]
......@@ -130,4 +133,4 @@ const tokens = [...new Set([...builtInTokens, ...Object.keys(customIcons)])].sor
130133const generatedSource = `import type { ImageSourcePropType } from "react-native";\n\nexport const MARKDOWN_FILE_ICON_SOURCES = {\n${tokens
131134 .map((token) => ` ${token}: require("../assets/file-icons/pierre_${token}.png"),`)
132135 .join("\n")}\n} as const satisfies Readonly<Record<string, ImageSourcePropType>>;\n`;
133writeFileSync(generatedModulePath, generatedSource);
136NodeFS.writeFileSync(generatedModulePath, generatedSource);
apps/server/integration/OrchestrationEngineHarness.integration.ts+2-2
......@@ -1,5 +1,5 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { execFileSync } from "node:child_process";
2import * as NodeChildProcess from "node:child_process";
33
44import * as NodeServices from "@effect/platform-node/NodeServices";
55import {
......@@ -82,7 +82,7 @@ import * as AgentAwarenessRelay from "../src/relay/AgentAwarenessRelay.ts";
8282const decodeCodexSettings = Schema.decodeEffect(CodexSettings);
8383
8484function runGit(cwd: string, args: ReadonlyArray<string>) {
85 return execFileSync("git", args, {
85 return NodeChildProcess.execFileSync("git", args, {
8686 cwd,
8787 stdio: ["ignore", "pipe", "pipe"],
8888 encoding: "utf8",
apps/server/integration/orchestrationEngine.integration.test.ts+12-9
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
44
55import {
66 ApprovalRequestId,
......@@ -409,7 +409,7 @@ it.live("runs multi-turn file edits and persists checkpoint diffs", () =>
409409 ],
410410 mutateWorkspace: ({ cwd }) =>
411411 Effect.sync(() => {
412 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
412 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8");
413413 }),
414414 });
415415
......@@ -456,7 +456,7 @@ it.live("runs multi-turn file edits and persists checkpoint diffs", () =>
456456 ],
457457 mutateWorkspace: ({ cwd }) =>
458458 Effect.sync(() => {
459 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
459 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8");
460460 }),
461461 });
462462
......@@ -752,7 +752,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git
752752 ],
753753 mutateWorkspace: ({ cwd }) =>
754754 Effect.sync(() => {
755 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
755 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8");
756756 }),
757757 });
758758 yield* startTurn({
......@@ -811,7 +811,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git
811811 ],
812812 mutateWorkspace: ({ cwd }) =>
813813 Effect.sync(() => {
814 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
814 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8");
815815 }),
816816 });
817817 yield* startTurn({
......@@ -869,7 +869,10 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git
869869 ),
870870 true,
871871 );
872 assert.equal(fs.readFileSync(path.join(harness.workspaceDir, "README.md"), "utf8"), "v2\n");
872 assert.equal(
873 NodeFS.readFileSync(NodePath.join(harness.workspaceDir, "README.md"), "utf8"),
874 "v2\n",
875 );
873876 assert.equal(
874877 gitRefExists(harness.workspaceDir, checkpointRefForThreadTurn(THREAD_ID, 2)),
875878 false,
......@@ -1332,7 +1335,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state",
13321335 ],
13331336 mutateWorkspace: ({ cwd }) =>
13341337 Effect.sync(() => {
1335 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
1338 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8");
13361339 }),
13371340 });
13381341
......@@ -1390,7 +1393,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state",
13901393 ],
13911394 mutateWorkspace: ({ cwd }) =>
13921395 Effect.sync(() => {
1393 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
1396 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8");
13941397 }),
13951398 });
13961399
apps/server/scripts/acp-mock-agent.ts+3-3
......@@ -1,6 +1,6 @@
11#!/usr/bin/env node
22// @effect-diagnostics nodeBuiltinImport:off
3import { appendFileSync } from "node:fs";
3import * as NodeFS from "node:fs";
44
55import * as Effect from "effect/Effect";
66
......@@ -42,7 +42,7 @@ function logExit(reason: string): void {
4242 if (!exitLogPath) {
4343 return;
4444 }
45 appendFileSync(exitLogPath, `${reason}\n`, "utf8");
45 NodeFS.appendFileSync(exitLogPath, `${reason}\n`, "utf8");
4646}
4747
4848process.once("SIGTERM", () => {
......@@ -693,7 +693,7 @@ const program = Effect.gen(function* () {
693693 }
694694 const payload = event.payload;
695695 return Effect.sync(() => {
696 appendFileSync(
696 NodeFS.appendFileSync(
697697 requestLogPath,
698698 payload.endsWith("\n") ? payload : `${payload}\n`,
699699 "utf8",
apps/server/scripts/cursor-acp-model-mismatch-probe.ts+24-24
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
3import process from "node:process";
4import readline from "node:readline";
2import * as NodeChildProcess from "node:child_process";
3import * as NodeProcess from "node:process";
4import * as NodeReadline from "node:readline";
55import * as NodeTimers from "node:timers";
66import { resolveSpawnCommand } from "@t3tools/shared/shell";
77import * as Effect from "effect/Effect";
......@@ -56,19 +56,19 @@ type PendingRequest = {
5656 reject: (error: Error) => void;
5757};
5858
59const targetCwd = process.argv[2] ?? process.cwd();
60const targetModel = process.argv[3] ?? "gpt-5.4";
61const promptText = process.argv[4] ?? "helo";
62const targetReasoning = process.env.CURSOR_REASONING ?? "";
63const targetContext = process.env.CURSOR_CONTEXT ?? "";
64const targetFast = process.env.CURSOR_FAST ?? "";
65const agentBin = process.env.CURSOR_AGENT_BIN ?? "agent";
66const promptWaitMs = Number(process.env.CURSOR_PROMPT_WAIT_MS ?? "4000");
67const requestTimeoutMs = Number(process.env.CURSOR_REQUEST_TIMEOUT_MS ?? "20000");
59const targetCwd = NodeProcess.argv[2] ?? NodeProcess.cwd();
60const targetModel = NodeProcess.argv[3] ?? "gpt-5.4";
61const promptText = NodeProcess.argv[4] ?? "helo";
62const targetReasoning = NodeProcess.env.CURSOR_REASONING ?? "";
63const targetContext = NodeProcess.env.CURSOR_CONTEXT ?? "";
64const targetFast = NodeProcess.env.CURSOR_FAST ?? "";
65const agentBin = NodeProcess.env.CURSOR_AGENT_BIN ?? "agent";
66const promptWaitMs = Number(NodeProcess.env.CURSOR_PROMPT_WAIT_MS ?? "4000");
67const requestTimeoutMs = Number(NodeProcess.env.CURSOR_REQUEST_TIMEOUT_MS ?? "20000");
6868
6969function logSection(title: string, value: unknown) {
70 process.stdout.write(`\n=== ${title} ===\n`);
71 process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
70 NodeProcess.stdout.write(`\n=== ${title} ===\n`);
71 NodeProcess.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
7272}
7373
7474function fail(message: string): never {
......@@ -124,18 +124,18 @@ function sleep(ms: number) {
124124}
125125
126126class JsonRpcChild {
127 readonly child: ChildProcessWithoutNullStreams;
127 readonly child: NodeChildProcess.ChildProcessWithoutNullStreams;
128128 readonly pending = new Map<JsonRpcId, PendingRequest>();
129129 nextId = 1;
130130 closed = false;
131131
132132 constructor(bin: string, args: string[], cwd: string) {
133133 const spawnCommand = Effect.runSync(resolveSpawnCommand(bin, args));
134 this.child = spawn(spawnCommand.command, spawnCommand.args, {
134 this.child = NodeChildProcess.spawn(spawnCommand.command, spawnCommand.args, {
135135 cwd,
136136 shell: spawnCommand.shell,
137137 stdio: ["pipe", "pipe", "pipe"],
138 env: process.env,
138 env: NodeProcess.env,
139139 });
140140
141141 this.child.on("exit", (code, signal) => {
......@@ -155,14 +155,14 @@ class JsonRpcChild {
155155 this.pending.clear();
156156 });
157157
158 const stdout = readline.createInterface({ input: this.child.stdout });
158 const stdout = NodeReadline.createInterface({ input: this.child.stdout });
159159 stdout.on("line", (line) => {
160160 void this.handleStdoutLine(line);
161161 });
162162
163 const stderr = readline.createInterface({ input: this.child.stderr });
163 const stderr = NodeReadline.createInterface({ input: this.child.stderr });
164164 stderr.on("line", (line) => {
165 process.stdout.write(`[stderr] ${line}\n`);
165 NodeProcess.stdout.write(`[stderr] ${line}\n`);
166166 });
167167 }
168168
......@@ -175,7 +175,7 @@ class JsonRpcChild {
175175 headers: [],
176176 ...message,
177177 });
178 process.stdout.write(`>>> ${payload}\n`);
178 NodeProcess.stdout.write(`>>> ${payload}\n`);
179179 this.child.stdin.write(`${payload}\n`);
180180 }
181181
......@@ -240,13 +240,13 @@ class JsonRpcChild {
240240 return;
241241 }
242242
243 process.stdout.write(`<<< ${line}\n`);
243 NodeProcess.stdout.write(`<<< ${line}\n`);
244244
245245 let message: JsonRpcMessage;
246246 try {
247247 message = JSON.parse(line) as JsonRpcMessage;
248248 } catch (error) {
249 process.stdout.write(`[parse-error] ${(error as Error).message}\n`);
249 NodeProcess.stdout.write(`[parse-error] ${(error as Error).message}\n`);
250250 return;
251251 }
252252
......@@ -435,7 +435,7 @@ async function main() {
435435}
436436
437437void main().catch((error: unknown) => {
438 process.stderr.write(
438 NodeProcess.stderr.write(
439439 `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`,
440440 );
441441 process.exitCode = 1;
apps/server/src/attachmentPaths.ts+1-1
......@@ -1,5 +1,5 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import NodePath from "node:path";
2import * as NodePath from "node:path";
33
44export function normalizeAttachmentRelativePath(rawRelativePath: string): string | null {
55 const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
apps/server/src/attachmentStore.test.ts+13-9
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import { describe, expect, it } from "vite-plus/test";
77
......@@ -45,11 +45,13 @@ describe("attachmentStore", () => {
4545 });
4646
4747 it("resolves attachment path by id using the extension that exists on disk", () => {
48 const attachmentsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-attachment-store-"));
48 const attachmentsDir = NodeFS.mkdtempSync(
49 NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"),
50 );
4951 try {
5052 const attachmentId = "thread-1-attachment";
51 const pngPath = path.join(attachmentsDir, `${attachmentId}.png`);
52 fs.writeFileSync(pngPath, Buffer.from("hello"));
53 const pngPath = NodePath.join(attachmentsDir, `${attachmentId}.png`);
54 NodeFS.writeFileSync(pngPath, Buffer.from("hello"));
5355
5456 const resolved = resolveAttachmentPathById({
5557 attachmentsDir,
......@@ -57,12 +59,14 @@ describe("attachmentStore", () => {
5759 });
5860 expect(resolved).toBe(pngPath);
5961 } finally {
60 fs.rmSync(attachmentsDir, { recursive: true, force: true });
62 NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });
6163 }
6264 });
6365
6466 it("returns null when no attachment file exists for the id", () => {
65 const attachmentsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-attachment-store-"));
67 const attachmentsDir = NodeFS.mkdtempSync(
68 NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"),
69 );
6670 try {
6771 const resolved = resolveAttachmentPathById({
6872 attachmentsDir,
......@@ -70,7 +74,7 @@ describe("attachmentStore", () => {
7074 });
7175 expect(resolved).toBeNull();
7276 } finally {
73 fs.rmSync(attachmentsDir, { recursive: true, force: true });
77 NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });
7478 }
7579 });
7680});
apps/server/src/attachmentStore.ts+4-4
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { randomUUID } from "node:crypto";
3import { existsSync } from "node:fs";
2import * as NodeCrypto from "node:crypto";
3import * as NodeFS from "node:fs";
44
55import type { ChatAttachment } from "@t3tools/contracts";
66
......@@ -39,7 +39,7 @@ export function createAttachmentId(threadId: string): string | null {
3939 if (!threadSegment) {
4040 return null;
4141 }
42 return `${threadSegment}-${randomUUID()}`;
42 return `${threadSegment}-${NodeCrypto.randomUUID()}`;
4343}
4444
4545export function parseThreadSegmentFromAttachmentId(attachmentId: string): string | null {
......@@ -89,7 +89,7 @@ export function resolveAttachmentPathById(input: {
8989 attachmentsDir: input.attachmentsDir,
9090 relativePath: `${normalizedId}${extension}`,
9191 });
92 if (maybePath && existsSync(maybePath)) {
92 if (maybePath && NodeFS.existsSync(maybePath)) {
9393 return maybePath;
9494 }
9595 }
apps/server/src/auth/utils.ts+3-3
......@@ -4,7 +4,7 @@ import type {
44 AuthClientPresentationMetadata,
55} from "@t3tools/contracts";
66import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
7import * as Crypto from "node:crypto";
7import * as NodeCrypto from "node:crypto";
88import * as Encoding from "effect/Encoding";
99import * as Result from "effect/Result";
1010
......@@ -32,7 +32,7 @@ export function base64UrlDecodeUtf8(input: string): string {
3232}
3333
3434export function signPayload(payload: string, secret: Uint8Array): string {
35 return Crypto.createHmac("sha256", Buffer.from(secret)).update(payload).digest("base64url");
35 return NodeCrypto.createHmac("sha256", Buffer.from(secret)).update(payload).digest("base64url");
3636}
3737
3838export function timingSafeEqualBase64Url(left: string, right: string): boolean {
......@@ -41,7 +41,7 @@ export function timingSafeEqualBase64Url(left: string, right: string): boolean {
4141 if (leftBuffer.length !== rightBuffer.length) {
4242 return false;
4343 }
44 return Crypto.timingSafeEqual(leftBuffer, rightBuffer);
44 return NodeCrypto.timingSafeEqual(leftBuffer, rightBuffer);
4545}
4646
4747function normalizeNonEmptyString(value: string | null | undefined): string | undefined {
apps/server/src/bin.test.ts+47-25
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off - CLI integration exercises Node HTTP and filesystem boundaries.
2import { createServer } from "node:http";
3import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
4import { tmpdir } from "node:os";
5import { join } from "node:path";
2import * as NodeHttp from "node:http";
3import * as NodeFS from "node:fs";
4import * as NodeOS from "node:os";
5import * as NodePath from "node:path";
66
77import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
88import * as NodeServices from "@effect/platform-node/NodeServices";
......@@ -124,7 +124,7 @@ const withLiveProjectCliServer = <A, E, R>(baseDir: string, run: () => Effect.Ef
124124 ),
125125 Layer.provideMerge(makeProjectPersistenceLayer(config)),
126126 Layer.provideMerge(
127 NodeHttpServer.layer(createServer, {
127 NodeHttpServer.layer(NodeHttp.createServer, {
128128 host: "127.0.0.1",
129129 port: 0,
130130 }),
......@@ -197,7 +197,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
197197
198198 it.effect("reports fresh headless connect state without requiring local configuration", () =>
199199 Effect.gen(function* () {
200 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-status-test-"));
200 const baseDir = NodeFS.mkdtempSync(
201 NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-status-test-"),
202 );
201203 const { output } = yield* captureStdout(
202204 runConnectCli(["connect", "status", "--base-dir", baseDir, "--json"]),
203205 );
......@@ -220,7 +222,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
220222
221223 it.effect("reports actionable human-readable headless connect state", () =>
222224 Effect.gen(function* () {
223 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-status-human-test-"));
225 const baseDir = NodeFS.mkdtempSync(
226 NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-status-human-test-"),
227 );
224228 const { output } = yield* captureStdout(
225229 runConnectCli(["connect", "status", "--base-dir", baseDir]),
226230 );
......@@ -234,11 +238,13 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
234238
235239 it.effect("logs in to headless connect without enabling access", () =>
236240 Effect.gen(function* () {
237 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-login-test-"));
241 const baseDir = NodeFS.mkdtempSync(
242 NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-login-test-"),
243 );
238244 const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined);
239 mkdirSync(secretsDir, { recursive: true });
240 writeFileSync(
241 join(secretsDir, "cloud-cli-oauth-token.bin"),
245 NodeFS.mkdirSync(secretsDir, { recursive: true });
246 NodeFS.writeFileSync(
247 NodePath.join(secretsDir, "cloud-cli-oauth-token.bin"),
242248 // @effect-diagnostics-next-line preferSchemaOverJson:off - Test fixture matches the persisted CLI token representation.
243249 JSON.stringify({
244250 accessToken: "access-token",
......@@ -267,7 +273,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
267273
268274 it.effect("disables headless connect without a running server", () =>
269275 Effect.gen(function* () {
270 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-unlink-test-"));
276 const baseDir = NodeFS.mkdtempSync(
277 NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-unlink-test-"),
278 );
271279 const { output } = yield* captureStdout(
272280 runConnectCli(["connect", "unlink", "--base-dir", baseDir]),
273281 );
......@@ -278,24 +286,28 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
278286
279287 it.effect("logs out of headless connect and removes the stored CLI authorization", () =>
280288 Effect.gen(function* () {
281 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-cloud-logout-test-"));
289 const baseDir = NodeFS.mkdtempSync(
290 NodePath.join(NodeOS.tmpdir(), "t3-cli-cloud-logout-test-"),
291 );
282292 const { secretsDir } = yield* ServerConfig.deriveServerPaths(baseDir, undefined);
283 const tokenPath = join(secretsDir, "cloud-cli-oauth-token.bin");
284 mkdirSync(secretsDir, { recursive: true });
285 writeFileSync(tokenPath, "invalid persisted token");
293 const tokenPath = NodePath.join(secretsDir, "cloud-cli-oauth-token.bin");
294 NodeFS.mkdirSync(secretsDir, { recursive: true });
295 NodeFS.writeFileSync(tokenPath, "invalid persisted token");
286296
287297 const { output } = yield* captureStdout(
288298 runConnectCli(["connect", "logout", "--base-dir", baseDir]),
289299 );
290300
291301 assert.equal(output, "Signed out of T3 Connect locally.");
292 assert.isFalse(existsSync(tokenPath));
302 assert.isFalse(NodeFS.existsSync(tokenPath));
293303 }),
294304 );
295305
296306 it.effect("executes auth pairing subcommands and redacts secrets from list output", () =>
297307 Effect.gen(function* () {
298 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-auth-pairing-test-"));
308 const baseDir = NodeFS.mkdtempSync(
309 NodePath.join(NodeOS.tmpdir(), "t3-cli-auth-pairing-test-"),
310 );
299311
300312 const createdOutput = yield* captureStdout(
301313 runCli(["auth", "pairing", "create", "--base-dir", baseDir, "--json"]),
......@@ -325,7 +337,9 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
325337
326338 it.effect("executes auth session subcommands and redacts secrets from list output", () =>
327339 Effect.gen(function* () {
328 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-auth-session-test-"));
340 const baseDir = NodeFS.mkdtempSync(
341 NodePath.join(NodeOS.tmpdir(), "t3-cli-auth-session-test-"),
342 );
329343
330344 const issuedOutput = yield* captureStdout(
331345 runCli(["auth", "session", "issue", "--base-dir", baseDir, "--json"]),
......@@ -400,8 +414,12 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
400414
401415 it.effect("adds, renames, and removes projects offline through the orchestration engine", () =>
402416 Effect.gen(function* () {
403 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-projects-offline-test-"));
404 const workspaceRoot = mkdtempSync(join(tmpdir(), "t3-cli-projects-workspace-"));
417 const baseDir = NodeFS.mkdtempSync(
418 NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-offline-test-"),
419 );
420 const workspaceRoot = NodeFS.mkdtempSync(
421 NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-workspace-"),
422 );
405423
406424 yield* runCliWithRuntime([
407425 "project",
......@@ -444,8 +462,12 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
444462
445463 it.effect("routes project commands through a running server when runtime state is present", () =>
446464 Effect.gen(function* () {
447 const baseDir = mkdtempSync(join(tmpdir(), "t3-cli-projects-live-test-"));
448 const workspaceRoot = mkdtempSync(join(tmpdir(), "t3-cli-projects-live-workspace-"));
465 const baseDir = NodeFS.mkdtempSync(
466 NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-live-test-"),
467 );
468 const workspaceRoot = NodeFS.mkdtempSync(
469 NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-live-workspace-"),
470 );
449471
450472 yield* withLiveProjectCliServer(baseDir, () =>
451473 Effect.gen(function* () {
......@@ -472,8 +494,8 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => {
472494
473495 it.effect("rejects dev-url on project commands", () =>
474496 Effect.gen(function* () {
475 const workspaceRoot = mkdtempSync(
476 join(tmpdir(), "t3-cli-projects-unknown-option-workspace-"),
497 const workspaceRoot = NodeFS.mkdtempSync(
498 NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-unknown-option-workspace-"),
477499 );
478500 const error = yield* runCliWithRuntime([
479501 "project",
apps/server/src/bootstrap.test.ts+13-13
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as NFS from "node:fs";
3import * as path from "node:path";
4import { execFileSync, spawn } from "node:child_process";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeChildProcess from "node:child_process";
55import * as NodeServices from "@effect/platform-node/NodeServices";
66import { it } from "@effect/vitest";
77import * as FileSystem from "effect/FileSystem";
......@@ -53,8 +53,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
5353 );
5454
5555 const fd = yield* Effect.acquireRelease(
56 Effect.sync(() => NFS.openSync(filePath, "r")),
57 (fd) => Effect.sync(() => NFS.closeSync(fd)),
56 Effect.sync(() => NodeFS.openSync(filePath, "r")),
57 (fd) => Effect.sync(() => NodeFS.closeSync(fd)),
5858 );
5959
6060 const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 });
......@@ -78,7 +78,7 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
7878 // so the stream owns the fd lifecycle and closes it asynchronously on end.
7979 // Attempting to also close it synchronously in a finalizer races with the
8080 // stream's async close and produces an uncaught EBADF.
81 const fd = NFS.openSync(filePath, "r");
81 const fd = NodeFS.openSync(filePath, "r");
8282
8383 openSyncInterceptor.failPath = `/proc/self/fd/${fd}`;
8484 try {
......@@ -96,8 +96,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
9696
9797 it.effect("returns none when the fd is unavailable", () =>
9898 Effect.gen(function* () {
99 const fd = NFS.openSync("/dev/null", "r");
100 NFS.closeSync(fd);
99 const fd = NodeFS.openSync("/dev/null", "r");
100 NodeFS.closeSync(fd);
101101
102102 const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 });
103103 assertNone(payload);
......@@ -108,13 +108,13 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
108108 Effect.gen(function* () {
109109 const fs = yield* FileSystem.FileSystem;
110110 const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-bootstrap-" });
111 const fifoPath = path.join(tempDir, "bootstrap.pipe");
111 const fifoPath = NodePath.join(tempDir, "bootstrap.pipe");
112112
113 yield* Effect.sync(() => execFileSync("mkfifo", [fifoPath]));
113 yield* Effect.sync(() => NodeChildProcess.execFileSync("mkfifo", [fifoPath]));
114114
115115 const _writer = yield* Effect.acquireRelease(
116116 Effect.sync(() =>
117 spawn("sh", ["-c", 'exec 3>"$1"; sleep 60', "sh", fifoPath], {
117 NodeChildProcess.spawn("sh", ["-c", 'exec 3>"$1"; sleep 60', "sh", fifoPath], {
118118 stdio: ["ignore", "ignore", "ignore"],
119119 }),
120120 ),
......@@ -125,8 +125,8 @@ it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
125125 );
126126
127127 const fd = yield* Effect.acquireRelease(
128 Effect.sync(() => NFS.openSync(fifoPath, "r")),
129 (fd) => Effect.sync(() => NFS.closeSync(fd)),
128 Effect.sync(() => NodeFS.openSync(fifoPath, "r")),
129 (fd) => Effect.sync(() => NodeFS.closeSync(fd)),
130130 );
131131
132132 const fiber = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, {
apps/server/src/bootstrap.ts+13-13
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as NFS from "node:fs";
3import * as Net from "node:net";
4import * as readline from "node:readline";
5import type { Readable } from "node:stream";
2import * as NodeFS from "node:fs";
3import * as NodeNet from "node:net";
4import * as NodeReadline from "node:readline";
5import type * as NodeStream from "node:stream";
66
77import * as Data from "effect/Data";
88import * as Effect from "effect/Effect";
......@@ -33,7 +33,7 @@ export const readBootstrapEnvelope = Effect.fn("readBootstrapEnvelope")(function
3333 const timeoutMs = options?.timeoutMs ?? 1000;
3434
3535 return yield* Effect.callback<Option.Option<A>, BootstrapError>((resume) => {
36 const input = readline.createInterface({
36 const input = NodeReadline.createInterface({
3737 input: stream,
3838 crlfDelay: Infinity,
3939 });
......@@ -96,7 +96,7 @@ const isUnavailableBootstrapFdError = Predicate.compose(
9696
9797const isFdReady = (fd: number) =>
9898 Effect.try({
99 try: () => NFS.fstatSync(fd),
99 try: () => NodeFS.fstatSync(fd),
100100 catch: (error) =>
101101 new BootstrapError({
102102 message: "Failed to stat bootstrap fd.",
......@@ -113,7 +113,7 @@ const isFdReady = (fd: number) =>
113113const makeBootstrapInputStream = (fd: number) =>
114114 Effect.gen(function* () {
115115 const platform = yield* HostProcessPlatform;
116 return yield* Effect.try<Readable, BootstrapError>({
116 return yield* Effect.try<NodeStream.Readable, BootstrapError>({
117117 try: () => {
118118 const fdPath = resolveFdPath(fd, platform);
119119 if (fdPath === undefined) {
......@@ -122,8 +122,8 @@ const makeBootstrapInputStream = (fd: number) =>
122122
123123 let streamFd: number | undefined;
124124 try {
125 streamFd = NFS.openSync(fdPath, "r");
126 return NFS.createReadStream("", {
125 streamFd = NodeFS.openSync(fdPath, "r");
126 return NodeFS.createReadStream("", {
127127 fd: streamFd,
128128 encoding: "utf8",
129129 autoClose: true,
......@@ -131,7 +131,7 @@ const makeBootstrapInputStream = (fd: number) =>
131131 } catch (error) {
132132 if (isBootstrapFdPathDuplicationError(error)) {
133133 if (streamFd !== undefined) {
134 NFS.closeSync(streamFd);
134 NodeFS.closeSync(streamFd);
135135 }
136136 return makeDirectBootstrapStream(fd);
137137 }
......@@ -146,15 +146,15 @@ const makeBootstrapInputStream = (fd: number) =>
146146 });
147147 });
148148
149const makeDirectBootstrapStream = (fd: number): Readable => {
149const makeDirectBootstrapStream = (fd: number): NodeStream.Readable => {
150150 try {
151 return NFS.createReadStream("", {
151 return NodeFS.createReadStream("", {
152152 fd,
153153 encoding: "utf8",
154154 autoClose: true,
155155 });
156156 } catch {
157 const stream = new Net.Socket({
157 const stream = new NodeNet.Socket({
158158 fd,
159159 readable: true,
160160 writable: false,
apps/server/src/checkpointing/CheckpointStore.test.ts+4-4
......@@ -1,5 +1,5 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import path from "node:path";
2import * as NodePath from "node:path";
33
44import * as NodeServices from "@effect/platform-node/NodeServices";
55import { it } from "@effect/vitest";
......@@ -80,7 +80,7 @@ function initRepoWithCommit(
8080 yield* git(cwd, ["init"]);
8181 yield* git(cwd, ["config", "user.email", "test@test.com"]);
8282 yield* git(cwd, ["config", "user.name", "Test"]);
83 yield* writeTextFile(path.join(cwd, "README.md"), "# test\n");
83 yield* writeTextFile(NodePath.join(cwd, "README.md"), "# test\n");
8484 yield* git(cwd, ["add", "."]);
8585 yield* git(cwd, ["commit", "-m", "initial commit"]);
8686 });
......@@ -107,7 +107,7 @@ it.layer(TestLayer)("CheckpointStore.layer", (it) => {
107107 cwd: tmp,
108108 checkpointRef: fromCheckpointRef,
109109 });
110 yield* writeTextFile(path.join(tmp, "README.md"), buildLargeText());
110 yield* writeTextFile(NodePath.join(tmp, "README.md"), buildLargeText());
111111 yield* checkpointStore.captureCheckpoint({
112112 cwd: tmp,
113113 checkpointRef: toCheckpointRef,
......@@ -135,7 +135,7 @@ it.layer(TestLayer)("CheckpointStore.layer", (it) => {
135135 const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0);
136136 const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
137137
138 const componentPath = path.join(tmp, "Component.tsx");
138 const componentPath = NodePath.join(tmp, "Component.tsx");
139139 yield* writeTextFile(
140140 componentPath,
141141 [
apps/server/src/cloud/CliTokenManager.ts+2-2
......@@ -1,5 +1,5 @@
11// @effect-diagnostics nodeBuiltinImport:off - The CLI loopback OAuth callback is a Node HTTP boundary.
2import { createServer } from "node:http";
2import * as NodeHttp from "node:http";
33
44import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
55import * as Clock from "effect/Clock";
......@@ -206,7 +206,7 @@ export const make = Effect.gen(function* () {
206206 disableLogger: true,
207207 }).pipe(
208208 Layer.provide(
209 NodeHttpServer.layer(createServer, {
209 NodeHttpServer.layer(NodeHttp.createServer, {
210210 host: "127.0.0.1",
211211 port: 34338,
212212 disablePreemptiveShutdown: true,
apps/server/src/cloud/http.ts+2-2
......@@ -1,4 +1,4 @@
1import { createPublicKey } from "node:crypto";
1import * as NodeCrypto from "node:crypto";
22import {
33 AuthRelayReadScope,
44 AuthRelayWriteScope,
......@@ -152,7 +152,7 @@ function validateCloudMintPublicKey(
152152 publicKey: string,
153153): Effect.Effect<void, EnvironmentHttpBadRequestError> {
154154 return Effect.try({
155 try: () => createPublicKey(publicKey.replace(/\\n/g, "\n")),
155 try: () => NodeCrypto.createPublicKey(publicKey.replace(/\\n/g, "\n")),
156156 catch: () =>
157157 new EnvironmentHttpBadRequestError({
158158 message: "Cloud mint public key must be a valid Ed25519 public key.",
apps/server/src/environment/ServerEnvironment.test.ts+2-2
......@@ -1,5 +1,5 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { dirname } from "node:path";
2import * as NodePath from "node:path";
33import * as NodeServices from "@effect/platform-node/NodeServices";
44import { expect, it } from "@effect/vitest";
55import * as Effect from "effect/Effect";
......@@ -76,7 +76,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => {
7676 });
7777 const serverConfig = yield* makeServerConfig(baseDir);
7878 const environmentIdPath = serverConfig.environmentIdPath;
79 yield* fileSystem.makeDirectory(dirname(environmentIdPath), { recursive: true });
79 yield* fileSystem.makeDirectory(NodePath.dirname(environmentIdPath), { recursive: true });
8080 yield* fileSystem.writeFileString(environmentIdPath, "persisted-environment-id\n");
8181 const writeAttempts: string[] = [];
8282 const failingFileSystemLayer = FileSystem.layerNoop({
apps/server/src/git/GitManager.test.ts+64-56
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import path from "node:path";
4import { spawnSync } from "node:child_process";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeChildProcess from "node:child_process";
55
66import * as NodeServices from "@effect/platform-node/NodeServices";
77import { it } from "@effect/vitest";
......@@ -164,7 +164,7 @@ function normalizeFakePullRequestSummary(raw: unknown): GitHubCli.GitHubPullRequ
164164}
165165
166166function runGitSyncForFakeGh(cwd: string, args: readonly string[]): void {
167 const result = spawnSync("git", args, {
167 const result = NodeChildProcess.spawnSync("git", args, {
168168 cwd,
169169 encoding: "utf8",
170170 });
......@@ -254,7 +254,7 @@ function initRepo(
254254 yield* runGit(cwd, ["init", "--initial-branch=main"]);
255255 yield* runGit(cwd, ["config", "user.email", "test@example.com"]);
256256 yield* runGit(cwd, ["config", "user.name", "Test User"]);
257 yield* fs.writeFileString(path.join(cwd, "README.md"), "hello\n");
257 yield* fs.writeFileString(NodePath.join(cwd, "README.md"), "hello\n");
258258 yield* runGit(cwd, ["add", "README.md"]);
259259 yield* runGit(cwd, ["commit", "-m", "Initial commit"]);
260260 });
......@@ -459,7 +459,7 @@ function createGitHubCliWithFakeGh(scenario: FakeGhScenario = {}): {
459459 try: () => {
460460 const headBranch = scenario.pullRequest?.headRefName;
461461 if (headBranch) {
462 const existingBranch = spawnSync(
462 const existingBranch = NodeChildProcess.spawnSync(
463463 "git",
464464 ["show-ref", "--verify", "--quiet", `refs/heads/${headBranch}`],
465465 {
......@@ -916,7 +916,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
916916 it.effect("status returns an explicit non-repo result for deleted directories", () =>
917917 Effect.gen(function* () {
918918 const rootDir = yield* makeTempDir("t3code-git-manager-missing-dir-");
919 const cwd = path.join(rootDir, "deleted-repo");
919 const cwd = NodePath.join(rootDir, "deleted-repo");
920920 yield* makeDirectory(cwd);
921921 yield* removePath(cwd);
922922 const { manager } = yield* makeManager();
......@@ -1026,7 +1026,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
10261026 const forkDir = yield* createBareRemote();
10271027 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
10281028 yield* runGit(repoDir, ["checkout", "-b", "statemachine"]);
1029 fs.writeFileSync(path.join(repoDir, "fork-pr.txt"), "fork pr\n");
1029 NodeFS.writeFileSync(NodePath.join(repoDir, "fork-pr.txt"), "fork pr\n");
10301030 yield* runGit(repoDir, ["add", "fork-pr.txt"]);
10311031 yield* runGit(repoDir, ["commit", "-m", "Fork PR branch"]);
10321032 yield* runGit(repoDir, ["push", "-u", "fork-seed", "statemachine"]);
......@@ -1348,7 +1348,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
13481348 Effect.gen(function* () {
13491349 const repoDir = yield* makeTempDir("t3code-git-manager-");
13501350 yield* initRepo(repoDir);
1351 fs.writeFileSync(path.join(repoDir, "README.md"), "hello\nworld\n");
1351 NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\nworld\n");
13521352
13531353 const { manager } = yield* makeManager();
13541354 const result = yield* runStackedAction(manager, {
......@@ -1383,7 +1383,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
13831383 Effect.gen(function* () {
13841384 const repoDir = yield* makeTempDir("t3code-git-manager-");
13851385 yield* initRepo(repoDir);
1386 fs.writeFileSync(path.join(repoDir, "README.md"), "hello\ncustom\n");
1386 NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\ncustom\n");
13871387 let generatedCount = 0;
13881388
13891389 const { manager } = yield* makeManager({
......@@ -1426,8 +1426,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
14261426 Effect.gen(function* () {
14271427 const repoDir = yield* makeTempDir("t3code-git-manager-");
14281428 yield* initRepo(repoDir);
1429 fs.writeFileSync(path.join(repoDir, "a.txt"), "file a\n");
1430 fs.writeFileSync(path.join(repoDir, "b.txt"), "file b\n");
1429 NodeFS.writeFileSync(NodePath.join(repoDir, "a.txt"), "file a\n");
1430 NodeFS.writeFileSync(NodePath.join(repoDir, "b.txt"), "file b\n");
14311431
14321432 const { manager } = yield* makeManager();
14331433 const result = yield* runStackedAction(manager, {
......@@ -1454,7 +1454,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
14541454 const remoteDir = yield* createBareRemote();
14551455 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
14561456 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
1457 fs.writeFileSync(path.join(repoDir, "README.md"), "hello\nfeature-branch\n");
1457 NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\nfeature-branch\n");
14581458 let generatedCount = 0;
14591459
14601460 const { manager } = yield* makeManager({
......@@ -1514,7 +1514,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
15141514 Effect.gen(function* () {
15151515 const repoDir = yield* makeTempDir("t3code-git-manager-");
15161516 yield* initRepo(repoDir);
1517 fs.writeFileSync(path.join(repoDir, "README.md"), "hello\ncustom-feature\n");
1517 NodeFS.writeFileSync(NodePath.join(repoDir, "README.md"), "hello\ncustom-feature\n");
15181518 let generatedCount = 0;
15191519
15201520 const { manager } = yield* makeManager({
......@@ -1597,7 +1597,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
15971597 yield* runGit(repoDir, ["checkout", "-b", "feature/stacked-flow"]);
15981598 const remoteDir = yield* createBareRemote();
15991599 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
1600 fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n");
1600 NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n");
16011601
16021602 const { manager } = yield* makeManager();
16031603 const result = yield* runStackedAction(manager, {
......@@ -1626,7 +1626,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
16261626 yield* runGit(repoDir, ["checkout", "-b", "feature/no-upstream-pr"]);
16271627 const remoteDir = yield* createBareRemote();
16281628 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
1629 fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n");
1629 NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n");
16301630
16311631 const { manager, ghCalls } = yield* makeManager({
16321632 ghScenario: {
......@@ -1697,7 +1697,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
16971697 yield* runGit(repoDir, ["checkout", "-b", "feature/push-only"]);
16981698 const remoteDir = yield* createBareRemote();
16991699 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
1700 fs.writeFileSync(path.join(repoDir, "push-only.txt"), "push only\n");
1700 NodeFS.writeFileSync(NodePath.join(repoDir, "push-only.txt"), "push only\n");
17011701 yield* runGit(repoDir, ["add", "push-only.txt"]);
17021702 yield* runGit(repoDir, ["commit", "-m", "Push only branch"]);
17031703
......@@ -1725,11 +1725,11 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
17251725 yield* runGit(repoDir, ["checkout", "-b", "feature/push-dirty"]);
17261726 const remoteDir = yield* createBareRemote();
17271727 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
1728 fs.writeFileSync(path.join(repoDir, "push-dirty.txt"), "push dirty\n");
1728 NodeFS.writeFileSync(NodePath.join(repoDir, "push-dirty.txt"), "push dirty\n");
17291729 yield* runGit(repoDir, ["add", "push-dirty.txt"]);
17301730 yield* runGit(repoDir, ["commit", "-m", "Push dirty branch"]);
1731 fs.mkdirSync(path.join(repoDir, ".vercel"));
1732 fs.writeFileSync(path.join(repoDir, ".vercel", "project.json"), "{}\n");
1731 NodeFS.mkdirSync(NodePath.join(repoDir, ".vercel"));
1732 NodeFS.writeFileSync(NodePath.join(repoDir, ".vercel", "project.json"), "{}\n");
17331733
17341734 const { manager } = yield* makeManager();
17351735 const result = yield* runStackedAction(manager, {
......@@ -1760,7 +1760,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
17601760 yield* runGit(repoDir, ["checkout", "-b", "feature/create-pr-only"]);
17611761 const remoteDir = yield* createBareRemote();
17621762 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
1763 fs.writeFileSync(path.join(repoDir, "create-pr-only.txt"), "create pr\n");
1763 NodeFS.writeFileSync(NodePath.join(repoDir, "create-pr-only.txt"), "create pr\n");
17641764 yield* runGit(repoDir, ["add", "create-pr-only.txt"]);
17651765 yield* runGit(repoDir, ["commit", "-m", "Create PR only branch"]);
17661766
......@@ -1805,7 +1805,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
18051805 const repoDir = yield* makeTempDir("t3code-git-manager-");
18061806 yield* initRepo(repoDir);
18071807 yield* runGit(repoDir, ["checkout", "-b", "feature/provider-fallback"]);
1808 fs.writeFileSync(path.join(repoDir, "provider-fallback.txt"), "fallback\n");
1808 NodeFS.writeFileSync(NodePath.join(repoDir, "provider-fallback.txt"), "fallback\n");
18091809 yield* runGit(repoDir, ["add", "provider-fallback.txt"]);
18101810 yield* runGit(repoDir, ["commit", "-m", "Provider fallback"]);
18111811 const remoteDir = yield* createBareRemote();
......@@ -1982,7 +1982,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
19821982 yield* runGit(repoDir, ["checkout", "main"]);
19831983 yield* runGit(repoDir, ["branch", "-D", "effect-atom"]);
19841984 yield* runGit(repoDir, ["checkout", "--track", "my-org/upstream/effect-atom"]);
1985 fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n");
1985 NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n");
19861986 yield* runGit(repoDir, ["add", "changes.txt"]);
19871987 yield* runGit(repoDir, ["commit", "-m", "Feature commit"]);
19881988
......@@ -2200,7 +2200,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
22002200 yield* runGit(repoDir, ["checkout", "-b", "feature-create-pr"]);
22012201 const remoteDir = yield* createBareRemote();
22022202 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
2203 fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n");
2203 NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n");
22042204 yield* runGit(repoDir, ["add", "changes.txt"]);
22052205 yield* runGit(repoDir, ["commit", "-m", "Feature commit"]);
22062206 yield* runGit(repoDir, ["push", "-u", "origin", "feature-create-pr"]);
......@@ -2252,7 +2252,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
22522252 yield* runGit(peerDir, ["clone", remoteDir, "."]);
22532253 yield* runGit(peerDir, ["config", "user.email", "peer@example.com"]);
22542254 yield* runGit(peerDir, ["config", "user.name", "Peer User"]);
2255 fs.writeFileSync(path.join(peerDir, "remote.txt"), "remote\n");
2255 NodeFS.writeFileSync(NodePath.join(peerDir, "remote.txt"), "remote\n");
22562256 yield* runGit(peerDir, ["add", "remote.txt"]);
22572257 yield* runGit(peerDir, ["commit", "-m", "Remote base commit"]);
22582258 yield* runGit(peerDir, ["push", "origin", "main"]);
......@@ -2265,7 +2265,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
22652265 "feature/remote-base",
22662266 "origin/main",
22672267 ]);
2268 fs.writeFileSync(path.join(repoDir, "feature.txt"), "feature\n");
2268 NodeFS.writeFileSync(NodePath.join(repoDir, "feature.txt"), "feature\n");
22692269 yield* runGit(repoDir, ["add", "feature.txt"]);
22702270 yield* runGit(repoDir, ["commit", "-m", "Feature commit"]);
22712271 yield* runGit(repoDir, ["push", "-u", "origin", "feature/remote-base"]);
......@@ -2304,7 +2304,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
23042304 yield* runGit(repoDir, ["checkout", "-b", "feature/no-fork-match"]);
23052305 const remoteDir = yield* createBareRemote();
23062306 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
2307 fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n");
2307 NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n");
23082308 yield* runGit(repoDir, ["add", "changes.txt"]);
23092309 yield* runGit(repoDir, ["commit", "-m", "Feature commit"]);
23102310 yield* runGit(repoDir, ["push", "-u", "origin", "feature/no-fork-match"]);
......@@ -2376,7 +2376,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
23762376 const forkDir = yield* createBareRemote();
23772377 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
23782378 yield* runGit(repoDir, ["checkout", "-b", "statemachine"]);
2379 fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n");
2379 NodeFS.writeFileSync(NodePath.join(repoDir, "changes.txt"), "change\n");
23802380 yield* runGit(repoDir, ["add", "changes.txt"]);
23812381 yield* runGit(repoDir, ["commit", "-m", "Feature commit"]);
23822382 yield* runGit(repoDir, ["push", "-u", "fork-seed", "statemachine"]);
......@@ -2556,7 +2556,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
25562556 const repoDir = yield* makeTempDir("t3code-git-manager-");
25572557 yield* initRepo(repoDir);
25582558 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local"]);
2559 fs.writeFileSync(path.join(repoDir, "local.txt"), "local\n");
2559 NodeFS.writeFileSync(NodePath.join(repoDir, "local.txt"), "local\n");
25602560 yield* runGit(repoDir, ["add", "local.txt"]);
25612561 yield* runGit(repoDir, ["commit", "-m", "Local PR branch"]);
25622562
......@@ -2597,7 +2597,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
25972597 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
25982598 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
25992599 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-upstream"]);
2600 fs.writeFileSync(path.join(repoDir, "upstream.txt"), "upstream\n");
2600 NodeFS.writeFileSync(NodePath.join(repoDir, "upstream.txt"), "upstream\n");
26012601 yield* runGit(repoDir, ["add", "upstream.txt"]);
26022602 yield* runGit(repoDir, ["commit", "-m", "Local upstream PR branch"]);
26032603 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-local-upstream"]);
......@@ -2655,7 +2655,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
26552655 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
26562656 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
26572657 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-no-head-repo"]);
2658 fs.writeFileSync(path.join(repoDir, "no-head-repo.txt"), "upstream\n");
2658 NodeFS.writeFileSync(NodePath.join(repoDir, "no-head-repo.txt"), "upstream\n");
26592659 yield* runGit(repoDir, ["add", "no-head-repo.txt"]);
26602660 yield* runGit(repoDir, ["commit", "-m", "Local PR branch without repo metadata"]);
26612661 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-local-no-head-repo"]);
......@@ -2702,7 +2702,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
27022702 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
27032703 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
27042704 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-worktree"]);
2705 fs.writeFileSync(path.join(repoDir, "worktree.txt"), "worktree\n");
2705 NodeFS.writeFileSync(NodePath.join(repoDir, "worktree.txt"), "worktree\n");
27062706 yield* runGit(repoDir, ["add", "worktree.txt"]);
27072707 yield* runGit(repoDir, ["commit", "-m", "PR worktree branch"]);
27082708 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-worktree"]);
......@@ -2730,7 +2730,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
27302730
27312731 expect(result.branch).toBe("feature/pr-worktree");
27322732 expect(result.worktreePath).not.toBeNull();
2733 expect(fs.existsSync(result.worktreePath as string)).toBe(true);
2733 expect(NodeFS.existsSync(result.worktreePath as string)).toBe(true);
27342734 const worktreeBranch = (yield* runGit(result.worktreePath as string, [
27352735 "branch",
27362736 "--show-current",
......@@ -2747,7 +2747,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
27472747 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
27482748 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
27492749 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-worktree-setup"]);
2750 fs.writeFileSync(path.join(repoDir, "setup.txt"), "setup\n");
2750 NodeFS.writeFileSync(NodePath.join(repoDir, "setup.txt"), "setup\n");
27512751 yield* runGit(repoDir, ["add", "setup.txt"]);
27522752 yield* runGit(repoDir, ["commit", "-m", "PR worktree setup branch"]);
27532753 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-worktree-setup"]);
......@@ -2802,7 +2802,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
28022802 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
28032803 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
28042804 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-fork"]);
2805 fs.writeFileSync(path.join(repoDir, "fork.txt"), "fork\n");
2805 NodeFS.writeFileSync(NodePath.join(repoDir, "fork.txt"), "fork\n");
28062806 yield* runGit(repoDir, ["add", "fork.txt"]);
28072807 yield* runGit(repoDir, ["commit", "-m", "Fork PR branch"]);
28082808 yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-fork"]);
......@@ -2864,7 +2864,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
28642864 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
28652865 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
28662866 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-local-fork"]);
2867 fs.writeFileSync(path.join(repoDir, "local-fork.txt"), "local fork\n");
2867 NodeFS.writeFileSync(NodePath.join(repoDir, "local-fork.txt"), "local fork\n");
28682868 yield* runGit(repoDir, ["add", "local-fork.txt"]);
28692869 yield* runGit(repoDir, ["commit", "-m", "Local fork PR branch"]);
28702870 yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-local-fork"]);
......@@ -2917,7 +2917,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
29172917 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
29182918 yield* runGit(repoDir, ["remote", "add", "binbandit-seed", forkDir]);
29192919 yield* runGit(repoDir, ["checkout", "-b", "fix/git-action-default-without-origin"]);
2920 fs.writeFileSync(path.join(repoDir, "derived-fork.txt"), "derived fork\n");
2920 NodeFS.writeFileSync(NodePath.join(repoDir, "derived-fork.txt"), "derived fork\n");
29212921 yield* runGit(repoDir, ["add", "derived-fork.txt"]);
29222922 yield* runGit(repoDir, ["commit", "-m", "Derived fork PR branch"]);
29232923 yield* runGit(repoDir, [
......@@ -2969,11 +2969,15 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
29692969 const repoDir = yield* makeTempDir("t3code-git-manager-");
29702970 yield* initRepo(repoDir);
29712971 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-existing-worktree"]);
2972 fs.writeFileSync(path.join(repoDir, "existing.txt"), "existing\n");
2972 NodeFS.writeFileSync(NodePath.join(repoDir, "existing.txt"), "existing\n");
29732973 yield* runGit(repoDir, ["add", "existing.txt"]);
29742974 yield* runGit(repoDir, ["commit", "-m", "Existing worktree branch"]);
29752975 yield* runGit(repoDir, ["checkout", "main"]);
2976 const worktreePath = path.join(repoDir, "..", `pr-existing-${path.basename(repoDir)}`);
2976 const worktreePath = NodePath.join(
2977 repoDir,
2978 "..",
2979 `pr-existing-${NodePath.basename(repoDir)}`,
2980 );
29772981 yield* runGit(repoDir, ["worktree", "add", worktreePath, "feature/pr-existing-worktree"]);
29782982
29792983 const setupCalls: ProjectSetupScriptRunner.ProjectSetupScriptRunnerInput[] = [];
......@@ -3004,8 +3008,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
30043008 threadId: asThreadId("thread-pr-existing-worktree"),
30053009 });
30063010
3007 expect(result.worktreePath && fs.realpathSync.native(result.worktreePath)).toBe(
3008 fs.realpathSync.native(worktreePath),
3011 expect(result.worktreePath && NodeFS.realpathSync.native(result.worktreePath)).toBe(
3012 NodeFS.realpathSync.native(worktreePath),
30093013 );
30103014 expect(result.branch).toBe("feature/pr-existing-worktree");
30113015 expect(setupCalls).toHaveLength(0);
......@@ -3024,7 +3028,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
30243028 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
30253029 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
30263030 yield* runGit(repoDir, ["checkout", "-b", "fork-main-source"]);
3027 fs.writeFileSync(path.join(repoDir, "fork-main.txt"), "fork main\n");
3031 NodeFS.writeFileSync(NodePath.join(repoDir, "fork-main.txt"), "fork main\n");
30283032 yield* runGit(repoDir, ["add", "fork-main.txt"]);
30293033 yield* runGit(repoDir, ["commit", "-m", "Fork main branch"]);
30303034 yield* runGit(repoDir, ["push", "-u", "fork-seed", "fork-main-source:main"]);
......@@ -3084,7 +3088,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
30843088 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
30853089 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
30863090 yield* runGit(repoDir, ["checkout", "-b", "fork-main-source"]);
3087 fs.writeFileSync(path.join(repoDir, "fork-main-second.txt"), "fork main second\n");
3091 NodeFS.writeFileSync(NodePath.join(repoDir, "fork-main-second.txt"), "fork main second\n");
30883092 yield* runGit(repoDir, ["add", "fork-main-second.txt"]);
30893093 yield* runGit(repoDir, ["commit", "-m", "Fork main second branch"]);
30903094 yield* runGit(repoDir, ["push", "-u", "fork-seed", "fork-main-source:main"]);
......@@ -3142,12 +3146,16 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
31423146 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
31433147 yield* runGit(repoDir, ["remote", "add", "fork-seed", forkDir]);
31443148 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-reused-fork"]);
3145 fs.writeFileSync(path.join(repoDir, "reused-fork.txt"), "reused fork\n");
3149 NodeFS.writeFileSync(NodePath.join(repoDir, "reused-fork.txt"), "reused fork\n");
31463150 yield* runGit(repoDir, ["add", "reused-fork.txt"]);
31473151 yield* runGit(repoDir, ["commit", "-m", "Reused fork PR branch"]);
31483152 yield* runGit(repoDir, ["push", "-u", "fork-seed", "feature/pr-reused-fork"]);
31493153 yield* runGit(repoDir, ["checkout", "main"]);
3150 const worktreePath = path.join(repoDir, "..", `pr-reused-fork-${path.basename(repoDir)}`);
3154 const worktreePath = NodePath.join(
3155 repoDir,
3156 "..",
3157 `pr-reused-fork-${NodePath.basename(repoDir)}`,
3158 );
31513159 yield* runGit(repoDir, ["worktree", "add", worktreePath, "feature/pr-reused-fork"]);
31523160 yield* runGit(worktreePath, ["branch", "--unset-upstream"], true);
31533161
......@@ -3179,8 +3187,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
31793187 mode: "worktree",
31803188 });
31813189
3182 expect(result.worktreePath && fs.realpathSync.native(result.worktreePath)).toBe(
3183 fs.realpathSync.native(worktreePath),
3190 expect(result.worktreePath && NodeFS.realpathSync.native(result.worktreePath)).toBe(
3191 NodeFS.realpathSync.native(worktreePath),
31843192 );
31853193 expect(
31863194 (yield* runGit(worktreePath, ["rev-parse", "--abbrev-ref", "@{upstream}"])).stdout.trim(),
......@@ -3196,7 +3204,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
31963204 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
31973205 yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
31983206 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-setup-failure"]);
3199 fs.writeFileSync(path.join(repoDir, "setup-failure.txt"), "setup failure\n");
3207 NodeFS.writeFileSync(NodePath.join(repoDir, "setup-failure.txt"), "setup failure\n");
32003208 yield* runGit(repoDir, ["add", "setup-failure.txt"]);
32013209 yield* runGit(repoDir, ["commit", "-m", "PR setup failure branch"]);
32023210 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-setup-failure"]);
......@@ -3236,7 +3244,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
32363244
32373245 expect(result.branch).toBe("feature/pr-setup-failure");
32383246 expect(result.worktreePath).not.toBeNull();
3239 expect(fs.existsSync(result.worktreePath as string)).toBe(true);
3247 expect(NodeFS.existsSync(result.worktreePath as string)).toBe(true);
32403248 }),
32413249 );
32423250
......@@ -3276,9 +3284,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
32763284 Effect.gen(function* () {
32773285 const repoDir = yield* makeTempDir("t3code-git-manager-");
32783286 yield* initRepo(repoDir);
3279 fs.writeFileSync(path.join(repoDir, "hooked.txt"), "hooked\n");
3280 fs.writeFileSync(
3281 path.join(repoDir, ".git", "hooks", "pre-commit"),
3287 NodeFS.writeFileSync(NodePath.join(repoDir, "hooked.txt"), "hooked\n");
3288 NodeFS.writeFileSync(
3289 NodePath.join(repoDir, ".git", "hooks", "pre-commit"),
32823290 '#!/bin/sh\necho "hook: start" >&2\nsleep 0.05\necho "hook: end" >&2\n',
32833291 { mode: 0o755 },
32843292 );
......@@ -3339,9 +3347,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
33393347 Effect.gen(function* () {
33403348 const repoDir = yield* makeTempDir("t3code-git-manager-");
33413349 yield* initRepo(repoDir);
3342 fs.writeFileSync(path.join(repoDir, "hook-failure.txt"), "broken\n");
3343 fs.writeFileSync(
3344 path.join(repoDir, ".git", "hooks", "pre-commit"),
3350 NodeFS.writeFileSync(NodePath.join(repoDir, "hook-failure.txt"), "broken\n");
3351 NodeFS.writeFileSync(
3352 NodePath.join(repoDir, ".git", "hooks", "pre-commit"),
33453353 '#!/bin/sh\necho "hook: fail" >&2\nexit 1\n',
33463354 { mode: 0o755 },
33473355 );
......@@ -3392,7 +3400,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
33923400 yield* runGit(repoDir, ["checkout", "-b", "feature/pr-only-follow-up"]);
33933401 const remoteDir = yield* createBareRemote();
33943402 yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
3395 fs.writeFileSync(path.join(repoDir, "pr-only.txt"), "pr only\n");
3403 NodeFS.writeFileSync(NodePath.join(repoDir, "pr-only.txt"), "pr only\n");
33963404 yield* runGit(repoDir, ["add", "pr-only.txt"]);
33973405 yield* runGit(repoDir, ["commit", "-m", "PR only branch"]);
33983406 yield* runGit(repoDir, ["push", "-u", "origin", "feature/pr-only-follow-up"]);
apps/server/src/git/Utils.ts+3-3
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { existsSync } from "node:fs";
3import { join } from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
44
55export function isGitRepository(cwd: string): boolean {
6 return existsSync(join(cwd, ".git"));
6 return NodeFS.existsSync(NodePath.join(cwd, ".git"));
77}
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+17-17
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
5import { execFileSync } from "node:child_process";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
5import * as NodeChildProcess from "node:child_process";
66
77import {
88 ProviderDriverKind,
......@@ -198,7 +198,7 @@ async function waitForEvent(
198198}
199199
200200function runGit(cwd: string, args: ReadonlyArray<string>) {
201 return execFileSync("git", args, {
201 return NodeChildProcess.execFileSync("git", args, {
202202 cwd,
203203 stdio: ["ignore", "pipe", "pipe"],
204204 encoding: "utf8",
......@@ -206,11 +206,11 @@ function runGit(cwd: string, args: ReadonlyArray<string>) {
206206}
207207
208208function createGitRepository() {
209 const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "t3-checkpoint-handler-"));
209 const cwd = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-checkpoint-handler-"));
210210 runGit(cwd, ["init", "--initial-branch=main"]);
211211 runGit(cwd, ["config", "user.email", "test@example.com"]);
212212 runGit(cwd, ["config", "user.name", "Test User"]);
213 fs.writeFileSync(path.join(cwd, "README.md"), "v1\n", "utf8");
213 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v1\n", "utf8");
214214 runGit(cwd, ["add", "."]);
215215 runGit(cwd, ["commit", "-m", "Initial"]);
216216 return cwd;
......@@ -267,7 +267,7 @@ describe("CheckpointReactor", () => {
267267 while (tempDirs.length > 0) {
268268 const dir = tempDirs.pop();
269269 if (dir) {
270 fs.rmSync(dir, { recursive: true, force: true });
270 NodeFS.rmSync(dir, { recursive: true, force: true });
271271 }
272272 }
273273 });
......@@ -395,14 +395,14 @@ describe("CheckpointReactor", () => {
395395 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
396396 }),
397397 );
398 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
398 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8");
399399 await runtime.runPromise(
400400 checkpointStore.captureCheckpoint({
401401 cwd,
402402 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
403403 }),
404404 );
405 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
405 NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8");
406406 await runtime.runPromise(
407407 checkpointStore.captureCheckpoint({
408408 cwd,
......@@ -456,7 +456,7 @@ describe("CheckpointReactor", () => {
456456 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
457457 );
458458
459 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
459 NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8");
460460 harness.provider.emit({
461461 type: "turn.completed",
462462 eventId: EventId.make("evt-turn-completed-1"),
......@@ -554,7 +554,7 @@ describe("CheckpointReactor", () => {
554554 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
555555 );
556556
557 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
557 NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8");
558558
559559 harness.provider.emit({
560560 type: "turn.completed",
......@@ -628,7 +628,7 @@ describe("CheckpointReactor", () => {
628628 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
629629 );
630630
631 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
631 NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8");
632632 harness.provider.emit({
633633 type: "turn.completed",
634634 eventId: EventId.make("evt-turn-completed-claude-1"),
......@@ -761,7 +761,7 @@ describe("CheckpointReactor", () => {
761761 }),
762762 );
763763
764 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
764 NodeFS.writeFileSync(NodePath.join(harness.cwd, "README.md"), "v2\n", "utf8");
765765 harness.provider.emit({
766766 type: "turn.completed",
767767 eventId: EventId.make("evt-turn-completed-missing-provider-cwd"),
......@@ -829,8 +829,8 @@ describe("CheckpointReactor", () => {
829829 });
830830
831831 it("continues processing runtime events after a single checkpoint runtime failure", async () => {
832 const nonRepositorySessionCwd = fs.mkdtempSync(
833 path.join(os.tmpdir(), "t3-checkpoint-runtime-non-repo-"),
832 const nonRepositorySessionCwd = NodeFS.mkdtempSync(
833 NodePath.join(NodeOS.tmpdir(), "t3-checkpoint-runtime-non-repo-"),
834834 );
835835 tempDirs.push(nonRepositorySessionCwd);
836836
......@@ -963,7 +963,7 @@ describe("CheckpointReactor", () => {
963963 threadId: ThreadId.make("thread-1"),
964964 numTurns: 1,
965965 });
966 expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");
966 expect(NodeFS.readFileSync(NodePath.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");
967967 expect(
968968 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2)),
969969 ).toBe(false);
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+7-6
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import {
77 ModelSelection,
......@@ -107,11 +107,11 @@ describe("ProviderCommandReactor", () => {
107107 }
108108 runtime = null;
109109 for (const stateDir of createdStateDirs) {
110 fs.rmSync(stateDir, { recursive: true, force: true });
110 NodeFS.rmSync(stateDir, { recursive: true, force: true });
111111 }
112112 createdStateDirs.clear();
113113 for (const baseDir of createdBaseDirs) {
114 fs.rmSync(baseDir, { recursive: true, force: true });
114 NodeFS.rmSync(baseDir, { recursive: true, force: true });
115115 }
116116 createdBaseDirs.clear();
117117 });
......@@ -147,7 +147,8 @@ describe("ProviderCommandReactor", () => {
147147 readonly requiresNewThreadForModelChange?: boolean;
148148 }) {
149149 const now = "2026-01-01T00:00:00.000Z";
150 const baseDir = input?.baseDir ?? fs.mkdtempSync(path.join(os.tmpdir(), "t3code-reactor-"));
150 const baseDir =
151 input?.baseDir ?? NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-reactor-"));
151152 createdBaseDirs.add(baseDir);
152153 const { stateDir } = deriveServerPathsSync(baseDir, undefined);
153154 createdStateDirs.add(stateDir);
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts+6-6
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import {
77 OrchestrationReadModel,
......@@ -198,7 +198,7 @@ describe("ProviderRuntimeIngestion", () => {
198198 const tempDirs: string[] = [];
199199
200200 function makeTempDir(prefix: string): string {
201 const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
201 const dir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), prefix));
202202 tempDirs.push(dir);
203203 return dir;
204204 }
......@@ -213,13 +213,13 @@ describe("ProviderRuntimeIngestion", () => {
213213 }
214214 runtime = null;
215215 for (const dir of tempDirs.splice(0)) {
216 fs.rmSync(dir, { recursive: true, force: true });
216 NodeFS.rmSync(dir, { recursive: true, force: true });
217217 }
218218 });
219219
220220 async function createHarness(options?: { serverSettings?: Partial<ServerSettings> }) {
221221 const workspaceRoot = makeTempDir("t3-provider-project-");
222 fs.mkdirSync(path.join(workspaceRoot, ".git"));
222 NodeFS.mkdirSync(NodePath.join(workspaceRoot, ".git"));
223223 const provider = createProviderServiceHarness();
224224 const orchestrationLayer = OrchestrationEngineLive.pipe(
225225 Layer.provide(OrchestrationProjectionSnapshotQueryLive),
apps/server/src/pathExpansion.test.ts+5-5
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { homedir } from "node:os";
3import { join } from "node:path";
2import * as NodeOS from "node:os";
3import * as NodePath from "node:path";
44import { describe, expect, it } from "vite-plus/test";
55
66import { expandHomePath } from "./pathExpansion.ts";
......@@ -17,15 +17,15 @@ describe("expandHomePath", () => {
1717 });
1818
1919 it("expands a lone tilde to the home directory", () => {
20 expect(expandHomePath("~")).toBe(homedir());
20 expect(expandHomePath("~")).toBe(NodeOS.homedir());
2121 });
2222
2323 it("expands ~/ to a subpath of the home directory", () => {
24 expect(expandHomePath("~/.codex-work")).toBe(join(homedir(), ".codex-work"));
24 expect(expandHomePath("~/.codex-work")).toBe(NodePath.join(NodeOS.homedir(), ".codex-work"));
2525 });
2626
2727 it("expands a Windows-style ~\\ prefix", () => {
28 expect(expandHomePath("~\\.codex")).toBe(join(homedir(), ".codex"));
28 expect(expandHomePath("~\\.codex")).toBe(NodePath.join(NodeOS.homedir(), ".codex"));
2929 });
3030
3131 it("does not expand ~user paths", () => {
apps/server/src/pathExpansion.ts+4-4
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { homedir } from "node:os";
3import { join } from "node:path";
2import * as NodeOS from "node:os";
3import * as NodePath from "node:path";
44
55/**
66 * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the
......@@ -16,9 +16,9 @@ import { join } from "node:path";
1616 */
1717export function expandHomePath(value: string): string {
1818 if (!value) return value;
19 if (value === "~") return homedir();
19 if (value === "~") return NodeOS.homedir();
2020 if (value.startsWith("~/") || value.startsWith("~\\")) {
21 return join(homedir(), value.slice(2));
21 return NodePath.join(NodeOS.homedir(), value.slice(2));
2222 }
2323 return value;
2424}
apps/server/src/persistence/NodeSqliteClient.ts+11-7
......@@ -4,7 +4,7 @@
44 *
55 * @module SqliteClient
66 */
7import { DatabaseSync, type StatementSync } from "node:sqlite";
7import * as NodeSqlite from "node:sqlite";
88
99import * as Cache from "effect/Cache";
1010import * as Config from "effect/Config";
......@@ -69,7 +69,7 @@ const checkNodeSqliteCompat = () => {
6969
7070const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (
7171 options: SqliteClientConfig,
72 openDatabase: () => DatabaseSync,
72 openDatabase: () => NodeSqlite.DatabaseSync,
7373): Effect.fn.Return<Client.SqlClient, never, Scope.Scope | Reactivity.Reactivity> {
7474 yield* checkNodeSqliteCompat();
7575
......@@ -86,8 +86,8 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (
8686 Effect.sync(() => db.close()),
8787 );
8888
89 const statementReaderCache = new WeakMap<StatementSync, boolean>();
90 const hasRows = (statement: StatementSync): boolean => {
89 const statementReaderCache = new WeakMap<NodeSqlite.StatementSync, boolean>();
90 const hasRows = (statement: NodeSqlite.StatementSync): boolean => {
9191 const cached = statementReaderCache.get(statement);
9292 if (cached !== undefined) {
9393 return cached;
......@@ -113,7 +113,11 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (
113113 }),
114114 });
115115
116 const runStatement = (statement: StatementSync, params: ReadonlyArray<unknown>, raw: boolean) =>
116 const runStatement = (
117 statement: NodeSqlite.StatementSync,
118 params: ReadonlyArray<unknown>,
119 raw: boolean,
120 ) =>
117121 Effect.withFiber<ReadonlyArray<any>, SqlError>((fiber) => {
118122 statement.setReadBigInts(Boolean(Context.get(fiber.context, Client.SafeIntegers)));
119123 try {
......@@ -220,7 +224,7 @@ const make = (
220224 makeWithDatabase(
221225 options,
222226 () =>
223 new DatabaseSync(options.filename, {
227 new NodeSqlite.DatabaseSync(options.filename, {
224228 readOnly: options.readonly ?? false,
225229 allowExtension: options.allowExtension ?? false,
226230 }),
......@@ -236,7 +240,7 @@ const makeMemory = (
236240 readonly: false,
237241 },
238242 () => {
239 const database = new DatabaseSync(":memory:", {
243 const database = new NodeSqlite.DatabaseSync(":memory:", {
240244 allowExtension: config.allowExtension ?? false,
241245 });
242246 return database;
apps/server/src/preview/PortScanner.test.ts+4-4
......@@ -1,4 +1,4 @@
1import * as net from "node:net";
1import * as NodeNet from "node:net";
22
33import { it as effectIt } from "@effect/vitest";
44import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
......@@ -17,9 +17,9 @@ const TestPortDiscoveryLive = PortScanner.layer.pipe(
1717 ),
1818);
1919
20const openServer = (port: number): Effect.Effect<net.Server | null> =>
20const openServer = (port: number): Effect.Effect<NodeNet.Server | null> =>
2121 Effect.callback((resume) => {
22 const server = net.createServer();
22 const server = NodeNet.createServer();
2323 server.once("error", () => {
2424 resume(Effect.succeed(null));
2525 });
......@@ -31,7 +31,7 @@ const openServer = (port: number): Effect.Effect<net.Server | null> =>
3131 });
3232 });
3333
34const closeServer = (server: net.Server): Effect.Effect<void> =>
34const closeServer = (server: NodeNet.Server): Effect.Effect<void> =>
3535 Effect.callback((resume) => {
3636 server.close(() => resume(Effect.void));
3737 });
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+9-9
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import * as NodeServices from "@effect/platform-node/NodeServices";
77import type {
......@@ -395,7 +395,7 @@ describe("ClaudeAdapterLive", () => {
395395 });
396396
397397 const createInput = harness.getLastCreateQueryInput();
398 assert.equal(createInput?.options.env?.HOME, path.join(os.homedir(), ".claude-work"));
398 assert.equal(createInput?.options.env?.HOME, NodePath.join(NodeOS.homedir(), ".claude-work"));
399399 }).pipe(
400400 Effect.provideService(Random.Random, makeDeterministicRandomService()),
401401 Effect.provide(harness.layer),
......@@ -649,7 +649,7 @@ describe("ClaudeAdapterLive", () => {
649649 });
650650
651651 it.effect("embeds image attachments in Claude user messages", () => {
652 const baseDir = mkdtempSync(path.join(os.tmpdir(), "claude-attachments-"));
652 const baseDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "claude-attachments-"));
653653 const harness = makeHarness({
654654 cwd: "/tmp/project-claude-attachments",
655655 baseDir,
......@@ -657,7 +657,7 @@ describe("ClaudeAdapterLive", () => {
657657 return Effect.gen(function* () {
658658 yield* Effect.addFinalizer(() =>
659659 Effect.sync(() =>
660 rmSync(baseDir, {
660 NodeFS.rmSync(baseDir, {
661661 recursive: true,
662662 force: true,
663663 }),
......@@ -674,9 +674,9 @@ describe("ClaudeAdapterLive", () => {
674674 mimeType: "image/png",
675675 sizeBytes: 4,
676676 };
677 const attachmentPath = path.join(attachmentsDir, attachmentRelativePath(attachment));
678 mkdirSync(path.dirname(attachmentPath), { recursive: true });
679 writeFileSync(attachmentPath, Uint8Array.from([1, 2, 3, 4]));
677 const attachmentPath = NodePath.join(attachmentsDir, attachmentRelativePath(attachment));
678 NodeFS.mkdirSync(NodePath.dirname(attachmentPath), { recursive: true });
679 NodeFS.writeFileSync(attachmentPath, Uint8Array.from([1, 2, 3, 4]));
680680
681681 const session = yield* adapter.startSession({
682682 threadId: THREAD_ID,
apps/server/src/provider/Layers/CodexAdapter.test.ts+98-96
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import assert from "node:assert/strict";
3import fs from "node:fs";
4import os from "node:os";
5import path from "node:path";
2import * as NodeAssert from "node:assert/strict";
3import * as NodeFS from "node:fs";
4import * as NodeOS from "node:os";
5import * as NodePath from "node:path";
66import {
77 ApprovalRequestId,
88 CodexSettings,
......@@ -250,8 +250,8 @@ validationLayer("CodexAdapterLive validation", (it) => {
250250 })
251251 .pipe(Effect.result);
252252
253 assert.equal(result._tag, "Failure");
254 assert.deepStrictEqual(
253 NodeAssert.equal(result._tag, "Failure");
254 NodeAssert.deepStrictEqual(
255255 result.failure,
256256 new ProviderAdapterValidationError({
257257 provider: ProviderDriverKind.make("codex"),
......@@ -259,7 +259,7 @@ validationLayer("CodexAdapterLive validation", (it) => {
259259 issue: "Expected provider 'codex' but received 'claudeAgent'.",
260260 }),
261261 );
262 assert.equal(validationRuntimeFactory.factory.mock.calls.length, 0);
262 NodeAssert.equal(validationRuntimeFactory.factory.mock.calls.length, 0);
263263 }),
264264 );
265265 it.effect("maps codex model options before starting a session", () =>
......@@ -276,7 +276,7 @@ validationLayer("CodexAdapterLive validation", (it) => {
276276 runtimeMode: "full-access",
277277 });
278278
279 assert.deepStrictEqual(validationRuntimeFactory.factory.mock.calls[0]?.[0], {
279 NodeAssert.deepStrictEqual(validationRuntimeFactory.factory.mock.calls[0]?.[0], {
280280 binaryPath: "codex",
281281 cwd: process.cwd(),
282282 model: "gpt-5.3-codex",
......@@ -319,10 +319,10 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => {
319319 })
320320 .pipe(Effect.result);
321321
322 assert.equal(result._tag, "Failure");
323 assert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError");
324 assert.equal(result.failure.provider, "codex");
325 assert.equal(result.failure.threadId, "sess-missing");
322 NodeAssert.equal(result._tag, "Failure");
323 NodeAssert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError");
324 NodeAssert.equal(result.failure.provider, "codex");
325 NodeAssert.equal(result.failure.threadId, "sess-missing");
326326 }),
327327 );
328328
......@@ -335,7 +335,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => {
335335 runtimeMode: "full-access",
336336 });
337337 const runtime = sessionRuntimeFactory.lastRuntime;
338 assert.ok(runtime);
338 NodeAssert.ok(runtime);
339339 runtime.sendTurnImpl.mockClear();
340340
341341 yield* Effect.ignore(
......@@ -350,7 +350,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => {
350350 }),
351351 );
352352
353 assert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], {
353 NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], {
354354 input: "hello",
355355 model: "gpt-5.3-codex",
356356 effort: "high",
......@@ -386,7 +386,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => {
386386 runtimeMode: "full-access",
387387 });
388388 const runtime = customRuntimeFactory.lastRuntime;
389 assert.ok(runtime);
389 NodeAssert.ok(runtime);
390390 runtime.sendTurnImpl.mockClear();
391391
392392 yield* Effect.ignore(
......@@ -405,7 +405,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => {
405405 }),
406406 );
407407
408 assert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], {
408 NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], {
409409 input: "hello",
410410 model: "gpt-5.3-codex",
411411 effort: "high",
......@@ -442,7 +442,7 @@ function startLifecycleRuntime() {
442442 runtimeMode: "full-access",
443443 });
444444 const runtime = lifecycleRuntimeFactory.lastRuntime;
445 assert.ok(runtime);
445 NodeAssert.ok(runtime);
446446 return { adapter, runtime };
447447 });
448448}
......@@ -477,17 +477,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
477477 yield* runtime.emit(event);
478478 const firstEvent = yield* Fiber.join(firstEventFiber);
479479
480 assert.equal(firstEvent._tag, "Some");
480 NodeAssert.equal(firstEvent._tag, "Some");
481481 if (firstEvent._tag !== "Some") {
482482 return;
483483 }
484 assert.equal(firstEvent.value.type, "item.completed");
484 NodeAssert.equal(firstEvent.value.type, "item.completed");
485485 if (firstEvent.value.type !== "item.completed") {
486486 return;
487487 }
488 assert.equal(firstEvent.value.itemId, "msg_1");
489 assert.equal(firstEvent.value.turnId, "turn-1");
490 assert.equal(firstEvent.value.payload.itemType, "assistant_message");
488 NodeAssert.equal(firstEvent.value.itemId, "msg_1");
489 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
490 NodeAssert.equal(firstEvent.value.payload.itemType, "assistant_message");
491491 }),
492492 );
493493
......@@ -524,13 +524,13 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
524524 });
525525 const firstEvent = yield* Fiber.join(firstEventFiber);
526526
527 assert.equal(firstEvent._tag, "Some");
527 NodeAssert.equal(firstEvent._tag, "Some");
528528 if (firstEvent._tag !== "Some" || firstEvent.value.type !== "item.completed") {
529529 return;
530530 }
531 assert.equal(firstEvent.value.payload.itemType, "mcp_tool_call");
532 assert.equal(firstEvent.value.payload.title, "t3-code · preview_status");
533 assert.deepStrictEqual(firstEvent.value.payload.data, {
531 NodeAssert.equal(firstEvent.value.payload.itemType, "mcp_tool_call");
532 NodeAssert.equal(firstEvent.value.payload.title, "t3-code · preview_status");
533 NodeAssert.deepStrictEqual(firstEvent.value.payload.data, {
534534 completedAtMs: 1_778_000_000_000,
535535 threadId: "thread-1",
536536 turnId: "turn-1",
......@@ -578,16 +578,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
578578 yield* runtime.emit(event);
579579 const firstEvent = yield* Fiber.join(firstEventFiber);
580580
581 assert.equal(firstEvent._tag, "Some");
581 NodeAssert.equal(firstEvent._tag, "Some");
582582 if (firstEvent._tag !== "Some") {
583583 return;
584584 }
585 assert.equal(firstEvent.value.type, "turn.proposed.completed");
585 NodeAssert.equal(firstEvent.value.type, "turn.proposed.completed");
586586 if (firstEvent.value.type !== "turn.proposed.completed") {
587587 return;
588588 }
589 assert.equal(firstEvent.value.turnId, "turn-1");
590 assert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two");
589 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
590 NodeAssert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two");
591591 }),
592592 );
593593
......@@ -615,16 +615,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
615615
616616 const firstEvent = yield* Fiber.join(firstEventFiber);
617617
618 assert.equal(firstEvent._tag, "Some");
618 NodeAssert.equal(firstEvent._tag, "Some");
619619 if (firstEvent._tag !== "Some") {
620620 return;
621621 }
622 assert.equal(firstEvent.value.type, "turn.proposed.delta");
622 NodeAssert.equal(firstEvent.value.type, "turn.proposed.delta");
623623 if (firstEvent.value.type !== "turn.proposed.delta") {
624624 return;
625625 }
626 assert.equal(firstEvent.value.turnId, "turn-1");
627 assert.equal(firstEvent.value.payload.delta, "## Final plan");
626 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
627 NodeAssert.equal(firstEvent.value.payload.delta, "## Final plan");
628628 }),
629629 );
630630
......@@ -646,16 +646,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
646646 yield* runtime.emit(event);
647647 const firstEvent = yield* Fiber.join(firstEventFiber);
648648
649 assert.equal(firstEvent._tag, "Some");
649 NodeAssert.equal(firstEvent._tag, "Some");
650650 if (firstEvent._tag !== "Some") {
651651 return;
652652 }
653 assert.equal(firstEvent.value.type, "session.exited");
653 NodeAssert.equal(firstEvent.value.type, "session.exited");
654654 if (firstEvent.value.type !== "session.exited") {
655655 return;
656656 }
657 assert.equal(firstEvent.value.threadId, "thread-1");
658 assert.equal(firstEvent.value.payload.reason, "Session stopped");
657 NodeAssert.equal(firstEvent.value.threadId, "thread-1");
658 NodeAssert.equal(firstEvent.value.payload.reason, "Session stopped");
659659 }),
660660 );
661661
......@@ -684,16 +684,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
684684
685685 const firstEvent = yield* Fiber.join(firstEventFiber);
686686
687 assert.equal(firstEvent._tag, "Some");
687 NodeAssert.equal(firstEvent._tag, "Some");
688688 if (firstEvent._tag !== "Some") {
689689 return;
690690 }
691 assert.equal(firstEvent.value.type, "runtime.warning");
691 NodeAssert.equal(firstEvent.value.type, "runtime.warning");
692692 if (firstEvent.value.type !== "runtime.warning") {
693693 return;
694694 }
695 assert.equal(firstEvent.value.turnId, "turn-1");
696 assert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5");
695 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
696 NodeAssert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5");
697697 }),
698698 );
699699
......@@ -715,16 +715,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
715715
716716 const firstEvent = yield* Fiber.join(firstEventFiber);
717717
718 assert.equal(firstEvent._tag, "Some");
718 NodeAssert.equal(firstEvent._tag, "Some");
719719 if (firstEvent._tag !== "Some") {
720720 return;
721721 }
722 assert.equal(firstEvent.value.type, "runtime.warning");
722 NodeAssert.equal(firstEvent.value.type, "runtime.warning");
723723 if (firstEvent.value.type !== "runtime.warning") {
724724 return;
725725 }
726 assert.equal(firstEvent.value.turnId, "turn-1");
727 assert.equal(
726 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
727 NodeAssert.equal(
728728 firstEvent.value.payload.message,
729729 "The filename or extension is too long. (os error 206)",
730730 );
......@@ -752,16 +752,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
752752
753753 const firstEvent = yield* Fiber.join(firstEventFiber);
754754
755 assert.equal(firstEvent._tag, "Some");
755 NodeAssert.equal(firstEvent._tag, "Some");
756756 if (firstEvent._tag !== "Some") {
757757 return;
758758 }
759 assert.equal(firstEvent.value.type, "thread.realtime.started");
759 NodeAssert.equal(firstEvent.value.type, "thread.realtime.started");
760760 if (firstEvent.value.type !== "thread.realtime.started") {
761761 return;
762762 }
763 assert.equal(firstEvent.value.threadId, "thread-1");
764 assert.equal(firstEvent.value.payload.realtimeSessionId, "realtime-session-1");
763 NodeAssert.equal(firstEvent.value.threadId, "thread-1");
764 NodeAssert.equal(firstEvent.value.payload.realtimeSessionId, "realtime-session-1");
765765 }),
766766 );
767767
......@@ -784,17 +784,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
784784
785785 const firstEvent = yield* Fiber.join(firstEventFiber);
786786
787 assert.equal(firstEvent._tag, "Some");
787 NodeAssert.equal(firstEvent._tag, "Some");
788788 if (firstEvent._tag !== "Some") {
789789 return;
790790 }
791 assert.equal(firstEvent.value.type, "runtime.error");
791 NodeAssert.equal(firstEvent.value.type, "runtime.error");
792792 if (firstEvent.value.type !== "runtime.error") {
793793 return;
794794 }
795 assert.equal(firstEvent.value.turnId, "turn-1");
796 assert.equal(firstEvent.value.payload.class, "provider_error");
797 assert.equal(
795 NodeAssert.equal(firstEvent.value.turnId, "turn-1");
796 NodeAssert.equal(firstEvent.value.payload.class, "provider_error");
797 NodeAssert.equal(
798798 firstEvent.value.payload.message,
799799 "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",
800800 );
......@@ -824,15 +824,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
824824 yield* runtime.emit(event);
825825 const firstEvent = yield* Fiber.join(firstEventFiber);
826826
827 assert.equal(firstEvent._tag, "Some");
827 NodeAssert.equal(firstEvent._tag, "Some");
828828 if (firstEvent._tag !== "Some") {
829829 return;
830830 }
831 assert.equal(firstEvent.value.type, "request.resolved");
831 NodeAssert.equal(firstEvent.value.type, "request.resolved");
832832 if (firstEvent.value.type !== "request.resolved") {
833833 return;
834834 }
835 assert.equal(firstEvent.value.payload.requestType, "command_execution_approval");
835 NodeAssert.equal(firstEvent.value.payload.requestType, "command_execution_approval");
836836 }),
837837 );
838838
......@@ -859,15 +859,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
859859 yield* runtime.emit(event);
860860 const firstEvent = yield* Fiber.join(firstEventFiber);
861861
862 assert.equal(firstEvent._tag, "Some");
862 NodeAssert.equal(firstEvent._tag, "Some");
863863 if (firstEvent._tag !== "Some") {
864864 return;
865865 }
866 assert.equal(firstEvent.value.type, "request.resolved");
866 NodeAssert.equal(firstEvent.value.type, "request.resolved");
867867 if (firstEvent.value.type !== "request.resolved") {
868868 return;
869869 }
870 assert.equal(firstEvent.value.payload.requestType, "file_read_approval");
870 NodeAssert.equal(firstEvent.value.payload.requestType, "file_read_approval");
871871 }),
872872 );
873873
......@@ -895,15 +895,15 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
895895 yield* runtime.emit(event);
896896 const firstEvent = yield* Fiber.join(firstEventFiber);
897897
898 assert.equal(firstEvent._tag, "Some");
898 NodeAssert.equal(firstEvent._tag, "Some");
899899 if (firstEvent._tag !== "Some") {
900900 return;
901901 }
902 assert.equal(firstEvent.value.type, "user-input.resolved");
902 NodeAssert.equal(firstEvent.value.type, "user-input.resolved");
903903 if (firstEvent.value.type !== "user-input.resolved") {
904904 return;
905905 }
906 assert.deepEqual(firstEvent.value.payload.answers, {
906 NodeAssert.deepEqual(firstEvent.value.payload.answers, {
907907 scope: [],
908908 });
909909 }),
......@@ -934,20 +934,20 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
934934 yield* runtime.emit(event);
935935 const events = Array.from(yield* Fiber.join(eventsFiber));
936936
937 assert.equal(events.length, 2);
937 NodeAssert.equal(events.length, 2);
938938
939939 const firstEvent = events[0];
940940 const secondEvent = events[1];
941941
942 assert.equal(firstEvent?.type, "session.state.changed");
942 NodeAssert.equal(firstEvent?.type, "session.state.changed");
943943 if (firstEvent?.type === "session.state.changed") {
944 assert.equal(firstEvent.payload.state, "error");
945 assert.equal(firstEvent.payload.reason, "Sandbox setup failed");
944 NodeAssert.equal(firstEvent.payload.state, "error");
945 NodeAssert.equal(firstEvent.payload.reason, "Sandbox setup failed");
946946 }
947947
948 assert.equal(secondEvent?.type, "runtime.warning");
948 NodeAssert.equal(secondEvent?.type, "runtime.warning");
949949 if (secondEvent?.type === "runtime.warning") {
950 assert.equal(secondEvent.payload.message, "Sandbox setup failed");
950 NodeAssert.equal(secondEvent.payload.message, "Sandbox setup failed");
951951 }
952952 }),
953953 );
......@@ -1006,17 +1006,17 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
10061006 } satisfies ProviderEvent);
10071007
10081008 const events = Array.from(yield* Fiber.join(eventsFiber));
1009 assert.equal(events[0]?.type, "user-input.requested");
1009 NodeAssert.equal(events[0]?.type, "user-input.requested");
10101010 if (events[0]?.type === "user-input.requested") {
1011 assert.equal(events[0].requestId, "req-user-input-1");
1012 assert.equal(events[0].payload.questions[0]?.id, "sandbox_mode");
1013 assert.equal(events[0].payload.questions[0]?.multiSelect, false);
1011 NodeAssert.equal(events[0].requestId, "req-user-input-1");
1012 NodeAssert.equal(events[0].payload.questions[0]?.id, "sandbox_mode");
1013 NodeAssert.equal(events[0].payload.questions[0]?.multiSelect, false);
10141014 }
10151015
1016 assert.equal(events[1]?.type, "user-input.resolved");
1016 NodeAssert.equal(events[1]?.type, "user-input.resolved");
10171017 if (events[1]?.type === "user-input.resolved") {
1018 assert.equal(events[1].requestId, "req-user-input-1");
1019 assert.deepEqual(events[1].payload.answers, {
1018 NodeAssert.equal(events[1].requestId, "req-user-input-1");
1019 NodeAssert.deepEqual(events[1].payload.answers, {
10201020 sandbox_mode: "workspace-write",
10211021 });
10221022 }
......@@ -1060,16 +1060,16 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
10601060 } satisfies ProviderEvent);
10611061
10621062 const firstEvent = yield* Fiber.join(firstEventFiber);
1063 assert.equal(firstEvent._tag, "Some");
1063 NodeAssert.equal(firstEvent._tag, "Some");
10641064 if (firstEvent._tag !== "Some") {
10651065 return;
10661066 }
1067 assert.equal(firstEvent.value.type, "thread.token-usage.updated");
1067 NodeAssert.equal(firstEvent.value.type, "thread.token-usage.updated");
10681068 if (firstEvent.value.type !== "thread.token-usage.updated") {
10691069 return;
10701070 }
10711071
1072 assert.deepEqual(firstEvent.value.payload.usage, {
1072 NodeAssert.deepEqual(firstEvent.value.payload.usage, {
10731073 usedTokens: 126,
10741074 totalProcessedTokens: 11_839,
10751075 maxTokens: 258_400,
......@@ -1119,15 +1119,15 @@ scopedLifecycleLayer("CodexAdapterLive scoped lifecycle", (it) => {
11191119 });
11201120
11211121 const runtime = scopedLifecycleRuntimeFactory.lastRuntime;
1122 assert.ok(runtime);
1122 NodeAssert.ok(runtime);
11231123
11241124 yield* adapter.stopSession(asThreadId("thread-stop"));
11251125
1126 assert.equal(runtime.closeImpl.mock.calls.length, 1);
1127 assert.deepStrictEqual(scopedLifecycleRuntimeFactory.releasedThreadIds, [
1126 NodeAssert.equal(runtime.closeImpl.mock.calls.length, 1);
1127 NodeAssert.deepStrictEqual(scopedLifecycleRuntimeFactory.releasedThreadIds, [
11281128 asThreadId("thread-stop"),
11291129 ]);
1130 assert.equal(yield* adapter.hasSession(asThreadId("thread-stop")), false);
1130 NodeAssert.equal(yield* adapter.hasSession(asThreadId("thread-stop")), false);
11311131 }),
11321132 );
11331133});
......@@ -1164,20 +1164,22 @@ scopedFailureLayer("CodexAdapterLive scoped startup failure", (it) => {
11641164 })
11651165 .pipe(Effect.result);
11661166
1167 assert.equal(result._tag, "Failure");
1168 assert.equal(result.failure._tag, "ProviderAdapterProcessError");
1169 assert.deepStrictEqual(scopedFailureRuntimeFactory.releasedThreadIds, [
1167 NodeAssert.equal(result._tag, "Failure");
1168 NodeAssert.equal(result.failure._tag, "ProviderAdapterProcessError");
1169 NodeAssert.deepStrictEqual(scopedFailureRuntimeFactory.releasedThreadIds, [
11701170 asThreadId("thread-fail"),
11711171 ]);
1172 assert.equal(yield* adapter.hasSession(asThreadId("thread-fail")), false);
1172 NodeAssert.equal(yield* adapter.hasSession(asThreadId("thread-fail")), false);
11731173 }),
11741174 );
11751175});
11761176
11771177it.effect("flushes managed native logs when the adapter layer shuts down", () =>
11781178 Effect.gen(function* () {
1179 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-codex-adapter-native-log-"));
1180 const basePath = path.join(tempDir, "provider-native.ndjson");
1179 const tempDir = NodeFS.mkdtempSync(
1180 NodePath.join(NodeOS.tmpdir(), "t3-codex-adapter-native-log-"),
1181 );
1182 const basePath = NodePath.join(tempDir, "provider-native.ndjson");
11811183 const runtimeFactory = makeRuntimeFactory();
11821184 const scope = yield* Scope.make("sequential");
11831185 let scopeClosed = false;
......@@ -1208,7 +1210,7 @@ it.effect("flushes managed native logs when the adapter layer shuts down", () =>
12081210 });
12091211
12101212 const runtime = runtimeFactory.lastRuntime;
1211 assert.ok(runtime);
1213 NodeAssert.ok(runtime);
12121214
12131215 const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild);
12141216 yield* runtime.emit({
......@@ -1225,15 +1227,15 @@ it.effect("flushes managed native logs when the adapter layer shuts down", () =>
12251227 yield* Scope.close(scope, Exit.void);
12261228 scopeClosed = true;
12271229
1228 const threadLogPath = path.join(tempDir, "thread-logger.log");
1229 assert.equal(fs.existsSync(threadLogPath), true);
1230 const contents = fs.readFileSync(threadLogPath, "utf8");
1231 assert.match(contents, /NTIVE: .*"message":"native flush test"/);
1230 const threadLogPath = NodePath.join(tempDir, "thread-logger.log");
1231 NodeAssert.equal(NodeFS.existsSync(threadLogPath), true);
1232 const contents = NodeFS.readFileSync(threadLogPath, "utf8");
1233 NodeAssert.match(contents, /NTIVE: .*"message":"native flush test"/);
12321234 } finally {
12331235 if (!scopeClosed) {
12341236 yield* Scope.close(scope, Exit.void);
12351237 }
1236 fs.rmSync(tempDir, { recursive: true, force: true });
1238 NodeFS.rmSync(tempDir, { recursive: true, force: true });
12371239 }
12381240 }),
12391241);
apps/server/src/provider/Layers/CodexSessionRuntime.test.ts+18-18
......@@ -1,4 +1,4 @@
1import assert from "node:assert/strict";
1import * as NodeAssert from "node:assert/strict";
22
33import * as Effect from "effect/Effect";
44import * as Schema from "effect/Schema";
......@@ -55,7 +55,7 @@ describe("buildTurnStartParams", () => {
5555 }),
5656 );
5757
58 assert.deepStrictEqual(params, {
58 NodeAssert.deepStrictEqual(params, {
5959 threadId: "provider-thread-1",
6060 approvalPolicy: "never",
6161 sandboxPolicy: {
......@@ -97,7 +97,7 @@ describe("buildTurnStartParams", () => {
9797 }),
9898 );
9999
100 assert.deepStrictEqual(params, {
100 NodeAssert.deepStrictEqual(params, {
101101 threadId: "provider-thread-1",
102102 approvalPolicy: "on-request",
103103 sandboxPolicy: {
......@@ -134,7 +134,7 @@ describe("buildTurnStartParams", () => {
134134 }),
135135 );
136136
137 assert.deepStrictEqual(params, {
137 NodeAssert.deepStrictEqual(params, {
138138 threadId: "provider-thread-1",
139139 approvalPolicy: "untrusted",
140140 sandboxPolicy: {
......@@ -156,19 +156,19 @@ describe("T3 browser developer instructions", () => {
156156 CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
157157 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
158158 ]) {
159 assert.match(instructions, /t3-code/);
160 assert.match(instructions, /preview_status/);
161 assert.match(instructions, /preview_open/);
162 assert.match(instructions, /Do not switch to global browser skills/);
159 NodeAssert.match(instructions, /t3-code/);
160 NodeAssert.match(instructions, /preview_status/);
161 NodeAssert.match(instructions, /preview_open/);
162 NodeAssert.match(instructions, /Do not switch to global browser skills/);
163163 }
164164 });
165165});
166166
167167describe("hasConfiguredMcpServer", () => {
168168 it("detects inline Codex MCP configuration arguments", () => {
169 assert.equal(hasConfiguredMcpServer(undefined), false);
170 assert.equal(hasConfiguredMcpServer(["--model", "gpt-5.4"]), false);
171 assert.equal(
169 NodeAssert.equal(hasConfiguredMcpServer(undefined), false);
170 NodeAssert.equal(hasConfiguredMcpServer(["--model", "gpt-5.4"]), false);
171 NodeAssert.equal(
172172 hasConfiguredMcpServer(["-c", 'mcp_servers.t3-code.url="http://127.0.0.1/mcp"']),
173173 true,
174174 );
......@@ -177,7 +177,7 @@ describe("hasConfiguredMcpServer", () => {
177177
178178describe("isRecoverableThreadResumeError", () => {
179179 it("matches missing thread errors", () => {
180 assert.equal(
180 NodeAssert.equal(
181181 isRecoverableThreadResumeError(
182182 new CodexErrors.CodexAppServerRequestError({
183183 code: -32603,
......@@ -189,7 +189,7 @@ describe("isRecoverableThreadResumeError", () => {
189189 });
190190
191191 it("ignores non-recoverable resume errors", () => {
192 assert.equal(
192 NodeAssert.equal(
193193 isRecoverableThreadResumeError(
194194 new CodexErrors.CodexAppServerRequestError({
195195 code: -32603,
......@@ -201,7 +201,7 @@ describe("isRecoverableThreadResumeError", () => {
201201 });
202202
203203 it("ignores unrelated missing-resource errors that do not mention threads", () => {
204 assert.equal(
204 NodeAssert.equal(
205205 isRecoverableThreadResumeError(
206206 new CodexErrors.CodexAppServerRequestError({
207207 code: -32603,
......@@ -210,7 +210,7 @@ describe("isRecoverableThreadResumeError", () => {
210210 ),
211211 false,
212212 );
213 assert.equal(
213 NodeAssert.equal(
214214 isRecoverableThreadResumeError(
215215 new CodexErrors.CodexAppServerRequestError({
216216 code: -32603,
......@@ -256,8 +256,8 @@ describe("openCodexThread", () => {
256256 }),
257257 );
258258
259 assert.equal(opened.thread.id, "fresh-thread");
260 assert.deepStrictEqual(
259 NodeAssert.equal(opened.thread.id, "fresh-thread");
260 NodeAssert.deepStrictEqual(
261261 calls.map((call) => call.method),
262262 ["thread/resume", "thread/start"],
263263 );
......@@ -283,7 +283,7 @@ describe("openCodexThread", () => {
283283 },
284284 };
285285
286 await assert.rejects(
286 await NodeAssert.rejects(
287287 Effect.runPromise(
288288 openCodexThread({
289289 client,
apps/server/src/provider/Layers/CursorAdapter.test.ts+63-49
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as path from "node:path";
3import * as os from "node:os";
4import { chmod, mkdtemp, readFile, writeFile } from "node:fs/promises";
5import { fileURLToPath } from "node:url";
2import * as NodePath from "node:path";
3import * as NodeOS from "node:os";
4import * as NodeFSP from "node:fs/promises";
5import * as NodeURL from "node:url";
66
77import * as NodeServices from "@effect/platform-node/NodeServices";
88import { assert, it } from "@effect/vitest";
......@@ -36,8 +36,8 @@ class CursorAdapter extends Context.Service<CursorAdapter, CursorAdapterShape>()
3636 "t3/provider/Layers/CursorAdapter.test/CursorAdapter",
3737) {}
3838
39const __dirname = path.dirname(fileURLToPath(import.meta.url));
40const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts");
39const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
40const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts");
4141const mockAgentCommand = "node";
4242const mockAgentArgs = [mockAgentPath] as const;
4343
......@@ -45,8 +45,8 @@ async function makeMockAgentWrapper(
4545 extraEnv?: Record<string, string>,
4646 options?: { initialDelaySeconds?: number },
4747) {
48 const dir = await mkdtemp(path.join(os.tmpdir(), "cursor-acp-mock-"));
49 const wrapperPath = path.join(dir, "fake-agent.sh");
48 const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-mock-"));
49 const wrapperPath = NodePath.join(dir, "fake-agent.sh");
5050 const envExports = Object.entries(extraEnv ?? {})
5151 .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
5252 .join("\n");
......@@ -55,8 +55,8 @@ ${envExports}
5555${options?.initialDelaySeconds ? `sleep ${JSON.stringify(String(options.initialDelaySeconds))}` : ""}
5656exec ${JSON.stringify(mockAgentCommand)} ${mockAgentArgs.map((arg) => JSON.stringify(arg)).join(" ")} "$@"
5757`;
58 await writeFile(wrapperPath, script, "utf8");
59 await chmod(wrapperPath, 0o755);
58 await NodeFSP.writeFile(wrapperPath, script, "utf8");
59 await NodeFSP.chmod(wrapperPath, 0o755);
6060 return wrapperPath;
6161}
6262
......@@ -65,8 +65,8 @@ async function makeProbeWrapper(
6565 argvLogPath: string,
6666 extraEnv?: Record<string, string>,
6767) {
68 const dir = await mkdtemp(path.join(os.tmpdir(), "cursor-acp-probe-"));
69 const wrapperPath = path.join(dir, "fake-agent.sh");
68 const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-probe-"));
69 const wrapperPath = NodePath.join(dir, "fake-agent.sh");
7070 const envExports = Object.entries(extraEnv ?? {})
7171 .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
7272 .join("\n");
......@@ -77,13 +77,13 @@ export T3_ACP_REQUEST_LOG_PATH=${JSON.stringify(requestLogPath)}
7777${envExports}
7878exec ${JSON.stringify(mockAgentCommand)} ${mockAgentArgs.map((arg) => JSON.stringify(arg)).join(" ")} "$@"
7979`;
80 await writeFile(wrapperPath, script, "utf8");
81 await chmod(wrapperPath, 0o755);
80 await NodeFSP.writeFile(wrapperPath, script, "utf8");
81 await NodeFSP.chmod(wrapperPath, 0o755);
8282 return wrapperPath;
8383}
8484
8585async function readArgvLog(filePath: string) {
86 const raw = await readFile(filePath, "utf8");
86 const raw = await NodeFSP.readFile(filePath, "utf8");
8787 return raw
8888 .split("\n")
8989 .map((line) => line.trim())
......@@ -92,7 +92,7 @@ async function readArgvLog(filePath: string) {
9292}
9393
9494async function readJsonLines(filePath: string) {
95 const raw = await readFile(filePath, "utf8");
95 const raw = await NodeFSP.readFile(filePath, "utf8");
9696 return raw
9797 .split("\n")
9898 .map((line) => line.trim())
......@@ -103,7 +103,7 @@ async function readJsonLines(filePath: string) {
103103async function waitForFileContent(filePath: string, attempts = 40) {
104104 for (let attempt = 0; attempt < attempts; attempt += 1) {
105105 try {
106 const raw = await readFile(filePath, "utf8");
106 const raw = await NodeFSP.readFile(filePath, "utf8");
107107 if (raw.trim().length > 0) {
108108 return raw;
109109 }
......@@ -315,9 +315,9 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
315315 const settings = yield* ServerSettingsService;
316316 const threadId = ThreadId.make("cursor-stop-session-close");
317317 const tempDir = yield* Effect.promise(() =>
318 mkdtemp(path.join(os.tmpdir(), "cursor-adapter-exit-log-")),
318 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-adapter-exit-log-")),
319319 );
320 const exitLogPath = path.join(tempDir, "exit.log");
320 const exitLogPath = NodePath.join(tempDir, "exit.log");
321321
322322 const wrapperPath = yield* Effect.promise(() =>
323323 makeMockAgentWrapper({
......@@ -349,9 +349,9 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
349349 const settings = yield* ServerSettingsService;
350350 const threadId = ThreadId.make("cursor-concurrent-start-session");
351351 const tempDir = yield* Effect.promise(() =>
352 mkdtemp(path.join(os.tmpdir(), "cursor-adapter-concurrent-exit-log-")),
352 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-adapter-concurrent-exit-log-")),
353353 );
354 const exitLogPath = path.join(tempDir, "exit.log");
354 const exitLogPath = NodePath.join(tempDir, "exit.log");
355355
356356 const wrapperPath = yield* Effect.promise(() =>
357357 makeMockAgentWrapper(
......@@ -414,10 +414,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
414414 const adapter = yield* CursorAdapter;
415415 const serverSettings = yield* ServerSettingsService;
416416 const threadId = ThreadId.make("cursor-plan-mode-probe");
417 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
418 const requestLogPath = path.join(tempDir, "requests.ndjson");
419 const argvLogPath = path.join(tempDir, "argv.txt");
420 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
417 const tempDir = yield* Effect.promise(() =>
418 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
419 );
420 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
421 const argvLogPath = NodePath.join(tempDir, "argv.txt");
422 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
421423 const wrapperPath = yield* Effect.promise(() =>
422424 makeProbeWrapper(requestLogPath, argvLogPath),
423425 );
......@@ -470,10 +472,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
470472 const adapter = yield* CursorAdapter;
471473 const serverSettings = yield* ServerSettingsService;
472474 const threadId = ThreadId.make("cursor-initial-config-probe");
473 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
474 const requestLogPath = path.join(tempDir, "requests.ndjson");
475 const argvLogPath = path.join(tempDir, "argv.txt");
476 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
475 const tempDir = yield* Effect.promise(() =>
476 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
477 );
478 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
479 const argvLogPath = NodePath.join(tempDir, "argv.txt");
480 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
477481 const wrapperPath = yield* Effect.promise(() =>
478482 makeProbeWrapper(requestLogPath, argvLogPath),
479483 );
......@@ -713,10 +717,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
713717 const runtimeEvents: Array<ProviderRuntimeEvent> = [];
714718 const settledEventTypes = new Set<string>();
715719 const settledEventsReady = yield* Deferred.make<void>();
716 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
717 const requestLogPath = path.join(tempDir, "requests.ndjson");
718 const argvLogPath = path.join(tempDir, "argv.txt");
719 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
720 const tempDir = yield* Effect.promise(() =>
721 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
722 );
723 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
724 const argvLogPath = NodePath.join(tempDir, "argv.txt");
725 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
720726 const wrapperPath = yield* Effect.promise(() =>
721727 makeProbeWrapper(requestLogPath, argvLogPath, { T3_ACP_EMIT_TOOL_CALLS: "1" }),
722728 );
......@@ -931,10 +937,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
931937 const adapter = yield* CursorAdapter;
932938 const serverSettings = yield* ServerSettingsService;
933939 const threadId = ThreadId.make("cursor-cancel-probe");
934 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
935 const requestLogPath = path.join(tempDir, "requests.ndjson");
936 const argvLogPath = path.join(tempDir, "argv.txt");
937 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
940 const tempDir = yield* Effect.promise(() =>
941 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
942 );
943 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
944 const argvLogPath = NodePath.join(tempDir, "argv.txt");
945 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
938946 const wrapperPath = yield* Effect.promise(() =>
939947 makeProbeWrapper(requestLogPath, argvLogPath, { T3_ACP_EMIT_TOOL_CALLS: "1" }),
940948 );
......@@ -1192,10 +1200,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
11921200 const adapter = yield* CursorAdapter;
11931201 const serverSettings = yield* ServerSettingsService;
11941202 const threadId = ThreadId.make("cursor-model-switch");
1195 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
1196 const requestLogPath = path.join(tempDir, "requests.ndjson");
1197 const argvLogPath = path.join(tempDir, "argv.txt");
1198 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
1203 const tempDir = yield* Effect.promise(() =>
1204 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
1205 );
1206 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
1207 const argvLogPath = NodePath.join(tempDir, "argv.txt");
1208 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
11991209 const wrapperPath = yield* Effect.promise(() =>
12001210 makeProbeWrapper(requestLogPath, argvLogPath),
12011211 );
......@@ -1255,10 +1265,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
12551265 const adapter = yield* CursorAdapter;
12561266 const serverSettings = yield* ServerSettingsService;
12571267 const threadId = ThreadId.make("cursor-fast-mode-reset");
1258 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
1259 const requestLogPath = path.join(tempDir, "requests.ndjson");
1260 const argvLogPath = path.join(tempDir, "argv.txt");
1261 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
1268 const tempDir = yield* Effect.promise(() =>
1269 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
1270 );
1271 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
1272 const argvLogPath = NodePath.join(tempDir, "argv.txt");
1273 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
12621274 const wrapperPath = yield* Effect.promise(() =>
12631275 makeProbeWrapper(requestLogPath, argvLogPath),
12641276 );
......@@ -1339,10 +1351,12 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => {
13391351 const adapter = yield* CursorAdapter;
13401352 const serverSettings = yield* ServerSettingsService;
13411353 const threadId = ThreadId.make("cursor-fast-mode-custom-instance");
1342 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "cursor-acp-")));
1343 const requestLogPath = path.join(tempDir, "requests.ndjson");
1344 const argvLogPath = path.join(tempDir, "argv.txt");
1345 yield* Effect.promise(() => writeFile(requestLogPath, "", "utf8"));
1354 const tempDir = yield* Effect.promise(() =>
1355 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "cursor-acp-")),
1356 );
1357 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
1358 const argvLogPath = NodePath.join(tempDir, "argv.txt");
1359 yield* Effect.promise(() => NodeFSP.writeFile(requestLogPath, "", "utf8"));
13461360 const wrapperPath = yield* Effect.promise(() =>
13471361 makeProbeWrapper(requestLogPath, argvLogPath),
13481362 );
apps/server/src/provider/Layers/CursorProvider.ts+2-2
......@@ -1,4 +1,4 @@
1import * as NodeOs from "node:os";
1import * as NodeOS from "node:os";
22import type {
33 CursorSettings,
44 ModelCapabilities,
......@@ -746,7 +746,7 @@ function isCursorAboutJsonFormatUnsupported(result: CommandResult): boolean {
746746const readCursorCliConfigChannel = Effect.fn("readCursorCliConfigChannel")(function* () {
747747 const fileSystem = yield* FileSystem.FileSystem;
748748 const path = yield* Path.Path;
749 const configPath = path.join(NodeOs.homedir(), ".cursor", "cli-config.json");
749 const configPath = path.join(NodeOS.homedir(), ".cursor", "cli-config.json");
750750 const raw = yield* fileSystem.readFileString(configPath).pipe(Effect.orElseSucceed(() => ""));
751751 return parseCursorCliConfigChannel(raw);
752752});
apps/server/src/provider/Layers/EventNdjsonLogger.test.ts+28-31
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import { ThreadId } from "@t3tools/contracts";
77import { assert, describe, it } from "@effect/vitest";
......@@ -31,8 +31,8 @@ function parseLogLine(line: string) {
3131describe("EventNdjsonLogger", () => {
3232 it.effect("writes effect-style lines to thread-scoped files", () =>
3333 Effect.gen(function* () {
34 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-"));
35 const basePath = path.join(tempDir, "provider-native.ndjson");
34 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
35 const basePath = NodePath.join(tempDir, "provider-native.ndjson");
3636
3737 try {
3838 const logger = yield* makeEventNdjsonLogger(basePath, { stream: "native" });
......@@ -51,13 +51,13 @@ describe("EventNdjsonLogger", () => {
5151 );
5252 yield* logger.close();
5353
54 const threadOnePath = path.join(tempDir, "thread-1.log");
55 const threadTwoPath = path.join(tempDir, "thread-2.log");
56 assert.equal(fs.existsSync(threadOnePath), true);
57 assert.equal(fs.existsSync(threadTwoPath), true);
54 const threadOnePath = NodePath.join(tempDir, "thread-1.log");
55 const threadTwoPath = NodePath.join(tempDir, "thread-2.log");
56 assert.equal(NodeFS.existsSync(threadOnePath), true);
57 assert.equal(NodeFS.existsSync(threadTwoPath), true);
5858
59 const first = parseLogLine(fs.readFileSync(threadOnePath, "utf8").trim());
60 const second = parseLogLine(fs.readFileSync(threadTwoPath, "utf8").trim());
59 const first = parseLogLine(NodeFS.readFileSync(threadOnePath, "utf8").trim());
60 const second = parseLogLine(NodeFS.readFileSync(threadTwoPath, "utf8").trim());
6161
6262 assert.equal(Number.isNaN(Date.parse(first.observedAt)), false);
6363 assert.equal(first.stream, "NTIVE");
......@@ -70,7 +70,7 @@ describe("EventNdjsonLogger", () => {
7070 '{"type":"turn.completed","threadId":"provider-thread-2","id":"evt-2"}',
7171 );
7272 } finally {
73 fs.rmSync(tempDir, { recursive: true, force: true });
73 NodeFS.rmSync(tempDir, { recursive: true, force: true });
7474 }
7575 }),
7676 );
......@@ -79,8 +79,8 @@ describe("EventNdjsonLogger", () => {
7979 "falls back to a global segment when orchestration thread id is missing or invalid",
8080 () =>
8181 Effect.gen(function* () {
82 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-"));
83 const basePath = path.join(tempDir, "provider-canonical.ndjson");
82 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
83 const basePath = NodePath.join(tempDir, "provider-canonical.ndjson");
8484
8585 try {
8686 const logger = yield* makeEventNdjsonLogger(basePath, { stream: "orchestration" });
......@@ -93,10 +93,9 @@ describe("EventNdjsonLogger", () => {
9393 yield* logger.write({ id: "evt-invalid-thread" }, "!!!" as unknown as ThreadId);
9494 yield* logger.close();
9595
96 const globalPath = path.join(tempDir, "_global.log");
97 assert.equal(fs.existsSync(globalPath), true);
98 const lines = fs
99 .readFileSync(globalPath, "utf8")
96 const globalPath = NodePath.join(tempDir, "_global.log");
97 assert.equal(NodeFS.existsSync(globalPath), true);
98 const lines = NodeFS.readFileSync(globalPath, "utf8")
10099 .trim()
101100 .split("\n")
102101 .map((line) => parseLogLine(line));
......@@ -108,15 +107,15 @@ describe("EventNdjsonLogger", () => {
108107 assert.equal(lines[1]?.stream, "CANON");
109108 assert.equal(lines[1]?.payload, '{"id":"evt-invalid-thread"}');
110109 } finally {
111 fs.rmSync(tempDir, { recursive: true, force: true });
110 NodeFS.rmSync(tempDir, { recursive: true, force: true });
112111 }
113112 }),
114113 );
115114
116115 it.effect("serializes concurrent first writes for the same segment", () =>
117116 Effect.gen(function* () {
118 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-"));
119 const basePath = path.join(tempDir, "provider-canonical.ndjson");
117 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
118 const basePath = NodePath.join(tempDir, "provider-canonical.ndjson");
120119
121120 try {
122121 const logger = yield* makeEventNdjsonLogger(basePath, {
......@@ -137,10 +136,9 @@ describe("EventNdjsonLogger", () => {
137136 );
138137 yield* logger.close();
139138
140 const globalPath = path.join(tempDir, "_global.log");
141 assert.equal(fs.existsSync(globalPath), true);
142 const lines = fs
143 .readFileSync(globalPath, "utf8")
139 const globalPath = NodePath.join(tempDir, "_global.log");
140 assert.equal(NodeFS.existsSync(globalPath), true);
141 const lines = NodeFS.readFileSync(globalPath, "utf8")
144142 .trim()
145143 .split("\n")
146144 .map((line) => parseLogLine(line));
......@@ -151,15 +149,15 @@ describe("EventNdjsonLogger", () => {
151149 '{"id":"evt-concurrent-2"}',
152150 ]);
153151 } finally {
154 fs.rmSync(tempDir, { recursive: true, force: true });
152 NodeFS.rmSync(tempDir, { recursive: true, force: true });
155153 }
156154 }),
157155 );
158156
159157 it.effect("rotates per-thread files when max size is exceeded", () =>
160158 Effect.gen(function* () {
161 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-"));
162 const basePath = path.join(tempDir, "provider-native.ndjson");
159 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
160 const basePath = NodePath.join(tempDir, "provider-native.ndjson");
163161
164162 try {
165163 const logger = yield* makeEventNdjsonLogger(basePath, {
......@@ -185,8 +183,7 @@ describe("EventNdjsonLogger", () => {
185183 yield* logger.close();
186184
187185 const fileStem = "thread-rotate.log";
188 const matchingFiles = fs
189 .readdirSync(tempDir)
186 const matchingFiles = NodeFS.readdirSync(tempDir)
190187 .filter((entry) => entry === fileStem || entry.startsWith(`${fileStem}.`))
191188 .toSorted();
192189
......@@ -203,7 +200,7 @@ describe("EventNdjsonLogger", () => {
203200 false,
204201 );
205202 } finally {
206 fs.rmSync(tempDir, { recursive: true, force: true });
203 NodeFS.rmSync(tempDir, { recursive: true, force: true });
207204 }
208205 }),
209206 );
apps/server/src/provider/Layers/EventNdjsonLogger.ts+4-4
......@@ -6,8 +6,8 @@
66 * single effect-style text line in a thread-scoped file. Failures are
77 * downgraded to warnings so provider runtime behavior is unaffected.
88 */
9import fs from "node:fs";
10import path from "node:path";
9import * as NodeFS from "node:fs";
10import * as NodePath from "node:path";
1111
1212import type { ThreadId } from "@t3tools/contracts";
1313import { RotatingFileSink } from "@t3tools/shared/logging";
......@@ -178,7 +178,7 @@ export const makeEventNdjsonLogger = Effect.fn("makeEventNdjsonLogger")(function
178178
179179 const directoryReady = yield* Effect.sync(() => {
180180 try {
181 fs.mkdirSync(path.dirname(filePath), { recursive: true });
181 NodeFS.mkdirSync(NodePath.dirname(filePath), { recursive: true });
182182 return true;
183183 } catch (error) {
184184 return { ok: false as const, error };
......@@ -211,7 +211,7 @@ export const makeEventNdjsonLogger = Effect.fn("makeEventNdjsonLogger")(function
211211 }
212212
213213 return makeThreadWriter({
214 filePath: path.join(path.dirname(filePath), `${threadSegment}.log`),
214 filePath: NodePath.join(NodePath.dirname(filePath), `${threadSegment}.log`),
215215 maxBytes,
216216 maxFiles,
217217 batchWindowMs,
apps/server/src/provider/Layers/GrokAdapter.test.ts+18-16
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as path from "node:path";
3import * as os from "node:os";
4import { chmod, mkdtemp, readFile, writeFile } from "node:fs/promises";
5import { fileURLToPath } from "node:url";
2import * as NodePath from "node:path";
3import * as NodeOS from "node:os";
4import * as NodeFSP from "node:fs/promises";
5import * as NodeURL from "node:url";
66
77import * as NodeServices from "@effect/platform-node/NodeServices";
88import { assert, it } from "@effect/vitest";
......@@ -26,13 +26,13 @@ import { ServerConfig } from "../../config.ts";
2626import { makeGrokAdapter } from "./GrokAdapter.ts";
2727const decodeGrokSettings = Schema.decodeSync(GrokSettings);
2828
29const __dirname = path.dirname(fileURLToPath(import.meta.url));
30const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts");
29const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
30const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts");
3131const mockAgentCommand = process.execPath;
3232
3333async function makeMockGrokWrapper(extraEnv?: Record<string, string>) {
34 const dir = await mkdtemp(path.join(os.tmpdir(), "grok-acp-mock-"));
35 const wrapperPath = path.join(dir, "fake-grok.sh");
34 const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-mock-"));
35 const wrapperPath = NodePath.join(dir, "fake-grok.sh");
3636 const envExports = Object.entries(extraEnv ?? {})
3737 .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
3838 .join("\n");
......@@ -40,8 +40,8 @@ async function makeMockGrokWrapper(extraEnv?: Record<string, string>) {
4040${envExports}
4141exec ${JSON.stringify(mockAgentCommand)} ${JSON.stringify(mockAgentPath)} "$@"
4242`;
43 await writeFile(wrapperPath, script, "utf8");
44 await chmod(wrapperPath, 0o755);
43 await NodeFSP.writeFile(wrapperPath, script, "utf8");
44 await NodeFSP.chmod(wrapperPath, 0o755);
4545 return wrapperPath;
4646}
4747
......@@ -51,7 +51,7 @@ function waitForFileContent(filePath: string, attempts = 40): Effect.Effect<stri
5151 if (remainingAttempts <= 0) {
5252 return yield* Effect.die(new Error(`Timed out waiting for file content at ${filePath}`));
5353 }
54 const raw = yield* Effect.tryPromise(() => readFile(filePath, "utf8")).pipe(
54 const raw = yield* Effect.tryPromise(() => NodeFSP.readFile(filePath, "utf8")).pipe(
5555 Effect.orElseSucceed(() => ""),
5656 );
5757 if (raw.trim().length > 0) {
......@@ -64,7 +64,7 @@ function waitForFileContent(filePath: string, attempts = 40): Effect.Effect<stri
6464}
6565
6666async function readJsonLines(filePath: string) {
67 const raw = await readFile(filePath, "utf8");
67 const raw = await NodeFSP.readFile(filePath, "utf8");
6868 return raw
6969 .split("\n")
7070 .map((line) => line.trim())
......@@ -149,9 +149,9 @@ it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => {
149149 Effect.gen(function* () {
150150 const threadId = ThreadId.make("grok-stop-session-close");
151151 const tempDir = yield* Effect.promise(() =>
152 mkdtemp(path.join(os.tmpdir(), "grok-adapter-exit-log-")),
152 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-adapter-exit-log-")),
153153 );
154 const exitLogPath = path.join(tempDir, "exit.log");
154 const exitLogPath = NodePath.join(tempDir, "exit.log");
155155
156156 const wrapperPath = yield* Effect.promise(() =>
157157 makeMockGrokWrapper({
......@@ -227,8 +227,10 @@ it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => {
227227 it.effect("responds to ACP approvals using provider-supplied option ids", () =>
228228 Effect.gen(function* () {
229229 const threadId = ThreadId.make("grok-custom-approval-option-id");
230 const tempDir = yield* Effect.promise(() => mkdtemp(path.join(os.tmpdir(), "grok-acp-")));
231 const requestLogPath = path.join(tempDir, "requests.ndjson");
230 const tempDir = yield* Effect.promise(() =>
231 NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-")),
232 );
233 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
232234 const wrapperPath = yield* Effect.promise(() =>
233235 makeMockGrokWrapper({
234236 T3_ACP_REQUEST_LOG_PATH: requestLogPath,
apps/server/src/provider/Layers/OpenCodeAdapter.test.ts+48-48
......@@ -1,4 +1,4 @@
1import assert from "node:assert/strict";
1import * as NodeAssert from "node:assert/strict";
22import * as NodeServices from "@effect/platform-node/NodeServices";
33import { it } from "@effect/vitest";
44import * as Context from "effect/Context";
......@@ -238,11 +238,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
238238 runtimeMode: "full-access",
239239 });
240240
241 assert.equal(session.provider, "opencode");
242 assert.equal(session.threadId, "thread-opencode");
243 assert.deepEqual(runtimeMock.state.startCalls, []);
244 assert.deepEqual(runtimeMock.state.sessionCreateUrls, ["http://127.0.0.1:9999"]);
245 assert.deepEqual(runtimeMock.state.authHeaders, [
241 NodeAssert.equal(session.provider, "opencode");
242 NodeAssert.equal(session.threadId, "thread-opencode");
243 NodeAssert.deepEqual(runtimeMock.state.startCalls, []);
244 NodeAssert.deepEqual(runtimeMock.state.sessionCreateUrls, ["http://127.0.0.1:9999"]);
245 NodeAssert.deepEqual(runtimeMock.state.authHeaders, [
246246 `Basic ${btoa("opencode:secret-password")}`,
247247 ]);
248248 }),
......@@ -259,8 +259,8 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
259259
260260 yield* adapter.stopSession(asThreadId("thread-opencode"));
261261
262 assert.deepEqual(runtimeMock.state.startCalls, []);
263 assert.deepEqual(
262 NodeAssert.deepEqual(runtimeMock.state.startCalls, []);
263 NodeAssert.deepEqual(
264264 runtimeMock.state.abortCalls.includes("http://127.0.0.1:9999/session"),
265265 true,
266266 );
......@@ -286,7 +286,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
286286 yield* adapter.stopSession(threadId);
287287
288288 const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second")));
289 assert.deepEqual(
289 NodeAssert.deepEqual(
290290 events.map((event) => event.type),
291291 ["session.started", "thread.started", "session.exited"],
292292 );
......@@ -316,11 +316,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
316316 yield* Effect.exit(adapter.stopAll());
317317 const sessions = yield* adapter.listSessions();
318318
319 assert.deepEqual(runtimeMock.state.closeCalls, [
319 NodeAssert.deepEqual(runtimeMock.state.closeCalls, [
320320 "http://127.0.0.1:9999",
321321 "http://127.0.0.1:9999",
322322 ]);
323 assert.deepEqual(sessions, []);
323 NodeAssert.deepEqual(sessions, []);
324324 }),
325325 );
326326
......@@ -348,7 +348,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
348348 scopeClosed = true;
349349
350350 const exit = yield* Fiber.await(eventsFiber).pipe(Effect.timeout("1 second"));
351 assert.equal(Exit.hasInterrupts(exit), true);
351 NodeAssert.equal(Exit.hasInterrupts(exit), true);
352352 } finally {
353353 if (!scopeClosed) {
354354 yield* Scope.close(scope, Exit.void).pipe(Effect.ignore);
......@@ -379,19 +379,19 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
379379 .pipe(Effect.flip);
380380 const sessions = yield* adapter.listSessions();
381381
382 assert.equal(error._tag, "ProviderAdapterRequestError");
382 NodeAssert.equal(error._tag, "ProviderAdapterRequestError");
383383 if (error._tag !== "ProviderAdapterRequestError") {
384384 throw new Error("Unexpected error type");
385385 }
386 assert.equal(error.detail, "prompt failed");
387 assert.equal(
386 NodeAssert.equal(error.detail, "prompt failed");
387 NodeAssert.equal(
388388 error.message,
389389 "Provider adapter request failed (opencode) for session.promptAsync: prompt failed",
390390 );
391 assert.equal(sessions.length, 1);
392 assert.equal(sessions[0]?.status, "ready");
393 assert.equal(sessions[0]?.activeTurnId, undefined);
394 assert.equal(sessions[0]?.lastError, "prompt failed");
391 NodeAssert.equal(sessions.length, 1);
392 NodeAssert.equal(sessions[0]?.status, "ready");
393 NodeAssert.equal(sessions[0]?.activeTurnId, undefined);
394 NodeAssert.equal(sessions[0]?.lastError, "prompt failed");
395395 }),
396396 );
397397
......@@ -424,13 +424,13 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
424424 model: "openai/gpt-5",
425425 },
426426 });
427 assert.equal(String(steeredTurn.turnId), String(turn.turnId));
427 NodeAssert.equal(String(steeredTurn.turnId), String(turn.turnId));
428428
429429 const sessions = yield* adapter.listSessions();
430430 const session = sessions.find((entry) => entry.threadId === threadId);
431 assert.equal(session?.status, "running");
432 assert.equal(String(session?.activeTurnId), String(turn.turnId));
433 assert.equal(runtimeMock.state.promptCalls.length, 2);
431 NodeAssert.equal(session?.status, "running");
432 NodeAssert.equal(String(session?.activeTurnId), String(turn.turnId));
433 NodeAssert.equal(runtimeMock.state.promptCalls.length, 2);
434434 }),
435435 );
436436
......@@ -466,11 +466,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
466466 .pipe(Effect.flip);
467467
468468 // The original turn keeps running — only the steer prompt failed.
469 assert.equal(error._tag, "ProviderAdapterRequestError");
469 NodeAssert.equal(error._tag, "ProviderAdapterRequestError");
470470 const sessions = yield* adapter.listSessions();
471471 const session = sessions.find((entry) => entry.threadId === threadId);
472 assert.equal(session?.status, "running");
473 assert.equal(String(session?.activeTurnId), String(turn.turnId));
472 NodeAssert.equal(session?.status, "running");
473 NodeAssert.equal(String(session?.activeTurnId), String(turn.turnId));
474474 }),
475475 );
476476
......@@ -508,7 +508,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
508508 ),
509509 });
510510
511 assert.deepEqual(runtimeMock.state.promptCalls.at(-1), {
511 NodeAssert.deepEqual(runtimeMock.state.promptCalls.at(-1), {
512512 sessionID: "http://127.0.0.1:9999/session",
513513 model: {
514514 providerID: "anthropic",
......@@ -552,7 +552,7 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
552552 input: "Fix it",
553553 });
554554
555 assert.deepEqual(runtimeMock.state.promptCalls.at(-1), {
555 NodeAssert.deepEqual(runtimeMock.state.promptCalls.at(-1), {
556556 sessionID: "http://127.0.0.1:9999/session",
557557 model: {
558558 providerID: "anthropic",
......@@ -596,15 +596,15 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
596596 })
597597 .pipe(Effect.flip);
598598
599 assert.equal(error._tag, "ProviderAdapterValidationError");
599 NodeAssert.equal(error._tag, "ProviderAdapterValidationError");
600600 if (error._tag !== "ProviderAdapterValidationError") {
601601 throw new Error("Unexpected error type");
602602 }
603 assert.equal(
603 NodeAssert.equal(
604604 error.issue,
605605 "OpenCode model selection is bound to instance 'opencode', expected 'opencode_zen'.",
606606 );
607 assert.deepEqual(runtimeMock.state.promptCalls, []);
607 NodeAssert.deepEqual(runtimeMock.state.promptCalls, []);
608608 }).pipe(Effect.provide(adapterLayer));
609609 });
610610
......@@ -631,10 +631,10 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
631631
632632 const snapshot = yield* adapter.rollbackThread(threadId, 2);
633633
634 assert.deepEqual(runtimeMock.state.revertCalls, [
634 NodeAssert.deepEqual(runtimeMock.state.revertCalls, [
635635 { sessionID: "http://127.0.0.1:9999/session" },
636636 ]);
637 assert.deepEqual(snapshot.turns, []);
637 NodeAssert.deepEqual(snapshot.turns, []);
638638 }),
639639 );
640640
......@@ -644,11 +644,11 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
644644 const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world");
645645 const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hellolo world");
646646
647 assert.deepEqual(
647 NodeAssert.deepEqual(
648648 [firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit],
649649 ["Hello", "lo world", ""],
650650 );
651 assert.equal(secondUpdate.latestText, "Hellolo world");
651 NodeAssert.equal(secondUpdate.latestText, "Hellolo world");
652652 }),
653653 );
654654
......@@ -721,14 +721,14 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
721721
722722 const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second")));
723723 const deltas = events.filter((event) => event.type === "content.delta");
724 assert.deepEqual(
724 NodeAssert.deepEqual(
725725 deltas.map((event) => (event.type === "content.delta" ? event.payload.delta : "")),
726726 ["A B", "Bonus"],
727727 );
728 assert.equal(events.at(-1)?.type, "item.completed");
728 NodeAssert.equal(events.at(-1)?.type, "item.completed");
729729 const completed = events.at(-1);
730730 if (completed?.type === "item.completed") {
731 assert.equal(completed.payload.detail, "A BBonus");
731 NodeAssert.equal(completed.payload.detail, "A BBonus");
732732 }
733733 }),
734734 );
......@@ -820,27 +820,27 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
820820 return started;
821821 }).pipe(Effect.provide(adapterLayer));
822822
823 assert.equal(session.threadId, "thread-native-log");
824 assert.equal(nativeEvents.length, 1);
825 assert.equal(
823 NodeAssert.equal(session.threadId, "thread-native-log");
824 NodeAssert.equal(nativeEvents.length, 1);
825 NodeAssert.equal(
826826 nativeEvents.some((record) => record.event?.provider === "opencode"),
827827 true,
828828 );
829 assert.equal(
829 NodeAssert.equal(
830830 nativeEvents.some(
831831 (record) => record.event?.providerThreadId === "http://127.0.0.1:9999/session",
832832 ),
833833 true,
834834 );
835 assert.equal(
835 NodeAssert.equal(
836836 nativeEvents.some((record) => record.event?.threadId === "thread-native-log"),
837837 true,
838838 );
839 assert.equal(
839 NodeAssert.equal(
840840 nativeEvents.some((record) => record.event?.type === "message.updated"),
841841 true,
842842 );
843 assert.equal(
843 NodeAssert.equal(
844844 nativeThreadIds.every((threadId) => threadId === "thread-native-log"),
845845 true,
846846 );
......@@ -911,9 +911,9 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
911911 };
912912 }).pipe(Effect.provide(adapterLayer));
913913
914 assert.equal(sessions.length, 1);
915 assert.equal(sessions[0]?.threadId, "thread-native-log-failure");
916 assert.deepEqual(closeCallsDuringRun, []);
914 NodeAssert.equal(sessions.length, 1);
915 NodeAssert.equal(sessions[0]?.threadId, "thread-native-log-failure");
916 NodeAssert.deepEqual(closeCallsDuringRun, []);
917917 }),
918918 );
919919});
apps/server/src/provider/Layers/OpenCodeProvider.test.ts+22-19
......@@ -1,4 +1,4 @@
1import assert from "node:assert/strict";
1import * as NodeAssert from "node:assert/strict";
22
33import * as NodeServices from "@effect/platform-node/NodeServices";
44import { it } from "@effect/vitest";
......@@ -122,9 +122,12 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => {
122122 runtimeMock.state.runVersionError = new Error("spawn opencode ENOENT");
123123 const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd());
124124
125 assert.equal(snapshot.status, "error");
126 assert.equal(snapshot.installed, false);
127 assert.equal(snapshot.message, "OpenCode CLI (`opencode`) is not installed or not on PATH.");
125 NodeAssert.equal(snapshot.status, "error");
126 NodeAssert.equal(snapshot.installed, false);
127 NodeAssert.equal(
128 snapshot.message,
129 "OpenCode CLI (`opencode`) is not installed or not on PATH.",
130 );
128131 }),
129132 );
130133
......@@ -133,9 +136,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => {
133136 runtimeMock.state.runVersionError = new Error("An error occurred in Effect.tryPromise");
134137 const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd());
135138
136 assert.equal(snapshot.status, "error");
137 assert.equal(snapshot.installed, true);
138 assert.equal(snapshot.message, "Failed to execute OpenCode CLI health check.");
139 NodeAssert.equal(snapshot.status, "error");
140 NodeAssert.equal(snapshot.installed, true);
141 NodeAssert.equal(snapshot.message, "Failed to execute OpenCode CLI health check.");
139142 }),
140143 );
141144
......@@ -174,20 +177,20 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => {
174177 const snapshot = yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd());
175178 const model = snapshot.models.find((entry) => entry.slug === "openai/gpt-5.4");
176179
177 assert.ok(model);
180 NodeAssert.ok(model);
178181 const variantDescriptor = model.capabilities?.optionDescriptors?.find(
179182 (descriptor) => descriptor.id === "variant" && descriptor.type === "select",
180183 );
181 assert.ok(variantDescriptor && variantDescriptor.type === "select");
182 assert.equal(
184 NodeAssert.ok(variantDescriptor && variantDescriptor.type === "select");
185 NodeAssert.equal(
183186 variantDescriptor.options.find((option) => option.isDefault === true)?.id,
184187 "medium",
185188 );
186189 const agentDescriptor = model.capabilities?.optionDescriptors?.find(
187190 (descriptor) => descriptor.id === "agent" && descriptor.type === "select",
188191 );
189 assert.ok(agentDescriptor && agentDescriptor.type === "select");
190 assert.equal(
192 NodeAssert.ok(agentDescriptor && agentDescriptor.type === "select");
193 NodeAssert.equal(
191194 agentDescriptor.options.find((option) => option.isDefault === true)?.id,
192195 "build",
193196 );
......@@ -198,7 +201,7 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => {
198201 Effect.gen(function* () {
199202 yield* checkOpenCodeProviderStatus(makeOpenCodeSettings(), process.cwd());
200203
201 assert.equal(runtimeMock.state.closeCalls, 1);
204 NodeAssert.equal(runtimeMock.state.closeCalls, 1);
202205 }),
203206 );
204207});
......@@ -215,9 +218,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus with configured server URL", (i
215218 process.cwd(),
216219 );
217220
218 assert.equal(snapshot.status, "error");
219 assert.equal(snapshot.installed, true);
220 assert.equal(
221 NodeAssert.equal(snapshot.status, "error");
222 NodeAssert.equal(snapshot.installed, true);
223 NodeAssert.equal(
221224 snapshot.message,
222225 "OpenCode server rejected authentication. Check the server URL and password.",
223226 );
......@@ -237,9 +240,9 @@ it.layer(testLayer)("checkOpenCodeProviderStatus with configured server URL", (i
237240 process.cwd(),
238241 );
239242
240 assert.equal(snapshot.status, "error");
241 assert.equal(snapshot.installed, true);
242 assert.equal(
243 NodeAssert.equal(snapshot.status, "error");
244 NodeAssert.equal(snapshot.installed, true);
245 NodeAssert.equal(
243246 snapshot.message,
244247 "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.",
245248 );
apps/server/src/provider/Layers/ProviderService.test.ts+21-15
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import type {
77 ProviderApprovalDecision,
......@@ -644,8 +644,8 @@ it.effect("ProviderServiceLive writes canonical events to the emitting thread se
644644
645645it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", () =>
646646 Effect.gen(function* () {
647 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-"));
648 const dbPath = path.join(tempDir, "orchestration.sqlite");
647 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-service-"));
648 const dbPath = NodePath.join(tempDir, "orchestration.sqlite");
649649
650650 const codex = makeFakeCodexAdapter();
651651 const registry = makeAdapterRegistryMock({
......@@ -706,7 +706,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", (
706706 }).pipe(Effect.provide(persistenceLayer));
707707 assert.equal(legacyTableRows.length, 0);
708708
709 fs.rmSync(tempDir, { recursive: true, force: true });
709 NodeFS.rmSync(tempDir, { recursive: true, force: true });
710710 }).pipe(Effect.provide(NodeServices.layer)),
711711);
712712
......@@ -714,8 +714,10 @@ it.effect(
714714 "ProviderServiceLive restores rollback routing after restart using persisted thread mapping",
715715 () =>
716716 Effect.gen(function* () {
717 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-restart-"));
718 const dbPath = path.join(tempDir, "orchestration.sqlite");
717 const tempDir = NodeFS.mkdtempSync(
718 NodePath.join(NodeOS.tmpdir(), "t3-provider-service-restart-"),
719 );
720 const dbPath = NodePath.join(tempDir, "orchestration.sqlite");
719721 const persistenceLayer = makeSqlitePersistenceLive(dbPath);
720722 const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(
721723 Layer.provide(persistenceLayer),
......@@ -834,7 +836,7 @@ it.effect(
834836 assert.equal(typeof rollbackCall?.[0], "string");
835837 assert.equal(rollbackCall?.[1], 1);
836838
837 fs.rmSync(tempDir, { recursive: true, force: true });
839 NodeFS.rmSync(tempDir, { recursive: true, force: true });
838840 }).pipe(Effect.provide(NodeServices.layer)),
839841);
840842
......@@ -1283,8 +1285,10 @@ routing.layer("ProviderServiceLive routing", (it) => {
12831285
12841286 it.effect("reuses persisted resume cursor when startSession is called after a restart", () =>
12851287 Effect.gen(function* () {
1286 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-start-"));
1287 const dbPath = path.join(tempDir, "orchestration.sqlite");
1288 const tempDir = NodeFS.mkdtempSync(
1289 NodePath.join(NodeOS.tmpdir(), "t3-provider-service-start-"),
1290 );
1291 const dbPath = NodePath.join(tempDir, "orchestration.sqlite");
12881292 const persistenceLayer = makeSqlitePersistenceLive(dbPath);
12891293 const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(
12901294 Layer.provide(persistenceLayer),
......@@ -1379,7 +1383,7 @@ routing.layer("ProviderServiceLive routing", (it) => {
13791383 assert.equal(startPayload.threadId, initial.threadId);
13801384 }
13811385
1382 fs.rmSync(tempDir, { recursive: true, force: true });
1386 NodeFS.rmSync(tempDir, { recursive: true, force: true });
13831387 }).pipe(Effect.provide(NodeServices.layer)),
13841388 );
13851389
......@@ -1387,8 +1391,10 @@ routing.layer("ProviderServiceLive routing", (it) => {
13871391 "reuses persisted cwd when startSession resumes a claude session without cwd input",
13881392 () =>
13891393 Effect.gen(function* () {
1390 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-service-cwd-"));
1391 const dbPath = path.join(tempDir, "orchestration.sqlite");
1394 const tempDir = NodeFS.mkdtempSync(
1395 NodePath.join(NodeOS.tmpdir(), "t3-provider-service-cwd-"),
1396 );
1397 const dbPath = NodePath.join(tempDir, "orchestration.sqlite");
13921398 const persistenceLayer = makeSqlitePersistenceLive(dbPath);
13931399 const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(
13941400 Layer.provide(persistenceLayer),
......@@ -1477,7 +1483,7 @@ routing.layer("ProviderServiceLive routing", (it) => {
14771483 assert.equal(startPayload.threadId, initial.threadId);
14781484 }
14791485
1480 fs.rmSync(tempDir, { recursive: true, force: true });
1486 NodeFS.rmSync(tempDir, { recursive: true, force: true });
14811487 }).pipe(Effect.provide(NodeServices.layer)),
14821488 );
14831489});
apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts+6-6
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import os from "node:os";
4import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55
66import * as NodeServices from "@effect/platform-node/NodeServices";
77import { ProviderDriverKind, ThreadId } from "@t3tools/contracts";
......@@ -229,8 +229,8 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
229229
230230 it("rehydrates persisted mappings across layer restart", () =>
231231 Effect.gen(function* () {
232 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-directory-"));
233 const dbPath = path.join(tempDir, "orchestration.sqlite");
232 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-directory-"));
233 const dbPath = NodePath.join(tempDir, "orchestration.sqlite");
234234 const directoryLayer = makeDirectoryLayer(makeSqlitePersistenceLive(dbPath));
235235
236236 const threadId = ThreadId.make("thread-restart");
......@@ -266,6 +266,6 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
266266 assert.equal(legacyTableRows.length, 0);
267267 }).pipe(Effect.provide(directoryLayer));
268268
269 fs.rmSync(tempDir, { recursive: true, force: true });
269 NodeFS.rmSync(tempDir, { recursive: true, force: true });
270270 }));
271271});
apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts+10-10
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as path from "node:path";
3import * as os from "node:os";
4import { fileURLToPath } from "node:url";
5import { mkdtempSync, readFileSync, rmSync } from "node:fs";
2import * as NodePath from "node:path";
3import * as NodeOS from "node:os";
4import * as NodeURL from "node:url";
5import * as NodeFS from "node:fs";
66
77import * as NodeServices from "@effect/platform-node/NodeServices";
88import { it } from "@effect/vitest";
......@@ -13,8 +13,8 @@ import { describe, expect } from "vite-plus/test";
1313import * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1414import type * as EffectAcpProtocol from "effect-acp/protocol";
1515
16const __dirname = path.dirname(fileURLToPath(import.meta.url));
17const mockAgentPath = path.join(__dirname, "../../../scripts/acp-mock-agent.ts");
16const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
17const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts");
1818const mockAgentCommand = "node";
1919const mockAgentArgs = [mockAgentPath];
2020
......@@ -347,8 +347,8 @@ describe("AcpSessionRuntime", () => {
347347 });
348348
349349 it.effect("rejects invalid config option values before sending session/set_config_option", () => {
350 const tempDir = mkdtempSync(path.join(os.tmpdir(), "acp-runtime-"));
351 const requestLogPath = path.join(tempDir, "requests.ndjson");
350 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "acp-runtime-"));
351 const requestLogPath = NodePath.join(tempDir, "requests.ndjson");
352352 return Effect.gen(function* () {
353353 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
354354 yield* runtime.start();
......@@ -363,7 +363,7 @@ describe("AcpSessionRuntime", () => {
363363 expect(error.message).toContain("composer-2[fast=true]");
364364 }
365365
366 const recordedRequests = readFileSync(requestLogPath, "utf8")
366 const recordedRequests = NodeFS.readFileSync(requestLogPath, "utf8")
367367 .trim()
368368 .split("\n")
369369 .filter((line) => line.length > 0)
......@@ -392,7 +392,7 @@ describe("AcpSessionRuntime", () => {
392392 ),
393393 Effect.scoped,
394394 Effect.provide(NodeServices.layer),
395 Effect.ensuring(Effect.sync(() => rmSync(tempDir, { recursive: true, force: true }))),
395 Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))),
396396 );
397397 });
398398});
apps/server/src/provider/opencodeRuntime.ts+2-2
......@@ -1,4 +1,4 @@
1import { pathToFileURL } from "node:url";
1import * as NodeURL from "node:url";
22
33import type { ChatAttachment, ProviderApprovalDecision, RuntimeMode } from "@t3tools/contracts";
44import {
......@@ -206,7 +206,7 @@ export function toOpenCodeFileParts(input: {
206206 type: "file",
207207 mime: attachment.mimeType,
208208 filename: attachment.name,
209 url: pathToFileURL(attachmentPath).href,
209 url: NodeURL.pathToFileURL(attachmentPath).href,
210210 });
211211 }
212212
apps/server/src/provider/providerMaintenance.test.ts+44-44
......@@ -1,9 +1,9 @@
11// @effect-diagnostics nodeBuiltinImport:off
22import { expect, it } from "@effect/vitest";
3import { chmodSync, mkdirSync, symlinkSync, writeFileSync } from "node:fs";
3import * as NodeFS from "node:fs";
44import * as NodeServices from "@effect/platform-node/NodeServices";
55import * as NodeOS from "node:os";
6import path from "node:path";
6import * as NodePath from "node:path";
77import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
88import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
99import * as Crypto from "effect/Crypto";
......@@ -25,7 +25,7 @@ const driver = (value: string) => ProviderDriverKind.make(value);
2525const makeTempDir = (name: string) =>
2626 Crypto.Crypto.pipe(
2727 Effect.flatMap((crypto) => crypto.randomUUIDv4),
28 Effect.map((id) => path.join(NodeOS.tmpdir(), `${name}-${id}`)),
28 Effect.map((id) => NodePath.join(NodeOS.tmpdir(), `${name}-${id}`)),
2929 );
3030const isNativeTestCommandPath =
3131 (expectedPathSegment: string) =>
......@@ -203,11 +203,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
203203 () =>
204204 Effect.gen(function* () {
205205 const tempDir = yield* makeTempDir("t3-vite-plus-capabilities");
206 const vitePlusBinDir = path.join(tempDir, ".vite-plus", "bin");
207 mkdirSync(vitePlusBinDir, { recursive: true });
208 const packageToolPath = path.join(vitePlusBinDir, "package-tool");
209 writeFileSync(packageToolPath, "#!/bin/sh\n");
210 chmodSync(packageToolPath, 0o755);
206 const vitePlusBinDir = NodePath.join(tempDir, ".vite-plus", "bin");
207 NodeFS.mkdirSync(vitePlusBinDir, { recursive: true });
208 const packageToolPath = NodePath.join(vitePlusBinDir, "package-tool");
209 NodeFS.writeFileSync(packageToolPath, "#!/bin/sh\n");
210 NodeFS.chmodSync(packageToolPath, 0o755);
211211
212212 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(
213213 packageToolUpdate,
......@@ -240,9 +240,9 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
240240 () =>
241241 Effect.gen(function* () {
242242 const tempDir = yield* makeTempDir("t3-bun-capabilities");
243 const bunBinDir = path.join(tempDir, ".bun", "bin");
244 mkdirSync(bunBinDir, { recursive: true });
245 writeFileSync(path.join(bunBinDir, "native-package-tool.exe"), "MZ");
243 const bunBinDir = NodePath.join(tempDir, ".bun", "bin");
244 NodeFS.mkdirSync(bunBinDir, { recursive: true });
245 NodeFS.writeFileSync(NodePath.join(bunBinDir, "native-package-tool.exe"), "MZ");
246246
247247 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(
248248 nativePackageToolUpdate,
......@@ -276,11 +276,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
276276 () =>
277277 Effect.gen(function* () {
278278 const tempDir = yield* makeTempDir("t3-pnpm-capabilities");
279 const pnpmHomeDir = path.join(tempDir, ".local", "share", "pnpm");
280 mkdirSync(pnpmHomeDir, { recursive: true });
281 const scopedPackageToolPath = path.join(pnpmHomeDir, "scoped-package-tool");
282 writeFileSync(scopedPackageToolPath, "#!/bin/sh\n");
283 chmodSync(scopedPackageToolPath, 0o755);
279 const pnpmHomeDir = NodePath.join(tempDir, ".local", "share", "pnpm");
280 NodeFS.mkdirSync(pnpmHomeDir, { recursive: true });
281 const scopedPackageToolPath = NodePath.join(pnpmHomeDir, "scoped-package-tool");
282 NodeFS.writeFileSync(scopedPackageToolPath, "#!/bin/sh\n");
283 NodeFS.chmodSync(scopedPackageToolPath, 0o755);
284284
285285 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(
286286 scopedPackageToolUpdate,
......@@ -336,11 +336,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
336336 () =>
337337 Effect.gen(function* () {
338338 const tempDir = yield* makeTempDir("t3-native-package-tool-native-capabilities");
339 const nativeBinDir = path.join(tempDir, ".local", "bin");
340 mkdirSync(nativeBinDir, { recursive: true });
341 const nativePackageToolPath = path.join(nativeBinDir, "native-package-tool");
342 writeFileSync(nativePackageToolPath, "#!/bin/sh\n");
343 chmodSync(nativePackageToolPath, 0o755);
339 const nativeBinDir = NodePath.join(tempDir, ".local", "bin");
340 NodeFS.mkdirSync(nativeBinDir, { recursive: true });
341 const nativePackageToolPath = NodePath.join(nativeBinDir, "native-package-tool");
342 NodeFS.writeFileSync(nativePackageToolPath, "#!/bin/sh\n");
343 NodeFS.chmodSync(nativePackageToolPath, 0o755);
344344
345345 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(
346346 nativePackageToolUpdate,
......@@ -373,11 +373,11 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
373373 () =>
374374 Effect.gen(function* () {
375375 const tempDir = yield* makeTempDir("t3-scoped-package-tool-native-capabilities");
376 const nativeBinDir = path.join(tempDir, ".scoped-package-tool", "bin");
377 mkdirSync(nativeBinDir, { recursive: true });
378 const scopedPackageToolPath = path.join(nativeBinDir, "scoped-package-tool");
379 writeFileSync(scopedPackageToolPath, "#!/bin/sh\n");
380 chmodSync(scopedPackageToolPath, 0o755);
376 const nativeBinDir = NodePath.join(tempDir, ".scoped-package-tool", "bin");
377 NodeFS.mkdirSync(nativeBinDir, { recursive: true });
378 const scopedPackageToolPath = NodePath.join(nativeBinDir, "scoped-package-tool");
379 NodeFS.writeFileSync(scopedPackageToolPath, "#!/bin/sh\n");
380 NodeFS.chmodSync(scopedPackageToolPath, 0o755);
381381
382382 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(
383383 scopedPackageToolUpdate,
......@@ -454,8 +454,8 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
454454 it.effect("keeps npm updates for binaries symlinked into npm's global node_modules tree", () =>
455455 Effect.gen(function* () {
456456 const tempDir = yield* makeTempDir("t3-npm-capabilities");
457 const binDir = path.join(tempDir, "bin");
458 const packageBinDir = path.join(
457 const binDir = NodePath.join(tempDir, "bin");
458 const packageBinDir = NodePath.join(
459459 tempDir,
460460 "lib",
461461 "node_modules",
......@@ -463,13 +463,13 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
463463 "package-tool",
464464 "bin",
465465 );
466 mkdirSync(binDir, { recursive: true });
467 mkdirSync(packageBinDir, { recursive: true });
468 const packageBinPath = path.join(packageBinDir, "package-tool.js");
469 const symlinkPath = path.join(binDir, "package-tool");
470 writeFileSync(packageBinPath, "#!/usr/bin/env node\n");
471 chmodSync(packageBinPath, 0o755);
472 symlinkSync(packageBinPath, symlinkPath);
466 NodeFS.mkdirSync(binDir, { recursive: true });
467 NodeFS.mkdirSync(packageBinDir, { recursive: true });
468 const packageBinPath = NodePath.join(packageBinDir, "package-tool.js");
469 const symlinkPath = NodePath.join(binDir, "package-tool");
470 NodeFS.writeFileSync(packageBinPath, "#!/usr/bin/env node\n");
471 NodeFS.chmodSync(packageBinPath, 0o755);
472 NodeFS.symlinkSync(packageBinPath, symlinkPath);
473473
474474 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, {
475475 binaryPath: symlinkPath,
......@@ -497,8 +497,8 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
497497 it.effect("uses Effect FileSystem realPath when detecting pnpm global symlinks", () =>
498498 Effect.gen(function* () {
499499 const tempDir = yield* makeTempDir("t3-pnpm-realpath-capabilities");
500 const binDir = path.join(tempDir, "bin");
501 const packageBinDir = path.join(
500 const binDir = NodePath.join(tempDir, "bin");
501 const packageBinDir = NodePath.join(
502502 tempDir,
503503 ".local",
504504 "share",
......@@ -510,13 +510,13 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
510510 "package-tool",
511511 "bin",
512512 );
513 mkdirSync(binDir, { recursive: true });
514 mkdirSync(packageBinDir, { recursive: true });
515 const packageBinPath = path.join(packageBinDir, "package-tool.js");
516 const symlinkPath = path.join(binDir, "package-tool");
517 writeFileSync(packageBinPath, "#!/usr/bin/env node\n");
518 chmodSync(packageBinPath, 0o755);
519 symlinkSync(packageBinPath, symlinkPath);
513 NodeFS.mkdirSync(binDir, { recursive: true });
514 NodeFS.mkdirSync(packageBinDir, { recursive: true });
515 const packageBinPath = NodePath.join(packageBinDir, "package-tool.js");
516 const symlinkPath = NodePath.join(binDir, "package-tool");
517 NodeFS.writeFileSync(packageBinPath, "#!/usr/bin/env node\n");
518 NodeFS.chmodSync(packageBinPath, 0o755);
519 NodeFS.symlinkSync(packageBinPath, symlinkPath);
520520
521521 const capabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(packageToolUpdate, {
522522 binaryPath: symlinkPath,
apps/server/src/relay/AgentAwarenessRelay.test.ts+2-2
......@@ -1,4 +1,4 @@
1import { generateKeyPairSync } from "node:crypto";
1import * as NodeCrypto from "node:crypto";
22import * as NodeServices from "@effect/platform-node/NodeServices";
33
44import type {
......@@ -348,7 +348,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => {
348348 });
349349
350350 it("signs the activity publish JWT and rejects tampering", async () => {
351 const keyPair = generateKeyPairSync("ed25519", {
351 const keyPair = NodeCrypto.generateKeyPairSync("ed25519", {
352352 privateKeyEncoding: { format: "pem", type: "pkcs8" },
353353 publicKeyEncoding: { format: "pem", type: "spki" },
354354 });
apps/server/src/server.test.ts+22-22
......@@ -1,7 +1,7 @@
11import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
22import * as NodeSocket from "@effect/platform-node/NodeSocket";
33import * as NodeServices from "@effect/platform-node/NodeServices";
4import { generateKeyPairSync, type KeyObject, sign } from "node:crypto";
4import * as NodeCrypto from "node:crypto";
55
66import {
77 AuthAccessTokenType,
......@@ -950,14 +950,14 @@ const makeDpopProof = (input: {
950950 readonly iat: number;
951951 readonly accessToken?: string;
952952 readonly jti?: string;
953 readonly privateKey?: KeyObject;
953 readonly privateKey?: NodeCrypto.KeyObject;
954954 readonly publicJwk?: DpopPublicJwk;
955955}) => {
956956 const keyPair =
957957 input.privateKey && input.publicJwk
958958 ? { privateKey: input.privateKey, publicJwk: input.publicJwk }
959959 : (() => {
960 const { privateKey, publicKey } = generateKeyPairSync("ec", {
960 const { privateKey, publicKey } = NodeCrypto.generateKeyPairSync("ec", {
961961 namedCurve: "P-256",
962962 });
963963 return { privateKey, publicJwk: publicKey.export({ format: "jwk" }) as DpopPublicJwk };
......@@ -978,7 +978,7 @@ const makeDpopProof = (input: {
978978 ...(input.accessToken ? { ath: computeDpopAccessTokenHash(input.accessToken) } : {}),
979979 }),
980980 ).toString("base64url");
981 const signature = sign("sha256", Buffer.from(`${header}.${payload}`), {
981 const signature = NodeCrypto.sign("sha256", Buffer.from(`${header}.${payload}`), {
982982 key: keyPair.privateKey,
983983 dsaEncoding: "ieee-p1363",
984984 }).toString("base64url");
......@@ -1024,7 +1024,7 @@ const makeCloudMintCredentialRequest = (input: {
10241024 const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
10251025 const signingInput = `${header}.${encodedPayload}`;
10261026 return {
1027 proof: `${signingInput}.${sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`,
1027 proof: `${signingInput}.${NodeCrypto.sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`,
10281028 };
10291029};
10301030
......@@ -1057,7 +1057,7 @@ const makeCloudEnvironmentHealthRequest = (input: {
10571057 const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
10581058 const signingInput = `${header}.${encodedPayload}`;
10591059 return {
1060 proof: `${signingInput}.${sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`,
1060 proof: `${signingInput}.${NodeCrypto.sign(null, Buffer.from(signingInput), input.privateKey).toString("base64url")}`,
10611061 };
10621062};
10631063
......@@ -2054,7 +2054,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
20542054 Effect.gen(function* () {
20552055 yield* buildAppUnderTest();
20562056
2057 const cloudKeyPair = generateKeyPairSync("ed25519", {
2057 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
20582058 privateKeyEncoding: { format: "pem", type: "pkcs8" },
20592059 publicKeyEncoding: { format: "pem", type: "spki" },
20602060 });
......@@ -2131,7 +2131,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
21312131 Effect.gen(function* () {
21322132 yield* buildAppUnderTest();
21332133
2134 const cloudKeyPair = generateKeyPairSync("ed25519", {
2134 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
21352135 privateKeyEncoding: { format: "pem", type: "pkcs8" },
21362136 publicKeyEncoding: { format: "pem", type: "spki" },
21372137 });
......@@ -2174,7 +2174,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
21742174 Effect.gen(function* () {
21752175 yield* buildAppUnderTest();
21762176
2177 const cloudKeyPair = generateKeyPairSync("ed25519", {
2177 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
21782178 privateKeyEncoding: { format: "pem", type: "pkcs8" },
21792179 publicKeyEncoding: { format: "pem", type: "spki" },
21802180 });
......@@ -2268,7 +2268,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
22682268 },
22692269 });
22702270
2271 const cloudKeyPair = generateKeyPairSync("ed25519", {
2271 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
22722272 privateKeyEncoding: { format: "pem", type: "pkcs8" },
22732273 publicKeyEncoding: { format: "pem", type: "spki" },
22742274 });
......@@ -2345,7 +2345,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
23452345 Effect.gen(function* () {
23462346 yield* buildAppUnderTest();
23472347
2348 const cloudKeyPair = generateKeyPairSync("ed25519", {
2348 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
23492349 privateKeyEncoding: { format: "pem", type: "pkcs8" },
23502350 publicKeyEncoding: { format: "pem", type: "spki" },
23512351 });
......@@ -2404,7 +2404,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
24042404 Effect.gen(function* () {
24052405 yield* buildAppUnderTest();
24062406
2407 const cloudKeyPair = generateKeyPairSync("ed25519", {
2407 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
24082408 privateKeyEncoding: { format: "pem", type: "pkcs8" },
24092409 publicKeyEncoding: { format: "pem", type: "spki" },
24102410 });
......@@ -2463,7 +2463,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
24632463 Effect.gen(function* () {
24642464 yield* buildAppUnderTest();
24652465
2466 const cloudKeyPair = generateKeyPairSync("ed25519", {
2466 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
24672467 privateKeyEncoding: { format: "pem", type: "pkcs8" },
24682468 publicKeyEncoding: { format: "pem", type: "spki" },
24692469 });
......@@ -2523,7 +2523,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
25232523 Effect.gen(function* () {
25242524 yield* buildAppUnderTest();
25252525
2526 const cloudKeyPair = generateKeyPairSync("ed25519", {
2526 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
25272527 privateKeyEncoding: { format: "pem", type: "pkcs8" },
25282528 publicKeyEncoding: { format: "pem", type: "spki" },
25292529 });
......@@ -2584,7 +2584,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
25842584 Effect.gen(function* () {
25852585 yield* buildAppUnderTest();
25862586
2587 const cloudKeyPair = generateKeyPairSync("ed25519", {
2587 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
25882588 privateKeyEncoding: { format: "pem", type: "pkcs8" },
25892589 publicKeyEncoding: { format: "pem", type: "spki" },
25902590 });
......@@ -2664,7 +2664,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
26642664 },
26652665 });
26662666
2667 const cloudKeyPair = generateKeyPairSync("ed25519", {
2667 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
26682668 privateKeyEncoding: { format: "pem", type: "pkcs8" },
26692669 publicKeyEncoding: { format: "pem", type: "spki" },
26702670 });
......@@ -2733,7 +2733,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
27332733 Effect.gen(function* () {
27342734 yield* buildAppUnderTest();
27352735
2736 const cloudKeyPair = generateKeyPairSync("ed25519", {
2736 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
27372737 privateKeyEncoding: { format: "pem", type: "pkcs8" },
27382738 publicKeyEncoding: { format: "pem", type: "spki" },
27392739 });
......@@ -2800,7 +2800,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
28002800 Effect.gen(function* () {
28012801 yield* buildAppUnderTest();
28022802
2803 const cloudKeyPair = generateKeyPairSync("ed25519", {
2803 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
28042804 privateKeyEncoding: { format: "pem", type: "pkcs8" },
28052805 publicKeyEncoding: { format: "pem", type: "spki" },
28062806 });
......@@ -2851,7 +2851,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
28512851 Effect.gen(function* () {
28522852 yield* buildAppUnderTest();
28532853
2854 const cloudKeyPair = generateKeyPairSync("ed25519", {
2854 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
28552855 privateKeyEncoding: { format: "pem", type: "pkcs8" },
28562856 publicKeyEncoding: { format: "pem", type: "spki" },
28572857 });
......@@ -2902,7 +2902,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
29022902 Effect.gen(function* () {
29032903 yield* buildAppUnderTest();
29042904
2905 const cloudKeyPair = generateKeyPairSync("ed25519", {
2905 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
29062906 privateKeyEncoding: { format: "pem", type: "pkcs8" },
29072907 publicKeyEncoding: { format: "pem", type: "spki" },
29082908 });
......@@ -2967,7 +2967,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
29672967 Effect.gen(function* () {
29682968 yield* buildAppUnderTest();
29692969
2970 const cloudKeyPair = generateKeyPairSync("ed25519", {
2970 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
29712971 privateKeyEncoding: { format: "pem", type: "pkcs8" },
29722972 publicKeyEncoding: { format: "pem", type: "spki" },
29732973 });
......@@ -3017,7 +3017,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
30173017 Effect.gen(function* () {
30183018 yield* buildAppUnderTest();
30193019
3020 const cloudKeyPair = generateKeyPairSync("ed25519", {
3020 const cloudKeyPair = NodeCrypto.generateKeyPairSync("ed25519", {
30213021 privateKeyEncoding: { format: "pem", type: "pkcs8" },
30223022 publicKeyEncoding: { format: "pem", type: "spki" },
30233023 });
apps/server/src/terminal/NodePtyAdapter.ts+2-2
......@@ -1,4 +1,4 @@
1import { createRequire } from "node:module";
1import * as NodeModule from "node:module";
22
33import * as Effect from "effect/Effect";
44import * as FileSystem from "effect/FileSystem";
......@@ -11,7 +11,7 @@ import * as PtyAdapter from "./PtyAdapter.ts";
1111let didEnsureSpawnHelperExecutable = false;
1212
1313const resolveNodePtySpawnHelperPath = Effect.gen(function* () {
14 const requireForNodePty = createRequire(import.meta.url);
14 const requireForNodePty = NodeModule.createRequire(import.meta.url);
1515 const path = yield* Path.Path;
1616 const fs = yield* FileSystem.FileSystem;
1717 const platform = yield* HostProcessPlatform;
apps/server/src/textGeneration/CursorTextGeneration.test.ts+25-21
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as path from "node:path";
3import * as os from "node:os";
4import { fileURLToPath } from "node:url";
5import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2import * as NodePath from "node:path";
3import * as NodeOS from "node:os";
4import * as NodeURL from "node:url";
5import * as NodeFS from "node:fs";
66
77import * as NodeServices from "@effect/platform-node/NodeServices";
88import { it } from "@effect/vitest";
......@@ -21,8 +21,8 @@ import * as TextGeneration from "./TextGeneration.ts";
2121import { makeCursorTextGeneration } from "./CursorTextGeneration.ts";
2222const decodeCursorSettings = Schema.decodeSync(CursorSettings);
2323
24const __dirname = path.dirname(fileURLToPath(import.meta.url));
25const mockAgentPath = path.join(__dirname, "../../scripts/acp-mock-agent.ts");
24const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
25const mockAgentPath = NodePath.join(__dirname, "../../scripts/acp-mock-agent.ts");
2626
2727function shellSingleQuote(value: string): string {
2828 return `'${value.replaceAll("'", `'"'"'`)}'`;
......@@ -33,10 +33,10 @@ const CursorTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces
3333}).pipe(Layer.provideMerge(NodeServices.layer));
3434
3535function makeAcpAgentWrapper(dir: string, env: Record<string, string>): string {
36 const binDir = path.join(dir, "bin");
37 const agentPath = path.join(binDir, "agent");
38 mkdirSync(binDir, { recursive: true });
39 writeFileSync(
36 const binDir = NodePath.join(dir, "bin");
37 const agentPath = NodePath.join(binDir, "agent");
38 NodeFS.mkdirSync(binDir, { recursive: true });
39 NodeFS.writeFileSync(
4040 agentPath,
4141 [
4242 "#!/bin/sh",
......@@ -50,7 +50,7 @@ function makeAcpAgentWrapper(dir: string, env: Record<string, string>): string {
5050 ].join("\n"),
5151 "utf8",
5252 );
53 chmodSync(agentPath, 0o755);
53 NodeFS.chmodSync(agentPath, 0o755);
5454 return agentPath;
5555}
5656
......@@ -59,10 +59,10 @@ function withFakeAcpAgent<A, E, R>(
5959 effectFn: (textGeneration: TextGeneration.TextGeneration["Service"]) => Effect.Effect<A, E, R>,
6060) {
6161 return Effect.gen(function* () {
62 const tempDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-acp-"));
62 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-acp-"));
6363 yield* Effect.addFinalizer(() =>
6464 Effect.sync(() => {
65 rmSync(tempDir, { recursive: true, force: true });
65 NodeFS.rmSync(tempDir, { recursive: true, force: true });
6666 }),
6767 );
6868 const agentPath = makeAcpAgentWrapper(tempDir, env);
......@@ -76,7 +76,7 @@ function waitForFileContent(path: string): Effect.Effect<string> {
7676 return Effect.gen(function* () {
7777 const deadline = (yield* Clock.currentTimeMillis) + 5_000;
7878 for (;;) {
79 const result = yield* Effect.exit(Effect.sync(() => readFileSync(path, "utf8")));
79 const result = yield* Effect.exit(Effect.sync(() => NodeFS.readFileSync(path, "utf8")));
8080 if (Exit.isSuccess(result)) {
8181 return result.value;
8282 }
......@@ -92,8 +92,10 @@ function waitForFileContent(path: string): Effect.Effect<string> {
9292
9393it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => {
9494 it.effect("uses ACP model config options instead of raw CLI model ids", () => {
95 const requestLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-log-"));
96 const requestLogPath = path.join(requestLogDir, "requests.ndjson");
95 const requestLogDir = NodeFS.mkdtempSync(
96 NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-log-"),
97 );
98 const requestLogPath = NodePath.join(requestLogDir, "requests.ndjson");
9799
98100 return withFakeAcpAgent(
99101 {
......@@ -123,7 +125,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => {
123125 expect(generated.subject).toBe("Add generated commit message");
124126 expect(generated.body).toBe("- verify cursor acp model config path");
125127
126 const requests = readFileSync(requestLogPath, "utf8")
128 const requests = NodeFS.readFileSync(requestLogPath, "utf8")
127129 .trim()
128130 .split("\n")
129131 .filter((line) => line.length > 0)
......@@ -181,7 +183,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => {
181183 ]),
182184 );
183185
184 rmSync(requestLogDir, { recursive: true, force: true });
186 NodeFS.rmSync(requestLogDir, { recursive: true, force: true });
185187 }),
186188 );
187189 });
......@@ -235,8 +237,10 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => {
235237 );
236238
237239 it.effect("closes the ACP child process after text generation completes", () => {
238 const exitLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-cursor-text-exit-log-"));
239 const exitLogPath = path.join(exitLogDir, "exit.log");
240 const exitLogDir = NodeFS.mkdtempSync(
241 NodePath.join(NodeOS.tmpdir(), "t3code-cursor-text-exit-log-"),
242 );
243 const exitLogPath = NodePath.join(exitLogDir, "exit.log");
240244
241245 return withFakeAcpAgent(
242246 {
......@@ -265,7 +269,7 @@ it.layer(CursorTextGenerationTestLayer)("CursorTextGeneration", (it) => {
265269 const exitLog = yield* waitForFileContent(exitLogPath);
266270 expect(exitLog).toContain("exit:0");
267271
268 rmSync(exitLogDir, { recursive: true, force: true });
272 NodeFS.rmSync(exitLogDir, { recursive: true, force: true });
269273 }),
270274 );
271275 });
apps/server/src/textGeneration/GrokTextGeneration.test.ts+18-16
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import * as path from "node:path";
3import * as os from "node:os";
4import { fileURLToPath } from "node:url";
5import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2import * as NodePath from "node:path";
3import * as NodeOS from "node:os";
4import * as NodeURL from "node:url";
5import * as NodeFS from "node:fs";
66
77import * as NodeServices from "@effect/platform-node/NodeServices";
88import { it } from "@effect/vitest";
......@@ -18,8 +18,8 @@ import * as TextGeneration from "./TextGeneration.ts";
1818import { makeGrokTextGeneration } from "./GrokTextGeneration.ts";
1919const decodeGrokSettings = Schema.decodeSync(GrokSettings);
2020
21const __dirname = path.dirname(fileURLToPath(import.meta.url));
22const mockAgentPath = path.join(__dirname, "../../scripts/acp-mock-agent.ts");
21const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
22const mockAgentPath = NodePath.join(__dirname, "../../scripts/acp-mock-agent.ts");
2323
2424function shellSingleQuote(value: string): string {
2525 return `'${value.replaceAll("'", `'"'"'`)}'`;
......@@ -30,10 +30,10 @@ const GrokTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(process.
3030}).pipe(Layer.provideMerge(NodeServices.layer));
3131
3232function makeAcpGrokWrapper(dir: string, env: Record<string, string>): string {
33 const binDir = path.join(dir, "bin");
34 const grokPath = path.join(binDir, "grok");
35 mkdirSync(binDir, { recursive: true });
36 writeFileSync(
33 const binDir = NodePath.join(dir, "bin");
34 const grokPath = NodePath.join(binDir, "grok");
35 NodeFS.mkdirSync(binDir, { recursive: true });
36 NodeFS.writeFileSync(
3737 grokPath,
3838 [
3939 "#!/bin/sh",
......@@ -47,7 +47,7 @@ function makeAcpGrokWrapper(dir: string, env: Record<string, string>): string {
4747 ].join("\n"),
4848 "utf8",
4949 );
50 chmodSync(grokPath, 0o755);
50 NodeFS.chmodSync(grokPath, 0o755);
5151 return grokPath;
5252}
5353
......@@ -56,10 +56,10 @@ function withFakeAcpGrok<A, E, R>(
5656 effectFn: (textGeneration: TextGeneration.TextGeneration["Service"]) => Effect.Effect<A, E, R>,
5757) {
5858 return Effect.gen(function* () {
59 const tempDir = mkdtempSync(path.join(os.tmpdir(), "t3code-grok-text-acp-"));
59 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-acp-"));
6060 yield* Effect.addFinalizer(() =>
6161 Effect.sync(() => {
62 rmSync(tempDir, { recursive: true, force: true });
62 NodeFS.rmSync(tempDir, { recursive: true, force: true });
6363 }),
6464 );
6565 const binaryPath = makeAcpGrokWrapper(tempDir, env);
......@@ -72,7 +72,7 @@ function withFakeAcpGrok<A, E, R>(
7272function readJsonRpcRequests(
7373 filePath: string,
7474): ReadonlyArray<{ readonly method?: string; readonly params?: Record<string, unknown> }> {
75 return readFileSync(filePath, "utf8")
75 return NodeFS.readFileSync(filePath, "utf8")
7676 .trim()
7777 .split("\n")
7878 .filter((line) => line.length > 0)
......@@ -81,8 +81,10 @@ function readJsonRpcRequests(
8181
8282it.layer(GrokTextGenerationTestLayer)("GrokTextGeneration", (it) => {
8383 it.effect("uses ACP with disabled tool capabilities and forwards the requested model id", () => {
84 const requestLogDir = mkdtempSync(path.join(os.tmpdir(), "t3code-grok-text-log-"));
85 const requestLogPath = path.join(requestLogDir, "requests.ndjson");
84 const requestLogDir = NodeFS.mkdtempSync(
85 NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-log-"),
86 );
87 const requestLogPath = NodePath.join(requestLogDir, "requests.ndjson");
8688
8789 return withFakeAcpGrok(
8890 {
apps/server/src/vcs/GitVcsDriver.ts+5-2
......@@ -1,4 +1,4 @@
1import { randomUUID } from "node:crypto";
1import * as NodeCrypto from "node:crypto";
22
33import * as Context from "effect/Context";
44import * as DateTime from "effect/DateTime";
......@@ -651,7 +651,10 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* (
651651 captureCheckpoint: Effect.fn("GitVcsDriver.checkpoints.captureCheckpoint")(function* (input) {
652652 const operation = "GitVcsDriver.checkpoints.captureCheckpoint";
653653 const gitCommonDir = yield* resolveGitCommonDir(input.cwd);
654 const tempIndexPath = path.join(gitCommonDir, `t3-checkpoint-index-${randomUUID()}`);
654 const tempIndexPath = path.join(
655 gitCommonDir,
656 `t3-checkpoint-index-${NodeCrypto.randomUUID()}`,
657 );
655658 const commitEnv: NodeJS.ProcessEnv = {
656659 ...process.env,
657660 GIT_INDEX_FILE: tempIndexPath,
apps/server/src/workspace/WorkspaceEntries.test.ts+9-3
......@@ -1,13 +1,14 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fsPromises from "node:fs/promises";
2import * as NodeFSP from "node:fs/promises";
33import * as NodeServices from "@effect/platform-node/NodeServices";
44import { FileFinder } from "@ff-labs/fff-node";
5import { it, afterEach, describe, expect, vi } from "@effect/vitest";
5import { it, afterEach, describe, expect } from "@effect/vitest";
66import * as Effect from "effect/Effect";
77import * as FileSystem from "effect/FileSystem";
88import * as Layer from "effect/Layer";
99import * as Path from "effect/Path";
1010import * as PlatformError from "effect/PlatformError";
11import { vi } from "vite-plus/test";
1112
1213import * as ServerConfig from "../config.ts";
1314import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
......@@ -15,6 +16,11 @@ import * as VcsProcess from "../vcs/VcsProcess.ts";
1516import * as WorkspaceEntries from "./WorkspaceEntries.ts";
1617import * as WorkspacePaths from "./WorkspacePaths.ts";
1718
19vi.mock("node:fs/promises", async (importOriginal) => {
20 const actual = await importOriginal<typeof import("node:fs/promises")>();
21 return { ...actual, readdir: vi.fn(actual.readdir) };
22});
23
1824const TestLayer = Layer.empty.pipe(
1925 Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))),
2026 Layer.provideMerge(WorkspacePaths.layer),
......@@ -376,7 +382,7 @@ it.layer(TestLayer, { excludeTestServices: true })("WorkspaceEntries", (it) => {
376382 const cwd = yield* makeTempDir({ prefix: "t3code-workspace-browse-eacces-" });
377383
378384 const denied = Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" });
379 vi.spyOn(fsPromises, "readdir").mockRejectedValueOnce(denied);
385 vi.mocked(NodeFSP.readdir).mockRejectedValueOnce(denied);
380386
381387 const result = yield* workspaceEntries.browse({
382388 partialPath: yield* appendSeparator(cwd),
apps/server/src/workspace/WorkspaceEntries.ts+5-5
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { readdir } from "node:fs/promises";
3import { homedir } from "node:os";
2import * as NodeFSP from "node:fs/promises";
3import * as NodeOS from "node:os";
44
55import * as Context from "effect/Context";
66import * as Effect from "effect/Effect";
......@@ -98,10 +98,10 @@ export class WorkspaceEntries extends Context.Service<
9898
9999function expandHomePath(input: string, path: Path.Path): string {
100100 if (input === "~") {
101 return homedir();
101 return NodeOS.homedir();
102102 }
103103 if (input.startsWith("~/") || input.startsWith("~\\")) {
104 return path.join(homedir(), input.slice(2));
104 return path.join(NodeOS.homedir(), input.slice(2));
105105 }
106106 return input;
107107}
......@@ -176,7 +176,7 @@ export const make = Effect.gen(function* () {
176176 const prefix = endsWithSeparator ? "" : path.basename(resolvedInputPath);
177177
178178 const dirents = yield* Effect.tryPromise({
179 try: () => readdir(parentPath, { withFileTypes: true }),
179 try: () => NodeFSP.readdir(parentPath, { withFileTypes: true }),
180180 catch: (cause) =>
181181 new WorkspaceEntriesReadDirectoryError({
182182 cwd: input.cwd,
apps/server/src/workspace/WorkspaceFileSystem.ts+4-4
......@@ -7,7 +7,7 @@
77 *
88 * @module WorkspaceFileSystem
99 */
10import { open, realpath } from "node:fs/promises";
10import * as NodeFSP from "node:fs/promises";
1111
1212import type {
1313 ProjectReadFileInput,
......@@ -89,8 +89,8 @@ export const make = Effect.gen(function* () {
8989 return yield* Effect.tryPromise({
9090 try: async () => {
9191 const [realWorkspaceRoot, realTargetPath] = await Promise.all([
92 realpath(input.cwd),
93 realpath(target.absolutePath),
92 NodeFSP.realpath(input.cwd),
93 NodeFSP.realpath(target.absolutePath),
9494 ]);
9595 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
9696 if (
......@@ -101,7 +101,7 @@ export const make = Effect.gen(function* () {
101101 throw new Error("Workspace file path resolves outside the project root.");
102102 }
103103
104 const handle = await open(realTargetPath, "r");
104 const handle = await NodeFSP.open(realTargetPath, "r");
105105 try {
106106 const stat = await handle.stat();
107107 if (!stat.isFile()) {
apps/server/src/workspace/WorkspacePaths.ts+3-3
......@@ -6,7 +6,7 @@
66 *
77 * @module WorkspacePaths
88 */
9import { homedir } from "node:os";
9import * as NodeOS from "node:os";
1010
1111import * as Context from "effect/Context";
1212import * as Effect from "effect/Effect";
......@@ -105,10 +105,10 @@ function toPosixRelativePath(input: string): string {
105105
106106function expandHomePath(input: string, path: Path.Path): string {
107107 if (input === "~") {
108 return homedir();
108 return NodeOS.homedir();
109109 }
110110 if (input.startsWith("~/") || input.startsWith("~\\")) {
111 return path.join(homedir(), input.slice(2));
111 return path.join(NodeOS.homedir(), input.slice(2));
112112 }
113113 return input;
114114}
oxlint-plugin-t3code/index.ts+2
......@@ -1,5 +1,6 @@
11import { definePlugin } from "@oxlint/plugins";
22
3import namespaceNodeImports from "./rules/namespace-node-imports.ts";
34import noGlobalProcessRuntime from "./rules/no-global-process-runtime.ts";
45import noInlineSchemaCompile from "./rules/no-inline-schema-compile.ts";
56import noManualEffectRuntimeInTests from "./rules/no-manual-effect-runtime-in-tests.ts";
......@@ -9,6 +10,7 @@ export default definePlugin({
910 name: "t3code",
1011 },
1112 rules: {
13 "namespace-node-imports": namespaceNodeImports,
1214 "no-global-process-runtime": noGlobalProcessRuntime,
1315 "no-inline-schema-compile": noInlineSchemaCompile,
1416 "no-manual-effect-runtime-in-tests": noManualEffectRuntimeInTests,
oxlint-plugin-t3code/rules/namespace-node-imports.test.ts created+63
......@@ -0,0 +1,63 @@
1import { assert, describe } from "@effect/vitest";
2
3import { createOxlintRuleHarness } from "../test/utils.ts";
4
5const rule = createOxlintRuleHarness("t3code/namespace-node-imports");
6
7describe("t3code/namespace-node-imports", () => {
8 rule.valid(
9 "allows canonical Node namespaces",
10 `
11 import * as NodeFS from "node:fs";
12 import * as NodeFSP from "node:fs/promises";
13 import * as NodeAssert from "node:assert/strict";
14 import * as NodeChildProcess from "node:child_process";
15 import * as NodeTimersPromises from "node:timers/promises";
16 import type * as NodeStream from "node:stream";
17
18 NodeAssert.ok(NodeChildProcess.spawn && NodeTimersPromises.setTimeout);
19 export const read = NodeFS.readFileSync;
20 export const readAsync = NodeFSP.readFile;
21 export type Input = NodeStream.Readable;
22 `,
23 );
24
25 rule.valid(
26 "does not apply to non-Node packages",
27 `
28 import { BrowserWindow } from "electron";
29 `,
30 );
31
32 rule.invalid(
33 "reports named imports",
34 `
35 import { readFile } from "node:fs/promises";
36 `,
37 (output) => {
38 assert.match(output, /namespace named NodeFSP/);
39 },
40 );
41
42 rule.invalid(
43 "reports default imports",
44 `
45 import path from "node:path";
46 `,
47 (output) => {
48 assert.match(output, /namespace named NodePath/);
49 },
50 );
51
52 rule.invalid(
53 "reports non-canonical namespace aliases",
54 `
55 import * as Crypto from "node:crypto";
56 import * as NodeOs from "node:os";
57 `,
58 (output) => {
59 assert.match(output, /namespace named NodeCrypto/);
60 assert.match(output, /namespace named NodeOS/);
61 },
62 );
63});
oxlint-plugin-t3code/rules/namespace-node-imports.ts created+76
......@@ -0,0 +1,76 @@
1import { defineRule } from "@oxlint/plugins";
2
3const NODE_MODULE_ALIASES = new Map([
4 ["assert/strict", "Assert"],
5 ["fs/promises", "FSP"],
6]);
7
8const NODE_SEGMENT_ALIASES = new Map([
9 ["fs", "FS"],
10 ["os", "OS"],
11 ["url", "URL"],
12 ["vm", "VM"],
13]);
14
15const toPascalCase = (value: string) =>
16 value
17 .split(/[_-]/u)
18 .filter((segment) => segment.length > 0)
19 .map((segment) => segment[0]?.toUpperCase() + segment.slice(1))
20 .join("");
21
22const expectedNamespaceAlias = (source: string) => {
23 const moduleName = source.slice("node:".length);
24 const knownAlias = NODE_MODULE_ALIASES.get(moduleName);
25 if (knownAlias !== undefined) return `Node${knownAlias}`;
26
27 return `Node${moduleName
28 .split("/")
29 .map((segment) => NODE_SEGMENT_ALIASES.get(segment) ?? toPascalCase(segment))
30 .join("")}`;
31};
32
33const literalStringValue = (node: unknown): string | undefined => {
34 if (typeof node !== "object" || node === null) return undefined;
35 if (!("type" in node) || node.type !== "Literal") return undefined;
36 if (!("value" in node) || typeof node.value !== "string") return undefined;
37 return node.value;
38};
39
40const identifierName = (node: unknown): string | undefined => {
41 if (typeof node !== "object" || node === null) return undefined;
42 if (!("type" in node) || node.type !== "Identifier") return undefined;
43 if (!("name" in node) || typeof node.name !== "string") return undefined;
44 return node.name;
45};
46
47export default defineRule({
48 meta: {
49 type: "problem",
50 docs: {
51 description: "Require canonical namespace imports for Node.js built-in modules.",
52 },
53 },
54 create(context) {
55 return {
56 ImportDeclaration(node) {
57 const source = literalStringValue(node.source);
58 if (source === undefined || !source.startsWith("node:")) return;
59
60 const expectedAlias = expectedNamespaceAlias(source);
61 const namespaceImport =
62 node.specifiers.length === 1 && node.specifiers[0]?.type === "ImportNamespaceSpecifier"
63 ? node.specifiers[0]
64 : undefined;
65 const actualAlias = identifierName(namespaceImport?.local);
66
67 if (actualAlias === expectedAlias) return;
68
69 context.report({
70 node,
71 message: `Import ${source} as a namespace named ${expectedAlias}.`,
72 });
73 },
74 };
75 },
76});
packages/shared/src/logging.ts+15-15
......@@ -1,6 +1,6 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import fs from "node:fs";
3import path from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
44
55export interface RotatingFileSinkOptions {
66 readonly filePath: string;
......@@ -29,7 +29,7 @@ export class RotatingFileSink {
2929 this.maxFiles = options.maxFiles;
3030 this.throwOnError = options.throwOnError ?? false;
3131
32 fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
32 NodeFS.mkdirSync(NodePath.dirname(this.filePath), { recursive: true });
3333 this.pruneOverflowBackups();
3434 this.currentSize = this.readCurrentSize();
3535 }
......@@ -43,7 +43,7 @@ export class RotatingFileSink {
4343 this.rotate();
4444 }
4545
46 fs.appendFileSync(this.filePath, buffer);
46 NodeFS.appendFileSync(this.filePath, buffer);
4747 this.currentSize += buffer.length;
4848
4949 if (this.currentSize > this.maxBytes) {
......@@ -60,20 +60,20 @@ export class RotatingFileSink {
6060 private rotate(): void {
6161 try {
6262 const oldest = this.withSuffix(this.maxFiles);
63 if (fs.existsSync(oldest)) {
64 fs.rmSync(oldest, { force: true });
63 if (NodeFS.existsSync(oldest)) {
64 NodeFS.rmSync(oldest, { force: true });
6565 }
6666
6767 for (let index = this.maxFiles - 1; index >= 1; index -= 1) {
6868 const source = this.withSuffix(index);
6969 const target = this.withSuffix(index + 1);
70 if (fs.existsSync(source)) {
71 fs.renameSync(source, target);
70 if (NodeFS.existsSync(source)) {
71 NodeFS.renameSync(source, target);
7272 }
7373 }
7474
75 if (fs.existsSync(this.filePath)) {
76 fs.renameSync(this.filePath, this.withSuffix(1));
75 if (NodeFS.existsSync(this.filePath)) {
76 NodeFS.renameSync(this.filePath, this.withSuffix(1));
7777 }
7878
7979 this.currentSize = 0;
......@@ -87,13 +87,13 @@ export class RotatingFileSink {
8787
8888 private pruneOverflowBackups(): void {
8989 try {
90 const dir = path.dirname(this.filePath);
91 const baseName = path.basename(this.filePath);
92 for (const entry of fs.readdirSync(dir)) {
90 const dir = NodePath.dirname(this.filePath);
91 const baseName = NodePath.basename(this.filePath);
92 for (const entry of NodeFS.readdirSync(dir)) {
9393 if (!entry.startsWith(`${baseName}.`)) continue;
9494 const suffix = Number(entry.slice(baseName.length + 1));
9595 if (!Number.isInteger(suffix) || suffix <= this.maxFiles) continue;
96 fs.rmSync(path.join(dir, entry), { force: true });
96 NodeFS.rmSync(NodePath.join(dir, entry), { force: true });
9797 }
9898 } catch {
9999 if (this.throwOnError) {
......@@ -104,7 +104,7 @@ export class RotatingFileSink {
104104
105105 private readCurrentSize(): number {
106106 try {
107 return fs.statSync(this.filePath).size;
107 return NodeFS.statSync(this.filePath).size;
108108 } catch {
109109 return 0;
110110 }
packages/shared/src/shell.ts+8-8
......@@ -1,8 +1,8 @@
11// @effect-diagnostics nodeBuiltinImport:off
22import * as NodeOS from "node:os";
33import * as NodePath from "node:path";
4import { execFileSync } from "node:child_process";
5import { accessSync, constants as fileSystemConstants, statSync } from "node:fs";
4import * as NodeChildProcess from "node:child_process";
5import * as NodeFS from "node:fs";
66import * as Data from "effect/Data";
77import * as Effect from "effect/Effect";
88import * as FileSystem from "effect/FileSystem";
......@@ -26,7 +26,7 @@ type ExecFileSyncLike = (
2626
2727function canExecuteFile(filePath: string): boolean {
2828 try {
29 accessSync(filePath, fileSystemConstants.X_OK);
29 NodeFS.accessSync(filePath, NodeFS.constants.X_OK);
3030 return true;
3131 } catch {
3232 return false;
......@@ -108,7 +108,7 @@ function resolveSpawnExecutableWithNode(
108108 );
109109 const isExecutable = (candidate: string) => {
110110 try {
111 if (!statSync(candidate).isFile()) return false;
111 if (!NodeFS.statSync(candidate).isFile()) return false;
112112 if (platform === "win32") {
113113 return windowsPathExtensions.includes(path.extname(candidate).toUpperCase());
114114 }
......@@ -192,13 +192,13 @@ export function extractPathFromShellOutput(output: string): string | null {
192192
193193export function readPathFromLoginShell(
194194 shell: string,
195 execFile: ExecFileSyncLike = execFileSync,
195 execFile: ExecFileSyncLike = NodeChildProcess.execFileSync,
196196): string | undefined {
197197 return readEnvironmentFromLoginShell(shell, ["PATH"], execFile).PATH;
198198}
199199
200200export function readPathFromLaunchctl(
201 execFile: ExecFileSyncLike = execFileSync,
201 execFile: ExecFileSyncLike = NodeChildProcess.execFileSync,
202202): string | undefined {
203203 try {
204204 return trimNonEmpty(
......@@ -305,7 +305,7 @@ export type ShellEnvironmentReader = (
305305export const readEnvironmentFromLoginShell: ShellEnvironmentReader = (
306306 shell,
307307 names,
308 execFile = execFileSync,
308 execFile = NodeChildProcess.execFileSync,
309309) => {
310310 if (names.length === 0) {
311311 return {};
......@@ -371,7 +371,7 @@ export function readEnvironmentFromWindowsShell(
371371 const execFile: ExecFileSyncLike =
372372 typeof optionsOrExecFile === "function"
373373 ? optionsOrExecFile
374 : (maybeExecFile ?? (execFileSync as ExecFileSyncLike));
374 : (maybeExecFile ?? (NodeChildProcess.execFileSync as ExecFileSyncLike));
375375 const command = buildWindowsEnvironmentCaptureCommand(names);
376376 const args = [
377377 "-NoLogo",
packages/ssh/src/command.ts+5-2
......@@ -1,4 +1,4 @@
1import * as Crypto from "node:crypto";
1import * as NodeCrypto from "node:crypto";
22
33import type { DesktopSshEnvironmentTarget, DesktopUpdateChannel } from "@t3tools/contracts";
44import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
......@@ -74,7 +74,10 @@ export function targetConnectionKey(target: DesktopSshEnvironmentTarget): string
7474}
7575
7676export function remoteStateKey(target: DesktopSshEnvironmentTarget): string {
77 return Crypto.createHash("sha256").update(targetConnectionKey(target)).digest("hex").slice(0, 16);
77 return NodeCrypto.createHash("sha256")
78 .update(targetConnectionKey(target))
79 .digest("hex")
80 .slice(0, 16);
7881}
7982
8083export function buildSshHostSpec(target: DesktopSshEnvironmentTarget): string {
scripts/build-desktop-artifact.ts+2-2
......@@ -1,6 +1,6 @@
11#!/usr/bin/env node
22
3import { createRequire } from "node:module";
3import * as NodeModule from "node:module";
44
55import { fromYaml } from "@t3tools/shared/schemaYaml";
66import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
......@@ -482,7 +482,7 @@ const stageClerkPasskeyNativeBinaries = Effect.fn("stageClerkPasskeyNativeBinari
482482 path.join(stageAppDir, "node_modules", "@clerk", "electron-passkeys", "index.js"),
483483 );
484484 const packageDir = path.dirname(packageEntryPath);
485 const packageRequire = createRequire(packageEntryPath);
485 const packageRequire = NodeModule.createRequire(packageEntryPath);
486486
487487 for (const artifact of resolveClerkPasskeyNativeArtifacts(platform, arch)) {
488488 const sourcePath = yield* Effect.try({
scripts/lib/public-config.test.ts+9-9
......@@ -1,7 +1,7 @@
11// @effect-diagnostics nodeBuiltinImport:off - Tests exercise root env file precedence directly.
2import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3import { tmpdir } from "node:os";
4import { join } from "node:path";
2import * as NodeFS from "node:fs";
3import * as NodeOS from "node:os";
4import * as NodePath from "node:path";
55import { afterEach, describe, expect, it } from "vite-plus/test";
66
77import { loadRepoEnv, resolvePublicConfig } from "./public-config.ts";
......@@ -10,7 +10,7 @@ const temporaryDirectories: string[] = [];
1010
1111afterEach(() => {
1212 for (const directory of temporaryDirectories.splice(0)) {
13 rmSync(directory, { recursive: true, force: true });
13 NodeFS.rmSync(directory, { recursive: true, force: true });
1414 }
1515});
1616
......@@ -43,12 +43,12 @@ describe("loadRepoEnv", () => {
4343
4444 it("applies process, root local, and root precedence in that order", () => {
4545 const repoRoot = makeTemporaryDirectory();
46 writeFileSync(
47 join(repoRoot, ".env"),
46 NodeFS.writeFileSync(
47 NodePath.join(repoRoot, ".env"),
4848 "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",
4949 );
50 writeFileSync(
51 join(repoRoot, ".env.local"),
50 NodeFS.writeFileSync(
51 NodePath.join(repoRoot, ".env.local"),
5252 "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",
5353 );
5454
......@@ -148,7 +148,7 @@ describe("loadRepoEnv", () => {
148148});
149149
150150function makeTemporaryDirectory() {
151 const directory = mkdtempSync(join(tmpdir(), "t3code-public-config-"));
151 const directory = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-public-config-"));
152152 temporaryDirectories.push(directory);
153153 return directory;
154154}
scripts/release-smoke.ts+62-64
......@@ -1,21 +1,13 @@
11// @effect-diagnostics nodeBuiltinImport:off
2import { execFileSync } from "node:child_process";
3import {
4 cpSync,
5 existsSync,
6 mkdirSync,
7 mkdtempSync,
8 readFileSync,
9 rmSync,
10 writeFileSync,
11} from "node:fs";
12import { tmpdir } from "node:os";
13import { dirname, join, resolve } from "node:path";
14import { fileURLToPath } from "node:url";
2import * as NodeChildProcess from "node:child_process";
3import * as NodeFS from "node:fs";
4import * as NodeOS from "node:os";
5import * as NodePath from "node:path";
6import * as NodeURL from "node:url";
157import * as Console from "effect/Console";
168import * as Effect from "effect/Effect";
179
18const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10const repoRoot = NodePath.resolve(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), "..");
1911
2012const workspaceFiles = [
2113 "package.json",
......@@ -44,26 +36,26 @@ const workspaceFiles = [
4436
4537function copyWorkspaceManifestFixture(targetRoot: string): void {
4638 for (const relativePath of workspaceFiles) {
47 const sourcePath = resolve(repoRoot, relativePath);
48 const destinationPath = resolve(targetRoot, relativePath);
49 mkdirSync(dirname(destinationPath), { recursive: true });
50 cpSync(sourcePath, destinationPath);
39 const sourcePath = NodePath.resolve(repoRoot, relativePath);
40 const destinationPath = NodePath.resolve(targetRoot, relativePath);
41 NodeFS.mkdirSync(NodePath.dirname(destinationPath), { recursive: true });
42 NodeFS.cpSync(sourcePath, destinationPath);
5143 }
5244
53 const patchesDirectory = resolve(repoRoot, "patches");
54 if (existsSync(patchesDirectory)) {
55 cpSync(patchesDirectory, resolve(targetRoot, "patches"), { recursive: true });
45 const patchesDirectory = NodePath.resolve(repoRoot, "patches");
46 if (NodeFS.existsSync(patchesDirectory)) {
47 NodeFS.cpSync(patchesDirectory, NodePath.resolve(targetRoot, "patches"), { recursive: true });
5648 }
5749}
5850
5951function writeMacManifestFixtures(targetRoot: string): { arm64Path: string; x64Path: string } {
60 const assetDirectory = resolve(targetRoot, "release-assets");
61 mkdirSync(assetDirectory, { recursive: true });
52 const assetDirectory = NodePath.resolve(targetRoot, "release-assets");
53 NodeFS.mkdirSync(assetDirectory, { recursive: true });
6254
63 const arm64Path = resolve(assetDirectory, "latest-mac.yml");
64 const x64Path = resolve(assetDirectory, "latest-mac-x64.yml");
55 const arm64Path = NodePath.resolve(assetDirectory, "latest-mac.yml");
56 const x64Path = NodePath.resolve(assetDirectory, "latest-mac-x64.yml");
6557
66 writeFileSync(
58 NodeFS.writeFileSync(
6759 arm64Path,
6860 `version: 9.9.9-smoke.0
6961files:
......@@ -79,7 +71,7 @@ releaseDate: '2026-03-08T10:32:14.587Z'
7971`,
8072 );
8173
82 writeFileSync(
74 NodeFS.writeFileSync(
8375 x64Path,
8476 `version: 9.9.9-smoke.0
8577files:
......@@ -102,13 +94,13 @@ function writeWindowsManifestFixtures(
10294 targetRoot: string,
10395 channel: string,
10496): { arm64Path: string; x64Path: string } {
105 const assetDirectory = resolve(targetRoot, "release-assets");
106 mkdirSync(assetDirectory, { recursive: true });
97 const assetDirectory = NodePath.resolve(targetRoot, "release-assets");
98 NodeFS.mkdirSync(assetDirectory, { recursive: true });
10799
108 const arm64Path = resolve(assetDirectory, `${channel}-win-arm64.yml`);
109 const x64Path = resolve(assetDirectory, `${channel}-win-x64.yml`);
100 const arm64Path = NodePath.resolve(assetDirectory, `${channel}-win-arm64.yml`);
101 const x64Path = NodePath.resolve(assetDirectory, `${channel}-win-x64.yml`);
110102
111 writeFileSync(
103 NodeFS.writeFileSync(
112104 arm64Path,
113105 `version: 9.9.9-smoke.0
114106files:
......@@ -124,7 +116,7 @@ releaseDate: '2026-03-08T10:32:14.587Z'
124116`,
125117 );
126118
127 writeFileSync(
119 NodeFS.writeFileSync(
128120 x64Path,
129121 `version: 9.9.9-smoke.0
130122files:
......@@ -147,11 +139,11 @@ function writeWindowsBuilderDebugFixtures(targetRoot: string): {
147139 arm64Path: string;
148140 x64Path: string;
149141} {
150 const assetDirectory = resolve(targetRoot, "release-assets");
151 mkdirSync(assetDirectory, { recursive: true });
142 const assetDirectory = NodePath.resolve(targetRoot, "release-assets");
143 NodeFS.mkdirSync(assetDirectory, { recursive: true });
152144
153 const arm64Path = resolve(assetDirectory, "builder-debug-win-arm64.yml");
154 const x64Path = resolve(assetDirectory, "builder-debug-win-x64.yml");
145 const arm64Path = NodePath.resolve(assetDirectory, "builder-debug-win-arm64.yml");
146 const x64Path = NodePath.resolve(assetDirectory, "builder-debug-win-x64.yml");
155147 const debugFixture = `arm64:
156148 firstOrDefaultFilePatterns:
157149 - '**/*'
......@@ -160,8 +152,8 @@ nsis:
160152 !include "example.nsh"
161153`;
162154
163 writeFileSync(arm64Path, debugFixture);
164 writeFileSync(x64Path, debugFixture);
155 NodeFS.writeFileSync(arm64Path, debugFixture);
156 NodeFS.writeFileSync(x64Path, debugFixture);
165157
166158 return { arm64Path, x64Path };
167159}
......@@ -172,13 +164,13 @@ function assertContains(haystack: string, needle: string, message: string): void
172164}
173165
174166function assertExists(path: string, message: string): void {
175 if (!existsSync(path)) {
167 if (!NodeFS.existsSync(path)) {
176168 throw new Error(message);
177169 }
178170}
179171
180172function assertPackageVersion(path: string, version: string): void {
181 const packageJson = JSON.parse(readFileSync(path, "utf8")) as {
173 const packageJson = JSON.parse(NodeFS.readFileSync(path, "utf8")) as {
182174 readonly version?: unknown;
183175 };
184176
......@@ -188,20 +180,20 @@ function assertPackageVersion(path: string, version: string): void {
188180}
189181
190182function assertMissing(path: string, message: string): void {
191 if (existsSync(path)) {
183 if (NodeFS.existsSync(path)) {
192184 throw new Error(message);
193185 }
194186}
195187
196const tempRoot = mkdtempSync(join(tmpdir(), "t3-release-smoke-"));
188const tempRoot = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-release-smoke-"));
197189
198190try {
199191 copyWorkspaceManifestFixture(tempRoot);
200192
201 execFileSync(
193 NodeChildProcess.execFileSync(
202194 process.execPath,
203195 [
204 resolve(repoRoot, "scripts/update-release-package-versions.ts"),
196 NodePath.resolve(repoRoot, "scripts/update-release-package-versions.ts"),
205197 "9.9.9-smoke.0",
206198 "--root",
207199 tempRoot,
......@@ -212,14 +204,14 @@ try {
212204 },
213205 );
214206
215 rmSync(resolve(tempRoot, "pnpm-lock.yaml"), { force: true });
207 NodeFS.rmSync(NodePath.resolve(tempRoot, "pnpm-lock.yaml"), { force: true });
216208
217 execFileSync("vp", ["install", "--lockfile-only", "--ignore-scripts"], {
209 NodeChildProcess.execFileSync("vp", ["install", "--lockfile-only", "--ignore-scripts"], {
218210 cwd: tempRoot,
219211 stdio: "inherit",
220212 });
221213
222 const lockfile = readFileSync(resolve(tempRoot, "pnpm-lock.yaml"), "utf8");
214 const lockfile = NodeFS.readFileSync(NodePath.resolve(tempRoot, "pnpm-lock.yaml"), "utf8");
223215 assertContains(lockfile, "lockfileVersion:", "Expected pnpm-lock.yaml to be regenerated.");
224216
225217 for (const relativePath of [
......@@ -228,13 +220,13 @@ try {
228220 "apps/web/package.json",
229221 "packages/contracts/package.json",
230222 ]) {
231 assertPackageVersion(resolve(tempRoot, relativePath), "9.9.9-smoke.0");
223 assertPackageVersion(NodePath.resolve(tempRoot, relativePath), "9.9.9-smoke.0");
232224 }
233225
234 const nightlyReleaseMetadata = execFileSync(
226 const nightlyReleaseMetadata = NodeChildProcess.execFileSync(
235227 process.execPath,
236228 [
237 resolve(repoRoot, "scripts/resolve-nightly-release.ts"),
229 NodePath.resolve(repoRoot, "scripts/resolve-nightly-release.ts"),
238230 "--date",
239231 "20260413",
240232 "--run-number",
......@@ -266,10 +258,10 @@ try {
266258 );
267259
268260 const { arm64Path, x64Path } = writeMacManifestFixtures(tempRoot);
269 execFileSync(
261 NodeChildProcess.execFileSync(
270262 process.execPath,
271263 [
272 resolve(repoRoot, "scripts/merge-update-manifests.ts"),
264 NodePath.resolve(repoRoot, "scripts/merge-update-manifests.ts"),
273265 "--platform",
274266 "mac",
275267 arm64Path,
......@@ -281,7 +273,7 @@ try {
281273 },
282274 );
283275
284 const mergedManifest = readFileSync(arm64Path, "utf8");
276 const mergedManifest = NodeFS.readFileSync(arm64Path, "utf8");
285277 assertContains(
286278 mergedManifest,
287279 "T3-Code-9.9.9-smoke.0-arm64.zip",
......@@ -297,21 +289,21 @@ try {
297289 tempRoot,
298290 "latest",
299291 );
300 const mergedWindowsManifestPath = resolve(tempRoot, "release-assets/latest.yml");
292 const mergedWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/latest.yml");
301293 const { arm64Path: nightlyWinArm64Path, x64Path: nightlyWinX64Path } =
302294 writeWindowsManifestFixtures(tempRoot, "nightly");
303 const mergedNightlyWindowsManifestPath = resolve(tempRoot, "release-assets/nightly.yml");
295 const mergedNightlyWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/nightly.yml");
304296 const { arm64Path: previewWinArm64Path, x64Path: previewWinX64Path } =
305297 writeWindowsManifestFixtures(tempRoot, "preview");
306 const mergedPreviewWindowsManifestPath = resolve(tempRoot, "release-assets/preview.yml");
298 const mergedPreviewWindowsManifestPath = NodePath.resolve(tempRoot, "release-assets/preview.yml");
307299 const { arm64Path: winDebugArm64Path, x64Path: winDebugX64Path } =
308300 writeWindowsBuilderDebugFixtures(tempRoot);
309 execFileSync(
301 NodeChildProcess.execFileSync(
310302 "bash",
311303 [
312304 "-lc",
313305 `
314 release_assets_dir=${JSON.stringify(resolve(tempRoot, "release-assets"))}
306 release_assets_dir=${JSON.stringify(NodePath.resolve(tempRoot, "release-assets"))}
315307 shopt -s nullglob
316308 found_windows_manifest=false
317309 for x64_manifest in "$release_assets_dir"/*-win-x64.yml; do
......@@ -327,7 +319,7 @@ try {
327319 fi
328320
329321 found_windows_manifest=true
330 ${JSON.stringify(process.execPath)} ${JSON.stringify(resolve(repoRoot, "scripts/merge-update-manifests.ts"))} --platform win \
322 ${JSON.stringify(process.execPath)} ${JSON.stringify(NodePath.resolve(repoRoot, "scripts/merge-update-manifests.ts"))} --platform win \
331323 "$arm64_manifest" \
332324 "$x64_manifest" \
333325 "$output_manifest"
......@@ -346,7 +338,7 @@ try {
346338 },
347339 );
348340
349 const mergedWindowsManifest = readFileSync(mergedWindowsManifestPath, "utf8");
341 const mergedWindowsManifest = NodeFS.readFileSync(mergedWindowsManifestPath, "utf8");
350342 assertContains(
351343 mergedWindowsManifest,
352344 "T3-Code-9.9.9-smoke.0-arm64.exe",
......@@ -357,7 +349,10 @@ try {
357349 "T3-Code-9.9.9-smoke.0-x64.exe",
358350 "Merged Windows manifest is missing the x64 asset.",
359351 );
360 const mergedNightlyWindowsManifest = readFileSync(mergedNightlyWindowsManifestPath, "utf8");
352 const mergedNightlyWindowsManifest = NodeFS.readFileSync(
353 mergedNightlyWindowsManifestPath,
354 "utf8",
355 );
361356 assertContains(
362357 mergedNightlyWindowsManifest,
363358 "T3-Code-9.9.9-smoke.0-arm64.exe",
......@@ -368,7 +363,10 @@ try {
368363 "T3-Code-9.9.9-smoke.0-x64.exe",
369364 "Merged nightly Windows manifest is missing the x64 asset.",
370365 );
371 const mergedPreviewWindowsManifest = readFileSync(mergedPreviewWindowsManifestPath, "utf8");
366 const mergedPreviewWindowsManifest = NodeFS.readFileSync(
367 mergedPreviewWindowsManifestPath,
368 "utf8",
369 );
372370 assertContains(
373371 mergedPreviewWindowsManifest,
374372 "T3-Code-9.9.9-smoke.0-arm64.exe",
......@@ -411,5 +409,5 @@ try {
411409
412410 Effect.runSync(Console.log("Release smoke checks passed."));
413411} finally {
414 rmSync(tempRoot, { recursive: true, force: true });
412 NodeFS.rmSync(tempRoot, { recursive: true, force: true });
415413}
vite.config.ts+3-2
......@@ -1,11 +1,11 @@
11import "vite-plus/test/config";
22import { defineConfig } from "vite-plus";
3import { fileURLToPath } from "node:url";
3import * as NodeURL from "node:url";
44
55export default defineConfig({
66 resolve: {
77 alias: {
8 "~": fileURLToPath(new URL("./apps/web/src", import.meta.url)),
8 "~": NodeURL.fileURLToPath(new URL("./apps/web/src", import.meta.url)),
99 },
1010 },
1111 test: {
......@@ -111,6 +111,7 @@ export default defineConfig({
111111 "t3code/no-global-process-runtime": "error",
112112 "t3code/no-inline-schema-compile": "warn",
113113 "t3code/no-manual-effect-runtime-in-tests": "error",
114 "t3code/namespace-node-imports": "error",
114115 },
115116 options: {
116117 // Revisit once Oxlint's tsgolint path can integrate with @effect/tsgo diagnostics.