import type { Environment } from '../config/environments.js'; export interface ApiDeps { fetch: typeof globalThis.fetch; } /** * One status→exit-code mapping for both publish calls. * * The distinction that matters is 4 vs 5: "your engine is too old" is self-serve * (`bitmagic upgrade`), while a ban or an ownership mismatch is not. An agent that cannot * tell them apart will either loop on upgrade or give up on a fixable problem. */ export declare function publishExitCode(status: number, body: { code?: string; }): number; export interface BeginPublishRequest { gameId: string; /** Omitted entirely to keep the game's stored visibility — see commands/publish.ts's * resolveVisibility. Sending 'private' and omitting the field are DIFFERENT messages. */ visibility?: 'public' | 'private'; name?: string; description?: string; engineVersion: string; thumbnailUrl: string; /** * The project's `worldProfileData.faviconUrlOverride`, when it has one. Optional so an older * server simply ignores a field it does not read — the game then keeps its default icon. */ faviconSourceUrl?: string; /** * sha256 of the source archive this publish is about to upload. * * Sent on BEGIN, unlike everything else about the archive, because `begin` bakes it into the * meta block it mints — which is the whole mechanism: `complete` already verifies that block * byte-for-byte against what actually landed, so binding the archive's hash into it makes the * published bundle carry a claim about which source produced it, at no extra read. That is only * possible if the archive is built and hashed BEFORE this call, which is why `runPublish` * zips before it begins. * * Optional: a failure to build the archive must not fail the publish, and an older server * ignores a field it does not read (it then mints a block without the line, and `complete` * re-renders without it too — see `renderMetaBlock`). */ sourceSha256?: string; } export interface BeginPublishResponse { signedUrl: string; key: string; publishVersion: number; metaBlock: string; cacheControl: string; /** * The game's own tab icon, derived server-side from its cover art (or from the creator's * override). Optional in both directions: absent when the game has no art or derivation * failed, and absent from an older server entirely — in either case the bundle keeps the * Bitmagic mark it was built with. */ faviconUrl?: string; /** * The newest engine the platform has, for the upgrade nudge. Null when the server could not * read its manifest — publishing is never gated on this, so an unknown value just means the * CLI says nothing rather than guessing. */ currentEngineVersion: string | null; /** * Where to PUT the source archive, and the key it lands at. * * The server mints the key (it carries a random segment, so the archive cannot be enumerated * from a game's public id) — the CLI never proposes one. Both absent when no `sourceSha256` * was sent, or when talking to a server that predates this; the CLI then simply publishes * without an archive, exactly as it did before. */ sourceSignedUrl?: string; sourceKey?: string; /** Must be echoed on the archive PUT — bound into the signature, same contract as the bundle. */ sourceCacheControl?: string; } export declare function beginPublish(environment: Environment, accessToken: string, body: BeginPublishRequest, deps: ApiDeps): Promise; export interface CompletePublishRequest { gameId: string; key: string; publishVersion: number; /** Omitted in exactly the same case as `BeginPublishRequest.visibility`, and for the same * reason — the pair must agree, since `complete` re-authorizes from its own body. */ visibility?: 'public' | 'private'; name?: string; description?: string; thumbnailUrl: string; bundleBytes: number; sha256: string; /** * The audit trail the spec calls for: `engineVersion`, `fingerprint` and `sha256` recorded * together for the publish that actually landed. All three are declarative — the server logs * them, it does not verify them (there is no column for any of them today, and verifying the * hash would mean reading bytes this design deliberately never reads). * * `engineVersion` is NOT a second floor gate. It cannot be: `begin` is the only step that * mints the signed URL, so its floor check is already unavoidable, and a value re-declared * here proves nothing about what the bundle was built with. It is sent so the log line that * records a publish says which engine produced it — without it, the only record of a bundle's * engine is a `begin` log entry that may belong to an abandoned attempt. * * `fingerprint` is the same hash `verify` recorded and `publish` gated on, so a support * question about a published bundle can be tied back to the exact project state that produced * it. Nothing else on the wire identifies the source. */ engineVersion: string; fingerprint: string; /** * The source archive that was uploaded, so `complete` can audit it: the key `begin` minted * (echoed, not proposed — the server re-derives the expected shape and refuses anything else) * and the hash that was bound into the meta block. * * All three optional together. They are absent when the archive could not be built, and absent * from every already-installed CLI — so `complete` must treat missing as "cannot check", not as * a failure. Making them required would break every publish in the field the moment the server * deploys. */ sourceKey?: string; sourceSha256?: string; /** * Which fingerprint algorithm produced `fingerprint`. The server keeps every version it has * shipped and dispatches on this, because a creator's pinned CLI and the running server are * routinely different copies of the shared code — see SOURCE_INDEX_ALGO. */ fingerprintAlgo?: string; /** * The catalog's platform indicators, synced from the project's own work copy — see * `publish/platform.ts` for where each one is read and why an absent key is not the same * message as `'desktop'` / `null`. * * Optional in the same forward-compatible sense as the source-archive fields above: both are * sticky columns server-side, so a CLI that sends neither leaves whatever is stored alone, and * a server that has not deployed this yet simply ignores two unknown body keys. Neither * direction of the skew can fail a publish. */ primaryPlatform?: 'desktop' | 'mobile'; /** `null` clears the stored orientation — the project no longer declares one. */ mobileOrientation?: 'portrait' | 'landscape' | null; /** * Category tags declared in the project's `worldProfileData.categories` — see * `publish/categories.ts` for the three messages (absent / non-empty / explicit empty). * Forward-compatible in the same sense as the platform fields: an absent key lets the * server keep or auto-classify, and an older server ignores the unknown key. */ categories?: string[]; } /** Per-check outcome from the server's source audit. Advisory: none of it changes the exit code. */ export interface SourceCheckResult { status: 'pass' | 'warn' | 'skip'; warnings?: string[]; } export interface CompletePublishResponse { /** * The VERSIONED key this publish wrote — `games//indexN.html`. The one URL that names * these exact bytes, which is why the smoke check loads it; a poor thing to share, because * the next publish writes `indexN+1.html` and a link handed out today freezes on today's * build. `promotedPublishUrl` (publish/urls.ts) decides what the creator actually sees. */ url: string; /** * The directory form `games//`, which always resolves to the newest publish. Correct for * every game, listed or not. Absent from an older server, which `promotedPublishUrl` recovers * from `url`. */ latestUrl?: string; /** * The player-facing `/play/` page, at the game's vanity slug when it has one. Sent only for a * public publish whose page bake actually landed — and absent from an older server — so its * presence is the server saying there IS a page to send someone to. */ playUrl?: string; publishVersion: number; /** The EFFECTIVE visibility the publish landed on — set even when the request omitted it. */ visibility: 'public' | 'private'; /** * Where the creator manages this game on the portal: visibility, and withdrawing it. Comes * from the server (`PORTAL_URL` is its to know — see subscribe-flow.ts's `subscribeUrl`), and * is absent from an older server, in which case the CLI simply prints no line for it. */ manageUrl?: string; /** Absent from an older server; the CLI then prints nothing. */ sourceChecks?: SourceCheckResult; } export declare function completePublish(environment: Environment, accessToken: string, body: CompletePublishRequest, deps: ApiDeps): Promise; /** * `begin` always signs `text/html` for the entry HTML (cli-publish.ts's `getSignedUrl` call) but * does not echo a `contentType` field back in its response — there is nothing to echo, so this * fixed value is the one Content-Type that can ever be correct for the PUT. */ export declare const PUBLISH_BUNDLE_CONTENT_TYPE = "text/html"; /** PUT the built (and meta-block-injected) bundle to the signed URL `begin` minted. */ export declare function putBundle(signedUrl: string, html: string, cacheControl: string, deps: ApiDeps): Promise; /** * `begin` signs the source archive as `application/zip`. Fixed here for the same reason as * PUBLISH_BUNDLE_CONTENT_TYPE: the response echoes no content type because there is nothing to * echo — this is the one value the signature can have been minted with. */ export declare const PUBLISH_SOURCE_CONTENT_TYPE = "application/zip"; /** PUT the source archive to the signed URL `begin` minted. */ export declare function putSourceArchive(signedUrl: string, body: Uint8Array, cacheControl: string, deps: ApiDeps): Promise; export interface ThumbnailUploadRequest { gameId: string; filename: string; contentType: string; } /** `cli-uploads.ts`'s `generateUploadSignedUrl` response shape, the fields this lane uses. */ export interface ThumbnailUploadUrl { signedUrl: string; publicUrl: string; cacheControl: string; contentEncoding?: string; } export declare function requestThumbnailUploadUrl(environment: Environment, accessToken: string, body: ThumbnailUploadRequest, deps: ApiDeps): Promise; /** * PUT the thumbnail bytes to the signed URL `requestThumbnailUploadUrl` minted. `contentType` is * the SAME value this lane requested (the server never echoes it back — see * `PUBLISH_BUNDLE_CONTENT_TYPE`'s doc comment for why that is fine: the caller already knows it), * while `cacheControl` and `contentEncoding` are read back from the signed-URL response, because * the server — not this lane — decides both. */ export declare function putThumbnail(signed: ThumbnailUploadUrl, bytes: Uint8Array, contentType: string, deps: ApiDeps): Promise;