@omelhorsite/sdk 0.17.0 → 0.18.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.
@@ -20,7 +20,7 @@ export type NotificationCategory = (typeof NOTIFICATION_CATEGORIES)[number];
20
20
  * `admin_*` kinds only ever reach administrators. The `_done` / `_failed`
21
21
  * pairs of the media tools only reach the account that started the job.
22
22
  */
23
- export declare const NOTIFICATION_KINDS: readonly ["security_new_session", "security_password_changed", "security_email_changed", "security_passkey_added", "security_app_authorized", "friendship_request", "friendship_accepted", "user_followed", "message_received", "group_chat_message", "jam_invite", "fs_grant_received", "chest_expires_soon", "vocal_separation_done", "vocal_separation_failed", "transcription_done", "transcription_failed", "upscale_done", "upscale_failed", "background_removal_done", "background_removal_failed", "caption_job_done", "caption_job_failed", "jumpstyle_job_done", "jumpstyle_job_failed", "song_import_done", "song_import_failed", "spotify_sync_done", "spotify_sync_failed", "artist_import_done", "blog_new_post", "form_submission_received", "ticket_reply", "ticket_status_changed", "news_source_failing", "cron_run_failed", "cron_run_done", "oauth_application_approved", "oauth_application_rejected", "admin_oauth_application_submitted", "admin_ticket_created", "admin_feedback_received"];
23
+ export declare const NOTIFICATION_KINDS: readonly ["security_new_session", "security_password_changed", "security_email_changed", "security_passkey_added", "security_app_authorized", "friendship_request", "friendship_accepted", "user_followed", "message_received", "group_chat_message", "jam_invite", "fs_grant_received", "chest_expires_soon", "vocal_separation_done", "vocal_separation_failed", "transcription_done", "transcription_failed", "upscale_done", "upscale_failed", "background_removal_done", "background_removal_failed", "caption_job_done", "caption_job_failed", "jumpstyle_job_done", "jumpstyle_job_failed", "song_import_done", "song_import_failed", "spotify_sync_done", "spotify_sync_failed", "artist_import_done", "blog_new_post", "form_submission_received", "ticket_reply", "ticket_status_changed", "news_source_failing", "cron_run_failed", "cron_run_done", "bot_message_received", "bot_needs_human", "oauth_application_approved", "oauth_application_rejected", "admin_oauth_application_submitted", "admin_ticket_created", "admin_feedback_received"];
24
24
  /**
25
25
  * One of {@link NOTIFICATION_KINDS}.
26
26
  *
@@ -2,25 +2,49 @@
2
2
  * The `cron` namespace: TypeScript scripts of the signed-in person that the
3
3
  * server runs on a schedule.
4
4
  *
5
- * A script is an ES module exporting `run(ctx)`. It runs with nothing but the
6
- * API: no filesystem, no environment, no processes, and no network beyond
7
- * this SDK (`ctx.oms`, authenticated as the owner with the job's scopes) and,
8
- * when the job has `network` on, a `ctx.fetch` that goes through the server's
9
- * guard against private addresses. What it keeps in `ctx.state` is stored
10
- * when a run ends well and handed back on the next one; what it returns is
11
- * the run's `result`, and `result.summary` is what the listing shows.
5
+ * A script is an ES module exporting `run(ctx)`. WHERE it runs follows from
6
+ * what it declares: a job with no scopes runs in the light sandbox (the one
7
+ * the news sources use: no token, no SDK, only a guarded `fetchText` and the
8
+ * HTML/XML helpers, thousands of runs a day); a job with scopes runs in the
9
+ * full runner, with `ctx.oms` authenticated as the owner and, when the job
10
+ * has `network` on, a `ctx.fetch` that goes through the server's guard
11
+ * against private addresses. The base contract (`config`, `params`, `state`,
12
+ * `log`, `fetchText`, `select`, `parseXml`, `htmlText`) is the same in both.
13
+ * What a script keeps in `ctx.state` is stored when a run ends well and
14
+ * handed back on the next one; what it returns is the run's `result`, and
15
+ * `result.summary` is what the listing shows.
16
+ *
17
+ * A job whose `output` is `{ kind: "items" }` returns `{ items, cursor? }`
18
+ * and the server stores the items in the news source the run belongs to.
19
+ *
20
+ * A job has zero or more SCHEDULES ({@link CronSchedule}): each a cron
21
+ * expression in a timezone with its own `params` and switch. A run carries
22
+ * `params`: an object merged over the job's `config` (so the script reads
23
+ * `ctx.config` as usual) and handed on its own as `ctx.params`. They come
24
+ * from the schedule that fired, or from whoever called `run`.
12
25
  *
13
26
  * Needs the `cron:read` scope to read and `cron:write` to change anything.
14
- * A job's own token never carries either: a script cannot edit jobs.
27
+ * A job's own token never carries either; with `cron:run` it may start other
28
+ * jobs.
15
29
  */
16
30
  import { Resource, type ApiClient } from "../http";
