Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/prefer-asset-ref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@runflow-io/sdk": patch
"@runflow-io/studio": patch
---

Docs: `UploadedAsset.ref` (`runflow://assets/{id}`) is now the recommended model input — the API resolves refs to freshly signed URLs at dispatch (shipped backend-side, formerly RUN-418), so refs never expire in your hands. `url` remains the short-TTL signed HTTPS URL for immediate browser use. Examples and JSDoc updated accordingly; no runtime changes.
6 changes: 1 addition & 5 deletions packages/proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@
"require": "./dist/node.cjs"
}
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"files": ["dist", "README.md", "LICENSE"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
Expand Down
22 changes: 13 additions & 9 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,22 @@ The browser SDK never sees your API key — it's injected by
```ts
const asset = await rf.assets.upload(fileInput.files[0]);
await rf.models.run("google/nano-banana-pro/edit", {
input: { prompt: "remove the price tag", image_urls: [asset.url] },
// asset.ref is the stable runflow://assets/{id} reference — dispatch
// resolves it to a fresh signed URL server-side, so it never expires.
input: { prompt: "remove the price tag", image_urls: [asset.ref] },
});
```

`upload()` runs the platform's presigned flow (create session → PUT the
bytes to storage → confirm), retrying transient failures, and returns
`{ id, url, ref, ... }`. `url` is a short-TTL **signed HTTPS URL** —
pass it straight to any model's media inputs, but don't persist it:
store `id` (or the stable `runflow://assets/{id}` `ref`) and re-mint a
fresh url with `rf.assets.get(id)` when needed. Works in the browser
through `@runflow-io/proxy` (the upload + asset-read endpoints are on
its default allow-list). Raw `Blob`s need `{ filename }`; the cap is
50 MB.
`{ id, url, ref, ... }`. **Prefer `ref`** (`runflow://assets/{id}`) for
model inputs and for anything you store — the API resolves it to a
freshly signed URL at dispatch, so it never expires. `url` is a
short-TTL **signed HTTPS URL** for immediate browser use (previews,
`<img>`); re-mint one with `rf.assets.get(id)` when needed. Works in
the browser through `@runflow-io/proxy` (the upload + asset-read
endpoints are on its default allow-list). Raw `Blob`s need
`{ filename }`; the cap is 50 MB.

### Pin-based editing

Expand Down Expand Up @@ -148,7 +151,8 @@ information for `buildRequest` and the run helpers.
are URL-encoded; `..`/empty segments are rejected.
- `runflow.runs.get(id)` / `runflow.runs.poll(id)` / `runflow.runs.wait(id)`
- `runflow.assets.upload(file, opts?)` — presigned upload (with retry);
returns a signed https `url` + stable `runflow://` `ref`.
returns the stable `runflow://` `ref` (preferred model input) + a
short-TTL signed https `url` for browser display.
- `runflow.assets.get(id)` — re-fetch an asset with a freshly signed `url`.
- `runflow.tools.run(tool, args)` / `runflow.tools.dispatch(tool, args)`
- `runflow.health.check()`
Expand Down
6 changes: 1 addition & 5 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@
"require": "./dist/index.cjs"
}
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"files": ["dist", "README.md", "LICENSE"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
Expand Down
22 changes: 12 additions & 10 deletions packages/sdk/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ export interface UploadedAsset {
/** Asset id (uuid). The durable identifier — store THIS, not `url`. */
id: string;
/**
* Short-TTL signed HTTPS URL for the file. Pass it directly to model
* inputs (`image_url`, `image_urls`, `mask_url`, …) right away. Do not
* persist it — it expires; re-mint a fresh one with `rf.assets.get(id)`
* (allowed through the proxy by default).
* Short-TTL signed HTTPS URL for the file — for immediate browser use
* (previews, `<img>`, downloads). For model inputs prefer `ref`. If you
* do pass `url` somewhere, use it right away and never persist it — it
* expires; re-mint a fresh one with `rf.assets.get(id)`.
*/
url: string;
/**
* Canonical stable reference (`runflow://assets/{id}`). Accepted today by
* ComfyUI workflow file inputs; once the API resolves asset refs at model
* dispatch (RUN-418), prefer this over the expiring `url`.
* Canonical stable reference (`runflow://assets/{id}`) — the PREFERRED
* model input. Dispatch resolves it to a freshly signed URL server-side,
* so it never expires in your hands. Accepted by model media inputs
* (`image_url`, `image_urls`, `mask_url`, …) and ComfyUI file inputs
* alike. Store this (or `id`).
*/
ref: string;
/** Original filename. */
Expand Down Expand Up @@ -154,10 +156,10 @@ function mapAsset(asset: RawAsset): UploadedAsset {
* const rf = new Runflow({ baseUrl: "/api/runflow" });
* const asset = await rf.assets.upload(fileInput.files[0]);
* await rf.models.run("google/nano-banana-pro/edit", {
* input: { prompt, image_urls: [asset.url] },
* input: { prompt, image_urls: [asset.ref] }, // stable ref — never expires
* });
* // Later, if you stored asset.id and the signed url expired:
* const fresh = await rf.assets.get(asset.id);
* // asset.url is a short-TTL signed https URL for previews/<img>;
* // re-mint one later with rf.assets.get(asset.id) if needed.
* ```
*/
export class AssetsResource {
Expand Down
3 changes: 2 additions & 1 deletion packages/studio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ mask.syncToDisplay(rect.width, rect.height, devicePixelRatio);
// pointer events → beginStroke / strokeTo / endStroke; then:
const blob = await mask.toMaskBlob(img.naturalWidth, img.naturalHeight);
const asset = await rf.assets.upload(blob, { filename: "mask.png" });
// → feed asset.url as mask_url to runflow/reference-inpaint etc.
// → feed asset.ref (stable, never expires) as mask_url to
// runflow/reference-inpaint etc.
```

A React state reducer for building a custom shell is tracked as a
Expand Down
10 changes: 2 additions & 8 deletions packages/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"sideEffects": [
"./dist/styles.css"
],
"sideEffects": ["./dist/styles.css"],
"exports": {
".": {
"types": "./dist/index.d.ts",
Expand All @@ -34,11 +32,7 @@
},
"./styles.css": "./dist/styles.css"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"files": ["dist", "README.md", "LICENSE"],
"scripts": {
"build": "tsup && cp src/styles.css dist/styles.css",
"dev": "tsup --watch",
Expand Down
Loading