@nodaro/sdk 2.53.1 → 2.54.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.
Files changed (99) hide show
  1. package/dist/{chunk-GW76IRQD.js → chunk-47ULSVO2.js} +2 -2
  2. package/dist/chunk-47ULSVO2.js.map +1 -0
  3. package/dist/{chunk-V5MY46JA.js → chunk-AN36M6HO.js} +3 -3
  4. package/dist/{chunk-V5MY46JA.js.map → chunk-AN36M6HO.js.map} +1 -1
  5. package/dist/chunk-BEQCU7YJ.js +3 -0
  6. package/dist/chunk-BEQCU7YJ.js.map +1 -0
  7. package/dist/{chunk-NWMO4MHE.cjs → chunk-DAONQZPI.cjs} +2 -2
  8. package/dist/chunk-DAONQZPI.cjs.map +1 -0
  9. package/dist/{chunk-HLYRNWHW.js → chunk-EZXFDCII.js} +16 -12
  10. package/dist/chunk-EZXFDCII.js.map +1 -0
  11. package/dist/{chunk-35CNQ3WW.js → chunk-HAVIVOLO.js} +2 -2
  12. package/dist/chunk-HAVIVOLO.js.map +1 -0
  13. package/dist/{chunk-HGCVWOGK.cjs → chunk-HK5FSTKG.cjs} +11 -2
  14. package/dist/chunk-HK5FSTKG.cjs.map +1 -0
  15. package/dist/{chunk-CK3AMMSC.js → chunk-HWSYNUSC.js} +11 -2
  16. package/dist/chunk-HWSYNUSC.js.map +1 -0
  17. package/dist/{chunk-TVENC6PH.js → chunk-I3MEBVLP.js} +11 -2
  18. package/dist/chunk-I3MEBVLP.js.map +1 -0
  19. package/dist/{chunk-76V6IQZ5.cjs → chunk-JOVJAKBT.cjs} +3 -3
  20. package/dist/{chunk-76V6IQZ5.cjs.map → chunk-JOVJAKBT.cjs.map} +1 -1
  21. package/dist/{chunk-MQ74AKA2.cjs → chunk-JVDUGVLX.cjs} +11 -2
  22. package/dist/chunk-JVDUGVLX.cjs.map +1 -0
  23. package/dist/chunk-MQWZFWHE.js +45 -0
  24. package/dist/chunk-MQWZFWHE.js.map +1 -0
  25. package/dist/chunk-NQF6UKND.cjs +4 -0
  26. package/dist/chunk-NQF6UKND.cjs.map +1 -0
  27. package/dist/{chunk-HAKYPZLB.cjs → chunk-O74MSMFH.cjs} +11 -2
  28. package/dist/chunk-O74MSMFH.cjs.map +1 -0
  29. package/dist/{chunk-EJSGJDZY.js → chunk-PBFODIFT.js} +11 -2
  30. package/dist/chunk-PBFODIFT.js.map +1 -0
  31. package/dist/{chunk-7LSFG2AF.js → chunk-QGVOAPUI.js} +11 -2
  32. package/dist/chunk-QGVOAPUI.js.map +1 -0
  33. package/dist/chunk-RR45WBME.cjs +48 -0
  34. package/dist/chunk-RR45WBME.cjs.map +1 -0
  35. package/dist/{chunk-TK2CEVSK.cjs → chunk-VHTDOACT.cjs} +2 -2
  36. package/dist/chunk-VHTDOACT.cjs.map +1 -0
  37. package/dist/{chunk-CR5E2SF3.cjs → chunk-W5RMFTPA.cjs} +11 -2
  38. package/dist/chunk-W5RMFTPA.cjs.map +1 -0
  39. package/dist/{chunk-GAD3QCMM.cjs → chunk-XZRBRXGJ.cjs} +23 -19
  40. package/dist/chunk-XZRBRXGJ.cjs.map +1 -0
  41. package/dist/entries/characters.cjs +10 -10
  42. package/dist/entries/characters.d.cts +18 -1
  43. package/dist/entries/characters.d.ts +18 -1
  44. package/dist/entries/characters.js +1 -1
  45. package/dist/entries/client.cjs +17 -16
  46. package/dist/entries/client.d.cts +4 -1
  47. package/dist/entries/client.d.ts +4 -1
  48. package/dist/entries/client.js +12 -11
  49. package/dist/entries/core.cjs +5 -5
  50. package/dist/entries/core.js +1 -1
  51. package/dist/entries/creatures.cjs +6 -6
  52. package/dist/entries/creatures.d.cts +16 -1
  53. package/dist/entries/creatures.d.ts +16 -1
  54. package/dist/entries/creatures.js +1 -1
  55. package/dist/entries/keepers.cjs +17 -0
  56. package/dist/entries/keepers.cjs.map +1 -0
  57. package/dist/entries/keepers.d.cts +100 -0
  58. package/dist/entries/keepers.d.ts +100 -0
  59. package/dist/entries/keepers.js +4 -0
  60. package/dist/entries/keepers.js.map +1 -0
  61. package/dist/entries/locations.cjs +5 -5
  62. package/dist/entries/locations.d.cts +9 -1
  63. package/dist/entries/locations.d.ts +9 -1
  64. package/dist/entries/locations.js +1 -1
  65. package/dist/entries/nodes.cjs +2 -2
  66. package/dist/entries/nodes.d.cts +1 -1
  67. package/dist/entries/nodes.d.ts +1 -1
  68. package/dist/entries/nodes.js +1 -1
  69. package/dist/entries/objects.cjs +6 -6
  70. package/dist/entries/objects.d.cts +9 -1
  71. package/dist/entries/objects.d.ts +9 -1
  72. package/dist/entries/objects.js +1 -1
  73. package/dist/entries/scene3d.d.cts +2 -2
  74. package/dist/entries/scene3d.d.ts +2 -2
  75. package/dist/entries/trends.cjs +2 -2
  76. package/dist/entries/trends.d.cts +36 -6
  77. package/dist/entries/trends.d.ts +36 -6
  78. package/dist/entries/trends.js +1 -1
  79. package/dist/index.cjs +84 -74
  80. package/dist/index.d.cts +2 -1
  81. package/dist/index.d.ts +2 -1
  82. package/dist/index.js +17 -15
  83. package/dist/{nodes-C9j0PFto.d.ts → nodes-DuXF6XVv.d.ts} +9 -1
  84. package/dist/{nodes-DGIefTze.d.cts → nodes-bY51Z1Os.d.cts} +9 -1
  85. package/package.json +8 -3
  86. package/dist/chunk-35CNQ3WW.js.map +0 -1
  87. package/dist/chunk-7LSFG2AF.js.map +0 -1
  88. package/dist/chunk-CK3AMMSC.js.map +0 -1
  89. package/dist/chunk-CR5E2SF3.cjs.map +0 -1
  90. package/dist/chunk-EJSGJDZY.js.map +0 -1
  91. package/dist/chunk-GAD3QCMM.cjs.map +0 -1
  92. package/dist/chunk-GW76IRQD.js.map +0 -1
  93. package/dist/chunk-HAKYPZLB.cjs.map +0 -1
  94. package/dist/chunk-HGCVWOGK.cjs.map +0 -1
  95. package/dist/chunk-HLYRNWHW.js.map +0 -1
  96. package/dist/chunk-MQ74AKA2.cjs.map +0 -1
  97. package/dist/chunk-NWMO4MHE.cjs.map +0 -1
  98. package/dist/chunk-TK2CEVSK.cjs.map +0 -1
  99. package/dist/chunk-TVENC6PH.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodaro/sdk",
3
- "version": "2.53.1",
3
+ "version": "2.54.0",
4
4
  "description": "Typed REST client for the Nodaro API — workflows, jobs, executions, apps, characters, and more.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -120,6 +120,11 @@
120
120
  "import": "./dist/entries/jobs.js",
121
121
  "require": "./dist/entries/jobs.cjs"
122
122
  },
123
+ "./keepers": {
124
+ "types": "./dist/entries/keepers.d.ts",
125
+ "import": "./dist/entries/keepers.js",
126
+ "require": "./dist/entries/keepers.cjs"
127
+ },
123
128
  "./library": {
124
129
  "types": "./dist/entries/library.d.ts",
125
130
  "import": "./dist/entries/library.js",
@@ -317,8 +322,8 @@
317
322
  "test:watch": "vitest"
318
323
  },
319
324
  "dependencies": {
320
- "@nodaro/shared": "^3.46.0",
321
- "@nodaro/prompts": "^1.44.0"
325
+ "@nodaro/shared": "^3.47.0",
326
+ "@nodaro/prompts": "^1.46.0"
322
327
  },