17
31
  import type { ListParams } from "../listing";
18
- import type { Id, Json, Paginated, RequestOptions, Timestamp } from "../types";
19
- /** Scopes a job may ask for its token. Anything else answers `400`. */
20
- export declare const CRON_JOB_SCOPES: readonly ["profile", "news:read", "news:write", "storage:read", "storage:write", "llm", "tools:read", "tools:write", "blogs:read", "blogs:write"];
32
+ import type { Id, Json, JsonObject, Paginated, RequestOptions, Timestamp } from "../types";
33
+ /**
34
+ * Scopes a job may ask for its token. Anything else answers `400`. `cron:run`
35
+ * lets a script run other jobs (with params) without reading or editing them.
36
+ */
37
+ export declare const CRON_JOB_SCOPES: readonly ["profile", "news:read", "news:write", "storage:read", "storage:write", "llm", "tools:read", "tools:write", "blogs:read", "blogs:write", "bots:read", "bots:write", "cron:run"];
21
38
  export type CronJobScope = (typeof CRON_JOB_SCOPES)[number];
22
39
  export declare const CRON_JOB_HEALTHS: readonly ["unknown", "ok", "error"];
23
40
  export type CronJobHealth = (typeof CRON_JOB_HEALTHS)[number];
41
+ /** Where a job runs, derived from its scopes: none means the light sandbox. */
42
+ export declare const CRON_JOB_RUNTIMES: readonly ["sandbox", "full"];
43
+ export type CronJobRuntime = (typeof CRON_JOB_RUNTIMES)[number];
44
+ /** What a job's runs produce, when it is not just a result: news items for a source. */
45
+ export interface CronJobOutput {
46
+ readonly kind: "items";
47
+ }
24
48
  /** Consecutive failures after which a job switches itself off. Turn `enabled` back on to revive it. */
25
49
  export declare const CRON_JOB_DISABLE_AFTER_FAILURES = 10;
26
50
  /** Code longer than this answers `400`. */
@@ -28,8 +52,10 @@ export declare const CRON_JOB_MAX_CODE_BYTES: number;
28
52
  /** `state` and `config` ceilings. A run whose state grows past this fails and the state is not stored. */
29
53
  export declare const CRON_JOB_MAX_STATE_BYTES: number;
30
54
  export declare const CRON_JOB_MAX_CONFIG_BYTES: number;
31
- /** Two runs of one job must be at least this far apart. */
55
+ /** Two occurrences of one schedule must be at least this far apart. */
32
56
  export declare const CRON_JOB_MIN_INTERVAL_MINUTES = 5;
57
+ /** Schedules one job may have. */
58
+ export declare const CRON_SCHEDULES_MAX_PER_JOB = 200;
33
59
  /** `timeout_seconds` range; above 120 the account needs a trusted tier, like `network`. */
34
60
  export declare const CRON_JOB_MIN_TIMEOUT_SECONDS = 5;
35
61
  export declare const CRON_JOB_MAX_TIMEOUT_SECONDS = 1200;
