craterview 0.4.26 → 0.4.29

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 (3) hide show
  1. package/README.md +12 -11
  2. package/index.ts +62 -32
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # craterview
2
2
 
3
- TypeScript client for the [CraterView](https://craterview.ai) image enhancement and
3
+ TypeScript client for the [CraterView.ai](https://craterview.ai) image enhancement and
4
4
  restoration API.
5
5
 
6
6
  ```bash
@@ -45,7 +45,7 @@ reimplement.
45
45
  Build against `echo` first. It costs no credits, needs no GPU and returns a real result — a
46
46
  plain upscale — so the request, the parameters and the response are the ones a paid model
47
47
  gives, and your integration needs no change when you switch. When you are ready, swap the
48
- model name for the one you want.
48
+ model id for the one you want.
49
49
 
50
50
  ```ts
51
51
  const job = await cv.run(file, {
@@ -57,7 +57,7 @@ const job = await cv.run(file, {
57
57
  const blob = await job.blob();
58
58
  ```
59
59
 
60
- A parameter one model publishes is not one another accepts — `cv.models()` says which — and
60
+ A parameter one model publishes is not necessarily one another accepts — `cv.models()` says which — and
61
61
  a value a model does not publish is refused at submit rather than ignored.
62
62
 
63
63
  ## An API key
@@ -109,7 +109,7 @@ a presigned URL and `blob()` GETs the result the same way; the API moves keys, n
109
109
 
110
110
  ## Retries are yours to write
111
111
 
112
- **Outside `run()` and `waitFor()`, this client does not retry.** `run()` asks its submission
112
+ **Outside `run()` and `waitFor()`, this client does not retry.** `run()` sends its submission
113
113
  once more, under one idempotency key, when the first got no answer or a failure on the
114
114
  platform's side, and `waitFor()` reads on through the same until its timeout; neither can make a
115
115
  second job. Every other call is sent once. That is deliberate: only the caller knows where one
@@ -152,7 +152,7 @@ above tells the two apart.
152
152
 
153
153
  | Class | Meaning |
154
154
  |---|---|
155
- | `RateLimited` | 429 — one of five limits, and `.code` says which: `rate_limited` (the key's request rate), `too_many_uploads`, `too_many_keys`, `too_many_jobs` or `queue_full`. `.retryAfter` is seconds to wait: until the window rolls over for the two per-minute limits, until a new key can be made for the hourly one, or an interval to poll on for the other two. |
155
+ | `RateLimited` | 429 — one of five limits, and `.code` says which: `rate_limited` (the account's request rate), `too_many_uploads`, `too_many_keys`, `too_many_jobs` or `queue_full`. `.retryAfter` is seconds to wait: until the window rolls over for the two per-minute limits, until a new key can be made for the hourly one, or an interval to poll on for the other two. |
156
156
  | `JobFailed` | The job ran and did not succeed. `.message` says what you can do about it; `.errorCode` is the half to branch on; `.jobId` is the job. |
157
157
  | `InvalidSignature` | A webhook delivery did not verify. Do not act on its body. |
158
158
  | `CraterViewError` | Everything else, including 4xx and 5xx from the API, and storage refusing an upload or a download. |
@@ -187,8 +187,9 @@ result's file are getters onto `result.output` rather than separate fields — t
187
187
  those links once. A model with no file to hand back returns its answer in `result` and leaves
188
188
  every one of them null; the fields it answers with are its `result_schema` in `cv.models()`.
189
189
 
190
- `credits` is the only figure about cost the API states, and the price is fixed and published
191
- per model, so an invoice reconciles against `credits` alone. For how long a job took, subtract
190
+ `credits` is the only figure about cost the API states, and each model publishes its price in
191
+ `cv.models()` — `credits_per_image`, and `credit_steps` where a smaller image costs less — so
192
+ an invoice reconciles against `credits` alone. For how long a job took, subtract
192
193
  `createdAt` from `finishedAt` — that is the wait you had, queue included.
193
194
 
194
195
  **Uploads declare their size.** `upload()` and `run()` take the length from the `Blob`
@@ -197,8 +198,8 @@ accepts exactly that length and nothing else. You do not pass it — it is read
197
198
  in hand, which is what makes it impossible to get wrong.
198
199
 
199
200
  **Running out of credit does not stop you.** A job submitted against a balance of zero is
200
- accepted and run, and charged when it succeeds — it goes to the community queue, which runs
201
- on shared, free capacity and can wait longer at busy times. It comes back with `community`
201
+ accepted, and unless a subscription has work left this period it runs free on the community
202
+ queue, which runs on shared, free capacity and can wait longer at busy times. It comes back with `community`
202
203
  set, and `usage()` reports the same flag for your next job before you send it. There is no
203
204
  payment error to handle: paying — with credit, or with a subscription — moves work onto paid
204
205
  compute that scales with demand, rather than buying the right to submit.
@@ -327,7 +328,7 @@ to storage and have five minutes.
327
328
  ## From Claude Code
328
329
 
329
330
  This repository is also a Claude Code marketplace. Two plugins: `craterview` connects
330
- CraterView's hosted tools so the assistant enhances images in the conversation, and
331
+ CraterView.ai's hosted tools so the assistant enhances images in the conversation, and
331
332
  `craterview-api` teaches an agent to call the API from code with this client. See
332
333
  [`plugins/`](plugins/) for what each does and how it finds a key.
333
334
 
@@ -340,7 +341,7 @@ CraterView's hosted tools so the assistant enhances images in the conversation,
340
341
  ## Versioning
341
342
 
342
343
  Semver, from `0.1.0` on. A release that removes an exported member, renames a field on `Job`
343
- or `ModelInfo`, or changes the type of one, gets a minor bump while this is `0.x` and a major
344
+ or `ModelInfo`, or changes the type of one gets a minor bump while this is `0.x` and a major
344
345
  bump after `1.0.0`; anything purely additive gets a patch. Pin what you depend on.
345
346
 
346
347
  The API this wraps adds fields to its responses without warning, so treat an unfamiliar key in
package/index.ts CHANGED
@@ -18,7 +18,7 @@ import { imageSize } from "image-size";
18
18
  // Mirrored from package.json, which is the number a release bumps. It cannot be imported
19
19
  // from there — this ships as TypeScript, so the import would have to resolve in the
20
20
  // consumer's toolchain — so test/version.test.ts asserts the two agree.
21
- export const VERSION = "0.4.26";
21
+ export const VERSION = "0.4.29";
22
22
  const DEFAULT_BASE_URL = "https://api.craterview.ai";
23
23
  // The server rejects a longer wait outright, so asking for one costs a 422 rather than the
24
24
  // wait you asked for. `run()` clamps to this rather than letting that happen.
@@ -154,7 +154,8 @@ export class CraterViewError extends Error {
154
154
  * says which — `rate_limited`, `too_many_keys`, `too_many_uploads`, `too_many_jobs` or
155
155
  * `queue_full`, in this order:
156
156
  *
157
- * - the key's request rate, per minute;
157
+ * - the account's request rate, per minute — counted across all its keys, for submitting
158
+ * jobs, creating keys and gallery calls. No other call counts;
158
159
  * - the account's new keys, per hour — counted across all its keys, and only from
159
160
  * `createKey`;
160
161
  * - the account's upload URLs, per minute — counted across all its keys;
@@ -336,11 +337,19 @@ function timingSafeEqual(a: string, b: string): boolean {
336
337
  * with `JobData` — there is no wrapper class here because there is no behavior to add.
337
338
  */
338
339
  export interface ModelInfo {
339
- /** The public name, and what you pass as `model` when submitting. */
340
- name: string;
341
- version: string;
340
+ /** What you pass as `model` when submitting. Not for showing to a person; that is `title`. */
341
+ id: string;
342
+ /**
343
+ * Which revision of this model is in service. It changes when the same input gives a
344
+ * different result, while the id stays the same; record it to explain an old result later.
345
+ */
346
+ revision: string;
347
+ /** What to call this model when talking to a person. */
342
348
  title: string;
349
+ /** What this model is for and when to choose it over another; for a program or an agent. */
343
350
  description: string;
351
+ /** What this model does, in one short line for a person choosing it in an app. */
352
+ help: string;
344
353
  accepts: string[];
345
354
  /**
346
355
  * The media type a result usually comes back as, and advisory: a model that keeps the
@@ -365,6 +374,12 @@ export interface ModelInfo {
365
374
  * utility or fun experiment.
366
375
  */
367
376
  tenure?: "fixed" | "comet";
377
+ /**
378
+ * Whether this is a model for testing an integration rather than for use: free, and it
379
+ * returns a result of the right size without improving the picture. `echo` is one.
380
+ * CraterView's apps do not offer it; an app built on this catalog should leave it out too.
381
+ */
382
+ test_model?: boolean;
368
383
  max_input_bytes: number;
369
384
  /**
370
385
  * The ceilings **your key** is held to, not the model's widest. Community work is bounded
@@ -372,16 +387,16 @@ export interface ModelInfo {
372
387
  * before uploading rather than learning them from a rejected job.
373
388
  *
374
389
  * `max_frame_megapixels` bounds one frame you send, and applies to images too since a
375
- * still is one frame. `max_frames` bounds how many frames one job may carry. Zero means
376
- * that axis is unbounded for you.
390
+ * still is one frame. `max_frames` bounds how many frames one job may carry. Null means
391
+ * nothing bounds that axis for you; a limit is never 0.
377
392
  *
378
393
  * For a duration, divide: `max_frames / reference_fps` is the longest clip you may send, in
379
394
  * seconds. It is not published as its own field on purpose — one limit with two spellings
380
395
  * is one that can disagree with itself, and frames are what the platform counts, enforces
381
396
  * and bills on.
382
397
  */
383
- max_frame_megapixels: number;
384
- max_frames: number;
398
+ max_frame_megapixels: number | null;
399
+ max_frames: number | null;
385
400
  /**
386
401
  * The largest frame you may get **back**, in megapixels, and for a model that enlarges it
387
402
  * is the ceiling that decides whether a job runs: what returns is the frame you send
@@ -392,44 +407,53 @@ export interface ModelInfo {
392
407
  *
393
408
  * ```ts
394
409
  * const result = megapixels * scale ** 2;
395
- * if (model.max_output_megapixels && result > model.max_output_megapixels) {
410
+ * if (model.max_output_megapixels !== null && result > model.max_output_megapixels) {
396
411
  * // send a smaller region of the image, or ask for less enlargement
397
412
  * }
398
413
  * ```
399
414
  *
400
415
  * Sending a smaller region keeps every pixel of what it covers; asking for less
401
- * enlargement keeps the whole picture. Zero means this axis is unbounded for you.
416
+ * enlargement keeps the whole picture. Null means nothing bounds it for you.
402
417
  */
403
- max_output_megapixels: number;
418
+ max_output_megapixels: number | null;
404
419
  /**
405
- * The most of a picture this model works on in one job, in megapixels: the region you
420
+ * The most of a picture one job may work on for you, in megapixels: the region you
406
421
  * name with `roi`, or the whole image. Where it is set it binds at every enlargement, so
407
422
  * the largest frame you may send is the smaller of this and `max_output_megapixels`
408
423
  * divided by the square of the enlargement — and asking for less enlargement does not
409
- * help with it, where a smaller region does. Zero means this axis is unbounded for you.
424
+ * help with it, where a smaller region does. Null means nothing bounds it for you.
410
425
  */
411
- max_input_megapixels: number;
426
+ max_input_megapixels: number | null;
412
427
  /**
413
- * The most of a picture this model works on in one job with `denoise` sent as true, in
414
- * megapixels; `max_input_megapixels` applies otherwise. Null for a model that takes no
415
- * `denoise`. Zero means this axis is unbounded for you.
428
+ * The most of a picture one job may work on for you at each `binning` value, in megapixels,
429
+ * keyed by the value ("2x2", "3x3", ...); `max_input_megapixels` applies with binning off.
430
+ * Each is `max_output_megapixels` divided by the square of the largest scale that binning
431
+ * allows. Null for a model that takes no `binning`, or when nothing bounds it for you.
416
432
  */
417
- max_denoise_input_megapixels?: number | null;
433
+ max_input_megapixels_by_binning?: Record<string, number> | null;
418
434
  /**
419
- * The most of a picture this model works on in one job with `tiled` sent as true, in
420
- * megapixels: the region you name with `roi`, or the whole image, worked on at its own
421
- * resolution and returned at its own size. Without `tiled`, a larger picture is reduced
422
- * rather than refused: see `working_megapixels`. Null for a model that takes no `tiled`.
435
+ * The shortest side, in pixels, of the part of a picture this model works on: the region
436
+ * you name with `roi`, or the whole image. A job with a shorter one fails before the model
437
+ * runs, as `input_too_small`, and is not charged. The same for every key.
423
438
  */
424
- max_tiled_input_megapixels?: number | null;
439
+ min_input_side: number;
425
440
  /**
426
- * The most this model works on at a picture's own resolution, in megapixels: a larger
427
- * picture or region is reduced to about this area first and comes back at about this size,
428
- * unless `tiled` is sent as true. Null for a model that works at the size it is given.
441
+ * Whole credits: what one still image costs, or the most one costs where `credit_steps`
442
+ * charges less for a smaller one. Knowable before you send anything.
429
443
  */
430
- working_megapixels?: number | null;
431
- /** Whole credits. The price is flat and knowable before you send anything. */
432
444
  credits_per_image: number;
445
+ /**
446
+ * A lower price for a smaller still image, or null where every one costs
447
+ * `credits_per_image`. `by` is `megapixels` — the part of the image a job works on, the
448
+ * region you name with `roi` or the whole image, in millions of pixels, divided by 4 at
449
+ * `binning` 2x2, 9 at 3x3 and 16 at 4x4 — or `faces`, the faces the model redraws, counted
450
+ * when the job finishes. A still costs the first step its measure is at or under, and
451
+ * `credits_per_image` past the last.
452
+ */
453
+ credit_steps?: {
454
+ by: "megapixels" | "faces";
455
+ steps: { up_to: number; credits: number }[];
456
+ } | null;
433
457
  /** Whole credits. Null means this model takes stills only. */
434
458
  credits_per_video_second?: number | null;
435
459
  reference_fps: number;
@@ -439,6 +463,12 @@ export interface ModelInfo {
439
463
  * not order an object's keys, so a form drawn from the schema reads its order from here.
440
464
  */
441
465
  params_order: string[];
466
+ /**
467
+ * A short line for a person choosing each setting in a form, keyed by parameter name: every
468
+ * parameter in `params_schema` that one control can set. Show it under the control. For what
469
+ * a value may be and what may be sent together, read `params_schema`.
470
+ */
471
+ params_help: Record<string, string>;
442
472
  /**
443
473
  * JSON Schema for the fields a finished job of this model states under `result`, beside
444
474
  * any files it links. Empty for a model whose whole answer is its file.
@@ -466,7 +496,6 @@ export interface KeyInfo {
466
496
  created_at: string | null;
467
497
  /** When it stopped working, or null while it still does. */
468
498
  revoked_at?: string;
469
- rate_limit_per_minute: number;
470
499
  /**
471
500
  * Whether this is the key you are calling with. It cannot be revoked by id while it is;
472
501
  * `DELETE /v1/keys/current` ends it on request, unless it is the account's only way
@@ -482,7 +511,6 @@ export interface NewKey {
482
511
  key: string;
483
512
  prefix: string;
484
513
  name: string;
485
- rate_limit_per_minute: number;
486
514
  }
487
515
 
488
516
  /**
@@ -1023,7 +1051,9 @@ export class CraterView {
1023
1051
  * charged when it succeeds, so queued and running work is not in the figures yet, and a
1024
1052
  * failed job costs nothing.
1025
1053
  *
1026
- * `credits_remaining` is always a number, floored at zero. A balance of zero never stops a
1054
+ * The balance is `credits_remaining`; `credits_spent` is what the account has spent, all
1055
+ * time, and `credits_granted` what it has been given. `credits_remaining` is always a
1056
+ * number, floored at zero. A balance of zero never stops a
1027
1057
  * submission: `community` says whether your next job runs on the community queue, which is
1028
1058
  * free and can wait longer at busy times, or on paid compute. `jobs_in_flight` and
1029
1059
  * `max_jobs_in_flight` are the state of the queue and are what a 429 on submit is about.
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "craterview",
3
- "version": "0.4.26",
3
+ "version": "0.4.29",
4
4
  "dependencies": {
5
5
  "image-size": "^2.0.4"
6
6
  },
7
- "description": "TypeScript client for the CraterView image enhancement API",
7
+ "description": "TypeScript client for the CraterView.ai image enhancement API",
8
8
  "type": "module",
9
9
  "main": "index.ts",
10
10
  "types": "index.ts",
@@ -25,10 +25,10 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "devDependencies": {
28
- "@types/node": "^26.6.3",
28
+ "@types/node": "^26.6.6",
29
29
  "esbuild": "^0.28.2",
30
30
  "tsx": "^4.23.15",
31
31
  "typescript": "^7.0.2",
32
- "vitest": "^5.0.2"
32
+ "vitest": "^5.0.5"
33
33
  }
34
34
  }