import { RateLimiter, type RateLimitWait } from './rate-limiter.js'; import { type ParsedTestLogs } from '../testing/test-log-parser.js'; import { type LogFetchStats } from '../testing/log-fetch-stats.js'; /** * The largest place file the place version endpoint accepts, measured: 150 MiB * exactly uploads, one byte more is refused from `Content-Length` before the * body is sent, as `400 InvalidRequest: "Request upload was interrupted before * completion."` — and somewhere past ~200 MB as `413 Payload Too Large`. * Neither says anything about size, so it is checked here first. * * Not the 100 MB Roblox documents as supported * (https://create.roblox.com/docs/projects/place-files); that is where Studio * saving "might fail", not where the API cuts off. */ export declare const PLACE_UPLOAD_MAX_BYTES: number; /** * One engine message, with the severity Open Cloud reported for it. * * A message is not a line: an error arrives with its whole traceback attached, * so `message` can span several. `messageType` is OUTPUT, WARNING or ERROR. */ export interface TaskLogMessage { message: string; messageType?: string; } /** A task's log text together with what the fetch had to do to collect it. */ export interface TaskLogFetch { text: string; stats: LogFetchStats; /** * The same output as `text`, still split into typed messages. Kept because * severity is the API's to state and cannot be recovered from the joined * text: a traceback's continuation lines look like ordinary output. */ messages: TaskLogMessage[]; } export interface LuauTask { path: string; createTime: string; updateTime: string; user: string; state: 'STATE_UNSPECIFIED' | 'QUEUED' | 'PROCESSING' | 'CANCELLED' | 'COMPLETE' | 'FAILED'; script: string; timeout?: string; /** * What the script returned, populated on COMPLETE. `results` is a flat array * of the returned values — `return t, "s", 42` arrives as three entries — * natively typed: Roblox serializes them itself, so a returned Lua table is * real nested JSON here, not a JSON string. (Which is also why a script must * not JSONEncode its result: that lands as a double-encoded string.) * * Absent whenever the task produced no result. A FAILED task carries no * `output` at all, with no error message explaining why — and an oversize * return value (~4MB observed; ~2MB still arrives complete) fails the task * exactly that way rather than truncating the value. */ output?: { results?: unknown[]; }; /** Error details (populated on FAILED). */ error?: { code?: string; message?: string; }; } /** * The values a finished task's script returned, or `undefined` when the task * reported no result at all. * * The distinction matters: `undefined` means nothing came back to read, so a * caller can fall back to the task's logs, while `[]` means the task did report * a result and the script returned nothing — no fallback will find more. */ export declare function getTaskReturnValues(task: LuauTask): unknown[] | undefined; export interface OpenCloudClientOptions { apiKey: string | (() => Promise); rateLimiter: RateLimiter; } /** * Which end of a place's version history to resolve. Mirrors the `versionType` * the place-publishing API takes on upload, lowercased to match how it is * written in deploy.nevermore.json. */ export type PlaceVersionType = 'saved' | 'published'; /** Who owns a newly created asset. */ export type AssetCreator = { userId: number; } | { groupId: number; }; export interface AssetFile { filePath: string; /** MIME type of the file. The Assets API picks its importer from it. */ contentType: string; } export interface CreateAssetOptions extends AssetFile { /** `Model`, `Image`, `Audio`, ... as the Assets API spells them. */ assetType: string; displayName: string; description: string; creator: AssetCreator; } /** * The long-running operation every asset upload returns. Creating an asset is * not synchronous — the file is imported and moderated first — so the id only * exists once the operation reports `done`. */ export interface AssetOperation { /** `operations/{operationId}` */ path: string; done?: boolean; response?: { /** A string in practice, though the API's own schema calls it an integer. */ assetId?: string | number; revisionId?: string; moderationResult?: { moderationState?: string; }; }; error?: { code?: number | string; message?: string; }; } export interface UploadedAsset { assetId: number; revisionId?: string; /** * `Approved`, `Reviewing`, ... exactly as the API reports it — which is not * the `MODERATION_STATE_APPROVED` spelling its documentation shows. Audio * routinely finishes uploading while still `Reviewing`. */ moderationState?: string; } export declare class OpenCloudClient { private _apiKey; private _apiKeySource; private _rateLimiter; constructor(options: OpenCloudClientOptions); private _resolveApiKeyAsync; uploadPlaceAsync(universeId: number, placeId: number, rbxlPath: string, publish?: boolean, onProgress?: (transferredBytes: number, totalBytes: number) => void, onRateLimitWait?: (wait: RateLimitWait) => void): Promise; createExecutionTaskAsync(universeId: number, placeId: number, placeVersion: number, script: string, timeoutMs?: number, onRateLimitWait?: (wait: RateLimitWait) => void): Promise; pollTaskCompletionAsync(taskPath: string, onProgress?: (state: LuauTask['state']) => void, onRateLimitWait?: (wait: RateLimitWait) => void): Promise; getTaskLogsAsync(taskPath: string): Promise; /** * Fetch raw log text from a completed Luau execution task, along with the * shape of the fetch that produced it. * * Retries a few times if the API returns empty logs, since the test runner * always produces at least some output. The reported `requests` count spans * every attempt, so a fetch that only filled in on its third try says so. */ getRawTaskLogsAsync(taskPath: string): Promise; private _fetchRawLogsAsync; /** * Download a place file via the Asset Delivery API. * Requires the `legacy-asset:manage` scope on the API key. * * Two-step process: fetch a temporary CDN URL, then download the binary. * * Pass `version` to fetch a specific published version instead of the latest. * This is how deploys pin a `basePlace` so a broken Studio edit can't leak in. */ downloadPlaceAsync(universeId: number, placeId: number, onProgress?: (transferredBytes: number, totalBytes: number) => void, version?: number): Promise; /** * Resolve the newest saved or published version number of a place, via the * Open Cloud Assets API (`asset:read` scope). The returned number is the same * value the Asset Delivery API's `/version/{n}` route expects, so it can be * written straight into a `basePlace.version` pin. * * This used to add that `legacy-asset:manage` grants `asset:read` too. They * are separate boxes on the credentials page and nothing here ever verified * the implication, so the claim is gone rather than restated — it had already * been copied into the `nevermore login` instructions as fact once. * * `saved` is the newest version of any kind — Studio saves and publishes * share one increasing version sequence. `published` is the newest version * that actually went live, which is what players see and what a deploy * merging against a base place normally wants. * * The endpoint pages newest-first, which is what makes this affordable: an * actively-developed place accumulates tens of thousands of versions, and we * never walk the history. `saved` is the first entry of the first page, and * `published` only walks back as far as the most recent live version — one * page unless the place has been saved 50+ times without publishing. The scan * is capped so a never-published place fails fast instead of paging forever. */ resolveLatestPlaceVersionAsync(universeId: number, placeId: number, versionType: PlaceVersionType): Promise; /** * Start uploading a file as a new asset via the Open Cloud Assets API * (`asset:read` and `asset:write`). Returns the operation to hand to * {@link pollAssetOperationAsync}; the asset has no id until that finishes. */ createAssetAsync(options: CreateAssetOptions): Promise; /** * Start replacing an existing asset's file, keeping its id — the upload * becomes a new version of the same asset. The API only does this for models. */ updateAssetContentAsync(assetId: number, file: AssetFile): Promise; /** * Wait for an asset upload to finish and return the asset it produced. * * Throws if the operation reports an error or outlives `timeoutMs`. A timeout * is not a failed upload: Roblox may still finish creating the asset, which * is why the message says to look before uploading again. */ pollAssetOperationAsync(operation: AssetOperation, timeoutMs?: number): Promise; /** * Who owns a universe: the user or group its assets should belong to if the * experience is to use them. Needs no scope beyond a valid key. */ getUniverseOwnerAsync(universeId: number): Promise; /** The user the API key acts as — whoever created it. */ getApiKeyUserIdAsync(): Promise; private _sendAssetAsync; } //# sourceMappingURL=open-cloud-client.d.ts.map