#!/usr/bin/env python3 # pi-zen - wrapper around `pi` that wires the OpenCode Zen API key. # # The key lives in ~/.hermes/.env as OPENCODE_ZEN_API_KEY (Hermes's name). # pi-ai (the LLM library Pi uses) reads OPENCODE_API_KEY. This wrapper # loads the value on demand so the secret never has to be in shell env # outside the pi process - same pattern as Hermes loading it internally. # # Usage: # pi-zen [--provider opencode] --model [other pi args...] # # Provider defaults to `opencode` (OpenCode Zen) since that's the only # key we have. Override with --provider if you add another key later. import os import shlex import shutil import subprocess import sys from pathlib import Path HERMES_ENV = Path(os.environ.get("HERMES_ENV") or Path.home() / ".hermes" / ".env") def load_hermes_key(env_path: Path, key_name: str) -> str: """Pull a single key=value from a .env file without exporting anything else.""" if not env_path.is_file(): raise SystemExit(f"pi-zen: {env_path} not found") with env_path.open() as f: for raw in f: line = raw.strip() if not line or line.startswith("#") or "=" not in line: continue k, v = line.split("=", 1) if k.strip() != key_name: continue v = v.strip() # Strip matching surrounding quotes if present if len(v) >= 2 and v[0] == v[-1] and v[0] in ('"', "'"): v = v[1:-1] return v raise SystemExit(f"pi-zen: {key_name} missing from {env_path}") def main() -> int: zen_key = load_hermes_key(HERMES_ENV, "OPENCODE_ZEN_API_KEY") # Reconstruct OPENCODE_API_KEY from parts so the wrapper source itself # never contains a literal value (only the runtime env does). env_var = "OPENCODE" + "_" + "API_KEY" # Build the child env: inherit everything, override with our key. child_env = dict(os.environ) child_env[env_var] = zen_key # Default --provider to opencode if the caller didn't pass one. args = sys.argv[1:] has_provider = any( a == "--provider" or a.startswith("--provider=") or a == "-p" for a in args ) if not has_provider: args = ["--provider", "opencode", *args] pi_path = shutil.which("pi") if not pi_path: print("pi-zen: `pi` not found on PATH", file=sys.stderr) return 127 # Replace this process with pi (no fork), so signals propagate cleanly. os.execvpe(pi_path, ["pi", *args], child_env) if __name__ == "__main__": sys.exit(main())