/** * Thin HTTP client for the dashboard's context REST surface * (`/api/v1/context/*`). It is the *only* thing the MCP server talks to — the * CLI never opens a DB connection (server owns data, CLI owns the machine). * * Mirrors the error handling of `rules/pull-bundle.ts`: throw on a non-2xx * status (with any response detail), on invalid JSON, and on a response whose * shape does not match the protocol schema. Every response is validated with * the shared `@auden.to/protocol` `parse*` wrapper so a drifting server contract * fails loudly instead of feeding an agent malformed data. */ import type { Bundle, BundleListResponse, BundleMemberType, ContextItem, ContextItemsResponse, ContextItemVersionsResponse, InboxArchiveResponse, InboxListResponse, PushContentType } from '@auden.to/protocol'; export type ContextClientConfig = { dashboardUrl: string; token: string; fetchFn?: typeof fetch; }; export type PullContextParams = { bundle: string; query?: string | undefined; types?: string[] | undefined; limit?: number | undefined; }; export type PushContextParams = { bundle: string; name: string; contentType: PushContentType; content: string; /** Present → update the canonical item in place (PUT); absent → create (POST). */ itemId?: string | undefined; origin?: string | undefined; /** * Optimistic-concurrency precondition for an update (the `version` last read * from the item). When it no longer matches the server's current version the * PUT is rejected with 409 instead of clobbering a concurrent edit. Update * only — ignored on create. */ expectedVersion?: number | undefined; /** * Ids of the caller-owned context items the pushed document was produced * from. Provenance: the server validates them against the caller's own items * and stores them on the item. Accepted on create and update alike — an * update replaces the stored ids, so they describe the body as it now is, * and an empty array on an update clears them. */ sourceItemIds?: string[] | undefined; }; export type PushContextResult = { item: ContextItem; /** True when a new item was created, false when an existing one was updated. */ created: boolean; }; export type CreateBundleParams = { slug: string; name: string; }; /** * Membership addressing mirrors the `bundle_items` composite key: `itemType` is * part of identity, so both verbs carry it — an `itemId` alone cannot address a * `guide` membership. */ export type BundleMemberParams = { bundle: string; itemId: string; itemType: BundleMemberType; }; export type ListInboxParams = { /** ISO timestamp lower bound (already resolved from any relative window). */ since?: string | undefined; types?: string[] | undefined; }; export type ListItemVersionsParams = { bundle: string; itemId: string; /** Page backwards: return versions older than this one. */ before?: number | undefined; limit?: number | undefined; }; export type ContextClient = { listBundles(): Promise; createBundle(params: CreateBundleParams): Promise; /** * Add an existing canonical item to a bundle. Idempotent — `attached` is * false when the membership was already there. */ attachMember(params: BundleMemberParams): Promise<{ attached: boolean; }>; /** * Remove a membership. Membership-only: it never touches the archive marker, * so the item survives in every other bundle it belongs to. * * The server's tagged "not a member" 404 is convergence, not a failure — the * membership is already absent, which is the state the caller asked for — so * that one case resolves `{ detached: false }` rather than throwing. Every * other 404 the route can return (unknown bundle, unknown or foreign item) * means the request was wrong and still throws. */ detachMember(params: BundleMemberParams): Promise<{ detached: boolean; }>; pullContext(params: PullContextParams): Promise; pushContext(params: PushContextParams): Promise; listInbox(params: ListInboxParams): Promise; archiveInbox(ids: string[]): Promise; /** * A doc's saved versions, newest first — **metadata only**, no bodies. The * docs phase uses the `contentHash` on each to recognize a local file as an * unmodified copy of some past version, which is what lets it tell a stale * file (safe to overwrite) from an edited one without keeping a baseline hash * of its own. * * Membership-gated: the server 404s once the item leaves the bundle * (`versionAccess.ts` → `isGuideFileInBundle`), so this cannot answer * questions about an orphaned file. */ listItemVersions(params: ListItemVersionsParams): Promise; }; /** * A non-2xx response, carrying the status — and, when the server sent one, a * machine-readable `code` — as fields. * * The status used to live only inside the message string, which meant the one * caller that has to branch on a specific code — the docs phase, where a 409 * from the `expectedVersion` precondition *is* the both-changed signal rather * than a failure — would have had to regex the message. That reads as working * right up until the message is reworded, and it cannot distinguish a real 409 * from a server that mentioned "409" in an error body. * * `code` extends that to statuses one route uses for several distinct causes: * the members DELETE answers "not a member", "no such bundle", and "no such * item" all with 404, and only the first is convergence. */ export declare class ContextHttpError extends Error { readonly status: number; readonly code: string | null; constructor(message: string, status: number, code?: string | null); } /** True for a version-precondition rejection (`expectedVersion` no longer current). */ export declare function isVersionConflict(error: unknown): boolean; /** * True only for "that item is not in that bundle" — the members DELETE tags it, * because it also 404s on an unknown bundle and on an item id that names * nothing the caller owns. Those are wrong requests, not convergence: reading * them as "already detached" would report a typo'd id as a successful filing * while the real item sat where it was. */ export declare function isNotAMember(error: unknown): boolean; export declare function createContextClient(config: ContextClientConfig): ContextClient; //# sourceMappingURL=client.d.ts.map