@prohost/cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,8 +4,83 @@ Versions follow [semver](https://semver.org/). Publishing is automated: merging
4
4
  a version bump to `main` triggers `.github/workflows/npm-publish-cli.yml`, which
5
5
  builds via `prepack`, runs the suite, publishes, and tags `cli-v<version>`.
6
6
 
7
+ ## 0.7.0
8
+
9
+ Machines, accounts and agents: a paired AI employee now runs on a named
10
+ Claude Code or Codex login, and ProhostAI can show — and change — which one.
11
+
12
+ - **Accounts.** `prohost agent account add <label> --runtime claude|codex`
13
+ creates a login in its own config directory under `~/.prohost-accounts`
14
+ (`$PROHOST_ACCOUNTS_ROOT`), prints the exact sign-in command
15
+ (`CLAUDE_CONFIG_DIR=… claude auth login` / `CODEX_HOME=… codex login`) and
16
+ waits until the agent CLI reports it signed in. The CLI never reads the
17
+ login: it asks `claude auth status` / `codex login status` and keeps only
18
+ "signed in or not". `account list` / `account remove` round it out, and
19
+ `default` is the CLI's usual `~/.claude` / `~/.codex`, so existing agents
20
+ change nothing.
21
+ - **One account per agent, applied per run.** `--account <label>` on `pair`
22
+ and `install-daemon` stores it in `agent.json`; every run, idle usage probe
23
+ and Codex MCP probe is spawned with that account's `CLAUDE_CONFIG_DIR` /
24
+ `CODEX_HOME`, so a change applies from the next run with no restart. An agent
25
+ set to an account that is not on the machine refuses to run rather than spend
26
+ another subscription. **Nothing ever switches accounts automatically.**
27
+ - **Picked in ProhostAI.** An `agent.config_updated` event (signed, verified
28
+ like a run) or `auth_ok.agent_config` on reconnect moves the agent to the
29
+ account an operator picked in the web app, from its next run. An account
30
+ that isn't on this machine is ignored with a warning. Picks carry the
31
+ server's `requested_at`; one older than a pick already applied — or than the
32
+ floor `auth_ok` sends on connect — arrived out of order and is dropped.
33
+ - **A stable machine id.** The first agent on a computer writes a random UUID
34
+ to `~/.prohost-accounts/machine.json`; every agent there sends it on auth as
35
+ `machine_id`. ProhostAI keys the machine's accounts by it — the label
36
+ (hostname by default) is display only, since two computers can share one.
37
+ - **The status frame names the account.** `runtime_status` gains `account`
38
+ (random key, label, runtime, billing as the login reports it, sign-in state) and `machine_accounts`
39
+ (every account on the machine), plus per-model weekly windows
40
+ (`weekly_model`, e.g. Fable) from Claude Code's `get_usage`.
41
+ - **Fix: the 5-hour window disappeared after five idle hours.** Claude Code
42
+ omits a rolled-over window from its own `rate_limit_event`, reports an idle
43
+ account's 5-hour window with no reset time, and the harness dropped any
44
+ window without a future reset — so an idle agent showed only its weekly
45
+ window. A window with known usage is now always reported: rolled over or not
46
+ started yet, it is `0%` with `resets_at: null`. New observations are merged
47
+ per window kind instead of replacing the whole list.
48
+ - **`prohost agent list`** shows every agent home on the machine (default,
49
+ `$PROHOST_HOME`, and every home a `ai.prohost.agent.*` launchd plist pins),
50
+ its account, and whether its daemon or a harness is running — plus every
51
+ account with its runtime, billing and sign-in state.
52
+ - **`$PROHOST_HOME/run.lock`.** A second `agent run` on the same home exits
53
+ with the pid that holds it, instead of executing every run twice. A lock
54
+ left by a dead process is taken over.
55
+ - **`prohost agent workspace init --developer --repo <checkout>`** (opt-in)
56
+ adds the ProhostAI PR-protocol skill (a pointer to the repo's
57
+ `pr_protocol.md`, not a copy) and the fleet's `gh pr create` gate as a
58
+ `PreToolUse` hook to the agent workspace's `.claude/`.
59
+
7
60
  ## 0.6.0
8
61
 
62
+ The thread now shows what a paired agent is doing, not just that it is doing
63
+ something.
64
+
65
+ - **Tool calls stream into the conversation.** With Claude Code, every
66
+ `tool_use` / `tool_result` in the `--output-format stream-json` stream is
67
+ reported to ProhostAI as it happens — the tool, a one-line preview of its
68
+ input (the command, the path, the query), whether it succeeded, and a short
69
+ line of what came back — and rendered in the thread exactly as an
70
+ in-product AI employee's tool calls are, beneath the "Working on
71
+ \<machine\>…" line. Events are batched on a one-second debounce
72
+ (`POST /v1/agent-runs/{id}/events`, at most 50 per request) and always
73
+ flushed before the reply is posted and before the run is completed.
74
+ - **Best-effort, and additive.** The path comes from the run event
75
+ (`events.path`); a server that doesn't advertise it gets no requests at
76
+ all, and one that answers `404` is told so once per run and then left alone.
77
+ No event failure can fail the run or delay its reply. The 60-second
78
+ heartbeat and its one-line `progress` are unchanged.
79
+ - **What stays on your machine.** Previews are capped at 200 characters and
80
+ only the headline field of a tool's input travels — a `Write`'s file
81
+ content, an `Edit`'s old and new text, never do. The server clips again and
82
+ redacts before anything reaches a browser.
83
+
9
84
  ProhostAI can now show which runtime, model and machine a paired AI employee
10
85
  runs on, and how much of the operator's Claude or Codex subscription is left.
11
86
 
package/README.md CHANGED
@@ -442,6 +442,46 @@ unmodified CLI through its own protocol (`get_usage`,
442
442
  `codex app-server`), which answers with its own login. Account ids, emails and
443
443
  org names are never sent.
444
444
 
445
+ ### Accounts: which login an agent runs on
446
+
447
+ A machine can hold several Claude Code or Codex logins — say a work Max plan
448
+ and a personal one — and each paired agent uses exactly one. Several agents can
449
+ share one account; they then share its limits.
450
+
451
+ ```sh
452
+ prohost agent account add work --runtime claude # prints the sign-in command, waits for it
453
+ # CLAUDE_CONFIG_DIR=~/.prohost-accounts/accounts/claude_code/work claude auth login
454
+ prohost agent install-daemon --exec 'claude -p' --account work
455
+ prohost agent account list
456
+ prohost agent list # every agent here, its account and daemon
457
+ ```
458
+
459
+ - Each account is its own config directory under `~/.prohost-accounts` (or
460
+ `$PROHOST_ACCOUNTS_ROOT`). You sign in with the agent CLI's own login command;
461
+ `prohost` only asks the CLI whether it is signed in and never reads the login.
462
+ - `default` is the CLI's usual `~/.claude` / `~/.codex`, so an agent paired
463
+ before accounts existed keeps working unchanged.
464
+ - The account is applied to every run (and every usage check) separately, so
465
+ picking another one — with `--account`, or in ProhostAI — takes effect from
466
+ the agent's next run, with no restart.
467
+ - **Accounts never switch on their own.** When an account's limit is reached,
468
+ the agent rests until it resets; moving it to another subscription is always
469
+ a person's decision.
470
+ - ProhostAI sees a random id for each account and the label you gave it. Never
471
+ the email, org or anything from the login.
472
+
473
+ Only one `agent run` can serve an agent home at a time: a second one exits,
474
+ naming the pid that holds `$PROHOST_HOME/run.lock`.
475
+
476
+ ### A developer workspace
477
+
478
+ For an agent that works on the ProhostAI codebase, `prohost agent workspace
479
+ init --developer --repo <backend-service checkout>` adds two things to its
480
+ workspace's `.claude/`: a skill that points it at the repo's PR protocol
481
+ (`prohost_agents/support/prompts/pr_protocol.md`, referenced, not copied) and
482
+ the `gh pr create` gate hook the fleet uses. Nothing else about the default
483
+ workspace changes.
484
+
445
485
  ### The trust model — read this once
446
486
 
447
487
  `agent run` is a standing instruction to execute a command on your machine when
@@ -514,6 +554,12 @@ for it:
514
554
  which is what stops the server reaping a run that is still being worked on.
515
555
  Against an older ProhostAI server the heartbeat endpoint answers `404`; the
516
556
  CLI says so once per run and otherwise behaves exactly as it did before.
557
+ - **The thread sees the tool calls.** With Claude Code, each tool the agent
558
+ calls — and whether it succeeded — is reported to ProhostAI as it happens
559
+ and shown in the conversation under the "Working on your machine…" line,
560
+ the way an in-product AI employee's tool calls are. Only a one-line preview
561
+ travels (the command, the path, the query; never a file's contents), and a
562
+ server that doesn't support it is told nothing.
517
563
  - **Cancel works.** Stopping a run in ProhostAI is answered on the next
518
564
  heartbeat: the command's whole process group gets `SIGTERM`, then `SIGKILL`
519
565
  five seconds later, and the run is reported as cancelled. No reply is posted.
@@ -576,6 +622,7 @@ decision to make.
576
622
  | Flag | Default | What it does |
577
623
  | --- | --- | --- |
578
624
  | `--code` | `$PROHOST_PAIRING_CODE` | The one-time pairing code. |
625
+ | `--account` | the home's current account, else `default` | Which Claude Code / Codex login this agent runs on (see Accounts). |
579
626
  | `--webhook-url` | none | Also push events to this HTTPS endpoint. Omit for stream-only, which is the normal laptop setup. |
580
627
  | `--base-url` | `https://connect.prohost.ai` | Public API base. |
581
628
 
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The account a running harness uses — resolved per spawn, switched on request.
3
+ *
4
+ * `agent run` reads the account from `agent.json` at every run and every idle
5
+ * probe rather than once at start-up, so a switch (made here with
6
+ * `--account`, or in ProhostAI through `agent.config_updated`) takes effect on
7
+ * the next run without restarting or reinstalling the daemon.
8
+ */
9
+ import type { AccountRecord, AccountRuntime, CommandRunner, ConfigUpdateOutcome } from './accounts.js';
10
+ import type { AccountsSnapshot } from './runtime_status.js';
11
+ export type AccountResolution = {
12
+ ok: true;
13
+ record: AccountRecord;
14
+ } | {
15
+ ok: false;
16
+ error: string;
17
+ };
18
+ export declare class HarnessAccount {
19
+ readonly runtime: AccountRuntime;
20
+ private readonly env;
21
+ private readonly binary;
22
+ private readonly run;
23
+ /** Label from `agent.json` at start-up; the file wins once it has one. */
24
+ private readonly initialLabel;
25
+ /** Label the most recent run was spawned with — what a run in flight is using. */
26
+ private spawnedLabel;
27
+ /** `requested_at` of the newest pick applied, so a late older one is dropped. */
28
+ private appliedRequestedAt;
29
+ constructor(options: {
30
+ runtime: AccountRuntime;
31
+ /** Environment that locates `$PROHOST_HOME` and the accounts root. */
32
+ env?: NodeJS.ProcessEnv;
33
+ initialLabel?: string;
34
+ /** Agent CLI binary used for sign-in checks. */
35
+ binary?: string;
36
+ run?: CommandRunner;
37
+ });
38
+ /** The label this agent home names now. */
39
+ label(): string;
40
+ /**
41
+ * The account for the next spawn. An account named but missing from this
42
+ * machine is an error: running on some other login would spend a
43
+ * subscription nobody picked.
44
+ */
45
+ resolve(): AccountResolution;
46
+ /** {@link resolve} for a run about to be spawned; remembers which account it got. */
47
+ resolveForSpawn(): AccountResolution;
48
+ /** Whether the chosen account differs from the one the last run was spawned with. */
49
+ changedSinceSpawn(): boolean;
50
+ /** Spawn environment for the current account; empty when it can't be resolved. */
51
+ spawnEnv(): NodeJS.ProcessEnv;
52
+ /**
53
+ * Apply an account picked in ProhostAI. Picks carry `requested_at` (the
54
+ * server stamps them in the order they were made); one older than a pick
55
+ * already applied arrived out of order and is ignored. A pick without a
56
+ * stamp (an older server) always applies.
57
+ */
58
+ apply(key: unknown, requestedAt?: unknown): ConfigUpdateOutcome;
59
+ /**
60
+ * Raise the floor without applying anything: `auth_ok` carries the newest
61
+ * pick's stamp even when nothing is pending, so a live pick still in flight
62
+ * from before the connect — or one a later pick cancelled — is dropped.
63
+ */
64
+ noteRequestedAt(requestedAt: unknown): void;
65
+ /**
66
+ * Accounts for the status frame: this agent's, plus every account on the machine.
67
+ *
68
+ * While a run executes, "this agent's" is the account it was spawned with,
69
+ * not a newer pick — the server would otherwise mark the pick fulfilled and
70
+ * credit that run's capacity to an account it isn't using.
71
+ */
72
+ snapshot(busy?: boolean): Promise<AccountsSnapshot>;
73
+ /** The command that signs the current account in, for a log line. */
74
+ signInHint(): string | undefined;
75
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The account a running harness uses — resolved per spawn, switched on request.
3
+ *
4
+ * `agent run` reads the account from `agent.json` at every run and every idle
5
+ * probe rather than once at start-up, so a switch (made here with
6
+ * `--account`, or in ProhostAI through `agent.config_updated`) takes effect on
7
+ * the next run without restarting or reinstalling the daemon.
8
+ */
9
+ import { DEFAULT_ACCOUNT_LABEL, MAX_REPORTED_ACCOUNTS, accountBlock, accountEnv, applyAccountKey, checkAuth, loadAccounts, resolveAgentAccount, runtimeName, signInCommand, } from './accounts.js';
10
+ import { tryLoadCredentials } from './credentials.js';
11
+ export class HarnessAccount {
12
+ runtime;
13
+ env;
14
+ binary;
15
+ run;
16
+ /** Label from `agent.json` at start-up; the file wins once it has one. */
17
+ initialLabel;
18
+ /** Label the most recent run was spawned with — what a run in flight is using. */
19
+ spawnedLabel;
20
+ /** `requested_at` of the newest pick applied, so a late older one is dropped. */
21
+ appliedRequestedAt;
22
+ constructor(options) {
23
+ this.runtime = options.runtime;
24
+ this.env = { ...process.env, ...options.env };
25
+ this.binary = options.binary;
26
+ this.run = options.run;
27
+ this.initialLabel = options.initialLabel;
28
+ }
29
+ /** The label this agent home names now. */
30
+ label() {
31
+ return tryLoadCredentials(this.env)?.account ?? this.initialLabel ?? DEFAULT_ACCOUNT_LABEL;
32
+ }
33
+ /**
34
+ * The account for the next spawn. An account named but missing from this
35
+ * machine is an error: running on some other login would spend a
36
+ * subscription nobody picked.
37
+ */
38
+ resolve() {
39
+ const label = this.label();
40
+ const record = resolveAgentAccount(label, this.runtime, this.env);
41
+ if (record)
42
+ return { ok: true, record };
43
+ return {
44
+ ok: false,
45
+ error: `this agent is set to the ${runtimeName(this.runtime)} account "${label}", which is not on this machine — ` +
46
+ `add it (prohost agent account add ${label} --runtime ${this.runtime === 'claude_code' ? 'claude' : 'codex'}) ` +
47
+ 'or pick another account for this agent in ProhostAI',
48
+ };
49
+ }
50
+ /** {@link resolve} for a run about to be spawned; remembers which account it got. */
51
+ resolveForSpawn() {
52
+ const resolved = this.resolve();
53
+ if (resolved.ok)
54
+ this.spawnedLabel = resolved.record.label;
55
+ return resolved;
56
+ }
57
+ /** Whether the chosen account differs from the one the last run was spawned with. */
58
+ changedSinceSpawn() {
59
+ return this.spawnedLabel !== undefined && this.spawnedLabel !== this.label();
60
+ }
61
+ /** Spawn environment for the current account; empty when it can't be resolved. */
62
+ spawnEnv() {
63
+ const resolved = this.resolve();
64
+ return resolved.ok ? accountEnv(resolved.record) : {};
65
+ }
66
+ /**
67
+ * Apply an account picked in ProhostAI. Picks carry `requested_at` (the
68
+ * server stamps them in the order they were made); one older than a pick
69
+ * already applied arrived out of order and is ignored. A pick without a
70
+ * stamp (an older server) always applies.
71
+ */
72
+ apply(key, requestedAt) {
73
+ const at = typeof requestedAt === 'string' ? Date.parse(requestedAt) : Number.NaN;
74
+ if (!Number.isNaN(at) && this.appliedRequestedAt !== undefined && at < this.appliedRequestedAt) {
75
+ return { applied: false, reason: 'a newer account pick was already applied' };
76
+ }
77
+ const outcome = applyAccountKey(key, this.runtime, this.env);
78
+ if (outcome.applied && !Number.isNaN(at))
79
+ this.appliedRequestedAt = at;
80
+ return outcome;
81
+ }
82
+ /**
83
+ * Raise the floor without applying anything: `auth_ok` carries the newest
84
+ * pick's stamp even when nothing is pending, so a live pick still in flight
85
+ * from before the connect — or one a later pick cancelled — is dropped.
86
+ */
87
+ noteRequestedAt(requestedAt) {
88
+ const at = typeof requestedAt === 'string' ? Date.parse(requestedAt) : Number.NaN;
89
+ if (Number.isNaN(at))
90
+ return;
91
+ if (this.appliedRequestedAt === undefined || at > this.appliedRequestedAt)
92
+ this.appliedRequestedAt = at;
93
+ }
94
+ /**
95
+ * Accounts for the status frame: this agent's, plus every account on the machine.
96
+ *
97
+ * While a run executes, "this agent's" is the account it was spawned with,
98
+ * not a newer pick — the server would otherwise mark the pick fulfilled and
99
+ * credit that run's capacity to an account it isn't using.
100
+ */
101
+ async snapshot(busy = false) {
102
+ const label = busy && this.spawnedLabel ? this.spawnedLabel : this.label();
103
+ const record = resolveAgentAccount(label, this.runtime, this.env);
104
+ const resolved = record ? { ok: true, record } : { ok: false, error: '' };
105
+ const records = loadAccounts(this.env);
106
+ // The current account first, so a machine with more than the cap still
107
+ // reports the one this agent is on.
108
+ const ordered = resolved.ok
109
+ ? [resolved.record, ...records.filter((r) => r.key !== resolved.record.key)]
110
+ : records;
111
+ const all = await Promise.all(ordered.slice(0, MAX_REPORTED_ACCOUNTS).map(async (record) => {
112
+ // Only the login of this runtime's CLI is checked with this binary;
113
+ // another runtime's accounts are checked with that CLI's default name.
114
+ const check = await checkAuth(record, {
115
+ binary: record.runtime === this.runtime ? this.binary : undefined,
116
+ run: this.run,
117
+ });
118
+ return accountBlock(record, check.state, check.billing);
119
+ }));
120
+ const current = resolved.ok ? all.find((a) => a.key === resolved.record.key) : undefined;
121
+ // More accounts than a frame carries (a hand-edited registry): send none
122
+ // rather than a partial list the server would prune to.
123
+ return { current, all: ordered.length > MAX_REPORTED_ACCOUNTS ? undefined : all };
124
+ }
125
+ /** The command that signs the current account in, for a log line. */
126
+ signInHint() {
127
+ const resolved = this.resolve();
128
+ return resolved.ok ? signInCommand(resolved.record) : undefined;
129
+ }
130
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Brain accounts — which Claude Code or Codex login a paired agent runs on.
3
+ *
4
+ * A machine can hold several logins ("work", "personal"), each in its own
5
+ * config directory: `CLAUDE_CONFIG_DIR` for Claude Code, `CODEX_HOME` for
6
+ * Codex. Every agent home on the machine uses exactly one of them, named in its
7
+ * `agent.json` as `account`, and several agents may share one (and so share its
8
+ * quota). The implicit `default` account is today's `~/.claude` / `~/.codex`,
9
+ * so an agent paired before accounts existed keeps running exactly as it did.
10
+ *
11
+ * **This module never reads a credential.** Signing in is done by the operator
12
+ * running the agent CLI's own login command with the account's directory; we
13
+ * only ever ask the unmodified CLI whether it is signed in (`claude auth status`
14
+ * → `loggedIn`, `codex login status` → its exit code) and keep nothing else
15
+ * from the answer — not the email, org or account id either one prints.
16
+ *
17
+ * **Accounts never switch on their own.** An exhausted subscription makes the
18
+ * agent rest until its window resets; only an operator (here, or through the
19
+ * picker in ProhostAI, which arrives as `agent.config_updated`) moves an agent
20
+ * to another account.
21
+ *
22
+ * The registry lives in a machine-wide root shared by every agent home —
23
+ * `~/.prohost-accounts`, or `$PROHOST_ACCOUNTS_ROOT` — so `account add` once
24
+ * serves every agent on the machine.
25
+ */
26
+ /** Server → CLI: an operator picked another account for this agent in ProhostAI. */
27
+ export declare const EVENT_AGENT_CONFIG_UPDATED = "agent.config_updated";
28
+ /** Label of the implicit account that is the agent CLI's own default config dir. */
29
+ export declare const DEFAULT_ACCOUNT_LABEL = "default";
30
+ /** Most accounts reported to the server in one frame (the server caps it too). */
31
+ export declare const MAX_REPORTED_ACCOUNTS = 16;
32
+ export type AccountRuntime = 'claude_code' | 'codex';
33
+ export type AccountBilling = 'subscription' | 'api_key';
34
+ export type AuthState = 'ok' | 'expired' | 'unknown';
35
+ export interface AccountRecord {
36
+ /** Random UUID made here when the account is created — the only identity the server sees. */
37
+ key: string;
38
+ /** Operator-chosen, unique per runtime on this machine. */
39
+ label: string;
40
+ runtime: AccountRuntime;
41
+ billing: AccountBilling;
42
+ /** Config directory for this login; `null` for the implicit default (the CLI's own default). */
43
+ dir: string | null;
44
+ created_at: string;
45
+ }
46
+ /** An account as it goes on the wire (`runtime_status.account` / `machine_accounts[]`). */
47
+ export interface AccountBlock {
48
+ key: string;
49
+ label: string;
50
+ runtime: AccountRuntime;
51
+ billing: AccountBilling;
52
+ auth_state: AuthState;
53
+ }
54
+ export declare class AccountError extends Error {
55
+ }
56
+ export declare function isValidLabel(label: string): boolean;
57
+ /** `claude` → `claude_code`; anything else that is not `codex` is `undefined`. */
58
+ export declare function parseAccountRuntime(value: string | undefined): AccountRuntime | undefined;
59
+ /** Machine-wide root shared by every agent home. */
60
+ export declare function accountsRoot(env?: NodeJS.ProcessEnv): string;
61
+ /** Every account on this machine. An unreadable registry is an empty one. */
62
+ export declare function loadAccounts(env?: NodeJS.ProcessEnv): AccountRecord[];
63
+ /**
64
+ * This computer's id: a random UUID made once and kept in the machine-wide
65
+ * accounts root, so every agent home on the machine sends the same one. It is
66
+ * what ProhostAI keys a machine's accounts by — the label (hostname by
67
+ * default) is display only, since two computers can share one. Nothing about
68
+ * the computer goes into it. `undefined` when it can't be stored, in which
69
+ * case the agent reports no accounts rather than risk another machine's.
70
+ */
71
+ export declare function machineId(env?: NodeJS.ProcessEnv): string | undefined;
72
+ export declare function findAccount(label: string, runtime: AccountRuntime, env?: NodeJS.ProcessEnv): AccountRecord | undefined;
73
+ /**
74
+ * The implicit default account for a runtime, created on first use so it has
75
+ * a stable key the server can recognise across restarts.
76
+ */
77
+ export declare function ensureDefaultAccount(runtime: AccountRuntime, env?: NodeJS.ProcessEnv): AccountRecord;
78
+ /** Create an account and its config directory. Signing in is the operator's next step. */
79
+ export declare function addAccount(options: {
80
+ label: string;
81
+ runtime: AccountRuntime;
82
+ billing?: AccountBilling;
83
+ }, env?: NodeJS.ProcessEnv): AccountRecord;
84
+ /**
85
+ * Forget an account and delete its config directory (the login inside it goes
86
+ * with it). The implicit default can't be removed — it is the CLI's own login.
87
+ */
88
+ export declare function removeAccount(label: string, runtime: AccountRuntime, env?: NodeJS.ProcessEnv): AccountRecord;
89
+ export declare function runtimeName(runtime: AccountRuntime): string;
90
+ /** Environment a spawn needs to run as this account. Empty for the default. */
91
+ export declare function accountEnv(record: AccountRecord | undefined): NodeJS.ProcessEnv;
92
+ /** POSIX single-quote a path for a shell command (pasted, or run as a hook). */
93
+ export declare function shellQuote(value: string): string;
94
+ /** The exact command that signs this account in, for the operator to run. */
95
+ export declare function signInCommand(record: AccountRecord): string;
96
+ export interface AuthCheck {
97
+ state: AuthState;
98
+ /** What the login itself says it bills to, when it says. */
99
+ billing?: AccountBilling;
100
+ }
101
+ /** Runs one short command with extra env; resolves exit code and stdout. Never rejects. */
102
+ export type CommandRunner = (command: string, args: string[], env: NodeJS.ProcessEnv) => Promise<{
103
+ code: number | null;
104
+ stdout: string;
105
+ }>;
106
+ export declare const defaultCommandRunner: CommandRunner;
107
+ /**
108
+ * Ask the agent CLI whether this account is signed in.
109
+ *
110
+ * Claude Code: `claude auth status --json` — only `loggedIn` and `authMethod`
111
+ * are read (the same JSON carries the email and org, which are dropped here).
112
+ * Codex: `codex login status` — exit 0 means signed in; its one line says
113
+ * whether that is ChatGPT or an API key. A CLI that can't be run, or answers
114
+ * something we don't recognise, is `unknown` — never `expired` on a guess.
115
+ */
116
+ export declare function checkAuth(record: AccountRecord, options?: {
117
+ binary?: string;
118
+ run?: CommandRunner;
119
+ }): Promise<AuthCheck>;
120
+ /**
121
+ * Poll until the operator has finished signing in, or until the deadline.
122
+ * Resolves the last check either way.
123
+ */
124
+ export declare function waitForSignIn(record: AccountRecord, options: {
125
+ timeoutMs: number;
126
+ intervalMs?: number;
127
+ check?: (record: AccountRecord) => Promise<AuthCheck>;
128
+ sleep?: (ms: number) => Promise<void>;
129
+ now?: () => number;
130
+ }): Promise<AuthCheck>;
131
+ /**
132
+ * Wire block for one account. `billing` is what the login itself reported
133
+ * when it said; the value given at `account add` is only the fallback.
134
+ */
135
+ export declare function accountBlock(record: AccountRecord, state: AuthState, billing?: AccountBilling): AccountBlock;
136
+ /**
137
+ * The account an agent home runs on: `agent.json`'s `account`, or the default.
138
+ *
139
+ * `undefined` when the home names an account that is not on this machine —
140
+ * the caller refuses to run rather than quietly using another subscription.
141
+ */
142
+ export declare function resolveAgentAccount(label: string | undefined, runtime: AccountRuntime, env?: NodeJS.ProcessEnv): AccountRecord | undefined;
143
+ export type ConfigUpdateOutcome = {
144
+ applied: true;
145
+ record: AccountRecord;
146
+ changed: boolean;
147
+ } | {
148
+ applied: false;
149
+ reason: string;
150
+ };
151
+ /**
152
+ * Apply an account picked in ProhostAI (`agent.config_updated` or
153
+ * `auth_ok.agent_config`): find it on this machine and persist it to the agent
154
+ * home's `agent.json`, so it is used from the next run and survives a restart.
155
+ *
156
+ * An unknown key, or one belonging to another runtime, changes nothing.
157
+ */
158
+ export declare function applyAccountKey(key: unknown, runtime: AccountRuntime, env?: NodeJS.ProcessEnv): ConfigUpdateOutcome;
159
+ /** Persist an account choice made on this machine (`--account`). Validates it exists. */
160
+ export declare function setAgentAccount(label: string, runtime: AccountRuntime | undefined, env?: NodeJS.ProcessEnv): void;
161
+ /** Whether the registry file exists at all (for `agent list`'s empty state). */
162
+ export declare function hasRegistry(env?: NodeJS.ProcessEnv): boolean;