@omelhorsite/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +321 -0
  2. package/dist/index.js +11589 -0
  3. package/dist/types/auth/device.d.ts +156 -0
  4. package/dist/types/auth/index.d.ts +127 -0
  5. package/dist/types/auth/tokens.d.ts +356 -0
  6. package/dist/types/client.d.ts +133 -0
  7. package/dist/types/errors.d.ts +202 -0
  8. package/dist/types/http.d.ts +204 -0
  9. package/dist/types/index.d.ts +33 -0
  10. package/dist/types/local/index.d.ts +42 -0
  11. package/dist/types/local/password.d.ts +169 -0
  12. package/dist/types/local/qr.d.ts +127 -0
  13. package/dist/types/local/wordlist.d.ts +26 -0
  14. package/dist/types/resources/account.d.ts +296 -0
  15. package/dist/types/resources/chests.d.ts +194 -0
  16. package/dist/types/resources/dynamicQrs.d.ts +172 -0
  17. package/dist/types/resources/forms.d.ts +331 -0
  18. package/dist/types/resources/index.d.ts +30 -0
  19. package/dist/types/resources/ipLookup.d.ts +63 -0
  20. package/dist/types/resources/jobs.d.ts +233 -0
  21. package/dist/types/resources/linkTrees.d.ts +249 -0
  22. package/dist/types/resources/notepads.d.ts +96 -0
  23. package/dist/types/resources/shortLinks.d.ts +248 -0
  24. package/dist/types/resources/storage/upload.d.ts +459 -0
  25. package/dist/types/resources/storage.d.ts +527 -0
  26. package/dist/types/resources/tickets.d.ts +236 -0
  27. package/dist/types/resources/tools/backgroundRemoval.d.ts +99 -0
  28. package/dist/types/resources/tools/captions.d.ts +318 -0
  29. package/dist/types/resources/tools/downloader.d.ts +397 -0
  30. package/dist/types/resources/tools/index.d.ts +215 -0
  31. package/dist/types/resources/tools/jumpstyle.d.ts +194 -0
  32. package/dist/types/resources/tools/transcription.d.ts +178 -0
  33. package/dist/types/resources/tools/upscale.d.ts +94 -0
  34. package/dist/types/resources/tools/vocalSeparation.d.ts +183 -0
  35. package/dist/types/types.d.ts +245 -0
  36. package/package.json +37 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * The `tickets` namespace: support tickets and the message thread on each one.
