export interface MachineConfig { /** Base URL of the Vincentt backend API (e.g. https://api.vincentt.studio). */ apiUrl: string; /** Console origin that serves /cli-auth (e.g. https://vincentt.app); used by `login`. */ consoleUrl: string; /** Personal access token (bearer) for machine auth. */ pat: string; /** Org slug commands act in when no --org flag or VINCENTT_ORG env is given. */ activeOrg: string; /** * Durable authorization for unattended `dx --send`. Written only by * `vincentt dx enable`, which requires a TTY. Cleared by `disable` and by `logout`. */ dxSendEnabled?: boolean; /** * ISO8601 UTC, when enablement was granted. Its AGE is the mitigation for having * no expiry, so an unparseable value fails closed rather than printing. */ dxSendEnabledAt?: string; } export interface ProjectBinding { /** The backend Project id this working tree publishes to. */ projectId: string; /** The project's slug — its . host. Informational (server-authoritative). */ slug: string; /** Optional per-project API override; else the machine config's apiUrl. */ apiUrl?: string; } export declare const MACHINE_CONFIG_PATH: string; export declare function projectBindingPath(cwd: string): string; export declare function loadMachineConfig(): Promise>; /** * Merge a patch into ~/.vincentt/config.json (used by `harness login` to persist * a freshly minted PAT). Preserves any other fields already present. The file * holds a bearer credential, so the dir/file get owner-only perms (enforced on * every write, even when the file pre-existed with looser perms). */ export declare function writeMachineConfig(patch: Partial): Promise; /** * Set or clear unattended-send enablement (`vincentt dx enable` / `disable`). * * A dedicated function rather than a `writeMachineConfig` patch because clearing * has to DELETE the keys. A patch of `{dxSendEnabled: undefined}` happens to work * — JSON.stringify drops undefined values — but only by a coincidence of the * serializer, and the read of a key that survived would be the difference between * off and on. */ export declare function writeDxEnablement(next: { at: string; } | undefined): Promise; /** * Remove the stored PAT from ~/.vincentt/config.json (used by `vincentt logout`), * preserving apiUrl and any other fields. Returns whether a token was present. * * Unattended-send enablement goes with the PAT. This function preserves every * other field by design, which would leave a disconnected machine still armed to * transmit for whoever signs in next — a creator who disconnects a machine has * disconnected it. */ export declare function clearMachineToken(): Promise<{ path: string; hadToken: boolean; }>; export declare function loadProjectBinding(cwd: string): Promise; export interface LocatedBinding { binding: ProjectBinding; /** The directory holding `.vincentt/project.json` (cwd, or an ancestor). */ dir: string; /** Absolute path of the binding file, for messages that name it. */ path: string; } /** * Locate the binding by PRECEDENCE, never inference: cwd, then each ancestor up * to and including the git root. Nothing is guessed from the folder name and * there is no "you only have one project" fallback — a wrong-but-plausible * binding is silent retargeting, which B12 forbids. * * The walk stops at the git root so a binding in a parent repo cannot capture an * unrelated sibling tree, and at the filesystem root when there is no repo. */ export declare function locateProjectBinding(cwd: string): Promise; /** * Write the per-tree binding and make sure `.vincentt/` is gitignored (so the * secret-free-but-tenant-scoped binding never rides a commit or `git archive`). */ export declare function writeProjectBinding(cwd: string, binding: ProjectBinding): Promise; /** Remove the per-tree binding so the directory can be re-created/rebound. */ export declare function clearProjectBinding(cwd: string): Promise<{ path: string; hadBinding: boolean; }>; export interface ResolvedConfig { apiUrl: string; pat: string; } export declare const DEFAULT_API_URL = "http://localhost:5051"; export interface ResolveConfigOptions { /** Honor a binding's apiUrl override (the `--allow-custom-api` flag). */ allowCustomApi?: boolean; /** Where advisory lines go. Defaults to a no-op so library callers stay quiet. */ warn?: (line: string) => void; } /** * Decide which API host a call goes to, given every source that can name one. * * The rule is PROVENANCE, not location. A machine config and an env var are set by * the person running the command; `.vincentt/project.json` travels with a * repository, so anyone who can land a commit can set it — and since the PAT comes * from machine-global config, honoring the file's host by default sends the user's * bearer token to a host chosen by whoever wrote the repo. So the binding's apiUrl * is ignored unless `--allow-custom-api` states intent, INVERTING the precedence * shipped in 0.1.x. */ export interface ApiUrlDecision { /** The host the request actually goes to. */ apiUrl: string; /** * The host the caller's OWN configuration names, whether or not it won. The * advisory compares against this, not against the decision — on the flagged * path the decision IS the binding, so reading it back would print the * override twice and claim it was the machine config. */ configuredUrl: string; ignoredBindingApiUrl?: string; usedBindingApiUrl?: string; } export declare function decideApiUrl(input: { bindingApiUrl?: string; envApiUrl?: string; machineApiUrl?: string; allowCustomApi?: boolean; }): ApiUrlDecision; /** * Resolve the API URL + PAT for a machine call. Throws a clear, actionable error * when no PAT is configured. When a binding's apiUrl diverges from the configured * one, the safe branch has ALREADY been taken by the time the advisory prints — * it reports a decision rather than asking for one. */ export declare function resolveConfig(cwd: string, opts?: ResolveConfigOptions): Promise; /** * The Flow L12 transcripts. Ignoring an override is the loud case (both hosts on * their own aligned lines, the mechanism named, and where to go look); honoring one * on purpose is two quiet lines, because a creator who typed the flag has already * stated intent and re-reading the lecture every run is how advisories get * filtered out mentally — taking the un-flagged one with them. */ export declare function apiUrlAdvisory(decided: ApiUrlDecision, ctx: { bindingPath: string; }): string[];