import { MachineCredentialLock } from "./credential-lock.js"; import type { EngineAdapter } from "./engine-adapter.js"; import { type RevokeTokenFn } from "./login-commit.js"; import type { MachineCredentialStore } from "./machine-credentials.js"; /** * Agent-driven enrollment (design 06/09 D13) — the engine-agnostic port of the CLIs' `enroll` flow. * Redeem a one-time enrollment code (minted by the server's `enroll_engine_plugin` tool from an * already-authorized agent session) for a plugin credential, with NO browser hop: plant the credential * in the SHARED machine store, record the enrolled server target in the committable project marker, and * upsert the `/p/` routing segment into existing project-local agent configs. * * Two carry-forwards from the design: * - **v2 pin (defect B5):** the pin is derived with {@link derivePinV2} (the `\`→`/` normalization), * which REPLACES the Unity CLI's local `projectRootForIdentity` `\`→`/` workaround — one algorithm * for every engine, so a Windows `path.resolve` backslash root matches the plugin's forward-slash * hash. * - **serverTarget = AS root (b2 review MED-2):** the marker records * {@link EngineAdapter.loginServerTarget}(redeemed target) — the AS root, NEVER a pinned * `/mcp/p/` hub URL, so the credential's refresh base is correct. */ /** The default hosted authorization-server base. */ export declare const DEFAULT_CLOUD_BASE_URL = "https://ai-game.dev"; /** Raised on any enrollment-redeem failure. Carries the HTTP status when one was received. */ export declare class EnrollmentError extends Error { readonly status?: number; constructor(message: string, status?: number); } /** Credential material returned by a successful `/api/auth/enroll/redeem`. */ export interface RedeemedCredential { accessToken?: string; refreshToken?: string; expiresAt?: string; serverTarget?: string; subject?: string; /** * The OAuth client id the credential was minted under (O5/a6 adds `client_id` to the redeem * response). Optional-but-preferred: older servers omit it, and the store never INFERS one * (04 §1) — absent here means absent in the stored family. */ clientId?: string; } export interface RedeemOptions { /** Authorization-server base; defaults to {@link DEFAULT_CLOUD_BASE_URL}. */ baseUrl?: string; /** `fetch` injection (tests). */ fetchImpl?: typeof fetch; /** Request timeout (ms); defaults to 30s. */ timeoutMs?: number; /** Injectable clock (ms); defaults to `Date.now`. */ now?: () => number; } /** * Normalize the redeem response. The AS's JSON key casing is not re-derivable from this repo, so both * snake_case and camelCase are accepted defensively; `expires_in` seconds convert to an absolute * `expiresAt` ISO timestamp when no explicit `expires_at` is present. */ export declare function normalizeRedeemResponse(data: Record, now?: () => number): RedeemedCredential; /** * Redeem an enrollment code against `POST /api/auth/enroll/redeem` with body `{enroll_code}`. * The code travels ONLY in the request body (never a query string). A non-2xx surfaces as an actionable * {@link EnrollmentError} (invalid/expired/already-used all return a uniform server error). */ export declare function redeemEnrollmentCode(code: string, opts?: RedeemOptions): Promise; /** * Resolve the enrollment code from `--enroll ` (argv) or `--enroll-stdin` (stdin), enforcing * mutual exclusion. `readStdin` is invoked ONLY in stdin mode, so the code never lands in argv/history. */ export declare function resolveEnrollCode(opts: { enroll?: string; enrollStdin?: boolean; }, readStdin: () => string): string; export interface PinUpsertResult { updatedFiles: string[]; } /** * Upsert the `/p/` routing segment into every EXISTING project-local JSON agent config that * carries the adapter's server entry with a `url` / `serverUrl`. User-global configs (Claude Desktop, * Antigravity, Cline, Copilot CLI) are never touched; TOML (Codex) is left to its own configurator. * Returns the files actually rewritten. */ export declare function upsertProjectPinIntoConfigs(projectRoot: string, pin: string, serverName: string): PinUpsertResult; export interface RunEnrollOptions { code: string; projectPath: string; adapter: EngineAdapter; store: MachineCredentialStore; /** The 04 §2 cross-process store lock; defaults to one on the store's own directory. */ lock?: MachineCredentialLock; baseUrl?: string; fetchImpl?: typeof fetch; now?: () => number; /** * D6/F7 account-switch confirmation (review fix B1): called when the redeemed credential's * `sub` differs from the store's subject. ABSENT ⇒ a mismatch is DECLINED (fail closed — on a * CI runner an unconfirmable switch must abort, `--yes`-gated on the CLIs). */ confirmAccountSwitch?: (info: { storedSubject: string; newSubject: string; }) => boolean | Promise; /** Injectable best-effort revoker; defaults to RFC 7009 against the redeemed serverTarget. */ revokeToken?: RevokeTokenFn; onWarning?: (message: string) => void; } export type RunEnrollResult = /** Redeem + store commit + project marker + pin upsert all completed. */ { status: "enrolled"; serverTarget: string; pin: string; credentialPath: string; markerPath: string; pinnedConfigs: string[]; } /** * D6/F7 decline (review fix B1): the machine is authorized as a DIFFERENT account and the * switch was not confirmed. The just-redeemed family was revoked best-effort; the store, the * project marker, and the agent configs are all untouched. */ | { status: "switch-declined"; storedSubject: string; newSubject: string; } /** The store's subject changed between the guard evaluation and the write hold (B2b); retry. */ | { status: "aborted"; reason: "guard-premise-changed"; }; /** * Execute the full enrollment side effect: redeem → persist the plugin credential to the SHARED machine * store → write the project marker with the AS-root server target (MED-2) → upsert the v2 pin (B5 fix) * into existing project-local configs. On a redeem failure NOTHING is written. * * The persist is a **plugin-family write under the 04 §2 lock** (enroll is the browser-less * tools-only mint path — F10): `families.plugin` (+ v1 mirror) carries the redeemed tokens, the * response's `client_id` when the server provides one (O5/a6 — never inferred), and * `scope=mcp:plugin`; any OTHER family already on the machine (e.g. an agent family) is * preserved. `subject` is written from the response's `sub` and simply omitted when unknown. * `replaceUnreadable` stays set — enrolling IS an explicit re-authorization, so it may replace * an unreadable store (04 §1; the pre-v2 bare-write path had the same semantic). * * **The D6/F7 account-switch guard applies here too (review fix B1).** Post-a6 the redeem * response carries `sub`; redeeming a code for account B on a machine authorized as A is an * account switch, and without the guard it would silently produce a mixed-account store * (subject B beside A's agent family). The persist routes through the SAME guard primitive as * every other login surface: mismatch ⇒ confirm-required; decline (or no confirm callback — * fail closed) ⇒ revoke the just-redeemed family best-effort and abort with NOTHING written * (no store write, no project marker, no pin upsert); confirm ⇒ revoke A's families and * REPLACE the store (single-account, D6). Pre-a6 servers return no `sub` — nothing to compare, * today's merge behavior is kept (F7.3). */ export declare function runEnroll(opts: RunEnrollOptions): Promise; //# sourceMappingURL=enroll.d.ts.map