3
+ *
4
+ * A ticket is `POST /tickets` with a subject plus an optional first message;
5
+ * everything after that is a `TicketMessage` on `/ticket_messages`. Attachments
6
+ * are data URIs on create (the backend caps them at 10 files / 10 MiB total,
7
+ * images and video only) and are read back one blob at a time.
8
+ *
9
+ * This is the namespace an agent reaches for when something went wrong, so
10
+ * `create()` is deliberately one call: subject, body and screenshots all land
11
+ * in the same request.
12
+ */
13
+ import { type ApiClient, Resource } from "../http";
14
+ import type { FileInput, Id, Paginated, PageParams, RequestOptions, Timestamp } from "../types";
15
+ /** Lifecycle of a ticket. */
16
+ export type TicketStatus = "open" | "closed";
17
+ /**
18
+ * Identifier of a ticket. A NUMBER, not the opaque string every other resource
19
+ * uses: `tickets` is one of the two tables that kept an auto-increment primary
20
+ * key, so the API renders `id` as a JSON number here. That is why {@link Ticket}
21
+ * does not extend `BaseRecord`.
22
+ */
23
+ export type TicketId = number;
24
+ /** Identifier of a ticket message. A number, for the same reason as {@link TicketId}. */
25
+ export type TicketMessageId = number;
26
+ /** Longest subject the backend accepts. Anything longer is a 400. */
27
+ export declare const TICKET_SUBJECT_MAX_LENGTH = 200;
28
+ /** Longest message body the backend accepts. Anything longer is a 400. */
29
+ export declare const TICKET_MESSAGE_MAX_LENGTH = 4000;
30
+ /** Most attachments one `create` may carry. The backend keeps the first ten. */
31
+ export declare const TICKET_MAX_ATTACHMENTS = 10;
32
+ /** Ceiling on the decoded bytes of all attachments in one `create`. */
33
+ export declare const TICKET_MAX_ATTACHMENT_BYTES: number;
34
+ /** MIME prefixes the backend accepts as a ticket attachment. */
35
+ export declare const TICKET_ATTACHMENT_TYPES: readonly ["image/", "video/"];
36
+ /**
37
+ * Free-form context the client attaches at create time. The backend keeps only
38
+ * these four keys and drops anything else, so do not smuggle payloads through.
39
+ */
40
+ export interface TicketContext {
41
+ readonly provider?: string;
42
+ readonly error?: string;
43
+ readonly path?: string;
44
+ readonly source?: string;
45
+ }
46
+ /** One file hanging off a ticket, as the blueprint renders it. */
47
+ export interface TicketAttachment {
48
+ /** Address it with {@link TicketsNamespace.attachment}. Numeric, like {@link TicketId}. */
49
+ readonly blob_id: number;
50
+ readonly filename: string;
51
+ readonly content_type: string;
52
+ readonly byte_size: number;
53
+ /** Derived from `content_type`; never anything else today. */
54
+ readonly kind: "image" | "video";
55
+ }
56
+ /** A support ticket. */
57
+ export interface Ticket {
58
+ readonly id: TicketId;
59
+ readonly created_at: Timestamp;
60
+ readonly updated_at: Timestamp;
61
+ readonly subject: string;
62
+ readonly status: TicketStatus;
63
+ readonly user_id: Id;
64
+ /** Bumped by every message. The listing is ordered on it, newest first. */
65
+ readonly last_activity_at: Timestamp;
66
+ /** Always rendered; `{}` when nothing was attached at create time. */
67
+ readonly context: TicketContext | null;
68
+ readonly attachments: TicketAttachment[];
69
+ /**
70
+ * The thread. Present on `get`, `create` and `update` (which render the
71
+ * `:extended` view) and absent from `list`, which renders the default view.
72
+ */
73
+ readonly ticket_messages?: TicketMessage[];
74
+ }
75
+ /** One message in a ticket thread. */
76
+ export interface TicketMessage {
77
+ readonly id: TicketMessageId;
78
+ readonly created_at: Timestamp;
79
+ readonly updated_at: Timestamp;
80
+ readonly ticket_id: TicketId;
81
+ /** Always the sender's own id: the server ignores any value you send. */
82
+ readonly sender_id: Id;
83
+ readonly content: string;
84
+ /** Whether the sender is a member of staff. */
85
+ readonly sender_admin: boolean;
86
+ }
87
+ /** Arguments for opening a ticket. */
88
+ export interface CreateTicketInput {
89
+ /** At most {@link TICKET_SUBJECT_MAX_LENGTH} characters. */
90
+ readonly subject: string;
91
+ /**
92
+ * Body of the first message. Optional: a subject alone opens a ticket.
93
+ *
94
+ * The backend creates this message without a bang, so a body that fails
95
+ * validation (over {@link TICKET_MESSAGE_MAX_LENGTH}) is dropped and the
96
+ * ticket is still created. The SDK checks the length first so that never
97
+ * happens silently.
98
+ */
99
+ readonly initialMessage?: string;
100
+ readonly context?: TicketContext;
101
+ /**
102
+ * Files to attach. The SDK turns each one into the data URI the endpoint
103
+ * expects, so every attachment is buffered in memory.
104
+ *
105
+ * Backend caps: {@link TICKET_MAX_ATTACHMENTS} files,
106
+ * {@link TICKET_MAX_ATTACHMENT_BYTES} in total, `image/*` and `video/*`
107
+ * only - and an attachment that breaks any of them is dropped in SILENCE,
108
+ * with the ticket still answering 201. The SDK therefore validates before
109
+ * sending and raises rather than let a screenshot disappear.
110
+ */
111
+ readonly attachments?: FileInput[];
112
+ }
113
+ /** Fields that can change after a ticket exists. */
114
+ export interface UpdateTicketInput {
115
+ readonly status?: TicketStatus;
116
+ }
117
+ /** Filters for {@link TicketsNamespace.list}. */
118
+ export interface ListTicketsParams extends PageParams {
119
+ readonly status?: TicketStatus;
120
+ /** Administrators only: someone else's tickets. */
121
+ readonly userId?: Id;
122
+ }
123
+ /** Filters for {@link TicketMessagesNamespace.list}. */
124
+ export interface ListTicketMessagesParams extends PageParams {
125
+ readonly ticketId: TicketId;
126
+ }
127
+ /** Arguments for replying to a ticket. */
128
+ export interface CreateTicketMessageInput {
129
+ readonly ticketId: TicketId;
130
+ /** At most {@link TICKET_MESSAGE_MAX_LENGTH} characters. */
131
+ readonly content: string;
132
+ }
133
+ /** The message thread of a ticket, reachable as `oms.tickets.messages`. */
134
+ export declare class TicketMessagesNamespace extends Resource {
135
+ /**
136
+ * `GET /ticket_messages?exact_search[ticket_id]=...` - the thread, oldest
137
+ * first.
138
+ *
139
+ * A thread you can already see whole through {@link TicketsNamespace.get}
140
+ * does not need this; reach for it when a thread is long enough to page.
141
+ */
142
+ list(params: ListTicketMessagesParams, options?: RequestOptions): Promise<Paginated<TicketMessage>>;
143
+ /**
144
+ * `POST /ticket_messages` - replies to a ticket.
145
+ *
146
+ * The sender is always the caller: `sender_id` cannot be forged. A closed
147
+ * ticket refuses replies with 401, so reopen it first.
148
+ *
149
+ * Not retried by default: a replayed reply would post the message twice.
150
+ */
151
+ create(input: CreateTicketMessageInput, options?: RequestOptions): Promise<TicketMessage>;
152
+ /**
153
+ * `DELETE /ticket_messages/:id`. Administrators only; the author of a
154
+ * message cannot take it back.
155
+ */
156
+ delete(id: TicketMessageId, options?: RequestOptions): Promise<void>;
157
+ }
158
+ /** The `tickets` namespace, reachable as `oms.tickets`. */
159
+ export declare class TicketsNamespace extends Resource {
160
+ /** The message thread of a ticket. */
161
+ readonly messages: TicketMessagesNamespace;
162
+ constructor(http: ApiClient);
163
+ /**
164
+ * `GET /tickets` - your tickets, most recently active first. An
165
+ * administrator sees everyone's and can narrow with `userId`.
166
+ *
167
+ * The rows carry no `ticket_messages`: the listing renders the default
168
+ * view. Call {@link get} for the thread.
169
+ */
170
+ list(params?: ListTicketsParams, options?: RequestOptions): Promise<Paginated<Ticket>>;
171
+ /** `GET /tickets/:id` - the ticket with its whole message thread. */
172
+ get(id: TicketId, options?: RequestOptions): Promise<Ticket>;
173
+ /**
174
+ * `POST /tickets` - opens a ticket, with its first message and its
175
+ * screenshots, in ONE request.
176
+ *
177
+ * ```ts
178
+ * await oms.tickets.create({
179
+ * subject: "Upload fails at 90%",
180
+ * initialMessage: "Every file over 2 GB stops at the same point.",
181
+ * context: { source: "cli", path: "/tools/storage" },
182
+ * attachments: [file(png, "screenshot.png")],
183
+ * });
184
+ * ```
185
+ *
186
+ * Attachments are encoded as data URIs here, so they are buffered whole;
187
+ * see {@link CreateTicketInput.attachments} for the caps the SDK enforces
188
+ * before sending.
189
+ *
190
+ * Not retried by default: a replayed create would open a second ticket.
191
+ */
192
+ create(input: CreateTicketInput, options?: RequestOptions): Promise<Ticket>;
193
+ /**
194
+ * `PATCH /tickets/:id` - today the only mutable field is `status`.
195
+ *
196
+ * Anything else in the body is dropped in silence and still answers 200
197
+ * with the untouched record, so read the returned ticket rather than
198
+ * inferring success from the status code.
199
+ */
200
+ update(id: TicketId, input: UpdateTicketInput, options?: RequestOptions): Promise<Ticket>;
201
+ /** Convenience over {@link update} with `status: "closed"`. */
202
+ close(id: TicketId, options?: RequestOptions): Promise<Ticket>;
203
+ /**
204
+ * Convenience over {@link update} with `status: "open"`. A closed ticket
205
+ * refuses new messages, so reopen before replying.
206
+ */
207
+ reopen(id: TicketId, options?: RequestOptions): Promise<Ticket>;
208
+ /**
209
+ * `DELETE /tickets/:id`. Administrators only: the owner of a ticket can
210
+ * close it but not destroy it, and gets 401 if they try.
211
+ */
212
+ delete(id: TicketId, options?: RequestOptions): Promise<void>;
213
+ /**
214
+ * `GET /tickets/:id/attachment/:blob_id` - one attachment's bytes.
215
+ *
216
+ * The endpoint answers 302 towards object storage and `fetch` follows it.
217
+ * The redirect is cross-origin, so the platform drops the `Authorization`
218
+ * header on the way: the credential never reaches the storage host.
219
+ *
220
+ * Take `blobId` from {@link TicketAttachment.blob_id}; the filename and
221
+ * content type are on the same record, which is why this hands back bare
222
+ * bytes.
223
+ */
224
+ attachment(id: TicketId, blobId: number, options?: RequestOptions): Promise<Blob>;
225
+ }
226
+ /**
227
+ * Encodes a file as a `data:<mime>;base64,<...>` URI.
228
+ *
229
+ * Exported because several endpoints take images as data URIs rather than as
230
+ * uploads (ticket attachments, a link tree avatar, a form theme's background),
231
+ * and the alternative is every caller writing this loop again.
232
+ *
233
+ * Buffers the whole file: a data URI has no streaming form. Base64 also costs
234
+ * a third more bytes than the original, which matters against the caps.
235
+ */
236
+ export declare function dataUrlFromFile(input: FileInput): Promise<string>;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Background removal: cuts the subject out of an image.
3
+ *
4
+ * `POST /background_removals` enqueues a proxy job and answers immediately with
5
+ * a row plus a `job_id` and, for an anonymous caller, a `watch_token`. Poll
6
+ * through `oms.jobs` with that handle, or use {@link BackgroundRemovalNamespace.run}.
7
+ *
8
+ * Backend limits: 15 MiB, and an image bomb (absurd pixel count for its byte
9
+ * size) is rejected with a 400 before any work starts.
10
+ *
11
+ * This tool has no daily quota - it is one of the two that the `Quotas`
12
+ * catalogue does not meter - so there is no `quota()` here to call first.
13
+ */
14
+ import { Resource } from "../../http";
15
+ import type { FileInput, Id, RequestOptions } from "../../types";
16
+ import { type ToolCaptcha, type ToolJobHandle, type ToolRecord, type ToolRunOptions } from "./index";
17
+ /** A background removal run. */
18
+ export interface BackgroundRemoval extends ToolRecord {
19
+ /** URL of the cut-out PNG, once the status is `"complete"`. */
20
+ readonly result_url?: string | null;
21
+ }
22
+ /** What `POST /background_removals` answers with. */
23
+ export type BackgroundRemovalCreated = BackgroundRemoval & ToolJobHandle;
24
+ /** Arguments for starting a run. */
25
+ export interface CreateBackgroundRemovalInput extends ToolCaptcha {
26
+ /** The image. Backend cap: 15 MiB. */
27
+ readonly file: FileInput;
28
+ }
29
+ /** The `backgroundRemoval` tool, reachable as `oms.tools.backgroundRemoval`. */
30
+ export declare class BackgroundRemovalNamespace extends Resource {
31
+ /**
32
+ * The one polling loop, reached through the jobs namespace.
33
+ *
34
+ * Built here rather than injected so every namespace keeps the same
35
+ * one-argument constructor `client.ts` relies on. It is a stateless wrapper
36
+ * over the same transport, so there is nothing to share.
37
+ */
38
+ private readonly jobs;
39
+ /**
40
+ * `POST /background_removals` - enqueues a run and returns straight away.
41
+ *
42
+ * NOT retried by default, unlike most of the SDK. The transport's policy
43
+ * replays a `POST` that died with a 502, and here that would re-upload the
44
+ * image and start a second run on a sidecar that serialises them. Pass
45
+ * `retry: {}` to opt back in.
46
+ *
47
+ * @throws {OmsApiError} 400 when the image is too large or looks like a bomb.
48
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
49
+ */
50
+ create(input: CreateBackgroundRemovalInput, options?: RequestOptions): Promise<BackgroundRemovalCreated>;
51
+ /**
52
+ * `GET /background_removals/:id` - one poll.
53
+ *
54
+ * Ownership is the caller's session, or - for an anonymous run - the IP the
55
+ * run was started from. Reading someone else's run is a 401, not a 404.
56
+ *
57
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
58
+ * @throws {OmsAuthError} 401 when the run belongs to someone else, which
59
+ * includes an anonymous run being read from a different address.
60
+ */
61
+ get(id: Id, options?: RequestOptions): Promise<BackgroundRemoval>;
62
+ /**
63
+ * Creates a run and waits for it, reporting progress along the way.
64
+ *
65
+ * Delegates the polling to `oms.jobs.wait` with the handle the create call
66
+ * returned; it does not open a second polling loop.
67
+ *
68
+ * Resolves with a `"failed"` row rather than throwing when the work failed -
69
+ * the request cycle worked, the work did not, and only the caller knows
70
+ * whether that is an exception. Pass `waitTimeoutMs` (or a `signal`) to bound
71
+ * the wait; there is no default deadline.
72
+ *
73
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
74
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
75
+ * up later with {@link get}.
76
+ */
77
+ run(input: CreateBackgroundRemovalInput, options?: ToolRunOptions): Promise<BackgroundRemoval>;
78
+ /**
79
+ * Downloads the cut-out image of a finished run.
80
+ *
81
+ * Two calls: one to read the row for its `result_url`, one to fetch the
82
+ * signed URL itself with no credential attached. Pass the row you already
83
+ * have to {@link resultUrl} plus {@link fetchToolArtifact} if you would
84
+ * rather not pay for the first.
85
+ *
86
+ * @throws {OmsError} `conflict` when the run has not finished,
87
+ * `invalid_request` when it failed, `not_found` when the artefact is gone.
88
+ */
89
+ download(id: Id, options?: RequestOptions): Promise<Blob>;
90
+ /**
91
+ * The signed URL of a finished run's PNG, from a row you already hold.
92
+ *
93
+ * Good for handing to a browser or a player instead of moving the bytes. It
94
+ * is a credential: anyone holding it can read the image.
95
+ *
96
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
97
+ */
98
+ resultUrl(record: BackgroundRemoval): string;
99
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Captions: karaoke subtitles burned into a video.
3
+ *
4
+ * The only tool with three steps rather than one, and they must go in order:
5
+ *
6
+ * 1. {@link CaptionsNamespace.create} uploads the video. The status becomes
7
+ * `"uploaded"` and the server reports the probed dimensions and duration.
8
+ * 2. {@link CaptionsNamespace.transcribe} transcribes ONE window of it. The
9
+ * quota is charged on that window, not on the whole file. The status walks
10
+ * `"transcribing"` -> `"transcribed"` and the words land on the row.
11
+ * 3. {@link CaptionsNamespace.render} burns the words in. The status walks
12
+ * `"rendering"` -> `"complete"` and `output_url` appears.
13
+ *
14
+ * Between steps the row must be idle: calling one while another is running is
15
+ * a 409, not a queue. That is why steps 2 and 3 WAIT before they return -
16
+ * handing back a busy row would just guarantee the caller's next call is the
17
+ * 409. Use {@link CaptionsNamespace.get} to poll a job somebody else started.
18
+ *
19
+ * Limits, all enforced by the backend:
20
+ *
21
+ * | | |
22
+ * |---|---|
23
+ * | file size | 250 MiB (`413`) |
24
+ * | video length | 20 minutes (`400`) |
25
+ * | one transcribed window | 15 minutes (`400`) |
26
+ * | words per render | 3000, 80 characters each |
27
+ * | daily quota, anonymous | 15 minutes of transcribed window |
28
+ * | daily quota, signed in | 60 minutes of transcribed window |
29
+ * | `POST` on any of the three steps | 20 a minute, shared with every other expensive tool |
30
+ *
31
+ * The upload itself spends no quota - only a transcribed window does - but the
32
+ * 20-minute cap on the video exists because a render re-encodes the WHOLE file,
33
+ * captioned part or not. So a 19-minute video with a 30-second window is a
34
+ * cheap transcription and an expensive render, and only the first of those is
35
+ * metered.
36
+ *
37
+ * Read {@link CaptionsNamespace.quota} before uploading, and again once the
38
+ * window is known: the second check is the exact one, and the server refuses a
39
+ * window that would cross the ceiling rather than truncating it.
40
+ */
41
+ import { Resource } from "../../http";
42
+ import type { FileInput, Id, Progress, RequestOptions } from "../../types";
43
+ import { type SecondsQuota, type ToolCaptcha, type ToolRecord, type ToolRunOptions } from "./index";
44
+ /**
45
+ * Lifecycle of a caption job. Wider than {@link ToolStatus} because the job
46
+ * has two distinct pieces of work.
47
+ */
48
+ export type CaptionStatus = "uploaded" | "transcribing" | "transcribed" | "rendering" | "complete" | "failed";
49
+ /**
50
+ * The two statuses with a sidecar call in flight.
51
+ *
52
+ * A job in one of these refuses new work with a 409. Note this is NOT the
53
+ * complement of "terminal": `"uploaded"` and `"transcribed"` are idle but not
54
+ * finished, which is the whole point of a three-step tool.
55
+ */
56
+ export declare const CAPTION_BUSY_STATUSES: readonly CaptionStatus[];
57
+ /** True while a step is running and the job will refuse another one. */
58
+ export declare function isCaptionBusy(status: string): boolean;
59
+ /**
60
+ * One timed word of the transcript.
61
+ *
62
+ * `text`, `t0`, `t1` - NOT `word`, `start`, `end`. These are the keys the
63
+ * render endpoint permits and the keys the transcriber writes onto the row, so
64
+ * a word object built any other way is silently dropped server-side and the
65
+ * render fails with "No words to render".
66
+ *
67
+ * Timings are seconds from the START OF THE VIDEO, not from the start of the
68
+ * transcribed window: the backend shifts them onto the video clock before
69
+ * saving, so an edited list can be sent straight back.
70
+ */
71
+ export interface CaptionWord {
72
+ /** The word itself. Trimmed and truncated to 80 characters server-side. */
73
+ readonly text: string;
74
+ /** Seconds from the video start. */
75
+ readonly t0: number;
76
+ /** Seconds from the video start. Must be `>= t0`. */
77
+ readonly t1: number;
78
+ }
79
+ /**
80
+ * Look of the burned-in captions.
81
+ *
82
+ * Open bag, because the renderer gains options without an SDK release and
83
+ * unknown keys are dropped server-side rather than rejected. The named keys are
84
+ * the ones the controller's allow-list actually permits today; anything else -
85
+ * a font name, a hex colour - is accepted by the request and then thrown away,
86
+ * which is worth knowing before spending a render on it.
87
+ *
88
+ * Every numeric key is CLAMPED, not validated: a value outside the range comes
89
+ * back as the nearest end of it rather than as a 400.
90
+ */
91
+ export interface CaptionStyle {
92
+ /** Font size as a fraction of the video height. Clamped to `[0.03, 0.09]`. */
93
+ readonly fontscale?: number;
94
+ /** Vertical placement, `0` top to `1` bottom. Clamped to `[0.3, 0.9]`. */
95
+ readonly pos?: number;
96
+ /** Words on screen at once. Clamped to `[1, 6]`. */
97
+ readonly max_words?: number;
98
+ /** Seconds of silence that starts a new caption chunk. Clamped to `[0.1, 1.0]`. */
99
+ readonly gap?: number;
100
+ /** x264 quality, lower is better and bigger. Clamped to `[16, 30]`. */
101
+ readonly crf?: number;
102
+ /** x264 speed. Anything outside these three is ignored. */
103
+ readonly preset?: "veryfast" | "medium" | "slow";
104
+ /**
105
+ * Colour of the word currently being sung, as `[r, g, b]`, each clamped to
106
+ * `[0, 255]`. Ignored unless it has exactly three entries.
107
+ */
108
+ readonly yellow?: readonly [number, number, number] | readonly number[];
109
+ readonly [key: string]: unknown;
110
+ }
111
+ /** A caption job. */
112
+ export interface CaptionJob extends ToolRecord {
113
+ readonly status: CaptionStatus;
114
+ readonly filename: string;
115
+ readonly width?: number | null;
116
+ readonly height?: number | null;
117
+ readonly fps?: number | null;
118
+ /** Seconds of video, probed at upload. */
119
+ readonly duration?: number | null;
120
+ readonly language?: string | null;
121
+ /** The transcribed window, seconds from the video start. */
122
+ readonly window_start?: number | null;
123
+ readonly window_end?: number | null;
124
+ /** Seconds charged against the quota so far. Accumulates across windows. */
125
+ readonly transcribed_seconds?: number | null;
126
+ /** Timed words. Present from `"transcribed"` onwards. */
127
+ readonly words?: CaptionWord[] | null;
128
+ /** What the renderer is doing right now. `null` unless rendering. */
129
+ readonly render_stage?: string | null;
130
+ /** The finished video, once the status is `"complete"`. */
131
+ readonly output_url?: string | null;
132
+ }
133
+ /** Arguments for uploading a video. */
134
+ export interface CreateCaptionJobInput extends ToolCaptcha {
135
+ /** The video. Sent as the `video` form field. Backend caps: 250 MiB, 20 minutes. */
136
+ readonly video: FileInput;
137
+ }
138
+ /** Arguments for transcribing a window. */
139
+ export interface TranscribeCaptionInput {
140
+ /** Seconds from the video start. */
141
+ readonly start: number;
142
+ /** Seconds from the video start. Must exceed `start` and be at most 15 minutes past it. */
143
+ readonly end: number;
144
+ /** ISO language, or `"auto"`. Defaults to `"auto"`. */
145
+ readonly language?: string;
146
+ }
147
+ /** Arguments for rendering. */
148
+ export interface RenderCaptionInput {
149
+ /**
150
+ * Words to burn in. Omit to use the words already on the row; pass an edited
151
+ * array to fix what the model misheard. Backend cap: 3000 words, 80
152
+ * characters each.
153
+ *
154
+ * Omitting costs one extra `GET`: the render endpoint has no fallback of its
155
+ * own and refuses a request with no word list, so the SDK reads the row and
156
+ * sends its words back. See {@link CaptionsNamespace.render}.
157
+ */
158
+ readonly words?: CaptionWord[];
159
+ readonly style?: CaptionStyle;
160
+ }
161
+ /**
162
+ * Renders a caption job as a {@link Progress}, render stage included.
163
+ *
164
+ * Same liberty as the vocal separator takes with the queue position: the
165
+ * sidecar's stage is folded into `status` - `"rendering (encoding)"` - because
166
+ * `status` is the only part of a {@link Progress} a host renders as text, and a
167
+ * whole-file re-encode is long enough that "rendering" alone tells a person
168
+ * nothing.
169
+ */
170
+ export declare function captionProgress(record: CaptionJob): Progress;
171
+ /** The `captions` tool, reachable as `oms.tools.captions`. */
172
+ export declare class CaptionsNamespace extends Resource {
173
+ /**
174
+ * `GET /caption_jobs/quota` - seconds of transcription spent and left today.
175
+ *
176
+ * Cheap and anonymous-safe. Counts seconds of transcribed WINDOW, not
177
+ * seconds of uploaded video: a job that was uploaded and never transcribed
178
+ * has spent nothing. `limit_seconds` and `remaining_seconds` are `null`
179
+ * exactly when `unlimited` is `true`.
180
+ *
181
+ * Worth reading twice - once before the upload, so 250 MiB is not spent on a
182
+ * budget that is already gone, and once when the window is known, because
183
+ * that check is exact and the server refuses a window that would cross the
184
+ * ceiling rather than truncating it.
185
+ */
186
+ quota(options?: RequestOptions): Promise<SecondsQuota>;
187
+ /**
188
+ * `POST /caption_jobs` - step 1. Uploads the video and probes it.
189
+ *
190
+ * The bytes go to the captions sidecar, which keeps the file; Rails stores
191
+ * only the row and, at the end, the rendered output. That is also why a
192
+ * caption job cannot be resumed after the sidecar's volume is wiped: the row
193
+ * survives and the video does not, and step 2 then fails.
194
+ *
195
+ * Answers with the row in `"uploaded"`, carrying the probed `width`,
196
+ * `height`, `fps` and `duration`. Nothing is running yet, so this call does
197
+ * not wait.
198
+ *
199
+ * NOT retried by default: replaying this `POST` after a 502 re-uploads up to
200
+ * 250 MiB and leaves an orphan job behind. Pass `retry: {}` to opt back in.
201
+ *
202
+ * @throws {OmsApiError} 413 over 250 MiB, 400 over 20 minutes or when the
203
+ * file cannot be read, 503 when the captions sidecar is down - in which
204
+ * case the row is destroyed rather than left dangling.
205
+ * @throws {OmsQuotaError} 429 from the expensive-tools throttle. The upload
206
+ * itself spends no daily quota.
207
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
208
+ */
209
+ create(input: CreateCaptionJobInput, options?: RequestOptions): Promise<CaptionJob>;
210
+ /**
211
+ * `GET /caption_jobs/:id` - one poll.
212
+ *
213
+ * Carries `progress_percent` while transcribing or rendering, `render_stage`
214
+ * while rendering, `words` from `"transcribed"` onwards and `output_url`
215
+ * once `"complete"`.
216
+ *
217
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
218
+ * @throws {OmsAuthError} 401 when the job belongs to someone else, which
219
+ * includes an anonymous job being read from a different address.
220
+ */
221
+ get(id: Id, options?: RequestOptions): Promise<CaptionJob>;
222
+ /**
223
+ * `POST /caption_jobs/:id/transcribe` - step 2, on one window.
224
+ *
225
+ * WAITS: the request only moves the row to `"transcribing"`, and this
226
+ * resolves once it has settled on `"transcribed"` or `"failed"`. Returning
227
+ * the busy row instead would hand the caller something whose only use is to
228
+ * cause a 409 on the next call.
229
+ *
230
+ * Windows accumulate. Transcribing a second window replaces `words` with the
231
+ * new window's words and ADDS its seconds to `transcribed_seconds`, so
232
+ * re-transcribing costs quota every time and does not give you both windows.
233
+ *
234
+ * `language` defaults to `"auto"` server-side. Whisper detects well enough
235
+ * that a hint is worth passing only when it has already got it wrong.
236
+ *
237
+ * Resolves with a `"failed"` row rather than throwing when the transcription
238
+ * itself failed - including the common case of a window with no speech in
239
+ * it, which fails rather than returning an empty word list.
240
+ *
241
+ * @throws {OmsApiError} 409 when the job is already busy, 400 for a window
242
+ * that is inverted, past the end of the video, longer than 15 minutes, or
243
+ * longer than the whole daily quota.
244
+ * @throws {OmsQuotaError} 429 when this window would cross the daily ceiling,
245
+ * or from the expensive-tools throttle.
246
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
247
+ * `code: "aborted"` when the signal fires. Neither cancels the step: the
248
+ * quota is spent either way and the words still land on the row.
249
+ */
250
+ transcribe(id: Id, input: TranscribeCaptionInput, options?: ToolRunOptions): Promise<CaptionJob>;
251
+ /**
252
+ * `POST /caption_jobs/:id/render` - step 3. Named `render` here even though
253
+ * the route is `start_render`, because `render` clashes with Rails' own
254
+ * method and that is the backend's problem, not the SDK's.
255
+ *
256
+ * WAITS, like {@link transcribe}, and for the same reason.
257
+ *
258
+ * Omitting `words` costs an extra `GET`. The endpoint has no fallback of its
259
+ * own - it refuses a request whose word list is missing or empty, even for a
260
+ * job whose row is full of words - so the SDK reads the row and sends those
261
+ * back. That is the documented behaviour of `RenderCaptionInput.words`, and
262
+ * it is cheaper to pay one `GET` here than to make every caller learn it.
263
+ *
264
+ * A render re-encodes the whole video, so this is the slow step: a
265
+ * twenty-minute file is minutes of x264 regardless of how short the
266
+ * captioned part is.
267
+ *
268
+ * Resolves with a `"failed"` row rather than throwing when the render failed.
269
+ *
270
+ * @throws {OmsError} `conflict` when there are no words to render at all -
271
+ * raised here, before the request, so the message says "transcribe first"
272
+ * rather than repeating the server's phrasing of it.
273
+ * @throws {OmsApiError} 409 when the job is already busy, 400 for a word
274
+ * whose timings fall outside the video or for more than 3000 of them.
275
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
276
+ * `code: "aborted"` when the signal fires. Neither cancels the render.
277
+ */
278
+ render(id: Id, input?: RenderCaptionInput, options?: ToolRunOptions): Promise<CaptionJob>;
279
+ /**
280
+ * `DELETE /caption_jobs/:id` - drops the row and the sidecar's copy.
281
+ *
282
+ * Best effort on the sidecar's side: the row goes either way. Worth calling
283
+ * on a job you have finished with, because the uploaded video sits on the
284
+ * sidecar's disk until the 24-hour sweep otherwise.
285
+ *
286
+ * @throws {OmsApiError} 404 when it is already gone.
287
+ * @throws {OmsAuthError} 401 when the job belongs to someone else.
288
+ */
289
+ delete(id: Id, options?: RequestOptions): Promise<void>;
290
+ /**
291
+ * Downloads the rendered video of a finished job.
292
+ *
293
+ * Two calls: one to read the row for its `output_url`, one to fetch the
294
+ * signed URL itself with NO credential attached. Hand a row you already hold
295
+ * to {@link outputUrl} plus `fetchToolArtifact` if you would rather not pay
296
+ * for the first.
297
+ *
298
+ * @throws {OmsError} `conflict` when the job has not been rendered yet -
299
+ * which includes a job that is merely `"transcribed"`, `invalid_request`
300
+ * when the render failed, `not_found` when the artefact is gone.
301
+ */
302
+ download(id: Id, options?: RequestOptions): Promise<Blob>;
303
+ /**
304
+ * The signed URL of the rendered video, from a row you already hold.
305
+ *
306
+ * It is a credential: anyone holding it can watch the video.
307
+ *
308
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
309
+ */
310
+ outputUrl(record: CaptionJob): string;
311
+ /**
312
+ * Waits for a started step to settle, reusing the one polling loop.
313
+ *
314
+ * `terminal` here is "not busy" rather than "finished": step 2 settles on
315
+ * `"transcribed"`, which is idle and very much not the end of the job.
316
+ */
317
+ private settle;
318
+ }