@@ -41,20 +67,27 @@ export interface CronJob {
41
67
  /** Up to 120 characters, unique per account. */
42
68
  readonly name: string;
43
69
  readonly description: string | null;
44
- /** Five-field cron expression, read in `timezone`. */
45
- readonly schedule: string;
46
- /** An IANA zone such as `"Europe/Lisbon"` (the default). */
70
+ /** The cron expression of the job's FIRST schedule, or `null` when it has none. The whole list is in {@link CronJobDetail.schedules}. */
71
+ readonly schedule: string | null;
72
+ /** How many schedules the job has. */
73
+ readonly schedules_count: number;
74
+ /** An IANA zone such as `"Europe/Lisbon"` (the default); the default timezone of new schedules. */
47
75
  readonly timezone: string;
48
- /** The scopes the run's token carries. */
76
+ /** The scopes the run's token carries. Empty means the job runs in the sandbox, with no token. */
49
77
  readonly scopes: CronJobScope[];
78
+ /** Derived from `scopes`: `sandbox` when there are none, `full` otherwise. */
79
+ readonly runtime: CronJobRuntime;
80
+ /** `{ kind: "items" }` when the script returns news items the server ingests; `null` for a plain result. */
81
+ readonly output: CronJobOutput | null;
50
82
  /** Names of the stored secrets. The values never leave the server. */
51
83
  readonly secret_keys: string[];
52
- /** Whether `ctx.fetch` exists in the script. */
84
+ /** Whether `ctx.fetch`/`ctx.fetchText` exist in the script. In the sandbox this needs no trusted tier. */
53
85
  readonly network: boolean;
54
86
  readonly timeout_seconds: number;
55
87
  /** A folder of the owner's storage handed to the script as `ctx.job.outputDirId`. */
56
88
  readonly output_dir_id: Id | null;
57
89
  readonly enabled: boolean;
90
+ /** The nearest occurrence among the enabled schedules; `null` when the job is off or has none. */
58
91
  readonly next_run_at: Timestamp | null;
59
92
  readonly last_run_at: Timestamp | null;
60
93
  readonly last_success_at: Timestamp | null;
@@ -80,26 +113,73 @@ export interface CronVar {
80
113
  readonly label: string;
81
114
  readonly description: string | null;
82
115
  }
83
- /** `GET /cron_jobs/:id` (and every write) adds the code, the config, the state and the declared variables. */
116
+ /** `GET /cron_jobs/:id` (and every write) adds the code, the config, the state, the declared variables and the schedules. */
84
117
  export interface CronJobDetail extends CronJob {
85
118
  readonly code: string;
86
119
  readonly config: Record<string, Json>;
87
120
  readonly state: Record<string, Json>;
88
121
  readonly vars: CronVar[];
122
+ /** Oldest first. */
123
+ readonly schedules: CronSchedule[];
124
+ }
125
+ /**
126
+ * One schedule of a job: when it fires and with what params. A job may have
127
+ * several (a source per site, a report per feed) or none (run by hand, by
128
+ * the API or by another job).
129
+ */
130
+ export interface CronSchedule {
131
+ readonly id: Id;
132
+ readonly created_at: Timestamp;
133
+ readonly updated_at: Timestamp;
134
+ readonly cron_job_id: Id;
135
+ /** Optional label, up to 120 characters. */
136
+ readonly name: string | null;
137
+ /** Five-field cron expression, read in `timezone`. */
138
+ readonly cron: string;
139
+ readonly timezone: string;
140
+ /** Handed to every run this schedule starts, merged over the job's config and as `ctx.params`. */
141
+ readonly params: Record<string, Json>;
142
+ /** Off, the schedule does not fire; the job's other schedules still do. */
143
+ readonly enabled: boolean;
144
+ readonly next_run_at: Timestamp | null;
145
+ readonly last_run_at: Timestamp | null;
146
+ }
147
+ export declare const CRON_SCHEDULE_FILTER_COLUMNS: readonly ["cron_job_id", "enabled"];
148
+ export interface ListCronSchedulesParams extends ListParams<(typeof CRON_SCHEDULE_FILTER_COLUMNS)[number]> {
149
+ readonly jobId?: Id;
150
+ readonly enabled?: boolean;
89
151
  }
152
+ export interface CreateCronScheduleInput {
153
+ readonly jobId: Id;
154
+ /** Five fields; two occurrences must be at least {@link CRON_JOB_MIN_INTERVAL_MINUTES} apart. */
155
+ readonly cron: string;
156
+ /** Defaults to `"Europe/Lisbon"`. */
157
+ readonly timezone?: string;
158
+ readonly name?: string | null;
159
+ /** Up to 64 KB. */
160
+ readonly params?: Record<string, Json>;
161
+ readonly enabled?: boolean;
162
+ }
163
+ export type UpdateCronScheduleInput = Partial<Omit<CreateCronScheduleInput, "jobId">>;
90
164
  export declare const CRON_JOB_FILTER_COLUMNS: readonly ["name", "enabled", "health", "template_slug"];
91
165
  export interface ListCronJobsParams extends ListParams<(typeof CRON_JOB_FILTER_COLUMNS)[number]> {
92
166
  }
93
167
  export interface CreateCronJobInput {
94
168
  readonly name: string;
95
169
  readonly description?: string | null;
96
- /** Five fields. Two runs must be at least {@link CRON_JOB_MIN_INTERVAL_MINUTES} apart. */
97
- readonly schedule: string;
170
+ /**
171
+ * Creates the job's first schedule (in `timezone`). Optional: a job with no
172
+ * schedule runs by hand, by the API or by another job. Add more with
173
+ * {@link CronSchedulesNamespace.create}; on update this edits the first one.
174
+ */
175
+ readonly schedule?: string;
98
176
  readonly timezone?: string;
99
177
  /** TypeScript. Checked for syntax on the server before it is stored. */
100
178
  readonly code: string;
101
- /** Defaults to every scope but `news:write` and `tools:write`. */
179
+ /** Defaults to every scope but `news:write` and `tools:write`. Pass `[]` for a sandbox job (no token, no SDK). */
102
180
  readonly scopes?: readonly CronJobScope[];
181
+ /** `{ kind: "items" }` makes the runs' items go to the news source of each run. `null` removes it. */
182
+ readonly output?: CronJobOutput | null;
103
183
  readonly config?: Record<string, Json>;
104
184
  /** Stored encrypted; on update the object is MERGED, and a `null` value removes that key. */
105
185
  readonly secrets?: Record<string, string | null>;
@@ -115,16 +195,28 @@ export interface CreateCronJobInput {
115
195
  export type UpdateCronJobInput = Partial<Omit<CreateCronJobInput, "templateSlug">>;
116
196
  export declare const CRON_RUN_STATUSES: readonly ["queued", "running", "ok", "error", "timeout", "skipped"];
117
197
  export type CronRunStatus = (typeof CRON_RUN_STATUSES)[number];
118
- export declare const CRON_RUN_TRIGGERS: readonly ["schedule", "manual", "test"];
198
+ /**
199
+ * Who asked for a run: a schedule, a person with a session (`manual`), a
200
+ * third-party OAuth token (`api`), another job's run token (`job`), or a test.
201
+ */
202
+ export declare const CRON_RUN_TRIGGERS: readonly ["schedule", "manual", "api", "job", "test"];
119
203
  export type CronRunTrigger = (typeof CRON_RUN_TRIGGERS)[number];
120
204
  export interface CronRun {
121
205
  readonly id: Id;
122
206
  readonly created_at: Timestamp;
123
207
  readonly updated_at: Timestamp;
124
208
  readonly cron_job_id: Id;
209
+ /** The schedule that fired it, when the trigger is `schedule` and it still exists. */
210
+ readonly cron_schedule_id: Id | null;
211
+ /** The news source that received the items, for runs of a job with `output` items bound to a source. */
212
+ readonly news_source_id: Id | null;
213
+ /** Where it ran; `null` while queued. Sandbox runs do not count towards `cron_run_seconds`. */
214
+ readonly runtime: CronJobRuntime | null;
125
215
  readonly status: CronRunStatus;
126
216
  /** `test` runs never write the state back and never count as failures. */
127
217
  readonly trigger: CronRunTrigger;
218
+ /** Merged over the job's config for this run and handed to the script as `ctx.params`. */
219
+ readonly params: Record<string, Json>;
128
220
  readonly scheduled_at: Timestamp;
129
221
  readonly started_at: Timestamp | null;
130
222
  readonly finished_at: Timestamp | null;
@@ -150,6 +242,11 @@ export interface ListCronRunsParams extends ListParams<(typeof CRON_RUN_FILTER_C
150
242
  readonly jobId?: Id;
151
243
  readonly status?: CronRunStatus;
152
244
  }
245
+ /** What `run` and `test` accept. */
246
+ export interface RunCronJobInput {
247
+ /** Merged over the job's config for this run only; the script also gets them as `ctx.params`. Up to 64 KB. */
248
+ readonly params?: Record<string, Json>;
249
+ }
153
250
  /** A ready-made script: copy it into a job with {@link CronJobsNamespace.create}. */
154
251
  export interface CronTemplate {
155
252
  readonly slug: string;
@@ -157,7 +254,10 @@ export interface CronTemplate {
157
254
  readonly description: string;
158
255
  /** The schedule the template expects. */
159
256
  readonly schedule: string;
257
+ /** Empty for sandbox templates (the news source scripts). */
160
258
  readonly scopes: CronJobScope[];
259
+ readonly runtime: CronJobRuntime;
260
+ readonly output: CronJobOutput | null;
161
261
  readonly network: boolean;
162
262
  /** The config the script reads, with defaults. */
163
263
  readonly config: Record<string, Json>;
@@ -195,15 +295,33 @@ export declare class CronJobsNamespace extends Resource {
195
295
  * `POST /cron_jobs/:id/run` - runs now, outside the schedule, as a normal
196
296
  * run (the state is stored). `202` with the queued run.
197
297
  *
198
- * @throws {OmsApiError} 400 `this job is already running`; 429
199
- * `error: "limit"` when the day's `cron_run_seconds` are spent.
298
+ * @throws {OmsApiError} 400 `this job is already running`, or
299
+ * `params must be an object`; 429 `error: "limit"` when the day's
300
+ * `cron_run_seconds` are spent.
200
301
  */
201
- run(id: Id, options?: RequestOptions): Promise<CronRun>;
302
+ run(id: Id, input?: RunCronJobInput, options?: RequestOptions): Promise<CronRun>;
202
303
  /** `POST /cron_jobs/:id/test` - runs now WITHOUT storing the state or counting a failure. `202`. */
203
- test(id: Id, options?: RequestOptions): Promise<CronRun>;
304
+ test(id: Id, input?: RunCronJobInput, options?: RequestOptions): Promise<CronRun>;
204
305
  /** `POST /cron_jobs/check` - the syntax check the server runs before storing code, on its own. */
205
306
  check(code: string, options?: RequestOptions): Promise<CronCheckResult>;
206
307
  }
308
+ export declare class CronSchedulesNamespace extends Resource {
309
+ /** `GET /cron_schedules` - oldest first, narrowed by job. */
310
+ list(params?: ListCronSchedulesParams, options?: RequestOptions): Promise<Paginated<CronSchedule>>;
311
+ get(id: Id, options?: RequestOptions): Promise<CronSchedule>;
312
+ /**
313
+ * `POST /cron_schedules`. `201`.
314
+ *
315
+ * @throws {OmsApiError} 400 with the sentence: a bad or too frequent cron,
316
+ * an unknown timezone, `params must be an object`,
317
+ * `cron_job_id must be one of your jobs`, or `schedule limit reached`.
318
+ */
319
+ create(input: CreateCronScheduleInput, options?: RequestOptions): Promise<CronSchedule>;
320
+ /** `PATCH /cron_schedules/:id`. Changing the cron or the timezone recomputes `next_run_at`. */
321
+ update(id: Id, input: UpdateCronScheduleInput, options?: RequestOptions): Promise<CronSchedule>;
322
+ /** `DELETE /cron_schedules/:id`. Past runs keep existing without it. `204`. */
323
+ delete(id: Id, options?: RequestOptions): Promise<void>;
324
+ }
207
325
  export declare class CronRunsNamespace extends Resource {
208
326
  /** `GET /cron_runs` - newest first. */
209
327
  list(params?: ListCronRunsParams, options?: RequestOptions): Promise<Paginated<CronRun>>;
@@ -217,9 +335,168 @@ export declare class CronTemplatesNamespace extends Resource {
217
335
  get(slug: string, options?: RequestOptions): Promise<CronTemplateDetail>;
218
336
  }
219
337
  /** The `cron` namespace, reachable as `oms.cron`. */
338
+ /**
339
+ * A job's small database: rows a script keeps between runs, instead of one
340
+ * JSON file it has to read and rewrite whole to change a line.
341
+ *
342
+ * A row lives in a COLLECTION of the job (`"mentions"`, `"seen"`), under a
343
+ * KEY unique within it, and holds a JSON object. `at` is the row's own
344
+ * instant - the date of the mention, the delivery, the decision - and is what
345
+ * {@link CronRecordsNamespace.list} orders by and
346
+ * {@link CronRecordsNamespace.prune} trims. Writing is an upsert: the data is
347
+ * replaced, and `at` only changes when the write carries one.
348
+ *
349
+ * Reading needs `cron:read` and writing `cron:write`; a script running as the
350
+ * job reaches its OWN job's records with the token it already has.
351
+ */
352
+ export interface CronRecord {
353
+ readonly id: Id;
354
+ readonly created_at: Timestamp;
355
+ readonly updated_at: Timestamp;
356
+ readonly cron_job_id: Id;
357
+ readonly collection: string;
358
+ readonly key: string;
359
+ readonly data: JsonObject;
360
+ readonly at: Timestamp | null;
361
+ }
362
+ /** Lowercase letters, digits, `-` and `_`, up to this many characters. */
363
+ export declare const CRON_RECORD_COLLECTION_MAX = 64;
364
+ /** Longer keys are shortened by {@link cronRecordKey} before they are sent. */
365
+ export declare const CRON_RECORD_KEY_MAX = 200;
366
+ /** A row's `data`, serialised. */
367
+ export declare const CRON_RECORD_MAX_DATA_BYTES: number;
368
+ /** Rows one job may hold. Past it new keys are refused; the ones already there still update. */
369
+ export declare const CRON_RECORD_MAX_PER_JOB = 200000;
370
+ /** Rows one write may carry. {@link CronRecordsNamespace.putMany} splits longer lists. */
371
+ export declare const CRON_RECORD_MAX_PER_BATCH = 500;
372
+ /** Rows one {@link CronRecordsNamespace.prune} deletes. Call it again until it answers `0`. */
373
+ export declare const CRON_RECORD_MAX_PER_DELETE = 10000;
374
+ /** `where` fields one listing may compare. */
375
+ export declare const CRON_RECORD_MAX_WHERE = 5;
376
+ export declare const CRON_RECORD_MAX_LIMIT = 500;
377
+ /** What a listing sorts by. Descending unless the suffix says otherwise; rows without an `at` come last either way. */
378
+ export type CronRecordOrder = "at" | "at:asc" | "at:desc" | "updated_at" | "updated_at:asc" | "updated_at:desc";
379
+ export interface ListCronRecordsParams {
380
+ readonly collection: string;
381
+ /** 1 to {@link CRON_RECORD_MAX_LIMIT}; 50 by default. */
382
+ readonly limit?: number;
383
+ /** The `next_cursor` of the previous page. */
384
+ readonly cursor?: string | null;
385
+ /** Over `at`, inclusive. A row without an `at` is outside both. */
386
+ readonly since?: string | Date;
387
+ readonly until?: string | Date;
388
+ /** Fetch several rows at once. Long keys are shortened as {@link CronRecordsNamespace.put} shortens them. */
389
+ readonly keys?: readonly string[];
390
+ /** Equality against `data->>field`, as text. At most {@link CRON_RECORD_MAX_WHERE} fields. */
391
+ readonly where?: Readonly<Record<string, string | number | boolean>>;
392
+ readonly order?: CronRecordOrder;
393
+ /** Ask for `count`: how many rows the filter matches, not just this page. */
394
+ readonly count?: boolean;
395
+ }
396
+ export interface CronRecordPage {
397
+ readonly records: CronRecord[];
398
+ /** `null` on the last page. */
399
+ readonly next_cursor: string | null;
400
+ /** `null` unless the request asked for it. */
401
+ readonly count: number | null;
402
+ }
403
+ export interface CronRecordInput {
404
+ readonly collection: string;
405
+ readonly key: string;
406
+ /** Up to {@link CRON_RECORD_MAX_DATA_BYTES} serialised. Defaults to `{}`. */
407
+ readonly data?: JsonObject;
408
+ /** The row's own instant. Left out, a row that already exists keeps the one it has. */
409
+ readonly at?: string | Date | null;
410
+ }
411
+ export interface CronRecordWriteResult {
412
+ readonly created: number;
413
+ readonly updated: number;
414
+ }
415
+ export interface CronRecordDeleteResult {
416
+ readonly deleted: number;
417
+ }
418
+ /** One collection of a job, as {@link CronRecordsNamespace.collections} reports it. */
419
+ export interface CronRecordCollection {
420
+ readonly collection: string;
421
+ readonly count: number;
422
+ readonly first_at: Timestamp | null;
423
+ readonly last_at: Timestamp | null;
424
+ readonly updated_at: Timestamp;
425
+ }
426
+ export interface PruneCronRecordsOptions {
427
+ /** Delete the rows at or before this instant. */
428
+ readonly before: string | Date;
429
+ /** Which instant to read: the row's own (default) or the last write. */
430
+ readonly on?: "at" | "updated_at";
431
+ }
432
+ /**
433
+ * The key the server stores for a value. Anything past
434
+ * {@link CRON_RECORD_KEY_MAX} keeps its start and gains a fingerprint of the
435
+ * whole, so a link stays a usable key and always resolves to the same row.
436
+ *
437
+ * @throws {TypeError} on a runtime without WebCrypto, and only for a value
438
+ * long enough to need shortening.
439
+ */
440
+ export declare function cronRecordKey(value: string): Promise<string>;
441
+ export declare class CronRecordsNamespace extends Resource {
442
+ /**
443
+ * `GET /cron_jobs/:id/records` - one page of a collection, newest first,
444
+ * paged by cursor rather than by offset so writes in between neither repeat
445
+ * nor skip a row.
446
+ *
447
+ * @throws {OmsApiError} 400 for a missing or malformed `collection`, a
448
+ * `limit` outside 1..500, an unknown `order`, an unparsable date, a
449
+ * cursor from somewhere else, or more than 5 `where` fields.
450
+ */
451
+ list(jobId: Id, params: ListCronRecordsParams, options?: RequestOptions): Promise<CronRecordPage>;
452
+ /** Every row of a collection, page after page. The filters of {@link list} apply. */
453
+ all(jobId: Id, collection: string, params?: Omit<ListCronRecordsParams, "collection" | "cursor" | "count">, options?: RequestOptions): AsyncGenerator<CronRecord>;
454
+ /** One row, or `null` when the collection has no such key. */
455
+ get(jobId: Id, collection: string, key: string, options?: RequestOptions): Promise<CronRecord | null>;
456
+ /** `POST /cron_jobs/:id/records` - creates or replaces one row. */
457
+ put(jobId: Id, collection: string, key: string, data?: JsonObject, at?: string | Date | null, options?: RequestOptions): Promise<CronRecordWriteResult>;
458
+ /**
459
+ * Creates or replaces many rows. Each request is one transaction; a list
460
+ * longer than {@link CRON_RECORD_MAX_PER_BATCH} goes in several, so a
461
+ * failure part-way leaves the batches before it written.
462
+ *
463
+ * @throws {OmsApiError} 400 for a bad collection, an empty or too long key,
464
+ * `data` that is not an object or is past the size ceiling, or
465
+ * `record limit reached` when the job is full.
466
+ */
467
+ putMany(jobId: Id, records: readonly CronRecordInput[], options?: RequestOptions): Promise<CronRecordWriteResult>;
468
+ /** `DELETE /cron_jobs/:id/records` - one row. `deleted` is `0` when there was none. */
469
+ remove(jobId: Id, collection: string, key: string, options?: RequestOptions): Promise<CronRecordDeleteResult>;
470
+ /** Several rows by key, in one request. */
471
+ removeMany(jobId: Id, collection: string, keys: readonly string[], options?: RequestOptions): Promise<CronRecordDeleteResult>;
472
+ /**
473
+ * Deletes the old rows of a collection, up to
474
+ * {@link CRON_RECORD_MAX_PER_DELETE} per call - call it again while
475
+ * `deleted` comes back full.
476
+ */
477
+ prune(jobId: Id, collection: string, options: PruneCronRecordsOptions, requestOptions?: RequestOptions): Promise<CronRecordDeleteResult>;
478
+ /** `GET /cron_jobs/:id/records/collections` - what the job holds, by name. */
479
+ collections(jobId: Id, options?: RequestOptions): Promise<CronRecordCollection[]>;
480
+ /** The same methods bound to one job, which is what a running script is handed. */
481
+ for(jobId: Id): BoundCronRecords;
482
+ }
483
+ /** {@link CronRecordsNamespace} with the job already filled in. */
484
+ export interface BoundCronRecords {
485
+ list(params: ListCronRecordsParams, options?: RequestOptions): Promise<CronRecordPage>;
486
+ all(collection: string, params?: Omit<ListCronRecordsParams, "collection" | "cursor" | "count">, options?: RequestOptions): AsyncGenerator<CronRecord>;
487
+ get(collection: string, key: string, options?: RequestOptions): Promise<CronRecord | null>;
488
+ put(collection: string, key: string, data?: JsonObject, at?: string | Date | null, options?: RequestOptions): Promise<CronRecordWriteResult>;
489
+ putMany(records: readonly CronRecordInput[], options?: RequestOptions): Promise<CronRecordWriteResult>;
490
+ remove(collection: string, key: string, options?: RequestOptions): Promise<CronRecordDeleteResult>;
491
+ removeMany(collection: string, keys: readonly string[], options?: RequestOptions): Promise<CronRecordDeleteResult>;
492
+ prune(collection: string, options: PruneCronRecordsOptions, requestOptions?: RequestOptions): Promise<CronRecordDeleteResult>;
493
+ collections(options?: RequestOptions): Promise<CronRecordCollection[]>;
494
+ }
220
495
  export declare class CronNamespace extends Resource {
221
496
  readonly jobs: CronJobsNamespace;
497
+ readonly schedules: CronSchedulesNamespace;
222
498
  readonly runs: CronRunsNamespace;
223
499
  readonly templates: CronTemplatesNamespace;
500
+ readonly records: CronRecordsNamespace;
224
501
  constructor(http: ApiClient);
225
502
  }
@@ -23,6 +23,7 @@ export * from "./auth/index";
23
23
  export * from "./chests";
24
24
  export * from "./content";
25
25
  export * from "./cron";
26
+ export * from "./bots";
26
27
  export * from "./dynamicQrs";
27
28
  export * from "./forms";
28
29
  export * from "./ipLookup";
@@ -123,6 +123,11 @@ export interface SearchPage {
123
123
  readonly title: string;
124
124
  /** Plain text, whitespace collapsed, cut at `maxChars`. Never empty: a page with nothing readable is a `422`. */
125
125
  readonly text: string;
126
+ /**
127
+ * When the page says it was published (`article:published_time`, JSON-LD
128
+ * `datePublished`, a `<time datetime>`), as ISO 8601; `null` when it does not.
129
+ */
130
+ readonly published_at: string | null;
126
131
  }
127
132
  /** The `search` namespace, reachable as `oms.search`. */
128
133
  export declare class SearchNamespace extends Resource {
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Image generation: a prompt in, a PNG out.
3
+ *
4
+ * Same shape as the other async tools - `POST /image_generations` enqueues a
5
+ * job and answers with a row plus a `job_id` and, when anonymous, a
6
+ * `watch_token`.
7
+ *
8
+ * What is different here is that the work can run in two places, and the
9
+ * caller picks: a model on the server itself, which is free, or a model at a
10
+ * paid provider, which is not. {@link ImageGenerationNamespace.models} lists
11
+ * the ones the CURRENT caller may actually use, so an anonymous caller never
12
+ * sees a model they would be refused for.
13
+ *
14
+ * Two daily quotas apply, not one: a count of images, which every model
15
+ * consumes, and a spend ceiling in USD, which only the paid ones touch.
16
+ */
17
+ import { Resource } from "../../http";
18
+ import type { Id, RequestOptions } from "../../types";
19
+ import { type ToolCaptcha, type ToolJobHandle, type ToolRecord, type ToolRunOptions } from "./index";
20
+ /** Where a model runs. `"local"` is the server itself and always free. */
21
+ export type ImageProvider = "local" | "fal";
22
+ /** One model the caller may pick, as `GET /image_generations/models` lists it. */
23
+ export interface ImageModel {
24
+ readonly key: string;
25
+ readonly name: string;
26
+ readonly description: string;
27
+ readonly provider: ImageProvider;
28
+ /** True when a run costs money and counts against the spend ceiling. */
29
+ readonly billable: boolean;
30
+ /** True on models kept behind an account flag. */
31
+ readonly adult: boolean;
32
+ /** `width * height` may not exceed this. */
33
+ readonly max_pixels: number;
34
+ readonly default_width: number;
35
+ readonly default_height: number;
36
+ readonly default_steps: number;
37
+ readonly max_steps: number;
38
+ /** False on models that ignore `negative_prompt` entirely, such as FLUX. */
39
+ readonly supports_negative_prompt: boolean;
40
+ /** Millionths of a USD per megapixel, rounded up. Zero on local models. */
41
+ readonly cost_per_megapixel_microusd: number;
42
+ }
43
+ /**
44
+ * One generation.
45
+ *
46
+ * Both routes that answer with one - `POST /image_generations` and
47
+ * `GET /image_generations/:id` - answer the same shape, so `result_url` is
48
+ * always PRESENT and simply `null` until the run completes.
49
+ */
50
+ export interface ImageGeneration extends ToolRecord {
51
+ readonly model_key: string;
52
+ readonly provider: ImageProvider;
53
+ readonly prompt: string;
54
+ readonly negative_prompt: string | null;
55
+ readonly width: number;
56
+ readonly height: number;
57
+ readonly steps: number;
58
+ /**
59
+ * The seed the image was made with. Null only while the run is still
60
+ * pending: a caller that sent none gets the one the server picked, which is
61
+ * what makes the image reproducible.
62
+ */
63
+ readonly seed: number | null;
64
+ readonly guidance: string | null;
65
+ /** What this run costs, in millionths of a USD. Zero on local models. */
66
+ readonly cost_microusd: number;
67
+ /**
68
+ * Signed URL of the PNG, or `null`. `null` covers three different
69
+ * situations - the run has not finished, it failed, or the 24-hour sweep
70
+ * took the attachment - which is why {@link ImageGenerationNamespace.resultUrl}
71
+ * exists rather than a bare read of this field.
72
+ */
73
+ readonly result_url: string | null;
74
+ }
75
+ /** What `POST /image_generations` answers with. */
76
+ export type ImageGenerationCreated = ImageGeneration & ToolJobHandle;
77
+ /** Arguments for starting a run. */
78
+ export interface CreateImageGenerationInput extends ToolCaptcha {
79
+ /** What to draw. Cap: 2000 characters. */
80
+ readonly prompt: string;
81
+ /** Defaults to the server's own default model when omitted. */
82
+ readonly model?: string;
83
+ /** Ignored by models whose `supports_negative_prompt` is false. */
84
+ readonly negativePrompt?: string;
85
+ /** Must be a multiple of 8, at least 256. Defaults to the model's own. */
86
+ readonly width?: number;
87
+ /** Must be a multiple of 8, at least 256. Defaults to the model's own. */
88
+ readonly height?: number;
89
+ readonly steps?: number;
90
+ /** Pass the seed of an earlier run to reproduce its image. */
91
+ readonly seed?: number;
92
+ readonly guidance?: number;
93
+ }
94
+ /** The `imageGeneration` tool, reachable as `oms.tools.imageGeneration`. */
95
+ export declare class ImageGenerationNamespace extends Resource {
96
+ private readonly jobs;
97
+ /**
98
+ * `GET /image_generations/models` - the models THIS caller may use.
99
+ *
100
+ * The list is already filtered by session and by account flags, so it is
101
+ * safe to render straight into a picker: everything in it is something
102
+ * {@link create} would accept.
103
+ */
104
+ models(options?: RequestOptions): Promise<ImageModel[]>;
105
+ /**
106
+ * `POST /image_generations` - enqueues a run and returns straight away.
107
+ *
108
+ * Every optional field is omitted from the body when the caller did not set
109
+ * it, so the server applies the chosen model's own defaults rather than the
110
+ * SDK guessing at them.
111
+ *
112
+ * NOT retried by default: replaying this `POST` after a 502 starts a second
113
+ * run, and on a paid model bills for it. Pass `retry: {}` to opt back in.
114
+ *
115
+ * @throws {OmsApiError} 400 for an unknown model, a blank prompt, or
116
+ * dimensions outside the model's limits; 403 for a model this caller may
117
+ * not use; 429 once either daily quota is spent.
118
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
119
+ */
120
+ create(input: CreateImageGenerationInput, options?: RequestOptions): Promise<ImageGenerationCreated>;
121
+ /**
122
+ * `GET /image_generations/:id` - one poll.
123
+ *
124
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
125
+ * @throws {OmsAuthError} 401 when the run belongs to someone else, which
126
+ * includes an anonymous run being read from a different address.
127
+ */
128
+ get(id: Id, options?: RequestOptions): Promise<ImageGeneration>;
129
+ /**
130
+ * Creates a run and waits for it, through `oms.jobs.wait`.
131
+ *
132
+ * Resolves with a `"failed"` row rather than throwing when the work failed.
133
+ * Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
134
+ * deadline.
135
+ *
136
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
137
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
138
+ * up later with {@link get}.
139
+ */
140
+ run(input: CreateImageGenerationInput, options?: ToolRunOptions): Promise<ImageGeneration>;
141
+ /**
142
+ * Downloads the PNG of a finished run.
143
+ *
144
+ * @throws {OmsError} `conflict` when the run has not finished,
145
+ * `invalid_request` when it failed, `not_found` when the artefact is gone.
146
+ */
147
+ download(id: Id, options?: RequestOptions): Promise<Blob>;
148
+ /**
149
+ * The signed URL of a finished run's image, from a row you already hold.
150
+ *
151
+ * It is a credential: anyone holding it can read the image.
152
+ *
153
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
154
+ */
155
+ resultUrl(record: ImageGeneration): string;
156
+ }