Skip to content

Commit 44112a5

Browse files
committed
fix(harness): make env.ts side-effect-free on module load for edge runtimes
Move .env file discovery and loadEnvFileCompat call out of module top level into an exported loadDotEnvFilesIfAvailable() helper. The module now loads cleanly in edge runtimes (Cloudflare Workers, Vercel Edge) without node:fs access. Node.js callers that relied on automatic .env loading must now call loadDotEnvFilesIfAvailable() explicitly from their entry point. The createAgentRuntime() function already does this.
1 parent e6b7ded commit 44112a5

4 files changed

Lines changed: 38 additions & 24 deletions

File tree

packages/harness/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# @ai-sdk-tool/harness
22

3+
## 1.3.2
4+
5+
### Fixes
6+
- `env.ts`: moved `.env` file discovery side-effects out of module top level into `loadDotEnvFilesIfAvailable()` helper. The module now loads safely in edge runtimes (Cloudflare Workers, Vercel Edge) without `node:fs` access. Callers that relied on the automatic `.env` loading must explicitly call `loadDotEnvFilesIfAvailable()` from their Node.js entry point.
7+
38
## 1.3.1
49

510
### Patch Changes

packages/harness/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ai-sdk-tool/harness",
3-
"version": "1.3.1",
3+
"version": "1.3.2",
44
"type": "module",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/harness/src/env.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,42 @@
1-
import { existsSync } from "node:fs";
2-
import { dirname, resolve } from "node:path";
31
import { createEnv } from "@t3-oss/env-core";
42
import { z } from "zod";
53
import { loadEnvFileCompat } from "./env-file";
64

7-
const findWorkspaceRootEnv = (startDir: string): string | null => {
8-
let current = resolve(startDir);
9-
10-
while (true) {
11-
if (existsSync(resolve(current, "pnpm-workspace.yaml"))) {
12-
return resolve(current, ".env");
5+
/** Call this only from Node.js entry points (CLI, test harness). Safe to omit in edge runtimes. */
6+
export const loadDotEnvFilesIfAvailable = (): void => {
7+
// eslint-disable-next-line @typescript-eslint/no-require-imports
8+
const { existsSync } = require("node:fs") as typeof import("node:fs");
9+
// eslint-disable-next-line @typescript-eslint/no-require-imports
10+
const { dirname, resolve } = require("node:path") as typeof import("node:path");
11+
12+
const findWorkspaceRootEnv = (startDir: string): string | null => {
13+
let current = resolve(startDir);
14+
15+
while (true) {
16+
if (existsSync(resolve(current, "pnpm-workspace.yaml"))) {
17+
return resolve(current, ".env");
18+
}
19+
20+
const parent = dirname(current);
21+
if (parent === current) {
22+
return null;
23+
}
24+
current = parent;
1325
}
26+
};
27+
28+
const envFileCandidates = [
29+
resolve(process.cwd(), ".env"),
30+
findWorkspaceRootEnv(process.cwd()),
31+
].filter((envPath): envPath is string => envPath !== null);
1432

15-
const parent = dirname(current);
16-
if (parent === current) {
17-
return null;
33+
for (const envPath of new Set(envFileCandidates)) {
34+
if (existsSync(envPath)) {
35+
loadEnvFileCompat(envPath);
1836
}
19-
current = parent;
2037
}
2138
};
2239

23-
const ENV_FILE_CANDIDATES = [
24-
resolve(process.cwd(), ".env"),
25-
findWorkspaceRootEnv(process.cwd()),
26-
].filter((envPath): envPath is string => envPath !== null);
27-
28-
for (const envPath of new Set(ENV_FILE_CANDIDATES)) {
29-
if (existsSync(envPath)) {
30-
loadEnvFileCompat(envPath);
31-
}
32-
}
33-
3440
export const env = createEnv({
3541
server: {
3642
/** Enable compaction debug logging to stderr. */

packages/harness/src/runtime/create-runtime.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createAgent } from "../agent";
22
import { CheckpointHistory } from "../checkpoint-history";
33
import { createModelSummarizer } from "../compaction-prompts";
4+
import { loadDotEnvFilesIfAvailable } from "../env";
45
import { SessionManager } from "../session";
56
import { SkillsEngine } from "../skills";
67
import type { Agent } from "../types";
@@ -42,6 +43,8 @@ export async function createAgentRuntime<
4243
>(
4344
config: AgentRuntimeConfig<TAgents, TContext>
4445
): Promise<AgentRuntime<TAgents, TContext>> {
46+
loadDotEnvFilesIfAvailable();
47+
4548
const agentMap = new Map<string, DefinedAgent<TContext>>();
4649
for (const definition of config.agents as readonly DefinedAgent<TContext>[]) {
4750
if (agentMap.has(definition.name)) {

0 commit comments

Comments
 (0)