You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds typed AssetVariant + image-variant fields on AssetRef (width_px, height_px,
blurhash, variant_spec_version, display_url, display_immutable_url, variants).
Threads fields end-to-end via ResolvedAssetRef → AssetManifestEntry → buildAssetRef
so r.assets.put and r.project(id).apply({ assets: { put: [...] } }) produce
identical refs.
Convenience getters thumbUrl/displayUrl are undefined for non-image AssetRefs
(TypeScript narrows; non-images can't accidentally render as broken thumbnails).
New imgTagWithSrcSet helper emits <picture> with WebP-only sources; throws at
call time on missing sizes OR missing variants — no silent fallback. imgTag
defaults src to display_url (HEIC-aware) and opportunistically emits width/height.
MCP assets_put tool description mentions the new fields; handler surfaces
Dimensions / Blurhash / Display URL / Variants in human output. CLI is JSON-only
by design and passes the new fields through verbatim.
Closes openspec change asset-image-variants-client (in run402-private).
Refs run402#392.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+26-1Lines changed: 26 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,31 @@
1
1
# Changelog
2
2
3
-
All notable changes to `@run402/sdk`, `run402` (CLI), `run402-mcp`, and `@run402/functions`. Versions are kept in lockstep across the four packages.
3
+
All notable changes to `@run402/sdk`, `run402` (CLI), and `run402-mcp`. Versions are kept in lockstep across the three packages in this repo. `@run402/functions` lives in the private gateway monorepo and publishes on its own cadence.
4
+
5
+
## 2.3.0 — unreleased
6
+
7
+
Surfaces the v1.49 gateway image-variant pipeline ([run402#392](https://github.com/kychee-com/run402/issues/392), parent change: [`asset-image-variants` in run402-private](https://github.com/kychee-com/run402-private/tree/main/openspec/changes/asset-image-variants)). Additive, non-breaking — old clients silently ignore the new fields.
8
+
9
+
### Added
10
+
11
+
-**`AssetVariant` interface** in `@run402/sdk` (`sdk/src/namespaces/assets.types.ts`). Shape: `{ url, cdn_url, width_px, height_px, format: 'webp' | 'jpeg', sha256 }`. Used by the new `AssetRef.variants` map.
12
+
-**Typed image-variant fields on `AssetRef`** — `width_px`, `height_px`, `blurhash`, `variant_spec_version`, `display_url`, `display_immutable_url`, `variants?: { thumb?, medium?, large?, display_jpeg? }`. All optional. Present only for image uploads (jpeg/png/webp/heic/heif ≥320×320) against a v1.49+ gateway. Threaded end-to-end through `ResolvedAssetRef` → `AssetManifestEntry` → `buildAssetRef`, so the same fields appear whether you upload via `r.assets.put(...)` or `r.project(id).apply({ assets: { put: [...] } })`.
13
+
-**`AssetRef.thumbUrl`** convenience getter — `variants.thumb.cdn_url ?? displayUrl` for image refs, `undefined` for non-images. Single field for grid thumbnails; TypeScript narrows so a picker that does `<img src={pdfRef.thumbUrl}>` is a compile error.
14
+
-**`AssetRef.displayUrl`** convenience getter — `display_url ?? cdn_url` for image refs, `undefined` for non-images. HEIC sources transparently get the JPEG transcode.
15
+
-**`AssetRef.imgTagWithSrcSet(opts)`** helper — emits a `<picture>` with a WebP-only `<source>` (three sizes: 320w / 800w / 1920w) and `display_url` as the `<img>` fallback. Throws at call time on (a) missing/empty `opts.sizes` (browsers over-fetch the largest candidate without it), or (b) missing `variants` (non-image / sub-320 / pre-v1.49 ref) — no silent fallback. AVIF deferred from v1 (documented in JSDoc; `<picture>` type-precedence footgun).
16
+
-**MCP `assets_put` human output** now surfaces `Dimensions: <w>×<h>`, `Blurhash: <hash>`, `Display URL` (when distinct from `cdn_url` — HEIC only), and a `Variants:` line listing kind + dimensions + format for each present variant.
17
+
18
+
### Changed
19
+
20
+
-**`AssetRef.imgTag(alt?)` defaults `<img src>` to `display_url ?? cdn_url`** (was `cdn_url`). Correct rendering for HEIC uploads without HEIC-aware caller code — for non-HEIC images `display_url === cdn_url`, so no behavior change there.
21
+
-**`AssetRef.imgTag(alt?)` opportunistically emits `width`/`height` attributes** when both `width_px` and `height_px` are present on the ref. Eliminates Cumulative Layout Shift for image grids. Silently omits both attributes when either dimension is absent — never throws on absence.
22
+
-**MCP `assets_put` tool description** updated to mention the new image fields and reference the SDK docs for the full AssetRef shape.
23
+
24
+
### Out of scope (deliberate carve-out)
25
+
26
+
-`@run402/functions` type updates — lives in `run402-private/packages/functions/` and co-evolves with the gateway via its own `/publish-functions` skill. The runtime returns the new fields regardless of which `@run402/functions` types are in use.
27
+
- AVIF generation or AVIF-aware helpers — deferred at the gateway. When AVIF returns, it must land at all three sizes simultaneously or via a dedicated `imgTagHero()` helper.
28
+
- On-demand `?w=N&fmt=webp` resize endpoint and project-configurable variant sizes.
`immutable: true` is the default since v1.45. The SDK always computes and sends the object SHA-256; pass `false` only when you specifically need mutable URL/cache semantics.
132
132
133
+
### Image variants (v1.49)
134
+
135
+
Image uploads (jpeg/png/webp/heic/heif) trigger automatic generation of three WebP variants — `thumb` 320w, `medium` 800w, `large` 1920w — plus dimensions, a blurhash placeholder, and (for HEIC/HEIF sources) a JPEG display variant. Everything ships on the returned `AssetRef`:
HEIC/HEIF uploads (from iPhones) preserve the source bytes verbatim — `cdn_url` serves the original HEIC, and a JPEG display variant is generated automatically and surfaced at `display_url`. The `imgTag` / `imgTagWithSrcSet` helpers default the `<img src>` to `displayUrl` so apps render correctly without HEIC-specific code.
168
+
169
+
Foolproof guards keep non-images from rendering broken layouts:
170
+
171
+
-`thumbUrl` and `displayUrl` are `undefined` (not a fallback to `cdn_url`) on non-image AssetRefs — TypeScript narrows them, so a `<img src={pdfRef.thumbUrl}>` is a compile error rather than a broken thumbnail at runtime.
172
+
-`imgTagWithSrcSet` throws at call time when `opts.sizes` is missing or empty (browsers over-fetch the largest candidate without it), AND when the AssetRef has no `variants` (use `imgTag()` instead — see the error message). No silent fallback.
173
+
-`imgTag` opportunistically emits `width`/`height` attributes when present (eliminates CLS) and silently omits them on non-image refs.
174
+
175
+
Variants apply to BOTH write paths — single-shot `r.assets.put(...)` AND the unified apply hero `r.project(id).apply({ assets: { put: [...] } })` return the same `AssetRef` shape with variants populated.
176
+
177
+
AVIF was deferred from v1 — `<picture>` browsers select sources by `type` precedence, not best size, so a single 1920w AVIF would be picked for thumbnails by AVIF-capable browsers. AVIF, if it returns, will land at all three sizes simultaneously or via a separate `imgTagHero()` helper.
178
+
133
179
### Mixed apply — site + assets in one atomic activation
134
180
135
181
Drop a per-key asset put into the same release as your site files. Both promote inside the same activation transaction that flips `live_release_id`, so the asset URLs are live the moment the new release is. Source shorthand: bare strings, `Uint8Array`, or any other `ContentSource` (Blob, FsFileSource from `fileSetFromDir`, `{ data, contentType? }` wrapper). The SDK normalizer hashes once and dedups across slices — same SHA in `site` and `assets` uploads as a single byte stream.
0 commit comments