/** Host environment the SDK is running inside. */ type Host = "claude_code" | "codex" | "openclaw" | "antigravity" | "copilot_cli" | "grok_cli" | "generic"; /** Creative formats the serve API can return. Text always renders; rich * media degrades to the text fallback when the terminal can't display it. */ type CreativeFormat = "text" | "logo" | "image" | "gif" | "video" | "audio"; /** Creative payload. `mediaUrl`/`textFallback` accompany non-text formats. */ interface Creative { format: CreativeFormat; title: string; body: string; cta?: string; /** Hosted asset URL for image/gif/video/audio creatives. */ mediaUrl?: string; /** pixel-art variant authored at the terminal grid (painter fast path) */ mediaPixelUrl?: string; /** optional audio track that plays when the card renders (any format) */ audioUrl?: string; /** Upload-time text fallback rendered when rich media can't display. */ textFallback?: string; } /** A served sponsor blip. `clickUrl` is the per-impression tracked redirect. */ interface Blip { impressionId: string; /** Campaign identity — used for LOCAL politeness (dismiss cooldown). */ campaignId?: string; clickUrl: string; sponsorLabel: string; creative: Creative; /** Render-proof from the serve response: HMAC binding this impression to the * serve that issued it. Echoed back on the impression + engagement reports so * the server can verify they came from a real serve. Optional — older servers * don't return it; when absent the SDK simply omits it from reports. */ renderToken?: string; } /** Publisher funding fallback rendered when no campaign fills. */ interface Fallback { title: string; body: string; url: string; } /** Server-controlled SDK config (cached locally between fetches). */ interface RemoteConfig { minIntervalSeconds: number; serveTimeoutMs: number; preloadPoolSize: number; killSwitch: boolean; /** How long a painted card stays on screen, in seconds (admin-tunable). */ displayDurationSeconds: number; /** Minimum SDK version the platform wants running. A runtime below this * force-updates immediately (bypassing the daily cycle) — the lever to push * a critical fix without waiting. Empty = no floor. */ minSdkVersion: string; } /** Response of POST /v1/serve. */ interface ServeResponse { fill: boolean; blip?: Blip; fallback?: Fallback | null; config: RemoteConfig; /** * Hooks the package owner selected for this host. The SDK registers every * host hook as a trigger but only RENDERS on these. Absent = the package * never saved a placement selection (unrestricted, legacy). */ allowedPlacements?: string[]; /** Dev-mode only: the dev's compute balance (micro-USD), drives auto-flip routing. */ computeBalanceMicroUsd?: number; /** Dev-mode only: total compute granted this week (micro-USD) — HUD % denominator. */ weeklyGrantMicroUsd?: number; /** Dev-mode only: compute earned since the last payout (micro-USD) — shown as * "pending" (becomes spendable at the next weekly grant). */ pendingMicroUsd?: number; } /** Response of POST /v1/serve/batch. */ interface BatchResponse { blips: Blip[]; fallback?: Fallback | null; config: RemoteConfig; allowedPlacements?: string[]; } /** Options accepted by every public `sponsor.*` call. */ interface SponsorOptions { publisherKey: string; packageName: string; placement: string; host?: Host; /** Override the API origin (default https://api.opencrater.to, or OPENCRATER_API_ORIGIN). */ apiOrigin?: string; } /** A preloaded blip waiting in the local pool. */ interface PooledBlip { blip: Blip; /** Epoch ms after which this pooled blip must not be rendered. */ renderBy: number; } /** Persistent on-disk state (~/.config/opencrater/state.json). */ interface OpenCraterState { installId: string; /** Epoch ms of the last rendered card (frequency cap anchor). 0 = never. */ lastShownAt: number; optOut: boolean; /** Whether the one-time first-run disclosure line has been shown. */ disclosureShown: boolean; cachedConfig: RemoteConfig | null; /** Epoch ms when cachedConfig was fetched. 0 = never. */ configFetchedAt: number; pool: PooledBlip[]; /** * Per-package render gate, cached from serve responses: package name → * hooks its owner selected. A package with no entry is unrestricted. */ allowedPlacements: Record; /** Rolling anonymized session topics (recsys signal), recency-stamped. */ sessionTopics: { topic: string; at: number; }[]; /** Cached developer-attribute tags (ns:value) detected from the project, * keyed by the cwd they were scanned from, with a timestamp for TTL. */ attrTags?: string[]; attrTagsCwd?: string; attrTagsAt?: number; /** * Publisher integrations wired on this machine: each is the (key, package) * a `opencrater on --key … --package …` ran with. The auto-integrator * re-wires every detected host for each of these (incl. hosts the user * installs LATER), so a new host gets connected on its own. */ integrations: { key: string; package: string; }[]; /** CLI device token from `opencrater login` (compute wallet). null = not signed in. */ devToken: string | null; /** * Auto-flip routing state (M5): when compute remains, the harness is pointed * at the gateway; when depleted, the dev's own config is restored. `active` * tracks whether we've flipped; `savedEnv` is the original harness env we * snapshotted (per key, null = the key was absent) so we restore it exactly. * null = never engaged. */ computeRouting: { active: boolean; savedEnv: Record; } | null; /** * Compute-card cadence/throttle bookkeeping (see compute-cards.ts): * turns — turn-end fires seen (drives the HUD's every-3-turns gate) * lastLimitAt — epoch ms of the last "limit" card (30-min throttle) * lastDepletedAt — epoch ms of the last "depleted" card (30-min throttle) * lastBackupAt — epoch ms of the last "backup" nudge (4-hour throttle) */ computeCards: { turns: number; lastLimitAt: number; lastDepletedAt: number; lastBackupAt: number; } | null; /** * The most recently rendered Blip's click target, persisted so the keyboard * `opencrater open` command can launch it even after the card has scrolled * out of view (the painter.lock is deleted when the painter exits, so it * can't be the source of truth here). `url` is the short /k/ * click route — opening it logs the click and redirects, so attribution * matches a real click on the card. null = no Blip has been shown yet. */ lastImpression: { id: string; url: string; at: number; } | null; } declare const DEFAULT_CONFIG: RemoteConfig; /** Claude Code hook events OpenCrater supports as placements. */ declare const HOOK_CATALOG: readonly ["SessionStart", "SessionEnd", "Stop", "PostToolUse", "Notification"]; type HookEvent = (typeof HOOK_CATALOG)[number]; interface RenderCardInput { /** e.g. "Neon" — rendered as "Sponsored · Neon". Empty -> just "Sponsored". */ sponsorLabel: string; title: string; body: string; url: string; cta?: string; /** Header override for NON-Blip system cards (e.g. "OpenCrater"). When set, it * replaces the "Sponsored · …" label entirely (compute cards aren't Blips). */ label?: string; } interface RenderOptions { /** Terminal column count; the card caps at min(width, 60). */ width?: number; color?: boolean; hyperlinks?: boolean; /** Prepend the one-time first-run disclosure line. */ disclosure?: boolean; } /** * Render a rounded box-drawing Blip. Pure string-in/string-out * (no I/O) so it is trivially testable. Always labeled "Sponsored". */ declare function renderCard(card: RenderCardInput, opts?: RenderOptions): string; /** * Terminal capability detection for inline images. * * Pure functions over an injected env/TTY flag so the matrix is trivially * testable. Detection is deliberately conservative: emitting graphics escape * sequences at a terminal that does not understand them prints garbage, which * violates the SDK's "never disturb the host tool" contract. */ type Env = Record; /** Inline-image protocol the current terminal understands. */ type ImageProtocol = "iterm2" | "kitty" | "none"; /** * Detect which inline-image protocol (if any) the terminal supports. * * - iTerm2 (`TERM_PROGRAM=iTerm.app` or `LC_TERMINAL=iTerm2`) → "iterm2" * - WezTerm (`TERM_PROGRAM=WezTerm`) → "iterm2" (it implements that protocol) * - Kitty (`TERM=xterm-kitty` or `KITTY_WINDOW_ID`) → "kitty" * - tmux/screen → "none": both re-wrap escape sequences, and we cannot verify * passthrough (`allow-passthrough`) from the environment alone. * - Anything else, or no TTY → "none". */ declare function detectImageProtocol(env?: Env, isTTY?: boolean): ImageProtocol; /** * The text card for a blip of ANY creative format (pure, sync — safe for the * non-TTY hook path, which must never fetch media): * - text → the standard card * - image/gif → standard card with body swapped for the text fallback * - video → text fallback card, CTA defaults to "▶ Watch" * - audio → text fallback card, CTA defaults to "🔊 Listen" * The click URL is always carried, so the link stays reachable everywhere. */ declare function textCardForBlip(blip: Blip): RenderCardInput; interface MediaRenderOptions extends RenderOptions { /** Override protocol detection (tests). Defaults to detectImageProtocol(). */ protocol?: ImageProtocol; /** Override the fetch implementation (tests). Defaults to global fetch. */ fetchImpl?: typeof fetch; timeoutMs?: number; maxBytes?: number; } /** * Render a blip of any creative format to a string. Image/GIF creatives in a * capable terminal get the inline image ABOVE the standard text card (the * sponsor label, body and click URL always remain as text). Everything else, * and every failure, degrades to the text card. Never throws, never plays * media: video/audio are click-through only. */ declare function renderBlip(blip: Blip, opts?: MediaRenderOptions): Promise; /** Keep in sync with package.json (bundled, so we avoid a runtime fs read). * AUTO-GENERATED by scripts/sync-version.mjs on `npm version`. */ declare const SDK_VERSION = "1.4.8"; /** Public API. Every method is fail-silent: it never throws. */ declare const sponsor: { /** Fetch + render + report an impression. Resolves true if rendered. */ show(opts: SponsorOptions): Promise; /** Fetch a blip without rendering. Resolves to the Blip or null. */ fetch(opts: SponsorOptions): Promise; /** Fill the local preload pool. Resolves to the number of blips pooled. */ preload(opts: SponsorOptions): Promise; /** Render a pooled blip without network on the hot path. */ renderFromPool(opts: SponsorOptions): Promise; }; export { type BatchResponse, type Blip, type Creative, type CreativeFormat, DEFAULT_CONFIG, type Fallback, HOOK_CATALOG, type HookEvent, type Host, type ImageProtocol, type MediaRenderOptions, type OpenCraterState, type PooledBlip, type RemoteConfig, type RenderCardInput, type RenderOptions, SDK_VERSION, type ServeResponse, type SponsorOptions, detectImageProtocol, renderBlip, renderCard, sponsor, textCardForBlip };