refactor: migrated environment variable access from process.env to Bun.env across 71 files
- Migrated all environment variable access from Node.js `process.env` to Bun runtime `Bun.env` API across the entire codebase. - Updated 71 files including source code, tests, documentation, and configuration to use Bun's native environment variable API. - Added comprehensive environment variables documentation in `packages/coding-agent/docs/environment-variables.md` covering 100+ environment variables organized by category. - Updated CHANGELOG entries to reflect migration from `process.env` to `Bun.env` and environment variable prefix changes (PI_* vs OMP_*). - Maintained identical behavior and logic across all changes - only the environment variable access method was updated.
This commit is contained in:
@@ -64,7 +64,7 @@ We run on Bun. Replace Node APIs only when Bun provides a better alternative.
|
||||
|
||||
**DO NOT replace (these work fine in Bun):**
|
||||
|
||||
- `os.homedir()` — do NOT replace with `process.env.HOME`, `Bun.env.HOME`, or literal `"~"`
|
||||
- `os.homedir()` — do NOT replace with `Bun.env.HOME`, `Bun.env.HOME`, or literal `"~"`
|
||||
- `os.tmpdir()` — do NOT replace with `Bun.env.TMPDIR || "/tmp"` or hardcoded paths
|
||||
- `fs.mkdtempSync()` — do NOT replace with manual path construction
|
||||
- `path.join()`, `path.resolve()`, etc. — these are fine
|
||||
@@ -82,7 +82,7 @@ We run on Bun. Replace Node APIs only when Bun provides a better alternative.
|
||||
|
||||
```typescript
|
||||
// BROKEN: env vars may be undefined, "~" is not expanded
|
||||
const home = process.env.HOME || Bun.env.HOME || "~";
|
||||
const home = Bun.env.HOME || Bun.env.HOME || "~";
|
||||
const tmp = Bun.env.TMPDIR || "/tmp";
|
||||
```
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
## [Unreleased]
|
||||
|
||||
## [11.0.0] - 2026-02-05
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced direct `process.env` access with `getEnv()` utility from `@oh-my-pi/pi-utils` for consistent environment variable handling across all providers
|
||||
- Replaced direct `Bun.env` access with `getEnv()` utility from `@oh-my-pi/pi-utils` for consistent environment variable handling across all providers
|
||||
- Updated environment variable names from `OMP_*` prefix to `PI_*` prefix for consistency (e.g., `OMP_CODING_AGENT_DIR` → `PI_CODING_AGENT_DIR`)
|
||||
|
||||
### Removed
|
||||
@@ -13,6 +14,7 @@
|
||||
- Removed automatic environment variable migration from `PI_*` to `OMP_*` prefixes via `migrate-env.ts` module
|
||||
|
||||
## [10.5.0] - 2026-02-04
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated @anthropic-ai/sdk to ^0.72.1
|
||||
@@ -28,18 +30,20 @@
|
||||
- Removed undici dependency
|
||||
|
||||
## [9.4.0] - 2026-01-31
|
||||
|
||||
### Added
|
||||
|
||||
- Added `getEnv()` function to retrieve environment variables from process.env, cwd/.env, or ~/.env
|
||||
- Added `getEnv()` function to retrieve environment variables from Bun.env, cwd/.env, or ~/.env
|
||||
- Added support for reading .env files from home directory and current working directory
|
||||
- Added support for `exa` and `perplexity` as known providers in `getEnvApiKey()`
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed `getEnvApiKey()` to check process.env, cwd/.env, and ~/.env files in order of precedence
|
||||
- Changed `getEnvApiKey()` to check Bun.env, cwd/.env, and ~/.env files in order of precedence
|
||||
- Refactored provider API key resolution to use a declarative service provider map
|
||||
|
||||
## [9.2.2] - 2026-01-31
|
||||
|
||||
### Added
|
||||
|
||||
- Added OpenCode Zen provider with API key authentication for accessing multiple AI models
|
||||
@@ -86,31 +90,40 @@
|
||||
## [8.6.0] - 2026-01-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced JSON5 dependency with Bun.JSON5 parsing
|
||||
|
||||
### Fixed
|
||||
|
||||
- Filtered empty user text blocks for OpenAI-compatible completions and normalized Kimi reasoning_content for OpenRouter tool-call messages
|
||||
|
||||
## [8.4.0] - 2026-01-25
|
||||
|
||||
### Added
|
||||
|
||||
- Added Azure OpenAI Responses provider with deployment mapping and resource-based base URL support
|
||||
|
||||
### Changed
|
||||
|
||||
- Added OpenRouter routing preferences for OpenAI-compatible completions
|
||||
|
||||
### Fixed
|
||||
|
||||
- Defaulted Google tool call arguments to empty objects when providers omit args
|
||||
- Guarded Responses/Codex streaming deltas against missing content parts and handled arguments.done events
|
||||
|
||||
## [8.2.1] - 2026-01-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed handling of streaming function call arguments in OpenAI responses to properly parse arguments when sent via `response.function_call_arguments.done` events
|
||||
|
||||
## [8.2.0] - 2026-01-24
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrated node module imports from named to namespace imports across all packages for consistency with project guidelines
|
||||
|
||||
## [8.0.0] - 2026-01-23
|
||||
|
||||
### Fixed
|
||||
@@ -119,6 +132,7 @@
|
||||
- Fixed 400 errors when reading multiple images via GitHub Copilot's Claude models. Claude requires tool_use -> tool_result adjacency with no user messages interleaved. Images from consecutive tool results are now batched into a single user message
|
||||
|
||||
## [7.0.0] - 2026-01-21
|
||||
|
||||
### Added
|
||||
|
||||
- Added usage tracking system with normalized schema for provider quota/limit endpoints
|
||||
@@ -152,12 +166,14 @@
|
||||
- Fixed Gemini CLI usage windows to consistently label quota windows for display suppression
|
||||
|
||||
## [6.9.69] - 2026-01-21
|
||||
|
||||
### Added
|
||||
|
||||
- Added duration and time-to-first-token (ttft) metrics to all AI provider responses
|
||||
- Added performance tracking for streaming responses across all providers
|
||||
|
||||
## [6.9.0] - 2026-01-21
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed openai-codex provider exports from main package index
|
||||
@@ -165,6 +181,7 @@
|
||||
- Removed vitest configuration file
|
||||
|
||||
## [6.8.4] - 2026-01-21
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated prompt caching strategy to follow Anthropic's recommended hierarchy
|
||||
@@ -173,6 +190,7 @@
|
||||
- Increased OAuth callback timeout from 120 seconds to 120,000 milliseconds
|
||||
|
||||
## [6.8.3] - 2026-01-21
|
||||
|
||||
### Added
|
||||
|
||||
- Added `headers` option to all providers for custom request headers
|
||||
@@ -197,6 +215,7 @@
|
||||
- Enhanced AWS credential detection to support ECS task roles and IRSA web identity tokens
|
||||
|
||||
## [6.8.2] - 2026-01-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improved error handling for aborted requests in Google Gemini CLI provider
|
||||
@@ -238,6 +257,7 @@
|
||||
- Fixed token refresh for all OAuth providers
|
||||
|
||||
## [6.7.670] - 2026-01-19
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated Claude Code compatibility headers and version
|
||||
@@ -247,16 +267,19 @@
|
||||
- Updated PKCE verifier generation for better security
|
||||
|
||||
## [5.7.67] - 2026-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added error handling for unknown OAuth providers
|
||||
|
||||
## [5.6.77] - 2026-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevented duplicate tool results for errored or aborted messages when results already exist
|
||||
|
||||
## [5.6.7] - 2026-01-18
|
||||
|
||||
### Added
|
||||
|
||||
- Added automatic retry logic for OpenAI Codex responses with configurable delay and max retries
|
||||
@@ -265,8 +288,8 @@
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated environment variable prefix from PI_ to OMP_ for better consistency
|
||||
- Added automatic migration for legacy PI_ environment variables to OMP_ equivalents
|
||||
- Updated environment variable prefix from PI* to OMP* for better consistency
|
||||
- Added automatic migration for legacy PI* environment variables to OMP* equivalents
|
||||
- Adjusted Bedrock Claude thinking budgets to reserve output tokens when maxTokens is too low
|
||||
|
||||
### Fixed
|
||||
@@ -281,6 +304,7 @@
|
||||
- Fixed orphaned tool call handling to skip synthetic results for errored assistant messages
|
||||
|
||||
## [5.5.0] - 2026-01-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated User-Agent header from 'opencode' to 'pi' for OpenAI Codex requests
|
||||
@@ -288,6 +312,7 @@
|
||||
- Removed bridge text override from Codex system prompt builder
|
||||
|
||||
## [5.3.0] - 2026-01-15
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced detailed Codex system instructions with simplified pi assistant instructions
|
||||
@@ -307,6 +332,7 @@
|
||||
- Fixed numbered list items showing "1." for all items when code blocks break list continuity (via `start` property)
|
||||
|
||||
## [5.0.0] - 2026-01-12
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for `xhigh` thinking level in `thinkingBudgets` configuration
|
||||
@@ -318,6 +344,7 @@
|
||||
- Changed `supportsXhigh()` to return true for all Anthropic models
|
||||
|
||||
## [4.6.0] - 2026-01-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed incorrect classification of thought signatures in Google Gemini responses—thought signatures are now correctly treated as metadata rather than thinking content indicators
|
||||
@@ -325,22 +352,26 @@
|
||||
- Fixed Google schema sanitization stripping property names that match schema keywords (e.g., "pattern", "format") from tool definitions
|
||||
|
||||
## [4.4.9] - 2026-01-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Google provider schema sanitization to strip additional unsupported JSON Schema fields (patternProperties, additionalProperties, min/max constraints, pattern, format)
|
||||
|
||||
## [4.4.8] - 2026-01-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Google provider schema sanitization to properly collapse `anyOf`/`oneOf` with const values into enum arrays
|
||||
- Fixed const-to-enum conversion to infer type from the const value when type is not specified
|
||||
|
||||
## [4.4.6] - 2026-01-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed tool parameter schema sanitization to only apply Google-specific transformations for Gemini models, preserving original schemas for other model types
|
||||
|
||||
## [4.4.5] - 2026-01-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Exported `sanitizeSchemaForGoogle` utility function for external use
|
||||
@@ -375,13 +406,14 @@
|
||||
|
||||
- Changed Cursor debug logging to use structured JSONL format with automatic MCP argument decoding
|
||||
- Changed MCP tool argument decoding to use protobuf Value schema for improved type handling
|
||||
- Changed tool advertisement to filter Cursor native tools (bash, read, write, delete, ls, grep, lsp) instead of only exposing mcp_ prefixed tools
|
||||
- Changed tool advertisement to filter Cursor native tools (bash, read, write, delete, ls, grep, lsp) instead of only exposing mcp\_ prefixed tools
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Cursor conversation history serialization so subagents retain task context and can call complete
|
||||
|
||||
## [4.2.1] - 2026-01-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated `reasoningSummary` option to accept only `"auto"`, `"concise"`, `"detailed"`, or `null` (removed `"off"` and `"on"` values)
|
||||
@@ -393,6 +425,7 @@
|
||||
- Fixed Cloud Code Assist tool schema conversion to avoid unsupported `const` fields
|
||||
|
||||
## [4.0.0] - 2026-01-10
|
||||
|
||||
### Added
|
||||
|
||||
- Added `betas` option in `AnthropicOptions` for passing custom Anthropic beta feature flags
|
||||
@@ -434,6 +467,7 @@
|
||||
- Crash when pasting text with trailing whitespace exceeding terminal width
|
||||
|
||||
## [3.37.1] - 2026-01-10
|
||||
|
||||
### Added
|
||||
|
||||
- Added automatic type coercion for tool arguments when LLMs return JSON-encoded strings instead of native types (numbers, booleans, arrays, objects)
|
||||
@@ -444,11 +478,13 @@
|
||||
- Changed validation error messages to include both original and normalized arguments when coercion was attempted
|
||||
|
||||
## [3.37.0] - 2026-01-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Enabled type coercion in JSON schema validation to automatically convert compatible types
|
||||
|
||||
## [3.35.0] - 2026-01-09
|
||||
|
||||
### Added
|
||||
|
||||
- Enhanced error messages to include retry-after timing information from API rate limit headers
|
||||
@@ -729,4 +765,4 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
|
||||
|
||||
## [0.9.4] - 2025-11-26
|
||||
|
||||
Initial release with multi-provider LLM support.
|
||||
Initial release with multi-provider LLM support.
|
||||
|
||||
@@ -105,7 +105,7 @@ const serviceProviderMap: Record<string, KeyResolver> = {
|
||||
* Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.
|
||||
*
|
||||
* Will not return API keys for providers that require OAuth tokens.
|
||||
* Checks process.env, then cwd/.env, then ~/.env.
|
||||
* Checks Bun.env, then cwd/.env, then ~/.env.
|
||||
*/
|
||||
export function getEnvApiKey(provider: string): string | undefined {
|
||||
const resolver = serviceProviderMap[provider];
|
||||
|
||||
@@ -100,7 +100,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("Anthropic (API Key)", () => {
|
||||
it("claude-3-5-haiku - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
const result = await testContextOverflow(model, process.env.ANTHROPIC_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.ANTHROPIC_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -167,7 +167,7 @@ describe("Context overflow error handling", () => {
|
||||
it("gpt-4o-mini - should detect overflow via isContextOverflow", async () => {
|
||||
const model = { ...getModel("openai", "gpt-4o-mini") };
|
||||
model.api = "openai-completions" as any;
|
||||
const result = await testContextOverflow(model, process.env.OPENAI_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENAI_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -179,7 +179,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("OPENAI_API_KEY"))("OpenAI Responses", () => {
|
||||
it("gpt-4o - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openai", "gpt-4o");
|
||||
const result = await testContextOverflow(model, process.env.OPENAI_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENAI_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -196,7 +196,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("GEMINI_API_KEY"))("Google", () => {
|
||||
it("gemini-2.0-flash - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("google", "gemini-2.0-flash");
|
||||
const result = await testContextOverflow(model, process.env.GEMINI_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.GEMINI_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -292,7 +292,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("XAI_API_KEY"))("xAI", () => {
|
||||
it("grok-3-fast - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("xai", "grok-3-fast");
|
||||
const result = await testContextOverflow(model, process.env.XAI_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.XAI_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -309,7 +309,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("GROQ_API_KEY"))("Groq", () => {
|
||||
it("llama-3.3-70b-versatile - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("groq", "llama-3.3-70b-versatile");
|
||||
const result = await testContextOverflow(model, process.env.GROQ_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.GROQ_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -326,7 +326,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("CEREBRAS_API_KEY"))("Cerebras", () => {
|
||||
it("qwen-3-235b - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("cerebras", "qwen-3-235b-a22b-instruct-2507");
|
||||
const result = await testContextOverflow(model, process.env.CEREBRAS_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.CEREBRAS_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -345,7 +345,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("ZAI_API_KEY"))("z.ai", () => {
|
||||
it("glm-4.5-flash - should detect overflow via isContextOverflow (silent overflow or rate limit)", async () => {
|
||||
const model = getModel("zai", "glm-4.5-flash");
|
||||
const result = await testContextOverflow(model, process.env.ZAI_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.ZAI_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
// z.ai behavior is inconsistent:
|
||||
@@ -371,7 +371,7 @@ describe("Context overflow error handling", () => {
|
||||
describe.skipIf(!e2eApiKey("MISTRAL_API_KEY"))("Mistral", () => {
|
||||
it("devstral-medium-latest - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("mistral", "devstral-medium-latest");
|
||||
const result = await testContextOverflow(model, process.env.MISTRAL_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.MISTRAL_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -388,7 +388,7 @@ describe("Context overflow error handling", () => {
|
||||
// Anthropic backend
|
||||
it("anthropic/claude-sonnet-4 via OpenRouter - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openrouter", "anthropic/claude-sonnet-4");
|
||||
const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENROUTER_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -399,7 +399,7 @@ describe("Context overflow error handling", () => {
|
||||
// DeepSeek backend
|
||||
it("deepseek/deepseek-v3.2 via OpenRouter - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openrouter", "deepseek/deepseek-v3.2");
|
||||
const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENROUTER_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -410,7 +410,7 @@ describe("Context overflow error handling", () => {
|
||||
// Mistral backend
|
||||
it("mistralai/mistral-large-2512 via OpenRouter - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openrouter", "mistralai/mistral-large-2512");
|
||||
const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENROUTER_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -421,7 +421,7 @@ describe("Context overflow error handling", () => {
|
||||
// Google backend
|
||||
it("google/gemini-2.5-flash via OpenRouter - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openrouter", "google/gemini-2.5-flash");
|
||||
const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENROUTER_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -432,7 +432,7 @@ describe("Context overflow error handling", () => {
|
||||
// Meta/Llama backend
|
||||
it("meta-llama/llama-4-maverick via OpenRouter - should detect overflow via isContextOverflow", async () => {
|
||||
const model = getModel("openrouter", "meta-llama/llama-4-maverick");
|
||||
const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!);
|
||||
const result = await testContextOverflow(model, Bun.env.OPENROUTER_API_KEY!);
|
||||
logResult(result);
|
||||
|
||||
expect(result.stopReason).toBe("error");
|
||||
@@ -447,7 +447,7 @@ describe("Context overflow error handling", () => {
|
||||
|
||||
// Check if ollama is installed and local LLM tests are enabled
|
||||
let ollamaInstalled = false;
|
||||
if (!process.env.PI_NO_LOCAL_LLM) {
|
||||
if (!Bun.env.PI_NO_LOCAL_LLM) {
|
||||
try {
|
||||
execSync("which ollama", { stdio: "ignore" });
|
||||
ollamaInstalled = true;
|
||||
@@ -541,7 +541,7 @@ describe("Context overflow error handling", () => {
|
||||
// =============================================================================
|
||||
|
||||
let lmStudioRunning = false;
|
||||
if (!process.env.PI_NO_LOCAL_LLM) {
|
||||
if (!Bun.env.PI_NO_LOCAL_LLM) {
|
||||
try {
|
||||
execSync("curl -s --max-time 1 http://localhost:1234/v1/models > /dev/null", { stdio: "ignore" });
|
||||
lmStudioRunning = true;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
* E2E tests require explicit opt-in via E2E=1 environment variable.
|
||||
* This prevents accidental API calls when keys happen to be in the environment.
|
||||
*/
|
||||
export const E2E_ENABLED = process.env.E2E === "1" || process.env.E2E === "true";
|
||||
export const E2E_ENABLED = Bun.env.E2E === "1" || Bun.env.E2E === "true";
|
||||
|
||||
/**
|
||||
* Get an API key from environment, but only if E2E tests are enabled.
|
||||
@@ -25,7 +25,7 @@ export const E2E_ENABLED = process.env.E2E === "1" || process.env.E2E === "true"
|
||||
*/
|
||||
export function e2eApiKey(envVar: string): string | undefined {
|
||||
if (!E2E_ENABLED) return undefined;
|
||||
return process.env[envVar];
|
||||
return Bun.env[envVar];
|
||||
}
|
||||
|
||||
const AUTH_PATH = path.join(os.homedir(), ".pi", "agent", "auth.json");
|
||||
|
||||
@@ -4,14 +4,14 @@ import type { Context, Model } from "@oh-my-pi/pi-ai/types";
|
||||
import { TempDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
const originalAgentDir = Bun.env.PI_CODING_AGENT_DIR;
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
if (originalAgentDir === undefined) {
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
delete Bun.env.PI_CODING_AGENT_DIR;
|
||||
} else {
|
||||
process.env.PI_CODING_AGENT_DIR = originalAgentDir;
|
||||
Bun.env.PI_CODING_AGENT_DIR = originalAgentDir;
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
@@ -19,7 +19,7 @@ afterEach(() => {
|
||||
describe("openai-codex streaming", () => {
|
||||
it("streams SSE responses into AssistantMessageEventStream", async () => {
|
||||
const tempDir = TempDir.createSync("@pi-codex-stream-");
|
||||
process.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
|
||||
@@ -130,7 +130,7 @@ describe("openai-codex streaming", () => {
|
||||
|
||||
it("sets conversation_id/session_id headers and prompt_cache_key when sessionId is provided", async () => {
|
||||
const tempDir = TempDir.createSync("@pi-codex-stream-");
|
||||
process.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
|
||||
@@ -231,7 +231,7 @@ describe("openai-codex streaming", () => {
|
||||
|
||||
it("does not set conversation_id/session_id headers when sessionId is not provided", async () => {
|
||||
const tempDir = TempDir.createSync("@pi-codex-stream-");
|
||||
process.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
|
||||
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
|
||||
|
||||
@@ -392,8 +392,8 @@ describe("Generate E2E Tests", () => {
|
||||
});
|
||||
|
||||
describe("Google Vertex Provider (gemini-3-flash-preview)", () => {
|
||||
const vertexProject = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
||||
const vertexLocation = process.env.GOOGLE_CLOUD_LOCATION;
|
||||
const vertexProject = Bun.env.GOOGLE_CLOUD_PROJECT || Bun.env.GCLOUD_PROJECT;
|
||||
const vertexLocation = Bun.env.GOOGLE_CLOUD_LOCATION;
|
||||
const isVertexConfigured = Boolean(vertexProject && vertexLocation);
|
||||
const vertexOptions = { project: vertexProject, location: vertexLocation } as const;
|
||||
const llm = getModel("google-vertex", "gemini-3-flash-preview");
|
||||
@@ -1350,7 +1350,7 @@ describe("Generate E2E Tests", () => {
|
||||
|
||||
// Check if ollama is installed and local LLM tests are enabled
|
||||
let ollamaInstalled = false;
|
||||
if (!process.env.PI_NO_LOCAL_LLM) {
|
||||
if (!Bun.env.PI_NO_LOCAL_LLM) {
|
||||
try {
|
||||
execSync("which ollama", { stdio: "ignore" });
|
||||
ollamaInstalled = true;
|
||||
|
||||
@@ -106,7 +106,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("anthropic", "claude-3-5-haiku-20241022");
|
||||
|
||||
console.log(`\nAnthropic / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.ANTHROPIC_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.ANTHROPIC_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -224,7 +224,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("xai", "grok-3-fast");
|
||||
|
||||
console.log(`\nxAI / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XAI_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.XAI_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -247,7 +247,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("groq", "openai/gpt-oss-120b");
|
||||
|
||||
console.log(`\nGroq / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.GROQ_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.GROQ_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -270,7 +270,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("cerebras", "gpt-oss-120b");
|
||||
|
||||
console.log(`\nCerebras / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.CEREBRAS_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.CEREBRAS_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -293,7 +293,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("zai", "glm-4.5-flash");
|
||||
|
||||
console.log(`\nz.ai / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.ZAI_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.ZAI_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -316,7 +316,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("mistral", "devstral-medium-latest");
|
||||
|
||||
console.log(`\nMistral / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.MISTRAL_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.MISTRAL_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -339,7 +339,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("openrouter", "anthropic/claude-sonnet-4");
|
||||
|
||||
console.log(`\nOpenRouter / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.OPENROUTER_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -356,7 +356,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("openrouter", "deepseek/deepseek-chat");
|
||||
|
||||
console.log(`\nOpenRouter / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.OPENROUTER_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -373,7 +373,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("openrouter", "mistralai/mistral-small-3.1-24b-instruct");
|
||||
|
||||
console.log(`\nOpenRouter / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.OPENROUTER_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -390,7 +390,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("openrouter", "google/gemini-2.0-flash-001");
|
||||
|
||||
console.log(`\nOpenRouter / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.OPENROUTER_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
@@ -407,7 +407,7 @@ describe("totalTokens field", () => {
|
||||
const llm = getModel("openrouter", "meta-llama/llama-4-maverick");
|
||||
|
||||
console.log(`\nOpenRouter / ${llm.id}:`);
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
|
||||
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: Bun.env.OPENROUTER_API_KEY });
|
||||
|
||||
logUsage("First request", first);
|
||||
logUsage("Second request", second);
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
|
||||
- Added comprehensive environment variables reference documentation at `docs/environment-variables.md` covering API keys, configuration, debugging, and testing variables
|
||||
- Added theme system with 44 customizable color tokens, two built-in themes (dark/light), and auto-detection based on terminal background
|
||||
- Added `/theme` command to interactively select and switch between themes
|
||||
- Added support for custom themes in `~/.pi/agent/themes/*.json` with live editing - changes apply immediately when files are saved
|
||||
- Added `userMessageText` theme token for customizing user message text color
|
||||
- Added `toolTitle` and `toolOutput` theme tokens for separate coloring of tool execution box titles and output
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed all environment variable access from `process.env` to `Bun.env` throughout the codebase for Bun runtime compatibility
|
||||
- Updated documentation to reference `Bun.env` instead of `process.env` in examples and comments
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `Text` component to properly implement `invalidate()` method, ensuring theme changes apply correctly to all UI elements
|
||||
- Fixed `TruncatedText` component to properly pad all lines to exactly match the specified width, preventing rendering artifacts
|
||||
- Fixed `TruncatedText` component to stop at the first newline and only display the first line
|
||||
- Fixed invalid or malformed themes to fall back gracefully to dark theme instead of crashing the application
|
||||
|
||||
## [11.0.2] - 2026-02-05
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ When disabled, neither case triggers automatic compaction (use `/compact` manual
|
||||
**Environment Variables (`env`):**
|
||||
|
||||
- Automatically sets environment variables when the application starts
|
||||
- Only sets variables that aren't already present in `process.env`
|
||||
- Only sets variables that aren't already present in `Bun.env`
|
||||
- Supports any environment variable, not just API keys
|
||||
- Order of precedence: existing env vars > settings.json env vars > auth.json env vars
|
||||
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
# Environment Variables Reference
|
||||
|
||||
This document lists all environment variables used by the coding agent.
|
||||
|
||||
## API Keys
|
||||
|
||||
### Multi-Provider Support
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `OPENAI_API_KEY` | OpenAI API key for GPT models | - |
|
||||
| `ANTHROPIC_API_KEY` | Anthropic API key for Claude models | - |
|
||||
| `ANTHROPIC_OAUTH_TOKEN` | Anthropic OAuth token (takes precedence over `ANTHROPIC_API_KEY`) | - |
|
||||
| `GOOGLE_API_KEY` | Google Gemini API key | - |
|
||||
| `COPILOT_GITHUB_TOKEN` | GitHub Copilot personal access token | `$GH_TOKEN` or `$GITHUB_TOKEN` |
|
||||
| `GH_TOKEN` | GitHub CLI token (fallback for Copilot) | - |
|
||||
| `GITHUB_TOKEN` | GitHub token (fallback for Copilot and API access) | - |
|
||||
| `EXA_API_KEY` | Exa search API key | - |
|
||||
|
||||
### Provider-Specific Configuration
|
||||
|
||||
#### AWS Bedrock
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `AWS_REGION` | AWS region for Bedrock | `$AWS_DEFAULT_REGION` or `us-east-1` |
|
||||
| `AWS_DEFAULT_REGION` | AWS default region | - |
|
||||
| `AWS_PROFILE` | AWS CLI profile name | - |
|
||||
| `AWS_ACCESS_KEY_ID` | AWS access key ID | - |
|
||||
| `AWS_SECRET_ACCESS_KEY` | AWS secret access key | - |
|
||||
| `AWS_BEARER_TOKEN_BEDROCK` | AWS bearer token for Bedrock | - |
|
||||
| `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` | ECS container credentials URI | - |
|
||||
| `AWS_CONTAINER_CREDENTIALS_FULL_URI` | ECS container credentials full URI | - |
|
||||
| `AWS_WEB_IDENTITY_TOKEN_FILE` | Web identity token file path | - |
|
||||
| `AWS_ROLE_ARN` | IAM role ARN for web identity | - |
|
||||
|
||||
#### Azure OpenAI
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key (required) | - |
|
||||
| `AZURE_OPENAI_API_VERSION` | Azure OpenAI API version | `2024-10-01-preview` |
|
||||
| `AZURE_OPENAI_BASE_URL` | Azure OpenAI base URL | Constructed from resource name |
|
||||
| `AZURE_OPENAI_RESOURCE_NAME` | Azure OpenAI resource name | - |
|
||||
| `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | JSON map of model IDs to deployment names | `{}` |
|
||||
|
||||
#### Google Cloud (Vertex AI)
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `GOOGLE_CLOUD_PROJECT` | Google Cloud project ID (required for Vertex AI) | `$GCLOUD_PROJECT` |
|
||||
| `GCLOUD_PROJECT` | Google Cloud project ID (alternative) | - |
|
||||
| `GOOGLE_CLOUD_PROJECT_ID` | Google Cloud project ID (used during OAuth discovery) | `$GOOGLE_CLOUD_PROJECT` |
|
||||
| `GOOGLE_CLOUD_LOCATION` | Google Cloud location (required for Vertex AI) | - |
|
||||
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to Google service account JSON | - |
|
||||
|
||||
#### Anthropic Search & Custom Base URLs
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `ANTHROPIC_BASE_URL` | Custom Anthropic API base URL | - |
|
||||
| `ANTHROPIC_SEARCH_API_KEY` | API key for Anthropic search (separate from main API) | - |
|
||||
| `ANTHROPIC_SEARCH_BASE_URL` | Custom base URL for Anthropic search | - |
|
||||
| `ANTHROPIC_SEARCH_MODEL` | Model to use for web search | `claude-sonnet-4` |
|
||||
|
||||
#### Kimi
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `KIMI_CODE_BASE_URL` | Kimi Code API base URL | `https://kimi.moonshot.cn` |
|
||||
| `KIMI_CODE_OAUTH_HOST` | Kimi Code OAuth host | `$KIMI_OAUTH_HOST` or `https://kimi.moonshot.cn` |
|
||||
| `KIMI_OAUTH_HOST` | Kimi OAuth host (fallback) | - |
|
||||
|
||||
## Model Configuration
|
||||
|
||||
### Model Role Overrides
|
||||
|
||||
Override model roles via environment variables (ephemeral, not persisted):
|
||||
|
||||
| Variable | Description | CLI Flag |
|
||||
|----------|-------------|----------|
|
||||
| `PI_SMOL_MODEL` | Fast model for lightweight tasks | `--smol` |
|
||||
| `PI_SLOW_MODEL` | Reasoning model for thorough analysis | `--slow` |
|
||||
| `PI_PLAN_MODEL` | Model for architectural planning | `--plan` |
|
||||
|
||||
## Agent Configuration
|
||||
|
||||
### Core Settings
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_CODING_AGENT_DIR` | Directory for agent data (sessions, auth, cache) | `~/.omp/agent` |
|
||||
| `PI_SUBPROCESS_CMD` | Custom command for spawning subagents | Auto-detected |
|
||||
| `PI_NO_TITLE` | Disable automatic session title generation | `false` |
|
||||
| `NULL_PROMPT` | Use empty system prompt (testing) | `false` |
|
||||
| `PI_BLOCKED_AGENT` | Override agent type in task tool | - |
|
||||
|
||||
### Caching
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_CACHE_RETENTION` | Prompt cache retention (`long` = 24h for OpenAI) | - |
|
||||
|
||||
## Python Configuration
|
||||
|
||||
### Python Kernel
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `VIRTUAL_ENV` | Python virtual environment path | Auto-detected (`.venv` or `venv`) |
|
||||
| `PI_PY` | Python tool mode (`per-session`, `per-call`, `off`) | `per-session` |
|
||||
| `PI_PYTHON_SKIP_CHECK` | Skip Python availability check (testing) | `false` |
|
||||
|
||||
### External Python Gateway
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_PYTHON_GATEWAY_URL` | External Python gateway URL | - |
|
||||
| `PI_PYTHON_GATEWAY_TOKEN` | Authentication token for gateway | - |
|
||||
|
||||
### Debugging
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_PYTHON_IPC_TRACE` | Trace Python IPC messages (`1` = enabled) | `false` |
|
||||
|
||||
## Task & Subagent Configuration
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_TASK_MAX_OUTPUT_BYTES` | Maximum output bytes per subagent | `500000` |
|
||||
| `PI_TASK_MAX_OUTPUT_LINES` | Maximum output lines per subagent | `5000` |
|
||||
|
||||
## TUI & Terminal Configuration
|
||||
|
||||
### Terminal Capabilities
|
||||
|
||||
| Variable | Description | Auto-Detected |
|
||||
|----------|-------------|---------------|
|
||||
| `COLORTERM` | Terminal color support (`truecolor`, `24bit`) | Yes |
|
||||
| `COLORFGBG` | Terminal foreground/background colors | Yes |
|
||||
| `TERM` | Terminal type | Yes |
|
||||
| `TERM_PROGRAM` | Terminal program name | Yes |
|
||||
| `TERM_PROGRAM_VERSION` | Terminal program version | Yes |
|
||||
| `TERMINAL_EMULATOR` | Terminal emulator name | Yes |
|
||||
| `WT_SESSION` | Windows Terminal session ID | Yes |
|
||||
|
||||
### TUI Behavior
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_NOTIFICATIONS` | Desktop notifications (`off`, `0`, `false` = disabled) | Enabled |
|
||||
| `PI_TUI_WRITE_LOG` | Log all TUI write operations to file | - |
|
||||
| `PI_HARDWARE_CURSOR` | Show hardware cursor (`1` = enabled) | `false` |
|
||||
|
||||
## Bash & Shell Configuration
|
||||
|
||||
### Shell Detection
|
||||
|
||||
| Variable | Description | Auto-Detected |
|
||||
|----------|-------------|---------------|
|
||||
| `SHELL` | User's default shell | Yes (Unix) |
|
||||
| `ComSpec` | Command processor | Yes (Windows) |
|
||||
|
||||
### Editor
|
||||
|
||||
| Variable | Description | Fallback |
|
||||
|----------|-------------|----------|
|
||||
| `VISUAL` | Visual editor for external editing (Ctrl+G) | `$EDITOR` |
|
||||
| `EDITOR` | Default text editor | - |
|
||||
|
||||
### Bash Tool Behavior
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_BASH_NO_CI` | Don't set `CI=true` in bash environment | `$CLAUDE_BASH_NO_CI` |
|
||||
| `CLAUDE_BASH_NO_CI` | Legacy name for `PI_BASH_NO_CI` | - |
|
||||
| `PI_BASH_NO_LOGIN` | Don't use login shell for bash | `$CLAUDE_BASH_NO_LOGIN` |
|
||||
| `CLAUDE_BASH_NO_LOGIN` | Legacy name for `PI_BASH_NO_LOGIN` | - |
|
||||
| `PI_SHELL_PREFIX` | Prefix for bash commands | `$CLAUDE_CODE_SHELL_PREFIX` |
|
||||
| `CLAUDE_CODE_SHELL_PREFIX` | Legacy name for `PI_SHELL_PREFIX` | - |
|
||||
|
||||
## Desktop Environment Detection
|
||||
|
||||
These are auto-detected for system prompt context:
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `KDE_FULL_SESSION` | Detect KDE desktop |
|
||||
| `XDG_CURRENT_DESKTOP` | Current desktop environment |
|
||||
| `DESKTOP_SESSION` | Desktop session name |
|
||||
| `XDG_SESSION_DESKTOP` | XDG desktop session |
|
||||
| `GDMSESSION` | GDM session type |
|
||||
| `WINDOWMANAGER` | Window manager name |
|
||||
| `XDG_CONFIG_HOME` | User config directory |
|
||||
| `APPDATA` | Windows app data directory |
|
||||
| `HOME` | User home directory |
|
||||
|
||||
## LSP Configuration
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_DISABLE_LSPMUX` | Disable lspmux integration (`1` = disabled) | `false` |
|
||||
|
||||
## Debugging & Development
|
||||
|
||||
### General Debugging
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `DEBUG` | Enable debug logging | `false` |
|
||||
| `PI_DEV` | Development mode (verbose native addon loading) | `false` |
|
||||
| `PI_TIMING` | Log tool factory and operation timings (`1` = enabled) | `false` |
|
||||
|
||||
### Startup Debugging
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_DEBUG_STARTUP` | Print startup stage timings to stderr | `false` |
|
||||
|
||||
### Provider-Specific Debugging
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `DEBUG_CURSOR` | Cursor provider debug logging (`1` = basic, `2` or `verbose` = detailed) | `false` |
|
||||
| `DEBUG_CURSOR_LOG` | Path to write Cursor debug log file | - |
|
||||
| `PI_CODEX_DEBUG` | OpenAI Codex debug logging (`1` or `true` = enabled) | `false` |
|
||||
|
||||
## Commit Message Generation
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PI_COMMIT_TEST_FALLBACK` | Force fallback commit generation (testing) | `false` |
|
||||
| `PI_COMMIT_NO_FALLBACK` | Disable fallback commit generation | `false` |
|
||||
| `PI_COMMIT_MAP_REDUCE` | Enable/disable map-reduce for large diffs (`false` = disabled) | Enabled |
|
||||
|
||||
## Testing & CI
|
||||
|
||||
These are auto-detected but documented for completeness:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `BUN_ENV` | Bun environment (`test` skips certain checks) |
|
||||
| `NODE_ENV` | Node environment (`test` skips certain checks) |
|
||||
| `E2E` | Enable end-to-end tests (`1` or `true`) |
|
||||
| `PI_NO_LOCAL_LLM` | Skip local LLM tests (Ollama, LM Studio) |
|
||||
@@ -87,7 +87,7 @@ interface AgentSession {
|
||||
prompt(text: string, options?: PromptOptions): Promise<void>;
|
||||
sendUserMessage(
|
||||
content: string | (TextContent | ImageContent)[],
|
||||
options?: { deliverAs?: "steer" | "followUp" },
|
||||
options?: { deliverAs?: "steer" | "followUp" }
|
||||
): Promise<void>;
|
||||
steer(text: string): void;
|
||||
followUp(text: string): void;
|
||||
@@ -106,7 +106,7 @@ interface AgentSession {
|
||||
setThinkingLevel(level: ThinkingLevel): void;
|
||||
cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
|
||||
cycleRoleModels(
|
||||
direction?: "forward" | "backward",
|
||||
direction?: "forward" | "backward"
|
||||
): Promise<{ model: Model; thinkingLevel: ThinkingLevel; role: ModelRole } | undefined>;
|
||||
cycleThinkingLevel(): ThinkingLevel | undefined;
|
||||
|
||||
@@ -141,7 +141,7 @@ interface AgentSession {
|
||||
// Compaction
|
||||
compact(
|
||||
customInstructions?: string,
|
||||
options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void },
|
||||
options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void }
|
||||
): Promise<CompactionResult>;
|
||||
abortCompaction(): void;
|
||||
|
||||
@@ -491,11 +491,7 @@ const { session } = await createAgentSession({
|
||||
Extensions intercept agent events and can register custom tools/commands. Hooks remain for legacy compatibility.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createAgentSession,
|
||||
discoverExtensions,
|
||||
type ExtensionFactory,
|
||||
} from "@oh-my-pi/pi-coding-agent";
|
||||
import { createAgentSession, discoverExtensions, type ExtensionFactory } from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
// Inline extension
|
||||
const loggingExtension: ExtensionFactory = (api) => {
|
||||
@@ -878,8 +874,8 @@ import systemPrompt from "./SYSTEM.md" with { type: "text" };
|
||||
const authStorage = await discoverAuthStorage();
|
||||
|
||||
// Runtime API key override (not persisted)
|
||||
if (process.env.MY_KEY) {
|
||||
authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);
|
||||
if (Bun.env.MY_KEY) {
|
||||
authStorage.setRuntimeApiKey("anthropic", Bun.env.MY_KEY);
|
||||
}
|
||||
|
||||
// Model registry
|
||||
|
||||
@@ -78,7 +78,7 @@ const { session } = await createAgentSession({
|
||||
|
||||
// Full control
|
||||
const customAuth = new AuthStorage("/my/app/auth.json");
|
||||
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
||||
customAuth.setRuntimeApiKey("anthropic", Bun.env.MY_KEY!);
|
||||
const customRegistry = new ModelRegistry(customAuth);
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
|
||||
@@ -150,7 +150,7 @@ function emptyCustomModelsResult(error?: string): CustomModelsResult {
|
||||
* Checks environment variable first, then treats as literal.
|
||||
*/
|
||||
function resolveApiKeyConfig(keyConfig: string): string | undefined {
|
||||
const envValue = process.env[keyConfig];
|
||||
const envValue = Bun.env[keyConfig];
|
||||
if (envValue) return envValue;
|
||||
return keyConfig;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function createReportBundle(options: ReportBundleOptions): Promise<
|
||||
files.push("system.json");
|
||||
|
||||
// Sanitized environment
|
||||
data["env.json"] = JSON.stringify(sanitizeEnv(process.env as Record<string, string>), null, 2);
|
||||
data["env.json"] = JSON.stringify(sanitizeEnv(Bun.env as Record<string, string>), null, 2);
|
||||
files.push("env.json");
|
||||
|
||||
// Settings/config
|
||||
|
||||
@@ -28,8 +28,8 @@ export async function collectSystemInfo(): Promise<SystemInfo> {
|
||||
const cpuModel = cpus[0]?.model ?? "Unknown CPU";
|
||||
|
||||
// Try to get shell from environment
|
||||
const shell = process.env.SHELL ?? process.env.ComSpec ?? "unknown";
|
||||
const terminal = process.env.TERM_PROGRAM ?? process.env.TERM ?? undefined;
|
||||
const shell = Bun.env.SHELL ?? Bun.env.ComSpec ?? "unknown";
|
||||
const terminal = Bun.env.TERM_PROGRAM ?? Bun.env.TERM ?? undefined;
|
||||
|
||||
return {
|
||||
os: `${os.type()} ${os.release()} (${os.platform()})`,
|
||||
|
||||
@@ -160,7 +160,7 @@ function extractMCPServersFromToml(toml: Record<string, unknown>): Record<string
|
||||
const env: Record<string, string> = { ...config.env };
|
||||
if (config.env_vars) {
|
||||
for (const varName of config.env_vars) {
|
||||
const value = process.env[varName];
|
||||
const value = Bun.env[varName];
|
||||
if (value !== undefined) {
|
||||
env[varName] = value;
|
||||
}
|
||||
@@ -174,14 +174,14 @@ function extractMCPServersFromToml(toml: Record<string, unknown>): Record<string
|
||||
const headers: Record<string, string> = { ...config.http_headers };
|
||||
if (config.env_http_headers) {
|
||||
for (const [headerName, envVarName] of Object.entries(config.env_http_headers)) {
|
||||
const value = process.env[envVarName];
|
||||
const value = Bun.env[envVarName];
|
||||
if (value !== undefined) {
|
||||
headers[headerName] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.bearer_token_env_var) {
|
||||
const token = process.env[config.bearer_token_env_var];
|
||||
const token = Bun.env[config.bearer_token_env_var];
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ export async function loadSkillsFromDir(
|
||||
*/
|
||||
export function expandEnvVars(value: string, extraEnv?: Record<string, string>): string {
|
||||
return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_, varName: string, defaultValue?: string) => {
|
||||
const envValue = extraEnv?.[varName] ?? process.env[varName];
|
||||
const envValue = extraEnv?.[varName] ?? Bun.env[varName];
|
||||
if (envValue !== undefined) return envValue;
|
||||
if (defaultValue !== undefined) return defaultValue;
|
||||
return `\${${varName}}`;
|
||||
|
||||
@@ -18,9 +18,9 @@ import type {
|
||||
MCPToolWrapperConfig,
|
||||
} from "./types";
|
||||
|
||||
/** Find EXA_API_KEY from process.env or .env files */
|
||||
/** Find EXA_API_KEY from Bun.env or .env files */
|
||||
export async function findApiKey(): Promise<string | null> {
|
||||
// Check process.env first
|
||||
// Check Bun.env first
|
||||
if ($env.EXA_API_KEY) {
|
||||
return $env.EXA_API_KEY;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function runDoctorChecks(): Promise<DoctorCheck[]> {
|
||||
];
|
||||
|
||||
for (const key of apiKeys) {
|
||||
const hasKey = !!process.env[key.name];
|
||||
const hasKey = !!Bun.env[key.name];
|
||||
checks.push({
|
||||
name: key.name,
|
||||
status: hasKey ? "ok" : "warning",
|
||||
|
||||
@@ -225,7 +225,7 @@ export async function warmPythonEnvironment(
|
||||
useSharedGateway?: boolean,
|
||||
sessionFile?: string,
|
||||
): Promise<{ ok: boolean; reason?: string; docs: PreludeHelper[] }> {
|
||||
const isTestEnv = process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test";
|
||||
const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
|
||||
let cacheState: PreludeCacheState | null = null;
|
||||
try {
|
||||
debugStartup("warmPython:ensureKernel:start");
|
||||
|
||||
@@ -108,7 +108,7 @@ export interface PythonKernelAvailability {
|
||||
}
|
||||
|
||||
export async function checkPythonKernelAvailability(cwd: string): Promise<PythonKernelAvailability> {
|
||||
if (process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test" || $env.PI_PYTHON_SKIP_CHECK === "1") {
|
||||
if (Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test" || $env.PI_PYTHON_SKIP_CHECK === "1") {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -412,7 +412,7 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
|
||||
const proc = ptree.spawn([command, ...args], {
|
||||
cwd,
|
||||
stdin: "pipe",
|
||||
env: env ? { ...process.env, ...env } : undefined,
|
||||
env: env ? { ...Bun.env, ...env } : undefined,
|
||||
});
|
||||
|
||||
const client: LspClient = {
|
||||
|
||||
@@ -66,11 +66,11 @@ function getConfigPath(): string {
|
||||
const home = os.homedir();
|
||||
switch (os.platform()) {
|
||||
case "win32":
|
||||
return path.join(process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "lspmux", "config.toml");
|
||||
return path.join(Bun.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "lspmux", "config.toml");
|
||||
case "darwin":
|
||||
return path.join(home, "Library", "Application Support", "lspmux", "config.toml");
|
||||
default:
|
||||
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "lspmux", "config.toml");
|
||||
return path.join(Bun.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "lspmux", "config.toml");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export class StdioTransport implements MCPTransport {
|
||||
|
||||
const args = this.config.args ?? [];
|
||||
const env = {
|
||||
...process.env,
|
||||
...Bun.env,
|
||||
...this.config.env,
|
||||
};
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export class RpcClient {
|
||||
|
||||
this.process = ptree.spawn(["bun", cliPath, ...args], {
|
||||
cwd: this.options.cwd,
|
||||
env: { ...process.env, ...this.options.env },
|
||||
env: { ...Bun.env, ...this.options.env },
|
||||
stdin: "pipe",
|
||||
});
|
||||
|
||||
|
||||
@@ -1053,15 +1053,15 @@ type ColorMode = "truecolor" | "256color";
|
||||
// ============================================================================
|
||||
|
||||
function detectColorMode(): ColorMode {
|
||||
const colorterm = process.env.COLORTERM;
|
||||
const colorterm = Bun.env.COLORTERM;
|
||||
if (colorterm === "truecolor" || colorterm === "24bit") {
|
||||
return "truecolor";
|
||||
}
|
||||
// Windows Terminal supports truecolor
|
||||
if (process.env.WT_SESSION) {
|
||||
if (Bun.env.WT_SESSION) {
|
||||
return "truecolor";
|
||||
}
|
||||
const term = process.env.TERM || "";
|
||||
const term = Bun.env.TERM || "";
|
||||
// Only fall back to 256color for truly limited terminals
|
||||
if (term === "dumb" || term === "" || term === "linux") {
|
||||
return "256color";
|
||||
@@ -1706,7 +1706,7 @@ export async function getThemeByName(name: string): Promise<Theme | undefined> {
|
||||
}
|
||||
|
||||
function detectTerminalBackground(): "dark" | "light" {
|
||||
const colorfgbg = process.env.COLORFGBG || "";
|
||||
const colorfgbg = Bun.env.COLORFGBG || "";
|
||||
if (colorfgbg) {
|
||||
const parts = colorfgbg.split(";");
|
||||
if (parts.length >= 2) {
|
||||
|
||||
@@ -218,7 +218,7 @@ export class EditTool implements AgentTool<TInput> {
|
||||
PI_EDIT_FUZZY: editFuzzy = "auto",
|
||||
PI_EDIT_FUZZY_THRESHOLD: editFuzzyThreshold = "auto",
|
||||
PI_EDIT_VARIANT: envEditVariant = "auto",
|
||||
} = process.env;
|
||||
} = Bun.env;
|
||||
this.envEditVariant = envEditVariant;
|
||||
|
||||
if (envEditVariant !== "replace" && envEditVariant !== "patch" && envEditVariant !== "auto") {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* // Full control
|
||||
* const session = await createAgentSession({
|
||||
* model: myModel,
|
||||
* getApiKey: async () => process.env.MY_KEY,
|
||||
* getApiKey: async () => Bun.env.MY_KEY,
|
||||
* toolNames: ["read", "bash", "edit", "write"], // Filter tools
|
||||
* extensions: [],
|
||||
* skills: [],
|
||||
@@ -566,7 +566,7 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
|
||||
* // Full control
|
||||
* const { session } = await createAgentSession({
|
||||
* model: myModel,
|
||||
* getApiKey: async () => process.env.MY_KEY,
|
||||
* getApiKey: async () => Bun.env.MY_KEY,
|
||||
* systemPrompt: 'You are helpful.',
|
||||
* tools: codingTools({ cwd: process.cwd() }),
|
||||
* skills: [],
|
||||
@@ -814,7 +814,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
|
||||
// If we extracted Exa API keys from MCP configs and EXA_API_KEY isn't set, use the first one
|
||||
if (mcpResult.exaApiKeys.length > 0 && !$env.EXA_API_KEY) {
|
||||
process.env.EXA_API_KEY = mcpResult.exaApiKeys[0];
|
||||
Bun.env.EXA_API_KEY = mcpResult.exaApiKeys[0];
|
||||
}
|
||||
|
||||
// Log MCP errors
|
||||
|
||||
@@ -193,15 +193,15 @@ async function getGpuModel(): Promise<string | null> {
|
||||
}
|
||||
|
||||
function getTerminalName(): string {
|
||||
const termProgram = process.env.TERM_PROGRAM;
|
||||
const termProgramVersion = process.env.TERM_PROGRAM_VERSION;
|
||||
const termProgram = Bun.env.TERM_PROGRAM;
|
||||
const termProgramVersion = Bun.env.TERM_PROGRAM_VERSION;
|
||||
if (termProgram) {
|
||||
return termProgramVersion ? `${termProgram} ${termProgramVersion}` : termProgram;
|
||||
}
|
||||
|
||||
if (process.env.WT_SESSION) return "Windows Terminal";
|
||||
if (Bun.env.WT_SESSION) return "Windows Terminal";
|
||||
|
||||
const term = firstNonEmpty(process.env.TERM, process.env.COLORTERM, process.env.TERMINAL_EMULATOR);
|
||||
const term = firstNonEmpty(Bun.env.TERM, Bun.env.COLORTERM, Bun.env.TERMINAL_EMULATOR);
|
||||
return term ?? "unknown";
|
||||
}
|
||||
|
||||
@@ -216,12 +216,12 @@ function normalizeDesktopValue(value: string): string {
|
||||
}
|
||||
|
||||
function getDesktopEnvironment(): string {
|
||||
if (process.env.KDE_FULL_SESSION === "true") return "KDE";
|
||||
if (Bun.env.KDE_FULL_SESSION === "true") return "KDE";
|
||||
const raw = firstNonEmpty(
|
||||
process.env.XDG_CURRENT_DESKTOP,
|
||||
process.env.DESKTOP_SESSION,
|
||||
process.env.XDG_SESSION_DESKTOP,
|
||||
process.env.GDMSESSION,
|
||||
Bun.env.XDG_CURRENT_DESKTOP,
|
||||
Bun.env.DESKTOP_SESSION,
|
||||
Bun.env.XDG_SESSION_DESKTOP,
|
||||
Bun.env.GDMSESSION,
|
||||
);
|
||||
return raw ? normalizeDesktopValue(raw) : "unknown";
|
||||
}
|
||||
@@ -252,10 +252,10 @@ function matchKnownWindowManager(value: string): string | null {
|
||||
}
|
||||
|
||||
function getWindowManager(): string {
|
||||
const explicit = firstNonEmpty(process.env.WINDOWMANAGER);
|
||||
const explicit = firstNonEmpty(Bun.env.WINDOWMANAGER);
|
||||
if (explicit) return explicit;
|
||||
|
||||
const desktop = firstNonEmpty(process.env.XDG_CURRENT_DESKTOP, process.env.DESKTOP_SESSION);
|
||||
const desktop = firstNonEmpty(Bun.env.XDG_CURRENT_DESKTOP, Bun.env.DESKTOP_SESSION);
|
||||
if (desktop) {
|
||||
const matched = matchKnownWindowManager(desktop);
|
||||
if (matched) return matched;
|
||||
|
||||
@@ -239,7 +239,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
||||
!skipPythonPreflight &&
|
||||
pythonMode !== "bash-only" &&
|
||||
(requestedTools === undefined || requestedTools.includes("python"));
|
||||
const isTestEnv = process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test";
|
||||
const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
|
||||
const skipPythonWarm = isTestEnv || $env.PI_PYTHON_SKIP_CHECK === "1";
|
||||
if (shouldCheckPython) {
|
||||
const availability = await checkPythonKernelAvailability(session.cwd);
|
||||
|
||||
@@ -161,7 +161,7 @@ describe("executeBash", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
const bashPath = process.env.SHELL?.includes("bash") ? process.env.SHELL : "/bin/bash";
|
||||
const bashPath = Bun.env.SHELL?.includes("bash") ? Bun.env.SHELL : "/bin/bash";
|
||||
if (!fs.existsSync(bashPath)) {
|
||||
return;
|
||||
}
|
||||
@@ -171,8 +171,8 @@ describe("executeBash", () => {
|
||||
shell: bashPath,
|
||||
args: ["-l", "-c"],
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "",
|
||||
HOME: process.env.HOME ?? tempDir,
|
||||
PATH: Bun.env.PATH ?? "",
|
||||
HOME: Bun.env.HOME ?? tempDir,
|
||||
},
|
||||
prefix: undefined,
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ interface KernelStub {
|
||||
|
||||
describe("executePython (per-call)", () => {
|
||||
it("shuts down kernel on timed-out cancellation", async () => {
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
using tempDir = TempDir.createSync("@omp-python-executor-per-call-");
|
||||
|
||||
let shutdownCalls = 0;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
PythonKernel,
|
||||
} from "@oh-my-pi/pi-coding-agent/ipy/kernel";
|
||||
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
|
||||
class FakeKernel {
|
||||
private result: KernelExecuteResult;
|
||||
|
||||
@@ -160,8 +160,8 @@ afterEach(async () => {
|
||||
|
||||
describe("warmPythonEnvironment", () => {
|
||||
it("caches prelude docs on warmup", async () => {
|
||||
const previousSkip = process.env.PI_PYTHON_SKIP_CHECK;
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
const previousSkip = Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
using tempDir = TempDir.createSync("@python-executor-");
|
||||
const docs: PreludeHelper[] = [
|
||||
{
|
||||
@@ -188,9 +188,9 @@ describe("warmPythonEnvironment", () => {
|
||||
|
||||
startSpy.mockRestore();
|
||||
if (previousSkip === undefined) {
|
||||
delete process.env.PI_PYTHON_SKIP_CHECK;
|
||||
delete Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
} else {
|
||||
process.env.PI_PYTHON_SKIP_CHECK = previousSkip;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = previousSkip;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("executePython kernel reuse", () => {
|
||||
let kernels: FakeKernel[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
startCalls = 0;
|
||||
kernels = [];
|
||||
PythonKernel.start = (async () => {
|
||||
|
||||
@@ -80,9 +80,9 @@ describe("PythonKernel gateway lifecycle", () => {
|
||||
const originalSleep = Bun.sleep;
|
||||
const originalWhich = Bun.which;
|
||||
const originalExecute = PythonKernel.prototype.execute;
|
||||
const originalGatewayUrl = process.env.PI_PYTHON_GATEWAY_URL;
|
||||
const originalGatewayToken = process.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
const originalBunEnv = process.env.BUN_ENV;
|
||||
const originalGatewayUrl = Bun.env.PI_PYTHON_GATEWAY_URL;
|
||||
const originalGatewayToken = Bun.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
const originalBunEnv = Bun.env.BUN_ENV;
|
||||
|
||||
let tempDir: TempDir;
|
||||
let env: MockEnvironment;
|
||||
@@ -91,9 +91,9 @@ describe("PythonKernel gateway lifecycle", () => {
|
||||
tempDir = TempDir.createSync("@omp-python-kernel-");
|
||||
env = { fetchCalls: [], spawnCalls: [] };
|
||||
|
||||
process.env.BUN_ENV = "test";
|
||||
delete process.env.PI_PYTHON_GATEWAY_URL;
|
||||
delete process.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
Bun.env.BUN_ENV = "test";
|
||||
delete Bun.env.PI_PYTHON_GATEWAY_URL;
|
||||
delete Bun.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
|
||||
FakeWebSocket.instances = [];
|
||||
globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
|
||||
@@ -125,19 +125,19 @@ describe("PythonKernel gateway lifecycle", () => {
|
||||
}
|
||||
|
||||
if (originalBunEnv === undefined) {
|
||||
delete process.env.BUN_ENV;
|
||||
delete Bun.env.BUN_ENV;
|
||||
} else {
|
||||
process.env.BUN_ENV = originalBunEnv;
|
||||
Bun.env.BUN_ENV = originalBunEnv;
|
||||
}
|
||||
if (originalGatewayUrl === undefined) {
|
||||
delete process.env.PI_PYTHON_GATEWAY_URL;
|
||||
delete Bun.env.PI_PYTHON_GATEWAY_URL;
|
||||
} else {
|
||||
process.env.PI_PYTHON_GATEWAY_URL = originalGatewayUrl;
|
||||
Bun.env.PI_PYTHON_GATEWAY_URL = originalGatewayUrl;
|
||||
}
|
||||
if (originalGatewayToken === undefined) {
|
||||
delete process.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
delete Bun.env.PI_PYTHON_GATEWAY_TOKEN;
|
||||
} else {
|
||||
process.env.PI_PYTHON_GATEWAY_TOKEN = originalGatewayToken;
|
||||
Bun.env.PI_PYTHON_GATEWAY_TOKEN = originalGatewayToken;
|
||||
}
|
||||
|
||||
globalThis.fetch = originalFetch;
|
||||
|
||||
@@ -145,24 +145,24 @@ class FakeWebSocket {
|
||||
}
|
||||
|
||||
describe("PythonKernel (external gateway)", () => {
|
||||
const originalEnv = { ...process.env };
|
||||
const originalEnv = { ...Bun.env };
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalWebSocket = globalThis.WebSocket;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.PI_PYTHON_GATEWAY_URL = "http://gateway.test";
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
Bun.env.PI_PYTHON_GATEWAY_URL = "http://gateway.test";
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const key of Object.keys(process.env)) {
|
||||
for (const key of Object.keys(Bun.env)) {
|
||||
if (!(key in originalEnv)) {
|
||||
delete process.env[key];
|
||||
delete Bun.env[key];
|
||||
}
|
||||
}
|
||||
for (const [key, value] of Object.entries(originalEnv)) {
|
||||
process.env[key] = value;
|
||||
Bun.env[key] = value;
|
||||
}
|
||||
globalThis.fetch = originalFetch;
|
||||
globalThis.WebSocket = originalWebSocket;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { resetPreludeDocsCache, warmPythonEnvironment } from "@oh-my-pi/pi-codin
|
||||
import { getPythonToolDescription, PythonTool } from "@oh-my-pi/pi-coding-agent/tools/python";
|
||||
|
||||
const resolvePythonPath = (): string | null => {
|
||||
const venvPath = process.env.VIRTUAL_ENV;
|
||||
const venvPath = Bun.env.VIRTUAL_ENV;
|
||||
const candidates = [venvPath, path.join(process.cwd(), ".venv"), path.join(process.cwd(), "venv")].filter(
|
||||
Boolean,
|
||||
) as string[];
|
||||
|
||||
+12
-12
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@ import { ProcessTerminal, TUI } from "@oh-my-pi/pi-tui";
|
||||
import { sleep } from "bun";
|
||||
|
||||
// Initialize dark theme with full color support
|
||||
process.env.COLORTERM = "truecolor";
|
||||
Bun.env.COLORTERM = "truecolor";
|
||||
initTheme("dark");
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { initTheme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
|
||||
// Initialize with dark theme explicitly
|
||||
process.env.COLORTERM = "truecolor";
|
||||
Bun.env.COLORTERM = "truecolor";
|
||||
initTheme("dark");
|
||||
|
||||
console.log("\n=== Foreground Colors ===\n");
|
||||
@@ -68,8 +68,8 @@ console.log("Direct #00d7ff test: \x1b[38;2;0;215;255mBRIGHT CYAN\x1b[0m");
|
||||
console.log("Theme cyan (should match above):", theme.fg("accent", "BRIGHT CYAN"));
|
||||
|
||||
console.log("\n=== Environment ===\n");
|
||||
console.log("TERM:", process.env.TERM);
|
||||
console.log("COLORTERM:", process.env.COLORTERM);
|
||||
console.log("TERM:", Bun.env.TERM);
|
||||
console.log("COLORTERM:", Bun.env.COLORTERM);
|
||||
console.log("Color mode:", theme.getColorMode());
|
||||
|
||||
console.log("\n");
|
||||
|
||||
@@ -46,8 +46,8 @@ describe("Coding Agent Tools", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// Force replace mode for edit tool tests using old_text/new_text
|
||||
originalEditVariant = process.env.PI_EDIT_VARIANT;
|
||||
process.env.PI_EDIT_VARIANT = "replace";
|
||||
originalEditVariant = Bun.env.PI_EDIT_VARIANT;
|
||||
Bun.env.PI_EDIT_VARIANT = "replace";
|
||||
|
||||
// Create a unique temporary directory for each test
|
||||
testDir = path.join(os.tmpdir(), `coding-agent-test-${Snowflake.next()}`);
|
||||
@@ -69,9 +69,9 @@ describe("Coding Agent Tools", () => {
|
||||
|
||||
// Restore original edit variant
|
||||
if (originalEditVariant === undefined) {
|
||||
delete process.env.PI_EDIT_VARIANT;
|
||||
delete Bun.env.PI_EDIT_VARIANT;
|
||||
} else {
|
||||
process.env.PI_EDIT_VARIANT = originalEditVariant;
|
||||
Bun.env.PI_EDIT_VARIANT = originalEditVariant;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -419,7 +419,7 @@ function b() {
|
||||
});
|
||||
|
||||
it("should persist environment variables between commands", async () => {
|
||||
if (process.platform === "win32" || process.env.PI_SHELL_PERSIST !== "1") {
|
||||
if (process.platform === "win32" || Bun.env.PI_SHELL_PERSIST !== "1") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -575,8 +575,8 @@ describe("edit tool CRLF handling", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// Force replace mode for edit tool tests using old_text/new_text
|
||||
originalEditVariant = process.env.PI_EDIT_VARIANT;
|
||||
process.env.PI_EDIT_VARIANT = "replace";
|
||||
originalEditVariant = Bun.env.PI_EDIT_VARIANT;
|
||||
Bun.env.PI_EDIT_VARIANT = "replace";
|
||||
|
||||
testDir = path.join(os.tmpdir(), `coding-agent-crlf-test-${Snowflake.next()}`);
|
||||
fs.mkdirSync(testDir, { recursive: true });
|
||||
@@ -588,9 +588,9 @@ describe("edit tool CRLF handling", () => {
|
||||
|
||||
// Restore original edit variant
|
||||
if (originalEditVariant === undefined) {
|
||||
delete process.env.PI_EDIT_VARIANT;
|
||||
delete Bun.env.PI_EDIT_VARIANT;
|
||||
} else {
|
||||
process.env.PI_EDIT_VARIANT = originalEditVariant;
|
||||
Bun.env.PI_EDIT_VARIANT = originalEditVariant;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { type SettingPath, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { createTools, HIDDEN_TOOLS, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
|
||||
function createTestSession(overrides: Partial<ToolSession> = {}): ToolSession {
|
||||
return {
|
||||
|
||||
@@ -19,8 +19,8 @@ function createSession(overrides: Partial<ToolSession> = {}): ToolSession {
|
||||
|
||||
describe("createTools python fallback", () => {
|
||||
it("falls back to bash when python is requested but disabled", async () => {
|
||||
const previous = process.env.PI_PYTHON_SKIP_CHECK;
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
const previous = Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
const session = createSession();
|
||||
const tools = await createTools(session, ["python"]);
|
||||
const names = tools.map(tool => tool.name).sort();
|
||||
@@ -28,9 +28,9 @@ describe("createTools python fallback", () => {
|
||||
expect(names).toEqual(["bash", "exit_plan_mode"]);
|
||||
|
||||
if (previous === undefined) {
|
||||
delete process.env.PI_PYTHON_SKIP_CHECK;
|
||||
delete Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
} else {
|
||||
process.env.PI_PYTHON_SKIP_CHECK = previous;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = previous;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,16 +9,16 @@ let previousSkipCheck: string | undefined;
|
||||
let tempDir: TempDir;
|
||||
beforeAll(() => {
|
||||
tempDir = TempDir.createSync("@omp-python-test-");
|
||||
previousSkipCheck = process.env.PI_PYTHON_SKIP_CHECK;
|
||||
process.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
previousSkipCheck = Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (previousSkipCheck === undefined) {
|
||||
delete process.env.PI_PYTHON_SKIP_CHECK;
|
||||
delete Bun.env.PI_PYTHON_SKIP_CHECK;
|
||||
return;
|
||||
}
|
||||
process.env.PI_PYTHON_SKIP_CHECK = previousSkipCheck;
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = previousSkipCheck;
|
||||
tempDir.removeSync();
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { handleIacr } from "@oh-my-pi/pi-coding-agent/web/scrapers/iacr";
|
||||
import { handlePubMed } from "@oh-my-pi/pi-coding-agent/web/scrapers/pubmed";
|
||||
import { handleSemanticScholar } from "@oh-my-pi/pi-coding-agent/web/scrapers/semantic-scholar";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleSemanticScholar", () => {
|
||||
it("returns null for non-S2 URLs", async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { handleOpenCorporates } from "@oh-my-pi/pi-coding-agent/web/scrapers/opencorporates";
|
||||
import { handleSecEdgar } from "@oh-my-pi/pi-coding-agent/web/scrapers/sec-edgar";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleSecEdgar", () => {
|
||||
it("returns null for non-matching URLs", async () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { handleGitLab } from "@oh-my-pi/pi-coding-agent/web/scrapers/gitlab";
|
||||
import { handleHackerNews } from "@oh-my-pi/pi-coding-agent/web/scrapers/hackernews";
|
||||
import { handleLobsters } from "@oh-my-pi/pi-coding-agent/web/scrapers/lobsters";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
// =============================================================================
|
||||
// HackerNews Tests
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { handleMDN } from "@oh-my-pi/pi-coding-agent/web/scrapers/mdn";
|
||||
import { handleReadTheDocs } from "@oh-my-pi/pi-coding-agent/web/scrapers/readthedocs";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleMDN", () => {
|
||||
it("returns null for non-MDN URLs", async () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { handleArtifactHub } from "@oh-my-pi/pi-coding-agent/web/scrapers/artifa
|
||||
import { handleCoinGecko } from "@oh-my-pi/pi-coding-agent/web/scrapers/coingecko";
|
||||
import { handleDiscogs } from "@oh-my-pi/pi-coding-agent/web/scrapers/discogs";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleCoinGecko", () => {
|
||||
it("returns null for non-CoinGecko URLs", async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { handleGitHub } from "@oh-my-pi/pi-coding-agent/web/scrapers/github";
|
||||
import { handleGitHubGist } from "@oh-my-pi/pi-coding-agent/web/scrapers/github-gist";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
// =============================================================================
|
||||
// GitHub Tests
|
||||
|
||||
@@ -3,7 +3,7 @@ import { handleHuggingFace } from "@oh-my-pi/pi-coding-agent/web/scrapers/huggin
|
||||
import { handleSpotify } from "@oh-my-pi/pi-coding-agent/web/scrapers/spotify";
|
||||
import { handleVimeo } from "@oh-my-pi/pi-coding-agent/web/scrapers/vimeo";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleVimeo", () => {
|
||||
it("returns null for non-Vimeo URLs", async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { handleMetaCPAN } from "@oh-my-pi/pi-coding-agent/web/scrapers/metacpan"
|
||||
import { handleRepology } from "@oh-my-pi/pi-coding-agent/web/scrapers/repology";
|
||||
import { handleTerraform } from "@oh-my-pi/pi-coding-agent/web/scrapers/terraform";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleMetaCPAN", () => {
|
||||
it("returns null for non-MetaCPAN URLs", async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { handleNuGet } from "@oh-my-pi/pi-coding-agent/web/scrapers/nuget";
|
||||
import { handlePackagist } from "@oh-my-pi/pi-coding-agent/web/scrapers/packagist";
|
||||
import { handleRubyGems } from "@oh-my-pi/pi-coding-agent/web/scrapers/rubygems";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleBrew", () => {
|
||||
it("returns null for non-Homebrew URLs", async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { handleNpm } from "@oh-my-pi/pi-coding-agent/web/scrapers/npm";
|
||||
import { handlePubDev } from "@oh-my-pi/pi-coding-agent/web/scrapers/pub-dev";
|
||||
import { handlePyPI } from "@oh-my-pi/pi-coding-agent/web/scrapers/pypi";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handlePyPI", () => {
|
||||
it("returns null for non-PyPI URLs", async () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { handleBiorxiv } from "@oh-my-pi/pi-coding-agent/web/scrapers/biorxiv";
|
||||
import { handleOpenLibrary } from "@oh-my-pi/pi-coding-agent/web/scrapers/openlibrary";
|
||||
import { handleWikidata } from "@oh-my-pi/pi-coding-agent/web/scrapers/wikidata";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleWikidata", () => {
|
||||
it("returns null for non-matching URLs", async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { handleNvd } from "@oh-my-pi/pi-coding-agent/web/scrapers/nvd";
|
||||
import { handleOsv } from "@oh-my-pi/pi-coding-agent/web/scrapers/osv";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleNvd", () => {
|
||||
it("returns null for non-NVD URLs", async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import { handleBluesky } from "@oh-my-pi/pi-coding-agent/web/scrapers/bluesky";
|
||||
import { handleMastodon } from "@oh-my-pi/pi-coding-agent/web/scrapers/mastodon";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleMastodon", () => {
|
||||
it("returns null for non-Mastodon URLs", async () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { handleReddit } from "@oh-my-pi/pi-coding-agent/web/scrapers/reddit";
|
||||
import { handleStackOverflow } from "@oh-my-pi/pi-coding-agent/web/scrapers/stackoverflow";
|
||||
import { handleTwitter } from "@oh-my-pi/pi-coding-agent/web/scrapers/twitter";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleTwitter", () => {
|
||||
it("returns null for non-Twitter URLs", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { handleStackOverflow } from "@oh-my-pi/pi-coding-agent/web/scrapers/stackoverflow";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleStackOverflow", () => {
|
||||
it("returns null for non-SE URLs", async () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { handleCheatSh } from "@oh-my-pi/pi-coding-agent/web/scrapers/cheatsh";
|
||||
import { handleRfc } from "@oh-my-pi/pi-coding-agent/web/scrapers/rfc";
|
||||
import { handleTldr } from "@oh-my-pi/pi-coding-agent/web/scrapers/tldr";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleRfc", () => {
|
||||
it("returns null for non-RFC URLs", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { handleWikipedia } from "@oh-my-pi/pi-coding-agent/web/scrapers/wikipedia";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleWikipedia", () => {
|
||||
it("returns null for non-Wikipedia URLs", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { handleYouTube } from "@oh-my-pi/pi-coding-agent/web/scrapers/youtube";
|
||||
|
||||
const SKIP = !process.env.WEB_FETCH_INTEGRATION;
|
||||
const SKIP = !Bun.env.WEB_FETCH_INTEGRATION;
|
||||
|
||||
describe.skipIf(SKIP)("handleYouTube", () => {
|
||||
it("returns null for non-YouTube URLs", async () => {
|
||||
|
||||
@@ -7,9 +7,9 @@ const rustDir = path.join(repoRoot, "crates/pi-natives");
|
||||
const nativeDir = path.join(import.meta.dir, "../native");
|
||||
|
||||
const isDev = process.argv.includes("--dev");
|
||||
const crossTarget = process.env.CROSS_TARGET;
|
||||
const targetPlatform = process.env.TARGET_PLATFORM || process.platform;
|
||||
const targetArch = process.env.TARGET_ARCH || process.arch;
|
||||
const crossTarget = Bun.env.CROSS_TARGET;
|
||||
const targetPlatform = Bun.env.TARGET_PLATFORM || process.platform;
|
||||
const targetArch = Bun.env.TARGET_ARCH || process.arch;
|
||||
const isCrossCompile =
|
||||
Boolean(crossTarget) ||
|
||||
targetPlatform !== process.platform ||
|
||||
@@ -79,7 +79,7 @@ if (buildResult.exitCode !== 0) {
|
||||
|
||||
const profile = isDev ? "debug" : "release";
|
||||
const targetRoots = [
|
||||
process.env.CARGO_TARGET_DIR ? path.resolve(process.env.CARGO_TARGET_DIR) : undefined,
|
||||
Bun.env.CARGO_TARGET_DIR ? path.resolve(Bun.env.CARGO_TARGET_DIR) : undefined,
|
||||
path.join(repoRoot, "target"),
|
||||
path.join(rustDir, "target"),
|
||||
].filter((v): v is string => Boolean(v));
|
||||
|
||||
@@ -12,8 +12,8 @@ if (reset) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const targetPlatform = process.env.TARGET_PLATFORM || process.platform;
|
||||
const targetArch = process.env.TARGET_ARCH || process.arch;
|
||||
const targetPlatform = Bun.env.TARGET_PLATFORM || process.platform;
|
||||
const targetArch = Bun.env.TARGET_ARCH || process.arch;
|
||||
const platformTag = `${targetPlatform}-${targetArch}`;
|
||||
const addonFilename = `pi_natives.${platformTag}.node`;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const versionedDir = path.join(os.homedir(), ".omp", "natives", packageVersion);
|
||||
const versionedAddonPath = path.join(versionedDir, addonFilename);
|
||||
const legacyUserDataDir =
|
||||
process.platform === "win32"
|
||||
? path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "omp")
|
||||
? path.join(Bun.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "omp")
|
||||
: path.join(os.homedir(), ".local", "bin");
|
||||
const downloadUrl = `https://github.com/can1357/oh-my-pi/releases/latest/download/${addonFilename}`;
|
||||
const isCompiledBinary =
|
||||
|
||||
@@ -75,7 +75,7 @@ export const TERMINAL_ID: TerminalId = (() => {
|
||||
TERM_PROGRAM,
|
||||
TERM,
|
||||
COLORTERM,
|
||||
} = process.env;
|
||||
} = Bun.env;
|
||||
|
||||
if (KITTY_WINDOW_ID) return "kitty";
|
||||
if (GHOSTTY_RESOURCES_DIR) return "ghostty";
|
||||
|
||||
@@ -50,17 +50,17 @@ const projectEnv = parseEnvFile(path.join(process.cwd(), ".env"));
|
||||
|
||||
for (const file of [projectEnv, homeEnv]) {
|
||||
for (const [key, value] of Object.entries(file)) {
|
||||
if (!process.env[key]) {
|
||||
process.env[key] = value;
|
||||
if (!Bun.env[key]) {
|
||||
Bun.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intentional re-export of process.env.
|
||||
* Intentional re-export of Bun.env.
|
||||
*
|
||||
* All users should import this env module (import { $env } from "@oh-my-pi/pi-utils")
|
||||
* before using environment variables. This ensures that .env files have been loaded and
|
||||
* overrides (project, home) have been applied, so $env always reflects the correct values.
|
||||
*/
|
||||
export const $env: Record<string, string> = process.env as Record<string, string>;
|
||||
export const $env: Record<string, string> = Bun.env as Record<string, string>;
|
||||
|
||||
@@ -34,7 +34,7 @@ function isExecutable(path: string): boolean {
|
||||
function buildSpawnEnv(shell: string): Record<string, string> {
|
||||
const noCI = $env.PI_BASH_NO_CI || $env.CLAUDE_BASH_NO_CI;
|
||||
return {
|
||||
...process.env,
|
||||
...Bun.env,
|
||||
SHELL: shell,
|
||||
GIT_EDITOR: "true",
|
||||
GPG_TTY: "not a tty",
|
||||
@@ -135,11 +135,11 @@ export function getShellConfig(customShellPath?: string): ShellConfig {
|
||||
if (process.platform === "win32") {
|
||||
// 2. Try Git Bash in known locations
|
||||
const paths: string[] = [];
|
||||
const programFiles = process.env.ProgramFiles;
|
||||
const programFiles = Bun.env.ProgramFiles;
|
||||
if (programFiles) {
|
||||
paths.push(`${programFiles}\\Git\\bin\\bash.exe`);
|
||||
}
|
||||
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
||||
const programFilesX86 = Bun.env["ProgramFiles(x86)"];
|
||||
if (programFilesX86) {
|
||||
paths.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export function getShellConfig(customShellPath?: string): ShellConfig {
|
||||
}
|
||||
|
||||
// Unix: prefer user's shell from $SHELL if it's bash/zsh and executable
|
||||
const userShell = process.env.SHELL;
|
||||
const userShell = Bun.env.SHELL;
|
||||
const isValidShell = userShell && (userShell.includes("bash") || userShell.includes("zsh"));
|
||||
if (isValidShell && isExecutable(userShell)) {
|
||||
cachedShellConfig = buildConfig(userShell);
|
||||
|
||||
@@ -77,7 +77,7 @@ async function spawnBatch(count: number, basePort: number, timeout: number): Pro
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
stdin: "pipe",
|
||||
env: { ...process.env, NO_COLOR: "1", PI_DEBUG_STARTUP: "1" },
|
||||
env: { ...Bun.env, NO_COLOR: "1", PI_DEBUG_STARTUP: "1" },
|
||||
});
|
||||
instances.push({ proc, port, stdout: "", stderr: "", status: "pending" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user