fix(coding-agent): surface invalid inherited profile env as clean CLI error

- Revalidate OMP/PI profile env in runCli when no explicit --profile is passed
- Convert module-load profile parse failures into CLI error reporting path
- Preserve successful execution flow for valid profile selection paths
- Add integration test ensuring invalid env emits clear error and non-zero exit
This commit is contained in:
Ogrodev
2026-05-30 09:08:16 -03:00
parent 3a50761153
commit db8e090f91
2 changed files with 64 additions and 1 deletions
+15 -1
View File
@@ -1,5 +1,12 @@
#!/usr/bin/env bun
import { APP_NAME, getActiveProfile, MIN_BUN_VERSION, setProfile, VERSION } from "@oh-my-pi/pi-utils/dirs";
import {
APP_NAME,
getActiveProfile,
MIN_BUN_VERSION,
normalizeProfileName,
setProfile,
VERSION,
} from "@oh-my-pi/pi-utils/dirs";
// Strip macOS malloc-stack-logging env vars before any subprocess is spawned.
// Keep this local instead of importing `@oh-my-pi/pi-utils/procmgr`: that module
@@ -60,6 +67,13 @@ export async function runCli(argv: string[]): Promise<void> {
resolvedArgv = extracted.argv;
if (extracted.profile !== undefined) {
setProfile(extracted.profile);
} else {
// No explicit --profile: re-validate any OMP_PROFILE/PI_PROFILE inherited
// from the environment. Module-load resolution deliberately swallows an
// invalid value to avoid an uncaught throw before this try/catch is in
// scope (see `readProfileFromEnvSafe` in dirs.ts). Surfacing it here turns
// `OMP_PROFILE=.. omp --version` into a clean error instead of a stack trace.
normalizeProfileName(process.env.OMP_PROFILE || process.env.PI_PROFILE);
}
if (extracted.aliasName !== undefined) {
const profile = extracted.profile ?? getActiveProfile();
@@ -168,4 +168,53 @@ describe("global --profile flag", () => {
await fs.rm(root, { recursive: true, force: true });
}
});
it("surfaces an invalid OMP_PROFILE env as a clean error, not an import crash", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "omp-profile-cli-env-bad-"));
try {
const home = path.join(root, "home");
await fs.mkdir(home, { recursive: true });
const probePath = path.join(root, "probe.ts");
await Bun.write(
probePath,
[
`import { runCli } from ${JSON.stringify(url.pathToFileURL(cliEntry).href)};`,
'await runCli(["--version"]);',
// Reached only if the module import did NOT throw — i.e. the invalid
// env was deferred to runCli's error handler instead of crashing the
// process during the static import of dirs.ts.
'process.stdout.write("HANDLED");',
].join("\n"),
);
const childEnv: Record<string, string | undefined> = {
...process.env,
HOME: home,
PI_CONFIG_DIR: ".omp-profile-cli-env-bad",
OMP_PROFILE: "..",
NO_COLOR: "1",
};
delete childEnv.PI_PROFILE;
delete childEnv.PI_CODING_AGENT_DIR;
const proc = Bun.spawn([process.execPath, probePath], {
cwd: repoRoot,
stdout: "pipe",
stderr: "pipe",
env: childEnv,
});
const [stdout, stderr, exitCode] = await Promise.all([
readStream(proc.stdout as ReadableStream<Uint8Array>),
readStream(proc.stderr as ReadableStream<Uint8Array>),
proc.exited,
]);
expect(stdout, stderr).toContain("HANDLED");
expect(stderr).toContain("Invalid OMP profile");
expect(exitCode).toBe(1);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
});