323
328
  "peerDependencies": {
324
329
  "@fingerprintjs/fingerprintjs": "^5.2.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/trends.ts"],"names":[],"mappings":"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","file":"chunk-35CNQ3WW.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport type { CastSwapEngine } from \"./cast-swap.js\"\n\n/**\n * Trends — ready-made Nodaro Motion recipes. A trend is a short clip with slots\n * (the people, pets, products and place you can replace); fill its slots with\n * your own pictures and run it to get the clip back with them in it.\n *\n * Cloud edition only: `/v1/trends` 404s on self-hosted installs. Only\n * published trends exist here: a draft, unpublished or unknown slug is a 404\n * on every route. `\"runs\"` and `\"tags\"` are never slugs (`/v1/trends/runs` is\n * the run list, `/v1/trends/tags` the tag list): the slug methods throw a\n * `TypeError` for them before any request. `run()` answers 202 with a job id; follow it with\n * `client.jobs.get(jobId)` / `client.jobs.getStatus(jobId)` like any other\n * job, or read it back as a trend run with `getRun(jobId)` / `runs()`.\n *\n * Repeats: pass `{ idempotencyKey }` to `run()` and it is sent as the\n * `Idempotency-Key` header. A repeat with the same key answers with the first\n * run's `jobId`; nothing new is started or charged.\n *\n * Refusals (thrown as a `NodaroError` subclass; body `{ error: { code, message } }`):\n * - 400 `validation_error` (the body, a query, or a bad `cursor`);\n * `estimate()` / `run()` only, before anything is charged:\n * `missing_slot` (a required slot without pictures, or an optional one\n * left empty that the chosen example has no pictures for), `unknown_slot`,\n * `invalid_choice` (a chip the slot's kind does not take),\n * `chip_not_offered` (a chip of the kind that the slot's\n * {@link TrendDetailSlot.chips} does not list: the trend fixes it),\n * `example_out_of_range` (`example` names no example of the trend, or is\n * sent at all on a trend with none), `preview_unavailable` (the trend offers no\n * preview), `invalid_slot_mention` (a `{slot:<key>}` in `details` naming no\n * slot), `prompt_too_long` (shorten `details`), `draft_unsupported`\n * (`draft: true` on a trend whose `engine` has no draft: Nodaro Motion Pulse),\n * `resolution_unsupported` (a `resolution` not in the trend's\n * `resolutions`), `too_many_images` (a Nodaro Motion Pulse trend's run would send more\n * than 10 pictures: send fewer per slot). The slot errors carry the\n * slot's key as `slot` (in `NodaroError.details`), and `chip_not_offered`\n * the chip as `chip` too.\n * - 401 `unauthorized`.\n * - 404 `not_found`: the trend is not published (every method), or `run()`'s\n * `sessionId` is not a session you may use.\n * - 409 `trend_unavailable` (`estimate()` / `run()`): the trend's setup is out\n * of date — it cannot run until it is updated, so a retry gets the same answer.\n * - 502 `engine_error` (`estimate()` / `run()`).\n * - 503 `engine_unsupported` (this platform version cannot run trends yet),\n * `trend_unavailable` (`estimate()` / `run()`), `not_available` (this\n * server has no trend catalog yet).\n * - `estimate()` / `run()` answer the video engine's refusals with the trend\n * routes' OWN codes and fixed messages — never the engine's text or\n * `issues`: 400 `trend_run_rejected` (the inputs could not run), or\n * `clip_out_of_range` / `preview_out_of_range` / `video_unreadable` (this\n * trend's clip), `unsafe_url` (a picture is not a public http(s) address),\n * `raw_reference_token` (`details` holds `{image:` / `{video:` /\n * `{audio:`); 402 `insufficient_credits` (or a workspace cap) as given;\n * 413 `storage_limit_exceeded`; 422 `job_blocked` (the job policy\n * blocked it); 429 `rate_limited`; 503 `engine_unsupported`,\n * `estimate_unavailable`; 502 `engine_error` for anything else. `run()`\n * also answers 400 `validation_error` for an `idempotencyKey` outside\n * 8–200 characters.\n */\n\n/** `/v1/trends/runs` is the run list and `/v1/trends/tags` the tag list, so neither can be a trend's slug. */\nconst RESERVED_SLUGS: Readonly<Record<string, string>> = {\n runs: \"the run list; use runs()\",\n tags: \"the tag list; use tags()\",\n}\n\nfunction trendPath(slug: string, rest = \"\"): string {\n if (Object.hasOwn(RESERVED_SLUGS, slug)) {\n throw new TypeError(`\"${slug}\" is not a trend slug (it is reserved for ${RESERVED_SLUGS[slug]})`)\n }\n return `/v1/trends/${encodeURIComponent(slug)}${rest}`\n}\n\n/** What a slot holds. */\nexport type TrendSlotKind = \"person\" | \"location\" | \"product\" | \"pet\"\n\n/** A chip: make this trait anew from your pictures (`\"new\"`), or keep the clip's (`\"original\"`). */\nexport type TrendChoice = \"new\" | \"original\"\n\n/**\n * The chips a slot can take, by its kind: a person `body` / `outfit` / `hair`,\n * a pet `body` / `coat`, a product `shape` / `finish`, a location none.\n */\nexport interface TrendChips {\n body?: TrendChoice\n outfit?: TrendChoice\n hair?: TrendChoice\n coat?: TrendChoice\n shape?: TrendChoice\n finish?: TrendChoice\n}\n\n/** A chip's name: `body`, `outfit`, `hair`, `coat`, `shape` or `finish`. */\nexport type TrendChipKey = keyof TrendChips\n\nexport type TrendResolution = \"480p\" | \"720p\" | \"1080p\"\n\n/**\n * The engine a trend runs on, by its public id ({@link CastSwapEngine}):\n * `\"motion-prism\"` (Nodaro Motion Prism) or `\"motion-pulse\"` (Nodaro Motion Pulse).\n * The trend chooses it, never the person running it.\n */\nexport type TrendEngine = CastSwapEngine\n\n/** A trend in the catalog (`list()`). Text is in the resolved locale. */\nexport interface TrendCard {\n id: string\n slug: string\n name: string\n description?: string\n tags: string[]\n /** e.g. `\"trending\"`, `\"new\"`. */\n badges: string[]\n /**\n * The trend's first example, else (a trend with no example) its original's\n * display copy, as {@link TrendDetail.original}.\n */\n cover: { videoUrl: string; posterUrl?: string; width: number; height: number }\n /** How many slots of each kind the trend has. */\n needs: Record<TrendSlotKind, number>\n /** The length of a full run, in seconds. */\n durationSec: number\n isFavorite: boolean\n}\n\n/** One slot of a trend: what it asks you for. */\nexport interface TrendDetailSlot {\n /** The key to fill in {@link TrendRunRequest.slots}, and to mention in `details` as `{slot:<key>}`. */\n key: string\n label: string\n kind: TrendSlotKind\n /**\n * A required slot needs pictures; an optional one left empty takes the\n * chosen example's, or, on a trend with no example, stays as in the original.\n */\n required: boolean\n hint?: string\n /**\n * A picture showing who or what this slot is in the original (e.g. a frame\n * of that actor), when the trend has one. Show it on an empty slot; a run\n * never sends it.\n */\n hintImageUrl?: string\n /**\n * The chips you may set on this slot, in its kind's order; `[]` = none (show\n * no chips row). A chip of the kind that is not listed is fixed by the\n * trend: sending it is refused with `400 chip_not_offered`.\n */\n chips: TrendChipKey[]\n /** The starting value of the chips in {@link chips} (only those). */\n defaults?: TrendChips\n}\n\n/** A finished run shown on a trend's page, with the pictures it used per slot. */\nexport interface TrendExample {\n videoUrl: string\n posterUrl?: string\n width: number\n height: number\n /** Slot key → the pictures this example used for it. */\n slots: Record<string, string[]>\n}\n\n/**\n * The clip a trend recasts, as you may show it: a browser-safe copy (H.264\n * MP4) of the clip every run starts from. Its fields parallel\n * {@link TrendExample}'s.\n */\nexport interface TrendOriginal {\n videoUrl: string\n posterUrl?: string\n width: number\n height: number\n /** The copy's length, in seconds. */\n durationSec: number\n /**\n * Where the 4-second preview starts in the clip, in seconds (`0` included),\n * when the trend offers one ({@link TrendDetail.previewSec} is `4`); absent\n * otherwise.\n */\n previewStartSec?: number\n}\n\n/** A trend's page (`get()`). */\nexport interface TrendDetail extends TrendCard {\n /** The published version this page shows. */\n version: number\n /** `4` when the trend offers a 4-second preview run, else `null`. */\n previewSec: 4 | null\n /**\n * The original clip every run starts from, to show beside the examples.\n * Absent only on a version without a display copy (and from an older server).\n */\n original?: TrendOriginal\n /** The engine every run of this trend uses. Absent (an older server) = `\"motion-prism\"`. */\n engine?: TrendEngine\n /** Its name to show: \"Nodaro Motion Prism\" or \"Nodaro Motion Pulse\". Absent from an older server. */\n engineName?: string\n /**\n * The resolutions a run may ask for, the default first. Nodaro Motion Prism:\n * `[\"480p\", \"720p\", \"1080p\"]`, where 480p is a draft (show it as one);\n * Nodaro Motion Pulse: `[\"720p\", \"1080p\"]`, no draft. Offer exactly these.\n */\n resolutions: TrendResolution[]\n slots: TrendDetailSlot[]\n /** Finished runs to show beside the original; may be empty (a trend with only its original). */\n examples: TrendExample[]\n}\n\n/** What you put in one slot of a run: 1–3 pictures, and the chips the slot offers ({@link TrendDetailSlot.chips}). */\nexport interface TrendRunSlot extends TrendChips {\n images?: string[]\n}\n\n/** The body of `estimate()` and `run()`. */\nexport interface TrendRunRequest {\n /**\n * Slot key → what you put in it. Every required slot needs pictures; an\n * optional slot left out takes the chosen example's, or, on a trend with no\n * example, stays as in the original. 1–3 pictures per slot.\n */\n slots: Record<string, TrendRunSlot>\n /**\n * 1-based example whose pictures fill the optional slots you leave empty.\n * Default 1. Leave it out on a trend with no examples: sending one there\n * answers 400 `example_out_of_range` (and `0` is a `validation_error`).\n */\n example?: number\n /** Your own words, at most 1,000 characters. Mention a slot as `{slot:<key>}`. */\n details?: string\n /** `true` runs a 4-second preview (only when the trend's `previewSec` is `4`). */\n preview?: boolean\n /**\n * One of the trend's {@link TrendDetail.resolutions}; default its first\n * (`\"480p\"` on Nodaro Motion Prism, `\"720p\"` on Nodaro Motion Pulse). Another answers 400\n * `resolution_unsupported`.\n */\n resolution?: TrendResolution\n /**\n * A draft run, only at 480p on a Nodaro Motion Prism trend. Default `true` at\n * 480p. `true` on a Nodaro Motion Pulse trend answers 400 `draft_unsupported`.\n */\n draft?: boolean\n /**\n * A generation session of yours. Accepted (and checked: one you may not use\n * is a 404), but not yet applied: trend runs are not filed into sessions yet.\n */\n sessionId?: string\n /** Accepted, but not used by the run today. */\n projectId?: string\n /**\n * A full run's own preview: the `jobId` of a completed preview run of this\n * trend that you started. When that preview needed the trend's retry (the\n * clip refused at first), the full run is made that way from the start (the\n * quote includes it). Any other job is ignored, without an error. Must be a uuid\n * (else 400 `validation_error`).\n */\n previewJobId?: string\n /**\n * `run()`: your client app's slug (e.g. `\"tools\"`: lowercase letters,\n * digits and `-`, 1–40 characters, starting with a letter or digit), as on\n * any job: the run's job carries it as `input_data.origin`, so\n * `client.jobs.list({ origin })` finds it. Not a slug is a 400\n * `validation_error`; without it the run has no origin. `estimate()`\n * ignores it.\n */\n origin?: string\n}\n\n/** What a run would cost, in credits (`estimate()`). */\nexport interface TrendEstimate {\n /** The charge the run is expected to come to. */\n expectedCredits: number\n /** What is reserved when the run starts; the run is charged what it actually costs. */\n holdCredits: number\n /**\n * The parts of the quote, by name, for display only: some are alternatives\n * or sit outside the totals (`privacy`, the retry's, beside\n * `privacyCredits`), so read `expectedCredits` and `holdCredits`, never\n * their sum.\n */\n breakdown: Record<string, number>\n /**\n * Only on a trend that retries a clip the video model refuses: the extra\n * credits charged if that retry runs. Not included in `expectedCredits` or\n * `holdCredits`. 0 (or absent) when there is nothing extra to charge.\n */\n privacyCredits?: number\n /** @deprecated read {@link privacyCredits} (an older server answered this name). */\n maskingCredits?: number\n}\n\n/** Per-call options for {@link TrendsResource.run}. */\nexport interface TrendRunOptions {\n /**\n * Sent as the `Idempotency-Key` header, unchanged (an empty string sends no\n * header). The server takes 8–200 characters and answers 400 for any other\n * length. Reuse it when retrying the same run.\n */\n idempotencyKey?: string\n}\n\n/** One slot of a past run, as it was run. Labels are English. */\nexport interface TrendRunDisplaySlot extends TrendChips {\n key: string\n label: string\n kind: TrendSlotKind\n images: string[]\n /**\n * `true` when the slot was left empty and the example's pictures filled it.\n * `false` with no `images`: left empty on a trend with no example, so it\n * stayed as in the original.\n */\n fromExample: boolean\n}\n\n/**\n * One of your trend runs (`runs()`, `getRun()`). Stays readable after the\n * trend moves on or is unpublished.\n *\n * A Full Quality version of a draft is a run of its own, with its own status,\n * result and charge: `fullQualityOf` names the draft it was made from, and it\n * stays listed after that draft is deleted.\n */\nexport interface TrendRun {\n jobId: string\n status: string\n progress: number | null\n /**\n * Why a failed or cancelled run did not finish; `null` otherwise. `code` is\n * `safety-block` or `policy-block` when the job was blocked, `credits` when\n * it ran out of credits partway (it was refunded: ask the person to top up,\n * then run it again), else `cancelled` or `failed`. `message` is the same\n * fixed sentence for every code — branch on `code`, not on the text.\n */\n error: { code?: string; message: string } | null\n createdAt: string\n completedAt: string | null\n /** The trend and version it ran (name in English). */\n trend: { id: string; slug: string; version: number; name: string }\n /**\n * The 1-based example used for empty optional slots; `0` = the trend had no\n * example (none was used: never show it as \"Example 0\").\n */\n example: number\n slots: TrendRunDisplaySlot[]\n details?: string\n preview: boolean\n resolution: string\n draft: boolean\n /** The engine the run was made on. Absent (an older server) = `\"motion-prism\"`. */\n engine?: TrendEngine\n /** Its name to show: \"Nodaro Motion Prism\" or \"Nodaro Motion Pulse\". Absent from an older server. */\n engineName?: string\n /** The result, once completed. */\n videoUrl: string | null\n posterUrl: string | null\n /**\n * What the run was charged once settled, every step it ran included (its\n * reference preparation, mask and video); `null` until the run's own hold\n * settles. A step adds its charge once it settles.\n */\n creditsCharged: number | null\n /** The run's hold: its own plus its steps'. Absent from an older server. */\n creditsHeld?: number | null\n /** What went back from `creditsHeld` once every hold in the run settled; `null` until then. Absent from an older server. */\n creditsReleased?: number | null\n /** A Full Quality version: the job id of the run (a draft, or a Nodaro Motion Pulse 720p run) it was made from. `null` for any other run. */\n fullQualityOf: string | null\n /**\n * A draft's (or Nodaro Motion Pulse 720p run's) newest Full Quality try, if one was started: a pointer only,\n * to know an upgrade exists and how it is going. Its video and charge are on\n * that run, listed on its own (its `fullQualityOf` is this run's `jobId`) —\n * read it there (`getRun(fullQuality.jobId)`). Older tries of the draft (a\n * failed or cancelled one tried again) are not listed by `runs()`;\n * `getRun()` still reads one by its id.\n */\n fullQuality: { jobId: string; status: string } | null\n /**\n * Until when a completed run can be redone in Full Quality — a Nodaro Motion\n * Prism draft (the same take at 1080p) or a Nodaro Motion Pulse run at 720p (a new take at\n * 1080p); `null` otherwise.\n */\n fullQualityUntil: string | null\n}\n\nexport interface ListTrendsParams {\n /** Search text. */\n q?: string\n tag?: string\n /** `true` lists only your favorites. */\n favorites?: boolean\n /** A platform language for names and descriptions; default your profile's, then English. */\n locale?: string\n /** The previous page's `next`. */\n cursor?: string\n /** Page size, 1–60 (server default 30). */\n limit?: number\n}\n\nexport interface ListTrendRunsParams {\n /** Only runs of this trend (its slug). */\n trend?: string\n /** The previous page's `next`. */\n cursor?: string\n /** Page size, 1–60 (server default 30). */\n limit?: number\n}\n\n/** A tag in use on the published catalog (`tags()`). */\nexport interface TrendTag {\n /** The tag itself: pass it as `list({ tag })`. */\n tag: string\n /** The tag's label in the resolved locale; the tag itself when it has none. */\n label: string\n /** How many published trends carry it. */\n count: number\n}\n\nexport interface TrendPage<T> {\n data: T[]\n /** Pass back as `cursor` for the next page; `null` on the last one. */\n next: string | null\n}\n\nexport class TrendsResource {\n constructor(private client: NodaroClientCore) {}\n\n /** The published catalog in its curated order (`GET /v1/trends`), a page at a time. */\n list(params: ListTrendsParams = {}): Promise<TrendPage<TrendCard>> {\n const query: Record<string, string> = {}\n if (params.q) query.q = params.q\n if (params.tag) query.tag = params.tag\n if (params.favorites) query.favorites = \"1\"\n if (params.locale) query.locale = params.locale\n if (params.cursor) query.cursor = params.cursor\n if (params.limit !== undefined) query.limit = String(params.limit)\n return this.client.request(\"GET\", \"/v1/trends\", { query })\n }\n\n /** A published trend's page (`GET /v1/trends/:slug`). */\n async get(slug: string, params: { locale?: string } = {}): Promise<TrendDetail> {\n const query: Record<string, string> = {}\n if (params.locale) query.locale = params.locale\n const res = await this.client.request<{ data: TrendDetail }>(\"GET\", trendPath(slug), { query })\n return res.data\n }\n\n /** Quote a run before starting it (`POST /v1/trends/:slug/estimate`); spends nothing. */\n async estimate(slug: string, request: TrendRunRequest): Promise<TrendEstimate> {\n const res = await this.client.request<{ data: TrendEstimate }>(\n \"POST\",\n trendPath(slug, \"/estimate\"),\n { body: request },\n )\n return res.data\n }\n\n /**\n * Start a run (`POST /v1/trends/:slug/runs`, 202). Returns the job to follow.\n * With `options.idempotencyKey` (sent as the `Idempotency-Key` header) a\n * repeat with the same key answers with the first run's `jobId`.\n */\n async run(slug: string, request: TrendRunRequest, options?: TrendRunOptions): Promise<{ jobId: string }> {\n const res = await this.client.request<{ data: { jobId: string } }>(\n \"POST\",\n trendPath(slug, \"/runs\"),\n {\n body: request,\n ...(options?.idempotencyKey ? { headers: { \"Idempotency-Key\": options.idempotencyKey } } : {}),\n },\n )\n return res.data\n }\n\n /**\n * Your trend runs, newest first (`GET /v1/trends/runs`). A Full Quality\n * version of a draft is listed as its own run (`fullQualityOf`).\n */\n runs(params: ListTrendRunsParams = {}): Promise<TrendPage<TrendRun>> {\n const query: Record<string, string> = {}\n if (params.trend) query.trend = params.trend\n if (params.cursor) query.cursor = params.cursor\n if (params.limit !== undefined) query.limit = String(params.limit)\n return this.client.request(\"GET\", \"/v1/trends/runs\", { query })\n }\n\n /**\n * One of your trend runs by its job id (`GET /v1/trends/runs/:jobId`), in\n * the same shape as `runs()`. A job that is not your own trend run is a\n * `NotFoundError`.\n */\n async getRun(jobId: string): Promise<TrendRun> {\n const res = await this.client.request<{ data: TrendRun }>(\"GET\", `/v1/trends/runs/${encodeURIComponent(jobId)}`)\n return res.data\n }\n\n /**\n * The tags in use on the published catalog, each with its label in the\n * resolved locale (`GET /v1/trends/tags`), most used first.\n */\n async tags(params: { locale?: string } = {}): Promise<TrendTag[]> {\n const query: Record<string, string> = {}\n if (params.locale) query.locale = params.locale\n const res = await this.client.request<{ data: TrendTag[] }>(\"GET\", \"/v1/trends/tags\", { query })\n return res.data\n }\n\n /** Add a trend to your favorites (`PUT /v1/trends/:slug/favorite`). */\n async favorite(slug: string): Promise<void> {\n await this.client.request<void>(\"PUT\", trendPath(slug, \"/favorite\"))\n }\n\n /** Remove a trend from your favorites (`DELETE /v1/trends/:slug/favorite`). */\n async unfavorite(slug: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", trendPath(slug, \"/favorite\"))\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/objects.ts"],"names":[],"mappings":"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","file":"chunk-7LSFG2AF.js","sourcesContent":["import type { ObjectAspectRatio, ObjectAssetType, ObjectAttachColumn } from \"@nodaro/shared/entities\"\nexport type { ObjectAssetType, ObjectAttachColumn, ObjectAspectRatio } from \"@nodaro/shared/entities\"\nexport { OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, OBJECT_ASPECT_OPTIONS, OBJECT_ASPECT_DEFAULTS } from \"@nodaro/shared/entities\"\nimport type { NodaroClientCore } from \"../core.js\"\nimport type { GenerationSessionParams } from \"./nodes.js\"\n\n/**\n * Re-export the shared `ObjectAssetType` / `ObjectAttachColumn` unions and\n * their runtime tuples so SDK consumers don't have to add `@nodaro/shared` as\n * a second dependency just to typecheck the `assetType` / `attachToColumn`\n * fields. Single source of truth lives in `@nodaro/shared/entity-prompts`.\n *\n * `ObjectAspectRatio` is re-exported alongside them — `generateMotion`'s\n * `aspectRatio` field is the 5-value object enum (1:1 / 3:4 / 16:9 / 9:16 /\n * 4:3) from `@nodaro/shared/object-aspect-defaults`. Distinct from\n * `CharacterAspectRatio` because objects support an extra 4:3 framing for\n * product-showcase shots.\n */\n\n/**\n * Reference-photo kind discriminator — the mood-board roles a user can attach\n * to an object. Mirrors the `reference_photos.kind` field accepted by\n * `backend/src/routes/objects.ts` (the route accepts open strings; this SDK\n * type narrows to the 6 canonical roles surfaced by the Studio). `other` is\n * the free-form bucket.\n */\nexport type ObjectReferencePhotoKind =\n | \"front\"\n | \"side\"\n | \"detail\"\n | \"context\"\n | \"moodBoard\"\n | \"other\"\n\nexport interface ObjectReferencePhoto {\n url: string\n kind: ObjectReferencePhotoKind\n}\n\n/**\n * An object record returned by Nodaro's REST API. Mirrors the camelCase\n * shape produced by `backend/src/routes/objects.ts::toCamel()`.\n *\n * Asset buckets (`angles`, `materials`, `variations`, `motionClips`) are\n * independent JSONB arrays keyed by a human-readable variant name (e.g.\n * `\"front\"`, `\"wood\"`, `\"weathered\"`). Each entry's `url` points at an\n * R2-hosted asset.\n *\n * Identity-foundation fields:\n * - `referencePhotos` — caller-supplied mood-board refs (cap 20). Objects\n * do NOT carry a `piiConsentAt` field (location Phase 2 #7 only).\n * - `canonicalDescription` — ~80–120-word LLM-authored visual caption,\n * populated by `approveMainImage()` / `recaption()`. The wire still sends\n * `\"\"` on caption sub-failure (the breaking wire change is deferred to a\n * major bump), but `get()` normalizes `\"\"` → `null` so consumers see the\n * same `string | null` semantics as characters.\n * - `styleLock` — whether asset gens should anchor to the canonical style\n * captured at approval time. Defaults to `true` on new rows.\n *\n * `Object` shadows the JS global, which TypeScript handles cleanly via\n * local-scope resolution. Consumers who need both can alias as\n * `import type { Object as NodaroObject } from \"@nodaro/sdk\"`.\n */\nexport interface Object {\n id: string\n userId: string\n nodeId: string\n projectId: string | null\n name: string\n description: string | null\n category: string | null\n style: string | null\n sourceImageUrl: string | null\n /** MODEL_CATALOG image-model id the main image was generated with (or `null`).\n * Set on create + editable via the update route. */\n imageProvider: string | null\n angles: Array<{ name: string; url: string }>\n materials: Array<{ name: string; url: string }>\n variations: Array<{ name: string; url: string }>\n motionClips: Array<{ name: string; url: string }>\n /** Named Product Boards — dense reference sheets, one per variant/colorway\n * (the `generate-image/product-board` factory preset rendered from the\n * object's images). A first-class bucket: community publish snapshots it\n * and clone hands the consumer their own copy to extend. Defaults to `[]`.\n * `type` marks an image-collage `\"identity\"` sheet vs a plain `\"looks\"`\n * board; `sourceImages` are the R2 URLs it was collaged from. Both optional\n * + backward-compatible (legacy boards have neither). */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }> | null\n referencePhotos: ObjectReferencePhoto[]\n /** `null` when no caption is set (or the LLM caption sub-failed) — the wire\n * sends `\"\"`, normalized to `null` in `get()` to match character semantics. */\n canonicalDescription: string | null\n styleLock: boolean\n /** The user's chosen DEFAULT asset take per variant (Studio version history).\n * OPAQUE map: key `\"<bucket>:<variant>\"` (e.g. `\"angles:front\"`) → the chosen\n * asset URL (one already present in that bucket). Stored verbatim — keys are\n * NOT normalized; soft-capped server-side at 200 keys / 2048-char values\n * (overflow dropped silently). Defaults to `{}`. */\n selectedAssetByVariant?: Record<string, string> | null\n deletedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * GET /v1/objects/:id may append a `pendingJobs` bucket the studio uses to\n * rehydrate spinners after a reload. Optional on the SDK surface — it doesn't\n * appear on `list()` rows.\n */\nexport interface ObjectDetail extends Object {\n pendingJobs?: Array<{\n jobId: string\n assetType: string\n name: string\n status: string\n }>\n}\n\n/**\n * Body for `client.objects.create()`. Mirrors the INSERT branch of\n * `upsertObjectBody` in `backend/src/routes/objects.ts`. `name` + `nodeId`\n * are required on create.\n */\nexport interface CreateObjectInput {\n nodeId: string\n name: string\n description?: string\n category?: ObjectCategory\n style?: string\n workflowId?: string\n projectId?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: ObjectReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map.\n * Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n}\n\n/**\n * The 10-value Object category enum. Mirrors the literal accepted by\n * `POST /v1/generate-object` and surfaced in the Object Studio category\n * picker. Distinct from location's geography-based set.\n */\nexport type ObjectCategory =\n | \"furniture\"\n | \"vehicle\"\n | \"weapon\"\n | \"food\"\n | \"clothing\"\n | \"electronics\"\n | \"nature\"\n | \"tool\"\n | \"animal\"\n | \"other\"\n\n/**\n * Body for `client.objects.update()`. Mirrors the UPDATE branch of\n * `upsertObjectBody` in `backend/src/routes/objects.ts`.\n *\n * Worker-owned asset buckets (`angles` / `materials` / `variations` /\n * `motionClips`) are deliberately omitted — the route drops them on UPDATE\n * so a Studio auto-save with a stale snapshot cannot clobber the worker's\n * atomic `append_object_asset()` writes.\n *\n * `expectedUpdatedAt` is the optimistic-concurrency token: when present, the\n * UPDATE only succeeds if the row's `updated_at` still matches; on mismatch\n * the route returns 409 so the studio can re-fetch + merge.\n */\nexport interface UpdateObjectInput {\n name?: string\n description?: string\n category?: ObjectCategory\n style?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: ObjectReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map\n * (omit to leave untouched). Keys stored verbatim; soft-capped server-side\n * at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n /** Named Product Boards (see `Object.boards`) — whole-array replace,\n * USER-owned (unlike the worker-owned buckets it flows through UPDATE).\n * Server caps: 24 boards, 200-char names, 30 sourceImages per board. */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }>\n expectedUpdatedAt?: string\n}\n\nexport interface UpdateObjectResult {\n id: string\n updatedAt: string\n}\n\n/**\n * Combined create + update body (parameter for both branches). Exported for\n * callers that want to drive a single `upsert` flow without picking between\n * `Create*` and `Update*`. Mirrors the `upsertObjectBody` Zod schema in\n * `backend/src/routes/objects.ts`. `nodeId` + `name` are required on INSERT;\n * `id` flips the route into UPDATE mode.\n */\nexport interface UpsertObjectInput extends CreateObjectInput {\n id?: string\n expectedUpdatedAt?: string\n}\n\nexport type UpsertObjectResult = { id: string } | UpdateObjectResult\n\nexport interface ListObjectsParams {\n /** When true, return archived objects instead of active ones. */\n archived?: boolean\n /** Optional project filter — server-scoped to the caller's user. */\n projectId?: string\n /**\n * Search words: only the objects whose name or description contains every\n * word (split on spaces, the first 5), in any order, ignoring case. Each\n * word is matched as written (`%`, `_` and `*` are not wildcards;\n * punctuation is kept); blank means no search; up to 200 characters.\n * Combines with the other filters and with `cursor` paging.\n */\n q?: string\n /**\n * Max rows to return (cap 500). OPT-IN: omitted, the server returns the\n * full legacy listing with no `nextCursor`; supplied, the response is one\n * page plus `nextCursor` — keep passing it back until it is `null`.\n */\n limit?: number\n /** Opaque cursor from a prior page's `nextCursor` (fetches the next page). */\n cursor?: string\n}\n\n/**\n * Input for `client.objects.generate()` — fires the\n * `POST /v1/generate-object` route. Produces 1–10 candidate\n * main images; each lands as one `jobs` row in `pending` state and is then\n * enqueued for the worker.\n *\n * When `attachToObjectId` is set AND `count === 1`, the worker writes the\n * resulting URL directly to `objects.source_image_url` on completion —\n * caller doesn't need a separate `approveMainImage` call. Multi-candidate\n * batches MUST go through explicit approval so the user picks the winner.\n *\n * `seedPromptHint` (Pass 7 F-77) flows the parameter-picker's prompt fragment\n * through to the worker so a catalog selection (e.g. \"antique brass lantern\")\n * gets appended to the generated prompt context.\n */\nexport interface GenerateObjectInput extends GenerationSessionParams {\n name: string\n description?: string\n userPrompt?: string\n category?: ObjectCategory\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n sourceImageUrl?: string\n provider?: string\n /** Number of candidate images to generate (1–10; server-validated). */\n count?: number\n /** Auto-attach the result to this object row (single-candidate only). */\n attachToObjectId?: string\n /** Parameter-picker prompt-fragment pass-through. */\n seedPromptHint?: string\n /** Optional name to set on the attached row alongside the main image. */\n attachName?: string\n /** Optimistic-concurrency token for the single-candidate auto-attach path. */\n expectedUpdatedAt?: string\n}\n\n/**\n * `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,\n * matching characters). `jobId` is a deprecated back-compat alias populated only\n * on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)\n */\nexport interface GenerateObjectResult {\n jobIds: string[]\n /** @deprecated count===1 back-compat alias — use `jobIds`. */\n jobId?: string\n}\n\n/**\n * Input for `client.objects.generateAsset()` — fires the\n * `POST /v1/generate-object-asset` route. Produces a single\n * angles / materials / variations / custom variant.\n *\n * When all three studio-path fields are set (`attachToObjectId` +\n * `attachToColumn` + `attachName`), the worker appends\n * `{ name: attachName, url: <result> }` to the named JSONB array column on\n * the user's object row on completion. `attachToColumn` is REQUIRED for\n * `assetType === \"custom\"` — the worker can't infer the bucket from the\n * asset type.\n */\nexport interface GenerateObjectAssetInput extends GenerationSessionParams {\n assetType: ObjectAssetType\n variant: string\n name: string\n description?: string\n userPrompt?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n sourceImageUrl?: string\n provider?: string\n attachToObjectId?: string\n attachToColumn?: ObjectAttachColumn\n attachName?: string\n /** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */\n seedPromptHint?: string\n}\n\nexport interface GenerateObjectAssetResult {\n jobId: string\n}\n\n/**\n * Input for `client.objects.generateMotion()` — fires the\n * `POST /v1/generate-object-motion` route. Produces a single motion clip\n * (rotation, orbit, hover, drift, etc.) animated FROM a static product-shot\n * image.\n *\n * Mirrors `client.locations.generateMotion()` minus the location-specific\n * atmospheric fields. The route hardcodes the attach column to `motion_clips`\n * — callers supply `attachToObjectId` + `attachName` only.\n *\n * `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the\n * route has no fallback (no `source_image_url` column to pull from on the\n * objects row at this point in the flow; the studio path supplies the\n * canonical product-shot URL explicitly).\n *\n * Object-specific defaults vs location:\n * - `provider` defaults to `\"kling-turbo\"` (not location's `\"kling\"`)\n * - `aspectRatio` defaults to `\"1:1\"` server-side via\n * `resolveObjectAspectRatio({ assetType: \"motion\" })` — objects are\n * product-showcase framing, not cinematic establishing shots.\n *\n * When the studio path is set (`attachToObjectId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the object row's\n * `motion_clips` JSONB column on completion.\n */\nexport interface GenerateObjectMotionInput extends GenerationSessionParams {\n motionPrompt: string\n sourceImageUrl: string\n provider?: string\n name: string\n /** Source clip URL — when set, worker routes to video-to-video refine. */\n refineFromVideoUrl?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n canonicalDescription?: string\n /** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */\n seedPromptHint?: string\n attachToObjectId?: string\n attachName?: string\n /**\n * Optional aspect ratio override. Defaults to 1:1 server-side. One of the\n * 5-value `ObjectAspectRatio` union (1:1 / 3:4 / 16:9 / 9:16 / 4:3) —\n * objects have their own enum (with 4:3 added) vs the character set.\n */\n aspectRatio?: ObjectAspectRatio\n /**\n * Optional clip duration in seconds. Validated server-side against the chosen\n * provider's allowed durations (e.g. kling 5/10, wan-i2v 5/10/15); omitted →\n * the model's own default (no behavior change). Mirrors generate-video's\n * per-model i2v duration lever.\n */\n duration?: number\n}\n\nexport interface GenerateObjectMotionResult {\n jobId: string\n}\n\nexport interface ApproveObjectMainImageResult {\n sourceImageUrl: string\n /**\n * LLM-authored caption. `null` when the LLM caption sub-failed — the wire\n * sends `\"\"`, normalized to `null` here so consumers see the same\n * `string | null` semantics as characters. The main image is still set; call\n * `recaption()` to retry.\n */\n canonicalDescription: string | null\n}\n\nexport interface RecaptionObjectResult {\n canonicalDescription: string\n}\n\nexport class ObjectsResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * List the caller's objects. By default returns active objects only;\n * pass `archived: true` to fetch soft-deleted rows for an \"archive\" view.\n * Optional `projectId` scopes the result to a single project; `q` keeps the\n * objects whose name or description contains it.\n */\n list(params: ListObjectsParams = {}): Promise<{ objects: Object[]; nextCursor?: string | null }> {\n const query: Record<string, string | undefined> = {}\n if (params.archived) query.archived = \"true\"\n if (params.projectId) query.projectId = params.projectId\n if (params.q) query.q = params.q\n if (params.limit !== undefined) query.limit = String(params.limit)\n if (params.cursor) query.cursor = params.cursor\n return this.client.request(\"GET\", \"/v1/objects\", { query })\n }\n\n /**\n * Convenience wrapper for `list({ archived: true })`. Returns soft-deleted\n * rows so callers can drive a UI \"Archived\" tab without re-encoding the\n * query param.\n *\n * `archived` is omitted from the param type — it's always set to `true` here.\n */\n listArchived(params: Omit<ListObjectsParams, \"archived\"> = {}): Promise<{ objects: Object[]; nextCursor?: string | null }> {\n return this.list({ ...params, archived: true })\n }\n\n /**\n * Fetch a single object including in-flight asset job state. Soft-deleted\n * (archived) rows are NOT returned by id — the route enforces\n * `deleted_at IS NULL` so archived objects 404 (uniform Pass 10 F-90b\n * \"not_found\" — does not leak the deleted vs non-existent distinction).\n */\n async get(id: string): Promise<ObjectDetail> {\n const res = await this.client.request<ObjectDetail>(\n \"GET\",\n `/v1/objects/${encodeURIComponent(id)}`,\n )\n // Normalize the wire `\"\"` caption (DB null / LLM sub-failure) → null so\n // consumers see the same `string | null` semantics as characters. New\n // object — never mutate the response.\n return { ...res, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Create a new object. `name` + `nodeId` are required — the route 400s\n * otherwise. Returns the new row's id.\n *\n * Note: the underlying route is the same `POST /v1/objects` upsert that\n * powers `update()`. This convenience wrapper enforces the INSERT-required\n * fields at the type level and never sends an `id`.\n */\n create(data: CreateObjectInput): Promise<{ id: string }> {\n return this.client.request(\"POST\", \"/v1/objects\", { body: data })\n }\n\n /**\n * Update an object. Only the fields you pass are written — undefined keys\n * are NOT touched on the row. Worker-owned asset buckets are intentionally\n * not exposed on this surface (see `UpdateObjectInput` for the rationale).\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's\n * `updated_at` still matches; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh `updatedAt`. The SDK\n * surfaces that as a generic `NodaroError` with the same code (per Phase\n * E1 calibration finding — error centralization in `throwApiError`).\n */\n update(id: string, data: UpdateObjectInput): Promise<UpdateObjectResult> {\n return this.client.request(\"POST\", \"/v1/objects\", {\n body: { id, ...data },\n })\n }\n\n /**\n * Soft-delete (archive) an object. The row is hidden from `list()` by\n * default but recoverable via `restore(id)` or visible under\n * `listArchived()`. Idempotent — repeating a delete on an already-archived\n * row is a no-op.\n */\n delete(id: string): Promise<{ success: true; archived: true }> {\n return this.client.request(\"DELETE\", `/v1/objects/${encodeURIComponent(id)}`)\n }\n\n /**\n * Hard-delete (permanent) an object — the row + every R2 asset it\n * references. Archived rows ONLY: active objects return 400 `not_archived`.\n * Call `delete()` first to archive, then `permanentDelete()` to destroy.\n *\n * Mirrors the `app_runs` permanent-delete pattern (archive-first) so a\n * stray SDK / curl caller cannot bypass the studio's archive-first UI\n * flow.\n */\n permanentDelete(id: string): Promise<{ success: true; permanent: true }> {\n return this.client.request(\"DELETE\", `/v1/objects/${encodeURIComponent(id)}`, {\n query: { permanent: \"true\" },\n })\n }\n\n /**\n * Un-archive an object. If the original name now collides (case-\n * insensitive) with an active row, the server auto-suffixes \"(restored)\"\n * and returns the effective name.\n */\n restore(id: string): Promise<{ id: string; name: string }> {\n return this.client.request(\"POST\", `/v1/objects/${encodeURIComponent(id)}/restore`)\n }\n\n /**\n * Fire `POST /v1/generate-object` to produce one or more candidate main\n * images. With `count > 1`, all jobs are reserved up-front before any\n * is enqueued — mid-batch failures roll back atomically.\n *\n * When `attachToObjectId` is set AND `count === 1`, the worker writes\n * the result directly to the row's `source_image_url`; otherwise you must\n * call `approveMainImage()` after picking a candidate.\n */\n async generate(data: GenerateObjectInput): Promise<GenerateObjectResult> {\n const res = await this.client.request<{ jobId?: string; jobIds?: string[] }>(\n \"POST\", \"/v1/generate-object\", { body: data },\n )\n // Tolerate the legacy `{ jobId }`-only shape (older server): synthesize jobIds.\n const jobIds = res.jobIds ?? (res.jobId ? [res.jobId] : [])\n return res.jobId ? { jobIds, jobId: res.jobId } : { jobIds }\n }\n\n /**\n * Fire `POST /v1/generate-object-asset` to produce a single variant.\n * When the studio path is set (`attachToObjectId` + `attachToColumn` +\n * `attachName`), the worker appends `{ name: attachName, url: <result> }`\n * to the named JSONB array column on completion.\n *\n * Note: `attachToColumn` is REQUIRED for `assetType === \"custom\"` — the\n * worker can't infer the bucket from the asset type. For canonical asset\n * types (`angles` / `materials` / `variations` / `motion`), the column is\n * derived automatically by the route.\n */\n generateAsset(\n data: GenerateObjectAssetInput,\n ): Promise<GenerateObjectAssetResult> {\n return this.client.request(\"POST\", \"/v1/generate-object-asset\", { body: data })\n }\n\n /**\n * Fire `POST /v1/generate-object-motion` to animate the object's main\n * image into a motion clip. Image-to-video, single clip per call; the\n * attach column is hardcoded to `motion_clips` server-side (objects have a\n * single motion bucket so the caller doesn't supply `attachToColumn`).\n * When the studio path is set (`attachToObjectId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the row's\n * `motion_clips` column on completion.\n *\n * Defaults: `provider` → `\"kling-turbo\"`, `aspectRatio` → `\"1:1\"` (set\n * server-side via `resolveObjectAspectRatio({ assetType: \"motion\" })`).\n */\n generateMotion(\n data: GenerateObjectMotionInput,\n ): Promise<GenerateObjectMotionResult> {\n return this.client.request(\"POST\", \"/v1/generate-object-motion\", { body: data })\n }\n\n /**\n * Approve a completed `generate-object` job as the object's main image.\n * Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)\n * inline. Returns the new main-image URL plus the caption.\n *\n * Caption-failure semantics: the route still sends `\"\"` on LLM sub-failure,\n * but the SDK normalizes `\"\"` → `null` here so `canonicalDescription` carries\n * the same `string | null` semantics as characters. The main image is still\n * set; call `recaption()` to retry.\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to gate the update on\n * the row's current `updated_at`; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh token.\n */\n async approveMainImage(\n id: string,\n candidateJobId: string,\n expectedUpdatedAt?: string,\n ): Promise<ApproveObjectMainImageResult> {\n const res = await this.client.request<{ sourceImageUrl: string; canonicalDescription: string | null }>(\n \"POST\",\n `/v1/objects/${encodeURIComponent(id)}/approve-main-image`,\n { body: { candidateJobId, expectedUpdatedAt } },\n )\n // Normalize the wire `\"\"` (LLM sub-failure) → null; build a new object\n // rather than mutating the response.\n return { sourceImageUrl: res.sourceImageUrl, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Re-fire the LLM caption against the object's current main image. 502s\n * on LLM failure (unlike `approveMainImage` which preserves the side-effect\n * and returns \"\"); returns 400 `main_image_required` if no main image is\n * set yet.\n *\n * The route is a pure idempotent retry — it does NOT accept an\n * `expectedUpdatedAt` token (per Phase E1 calibration finding: backend\n * route is idempotent retry, not gated on optimistic-concurrency).\n */\n recaption(id: string): Promise<RecaptionObjectResult> {\n return this.client.request(\n \"POST\",\n `/v1/objects/${encodeURIComponent(id)}/llm-caption`,\n )\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/creatures.ts"],"names":[],"mappings":"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","file":"chunk-CK3AMMSC.js","sourcesContent":["import type { ObjectAspectRatio, CreatureAttachColumn } from \"@nodaro/shared/entities\"\nimport type { TtsProvider } from \"@nodaro/shared/models\"\nexport type { CreatureAttachColumn, ObjectAspectRatio as CreatureAspectRatio } from \"@nodaro/shared/entities\"\nexport { CREATURE_ATTACH_COLUMNS, OBJECT_ASPECT_OPTIONS as CREATURE_ASPECT_OPTIONS, OBJECT_ASPECT_DEFAULTS as CREATURE_ASPECT_DEFAULTS } from \"@nodaro/shared/entities\"\nimport type { NodaroClientCore } from \"../core.js\"\nimport type { GenerationSessionParams } from \"./nodes.js\"\n\n/**\n * Creature voice — IDENTICAL shape + semantics to `Character[\"voice\"]` (the\n * \"talking creature\" stack reuses the character voice plumbing verbatim).\n * Render speech via `client.nodes.run(\"text-to-speech\", { text, voice:\n * voiceId, provider: ttsProvider, voiceType })`, then feed the audio + the\n * creature's `sourceImageUrl` to `client.nodes.run(\"lip-sync\", …)` (or\n * speech-to-video) for a talking-creature clip.\n */\nexport interface CreatureVoice {\n voiceId: string\n voiceName: string\n traits: string\n voiceType?: \"premade\" | \"library\" | \"custom\"\n /** Playable preview sample (client-played only). */\n previewUrl?: string\n /** Recommended TTS provider — send as `provider` on text-to-speech. */\n ttsProvider?: TtsProvider\n}\n\n/**\n * Re-export the shared `CreatureAttachColumn` union + its runtime tuple so SDK\n * consumers don't have to add `@nodaro/shared` as a second dependency just to\n * typecheck the `attachToColumn` field. Single source of truth lives in\n * `@nodaro/shared/entity-prompts` (`CREATURE_ATTACH_COLUMNS`).\n *\n * `CreatureAspectRatio` is re-exported as an alias of the shared\n * `ObjectAspectRatio` — `generateMotion`'s `aspectRatio` field is the 5-value\n * object enum (1:1 / 3:4 / 16:9 / 9:16 / 4:3). The creature motion route\n * deliberately REUSES `OBJECT_ASPECT_OPTIONS` server-side (a creature reference\n * clip is centered product-showcase framing, not cinematic 16:9), so the SDK\n * surfaces the same enum under a creature-friendly name. The runtime tuple is\n * re-exported as `CREATURE_ASPECT_OPTIONS` / `CREATURE_ASPECT_DEFAULTS` aliases.\n */\n\n/**\n * Creature asset-type enum — the kinds of variant a user can generate off a\n * creature's anchor main image. Mirrors the literal accepted by\n * `POST /v1/generate-creature-asset` (`backend/src/routes/generate-creature-asset.ts`).\n *\n * Delta vs `ObjectAssetType`: object's `materials` becomes `poses` (a creature\n * has poses, not materials), and there is NO `motion` value — creature motion\n * variants flow through the dedicated `/v1/generate-creature-motion` endpoint\n * (worker-side a different BullMQ job type). `custom` is the free-form bucket;\n * callers must supply `attachToColumn` explicitly since the worker can't infer\n * the destination from the asset type.\n *\n * The shared `@nodaro/shared` package does NOT export a `CREATURE_ASSET_TYPES`\n * tuple (the route validates an inline Zod enum), so the SDK defines its own\n * single-source-of-truth tuple here.\n */\nexport const CREATURE_ASSET_TYPES = [\"angles\", \"poses\", \"variations\", \"custom\"] as const\nexport type CreatureAssetType = (typeof CREATURE_ASSET_TYPES)[number]\n\n/**\n * Reference-photo kind discriminator — the mood-board roles a user can attach\n * to a creature. Mirrors the `reference_photos.kind` field accepted by\n * `backend/src/routes/creatures.ts` (the route accepts open strings; this SDK\n * type narrows to the 6 canonical roles surfaced by the Studio). `other` is\n * the free-form bucket.\n */\nexport type CreatureReferencePhotoKind =\n | \"front\"\n | \"side\"\n | \"detail\"\n | \"context\"\n | \"moodBoard\"\n | \"other\"\n\nexport interface CreatureReferencePhoto {\n url: string\n kind: CreatureReferencePhotoKind\n}\n\n/**\n * A creature record returned by Nodaro's REST API. Mirrors the camelCase\n * shape produced by `backend/src/routes/creatures.ts::toCamel()`.\n *\n * Asset buckets (`angles`, `poses`, `variations`, `motionClips`) are\n * independent JSONB arrays keyed by a human-readable variant name (e.g.\n * `\"front\"`, `\"walking\"`, `\"scarred\"`). Each entry's `url` points at an\n * R2-hosted asset.\n *\n * Creature delta vs object:\n * - `species` — free-text creature type (e.g. `\"dragon\"`, `\"wolf\"`). This is\n * the subject of the establishing-shot prompt and the primary creature\n * differentiator (object has no equivalent).\n * - `poses` (where object has `materials`) — the pose-variant asset bucket.\n * - `category` is free-text (NOT object's fixed 10-value enum) — a creature\n * can be any animal/type.\n *\n * Identity-foundation fields:\n * - `referencePhotos` — caller-supplied mood-board refs (cap 20).\n * - `canonicalDescription` — ~80–120-word LLM-authored visual caption,\n * populated by `approveMainImage()` / `recaption()`. The wire still sends\n * `\"\"` on caption sub-failure, but `get()` normalizes `\"\"` → `null` so\n * consumers see the same `string | null` semantics as characters.\n * - `styleLock` — whether asset gens should anchor to the canonical style\n * captured at approval time. Defaults to `true` on new rows.\n */\nexport interface Creature {\n id: string\n userId: string\n nodeId: string\n projectId: string | null\n name: string\n description: string | null\n /** Free-text creature type/species (e.g. \"dragon\", \"wolf\") — the creature\n * delta vs object. `null` when unset. */\n species: string | null\n category: string | null\n style: string | null\n sourceImageUrl: string | null\n /** MODEL_CATALOG image-model id the main image was generated with (or `null`).\n * Set on create + editable via the update route. */\n imageProvider: string | null\n angles: Array<{ name: string; url: string }>\n poses: Array<{ name: string; url: string }>\n variations: Array<{ name: string; url: string }>\n motionClips: Array<{ name: string; url: string }>\n /** Named Creature Boards — dense reference sheets, one per variant/mood\n * (the `generate-image/creature-board` factory preset rendered from the\n * creature's images). A first-class bucket: community publish snapshots it\n * and clone hands the consumer their own copy to extend. Defaults to `[]`.\n * `type` marks an image-collage `\"identity\"` sheet vs a plain `\"looks\"`\n * board; `sourceImages` are the R2 URLs it was collaged from. Both optional\n * + backward-compatible (legacy boards have neither). */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }> | null\n /** The creature's voice (the \"talking creature\" stack) — same shape and\n * flow as `Character[\"voice\"]`. `null` when no voice is selected. */\n voice?: CreatureVoice | null\n referencePhotos: CreatureReferencePhoto[]\n /** `null` when no caption is set (or the LLM caption sub-failed) — the wire\n * sends `\"\"`, normalized to `null` in `get()` to match character semantics. */\n canonicalDescription: string | null\n styleLock: boolean\n /** The user's chosen DEFAULT asset take per variant (Studio version history).\n * OPAQUE map: key `\"<bucket>:<variant>\"` (e.g. `\"angles:front\"`) → the chosen\n * asset URL (one already present in that bucket). Stored verbatim — keys are\n * NOT normalized; soft-capped server-side at 200 keys / 2048-char values\n * (overflow dropped silently). Defaults to `{}`. */\n selectedAssetByVariant?: Record<string, string> | null\n deletedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * GET /v1/creatures/:id may append a `pendingJobs` bucket the studio uses to\n * rehydrate spinners after a reload. Optional on the SDK surface — it doesn't\n * appear on `list()` rows.\n */\nexport interface CreatureDetail extends Creature {\n pendingJobs?: Array<{\n jobId: string\n assetType: string\n name: string\n status: string\n }>\n}\n\n/**\n * Body for `client.creatures.create()`. Mirrors the INSERT branch of\n * `upsertCreatureBody` in `backend/src/routes/creatures.ts`. `name` + `nodeId`\n * are required on create.\n */\nexport interface CreateCreatureInput {\n nodeId: string\n name: string\n description?: string\n /** Free-text creature type/species (the creature delta vs object). */\n species?: string\n /** Free-text category (NOT object's fixed enum — a creature can be anything). */\n category?: string\n style?: string\n workflowId?: string\n projectId?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: CreatureReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map.\n * Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n /** Initial voice selection (see `CreatureVoice`). Omit for a voiceless creature. */\n voice?: CreatureVoice | null\n}\n\n/**\n * Body for `client.creatures.update()`. Mirrors the UPDATE branch of\n * `upsertCreatureBody` in `backend/src/routes/creatures.ts`.\n *\n * Worker-owned asset buckets (`angles` / `poses` / `variations` /\n * `motionClips`) are deliberately omitted — the route drops them on UPDATE\n * so a Studio auto-save with a stale snapshot cannot clobber the worker's\n * atomic `append_creature_asset()` writes.\n *\n * `expectedUpdatedAt` is the optimistic-concurrency token: when present, the\n * UPDATE only succeeds if the row's `updated_at` still matches; on mismatch\n * the route returns 409 so the studio can re-fetch + merge.\n */\nexport interface UpdateCreatureInput {\n name?: string\n description?: string\n /** Free-text creature type/species (the creature delta vs object). */\n species?: string\n category?: string\n style?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: CreatureReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map\n * (omit to leave untouched). Keys stored verbatim; soft-capped server-side\n * at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n /** Named Creature Boards (see `Creature.boards`) — whole-array replace,\n * USER-owned (unlike the worker-owned buckets it flows through UPDATE).\n * Server caps: 24 boards, 200-char names, 30 sourceImages per board. */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }>\n /** Voice selection (see `CreatureVoice`) — whole-object replace; pass\n * `null` to clear the voice, omit to leave untouched. */\n voice?: CreatureVoice | null\n expectedUpdatedAt?: string\n}\n\nexport interface UpdateCreatureResult {\n id: string\n updatedAt: string\n}\n\n/**\n * Combined create + update body (parameter for both branches). Exported for\n * callers that want to drive a single `upsert` flow without picking between\n * `Create*` and `Update*`. Mirrors the `upsertCreatureBody` Zod schema in\n * `backend/src/routes/creatures.ts`. `nodeId` + `name` are required on INSERT;\n * `id` flips the route into UPDATE mode.\n */\nexport interface UpsertCreatureInput extends CreateCreatureInput {\n id?: string\n expectedUpdatedAt?: string\n}\n\nexport type UpsertCreatureResult = { id: string } | UpdateCreatureResult\n\nexport interface ListCreaturesParams {\n /** When true, return archived creatures instead of active ones. */\n archived?: boolean\n /** Optional project filter — server-scoped to the caller's user. */\n projectId?: string\n /**\n * Search words: only the creatures whose name or description contains\n * every word (split on spaces, the first 5), in any order, ignoring case.\n * Each word is matched as written (`%`, `_` and `*` are not wildcards;\n * punctuation is kept); blank means no search; up to 200 characters.\n * Combines with the other filters and with `cursor` paging.\n */\n q?: string\n /**\n * Max rows to return (cap 500). OPT-IN: omitted, the server returns the\n * full legacy listing with no `nextCursor`; supplied, the response is one\n * page plus `nextCursor` — keep passing it back until it is `null`.\n */\n limit?: number\n /** Opaque cursor from a prior page's `nextCursor` (fetches the next page). */\n cursor?: string\n}\n\n/**\n * Input for `client.creatures.generate()` — fires the\n * `POST /v1/generate-creature` route. Produces 1–10 candidate\n * main images; each lands as one `jobs` row in `pending` state and is then\n * enqueued for the worker.\n *\n * When `attachToCreatureId` is set AND `count === 1`, the worker writes the\n * resulting URL directly to `creatures.source_image_url` on completion —\n * caller doesn't need a separate `approveMainImage` call. Multi-candidate\n * batches MUST go through explicit approval so the user picks the winner.\n *\n * `seedPromptHint` flows the parameter-picker's prompt fragment through to the\n * worker so a catalog selection (e.g. \"armored frost dragon\") gets appended to\n * the generated prompt context.\n */\nexport interface GenerateCreatureInput extends GenerationSessionParams {\n name: string\n description?: string\n userPrompt?: string\n /** Free-text creature type/species (the creature delta vs object). */\n species?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n sourceImageUrl?: string\n provider?: string\n /** Number of candidate images to generate (1–10; server-validated). */\n count?: number\n /** Auto-attach the result to this creature row (single-candidate only). */\n attachToCreatureId?: string\n /** Parameter-picker prompt-fragment pass-through. */\n seedPromptHint?: string\n /** Optional name to set on the attached row alongside the main image. */\n attachName?: string\n /** Optimistic-concurrency token for the single-candidate auto-attach path. */\n expectedUpdatedAt?: string\n}\n\n/**\n * `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,\n * matching characters). `jobId` is a deprecated back-compat alias populated only\n * on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)\n */\nexport interface GenerateCreatureResult {\n jobIds: string[]\n /** @deprecated count===1 back-compat alias — use `jobIds`. */\n jobId?: string\n}\n\n/**\n * Input for `client.creatures.generateAsset()` — fires the\n * `POST /v1/generate-creature-asset` route. Produces a single\n * angles / poses / variations / custom variant.\n *\n * When all three studio-path fields are set (`attachToCreatureId` +\n * `attachToColumn` + `attachName`), the worker appends\n * `{ name: attachName, url: <result> }` to the named JSONB array column on\n * the user's creature row on completion. `attachToColumn` is REQUIRED for\n * `assetType === \"custom\"` — the worker can't infer the bucket from the\n * asset type.\n */\nexport interface GenerateCreatureAssetInput extends GenerationSessionParams {\n assetType: CreatureAssetType\n variant: string\n name: string\n description?: string\n userPrompt?: string\n category?: string\n style?: string\n sourceImageUrl?: string\n provider?: string\n attachToCreatureId?: string\n attachToColumn?: CreatureAttachColumn\n attachName?: string\n /** Parameter-picker prompt-fragment pass-through. */\n seedPromptHint?: string\n}\n\nexport interface GenerateCreatureAssetResult {\n jobId: string\n}\n\n/**\n * Input for `client.creatures.generateMotion()` — fires the\n * `POST /v1/generate-creature-motion` route. Produces a single motion clip\n * (idle, prowl, attack, etc.) animated FROM a static creature-shot image.\n *\n * Mirrors `client.objects.generateMotion()` — the creature motion route reuses\n * the entity-agnostic object motion helpers server-side. The route hardcodes\n * the attach column to `motion_clips` — callers supply `attachToCreatureId` +\n * `attachName` only.\n *\n * `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the\n * route has no fallback (the studio path supplies the canonical creature-shot\n * URL explicitly).\n *\n * Defaults vs location:\n * - `provider` defaults to `\"kling-turbo\"` (not location's `\"kling\"`)\n * - `aspectRatio` defaults to `\"1:1\"` server-side via\n * `resolveObjectAspectRatio({ assetType: \"motion\" })` — creatures use\n * centered reference framing, not cinematic establishing shots.\n *\n * When the studio path is set (`attachToCreatureId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the creature row's\n * `motion_clips` JSONB column on completion.\n */\nexport interface GenerateCreatureMotionInput extends GenerationSessionParams {\n motionPrompt: string\n sourceImageUrl: string\n provider?: string\n name: string\n /** Source clip URL — when set, worker routes to video-to-video refine. */\n refineFromVideoUrl?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n canonicalDescription?: string\n /** Parameter-picker prompt-fragment pass-through. */\n seedPromptHint?: string\n attachToCreatureId?: string\n attachName?: string\n /**\n * Optional aspect ratio override. Defaults to 1:1 server-side. One of the\n * 5-value `CreatureAspectRatio` union (1:1 / 3:4 / 16:9 / 9:16 / 4:3) — the\n * creature route reuses the object aspect enum.\n */\n aspectRatio?: ObjectAspectRatio\n /**\n * Optional clip duration in seconds. Validated server-side against the chosen\n * provider's allowed durations (e.g. kling 5/10, wan-i2v 5/10/15); omitted →\n * the model's own default (no behavior change). Mirrors generate-video's\n * per-model i2v duration lever.\n */\n duration?: number\n}\n\nexport interface GenerateCreatureMotionResult {\n jobId: string\n}\n\nexport interface ApproveCreatureMainImageResult {\n sourceImageUrl: string\n /**\n * LLM-authored caption. `null` when the LLM caption sub-failed — the wire\n * sends `\"\"`, normalized to `null` here so consumers see the same\n * `string | null` semantics as characters. The main image is still set; call\n * `recaption()` to retry.\n */\n canonicalDescription: string | null\n}\n\nexport interface RecaptionCreatureResult {\n canonicalDescription: string\n}\n\nexport class CreaturesResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * List the caller's creatures. By default returns active creatures only;\n * pass `archived: true` to fetch soft-deleted rows for an \"archive\" view.\n * Optional `projectId` scopes the result to a single project; `q` keeps the\n * creatures whose name or description contains it.\n */\n list(params: ListCreaturesParams = {}): Promise<{ creatures: Creature[]; nextCursor?: string | null }> {\n const query: Record<string, string | undefined> = {}\n if (params.archived) query.archived = \"true\"\n if (params.projectId) query.projectId = params.projectId\n if (params.q) query.q = params.q\n if (params.limit !== undefined) query.limit = String(params.limit)\n if (params.cursor) query.cursor = params.cursor\n return this.client.request(\"GET\", \"/v1/creatures\", { query })\n }\n\n /**\n * Convenience wrapper for `list({ archived: true })`. Returns soft-deleted\n * rows so callers can drive a UI \"Archived\" tab without re-encoding the\n * query param.\n *\n * `archived` is omitted from the param type — it's always set to `true` here.\n */\n listArchived(params: Omit<ListCreaturesParams, \"archived\"> = {}): Promise<{ creatures: Creature[]; nextCursor?: string | null }> {\n return this.list({ ...params, archived: true })\n }\n\n /**\n * Fetch a single creature including in-flight asset job state. Soft-deleted\n * (archived) rows are NOT returned by id — the route enforces\n * `deleted_at IS NULL` so archived creatures 404 (uniform \"not_found\" — does\n * not leak the deleted vs non-existent distinction).\n */\n async get(id: string): Promise<CreatureDetail> {\n const res = await this.client.request<CreatureDetail>(\n \"GET\",\n `/v1/creatures/${encodeURIComponent(id)}`,\n )\n // Normalize the wire `\"\"` caption (DB null / LLM sub-failure) → null so\n // consumers see the same `string | null` semantics as characters. New\n // object — never mutate the response.\n return { ...res, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Create a new creature. `name` + `nodeId` are required — the route 400s\n * otherwise. Returns the new row's id.\n *\n * Note: the underlying route is the same `POST /v1/creatures` upsert that\n * powers `update()`. This convenience wrapper enforces the INSERT-required\n * fields at the type level and never sends an `id`.\n */\n create(data: CreateCreatureInput): Promise<{ id: string }> {\n return this.client.request(\"POST\", \"/v1/creatures\", { body: data })\n }\n\n /**\n * Update a creature. Only the fields you pass are written — undefined keys\n * are NOT touched on the row. Worker-owned asset buckets are intentionally\n * not exposed on this surface (see `UpdateCreatureInput` for the rationale).\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's\n * `updated_at` still matches; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh `updatedAt`. The SDK\n * surfaces that as a generic `NodaroError` with the same code.\n */\n update(id: string, data: UpdateCreatureInput): Promise<UpdateCreatureResult> {\n return this.client.request(\"POST\", \"/v1/creatures\", {\n body: { id, ...data },\n })\n }\n\n /**\n * Soft-delete (archive) a creature. The row is hidden from `list()` by\n * default but recoverable via `restore(id)` or visible under\n * `listArchived()`. Idempotent — repeating a delete on an already-archived\n * row is a no-op.\n */\n delete(id: string): Promise<{ success: true; archived: true }> {\n return this.client.request(\"DELETE\", `/v1/creatures/${encodeURIComponent(id)}`)\n }\n\n /**\n * Hard-delete (permanent) a creature — the row + every R2 asset it\n * references. Archived rows ONLY: active creatures return 400 `not_archived`.\n * Call `delete()` first to archive, then `permanentDelete()` to destroy.\n *\n * Mirrors the `app_runs` permanent-delete pattern (archive-first) so a\n * stray SDK / curl caller cannot bypass the studio's archive-first UI\n * flow.\n */\n permanentDelete(id: string): Promise<{ success: true; permanent: true }> {\n return this.client.request(\"DELETE\", `/v1/creatures/${encodeURIComponent(id)}`, {\n query: { permanent: \"true\" },\n })\n }\n\n /**\n * Un-archive a creature. If the original name now collides (case-\n * insensitive) with an active row, the server auto-suffixes \"(restored)\"\n * and returns the effective name.\n */\n restore(id: string): Promise<{ id: string; name: string }> {\n return this.client.request(\"POST\", `/v1/creatures/${encodeURIComponent(id)}/restore`)\n }\n\n /**\n * Fire `POST /v1/generate-creature` to produce one or more candidate main\n * images. With `count > 1`, all jobs are reserved up-front before any\n * is enqueued — mid-batch failures roll back atomically.\n *\n * When `attachToCreatureId` is set AND `count === 1`, the worker writes\n * the result directly to the row's `source_image_url`; otherwise you must\n * call `approveMainImage()` after picking a candidate.\n */\n async generate(data: GenerateCreatureInput): Promise<GenerateCreatureResult> {\n const res = await this.client.request<{ jobId?: string; jobIds?: string[] }>(\n \"POST\", \"/v1/generate-creature\", { body: data },\n )\n // Tolerate the legacy `{ jobId }`-only shape (older server): synthesize jobIds.\n const jobIds = res.jobIds ?? (res.jobId ? [res.jobId] : [])\n return res.jobId ? { jobIds, jobId: res.jobId } : { jobIds }\n }\n\n /**\n * Fire `POST /v1/generate-creature-asset` to produce a single variant.\n * When the studio path is set (`attachToCreatureId` + `attachToColumn` +\n * `attachName`), the worker appends `{ name: attachName, url: <result> }`\n * to the named JSONB array column on completion.\n *\n * Note: `attachToColumn` is REQUIRED for `assetType === \"custom\"` — the\n * worker can't infer the bucket from the asset type. For canonical asset\n * types (`angles` / `poses` / `variations`), the column is derived\n * automatically by the route.\n */\n generateAsset(\n data: GenerateCreatureAssetInput,\n ): Promise<GenerateCreatureAssetResult> {\n return this.client.request(\"POST\", \"/v1/generate-creature-asset\", { body: data })\n }\n\n /**\n * Fire `POST /v1/generate-creature-motion` to animate the creature's main\n * image into a motion clip. Image-to-video, single clip per call; the\n * attach column is hardcoded to `motion_clips` server-side (creatures have a\n * single motion bucket so the caller doesn't supply `attachToColumn`).\n * When the studio path is set (`attachToCreatureId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the row's\n * `motion_clips` column on completion.\n *\n * Defaults: `provider` → `\"kling-turbo\"`, `aspectRatio` → `\"1:1\"` (set\n * server-side via `resolveObjectAspectRatio({ assetType: \"motion\" })`).\n */\n generateMotion(\n data: GenerateCreatureMotionInput,\n ): Promise<GenerateCreatureMotionResult> {\n return this.client.request(\"POST\", \"/v1/generate-creature-motion\", { body: data })\n }\n\n /**\n * Approve a completed `generate-creature` job as the creature's main image.\n * Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)\n * inline. Returns the new main-image URL plus the caption.\n *\n * Caption-failure semantics: the route still sends `\"\"` on LLM sub-failure,\n * but the SDK normalizes `\"\"` → `null` here so `canonicalDescription` carries\n * the same `string | null` semantics as characters. The main image is still\n * set; call `recaption()` to retry.\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to gate the update on\n * the row's current `updated_at`; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh token.\n */\n async approveMainImage(\n id: string,\n candidateJobId: string,\n expectedUpdatedAt?: string,\n ): Promise<ApproveCreatureMainImageResult> {\n const res = await this.client.request<{ sourceImageUrl: string; canonicalDescription: string | null }>(\n \"POST\",\n `/v1/creatures/${encodeURIComponent(id)}/approve-main-image`,\n { body: { candidateJobId, expectedUpdatedAt } },\n )\n // Normalize the wire `\"\"` (LLM sub-failure) → null; build a new object\n // rather than mutating the response.\n return { sourceImageUrl: res.sourceImageUrl, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Re-fire the LLM caption against the creature's current main image. 502s\n * on LLM failure (unlike `approveMainImage` which preserves the side-effect\n * and returns \"\"); returns 400 `main_image_required` if no main image is\n * set yet.\n *\n * The route is a pure idempotent retry — it does NOT accept an\n * `expectedUpdatedAt` token (backend route is idempotent retry, not gated on\n * optimistic-concurrency).\n */\n recaption(id: string): Promise<RecaptionCreatureResult> {\n return this.client.request(\n \"POST\",\n `/v1/creatures/${encodeURIComponent(id)}/llm-caption`,\n )\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/objects.ts"],"names":[],"mappings":"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","file":"chunk-CR5E2SF3.cjs","sourcesContent":["import type { ObjectAspectRatio, ObjectAssetType, ObjectAttachColumn } from \"@nodaro/shared/entities\"\nexport type { ObjectAssetType, ObjectAttachColumn, ObjectAspectRatio } from \"@nodaro/shared/entities\"\nexport { OBJECT_ASSET_TYPES, OBJECT_ATTACH_COLUMNS, OBJECT_ASPECT_OPTIONS, OBJECT_ASPECT_DEFAULTS } from \"@nodaro/shared/entities\"\nimport type { NodaroClientCore } from \"../core.js\"\nimport type { GenerationSessionParams } from \"./nodes.js\"\n\n/**\n * Re-export the shared `ObjectAssetType` / `ObjectAttachColumn` unions and\n * their runtime tuples so SDK consumers don't have to add `@nodaro/shared` as\n * a second dependency just to typecheck the `assetType` / `attachToColumn`\n * fields. Single source of truth lives in `@nodaro/shared/entity-prompts`.\n *\n * `ObjectAspectRatio` is re-exported alongside them — `generateMotion`'s\n * `aspectRatio` field is the 5-value object enum (1:1 / 3:4 / 16:9 / 9:16 /\n * 4:3) from `@nodaro/shared/object-aspect-defaults`. Distinct from\n * `CharacterAspectRatio` because objects support an extra 4:3 framing for\n * product-showcase shots.\n */\n\n/**\n * Reference-photo kind discriminator — the mood-board roles a user can attach\n * to an object. Mirrors the `reference_photos.kind` field accepted by\n * `backend/src/routes/objects.ts` (the route accepts open strings; this SDK\n * type narrows to the 6 canonical roles surfaced by the Studio). `other` is\n * the free-form bucket.\n */\nexport type ObjectReferencePhotoKind =\n | \"front\"\n | \"side\"\n | \"detail\"\n | \"context\"\n | \"moodBoard\"\n | \"other\"\n\nexport interface ObjectReferencePhoto {\n url: string\n kind: ObjectReferencePhotoKind\n}\n\n/**\n * An object record returned by Nodaro's REST API. Mirrors the camelCase\n * shape produced by `backend/src/routes/objects.ts::toCamel()`.\n *\n * Asset buckets (`angles`, `materials`, `variations`, `motionClips`) are\n * independent JSONB arrays keyed by a human-readable variant name (e.g.\n * `\"front\"`, `\"wood\"`, `\"weathered\"`). Each entry's `url` points at an\n * R2-hosted asset.\n *\n * Identity-foundation fields:\n * - `referencePhotos` — caller-supplied mood-board refs (cap 20). Objects\n * do NOT carry a `piiConsentAt` field (location Phase 2 #7 only).\n * - `canonicalDescription` — ~80–120-word LLM-authored visual caption,\n * populated by `approveMainImage()` / `recaption()`. The wire still sends\n * `\"\"` on caption sub-failure (the breaking wire change is deferred to a\n * major bump), but `get()` normalizes `\"\"` → `null` so consumers see the\n * same `string | null` semantics as characters.\n * - `styleLock` — whether asset gens should anchor to the canonical style\n * captured at approval time. Defaults to `true` on new rows.\n *\n * `Object` shadows the JS global, which TypeScript handles cleanly via\n * local-scope resolution. Consumers who need both can alias as\n * `import type { Object as NodaroObject } from \"@nodaro/sdk\"`.\n */\nexport interface Object {\n id: string\n userId: string\n nodeId: string\n projectId: string | null\n name: string\n description: string | null\n category: string | null\n style: string | null\n sourceImageUrl: string | null\n /** MODEL_CATALOG image-model id the main image was generated with (or `null`).\n * Set on create + editable via the update route. */\n imageProvider: string | null\n angles: Array<{ name: string; url: string }>\n materials: Array<{ name: string; url: string }>\n variations: Array<{ name: string; url: string }>\n motionClips: Array<{ name: string; url: string }>\n /** Named Product Boards — dense reference sheets, one per variant/colorway\n * (the `generate-image/product-board` factory preset rendered from the\n * object's images). A first-class bucket: community publish snapshots it\n * and clone hands the consumer their own copy to extend. Defaults to `[]`.\n * `type` marks an image-collage `\"identity\"` sheet vs a plain `\"looks\"`\n * board; `sourceImages` are the R2 URLs it was collaged from. Both optional\n * + backward-compatible (legacy boards have neither). */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }> | null\n referencePhotos: ObjectReferencePhoto[]\n /** `null` when no caption is set (or the LLM caption sub-failed) — the wire\n * sends `\"\"`, normalized to `null` in `get()` to match character semantics. */\n canonicalDescription: string | null\n styleLock: boolean\n /** The user's chosen DEFAULT asset take per variant (Studio version history).\n * OPAQUE map: key `\"<bucket>:<variant>\"` (e.g. `\"angles:front\"`) → the chosen\n * asset URL (one already present in that bucket). Stored verbatim — keys are\n * NOT normalized; soft-capped server-side at 200 keys / 2048-char values\n * (overflow dropped silently). Defaults to `{}`. */\n selectedAssetByVariant?: Record<string, string> | null\n deletedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * GET /v1/objects/:id may append a `pendingJobs` bucket the studio uses to\n * rehydrate spinners after a reload. Optional on the SDK surface — it doesn't\n * appear on `list()` rows.\n */\nexport interface ObjectDetail extends Object {\n pendingJobs?: Array<{\n jobId: string\n assetType: string\n name: string\n status: string\n }>\n}\n\n/**\n * Body for `client.objects.create()`. Mirrors the INSERT branch of\n * `upsertObjectBody` in `backend/src/routes/objects.ts`. `name` + `nodeId`\n * are required on create.\n */\nexport interface CreateObjectInput {\n nodeId: string\n name: string\n description?: string\n category?: ObjectCategory\n style?: string\n workflowId?: string\n projectId?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: ObjectReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map.\n * Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n}\n\n/**\n * The 10-value Object category enum. Mirrors the literal accepted by\n * `POST /v1/generate-object` and surfaced in the Object Studio category\n * picker. Distinct from location's geography-based set.\n */\nexport type ObjectCategory =\n | \"furniture\"\n | \"vehicle\"\n | \"weapon\"\n | \"food\"\n | \"clothing\"\n | \"electronics\"\n | \"nature\"\n | \"tool\"\n | \"animal\"\n | \"other\"\n\n/**\n * Body for `client.objects.update()`. Mirrors the UPDATE branch of\n * `upsertObjectBody` in `backend/src/routes/objects.ts`.\n *\n * Worker-owned asset buckets (`angles` / `materials` / `variations` /\n * `motionClips`) are deliberately omitted — the route drops them on UPDATE\n * so a Studio auto-save with a stale snapshot cannot clobber the worker's\n * atomic `append_object_asset()` writes.\n *\n * `expectedUpdatedAt` is the optimistic-concurrency token: when present, the\n * UPDATE only succeeds if the row's `updated_at` still matches; on mismatch\n * the route returns 409 so the studio can re-fetch + merge.\n */\nexport interface UpdateObjectInput {\n name?: string\n description?: string\n category?: ObjectCategory\n style?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n referencePhotos?: ObjectReferencePhoto[]\n canonicalDescription?: string\n styleLock?: boolean\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map\n * (omit to leave untouched). Keys stored verbatim; soft-capped server-side\n * at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n /** Named Product Boards (see `Object.boards`) — whole-array replace,\n * USER-owned (unlike the worker-owned buckets it flows through UPDATE).\n * Server caps: 24 boards, 200-char names, 30 sourceImages per board. */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }>\n expectedUpdatedAt?: string\n}\n\nexport interface UpdateObjectResult {\n id: string\n updatedAt: string\n}\n\n/**\n * Combined create + update body (parameter for both branches). Exported for\n * callers that want to drive a single `upsert` flow without picking between\n * `Create*` and `Update*`. Mirrors the `upsertObjectBody` Zod schema in\n * `backend/src/routes/objects.ts`. `nodeId` + `name` are required on INSERT;\n * `id` flips the route into UPDATE mode.\n */\nexport interface UpsertObjectInput extends CreateObjectInput {\n id?: string\n expectedUpdatedAt?: string\n}\n\nexport type UpsertObjectResult = { id: string } | UpdateObjectResult\n\nexport interface ListObjectsParams {\n /** When true, return archived objects instead of active ones. */\n archived?: boolean\n /** Optional project filter — server-scoped to the caller's user. */\n projectId?: string\n /**\n * Search words: only the objects whose name or description contains every\n * word (split on spaces, the first 5), in any order, ignoring case. Each\n * word is matched as written (`%`, `_` and `*` are not wildcards;\n * punctuation is kept); blank means no search; up to 200 characters.\n * Combines with the other filters and with `cursor` paging.\n */\n q?: string\n /**\n * Max rows to return (cap 500). OPT-IN: omitted, the server returns the\n * full legacy listing with no `nextCursor`; supplied, the response is one\n * page plus `nextCursor` — keep passing it back until it is `null`.\n */\n limit?: number\n /** Opaque cursor from a prior page's `nextCursor` (fetches the next page). */\n cursor?: string\n}\n\n/**\n * Input for `client.objects.generate()` — fires the\n * `POST /v1/generate-object` route. Produces 1–10 candidate\n * main images; each lands as one `jobs` row in `pending` state and is then\n * enqueued for the worker.\n *\n * When `attachToObjectId` is set AND `count === 1`, the worker writes the\n * resulting URL directly to `objects.source_image_url` on completion —\n * caller doesn't need a separate `approveMainImage` call. Multi-candidate\n * batches MUST go through explicit approval so the user picks the winner.\n *\n * `seedPromptHint` (Pass 7 F-77) flows the parameter-picker's prompt fragment\n * through to the worker so a catalog selection (e.g. \"antique brass lantern\")\n * gets appended to the generated prompt context.\n */\nexport interface GenerateObjectInput extends GenerationSessionParams {\n name: string\n description?: string\n userPrompt?: string\n category?: ObjectCategory\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n sourceImageUrl?: string\n provider?: string\n /** Number of candidate images to generate (1–10; server-validated). */\n count?: number\n /** Auto-attach the result to this object row (single-candidate only). */\n attachToObjectId?: string\n /** Parameter-picker prompt-fragment pass-through. */\n seedPromptHint?: string\n /** Optional name to set on the attached row alongside the main image. */\n attachName?: string\n /** Optimistic-concurrency token for the single-candidate auto-attach path. */\n expectedUpdatedAt?: string\n}\n\n/**\n * `generate()` response — `jobIds` is ALWAYS present (the harmonized contract,\n * matching characters). `jobId` is a deprecated back-compat alias populated only\n * on `count === 1`; prefer `jobIds`. (Will be removed on the next major.)\n */\nexport interface GenerateObjectResult {\n jobIds: string[]\n /** @deprecated count===1 back-compat alias — use `jobIds`. */\n jobId?: string\n}\n\n/**\n * Input for `client.objects.generateAsset()` — fires the\n * `POST /v1/generate-object-asset` route. Produces a single\n * angles / materials / variations / custom variant.\n *\n * When all three studio-path fields are set (`attachToObjectId` +\n * `attachToColumn` + `attachName`), the worker appends\n * `{ name: attachName, url: <result> }` to the named JSONB array column on\n * the user's object row on completion. `attachToColumn` is REQUIRED for\n * `assetType === \"custom\"` — the worker can't infer the bucket from the\n * asset type.\n */\nexport interface GenerateObjectAssetInput extends GenerationSessionParams {\n assetType: ObjectAssetType\n variant: string\n name: string\n description?: string\n userPrompt?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n sourceImageUrl?: string\n provider?: string\n attachToObjectId?: string\n attachToColumn?: ObjectAttachColumn\n attachName?: string\n /** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */\n seedPromptHint?: string\n}\n\nexport interface GenerateObjectAssetResult {\n jobId: string\n}\n\n/**\n * Input for `client.objects.generateMotion()` — fires the\n * `POST /v1/generate-object-motion` route. Produces a single motion clip\n * (rotation, orbit, hover, drift, etc.) animated FROM a static product-shot\n * image.\n *\n * Mirrors `client.locations.generateMotion()` minus the location-specific\n * atmospheric fields. The route hardcodes the attach column to `motion_clips`\n * — callers supply `attachToObjectId` + `attachName` only.\n *\n * `sourceImageUrl` is REQUIRED — image-to-video needs a source frame and the\n * route has no fallback (no `source_image_url` column to pull from on the\n * objects row at this point in the flow; the studio path supplies the\n * canonical product-shot URL explicitly).\n *\n * Object-specific defaults vs location:\n * - `provider` defaults to `\"kling-turbo\"` (not location's `\"kling\"`)\n * - `aspectRatio` defaults to `\"1:1\"` server-side via\n * `resolveObjectAspectRatio({ assetType: \"motion\" })` — objects are\n * product-showcase framing, not cinematic establishing shots.\n *\n * When the studio path is set (`attachToObjectId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the object row's\n * `motion_clips` JSONB column on completion.\n */\nexport interface GenerateObjectMotionInput extends GenerationSessionParams {\n motionPrompt: string\n sourceImageUrl: string\n provider?: string\n name: string\n /** Source clip URL — when set, worker routes to video-to-video refine. */\n refineFromVideoUrl?: string\n category?: string\n style?: \"realistic\" | \"anime\" | \"3d-pixar\" | \"illustration\"\n canonicalDescription?: string\n /** Parameter-picker prompt-fragment pass-through (Pass 7 F-77). */\n seedPromptHint?: string\n attachToObjectId?: string\n attachName?: string\n /**\n * Optional aspect ratio override. Defaults to 1:1 server-side. One of the\n * 5-value `ObjectAspectRatio` union (1:1 / 3:4 / 16:9 / 9:16 / 4:3) —\n * objects have their own enum (with 4:3 added) vs the character set.\n */\n aspectRatio?: ObjectAspectRatio\n /**\n * Optional clip duration in seconds. Validated server-side against the chosen\n * provider's allowed durations (e.g. kling 5/10, wan-i2v 5/10/15); omitted →\n * the model's own default (no behavior change). Mirrors generate-video's\n * per-model i2v duration lever.\n */\n duration?: number\n}\n\nexport interface GenerateObjectMotionResult {\n jobId: string\n}\n\nexport interface ApproveObjectMainImageResult {\n sourceImageUrl: string\n /**\n * LLM-authored caption. `null` when the LLM caption sub-failed — the wire\n * sends `\"\"`, normalized to `null` here so consumers see the same\n * `string | null` semantics as characters. The main image is still set; call\n * `recaption()` to retry.\n */\n canonicalDescription: string | null\n}\n\nexport interface RecaptionObjectResult {\n canonicalDescription: string\n}\n\nexport class ObjectsResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * List the caller's objects. By default returns active objects only;\n * pass `archived: true` to fetch soft-deleted rows for an \"archive\" view.\n * Optional `projectId` scopes the result to a single project; `q` keeps the\n * objects whose name or description contains it.\n */\n list(params: ListObjectsParams = {}): Promise<{ objects: Object[]; nextCursor?: string | null }> {\n const query: Record<string, string | undefined> = {}\n if (params.archived) query.archived = \"true\"\n if (params.projectId) query.projectId = params.projectId\n if (params.q) query.q = params.q\n if (params.limit !== undefined) query.limit = String(params.limit)\n if (params.cursor) query.cursor = params.cursor\n return this.client.request(\"GET\", \"/v1/objects\", { query })\n }\n\n /**\n * Convenience wrapper for `list({ archived: true })`. Returns soft-deleted\n * rows so callers can drive a UI \"Archived\" tab without re-encoding the\n * query param.\n *\n * `archived` is omitted from the param type — it's always set to `true` here.\n */\n listArchived(params: Omit<ListObjectsParams, \"archived\"> = {}): Promise<{ objects: Object[]; nextCursor?: string | null }> {\n return this.list({ ...params, archived: true })\n }\n\n /**\n * Fetch a single object including in-flight asset job state. Soft-deleted\n * (archived) rows are NOT returned by id — the route enforces\n * `deleted_at IS NULL` so archived objects 404 (uniform Pass 10 F-90b\n * \"not_found\" — does not leak the deleted vs non-existent distinction).\n */\n async get(id: string): Promise<ObjectDetail> {\n const res = await this.client.request<ObjectDetail>(\n \"GET\",\n `/v1/objects/${encodeURIComponent(id)}`,\n )\n // Normalize the wire `\"\"` caption (DB null / LLM sub-failure) → null so\n // consumers see the same `string | null` semantics as characters. New\n // object — never mutate the response.\n return { ...res, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Create a new object. `name` + `nodeId` are required — the route 400s\n * otherwise. Returns the new row's id.\n *\n * Note: the underlying route is the same `POST /v1/objects` upsert that\n * powers `update()`. This convenience wrapper enforces the INSERT-required\n * fields at the type level and never sends an `id`.\n */\n create(data: CreateObjectInput): Promise<{ id: string }> {\n return this.client.request(\"POST\", \"/v1/objects\", { body: data })\n }\n\n /**\n * Update an object. Only the fields you pass are written — undefined keys\n * are NOT touched on the row. Worker-owned asset buckets are intentionally\n * not exposed on this surface (see `UpdateObjectInput` for the rationale).\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to require the row's\n * `updated_at` still matches; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh `updatedAt`. The SDK\n * surfaces that as a generic `NodaroError` with the same code (per Phase\n * E1 calibration finding — error centralization in `throwApiError`).\n */\n update(id: string, data: UpdateObjectInput): Promise<UpdateObjectResult> {\n return this.client.request(\"POST\", \"/v1/objects\", {\n body: { id, ...data },\n })\n }\n\n /**\n * Soft-delete (archive) an object. The row is hidden from `list()` by\n * default but recoverable via `restore(id)` or visible under\n * `listArchived()`. Idempotent — repeating a delete on an already-archived\n * row is a no-op.\n */\n delete(id: string): Promise<{ success: true; archived: true }> {\n return this.client.request(\"DELETE\", `/v1/objects/${encodeURIComponent(id)}`)\n }\n\n /**\n * Hard-delete (permanent) an object — the row + every R2 asset it\n * references. Archived rows ONLY: active objects return 400 `not_archived`.\n * Call `delete()` first to archive, then `permanentDelete()` to destroy.\n *\n * Mirrors the `app_runs` permanent-delete pattern (archive-first) so a\n * stray SDK / curl caller cannot bypass the studio's archive-first UI\n * flow.\n */\n permanentDelete(id: string): Promise<{ success: true; permanent: true }> {\n return this.client.request(\"DELETE\", `/v1/objects/${encodeURIComponent(id)}`, {\n query: { permanent: \"true\" },\n })\n }\n\n /**\n * Un-archive an object. If the original name now collides (case-\n * insensitive) with an active row, the server auto-suffixes \"(restored)\"\n * and returns the effective name.\n */\n restore(id: string): Promise<{ id: string; name: string }> {\n return this.client.request(\"POST\", `/v1/objects/${encodeURIComponent(id)}/restore`)\n }\n\n /**\n * Fire `POST /v1/generate-object` to produce one or more candidate main\n * images. With `count > 1`, all jobs are reserved up-front before any\n * is enqueued — mid-batch failures roll back atomically.\n *\n * When `attachToObjectId` is set AND `count === 1`, the worker writes\n * the result directly to the row's `source_image_url`; otherwise you must\n * call `approveMainImage()` after picking a candidate.\n */\n async generate(data: GenerateObjectInput): Promise<GenerateObjectResult> {\n const res = await this.client.request<{ jobId?: string; jobIds?: string[] }>(\n \"POST\", \"/v1/generate-object\", { body: data },\n )\n // Tolerate the legacy `{ jobId }`-only shape (older server): synthesize jobIds.\n const jobIds = res.jobIds ?? (res.jobId ? [res.jobId] : [])\n return res.jobId ? { jobIds, jobId: res.jobId } : { jobIds }\n }\n\n /**\n * Fire `POST /v1/generate-object-asset` to produce a single variant.\n * When the studio path is set (`attachToObjectId` + `attachToColumn` +\n * `attachName`), the worker appends `{ name: attachName, url: <result> }`\n * to the named JSONB array column on completion.\n *\n * Note: `attachToColumn` is REQUIRED for `assetType === \"custom\"` — the\n * worker can't infer the bucket from the asset type. For canonical asset\n * types (`angles` / `materials` / `variations` / `motion`), the column is\n * derived automatically by the route.\n */\n generateAsset(\n data: GenerateObjectAssetInput,\n ): Promise<GenerateObjectAssetResult> {\n return this.client.request(\"POST\", \"/v1/generate-object-asset\", { body: data })\n }\n\n /**\n * Fire `POST /v1/generate-object-motion` to animate the object's main\n * image into a motion clip. Image-to-video, single clip per call; the\n * attach column is hardcoded to `motion_clips` server-side (objects have a\n * single motion bucket so the caller doesn't supply `attachToColumn`).\n * When the studio path is set (`attachToObjectId` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the row's\n * `motion_clips` column on completion.\n *\n * Defaults: `provider` → `\"kling-turbo\"`, `aspectRatio` → `\"1:1\"` (set\n * server-side via `resolveObjectAspectRatio({ assetType: \"motion\" })`).\n */\n generateMotion(\n data: GenerateObjectMotionInput,\n ): Promise<GenerateObjectMotionResult> {\n return this.client.request(\"POST\", \"/v1/generate-object-motion\", { body: data })\n }\n\n /**\n * Approve a completed `generate-object` job as the object's main image.\n * Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)\n * inline. Returns the new main-image URL plus the caption.\n *\n * Caption-failure semantics: the route still sends `\"\"` on LLM sub-failure,\n * but the SDK normalizes `\"\"` → `null` here so `canonicalDescription` carries\n * the same `string | null` semantics as characters. The main image is still\n * set; call `recaption()` to retry.\n *\n * Optimistic-concurrency: pass `expectedUpdatedAt` to gate the update on\n * the row's current `updated_at`; on mismatch the route returns 409\n * `concurrent_modification` carrying the fresh token.\n */\n async approveMainImage(\n id: string,\n candidateJobId: string,\n expectedUpdatedAt?: string,\n ): Promise<ApproveObjectMainImageResult> {\n const res = await this.client.request<{ sourceImageUrl: string; canonicalDescription: string | null }>(\n \"POST\",\n `/v1/objects/${encodeURIComponent(id)}/approve-main-image`,\n { body: { candidateJobId, expectedUpdatedAt } },\n )\n // Normalize the wire `\"\"` (LLM sub-failure) → null; build a new object\n // rather than mutating the response.\n return { sourceImageUrl: res.sourceImageUrl, canonicalDescription: res.canonicalDescription || null }\n }\n\n /**\n * Re-fire the LLM caption against the object's current main image. 502s\n * on LLM failure (unlike `approveMainImage` which preserves the side-effect\n * and returns \"\"); returns 400 `main_image_required` if no main image is\n * set yet.\n *\n * The route is a pure idempotent retry — it does NOT accept an\n * `expectedUpdatedAt` token (per Phase E1 calibration finding: backend\n * route is idempotent retry, not gated on optimistic-concurrency).\n */\n recaption(id: string): Promise<RecaptionObjectResult> {\n return this.client.request(\n \"POST\",\n `/v1/objects/${encodeURIComponent(id)}/llm-caption`,\n )\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/characters.ts"],"names":[],"mappings":"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","file":"chunk-EJSGJDZY.js","sourcesContent":["import { buildPersonHints, PEOPLE, PERSON_DIMENSION_ORDER, PERSON_DIMENSION_LABELS, type PersonValue } from \"@nodaro/prompts/person\"\nimport type { CharacterAspectRatio, CharacterAttachColumn, EntityStyle } from \"@nodaro/shared/entities\"\nimport type { TtsProvider } from \"@nodaro/shared/models\"\nexport type { EntityStyle, CharacterAspectRatio } from \"@nodaro/shared/entities\"\nexport { CHARACTER_STYLES, CHARACTER_ASPECT_OPTIONS, CHARACTER_ASPECT_DEFAULTS } from \"@nodaro/shared/entities\"\nimport type { NodaroClientCore } from \"../core.js\"\nimport type { GenerationSessionParams } from \"./nodes.js\"\n\n/**\n * Re-export the shared `EntityStyle` union (realistic | anime | 3d-pixar |\n * illustration) and `CHARACTER_STYLES` runtime tuple so SDK consumers don't\n * have to add `@nodaro/shared` as a second dependency just to typecheck the\n * `style` field. Single source of truth lives in `@nodaro/shared/entity-prompts`.\n */\n\n/**\n * Re-export the 4-value aspect-ratio union accepted by the generate-character*\n * routes. Single source of truth lives in `@nodaro/shared`. See\n * `CHARACTER_ASPECT_DEFAULTS` for the per-asset-type defaults.\n */\n\n/**\n * Structured Person composer, re-exported from `@nodaro/shared` so SDK\n * consumers can build a detailed person description — Identity, Body, and the\n * Face facial-geometry layer (cheekbones, canthal tilt, eyelid type, lip\n * fullness/shape, nose tip, etc.) — and feed it as `seedPrompt` / `description`\n * to `generate()` / `upsert()` without adding `@nodaro/shared` as a second dep.\n *\n * `buildPersonSeedPrompt` collapses a `PersonValue` into the same comma-joined\n * fragment the editor's Person picker produces (e.g. \"almond-shaped eyes,\n * sharply sculpted high cheekbones, full plump lips\"). Returns \"\" when empty.\n */\n\n\n/**\n * A character record returned by Nodaro's REST API. Mirrors the camelCase\n * shape produced by `backend/src/routes/characters.ts::toCamel()`.\n *\n * `expressions`, `poses`, `motions`, `angles`, `bodyAngles`,\n * `lightingVariations` are independent buckets keyed by a human-readable\n * variant name (e.g. `\"smile\"`, `\"standing\"`, `\"3/4 left\"`). Each entry's\n * `url` points at an R2-hosted asset.\n *\n * Identity-foundation fields:\n * - `referencePhotos` — caller-supplied real-life refs (max one per\n * non-`other` kind; cap 20 total). Drive the i2v / i2i path when a\n * provider supports multi-image conditioning.\n * - `realLifeRefsByVariant` — per-variant reference URLs (cap 20 keys,\n * 5 URLs per key). Keys are lowercased+trimmed.\n * - `referenceVideosByVariant` — per-label user-uploaded reference VIDEO\n * URLs (cap 20 keys, 5 URLs per key, lowercased+trimmed keys). Mirrors\n * `realLifeRefsByVariant` for video clips (e.g. emotion takes). Read the\n * chosen URLs off the row to drive generate-video's `referenceVideoUrls`.\n * - `seedPrompt` — short prompt fragment that scaffolds portrait gen.\n * - `canonicalDescription` — ~80–120-word LLM-authored visual caption,\n * populated by `approvePortrait()` / `recaption()`.\n */\nexport interface Character {\n id: string\n userId: string\n /** Canvas node linkage. `null` only on legacy clone rows that predate the\n * clone-side node_id fix (new clones mint one; creates always had one). */\n nodeId: string | null\n projectId: string | null\n name: string\n description: string | null\n gender: string | null\n style: string | null\n baseOutfit: string | null\n sourceImageUrl: string | null\n /** MODEL_CATALOG image-model id the main image was generated with (or `null`).\n * Set on create (the provider you generated with) + editable via `upsert`. */\n imageProvider: string | null\n expressions: Array<{ name: string; url: string }> | null\n poses: Array<{ name: string; url: string }> | null\n lightingVariations: Array<{ name: string; url: string }> | null\n angles: Array<{ name: string; url: string }> | null\n bodyAngles: Array<{ name: string; url: string }> | null\n motions: Array<{ name: string; url: string }> | null\n /** Named Character Boards — dense reference sheets, one per persona/look\n * (the `generate-image/character-board` factory preset rendered from the\n * character's images). A first-class bucket: community publish snapshots it\n * and clone hands the consumer their own copy to extend. Defaults to `[]`.\n * `type` marks an image-collage `\"identity\"` sheet vs a plain `\"looks\"`\n * board; `sourceImages` are the R2 URLs it was collaged from. Both optional\n * + backward-compatible (legacy boards have neither). */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }> | null\n /** Per-label user-uploaded reference VIDEO URLs (R2), keyed by a\n * caller-owned label (lowercased+trimmed server-side). Mirrors\n * `realLifeRefsByVariant` for video clips; read the chosen URLs off the row\n * to feed generate-video's `referenceVideoUrls`. Defaults to `{}`. */\n referenceVideosByVariant?: Record<string, string[]> | null\n /** The user's chosen DEFAULT asset take per variant (Studio version history).\n * OPAQUE map: key `\"<bucket>:<variant>\"` (e.g. `\"bodyAngles:front\"`,\n * `\"expressions:smile\"`) → the chosen asset URL (one already present in that\n * bucket). Stored verbatim — keys are NOT normalized; soft-capped server-side\n * at 200 keys / 2048-char values (overflow dropped silently). Defaults to `{}`. */\n selectedAssetByVariant?: Record<string, string> | null\n /** `voiceType` records the selected voice's KIND (premade voices are\n * addressed by name; library/custom voices by id at text-to-speech time).\n * `previewUrl` is a playable audio sample (the voice's `preview_url` / clone\n * sample) the studio plays in an `<audio>` element — persisted so Voice Library\n * voices (which have no by-id lookup) stay previewable after reload. Client-\n * played only; the server never fetches it. `ttsProvider` is the library\n * voice's verified TTS provider (see `SharedVoice.recommendedProvider`) —\n * send it as the text-to-speech `provider` so the voice renders on a model\n * it's verified for. All optional — a character may have no voice, or a\n * legacy voice predating these fields. */\n voice: { voiceId: string; voiceName: string; traits: string; voiceType?: \"premade\" | \"library\" | \"custom\"; previewUrl?: string; ttsProvider?: TtsProvider } | null\n personality: {\n mood: string\n speechStyle: string\n movementStyle: string\n behavioralNotes: string\n } | null\n /** ~80–120-word LLM-authored visual caption (approve-portrait / recaption).\n * Optional on the read surface so existing literal consumers don't break;\n * the route always returns it (string | null). */\n canonicalDescription?: string | null\n /** Identity-lock strength for Character Studio asset generation. */\n identityLock?: \"off\" | \"soft\" | \"strict\"\n deletedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * GET /v1/characters/:id appends three live-progress buckets the studio uses\n * to rehydrate spinners after a reload. Optional in the SDK surface — they\n * don't appear on `list()` rows.\n */\nexport interface CharacterDetail extends Character {\n pendingJobs?: Array<{\n jobId: string\n assetType: \"expressions\" | \"poses\" | \"angles\" | \"bodyAngles\" | \"lighting\" | \"motions\"\n name: string\n }>\n portraitCandidates?: Array<{\n jobId: string\n url: string | undefined\n progress: number\n status: string\n }>\n /** Recently completed portrait takes (7 days, newest first): at most 5\n * alternates, never the current portrait — plus, with\n * `get(id, { candidates: \"all\" })`, the current portrait's own take. */\n previousCandidates?: Array<{\n jobId: string\n url: string\n createdAt: string\n /** The image model the take was requested with. */\n provider?: string\n /** The aspect ratio the request sent explicitly; absent when the take\n * used the default ratio. */\n aspectRatio?: string\n /** `true` only on the current portrait's own take (returned with\n * `candidates: \"all\"`); `false` on the alternates. */\n isPortrait?: boolean\n }>\n}\n\n/** Options for `client.characters.get()`. */\nexport interface GetCharacterOptions {\n /**\n * `\"all\"`: `previousCandidates` also carries the current portrait's own\n * take, flagged `isPortrait: true`, when its job is within the 7-day window\n * (the alternates stay at most 5). Default `\"alternates\"`: the current\n * portrait is left out.\n */\n candidates?: \"alternates\" | \"all\"\n}\n\nexport type ReferencePhotoKind =\n | \"frontFace\"\n | \"sideLeft\"\n | \"sideRight\"\n | \"threeQuarterLeft\"\n | \"threeQuarterRight\"\n | \"frontBody\"\n | \"other\"\n\nexport interface ReferencePhoto {\n url: string\n kind: ReferencePhotoKind\n}\n\n/**\n * Body for `client.characters.upsert()`. Mirrors `upsertCharacterBody` in\n * `backend/src/routes/characters.ts`. Omitting `id` triggers an INSERT;\n * supplying it triggers an UPDATE that only writes the fields you pass —\n * undefined keys are NOT touched on the row.\n *\n * `name` is optional at the type level. The route requires `name` on INSERT\n * (id absent) and rejects with `validation_error` otherwise; on UPDATE the\n * route just ignores `name` when omitted, which lets partial updates like\n * `update(id, { gender: \"female\" })` succeed without re-sending the same\n * name the caller already has.\n */\nexport interface UpsertCharacterInput {\n /** UUID of the character row; omit to create. */\n id?: string\n /** Canvas node id the character belongs to. REQUIRED on create (the route\n * 400s without it, like `name`); optional on update — the update branch\n * never touches node_id, so partial updates needn't round-trip it. */\n nodeId?: string\n workflowId?: string\n projectId?: string\n name?: string\n description?: string\n gender?: string\n style?: string\n baseOutfit?: string\n sourceImageUrl?: string\n /** Persistent image-model id (a MODEL_CATALOG image model). Validated\n * server-side — unknown / non-image / \"\" is stored as `null`. */\n imageProvider?: string | null\n expressions?: Array<{ name: string; url: string }>\n poses?: Array<{ name: string; url: string }>\n lightingVariations?: Array<{ name: string; url: string }>\n angles?: Array<{ name: string; url: string }>\n bodyAngles?: Array<{ name: string; url: string }>\n motions?: Array<{ name: string; url: string }>\n /** Named Character Boards (see `Character.boards`) — whole-array replace,\n * like the asset buckets. Server caps: 24 boards, 200-char names, 30\n * sourceImages per board. */\n boards?: Array<{ name: string; url: string; type?: \"looks\" | \"identity\"; sourceImages?: string[] }>\n /** See `Character.voice` — persisted alongside the voice so TTS can resolve a\n * library/custom voice by id, `previewUrl` keeps the sample playable after\n * reload, and `ttsProvider` keeps generation on a model the voice is\n * verified for. All optional. */\n voice?: { voiceId: string; voiceName: string; traits: string; voiceType?: \"premade\" | \"library\" | \"custom\"; previewUrl?: string; ttsProvider?: TtsProvider } | null\n personality?: {\n mood: string\n speechStyle: string\n movementStyle: string\n behavioralNotes: string\n } | null\n seedPrompt?: string\n canonicalDescription?: string\n /** Identity-lock strength for Character Studio asset generation (off/soft/strict). */\n identityLock?: \"off\" | \"soft\" | \"strict\"\n referencePhotos?: ReferencePhoto[]\n /** Per-variant real-life reference URLs. Keys are lowercased+trimmed server-side. */\n realLifeRefsByVariant?: Record<string, string[]>\n /** Per-label user-uploaded reference VIDEO URLs (e.g. emotion takes). Keys\n * are lowercased+trimmed server-side; max 20 keys, 5 URLs each. Stored R2\n * URLs are read back off the row to drive generate-video's\n * `referenceVideoUrls`. */\n referenceVideosByVariant?: Record<string, string[]>\n /** The user's chosen DEFAULT asset take per variant. OPAQUE\n * `\"<bucket>:<variant>\"` → chosen-URL map; a write REPLACES the whole map\n * (the studio sends the full map each save). Omit to leave the row untouched.\n * Keys stored verbatim; soft-capped server-side at 200 keys / 2048-char values. */\n selectedAssetByVariant?: Record<string, string>\n}\n\nexport interface UpsertCharacterResult {\n id: string\n name?: string\n}\n\nexport interface ListCharactersParams {\n /** Restrict to a single project. */\n projectId?: string\n /** When true, return archived characters instead of active ones. */\n archived?: boolean\n /**\n * Search words: only the characters whose name or description contains\n * every word (split on spaces, the first 5), in any order, ignoring case —\n * `\"red car\"` finds \"Car, red\". Each word is matched as written (`%`, `_`\n * and `*` are not wildcards; punctuation is kept); blank means no search; up\n * to 200 characters. Combines with the other filters and with `cursor`\n * paging.\n */\n q?: string\n /**\n * Max rows to return. Server defaults to 100, caps at 500. Omit to take the\n * server default — passing it just narrows further.\n */\n limit?: number\n /** Opaque cursor from a prior page's `nextCursor` (fetches the next page). */\n cursor?: string\n}\n\nexport interface ListCharactersResult {\n characters: Character[]\n /** Pass back as `cursor` for the next page; `null` when there are no more. */\n nextCursor: string | null\n}\n\nexport interface DuplicateCharacterInput {\n /** Optional canvas node id to bind the new row to. */\n nodeId?: string\n /** Optional project to drop the new row into. */\n projectId?: string\n}\n\nexport interface CharacterUsage {\n workflowCount: number\n workflows: Array<{ id: string; name: string }>\n}\n\n/**\n * Input for `client.characters.generate()` — fires the\n * `POST /v1/generate-character` route. Produces 1–10 portrait candidates;\n * each lands as one `jobs` row in `pending` state and is then enqueued for\n * the worker.\n *\n * Provide at least one of `seedPrompt`, `referencePhotos`, or `description`\n * (the backend's refinement rejects empty input with `validation_error`).\n *\n * When `attachToCharacterId` is set, the worker writes the resulting URL\n * directly to `characters.source_image_url` on completion — caller doesn't\n * need a separate `approvePortrait` call for single-candidate runs. Pass\n * `attachPortrait: false` to keep every take a candidate instead.\n */\nexport interface GenerateCharacterInput extends GenerationSessionParams {\n name: string\n description?: string\n userPrompt?: string\n gender?: string\n style?: EntityStyle\n baseOutfit?: string\n sourceImageUrl?: string\n provider?: string\n /** Auto-attach the result to this character row. */\n attachToCharacterId?: string\n /** `false`: the take stays a candidate; only the approve route\n * (`approvePortrait()`) sets the portrait. Omitted/`true`: the finished\n * take is written to the row's portrait. */\n attachPortrait?: boolean\n seedPrompt?: string\n referencePhotos?: ReferencePhoto[]\n /** Number of candidate images to generate (1–10; server-validated). */\n count?: number\n /**\n * Explicit aspect ratio. Highest precedence — overrides both the character\n * node toggle and the per-asset-type default (portraits default to `3:4`).\n * Must be one of the 4-value `CharacterAspectRatio` union.\n */\n aspectRatio?: CharacterAspectRatio\n /**\n * Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins\n * against the per-asset-type default, loses to `aspectRatio`.\n */\n characterNodeAspectRatio?: CharacterAspectRatio\n /**\n * Credit-affecting quality tier (e.g. `\"high\"` for gpt-image). Priced like\n * generate-image (composite ids such as `gpt-image:high`); values the chosen\n * model doesn't support are ignored server-side, never rejected.\n */\n quality?: string\n /**\n * Credit-affecting output resolution (e.g. `\"2K\"` / `\"4K\"` / `\"2 MP\"`).\n * Priced like generate-image (composite ids such as `nano-banana-pro:4K`);\n * values the chosen model doesn't support are ignored server-side, never\n * rejected.\n */\n resolution?: string\n}\n\nexport interface GenerateCharacterResult {\n /** First job-id; convenience alias for `jobIds[0]`. */\n jobId: string\n /** All job-ids when `count > 1`. */\n jobIds: string[]\n}\n\nexport interface GenerateAssetInput extends GenerationSessionParams {\n assetType:\n | \"expressions\"\n | \"poses\"\n | \"lighting\"\n | \"angles\"\n | \"headAngles\"\n | \"bodyAngles\"\n | \"custom\"\n /** The named variant (e.g. `\"smile\"`, `\"standing\"`, `\"3/4 left\"`). */\n variant: string\n /** Display name of the character; appears in the prompt. */\n name: string\n description?: string\n userPrompt?: string\n gender?: string\n style?: EntityStyle\n baseOutfit?: string\n sourceImageUrl?: string\n /** Real-life reference URLs (cap 5). */\n realLifeRefs?: string[]\n provider?: string\n /** Auto-attach to character row + asset bucket on completion. */\n attachToCharacterId?: string\n /** Shared type — auto-includes new buckets (sheets/detail_closeups/outfit_variations); mirrors objects.ts/locations.ts. */\n attachToColumn?: CharacterAttachColumn\n attachName?: string\n /**\n * Explicit aspect ratio. Highest precedence — overrides both the character\n * node toggle and the per-asset-type default (expressions=1:1, poses=9:16,\n * headAngles=3:4, bodyAngles=9:16, lighting=3:4, angles=3:4, custom=3:4).\n */\n aspectRatio?: CharacterAspectRatio\n /**\n * Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins\n * against the per-asset-type default, loses to `aspectRatio`.\n */\n characterNodeAspectRatio?: CharacterAspectRatio\n /**\n * Credit-affecting quality tier (e.g. `\"high\"` for gpt-image). Values the\n * chosen model doesn't support are ignored server-side, never rejected.\n */\n quality?: string\n /**\n * Credit-affecting output resolution (e.g. `\"2K\"` / `\"4K\"` / `\"2 MP\"`).\n * Values the chosen model doesn't support are ignored server-side, never\n * rejected.\n */\n resolution?: string\n}\n\nexport interface GenerateMotionInput extends GenerationSessionParams {\n motionPrompt: string\n /** Optional when `attachToCharacterId` is set — falls back to the row's portrait. */\n sourceImageUrl?: string\n provider?: string\n name: string\n description?: string\n motionDescription?: string\n gender?: string\n style?: EntityStyle\n baseOutfit?: string\n realLifeRefs?: string[]\n attachToCharacterId?: string\n attachName?: string\n /**\n * Explicit aspect ratio. Highest precedence — overrides both the character\n * node toggle and the motions default (`9:16`).\n */\n aspectRatio?: CharacterAspectRatio\n /**\n * Character node toggle (per-canvas-node `defaultAssetAspectRatio`). Wins\n * against the motions default, loses to `aspectRatio`.\n */\n characterNodeAspectRatio?: CharacterAspectRatio\n}\n\nexport interface ApprovePortraitResult {\n portraitUrl: string\n /**\n * LLM-authored caption. `null` when the LLM call failed during the approval\n * — the portrait is still set; call `recaption()` to retry.\n */\n canonicalDescription: string | null\n}\n\nexport interface RecaptionResult {\n canonicalDescription: string\n}\n\n/**\n * Where a character's high-fidelity training stands. `untrained`: nothing was\n * ever trained, the trained model was removed (`deleteLora`), or a training\n * could not be started (`training_dispatch_failed` — refunded; this resets the\n * status even for a character trained before). `queued` / `training`: in\n * flight. `succeeded` / `failed` / `cancelled`: the last training's outcome.\n *\n * Generation uses the trained model only while the status is `succeeded`.\n */\nexport type CharacterTrainingState = \"untrained\" | \"queued\" | \"training\" | \"succeeded\" | \"failed\" | \"cancelled\"\n\n/** `GET /v1/characters/:id/training` — the character's training record. */\nexport interface CharacterTrainingStatus {\n status: CharacterTrainingState\n /** The provider's id for the latest training; `null` before the first one. */\n trainingId: string | null\n /** Why the last training failed, as the user may read it; `null` otherwise. */\n error: string | null\n /**\n * When the last successful training finished (ISO 8601). It stays set through\n * a re-train, a failed or cancelled one, or one that could not be started —\n * but the model is used only while `status` is `succeeded`.\n */\n trainedAt: string | null\n /** The last successful training's model version; set or not, used only while `status` is `succeeded`. */\n version: string | null\n /** The token added to prompts for this character (`TOK_<slug>_<6hex>`); generation adds it for you. */\n triggerWord: string | null\n /** How many images the latest training used. */\n imageCount: number | null\n}\n\n/** Body for `characters.train()`. */\nexport interface TrainCharacterInput {\n /**\n * Train on these of the character's images only (max 200 URLs; at most 20\n * are used). Omitted or empty: every eligible image — the portrait,\n * reference photos, expressions, poses, head and body angles and lighting\n * variations. At least 4 distinct images must remain either way.\n */\n selectedImageUrls?: string[]\n}\n\n/** `POST /v1/characters/:id/train` — answered 202 while the training runs. */\nexport interface TrainCharacterResult {\n /** The training's job; it ends `completed`, `failed` or `cancelled` with the training. */\n jobId: string\n /** The provider's id for this training (also on `trainingStatus().trainingId`). */\n trainingId: string\n /** The token added to prompts for this character once trained. */\n triggerWord: string\n}\n\n/**\n * The `code` of a `NodaroError` `train()` can throw beyond the typed classes\n * (`ForbiddenError` — an app token without `assets:write`, `missingScope`\n * says so; `InsufficientCreditsError`; `RateLimitedError`; `JobBlockedError`;\n * `UnauthorizedError`; …):\n *\n * - `insufficient_training_images` (400) — fewer than 4 distinct images; the\n * message says how many there are.\n * - `validation_error` (400) — a malformed id or `selectedImageUrls`.\n * - `already_training_or_not_found` (409) — a training is already in flight,\n * or the character is not yours / archived.\n * - `claim_failed` (500) — the training slot could not be claimed; nothing\n * started. Retry.\n * - `training_dispatch_failed` (502) — the training could not be started;\n * nothing is charged.\n * - `public_url_not_configured` (503) — the deployment cannot receive the\n * provider's completion call.\n * - `price_not_configured` (503) — the deployment has no price for training.\n * - `dedup_race_winner_unresolvable` (503) — a duplicate of this request was\n * in flight and its outcome could not be read; this one reserved nothing.\n * Read {@link CharactersResource.trainingStatus} before retrying.\n */\nexport type TrainCharacterRefusalCode =\n | \"insufficient_training_images\"\n | \"validation_error\"\n | \"already_training_or_not_found\"\n | \"claim_failed\"\n | \"training_dispatch_failed\"\n | \"public_url_not_configured\"\n | \"price_not_configured\"\n | \"dedup_race_winner_unresolvable\"\n\nexport class CharactersResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * List the caller's characters. By default returns active characters only;\n * pass `archived: true` to fetch soft-deleted rows for an \"archive\" view.\n * When `projectId` is set, only characters belonging to that project are\n * returned; `q` keeps the ones whose name or description contains it.\n *\n * Cursor-paginated: a response carrying a non-null `nextCursor` means more\n * rows exist. Pass it back as `cursor` to fetch the next page, and keep going\n * until `nextCursor` is `null` — a single call returns at most `limit` rows\n * (default 100), so treating one call as \"all characters\" silently truncates\n * the list for anyone past that count.\n *\n * ```ts\n * const all: Character[] = []\n * let cursor: string | undefined\n * do {\n * const page = await client.characters.list({ cursor })\n * all.push(...page.characters)\n * cursor = page.nextCursor ?? undefined\n * } while (cursor)\n * ```\n */\n list(params: ListCharactersParams = {}): Promise<ListCharactersResult> {\n const query: Record<string, string | undefined> = {}\n if (params.projectId) query.projectId = params.projectId\n if (params.archived) query.archived = \"true\"\n if (params.q) query.q = params.q\n if (params.limit !== undefined) query.limit = String(params.limit)\n if (params.cursor) query.cursor = params.cursor\n return this.client.request(\"GET\", \"/v1/characters\", { query })\n }\n\n /**\n * Fetch a single character including in-flight portrait / asset job state.\n * Soft-deleted (archived) rows are returned by id intentionally so canvas\n * nodes that hold a stale `characterDbId` keep loading. Pass\n * `{ candidates: \"all\" }` to also get the current portrait's own take in\n * `previousCandidates` (flagged `isPortrait: true`).\n */\n get(id: string, options: GetCharacterOptions = {}): Promise<CharacterDetail> {\n const query: Record<string, string | undefined> = {}\n if (options.candidates) query.candidates = options.candidates\n return this.client.request(\"GET\", `/v1/characters/${encodeURIComponent(id)}`, { query })\n }\n\n /**\n * Create or update a character. Omit `id` to create; supply it to update\n * (only the fields you pass get written — undefined keys are untouched).\n *\n * If the caller-supplied `name` collides with an existing active character\n * for this user, the request returns 409 `name_taken`. To auto-number a\n * placeholder, pass the placeholder name from `@nodaro/shared` and the\n * server will derive \"Untitled character 2\", \"Untitled character 3\", etc.\n */\n upsert(input: UpsertCharacterInput): Promise<UpsertCharacterResult> {\n return this.client.request(\"POST\", \"/v1/characters\", { body: input })\n }\n\n /**\n * Convenience wrapper around `upsert()` for creating new characters.\n * Equivalent to `upsert({ ...input, id: undefined })`. `name` is REQUIRED\n * on create — the route 400s on INSERT-without-name; we narrow the type\n * here so callers fail at compile-time rather than runtime.\n */\n create(\n input: Omit<UpsertCharacterInput, \"id\"> & { name: string },\n ): Promise<UpsertCharacterResult> {\n return this.upsert(input)\n }\n\n /**\n * Convenience wrapper around `upsert()` for updating an existing character.\n * Equivalent to `upsert({ ...input, id })`.\n */\n update(\n id: string,\n input: Omit<UpsertCharacterInput, \"id\">,\n ): Promise<UpsertCharacterResult> {\n return this.upsert({ ...input, id })\n }\n\n /**\n * Soft-delete (archive) a character. The row is hidden from `list()` by\n * default but still loadable via `get(id)` so canvas nodes pointing at it\n * keep working. Restore with `restore(id)`.\n */\n delete(id: string): Promise<{ success: true; archived: true }> {\n return this.client.request(\"DELETE\", `/v1/characters/${encodeURIComponent(id)}`)\n }\n\n /**\n * Un-archive a character. If the original name now collides with an\n * active row, the server auto-suffixes \"(restored)\" and returns the\n * effective name.\n */\n restore(id: string): Promise<{ id: string; name: string }> {\n return this.client.request(\"POST\", `/v1/characters/${encodeURIComponent(id)}/restore`)\n }\n\n /**\n * Duplicate (fork) a character to a new row with a `\"(copy)\"` suffix.\n * Asset URLs are shared by reference — the new row can diverge by\n * regenerating any of them.\n */\n duplicate(id: string, input: DuplicateCharacterInput = {}): Promise<{ id: string; name: string }> {\n return this.client.request(\n \"POST\",\n `/v1/characters/${encodeURIComponent(id)}/duplicate`,\n { body: input },\n )\n }\n\n /**\n * Count of the caller's workflows that reference this character. Powers the\n * library \"Archive\" confirmation modal in the editor.\n */\n usage(id: string): Promise<CharacterUsage> {\n return this.client.request(\"GET\", `/v1/characters/${encodeURIComponent(id)}/usage`)\n }\n\n /**\n * Fire `POST /v1/generate-character` to produce one or more portrait\n * candidates. With `count > 1`, all jobs are reserved up-front before any\n * is enqueued — mid-batch failures roll back atomically.\n *\n * When `attachToCharacterId` is set, the worker writes the result directly\n * to the row's `source_image_url` (unless `attachPortrait: false`);\n * otherwise you must call `approvePortrait()` after picking a candidate.\n */\n generate(input: GenerateCharacterInput): Promise<GenerateCharacterResult> {\n return this.client.request(\"POST\", \"/v1/generate-character\", { body: input })\n }\n\n /**\n * Fire `POST /v1/generate-character-asset` to produce a single\n * expression / pose / angle / lighting variant. When the studio path is\n * set (`attachToCharacterId` + `attachToColumn` + `attachName`), the\n * worker appends `{ name: attachName, url: <result> }` to the named\n * JSONB array column on completion.\n */\n generateAsset(input: GenerateAssetInput): Promise<{ jobId: string }> {\n return this.client.request(\"POST\", \"/v1/generate-character-asset\", { body: input })\n }\n\n /**\n * Fire `POST /v1/generate-character-motion` to animate the character's\n * portrait into a motion clip. The result is appended to the character's\n * `motions[]` bucket when `attachToCharacterId` is set.\n */\n generateMotion(input: GenerateMotionInput): Promise<{ jobId: string }> {\n return this.client.request(\"POST\", \"/v1/generate-character-motion\", { body: input })\n }\n\n /**\n * Approve a completed `generate-character` job as the character's portrait.\n * Sets `source_image_url` and fires the LLM caption (Claude Sonnet vision)\n * inline. Returns the new portrait URL plus the caption — `canonicalDescription`\n * is `null` if the LLM call sub-failed (portrait still set; retry via `recaption()`).\n */\n approvePortrait(id: string, candidateJobId: string): Promise<ApprovePortraitResult> {\n return this.client.request(\n \"POST\",\n `/v1/characters/${encodeURIComponent(id)}/approve-portrait`,\n { body: { candidateJobId } },\n )\n }\n\n /**\n * Re-fire the LLM caption against the character's current portrait. 502s on\n * LLM failure; returns 400 `no_portrait` if no portrait is set yet.\n */\n recaption(id: string): Promise<RecaptionResult> {\n return this.client.request(\n \"POST\",\n `/v1/characters/${encodeURIComponent(id)}/llm-caption`,\n )\n }\n\n /**\n * Start training a high-fidelity model of the character on its own images\n * (Nodaro Cloud only — other editions answer `NotFoundError`). Reserves the\n * training price up front and refunds it if the training fails or is\n * cancelled. Resolves at once (202); a training takes about 15 minutes —\n * follow it with {@link trainingStatus}. Once it succeeds, a workflow's\n * Generate Image node whose prompt @-mentions this one character runs on the\n * trained model.\n *\n * An OAuth app token needs the `assets:write` scope (`ForbiddenError`\n * otherwise, before anything is reserved); a session or personal API key\n * needs none. Refusals are `NodaroError`s whose `code` is a\n * {@link TrainCharacterRefusalCode}; rate-limited to 3 calls a minute.\n */\n train(id: string, input: TrainCharacterInput = {}): Promise<TrainCharacterResult> {\n return this.client.request(\"POST\", `/v1/characters/${encodeURIComponent(id)}/train`, { body: input })\n }\n\n /**\n * The character's training record (Nodaro Cloud only). Poll it while\n * `status` is `queued` or `training` — every 10 s or so is plenty.\n */\n trainingStatus(id: string): Promise<CharacterTrainingStatus> {\n return this.client.request(\"GET\", `/v1/characters/${encodeURIComponent(id)}/training`)\n }\n\n /**\n * Remove the character's trained model (Nodaro Cloud only): cancels a\n * training in flight and refunds its reservation, deletes the model, and\n * clears the training record (`trainingStatus` then reads `untrained`).\n * Generations go back to reference images. Cannot be undone — a new model\n * means a new, paid training. An OAuth app token needs the `assets:write`\n * scope (`ForbiddenError` otherwise), like deleting the character.\n */\n async deleteLora(id: string): Promise<void> {\n await this.client.request(\"DELETE\", `/v1/characters/${encodeURIComponent(id)}/lora`)\n }\n}\n\n/** Person catalog + hint helpers, re-exported from @nodaro/prompts (FSL-licensed). */\nexport { buildPersonHints, PEOPLE, PERSON_DIMENSION_ORDER, PERSON_DIMENSION_LABELS }\nexport type { PersonValue }\n\n/**\n * Collapse a PersonValue into the comma-joined seed-prompt fragment used by\n * `characters.generate({ seedPrompt })` — same composition the Nodaro editor\n * performs. Powered by @nodaro/prompts.\n */\nexport function buildPersonSeedPrompt(value: PersonValue): string {\n return buildPersonHints(value as Record<string, unknown> & PersonValue).join(\", \")\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":["NodaroClientCore","WorkflowsResource","ProjectsResource","SessionsResource","TrashResource","JobsResource","VideoProResource","ExecutionsResource","NodesResource","Scene3DResource","DeveloperAppsResource","OAuthResource","WebhooksResource","AppsResource","CharactersResource","LocationsResource","ObjectsResource","CreaturesResource","PipelinesResource","ReduceResource","PromptHelperResource","VoicesResource","LlmResource","MediaResource","AudioResource","CreditsResource","UserSettingsResource","BillingResource","UploadsResource","LibraryResource","GalleryResource","PresetsResource","SavedPostsResource","CollectionsResource","PalettesResource","MoodboardsResource","CompetitorsResource","PickerCatalogsResource","CatalogsResource","ModelsResource","ShotsResource","RecastResource","CastSwapResource","TrendsResource","StudioResource","CopilotResource","CommunityResource","TemplatesResource","TutorialsResource","OrganizationsResource","WorkspacesResource","EditResource"],"mappings":"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","file":"chunk-GAD3QCMM.cjs","sourcesContent":["import { WorkflowsResource } from \"./resources/workflows.js\"\nimport { ProjectsResource } from \"./resources/projects.js\"\nimport { SessionsResource } from \"./resources/sessions.js\"\nimport { TrashResource } from \"./resources/trash.js\"\nimport { JobsResource } from \"./resources/jobs.js\"\nimport { VideoProResource } from \"./resources/video-pro.js\"\nimport { ExecutionsResource } from \"./resources/executions.js\"\nimport { NodesResource } from \"./resources/nodes.js\"\nimport { Scene3DResource } from \"./resources/scene3d.js\"\nimport { DeveloperAppsResource } from \"./resources/developer-apps.js\"\nimport { OAuthResource } from \"./resources/oauth.js\"\nimport { WebhooksResource } from \"./resources/webhooks.js\"\nimport { AppsResource } from \"./resources/apps.js\"\nimport { CharactersResource } from \"./resources/characters.js\"\nimport { LocationsResource } from \"./resources/locations.js\"\nimport { ObjectsResource } from \"./resources/objects.js\"\nimport { CreaturesResource } from \"./resources/creatures.js\"\nimport { PipelinesResource } from \"./resources/pipelines.js\"\nimport { ReduceResource } from \"./resources/reduce.js\"\nimport { PromptHelperResource } from \"./resources/prompt-helper.js\"\nimport { VoicesResource } from \"./resources/voices.js\"\nimport { LlmResource } from \"./resources/llm.js\"\nimport { MediaResource } from \"./resources/media.js\"\nimport { AudioResource } from \"./resources/audio.js\"\nimport { CreditsResource } from \"./resources/credits.js\"\nimport { BillingResource } from \"./resources/billing.js\"\nimport { UserSettingsResource } from \"./resources/user-settings.js\"\nimport { UploadsResource } from \"./resources/uploads.js\"\nimport { LibraryResource } from \"./resources/library.js\"\nimport { GalleryResource } from \"./resources/gallery.js\"\nimport { PresetsResource } from \"./resources/node-presets.js\"\nimport { SavedPostsResource } from \"./resources/saved-posts.js\"\nimport { CollectionsResource } from \"./resources/collections.js\"\nimport { PalettesResource } from \"./resources/palettes.js\"\nimport { MoodboardsResource } from \"./resources/moodboards.js\"\nimport { CompetitorsResource } from \"./resources/competitors.js\"\nimport { PickerCatalogsResource } from \"./resources/picker-catalogs.js\"\nimport { CatalogsResource } from \"./resources/catalogs.js\"\nimport { ModelsResource } from \"./resources/models.js\"\nimport { ShotsResource } from \"./resources/shots.js\"\nimport { RecastResource } from \"./resources/recast.js\"\nimport { CastSwapResource } from \"./resources/cast-swap.js\"\nimport { TrendsResource } from \"./resources/trends.js\"\nimport { StudioResource } from \"./resources/studio.js\"\nimport { CopilotResource } from \"./resources/copilot.js\"\nimport { CommunityResource } from \"./resources/community.js\"\nimport { TemplatesResource } from \"./resources/templates.js\"\nimport { TutorialsResource } from \"./resources/tutorials.js\"\nimport { OrganizationsResource } from \"./resources/organizations.js\"\nimport { WorkspacesResource } from \"./resources/workspaces.js\"\nimport { EditResource } from \"./resources/edit.js\"\nimport { NodaroClientCore, type ClientOptions } from \"./core.js\"\n\nexport { CLIENT_HEADER } from \"./core.js\"\nexport type { ClientOptions, UserIdentity, ConnectedApp } from \"./core.js\"\n\n/**\n * The full client: the core (`./core.ts` — config, transport, identity) and\n * every resource.\n */\nexport class NodaroClient extends NodaroClientCore {\n readonly workflows: WorkflowsResource\n readonly projects: ProjectsResource\n /** Generation sessions inside projects (Create Image / Video / Music). */\n readonly sessions: SessionsResource\n /** Your Trash: items removed from sessions and deleted jobs. */\n readonly trash: TrashResource\n readonly jobs: JobsResource\n readonly videoPro: VideoProResource\n readonly executions: ExecutionsResource\n readonly nodes: NodesResource\n readonly scene3d: Scene3DResource\n readonly developerApps: DeveloperAppsResource\n readonly oauth: OAuthResource\n /** Outbound webhooks: endpoints that receive your job.completed / job.failed. */\n readonly webhooks: WebhooksResource\n readonly apps: AppsResource\n readonly characters: CharactersResource\n readonly locations: LocationsResource\n readonly objects: ObjectsResource\n readonly creatures: CreaturesResource\n readonly pipelines: PipelinesResource\n readonly reduce: ReduceResource\n readonly promptHelper: PromptHelperResource\n readonly voices: VoicesResource\n readonly llm: LlmResource\n readonly media: MediaResource\n readonly audio: AudioResource\n readonly credits: CreditsResource\n /** The signed-in person's own settings (the interface language, the monthly statement opt-in, …). */\n readonly userSettings: UserSettingsResource\n /** Plans and prices, the subscription, and how this deployment bills. */\n readonly billing: BillingResource\n readonly uploads: UploadsResource\n readonly library: LibraryResource\n /** The public gallery and your favorites. */\n readonly gallery: GalleryResource\n readonly presets: PresetsResource\n readonly savedPosts: SavedPostsResource\n readonly collections: CollectionsResource\n /** Your colour palettes — a generate-image direction (`direction.colorLook: paletteDirectionId(id)`). */\n readonly palettes: PalettesResource\n /** Your moodboards — a generate-image direction (`direction.style: moodboardDirectionId(id)`). */\n readonly moodboards: MoodboardsResource\n readonly competitors: CompetitorsResource\n readonly pickerCatalogs: PickerCatalogsResource\n readonly catalogs: CatalogsResource\n readonly models: ModelsResource\n readonly shots: ShotsResource\n readonly recast: RecastResource\n /** Cast Swap: replace the people and/or location in a short video (Cloud). */\n readonly castSwap: CastSwapResource\n /** Trends: ready-made Cast Swap recipes you fill with your own pictures (Cloud). */\n readonly trends: TrendsResource\n readonly studio: StudioResource\n readonly copilot: CopilotResource\n readonly community: CommunityResource\n readonly templates: TemplatesResource\n readonly tutorials: TutorialsResource\n readonly organizations: OrganizationsResource\n readonly workspaces: WorkspacesResource\n readonly edit: EditResource\n\n constructor(opts: ClientOptions) {\n super(opts)\n\n this.workflows = new WorkflowsResource(this)\n this.projects = new ProjectsResource(this)\n this.sessions = new SessionsResource(this)\n this.trash = new TrashResource(this)\n this.jobs = new JobsResource(this)\n this.videoPro = new VideoProResource(this)\n this.executions = new ExecutionsResource(this)\n this.nodes = new NodesResource(this)\n this.scene3d = new Scene3DResource(this)\n this.developerApps = new DeveloperAppsResource(this)\n this.oauth = new OAuthResource(this)\n this.webhooks = new WebhooksResource(this)\n this.apps = new AppsResource(this)\n this.characters = new CharactersResource(this)\n this.locations = new LocationsResource(this)\n this.objects = new ObjectsResource(this)\n this.creatures = new CreaturesResource(this)\n this.pipelines = new PipelinesResource(this)\n this.reduce = new ReduceResource(this)\n this.promptHelper = new PromptHelperResource(this)\n this.voices = new VoicesResource(this)\n this.llm = new LlmResource(this)\n this.media = new MediaResource(this)\n this.audio = new AudioResource(this)\n this.credits = new CreditsResource(this)\n this.userSettings = new UserSettingsResource(this)\n this.billing = new BillingResource(this)\n this.uploads = new UploadsResource(this)\n this.library = new LibraryResource(this)\n this.gallery = new GalleryResource(this)\n this.presets = new PresetsResource(this)\n this.savedPosts = new SavedPostsResource(this)\n this.collections = new CollectionsResource(this)\n this.palettes = new PalettesResource(this)\n this.moodboards = new MoodboardsResource(this)\n this.competitors = new CompetitorsResource(this)\n this.pickerCatalogs = new PickerCatalogsResource(this)\n this.catalogs = new CatalogsResource(this)\n this.models = new ModelsResource(this)\n this.shots = new ShotsResource(this)\n this.recast = new RecastResource(this)\n this.castSwap = new CastSwapResource(this)\n this.trends = new TrendsResource(this)\n this.studio = new StudioResource(this)\n this.copilot = new CopilotResource(this)\n this.community = new CommunityResource(this)\n this.templates = new TemplatesResource(this)\n this.tutorials = new TutorialsResource(this)\n this.organizations = new OrganizationsResource(this)\n this.workspaces = new WorkspacesResource(this)\n this.edit = new EditResource(this)\n }\n\n /**\n * A client that acts in `workspaceId`, sharing this one's auth and config.\n *\n * A NEW client rather than a setter, deliberately. A mutable selection is\n * the bug this whole axis exists to prevent: two concurrent operations\n * against one client would race over which workspace they were in, and the\n * loser would create work in the wrong place with nothing failing. A\n * per-workspace client cannot be raced.\n *\n * Pass `null` for the personal space.\n */\n withWorkspace(workspaceId: string | null): NodaroClient {\n // The two private settings are read by bracket access, which TypeScript\n // allows on a subclass and which adds no member to the declarations. Read\n // through `this` (not a side table keyed by the instance), so a client\n // wrapped in a Proxy (Vue/Pinia `reactive()`) or reached through\n // `Object.create(client)` still works.\n const fetchOverride = this[\"fetchOverride\"]\n return new NodaroClient({\n baseUrl: this.baseUrl,\n auth: this.auth,\n timeoutMs: this.timeoutMs,\n ...(fetchOverride ? { fetch: fetchOverride } : {}),\n ...(this[\"sendClientHeader\"] ? { clientLabel: this.clientLabel } : {}),\n ...(workspaceId ? { workspaceId } : {}),\n })\n }\n}\n\n/** Factory function — preferred entry point. */\nexport function createClient(opts: ClientOptions): NodaroClient {\n return new NodaroClient(opts)\n}\n"]}