@meterapp/car-image-sdk 1.3.0 → 1.5.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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @meterapp/car-image-sdk
2
2
 
3
- TypeScript SDK for the **Car Image API** by Meter — studio-quality, transparent-background renders of any vehicle in the open [`@meterapp/vehicle-db`](https://github.com/MeterApp/vehicle-db) catalog (1,599 makes, 44,254 models, model years 1990–2027).
3
+ TypeScript SDK for the **Car Image API** by Meter — studio-quality, transparent-background renders of any vehicle in the open [`@meterapp/vehicle-db`](https://github.com/MeterApp/vehicle-db) catalog (1,599 makes, 44,254 models, model years 1990–2027), in any paint color; plus stable vehicle ids, free VIN decoding and textured 3D models.
4
4
 
5
5
  - Zero runtime dependencies. Works in Node 20+, Vercel/Cloudflare edge runtimes, Deno, Bun and browsers.
6
6
  - Typed parameters and responses for every endpoint.
7
7
  - `CarImageError` carries `status`, `title`, `detail`, `requestId` and the raw RFC 9457 problem.
8
8
  - Automatic retries with full jitter on `429`/`503` (honoring `Retry-After`); never retries `402`.
9
- - Signed-URL creation carries an `Idempotency-Key`, so a retried `POST` replays instead of billing twice.
9
+ - Signed-URL and 3D-model creation carry an `Idempotency-Key`, so a retried `POST` replays instead of billing twice.
10
10
  - Shared MCP tool definitions (`@meterapp/car-image-sdk/mcp`) used by both the stdio and the remote MCP servers.
11
11
 
12
12
  ```bash
@@ -15,7 +15,7 @@ npm install @meterapp/car-image-sdk
15
15
 
16
16
  ## Pricing in one line
17
17
 
18
- Every delivered image costs **1 credit**, cold or CDN hit. **$1 = 1,000 credits**, every account starts with **100 free credits**, there is no subscription. Signed delivery URLs cost 1 credit when created; loading them is free.
18
+ Every delivered image costs **1 credit**, cold or CDN hit. **$1 = 1,000 credits**, every account starts with **100 free credits**, there is no subscription. Signed delivery URLs cost 1 credit when created; loading them is free. A 3D model costs **1,000 credits** ($1.00) when requested; VIN decoding, catalog lookups, polling and downloads are free.
19
19
 
20
20
  Get a key at <https://carimage.dev/dashboard?ref=npm-sdk> or run `npx @meterapp/car-image login`.
21
21
 
@@ -32,7 +32,7 @@ const image = await client.getImage({
32
32
  model: "911",
33
33
  year: 2024,
34
34
  view: "front-3-4", // front | front-3-4 | side | side-right | rear | rear-3-4
35
- color: "red", // 15 presets: white black gray silver blue red green brown beige tan orange yellow gold burgundy purple
35
+ color: "red", // 15 presets (white black gray silver blue red green brown beige tan orange yellow gold burgundy purple) or any hex: "#1a2b3c"
36
36
  size: "medium", // thumb=256 small=512 medium=768 large=1024 — or width/height, 1–1024 px each
37
37
  format: "png", // png | webp | jpg | auto (auto negotiates WebP or PNG from the Accept header)
38
38
  });
@@ -43,9 +43,71 @@ console.log(image.source, image.creditsCharged, image.creditsRemaining, image.re
43
43
 
44
44
  `apiKey` and `baseUrl` fall back to `CAR_IMAGE_API_KEY` / `CAR_IMAGE_API_URL` when running on a server.
45
45
 
46
+ ## Any paint color
47
+
48
+ `color` is one of the fifteen presets or any hex paint — `#1a2b3c`, `1a2b3c`, `#abc` or `abc`. Every image call takes it (`getImage`, `getImageUrl`, `createImageUrls`, `feedback`) and so does `create3dModel`. The SDK spells a hex bare in query strings (`color=1a2b3c`) and sends it as written in JSON bodies; responses echo it as `#1a2b3c`. A hex that matches a preset's own swatch is that preset. The `Color` type keeps the preset names in autocomplete while accepting any string; `isColor(value)` tells the two apart from anything else, and `COLORS` is still the list of presets.
49
+
50
+ ```ts
51
+ await client.getImage({ make: "Porsche", model: "911", year: 2024, color: "#1a2b3c" });
52
+ ```
53
+
54
+ ## Stable vehicle ids
55
+
56
+ Every catalog vehicle (one make, model and year) has a stable id such as `veh_395yw8tn73ff8`. It never changes across catalog releases, so it is what to store instead of three strings. Every catalog response carries them (`searchVehicles` hits get `id` for the year the query named and `ids` for every year; `vehicles({ year, makeId })` models get `id`; `resolve()` params get `vehicle_id`; `decodeVin()` names one), and every image or 3D call accepts `{ vehicle }` in place of `{ make, model, year }`:
57
+
58
+ ```ts
59
+ const { data } = await client.searchVehicles("2024 porsche 911");
60
+ const id = data.results[0].id; // "veh_395yw8tn73ff8"
61
+ await client.getImage({ vehicle: id, view: "side" }); // same image as make/model/year
62
+ await client.vehicle(id); // { make, model, year, years, image_path, images } — public, no key
63
+ ```
64
+
65
+ `ImageParams` is `{ make, model, year } | { vehicle }` plus the options, so TypeScript refuses both at once; at runtime whichever you pass is sent and the server answers `400` to both. Responses echo the id as `vehicle.vehicle_id` (null when the catalog cannot name one).
66
+
67
+ ## VIN decoding
68
+
69
+ `decodeVin(vin, { year? })` decodes a full 17-character VIN, or a partial one of at least 5 characters with `*` for unknown positions, with NHTSA vPIC data — free, key required. `data.vehicle` is the catalog vehicle it maps to, with the id that renders it, or null when the catalog does not carry the car.
70
+
71
+ ```ts
72
+ const { data } = await client.decodeVin("1HGCM82633A004352");
73
+ data.year, data.make, data.model, data.trim; // 2003, "Honda", "Accord", "EX-V6"
74
+ data.engine, data.transmission, data.plant; // typed sub-objects; unknown values are null
75
+ data.attributes; // every decoded vPIC variable, verbatim
76
+ if (data.vehicle) await client.getImage({ vehicle: data.vehicle.id });
77
+ ```
78
+
79
+ `valid`, `errors` and `suggested_vin` carry the decoder's diagnostics (a bad check digit, a model-year mismatch with the `year` hint). A VIN NHTSA has no record of is a `404`; when neither decoder answers it is a `502`.
80
+
81
+ ## 3D models
82
+
83
+ `create3dModel` builds a textured 3D model — GLB, USDZ, FBX and a PNG thumbnail — of any catalog vehicle in any paint color, from the same renders the images use. It costs 1,000 credits at creation whether the model is cached, in progress or new (a failed model is refunded); polling and downloads are free. A model takes one to five minutes.
84
+
85
+ ```ts
86
+ const { data: model, billing } = await client.create3dModel(
87
+ { make: "Toyota", model: "Camry", year: 2025 }, // or { vehicle: "veh_…" }
88
+ { color: "#1a2b3c", webhookUrl: "https://example.com/hooks/3d", webhookSecret: process.env.HOOK_SECRET }
89
+ );
90
+ model.status; // "queued" | "processing" | "ready" (cached) | "failed"
91
+
92
+ const ready = await client.wait3dModel(model.id, { // polls GET /api/v1/3d/{id}
93
+ intervalMs: 5_000, // default 5 s
94
+ timeoutMs: 600_000, // default 10 min; throws a CarImageError with status 408
95
+ onProgress: (state) => console.log(state.status, state.progress, state.stage, state.estimated_seconds_remaining),
96
+ });
97
+ if (ready.status === "ready") {
98
+ const glb = await client.download3dModel(ready.id, "glb"); // { bytes, contentType } — also "usdz" | "fbx" | "thumbnail"
99
+ await writeFile("camry.glb", glb.bytes);
100
+ }
101
+
102
+ await client.get3dModel(model.id); // one poll
103
+ await client.list3dModels({ limit: 20 }); // your recent requests + pricing.credits_per_model
104
+ ```
105
+
106
+ `ready.files.<kind>.url` are API URLs that answer `302` to a signed download valid for an hour; `download3dModel` follows that redirect itself and never sends the API key to the storage host (`fetch` only attaches it to the client's own origin). With `webhookUrl` (https, public host) the server POSTs `{ event: "3d_model.ready" | "3d_model.failed", data, sent_at }` once the model settles, signed as `X-CarImage-Signature: sha256=<hex HMAC of the raw body>` when `webhookSecret` is set. `create3dModel` sends an `Idempotency-Key` like `createImageUrls` (pass `idempotencyKey` to make retries across processes safe). A `503` means 3D models are switched off or at the day's capacity; nothing is charged and `retryAfterSeconds` says when to try again.
107
+
46
108
  ## Sizing, backgrounds and trimming
47
109
 
48
- Six views, fifteen colors, PNG/WebP/JPG up to 1024 px — delivered at whatever box your layout needs.
110
+ Six views, any paint color, PNG/WebP/JPG up to 1024 px — delivered at whatever box your layout needs.
49
111
 
50
112
  ```ts
51
113
  await client.getImage({
@@ -61,6 +123,7 @@ await client.getImage({
61
123
 
62
124
  | Parameter | Values | What it does |
63
125
  | --- | --- | --- |
126
+ | `color` | one of `COLORS` · hex as `#rrggbb`, `rrggbb`, `#rgb` or `rgb` | The paint. Defaults to `silver`; echoed back as the preset name or `#rrggbb`. |
64
127
  | `size` | `thumb` 256 · `small` 512 · `medium` 768 · `large` 1024 | Square presets; shorthand for `width`. |
65
128
  | `width`, `height` | 1–1024 px each | One dimension keeps the aspect ratio. Both together give exactly `width`×`height` (see `fit`). Nothing is upscaled past 1024. |
66
129
  | `fit` | `contain` (default) · `cover` · `inside` | Only matters with both dimensions. `contain` returns exactly the box with the whole car and transparent (or `background`) padding; `cover` fills the box and centre-crops; `inside` keeps the car within the box and may return a smaller image — the old behaviour. |
@@ -69,7 +132,7 @@ await client.getImage({
69
132
  | `padding` | 0–50 | Margin around a trimmed car, as a percentage of its longer side. Only with `trim`. |
70
133
  | `format` | `png` (default) · `webp` · `jpg` · `auto` | `auto` picks WebP for clients whose `Accept` header lists it and PNG otherwise (`Vary: Accept`); a signed URL created with `auto` negotiates on every load. |
71
134
 
72
- The same parameters apply to `getImageUrl` and to every entry of `createImageUrls`; the returned `vehicle` echoes `fit`, `background`, `trim` and `padding` back. `assertImageParams` (and therefore every call) rejects a `padding` without `trim` and dimensions outside 1–1024 before anything is sent.
135
+ The same parameters apply to `getImageUrl` and to every entry of `createImageUrls`; the returned `vehicle` echoes `vehicle_id`, `color`, `fit`, `background`, `trim` and `padding` back. `assertImageParams` (and therefore every call) rejects a `padding` without `trim`, dimensions outside 1–1024, a color that is neither a preset nor a hex, and a request that names neither a vehicle id nor all of make, model and year, before anything is sent.
73
136
 
74
137
  ## Next.js route handler
75
138
 
@@ -122,13 +185,17 @@ await client.createImageUrls(images, { ttlSeconds: 604_800, idempotencyKey: `ord
122
185
 
123
186
  ```ts
124
187
  await client.getImageUrl(params); // 1 credit, returns a signed URL instead of bytes
125
- await client.options(); // views, colors, sizes, fits, backgrounds, trim limits, formats, pricing, catalog coverage
126
- await client.resolve("red 2024 porsche 911 side view"); // free text -> { make, model, year, view, color } + candidates + confidence high|medium|low
188
+ await client.options(); // views, colors (and how hex paint is spelled), sizes, fits, backgrounds, trim limits, formats, pricing, catalog coverage, vehicle ids
189
+ await client.resolve("red 2024 porsche 911 side view"); // free text -> { vehicle_id, make, model, year, view, color } + candidates + confidence high|medium|low
127
190
  await client.vehicles(); // years
128
191
  await client.vehicles({ year: 2024 }); // makes for a year
129
- await client.vehicles({ year: 2024, makeId: 7 }); // models for a year + make
130
- await client.searchVehicles("porshe 911", { limit: 5 }); // fuzzy catalog search
131
- await client.feedback({ requestId, verdict: "good" }); // or { make, model, year, rating: 4, reason }
192
+ await client.vehicles({ year: 2024, makeId: 7 }); // models for a year + make, each with its vehicle id
193
+ await client.vehicles({ make: "porsche", model: "911" }); // a model's years, each with its vehicle id
194
+ await client.vehicle("veh_395yw8tn73ff8"); // one vehicle by stable id (public)
195
+ await client.searchVehicles("porshe 911", { limit: 5 }); // fuzzy catalog search; model hits carry ids
196
+ await client.decodeVin("1HGCM82633A004352"); // free VIN decode + the catalog vehicle it maps to
197
+ await client.create3dModel({ vehicle: id }, { color }); // 1,000 credits; then wait3dModel, get3dModel, list3dModels, download3dModel
198
+ await client.feedback({ requestId, verdict: "good" }); // or { make, model, year, rating: 4, reason } or { vehicle, verdict }
132
199
  await client.listRequests({ sort: "top" }); // vehicle and feature requests; see "Requests & feedback"
133
200
  await client.account(); // credits, auto-reload, usage_30d, key scopes
134
201
  await client.createCheckout(5000); // hosted Stripe Checkout URL (scope billing:write)
@@ -173,12 +240,12 @@ try {
173
240
  await client.getImage({ make: "Porsche", model: "911", year: 2024 });
174
241
  } catch (error) {
175
242
  if (error instanceof CarImageError) {
176
- error.status; // 400 | 401 | 402 | 404 | 409 | 422 | 429 | 502 | 0 (network)
243
+ error.status; // 400 | 401 | 402 | 404 | 409 | 422 | 429 | 502 | 503 | 408 (wait3dModel timeout) | 0 (network)
177
244
  error.title; // "Insufficient credits"
178
245
  error.detail; // "This request needs 1 credit; balance is 0"
179
246
  error.requestId; // "0d1e…" — quote this when contacting support
180
247
  error.problem; // the raw application/problem+json body (402 includes balance, required_credits)
181
- error.retryAfterSeconds; // set on 429, and on a 409 Idempotency-Key conflict
248
+ error.retryAfterSeconds; // set on 429, on a 409 (Idempotency-Key conflict, or a 3D file requested before the model is ready) and on a 503
182
249
  if (error.isInsufficientCredits) {
183
250
  // Send a human to https://carimage.dev/dashboard?ref=npm-sdk#billing — never buy credits automatically.
184
251
  }
@@ -199,13 +266,13 @@ import {
199
266
  } from "@meterapp/car-image-sdk/mcp";
200
267
  ```
201
268
 
202
- Tools come in two sets. `MCP_TOOLSETS.core` (`DEFAULT_MCP_TOOLSET`, `"core"`) is the eight an agent needs to find, render and embed a car: `get_car_image`, `create_car_image_urls`, `search_vehicles`, `resolve_vehicle`, `list_image_options`, `get_account`, `rate_image`, `describe_api`. `MCP_TOOLSETS.all` adds the request board and the two "about you" tools: `list_requests`, `request_vehicle`, `request_feature`, `get_request`, `upvote_request`, `comment_on_request`, `share_building`, `share_referral`. The hosted server exposes `core` at `https://carimage.dev/api/mcp` and `all` at `https://carimage.dev/api/mcp?toolset=all`; the stdio server takes `car-image mcp --toolset all`. `mcpInstructions(toolset)` returns the matching server instructions (`MCP_INSTRUCTIONS` is the `all` text) and `isMcpToolset(value)` validates a name from a URL or a flag.
269
+ Tools come in two sets. `MCP_TOOLSETS.core` (`DEFAULT_MCP_TOOLSET`, `"core"`) is the eleven an agent needs to find, render, embed and model a car: `get_car_image`, `create_car_image_urls`, `search_vehicles`, `resolve_vehicle`, `decode_vin`, `list_image_options`, `get_account`, `rate_image`, `create_3d_model`, `get_3d_model`, `describe_api`. `MCP_TOOLSETS.all` adds the request board and the two "about you" tools: `list_requests`, `request_vehicle`, `request_feature`, `get_request`, `upvote_request`, `comment_on_request`, `share_building`, `share_referral`. The hosted server exposes `core` at `https://carimage.dev/api/mcp` and `all` at `https://carimage.dev/api/mcp?toolset=all`; the stdio server takes `car-image mcp --toolset all`. `mcpInstructions(toolset)` returns the matching server instructions (`MCP_INSTRUCTIONS` is the `all` text) and `isMcpToolset(value)` validates a name from a URL or a flag.
203
270
 
204
- `get_car_image` and each `create_car_image_urls` entry take `fit`, `background`, `trim` and `padding` alongside the vehicle, view, color, size and format; `create_car_image_urls` also honors `renew` and `renew_days`. Every tool but `describe_api`, whose result is the reference prose itself, advertises an `outputSchema` and returns `structuredContent` that matches it, so a host can hand the model typed data instead of re-parsing the text block. The schemas allow unknown fields: the payloads mirror the REST API and gain fields as it grows.
271
+ `get_car_image`, each `create_car_image_urls` entry, `rate_image` and `create_3d_model` name the vehicle by make, model and year or by `vehicle` (a stable id) and take `color` as a preset or a hex; the image tools also take `fit`, `background`, `trim` and `padding` alongside view, size and format (`auto` included; `inlineImageFormat` is what both servers deliver an inline image as for it: png), and `create_car_image_urls` honors `renew` and `renew_days` and takes `idempotency_key` (`IDEMPOTENCY_KEY_PATTERN`), which the stdio server forwards as the `Idempotency-Key` header. `create_3d_model` costs 1,000 credits, and its description and the server instructions tell the agent to confirm with the human first. Every tool but `describe_api`, whose result is the reference prose itself, advertises an `outputSchema` and returns `structuredContent` that matches it, so a host can hand the model typed data instead of re-parsing the text block. The schemas allow unknown fields: the payloads mirror the REST API and gain fields as it grows.
205
272
 
206
273
  ## Types
207
274
 
208
- `View`, `Color`, `Size`, `Format`, `RequestFormat`, `Fit`, `ImageParams`, `ImageResult`, `CreateImageUrlsOptions`, `CreateImageUrlsResponse`, `OptionsResponse`, `FitOption`, `ResolveResponse`, `ResolveConfidence` (`"high" | "medium" | "low"`), `VehicleSearchResponse`, `AccountResponse`, `RequestRecord`, `RequestComment`, `CreateRequestInput`, `ReferralSource`, … plus the constant arrays `VIEWS`, `COLORS`, `SIZES`, `FORMATS`, `REQUEST_FORMATS` (the formats plus `auto`), `FITS`, `REQUEST_STATUSES`, `REFERRAL_SOURCES` and the limits `MAX_DIMENSION` (1024) and `MAX_PADDING_PERCENT` (50).
275
+ `View`, `Color` (a `PresetColor` or any hex string), `Size`, `Format`, `RequestFormat`, `Fit`, `VehicleId`, `VehicleSelector`, `ImageParams`, `ImageOptions`, `ImageResult`, `CreateImageUrlsOptions`, `CreateImageUrlsResponse`, `OptionsResponse`, `FitOption`, `ResolveResponse`, `ResolveConfidence` (`"high" | "medium" | "low"`), `VehicleSearchResponse`, `VehicleResponse`, `VinResult`, `VinResponse`, `Model3d`, `Model3dFileKind`, `Model3dStatus`, `CreateModel3dOptions`, `CreateModel3dResponse`, `Model3dListResponse`, `Model3dFileResult`, `WaitModel3dOptions`, `AccountResponse`, `RequestRecord`, `RequestComment`, `CreateRequestInput`, `ReferralSource`, … plus the constant arrays `VIEWS`, `COLORS` (the presets), `SIZES`, `FORMATS`, `REQUEST_FORMATS` (the formats plus `auto`), `FITS`, `MODEL_3D_FILE_KINDS`, `REQUEST_STATUSES`, `REFERRAL_SOURCES`, the patterns `HEX_COLOR_PATTERN` and `VEHICLE_ID_PATTERN`, the helpers `isColor`, `isPresetColor`, `isHexColor`, `colorParam` and `vehicleRequestBody`, and the limits `MAX_DIMENSION` (1024), `MAX_PADDING_PERCENT` (50) and `CREDITS_PER_3D_MODEL` (1000).
209
276
 
210
277
  ## License
211
278
 
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AccountResponse, type BuildingResponse, type CheckoutResponse, type CreateImageUrlsOptions, type CreateImageUrlsResponse, type CreateRequestInput, type CreateRequestResponse, type DeviceCodeResponse, type DeviceTokenResult, type FeedbackInput, type FeedbackResponse, type FetchLike, type HealthResponse, type ImageParams, type ImageResult, type ImageUrlResponse, type ListRequestsParams, type OpenApiDocument, type OptionsResponse, type PortalResponse, type ReferralResponse, type ReferralSource, type RequestCommentResponse, type RequestCommentsResponse, type RequestDetailResponse, type RequestListResponse, type RequestResponse, type ResolveResponse, type TelemetryEvent, type VehicleMakesResponse, type VehicleModelsResponse, type VehicleSearchResponse, type VehicleYearsResponse } from "./types.js";
1
+ import { type AccountResponse, type BuildingResponse, type CheckoutResponse, type CreateImageUrlsOptions, type CreateImageUrlsResponse, type CreateModel3dOptions, type CreateModel3dResponse, type CreateRequestInput, type CreateRequestResponse, type DecodeVinOptions, type DeviceCodeResponse, type DeviceTokenResult, type FeedbackInput, type FeedbackResponse, type FetchLike, type HealthResponse, type ImageParams, type ImageResult, type ImageUrlResponse, type ListRequestsParams, type Model3d, type Model3dFileKind, type Model3dFileResult, type Model3dListResponse, type Model3dResponse, type OpenApiDocument, type OptionsResponse, type PortalResponse, type ReferralResponse, type ReferralSource, type RequestCommentResponse, type RequestCommentsResponse, type RequestDetailResponse, type RequestListResponse, type RequestResponse, type ResolveResponse, type TelemetryEvent, type VehicleMakeModelsResponse, type VehicleMakesResponse, type VehicleModelLookupResponse, type VehicleModelsResponse, type VehicleResponse, type VehicleSearchResponse, type VehicleSelector, type VehicleYearsResponse, type VinResponse, type WaitModel3dOptions } from "./types.js";
2
2
  export interface CarImageClientOptions {
3
3
  /** `cimg_…` key. Falls back to `process.env.CAR_IMAGE_API_KEY` when running on a server. */
4
4
  apiKey?: string;
@@ -62,7 +62,11 @@ export declare class CarImageClient {
62
62
  options(options?: RequestOptions): Promise<OptionsResponse>;
63
63
  /** POST /api/v1/images/resolve — free; turns "red 2024 porsche 911 side view" into parameters. */
64
64
  resolve(query: string, options?: RequestOptions): Promise<ResolveResponse>;
65
- /** GET /api/v1/vehicles — public. No filter: years; `year`: makes; `year`+`makeId`: models. */
65
+ /**
66
+ * GET /api/v1/vehicles — public. No filter: years; `year`: makes; `year`+`makeId`:
67
+ * models (each with its vehicle id); `make` slug: that make's models; `make`+`model`
68
+ * slugs: the model's years, each with its vehicle id.
69
+ */
66
70
  vehicles(filter?: Record<string, never>, options?: RequestOptions): Promise<VehicleYearsResponse>;
67
71
  vehicles(filter: {
68
72
  year: number;
@@ -71,12 +75,59 @@ export declare class CarImageClient {
71
75
  year: number;
72
76
  makeId: number;
73
77
  }, options?: RequestOptions): Promise<VehicleModelsResponse>;
78
+ vehicles(filter: {
79
+ make: string;
80
+ year?: number;
81
+ }, options?: RequestOptions): Promise<VehicleMakeModelsResponse>;
82
+ vehicles(filter: {
83
+ make: string;
84
+ model: string;
85
+ year?: number;
86
+ }, options?: RequestOptions): Promise<VehicleModelLookupResponse>;
87
+ /** GET /api/v1/vehicles/{id} — public; the vehicle a stable id names, its years and the image URLs that render it. */
88
+ vehicle(id: string, options?: RequestOptions): Promise<VehicleResponse>;
89
+ /**
90
+ * GET /api/v1/vin/{vin} — free; decodes a full 17-character VIN, or a partial
91
+ * one of at least 5 characters with `*` for unknown positions, and names the
92
+ * catalog vehicle it maps to (`data.vehicle`, with the id that renders it).
93
+ */
94
+ decodeVin(vin: string, options?: DecodeVinOptions & RequestOptions): Promise<VinResponse>;
95
+ /**
96
+ * POST /api/v1/3d — a textured 3D model (GLB, USDZ, FBX and a thumbnail) of one
97
+ * vehicle in one paint color. Costs `credits_per_model` credits (1,000 at
98
+ * launch) at creation, whether the model is cached, in progress or new; a
99
+ * failed model is refunded, and polling and downloads are free. Resolves at
100
+ * once with `status` queued or processing (202), or ready when it was cached
101
+ * (200); `wait3dModel` polls until it settles. Every call carries an
102
+ * `Idempotency-Key`, like `createImageUrls`.
103
+ */
104
+ create3dModel(vehicle: VehicleSelector, options?: CreateModel3dOptions & RequestOptions): Promise<CreateModel3dResponse>;
105
+ /** GET /api/v1/3d/{id} — free; status, progress and, once ready, the files of one 3D request. */
106
+ get3dModel(id: string, options?: RequestOptions): Promise<Model3dResponse>;
107
+ /** GET /api/v1/3d — free; the caller's recent 3D requests, newest first (`limit` 1–50, default 20). */
108
+ list3dModels(options?: {
109
+ limit?: number;
110
+ } & RequestOptions): Promise<Model3dListResponse>;
111
+ /**
112
+ * GET /api/v1/3d/{id}/files/{kind} — free; the bytes of one file of a ready
113
+ * model. The route answers 302 to a short-lived signed URL on the storage
114
+ * host, which is followed here by hand so the API key never travels there:
115
+ * `fetch` attaches the key only to the client's own origin.
116
+ */
117
+ download3dModel(id: string, kind: Model3dFileKind, options?: RequestOptions): Promise<Model3dFileResult>;
118
+ /**
119
+ * Polls GET /api/v1/3d/{id} until the model is `ready` or `failed` and
120
+ * returns it either way (check `status`; a failed model carries `error` and
121
+ * was refunded). Throws a `CarImageError` with status 408 when `timeoutMs`
122
+ * (default 10 minutes) passes first; `intervalMs` defaults to 5 seconds.
123
+ */
124
+ wait3dModel(id: string, options?: WaitModel3dOptions & RequestOptions): Promise<Model3d>;
74
125
  /** GET /api/v1/vehicles?q= — public fuzzy search across makes and models. */
75
126
  searchVehicles(query: string, options?: {
76
127
  limit?: number;
77
128
  year?: number;
78
129
  } & RequestOptions): Promise<VehicleSearchResponse>;
79
- /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle. */
130
+ /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle (make, model and year, or a vehicle id). */
80
131
  feedback(input: FeedbackInput, options?: RequestOptions): Promise<FeedbackResponse>;
81
132
  /** GET /api/v1/requests — public board of vehicle and feature requests; `viewer.voted` is filled in when a key is sent. */
82
133
  listRequests(params?: ListRequestsParams, options?: RequestOptions): Promise<RequestListResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CarImageError } from "./errors.js";
2
- import { imageRequestBody, imageSearchParams, normalizeYear } from "./params.js";
2
+ import { imageRequestBody, imageSearchParams, isColor, normalizeYear, vehicleRequestBody } from "./params.js";
3
3
  import { RETRYABLE_STATUSES, backoffDelayMs, parseRetryAfter } from "./retry.js";
4
- import { DEFAULT_BASE_URL, REFERRAL_SOURCES, } from "./types.js";
4
+ import { COLORS, DEFAULT_BASE_URL, MODEL_3D_FILE_KINDS, REFERRAL_SOURCES, } from "./types.js";
5
5
  import { SDK_VERSION } from "./version.js";
6
6
  function readEnv(name) {
7
7
  const proc = globalThis.process;
@@ -144,7 +144,12 @@ export class CarImageClient {
144
144
  method: "POST",
145
145
  body: JSON.stringify(body),
146
146
  });
147
- return this.json(response);
147
+ const result = await this.json(response);
148
+ // A replay carries `Idempotent-Replayed: true`; surface it so a caller
149
+ // (a batch script, the stdio MCP server) can tell a retry from a mint.
150
+ if (response.headers.get("idempotent-replayed") === "true")
151
+ result.idempotent_replayed = true;
152
+ return result;
148
153
  }
149
154
  /** GET /api/v1/images/options — public; views, colors, sizes, formats, pricing, catalog. */
150
155
  async options(options = {}) {
@@ -168,10 +173,169 @@ export class CarImageClient {
168
173
  search.set("year", String(normalizeYear(filter.year)));
169
174
  if (filter.makeId !== undefined)
170
175
  search.set("makeId", String(filter.makeId));
176
+ if (filter.make !== undefined)
177
+ search.set("make", filter.make.trim());
178
+ if (filter.model !== undefined)
179
+ search.set("model", filter.model.trim());
171
180
  const query = search.size ? `?${search}` : "";
172
181
  const response = await this.send(`/api/v1/vehicles${query}`, { ...options, auth: "optional" });
173
182
  return this.json(response);
174
183
  }
184
+ /** GET /api/v1/vehicles/{id} — public; the vehicle a stable id names, its years and the image URLs that render it. */
185
+ async vehicle(id, options = {}) {
186
+ if (typeof id !== "string" || !id.trim())
187
+ throw new TypeError("vehicle id is required");
188
+ const response = await this.send(`/api/v1/vehicles/${encodeURIComponent(id.trim())}`, { ...options, auth: "optional" });
189
+ return this.json(response);
190
+ }
191
+ // ------------------------------------------------------------------ VIN
192
+ /**
193
+ * GET /api/v1/vin/{vin} — free; decodes a full 17-character VIN, or a partial
194
+ * one of at least 5 characters with `*` for unknown positions, and names the
195
+ * catalog vehicle it maps to (`data.vehicle`, with the id that renders it).
196
+ */
197
+ async decodeVin(vin, options = {}) {
198
+ if (typeof vin !== "string" || !vin.trim())
199
+ throw new TypeError("vin is required");
200
+ const { year, ...request } = options;
201
+ const search = new URLSearchParams();
202
+ if (year !== undefined)
203
+ search.set("year", String(normalizeYear(year)));
204
+ const query = search.size ? `?${search}` : "";
205
+ const response = await this.send(`/api/v1/vin/${encodeURIComponent(vin.trim())}${query}`, request);
206
+ return this.json(response);
207
+ }
208
+ // ------------------------------------------------------------ 3D models
209
+ /**
210
+ * POST /api/v1/3d — a textured 3D model (GLB, USDZ, FBX and a thumbnail) of one
211
+ * vehicle in one paint color. Costs `credits_per_model` credits (1,000 at
212
+ * launch) at creation, whether the model is cached, in progress or new; a
213
+ * failed model is refunded, and polling and downloads are free. Resolves at
214
+ * once with `status` queued or processing (202), or ready when it was cached
215
+ * (200); `wait3dModel` polls until it settles. Every call carries an
216
+ * `Idempotency-Key`, like `createImageUrls`.
217
+ */
218
+ async create3dModel(vehicle, options = {}) {
219
+ const { color, webhookUrl, webhookSecret, idempotencyKey, ...request } = options;
220
+ const body = vehicleRequestBody(vehicle);
221
+ if (color !== undefined) {
222
+ if (!isColor(color))
223
+ throw new TypeError(`Invalid color "${String(color)}": expected one of ${COLORS.join(", ")}, or a hex paint such as #1a2b3c`);
224
+ body.color = String(color).trim();
225
+ }
226
+ if (webhookUrl !== undefined)
227
+ body.webhook_url = webhookUrl;
228
+ if (webhookSecret !== undefined)
229
+ body.webhook_secret = webhookSecret;
230
+ const key = idempotencyKey ?? generateIdempotencyKey();
231
+ const headers = { ...request.headers };
232
+ if (key)
233
+ headers["Idempotency-Key"] = key;
234
+ const response = await this.send("/api/v1/3d", {
235
+ ...request,
236
+ headers,
237
+ method: "POST",
238
+ body: JSON.stringify(body),
239
+ });
240
+ return this.json(response);
241
+ }
242
+ /** GET /api/v1/3d/{id} — free; status, progress and, once ready, the files of one 3D request. */
243
+ async get3dModel(id, options = {}) {
244
+ const response = await this.send(`/api/v1/3d/${requestPath(id)}`, options);
245
+ return this.json(response);
246
+ }
247
+ /** GET /api/v1/3d — free; the caller's recent 3D requests, newest first (`limit` 1–50, default 20). */
248
+ async list3dModels(options = {}) {
249
+ const { limit, ...request } = options;
250
+ const search = new URLSearchParams();
251
+ if (limit !== undefined)
252
+ search.set("limit", String(limit));
253
+ const query = search.size ? `?${search}` : "";
254
+ const response = await this.send(`/api/v1/3d${query}`, request);
255
+ return this.json(response);
256
+ }
257
+ /**
258
+ * GET /api/v1/3d/{id}/files/{kind} — free; the bytes of one file of a ready
259
+ * model. The route answers 302 to a short-lived signed URL on the storage
260
+ * host, which is followed here by hand so the API key never travels there:
261
+ * `fetch` attaches the key only to the client's own origin.
262
+ */
263
+ async download3dModel(id, kind, options = {}) {
264
+ if (!MODEL_3D_FILE_KINDS.includes(kind)) {
265
+ throw new RangeError(`kind must be one of: ${MODEL_3D_FILE_KINDS.join(", ")}`);
266
+ }
267
+ const redirect = await this.rawSend(`/api/v1/3d/${requestPath(id)}/files/${kind}`, { ...options, accept: "*/*", redirect: "manual" });
268
+ const requestId = redirect.headers.get("x-request-id");
269
+ const location = redirect.headers.get("location");
270
+ if (redirect.status >= 300 && redirect.status < 400 && location) {
271
+ try {
272
+ await redirect.body?.cancel();
273
+ }
274
+ catch {
275
+ // Nothing to release.
276
+ }
277
+ const target = new URL(location, this.baseUrl).toString();
278
+ const file = await this.fetch(target, { headers: { Accept: "*/*" }, signal: this.signalFor(options) });
279
+ if (!file.ok) {
280
+ throw new CarImageError({
281
+ status: file.status,
282
+ title: "3D file download failed",
283
+ detail: `The storage host answered HTTP ${file.status} for the ${kind} file. Ask for the model again to get a fresh link.`,
284
+ requestId,
285
+ });
286
+ }
287
+ return {
288
+ kind,
289
+ bytes: new Uint8Array(await file.arrayBuffer()),
290
+ contentType: file.headers.get("content-type") ?? redirect.headers.get("x-file-content-type") ?? "application/octet-stream",
291
+ requestId,
292
+ };
293
+ }
294
+ if (redirect.ok) {
295
+ return {
296
+ kind,
297
+ bytes: new Uint8Array(await redirect.arrayBuffer()),
298
+ contentType: redirect.headers.get("content-type") ?? "application/octet-stream",
299
+ requestId,
300
+ };
301
+ }
302
+ if (redirect.status === 0 || redirect.type === "opaqueredirect") {
303
+ throw new CarImageError({
304
+ status: 0,
305
+ title: "Redirect hidden by the runtime",
306
+ detail: `This runtime does not expose redirects; fetch model.files.${kind}.url yourself and follow it without the API key.`,
307
+ requestId,
308
+ });
309
+ }
310
+ throw await CarImageError.fromResponse(redirect);
311
+ }
312
+ /**
313
+ * Polls GET /api/v1/3d/{id} until the model is `ready` or `failed` and
314
+ * returns it either way (check `status`; a failed model carries `error` and
315
+ * was refunded). Throws a `CarImageError` with status 408 when `timeoutMs`
316
+ * (default 10 minutes) passes first; `intervalMs` defaults to 5 seconds.
317
+ */
318
+ async wait3dModel(id, options = {}) {
319
+ const { intervalMs = 5_000, timeoutMs = 600_000, onProgress, ...request } = options;
320
+ const deadline = Date.now() + timeoutMs;
321
+ for (;;) {
322
+ if (request.signal?.aborted)
323
+ throw request.signal.reason ?? new Error("wait3dModel aborted");
324
+ const { data } = await this.get3dModel(id, request);
325
+ onProgress?.(data);
326
+ if (data.status === "ready" || data.status === "failed")
327
+ return data;
328
+ const remaining = deadline - Date.now();
329
+ if (remaining <= 0) {
330
+ throw new CarImageError({
331
+ status: 408,
332
+ title: "Timed out waiting for the 3D model",
333
+ detail: `Model ${id} is still ${data.status} (${data.progress}%) after ${Math.round(timeoutMs / 1000)} s. Keep polling get3dModel, or pass a webhookUrl when creating one.`,
334
+ });
335
+ }
336
+ await this.sleep(Math.min(Math.max(1, intervalMs), remaining));
337
+ }
338
+ }
175
339
  /** GET /api/v1/vehicles?q= — public fuzzy search across makes and models. */
176
340
  async searchVehicles(query, options = {}) {
177
341
  if (typeof query !== "string" || !query.trim())
@@ -186,7 +350,7 @@ export class CarImageClient {
186
350
  return this.json(response);
187
351
  }
188
352
  // ------------------------------------------------------------- feedback
189
- /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle. */
353
+ /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle (make, model and year, or a vehicle id). */
190
354
  async feedback(input, options = {}) {
191
355
  if (input.rating === undefined && input.verdict === undefined) {
192
356
  throw new TypeError("feedback requires a rating (1-5) or a verdict (good|bad)");
@@ -195,14 +359,14 @@ export class CarImageClient {
195
359
  if ("requestId" in input && input.requestId) {
196
360
  body.request_id = input.requestId;
197
361
  }
198
- else if ("make" in input) {
362
+ else if ("make" in input || "vehicle" in input) {
199
363
  Object.assign(body, imageRequestBody(input));
200
364
  delete body.width;
201
365
  delete body.height;
202
366
  delete body.format;
203
367
  }
204
368
  else {
205
- throw new TypeError("feedback requires requestId or make/model/year");
369
+ throw new TypeError("feedback requires requestId, a vehicle id, or make/model/year");
206
370
  }
207
371
  if (input.rating !== undefined)
208
372
  body.rating = input.rating;
@@ -500,6 +664,7 @@ export class CarImageClient {
500
664
  headers,
501
665
  body: options.body,
502
666
  signal: this.signalFor(options),
667
+ ...(options.redirect ? { redirect: options.redirect } : {}),
503
668
  });
504
669
  }
505
670
  catch (error) {