refactor(dirs): restructured environment variable access and simplified configuration in test suite

- Refactored environment variable access to use dedicated getter/setter functions in test suite.
- Simplified configuration constants by removing ompConfig dependency and using hardcoded values.
- Introduced module-level caching for agent directory and subdirectory paths to improve performance.
- Added setAgentDir() function to manage agent directory updates and cache invalidation.
- Reorganized dirs.ts with logical section headers and consolidated subdirectory functions using helper utilities.
- Updated directory accessor functions to accept optional agentDir parameter for flexibility.
This commit is contained in:
can1357
2026-02-13 15:26:06 +01:00
parent fb72f5d6a6
commit 08e9c685f3
4 changed files with 197 additions and 228 deletions
+7 -10
View File
@@ -2,24 +2,21 @@ import { afterEach, describe, expect, it, vi } from "bun:test";
import { streamOpenAICodexResponses } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
import type { Context, Model } from "@oh-my-pi/pi-ai/types";
import { TempDir } from "@oh-my-pi/pi-utils";
import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs";
const originalFetch = global.fetch;
const originalAgentDir = Bun.env.PI_CODING_AGENT_DIR;
const originalAgentDir = getAgentDir();
afterEach(() => {
global.fetch = originalFetch;
if (originalAgentDir === undefined) {
delete Bun.env.PI_CODING_AGENT_DIR;
} else {
Bun.env.PI_CODING_AGENT_DIR = originalAgentDir;
}
setAgentDir(originalAgentDir);
vi.restoreAllMocks();
});
describe("openai-codex streaming", () => {
it("streams SSE responses into AssistantMessageEventStream", async () => {
const tempDir = TempDir.createSync("@pi-codex-stream-");
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
setAgentDir(tempDir.path());
const payload = Buffer.from(
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
@@ -130,7 +127,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-");
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
setAgentDir(tempDir.path());
const payload = Buffer.from(
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
@@ -230,7 +227,7 @@ describe("openai-codex streaming", () => {
it("clamps gpt-5.3-codex minimal reasoning effort to low", async () => {
const tempDir = TempDir.createSync("@pi-codex-stream-");
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
setAgentDir(tempDir.path());
const payload = Buffer.from(
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
@@ -326,7 +323,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-");
Bun.env.PI_CODING_AGENT_DIR = tempDir.path();
setAgentDir(tempDir.path());
const payload = Buffer.from(
JSON.stringify({ "https://api.openai.com/auth": { chatgpt_account_id: "acc_test" } }),
+2 -28
View File
@@ -448,6 +448,7 @@ The `--system-prompt` CLI flag overrides both files. Use `--append-system-prompt
### Custom Models and Providers
Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.omp/agent/models.yml` (`models.json` is still supported for legacy configs):
> See [models.yml provider integration guide](docs/models.md) for full schema, merge behavior, and provider integration patterns.
```json
@@ -751,7 +752,7 @@ export default function (omp: HookAPI) {
content,
display: true,
},
true
true,
); // triggerTurn: start agent loop
}
});
@@ -1036,33 +1037,6 @@ Omp is a fork of [Pi](https://github.com/badlogic/pi) by [Mario Zechner](https:/
## Development
### Forking / Rebranding
Configure via `package.json`:
```json
{
"ompConfig": {
"name": "omp",
"configDir": ".omp"
}
}
```
Change `name`, `configDir`, and `bin` field for your fork. Affects CLI banner, config paths, and environment variable names.
### Path Resolution
Three execution modes: npm install, standalone binary, tsx from source.
**Always use `src/config.ts`** for package assets:
```typescript
import { getPackageDir } from "./config.js";
```
Never use `__dirname` directly for package assets.
### Debug Command
`/debug` (hidden) writes rendered lines with ANSI codes to `~/.omp/agent/omp-debug.log` for TUI debugging, as well as the last set of messages that were sent to the LLM.
-4
View File
@@ -3,10 +3,6 @@
"version": "12.0.0",
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
"type": "module",
"ompConfig": {
"name": "omp",
"configDir": ".omp"
},
"bin": {
"omp": "src/cli.ts"
},
+188 -186
View File
@@ -6,245 +6,247 @@
*/
import * as os from "node:os";
import * as path from "node:path";
import packageJson from "../../coding-agent/package.json" with { type: "json" };
import { version } from "../package.json" with { type: "json" };
import { $env } from "./env";
/** App name (e.g. "omp") */
export const APP_NAME: string = packageJson.ompConfig?.name || "omp";
export const APP_NAME: string = "omp";
/** Config directory name (e.g. ".omp") */
export const CONFIG_DIR_NAME: string = packageJson.ompConfig?.configDir || ".omp";
export const CONFIG_DIR_NAME: string = ".omp";
/** Version (e.g. "1.0.0") */
export const VERSION: string = packageJson.version;
export const VERSION: string = version;
/**
* Get the config root directory (~/.omp).
*/
// =============================================================================
// Root directories
// =============================================================================
/** Get the config root directory (~/.omp). */
export function getConfigRootDir(): string {
return path.join(os.homedir(), $env.PI_CONFIG_DIR || CONFIG_DIR_NAME);
}
/**
* Get the path to the agent directory.
*/
export function getAgentDir(): string {
return $env.PI_CODING_AGENT_DIR || path.join(getConfigRootDir(), "agent");
let agentDir = $env.PI_CODING_AGENT_DIR || path.join(getConfigRootDir(), "agent");
/** Set the coding agent directory. */
export function setAgentDir(dir: string): void {
agentDir = dir;
agentCache.clear();
$env.PI_CODING_AGENT_DIR = dir;
}
/**
* Get the path to the project-local agent directory.
*/
/** Get the agent config directory (~/.omp/agent). */
export function getAgentDir(): string {
return agentDir;
}
/** Get the project-local config directory (.omp). */
export function getProjectAgentDir(cwd: string = process.cwd()): string {
return path.join(cwd, CONFIG_DIR_NAME);
}
/**
* Get the reports directory (~/.omp/reports).
*/
export function getReportsDir(): string {
return path.join(getConfigRootDir(), "reports");
}
// =============================================================================
// Caching utilities
// =============================================================================
/**
* Get the logs directory (~/.omp/logs).
*/
export function getLogsDir(): string {
return path.join(getConfigRootDir(), "logs");
}
const rootCache = new Map<string, any>();
/**
* Get the path to today's log file.
*/
export function getLogPath(date?: string): string {
const today = date ?? new Date().toISOString().slice(0, 10);
return path.join(getLogsDir(), `omp.${today}.log`);
}
/**
* Get the user-level Python modules directory (~/.omp/agent/modules).
*/
export function getAgentModulesDir(agentDir: string = getAgentDir()): string {
return path.join(agentDir, "modules");
}
/**
* Get the project-level Python modules directory (.omp/modules).
*/
export function getProjectModulesDir(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "modules");
}
/**
* Get the project-level prompts directory (.omp/prompts).
*/
export function getProjectPromptsDir(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "prompts");
}
/**
* Get the plugins directory (~/.omp/plugins).
*/
export function getPluginsDir(): string {
return path.join(getConfigRootDir(), "plugins");
}
/** Where npm installs packages: ~/.omp/plugins/node_modules */
export function getPluginsNodeModules(): string {
return path.join(getPluginsDir(), "node_modules");
}
/** Plugin manifest: ~/.omp/plugins/package.json */
export function getPluginsPackageJson(): string {
return path.join(getPluginsDir(), "package.json");
}
/** Plugin lock file: ~/.omp/plugins/omp-plugins.lock.json */
export function getPluginsLockfile(): string {
return path.join(getPluginsDir(), "omp-plugins.lock.json");
}
/**
* Get the remote mount directory (~/.omp/remote).
*/
export function getRemoteDir(): string {
return path.join(getConfigRootDir(), "remote");
}
/**
* Get the SSH control socket directory (~/.omp/ssh-control).
*/
export function getSshControlDir(): string {
return path.join(getConfigRootDir(), "ssh-control");
}
/**
* Get the remote host info directory (~/.omp/remote-host).
*/
export function getRemoteHostDir(): string {
return path.join(getConfigRootDir(), "remote-host");
}
/**
* Get the managed Python venv directory (~/.omp/python-env).
*/
export function getPythonEnvDir(): string {
return path.join(getConfigRootDir(), "python-env");
}
/**
* Get the project-level plugin overrides path (.omp/plugin-overrides.json).
*/
export function getProjectPluginOverridesPath(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "plugin-overrides.json");
}
/**
* Get the MCP config file path.
* @param scope - "user" for ~/.omp/mcp.json or "project" for .omp/mcp.json
*/
export function getMCPConfigPaths(scope: "user" | "project", cwd: string = process.cwd()): string[] {
if (scope === "user") {
return [path.join(getAgentDir(), "mcp.json"), path.join(getAgentDir(), ".mcp.json")];
function getRootSubdir(subdir: string): string {
if (rootCache.has(subdir)) {
return rootCache.get(subdir);
}
return [path.join(getProjectAgentDir(cwd), "mcp.json"), path.join(getProjectAgentDir(cwd), ".mcp.json")];
const result = path.join(getConfigRootDir(), subdir);
rootCache.set(subdir, result);
return result;
}
/**
* Get the primary MCP config file path (first candidate).
* @param scope - "user" for ~/.omp/agent/mcp.json or "project" for .omp/mcp.json
*/
export function getMCPConfigPath(scope: "user" | "project", cwd: string = process.cwd()): string {
return getMCPConfigPaths(scope, cwd)[0];
const agentCache = new Map<string, any>();
function getAgentSubdir(userAgentDir: string | undefined, subdir: string): string {
if (!userAgentDir || userAgentDir === agentDir) {
if (agentCache.has(subdir)) {
return agentCache.get(subdir);
} else {
const result = path.join(agentDir, subdir);
agentCache.set(subdir, result);
return result;
}
} else {
return path.join(userAgentDir, subdir);
}
}
/**
* Get the worktree base directory (~/.omp/wt).
*/
// =============================================================================
// Config-root subdirectories (~/.omp/*)
// =============================================================================
/** Get the reports directory (~/.omp/reports). */
export function getReportsDir(): string {
return getRootSubdir("reports");
}
/** Get the logs directory (~/.omp/logs). */
export function getLogsDir(): string {
return getRootSubdir("logs");
}
/** Get the path to a dated log file (~/.omp/logs/omp.YYYY-MM-DD.log). */
export function getLogPath(date = new Date()): string {
return path.join(getLogsDir(), `${APP_NAME}.${date.toISOString().slice(0, 10)}.log`);
}
/** Get the plugins directory (~/.omp/plugins). */
export function getPluginsDir(): string {
return getRootSubdir("plugins");
}
/** Where npm installs packages (~/.omp/plugins/node_modules). */
export function getPluginsNodeModules(): string {
return getRootSubdir("plugins/node_modules");
}
/** Plugin manifest (~/.omp/plugins/package.json). */
export function getPluginsPackageJson(): string {
return getRootSubdir("plugins/package.json");
}
/** Plugin lock file (~/.omp/plugins/omp-plugins.lock.json). */
export function getPluginsLockfile(): string {
return getRootSubdir("plugins/omp-plugins.lock.json");
}
/** Get the remote mount directory (~/.omp/remote). */
export function getRemoteDir(): string {
return getRootSubdir("remote");
}
/** Get the SSH control socket directory (~/.omp/ssh-control). */
export function getSshControlDir(): string {
return getRootSubdir("ssh-control");
}
/** Get the remote host info directory (~/.omp/remote-host). */
export function getRemoteHostDir(): string {
return getRootSubdir("remote-host");
}
/** Get the managed Python venv directory (~/.omp/python-env). */
export function getPythonEnvDir(): string {
return getRootSubdir("python-env");
}
/** Get the worktree base directory (~/.omp/wt). */
export function getWorktreeBaseDir(): string {
return path.join(getConfigRootDir(), "wt");
return getRootSubdir("wt");
}
/**
* Get the path to a worktree directory.
*/
/** Get the path to a worktree directory (~/.omp/wt/<project>/<id>). */
export function getWorktreeDir(encodedProject: string, id: string): string {
return path.join(getWorktreeBaseDir(), encodedProject, id);
}
/**
* Get the GPU cache path (~/.omp/gpu_cache.json).
*/
/** Get the GPU cache path (~/.omp/gpu_cache.json). */
export function getGpuCachePath(): string {
return path.join(getConfigRootDir(), "gpu_cache.json");
return getRootSubdir("gpu_cache.json");
}
/**
* Get the test auth database path (~/.omp/agent/testauth.db).
*/
export function getTestAuthPath(): string {
return path.join(getAgentDir(), "testauth.db");
}
/**
* Get the sessions directory (~/.omp/agent/sessions).
*/
export function getSessionsDir(): string {
return path.join(getAgentDir(), "sessions");
}
/**
* Get the natives directory (~/.omp/natives).
*/
/** Get the natives directory (~/.omp/natives). */
export function getNativesDir(): string {
return path.join(getConfigRootDir(), "natives");
return getRootSubdir("natives");
}
/**
* Get the stats database path (~/.omp/stats.db).
*/
/** Get the stats database path (~/.omp/stats.db). */
export function getStatsDbPath(): string {
return path.join(getConfigRootDir(), "stats.db");
return getRootSubdir("stats.db");
}
/**
* Get the crash log path (~/.omp/agent/omp-crash.log).
*/
export function getCrashLogPath(): string {
return path.join(getAgentDir(), "omp-crash.log");
// =============================================================================
// Agent subdirectories (~/.omp/agent/*)
// =============================================================================
/** Get the path to agent.db (SQLite database for settings and auth storage). */
export function getAgentDbPath(agentDir?: string): string {
return getAgentSubdir(agentDir, "agent.db");
}
/** Gets the path to agent.db (SQLite database for settings and auth storage) */
export function getAgentDbPath(agentDir: string = getAgentDir()): string {
return path.join(agentDir, "agent.db");
/** Get the sessions directory (~/.omp/agent/sessions). */
export function getSessionsDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "sessions");
}
/** Get path to user's custom themes directory */
export function getCustomThemesDir(): string {
return path.join(getAgentDir(), "themes");
/** Get the content-addressed blob store directory (~/.omp/agent/blobs). */
export function getBlobsDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "blobs");
}
/** Get path to tools directory */
export function getToolsDir(): string {
return path.join(getAgentDir(), "tools");
/** Get the custom themes directory (~/.omp/agent/themes). */
export function getCustomThemesDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "themes");
}
/** Get path to slash commands directory */
export function getCommandsDir(): string {
return path.join(getAgentDir(), "commands");
/** Get the tools directory (~/.omp/agent/tools). */
export function getToolsDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "tools");
}
/** Get path to prompts directory */
export function getPromptsDir(): string {
return path.join(getAgentDir(), "prompts");
/** Get the slash commands directory (~/.omp/agent/commands). */
export function getCommandsDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "commands");
}
/** Get path to content-addressed blob store directory */
export function getBlobsDir(): string {
return path.join(getAgentDir(), "blobs");
/** Get the prompts directory (~/.omp/agent/prompts). */
export function getPromptsDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "prompts");
}
/** Get path to debug log file */
export function getDebugLogPath(): string {
return path.join(getAgentDir(), `${APP_NAME}-debug.log`);
/** Get the user-level Python modules directory (~/.omp/agent/modules). */
export function getAgentModulesDir(agentDir?: string): string {
return getAgentSubdir(agentDir, "modules");
}
/** Get the test auth database path (~/.omp/agent/testauth.db). */
export function getTestAuthPath(agentDir?: string): string {
return getAgentSubdir(agentDir, "testauth.db");
}
/** Get the crash log path (~/.omp/agent/omp-crash.log). */
export function getCrashLogPath(agentDir?: string): string {
return getAgentSubdir(agentDir, "omp-crash.log");
}
/** Get the debug log path (~/.omp/agent/omp-debug.log). */
export function getDebugLogPath(agentDir?: string): string {
return getAgentSubdir(agentDir, `${APP_NAME}-debug.log`);
}
// =============================================================================
// Project subdirectories (.omp/*)
// =============================================================================
/** Get the project-level Python modules directory (.omp/modules). */
export function getProjectModulesDir(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "modules");
}
/** Get the project-level prompts directory (.omp/prompts). */
export function getProjectPromptsDir(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "prompts");
}
/** Get the project-level plugin overrides path (.omp/plugin-overrides.json). */
export function getProjectPluginOverridesPath(cwd: string = process.cwd()): string {
return path.join(getProjectAgentDir(cwd), "plugin-overrides.json");
}
// =============================================================================
// MCP config paths
// =============================================================================
/** Get the primary MCP config file path (first candidate). */
export function getMCPConfigPath(scope: "user" | "project", cwd: string = process.cwd()): string {
if (scope === "user") {
return path.join(getAgentDir(), "mcp.json");
}
return path.join(getProjectAgentDir(cwd), "mcp.json");
}