@phren/cli
Version:
Knowledge layer for AI agents - CLI, MCP server, and data layer
80 lines (79 loc) • 4.69 kB
TypeScript
import { type ModuleSnapshot } from "../modules/runtime.js";
import type { GatewayScope } from "./scoped-gateway.js";
import { type CodexServerEntry } from "./codex-servers.js";
export declare const peerForcedCommand = "command=\"sh ~/.local/share/phren/bridge/dispatch-scoped peer\"";
export declare const forcedCommand = "command=\"sh ~/.local/share/phren/bridge/dispatch\"";
/** A scoped key's forced command. A separate script, so a key added before
* the Hook can serve its scope fails closed instead of reaching `dispatch`. */
export declare const scopedForcedCommand: (scope: GatewayScope) => string;
export declare function upgradeKeys(text: string): {
text: string;
changed: number;
};
/** The existing LaunchAgent's own EnvironmentVariables beyond phren's, read
* with plutil. A missing or unreadable plist has none. */
export declare function extraLaunchAgentEnvironment(file: string, read?: (file: string) => Promise<string>): Promise<Record<string, string>>;
export interface LaunchAgentValues {
label: string;
node: string;
program: string;
path: string;
root: string;
herdr: string;
store: string;
profile: string;
}
/** The Hook's LaunchAgent: phren's own environment first, then every kept key. */
export declare function launchAgentXml(values: LaunchAgentValues, extra?: Record<string, string>): string;
/** The forwarder the forced command uses for the phone's byte pipe. */
export type GatewayKind = "socat" | "nc" | "node";
export interface GatewayEnvironment {
root: string;
herdr: string;
store: string;
profile: string;
node: string;
bundle: string;
socket: string;
timing: string;
}
/** Pick the cheapest reliable forwarder at install time: socat, then an nc that
* understands Unix sockets, then the node gateway that always works. */
export declare function detectGateway(env?: NodeJS.ProcessEnv): Promise<GatewayKind>;
/** The POSIX sh forced command: the phone's byte pipe goes straight to the Hook
* socket through a tiny forwarder, so a loaded machine never pays for a fresh
* node process; every other SSH command falls through to the node gateway. */
export declare function gatewayScript(gateway: GatewayKind, environment: GatewayEnvironment): string;
/** The enroll script, for an SSH session the user signed into with a password
* (the phone's "Password" sign-in): it adds one restricted phone key, then the
* phone connects with that key. It is never the forced command, so a phone key
* cannot use it to add more keys. */
export declare function enrollScript(environment: Pick<GatewayEnvironment, "root" | "herdr" | "store" | "profile" | "node" | "bundle">): string;
/** The scoped keys' forced command: always the node gateway (never the raw
* socat/nc pipe), told which scope the key holds. An unknown scope or an older
* bundle without `ssh-scoped` refuses. */
export declare function scopedGatewayScript(environment: Omit<GatewayEnvironment, "socket" | "timing">): string;
export declare const scopedGatewayPath: (root?: string) => string;
/** The commands that restart the Hook in `domain`, printed when launchd refuses. */
export declare function launchCommands(domain: string, plist: string): string[];
/** How long install waits for Codex turns that would stop with the Hook. */
export declare const CODEX_TURN_WAIT_MS: number;
/** Codex workers still running inside the Hook's service (started by a Hook
* from before each server got its own scope) stop with it, mid-turn or not.
* Say which, and wait up to `waitMs` for the running turns to finish. */
export declare function waitForCodexWorkers(waitMs?: number, inService?: () => Promise<CodexServerEntry[]>, log?: (line: string) => void, pollMs?: number): Promise<void>;
export declare function install(version: string, noService?: boolean, force?: boolean): Promise<void>;
export declare function uninstall(): Promise<void>;
interface SettingsEdit {
file: string;
before?: string;
after: string;
}
export declare function planAgentHooks(program: string, remove?: boolean, modules?: ModuleSnapshot, fastClaude?: boolean): Promise<SettingsEdit[]>;
/** The first line of every plugin copy phren wrote; a copy without it belongs to the user. */
export declare const OPENCODE_PLUGIN_MARKER = "// Installed by Phren Hook";
/** Whether to (re)write the installed plugin: a missing copy, or one phren wrote that is now out of date. */
export declare function opencodePluginNeedsWrite(existing: string | undefined, source: string): boolean;
export declare function rollback(): Promise<void>;
export declare function reconcileModuleHooks(store: string, profile?: string): Promise<void>;
export {};