Something went wrong. Try again.
Something went wrong. Try again.
prime-hyper hyper.ts
3.4 kB · 84 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485/** * Charm Hyper (https://hyper.charm.land) provider identity and auth for Prime Agent. */
import { readFileSync } from "node:fs";import { homedir } from "node:os";import { join } from "node:path";import { getJson } from "./http.js";
export const PROVIDER = "hyper";export const DISPLAY_NAME = "Charm Hyper";export const SITE_URL = "https://hyper.charm.land";export const API_BASE_URL = `${SITE_URL}/v1`;export const ENV_VAR = "HYPER_API_KEY";
/** * What Prime Agent resolves when no credential is stored. `!`-prefixed values run through * the shell (bash -c), and an empty result counts as an unresolved key rather than an * empty string, which turns "no key" into a readable error instead of a 401. */export const API_KEY_CONFIG = `!printf %s "$${ENV_VAR}"`;
/** * Prime Agent's agent dir, mirrored here: importing the runtime for it drags a * jiti-hostile module graph into the extension load path. */export function agentDir(): string { const configured = process.env.PRIME_AGENT_CODING_AGENT_DIR ?? process.env.PI_CODING_AGENT_DIR; if (!configured) return join(homedir(), ".prime", "agent"); if (configured === "~") return homedir(); return configured.startsWith("~/") ? join(homedir(), configured.slice(2)) : configured;}
const AUTH_FILE = (): string => join(agentDir(), "auth.json");
export const cachePath = (): string => join(agentDir(), "hyper-catalog.json");
export const isOffline = (): boolean => { const value = process.env.PI_OFFLINE; return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";};
/** * The API-key credential in an auth.json payload. A provider entry is one credential or a * list of them (Prime Agent pools accounts: a subscription and an API key coexist), so both * shapes resolve here. */export function storedKeyFrom(payload: unknown): string | undefined { if (typeof payload !== "object" || payload === null) return undefined; const entry = Object.getOwnPropertyDescriptor(payload, PROVIDER)?.value; for (const credential of Array.isArray(entry) ? entry : [entry]) { if (typeof credential !== "object" || credential === null) continue; const key = Object.getOwnPropertyDescriptor(credential, "key")?.value; if (typeof key === "string" && key !== "") return key; } return undefined;}
/** * Key saved by `/login hyper` - a pasted key, or the device-code subscription login. This * and `HYPER_API_KEY` are the whole story: no key file, so the credential has one home, and * a missing key is reported as unresolved rather than sent as a placeholder. */export function storedKey(): string | undefined { try { return storedKeyFrom(JSON.parse(readFileSync(AUTH_FILE(), "utf-8"))); } catch { return undefined; }}
/** Key for the probe scripts: `HYPER_API_KEY`, else the stored credential. */export const resolveKey = (): string | undefined => process.env[ENV_VAR] ?? storedKey();
/** The account's Hypercredit balance. `GET /v1/credits` is the only account endpoint. */export async function fetchBalance(apiKey: string): Promise<number | undefined> { const payload = await getJson(`${SITE_URL}/v1/credits`, { Authorization: `Bearer ${apiKey}` }); if (typeof payload !== "object" || payload === null) return undefined; for (const field of ["balance", "balance_usd"]) { const value = Object.getOwnPropertyDescriptor(payload, field)?.value; if (typeof value === "number") return value; } return undefined;}