@nodaro/sdk 2.47.0 → 2.51.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 (88) hide show
  1. package/dist/{chunk-KBHDUHKM.js → chunk-2SQ7V6EX.js} +2 -2
  2. package/dist/chunk-2SQ7V6EX.js.map +1 -0
  3. package/dist/{chunk-FIMBPKIH.cjs → chunk-4BAHF762.cjs} +43 -2
  4. package/dist/chunk-4BAHF762.cjs.map +1 -0
  5. package/dist/chunk-6FZN3QQO.cjs +146 -0
  6. package/dist/chunk-6FZN3QQO.cjs.map +1 -0
  7. package/dist/{chunk-5MOJSYHI.js → chunk-7QK2NXJA.js} +43 -2
  8. package/dist/chunk-7QK2NXJA.js.map +1 -0
  9. package/dist/{chunk-JTAW54UM.js → chunk-AEUPH7EE.js} +2 -2
  10. package/dist/{chunk-34PU2DAT.cjs.map → chunk-AEUPH7EE.js.map} +1 -1
  11. package/dist/{chunk-DIF7A5OH.cjs → chunk-E5MJK3W5.cjs} +2 -2
  12. package/dist/chunk-E5MJK3W5.cjs.map +1 -0
  13. package/dist/{chunk-I7AHLDXV.js → chunk-EC6MC46Y.js} +10 -10
  14. package/dist/{chunk-I7AHLDXV.js.map → chunk-EC6MC46Y.js.map} +1 -1
  15. package/dist/{chunk-NP6KDFIJ.cjs → chunk-HPSENMVN.cjs} +18 -18
  16. package/dist/{chunk-NP6KDFIJ.cjs.map → chunk-HPSENMVN.cjs.map} +1 -1
  17. package/dist/{chunk-XKDVEQVD.cjs → chunk-LTZKNAXM.cjs} +2 -2
  18. package/dist/chunk-LTZKNAXM.cjs.map +1 -0
  19. package/dist/{chunk-OWG3XAHQ.js → chunk-OCRUDBPG.js} +10 -6
  20. package/dist/chunk-OCRUDBPG.js.map +1 -0
  21. package/dist/{chunk-MYTMNPCP.cjs → chunk-OMX7JBGY.cjs} +3 -3
  22. package/dist/{chunk-MYTMNPCP.cjs.map → chunk-OMX7JBGY.cjs.map} +1 -1
  23. package/dist/{chunk-7LTVS75S.cjs → chunk-P6AM42UB.cjs} +2 -2
  24. package/dist/chunk-P6AM42UB.cjs.map +1 -0
  25. package/dist/{chunk-34PU2DAT.cjs → chunk-Q45PC4YD.cjs} +2 -2
  26. package/dist/chunk-Q45PC4YD.cjs.map +1 -0
  27. package/dist/{chunk-PQHZUWSL.js → chunk-THLTFB6I.js} +2 -2
  28. package/dist/chunk-THLTFB6I.js.map +1 -0
  29. package/dist/{chunk-GDDV5U32.cjs → chunk-TY2S25W5.cjs} +10 -6
  30. package/dist/chunk-TY2S25W5.cjs.map +1 -0
  31. package/dist/chunk-WYYDWJFF.js +144 -0
  32. package/dist/chunk-WYYDWJFF.js.map +1 -0
  33. package/dist/{chunk-FCYN4N3M.js → chunk-XISHBW5I.js} +3 -3
  34. package/dist/{chunk-FCYN4N3M.js.map → chunk-XISHBW5I.js.map} +1 -1
  35. package/dist/{chunk-XILR6TFO.js → chunk-YXVMQEJX.js} +2 -2
  36. package/dist/chunk-YXVMQEJX.js.map +1 -0
  37. package/dist/entries/billing.cjs +2 -2
  38. package/dist/entries/billing.d.cts +202 -3
  39. package/dist/entries/billing.d.ts +202 -3
  40. package/dist/entries/billing.js +1 -1
  41. package/dist/entries/cast-swap.cjs +2 -2
  42. package/dist/entries/cast-swap.d.cts +34 -31
  43. package/dist/entries/cast-swap.d.ts +34 -31
  44. package/dist/entries/cast-swap.js +1 -1
  45. package/dist/entries/client.cjs +14 -14
  46. package/dist/entries/client.js +9 -9
  47. package/dist/entries/core.cjs +5 -5
  48. package/dist/entries/core.js +1 -1
  49. package/dist/entries/credits.cjs +2 -2
  50. package/dist/entries/credits.d.cts +179 -1
  51. package/dist/entries/credits.d.ts +179 -1
  52. package/dist/entries/credits.js +1 -1
  53. package/dist/entries/jobs.cjs +2 -2
  54. package/dist/entries/jobs.d.cts +18 -7
  55. package/dist/entries/jobs.d.ts +18 -7
  56. package/dist/entries/jobs.js +1 -1
  57. package/dist/entries/node-presets.cjs +2 -2
  58. package/dist/entries/node-presets.d.cts +6 -0
  59. package/dist/entries/node-presets.d.ts +6 -0
  60. package/dist/entries/node-presets.js +1 -1
  61. package/dist/entries/trash.cjs +2 -2
  62. package/dist/entries/trash.d.cts +4 -1
  63. package/dist/entries/trash.d.ts +4 -1
  64. package/dist/entries/trash.js +1 -1
  65. package/dist/entries/trends.cjs +2 -2
  66. package/dist/entries/trends.d.cts +22 -13
  67. package/dist/entries/trends.d.ts +22 -13
  68. package/dist/entries/trends.js +1 -1
  69. package/dist/index.cjs +22 -22
  70. package/dist/index.d.cts +2 -2
  71. package/dist/index.d.ts +2 -2
  72. package/dist/index.js +9 -9
  73. package/package.json +3 -3
  74. package/dist/chunk-5MOJSYHI.js.map +0 -1
  75. package/dist/chunk-7LTVS75S.cjs.map +0 -1
  76. package/dist/chunk-DIF7A5OH.cjs.map +0 -1
  77. package/dist/chunk-FIMBPKIH.cjs.map +0 -1
  78. package/dist/chunk-GDDV5U32.cjs.map +0 -1
  79. package/dist/chunk-J4WFSKY6.js +0 -50
  80. package/dist/chunk-J4WFSKY6.js.map +0 -1
  81. package/dist/chunk-JTAW54UM.js.map +0 -1
  82. package/dist/chunk-KBHDUHKM.js.map +0 -1
  83. package/dist/chunk-NGKODS3J.cjs +0 -52
  84. package/dist/chunk-NGKODS3J.cjs.map +0 -1
  85. package/dist/chunk-OWG3XAHQ.js.map +0 -1
  86. package/dist/chunk-PQHZUWSL.js.map +0 -1
  87. package/dist/chunk-XILR6TFO.js.map +0 -1
  88. package/dist/chunk-XKDVEQVD.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/credits.ts"],"names":[],"mappings":";AAuNA,IAAM,iBAAA,GAAoB,EAAA;AAEnB,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,MAAM,OAAA,GAAgC;AACpC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA;AAAA,MAC5B,KAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAA,CAAa,OAAA,GAAqC,EAAC,EAAoC;AACrF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,KAAA,EAAO,0BAAA,EAA4B;AAAA,MACpF,OAAO,EAAE,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA;AAAO,KACvD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAAW,GAAA,EAA0C;AACnD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAA0B,MAAA,EAAQ,yBAAA,EAA2B;AAAA,MAC9E,MAAM,EAAE,MAAA,EAAQ,IAAI,KAAA,CAAM,CAAA,EAAG,iBAAiB,CAAA;AAAE,KACjD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,gBAAA,CAAiB,IAAA,GAAmB,EAAC,EAAoC;AACvE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,MAAA,EAAQ,gCAAA,EAAkC;AAAA,MAC3F,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,iBAAA,CAAkB,KAAA,GAAgC,EAAC,EAAqC;AACtF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAiC,MAAA,EAAQ,iCAAA,EAAmC;AAAA,MAC7F,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,oBAAA,GAAwD;AACtD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,MAAA,EAAQ,gCAAgC,CAAA;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUF","file":"chunk-5MOJSYHI.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Authenticated user's credit balance — the shape of `GET /v1/user/credits`'s\n * `data` field. Mirrors the canonical `UserBalance` from the backend billing\n * module (`backend/src/ee/billing/credits.ts`), the source of truth: keep this\n * in sync if that interface changes.\n */\nexport interface UserBalance {\n total: number\n subscription: number\n topup: number\n dailySpent: number\n dailyLimit: number | null\n monthlyAllocation: number\n /** Stored billing tier. Display should prefer `effectiveTier`. */\n tier: string\n /** Derived entitlement tier: \"payg\" when stored-free with purchased credits. */\n effectiveTier: string\n features: Record<string, unknown>\n periodEnd: string | null\n /** Credits earned for app usage (free tier only — earned by running flows). */\n appCreditsAllowance: number\n /** Present when a deployment uses an external shared wallet. Raw credits;\n * null means unavailable. Do not substitute the personal `total` balance. */\n externalWallet?: { available: number | null }\n /**\n * Where the account's free signup grant stands. `\"withheld\"` means the\n * account works but the grant did not land — show the activation path.\n * Absent where the deployment has no signup grant.\n */\n freeGrantState?: FreeGrantState\n /**\n * Present only with `freeGrantState: \"withheld\"`, when the claim showed no\n * browser signals at all: the app may say \"free credits need a regular\n * browser — reload\", because a reload in one judges the account again.\n */\n freeGrantNotice?: \"browser_unmeasured\"\n /**\n * The welcome-credits opt-in. PRESENT only while the deployment has the\n * offer switched on — absent means \"no popup, no banner, no block\".\n */\n welcomeOffer?: WelcomeOfferState\n}\n\n/** Where an account's free signup grant stands. */\nexport type FreeGrantState = \"unclaimed\" | \"granted\" | \"withheld\" | \"revoked\"\n\n/** `UserBalance.welcomeOffer` — what an app needs to show the welcome offer. */\nexport interface WelcomeOfferState {\n /** The one-time popup was already shown (see `markWelcomeOfferSeen`). */\n popupSeen: boolean\n /**\n * The account already has its credits (granted elsewhere) but still owes\n * the email consent: the web apps block creation until it is given — the\n * claim records it.\n */\n consentPending: boolean\n /** The credits a claim grants. Name this figure in the offer; never hard-code it. */\n credits: number\n}\n\n/** What `claimWelcomeOffer` sends: the browser signals plus the app answering. */\nexport interface WelcomeOfferClaimInput extends SignupKeys {\n /**\n * The answering app's slug (lowercase letters, digits and `-`, up to 40\n * characters), recorded with the consent. A value outside that grammar is\n * stored as absent, never refused.\n */\n sourceApp?: string\n /**\n * The interface language the user answered in — the id of a language Nodaro\n * ships, such as `\"en\"`, `\"ja\"` or `\"pt-BR\"`. Matched exactly and\n * case-sensitively: `\"en-US\"` or `\"pt-br\"` is stored as absent, never refused.\n * Recorded with the consent as the language of the LATEST consent: a later\n * \"yes\" in another language replaces it.\n */\n locale?: string\n}\n\n/** The answer of `POST /v1/credits/welcome-offer/claim`. */\nexport interface WelcomeOfferClaimResult {\n /** The email consent is recorded. */\n consent: \"granted\"\n /** Where the free grant stands after the claim — `\"withheld\"` when the abuse gate refused it. */\n grant: FreeGrantState\n /** The grant's figure. A grant tops the subscription credits UP TO it (it is not added on top), so a claim that lands it leaves the subscription credits at no less than this. */\n credits: number\n}\n\n/** The answer of `POST /v1/credits/welcome-offer/seen`. */\nexport interface WelcomeOfferSeenResult {\n ok: true\n}\n\n/**\n * Result of `POST /v1/credits/model-costs` — a batch cost lookup for editor\n * cost previews. `data` maps each priced identifier → its credit cost.\n *\n * Per-model fault isolation (the route runs the lookups under\n * `Promise.allSettled`): identifiers with no pricing row are reported in\n * `missing` (undisplayable until an operator seeds a price) and lookup\n * failures in `errors`, instead of failing the whole batch. Callers typically\n * render `'—'` for any identifier that lands in `missing`. The hard-fail\n * policy still triggers at reservation time when the user actually runs the\n * node — this preview lookup is intentionally lenient.\n */\nexport interface ModelCostsResult {\n data: Record<string, number>\n missing: string[]\n errors: string[]\n /**\n * Why each identifier in `errors` has no price — today only\n * `llm_model_unpriced`: a metered LLM price key whose model has no rate on\n * the lane it would be served on (it cannot be run). Omitted when `errors`\n * is empty.\n */\n errorCodes?: Record<string, ModelCostError>\n /**\n * The identifiers in `data` whose figure is an ESTIMATE, not a fixed price:\n * metered LLM price keys (`llmPriceKey` from `@nodaro/shared/llm-models`,\n * `<feature>:<model>[:direct]`), charged on what the call actually uses.\n * Show these as `≈N`. Omitted when there are none, so a batch of fixed ids\n * answers exactly as it always has. A metered key that lands in `missing`\n * instead means the server is not pricing LLM calls per model — show no\n * price for it.\n */\n estimates?: string[]\n}\n\n/** {@link ModelCostsResult.errorCodes} — why an identifier has no price. */\nexport interface ModelCostError {\n /** Today only `llm_model_unpriced`; open to codes a newer server may add. */\n code: \"llm_model_unpriced\" | (string & {})\n /** A sentence to show the user. */\n message: string\n}\n\n/**\n * Result of `GET /v1/credits/model-cost?model=<id>` (`data`): the credits one\n * identifier is charged. `estimate: true` marks a metered LLM price key's\n * figure as an estimate (show it as `≈N`); such an answer is never cached.\n */\nexport interface ModelCostResult {\n model: string\n creditCost: number\n estimate?: true\n}\n\n/**\n * The two browser signals a claim reports — SHA-256 hex digests computed by\n * `@nodaro/sdk/signup-signals` (`collectSignupKeys`). Either may be absent:\n * a browser that cannot be measured still claims, and the server treats the\n * absence as what it is. Any other value is stored as absent server-side.\n */\nexport interface SignupKeys {\n browserKey?: string\n deviceKey?: string\n}\n\n/** The answer of `POST /v1/credits/claim-signup-grant`. */\nexport interface SignupGrantClaimResult {\n /** Where the account's free grant stands after this call. */\n state: FreeGrantState\n /** True only when THIS call moved the credits — a repeat is `false` with the same state. */\n granted: boolean\n}\n\n/**\n * One row of `GET /v1/credits/transactions` — a run's reservation and how it\n * settled.\n *\n * Which amount to read: a run first HOLDS credits and is then settled — charged\n * all or part of the hold, the rest returned. `credits_charged` is what the run\n * cost (`null` while the hold is still open, `0` once refunded); sum it for\n * spend. `credits_reserved` is the hold, which never changes. `credits_used`\n * is the same hold under its original name, kept for existing integrations.\n */\nexport interface CreditTransaction {\n id: string\n /** ISO-8601. Also the paging cursor: pass the last row's value as `cursor`. */\n created_at: string\n /** Settled charge; `null` while `status` is `\"reserved\"`, `0` when refunded. */\n credits_charged: number | null\n /** The hold taken when the run was accepted. */\n credits_reserved: number | null\n /** The hold, under its original name. Prefer `credits_charged`. */\n credits_used: number | null\n /** The model or operation the credits were for. */\n action: string | null\n provider: string | null\n /** Reservation lifecycle: `\"reserved\"`, `\"committed\"` or `\"refunded\"`. */\n status: string | null\n /** The caller's own billing details for the run (model, which pool paid, …). */\n metadata: Record<string, unknown>\n /** `\"workspace\"` when a class or team budget paid, not your balance. */\n payer: \"user\" | \"workspace\"\n workspaceId: string | null\n}\n\n/** A page of {@link CreditTransaction}s, newest first. */\nexport interface CreditTransactionsPage {\n data: CreditTransaction[]\n /** Pass as `cursor` for the next page; `null` when there are no more rows. */\n nextCursor: string | null\n}\n\nexport interface CreditTransactionsOptions {\n /** Rows per page, 1–50 (server default 20). */\n limit?: number\n /** The `nextCursor` of the previous page. */\n cursor?: string\n}\n\n/** Max identifiers per `model-costs` batch — the route caps the request at 50. */\nconst MODEL_COSTS_LIMIT = 50\n\nexport class CreditsResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * `GET /v1/user/credits` → the authenticated user's credit balance and tier\n * info. Throws `UnauthorizedError` (401) when signed out, and the SDK's\n * other typed errors on the usual statuses.\n */\n async balance(): Promise<UserBalance> {\n const res = await this.client.request<{ data: UserBalance }>(\n \"GET\",\n \"/v1/user/credits\",\n )\n return res.data\n }\n\n /**\n * `GET /v1/credits/transactions` → one page of the caller's credit\n * transactions, newest first, with `nextCursor` for the next page (`null`\n * at the end). Read `credits_charged` for what each run cost — it can be\n * below the hold (`credits_reserved`) and is `null` while the hold is open.\n * Cloud-only route: a self-hosted instance answers 404.\n */\n transactions(options: CreditTransactionsOptions = {}): Promise<CreditTransactionsPage> {\n return this.client.request<CreditTransactionsPage>(\"GET\", \"/v1/credits/transactions\", {\n query: { limit: options.limit, cursor: options.cursor },\n })\n }\n\n /**\n * `POST /v1/credits/model-costs` → per-identifier credit cost, for editor\n * cost previews. Capped at the first {@link MODEL_COSTS_LIMIT} identifiers\n * (the route's request limit). Preserves the `{ data, missing, errors }`\n * fault-isolation shape verbatim, plus `estimates` / `errorCodes` when a\n * metered LLM price key was asked (see {@link ModelCostsResult}).\n */\n modelCosts(ids: string[]): Promise<ModelCostsResult> {\n return this.client.request<ModelCostsResult>(\"POST\", \"/v1/credits/model-costs\", {\n body: { models: ids.slice(0, MODEL_COSTS_LIMIT) },\n })\n }\n\n /**\n * `POST /v1/credits/claim-signup-grant` → claims the account's free signup\n * grant, once, with the browser signals of the page making the claim.\n *\n * Call it once per page load as soon as the session is known, and never\n * wait on it for rendering: it is idempotent (a decided account answers\n * with its state and `granted: false`), it costs the user nothing when it\n * fails (the next load asks again), and the signals are what make the\n * claim worth sending — collect them with `collectSignupKeys()` from\n * `@nodaro/sdk/signup-signals` and pass whatever came back, `{}` included.\n * Cloud-only route: a self-hosted instance answers 404.\n */\n claimSignupGrant(keys: SignupKeys = {}): Promise<SignupGrantClaimResult> {\n return this.client.request<SignupGrantClaimResult>(\"POST\", \"/v1/credits/claim-signup-grant\", {\n body: keys,\n })\n }\n\n /**\n * `POST /v1/credits/welcome-offer/claim` → the user's \"yes\" to the welcome\n * offer, as ONE server action: the marketing-email consent is recorded,\n * then the free signup grant is claimed with that consent. The abuse gate\n * still decides — consent is necessary, not sufficient — so read `grant`:\n * `\"granted\"` (the grant has landed: this call topped the subscription credits up to `credits` — not added on top — or it had landed before), `\"withheld\"` (refused), `\"revoked\"` (taken back earlier; nothing moves), `\"unclaimed\"` (nothing moved — treat as a failure and let the user retry).\n * Pass the browser signals from `collectSignupKeys()` and your app's slug.\n *\n * **Browser session only.** Usable from a first-party web app signed in\n * with a Supabase session (the consent is a person's choice in a browser);\n * an API key or OAuth app token is refused with 403. 409\n * `welcome_offer_disabled` while the deployment has the offer off — show\n * it only when `balance().welcomeOffer` is present. Cloud-only route: a\n * self-hosted instance answers 404.\n */\n claimWelcomeOffer(input: WelcomeOfferClaimInput = {}): Promise<WelcomeOfferClaimResult> {\n return this.client.request<WelcomeOfferClaimResult>(\"POST\", \"/v1/credits/welcome-offer/claim\", {\n body: input,\n })\n }\n\n /**\n * `POST /v1/credits/welcome-offer/seen` → records that the one-time welcome\n * popup was shown, so `balance().welcomeOffer.popupSeen` turns true on every\n * app. Idempotent (the first stamp wins); a failed call only means the popup\n * may show once more, so callers usually ignore errors.\n *\n * **Browser session only**, like {@link claimWelcomeOffer}: 403 for an API\n * key or OAuth app token, 409 `welcome_offer_disabled` while the offer is\n * off, 404 on a self-hosted instance.\n */\n markWelcomeOfferSeen(): Promise<WelcomeOfferSeenResult> {\n return this.client.request<WelcomeOfferSeenResult>(\"POST\", \"/v1/credits/welcome-offer/seen\")\n }\n\n // NOTE: no `estimate(...)` helper. The backend exposes\n // `POST /v1/credits/estimate-workflow` (body `{ nodes }` → `{ data: {\n // totalCredits, nodeCount, estimate?: true } }` — `estimate` when a metered\n // LLM node's estimate is in the total), but no consumer has a settled shape for it\n // yet (studio pre-checks `balance >= Σ modelCosts` client-side rather than\n // calling an estimate endpoint). Adding it now would be inventing an API\n // surface ahead of a real caller, so it's deliberately omitted — add it when\n // a consumer needs it, shaped to that need.\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/jobs.ts"],"names":[],"mappings":"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","file":"chunk-7LTVS75S.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * A job's lifecycle status. Hand-copied mirror of `JOB_STATUSES` in the\n * platform's `backend/src/lib/job-status.ts` — nothing links the two at build\n * time, so a new member has to be added here in the same change.\n *\n * `pending_review` is IN-FLIGHT, NOT TERMINAL: a job policy registered by the\n * deployment held the output for a human reviewer. Treat it like `processing`\n * in a `switch` — keep waiting, do not re-run — and it resolves to `completed`\n * (approved), `failed` (rejected, carrying an `error_hint` with\n * `kind: \"policy-block\"`) or `cancelled` (a held job is cancellable like any\n * in-flight job). `credit_status` reads `\"reserved\"` for the whole hold. Only\n * appears on deployments that register a job policy; `runAndWait` throws\n * {@link JobHeldError} on the first held tick rather than waiting it out.\n */\nexport type JobStatus =\n | \"pending\"\n | \"queued\"\n | \"processing\"\n | \"pending_review\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\"\n\n/**\n * A PROVIDER's verdict: the model's own safety filter refused the output.\n * `class` distinguishes a deterministic block (copyright/likeness — retrying\n * the same request never helps) from `safety`, whose filter is known to be\n * non-deterministic for some models: `retried` says whether the worker\n * already spent its one extra attempt, and `suggestedProvider` — present\n * only when the catalog declares a fallback for the model that failed — is a\n * real model id the SAME prompt/references can be retried on.\n */\nexport type SafetyBlockHint = {\n kind: \"safety-block\"\n class: \"copyright\" | \"likeness\" | \"safety\"\n retried: boolean\n suggestedProvider?: string\n /**\n * Present when the provider refused the INPUT VIDEO itself — not the prompt,\n * a reference image or the output. Sending a different clip is the fix.\n */\n subject?: \"input_video\"\n}\n\n/**\n * A NODARO-side verdict, not a provider's: a job policy registered by the\n * deployment rejected the request (`hookPoint: \"request\"` — nothing ran, and\n * the call itself failed with {@link JobBlockedError}) or the finished output\n * (`hookPoint: \"result\"` — the result was blocked and never published).\n *\n * `reason` is USER-SAFE BY CONTRACT — the platform guarantees the policy's\n * machine text (scores, labels) never reaches it — so show it to the person\n * who made the request as-is. `policyId` identifies the deployment's policy,\n * not a Nodaro product feature. Only occurs on deployments that register a\n * job policy.\n */\nexport type PolicyBlockHint = {\n kind: \"policy-block\"\n policyId: string\n reason: string\n hookPoint: \"request\" | \"result\"\n}\n\n/**\n * The job ran out of credits partway: a step that reserves its own credits\n * (for example, the video a Cast Swap or trend run dispatches) was refused —\n * the balance could not cover it, or a plan or workspace cap was reached. The\n * job failed and its credits were refunded; ask the person to top up (or raise\n * the cap), then run it again.\n *\n * `code` is the refusal's own code, a lowercase snake_case token — for\n * example `insufficient_credits`, `free_video_limit`, `instance_cap_reached`,\n * `budget_exceeded`, `member_cap_exceeded` or `user_allowance_exceeded`. Treat\n * an unknown code like `insufficient_credits`.\n */\nexport type CreditsHint = {\n kind: \"credits\"\n code: string\n}\n\n/**\n * The provider refused the job's input video without naming a content-policy\n * class (its message is about the file, not a policy). Treat it like any\n * other failure — no fallback model is offered — except that the input to\n * change is known: send a different clip.\n */\nexport type InputRefusedHint = {\n kind: \"input-refused\"\n subject: \"input_video\"\n}\n\n/**\n * A job's `jobs.error_hint` (migration 376) — a user-safe, machine-readable\n * failure verdict, so a caller can act on it without parsing `error_message`\n * prose. One column, several verdict sources; `kind` is the discriminant.\n *\n * HAND-COPIED MIRROR of `ErrorHint` in the platform's\n * `backend/src/lib/safety-block.ts` (which names this file in its own\n * comment). Nothing links the two at build time: a new arm there has to be\n * pasted here verbatim in the same change, with a changeset.\n * `packages/client/dist/index.d.ts` is REBUILT, never hand-edited.\n */\nexport type JobErrorHint = SafetyBlockHint | PolicyBlockHint | CreditsHint | InputRefusedHint\n\n/**\n * The billing lifecycle of a job's credit reservation — `usage_logs.status`\n * for the job's `usage_log_id`. `null` when the job has no usage log (e.g. a\n * free run) or hasn't reserved credits yet.\n */\nexport type CreditStatus = \"reserved\" | \"committed\" | \"refunded\"\n\n/** A job owner's rating of their job — set with {@link JobsResource.rate}. */\nexport type JobRating = \"good\" | \"bad\"\n\n/**\n * The `rating` filter of {@link JobsResource.list}: one rating, any rating\n * (`\"rated\"`) or none (`\"unrated\"`).\n */\nexport type JobRatingFilter = JobRating | \"rated\" | \"unrated\"\n\n/**\n * Job record returned to non-admin SDK consumers. Field names are snake_case\n * to match the wire format (per the OpenAPI fix in Phase 1).\n *\n * Sensitive fields stripped server-side for non-admin callers:\n * `provider`, `provider_cost`, `display_cost`, `credits_actual`. USD\n * pricing is admin-only across api/sdk/mcp — non-admin consumers see\n * only the `credits` abstraction. Server-only nested job data (including\n * Recast's private remux base) is stripped for every caller, including admins.\n */\nexport interface Job {\n id: string\n status: JobStatus\n progress: number\n user_id: string\n input_data: unknown\n output_data: unknown\n error_message: string | null\n /** Present on a failed job the worker classified as a provider\n * content-policy block (`kind: \"safety-block\"`) or that a deployment's job\n * policy rejected (`kind: \"policy-block\"`); absent/null otherwise.\n * User-safe by construction — narrow on `kind` before reading the rest. */\n error_hint?: JobErrorHint | null\n /** PR9: the job's credit reservation lifecycle, derived server-side from\n * `usage_logs.status`. `null` when there is no usage log to report. Never\n * changes the meaning of `credits` (the reserved amount) below. */\n credit_status?: CreditStatus | null\n /** The credits HELD when the job was accepted. Never changes — a job can\n * settle below it; read {@link Job.credits_charged} for what it cost. */\n credits: number | null\n /**\n * What the job actually cost once its hold settled. Can be lower than\n * `credits` (the unused part goes back to the balance), `0` when the hold was\n * refunded, `null` while the job is still running or when it reserved\n * nothing of its own. Sum this — not `credits` — to total spend. Returned by\n * `get()` and `list()`.\n */\n credits_charged?: number | null\n job_type: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n /**\n * Provenance: which kind of caller created the job — one of\n * `\"internal\" | \"mcp\" | \"app\" | \"cli\" | \"sdk\" | \"extension\" | \"web\" | \"api\"`.\n * `source_detail` narrows it (origin host, `extension/<name>` label,\n * `sdk/<version>`, MCP client name, developer-app id). Descriptive only;\n * lets a library view label or filter media by origin.\n */\n source?: string | null\n source_detail?: string | null\n /** The generation session this job was born in (the `sessionId` sent with\n * the generation request), or null. See `client.sessions`. */\n session_id?: string | null\n /**\n * With `list({ withSessions: true })`: every session that holds the job now,\n * each linkable — empty when it is in no session (General). Absent otherwise.\n */\n sessions?: JobSessionRef[]\n /**\n * When the job was deleted, on a job read from the Trash\n * (`client.trash.list()`). A deleted job is left out of every other read, so\n * this is otherwise absent or `null`.\n */\n deleted_at?: string | null\n /**\n * Whether the job's owner favorited it (`client.gallery.toggleFavorite`).\n * Returned by `get()` and `list()`. Absent from an older server, and when\n * the server could not read it for a moment — treat absent as unknown,\n * never as `false`.\n */\n favorited?: boolean\n /**\n * The owner's rating of the job ({@link JobsResource.rate}), or `null` when\n * unrated. Returned by `get()` and `list()`; absent like `favorited`.\n */\n rating?: JobRating | null\n}\n\nexport interface CancelJobResult {\n success: true\n cancelled: number\n /**\n * `true` when the job could not be stopped because its provider call is\n * already running — a metered LLM job's call, an external provider task. It\n * finishes and is charged only what it used.\n */\n inFlight?: boolean\n}\n\n/**\n * The kind of caller that created a job (`Job.source`). Hand-copied mirror of\n * `JOB_SOURCES` in the platform's `backend/src/lib/job-source.ts`.\n */\nexport type JobSource = \"internal\" | \"mcp\" | \"app\" | \"cli\" | \"sdk\" | \"extension\" | \"web\" | \"api\"\n\nexport interface DeleteJobResult {\n success: true\n}\n\n/** A session that holds a job now (`Job.sessions`). */\nexport interface JobSessionRef {\n id: string\n title: string | null\n /** The session's project, or `null` when it has none. */\n projectId: string | null\n}\n\nexport interface ListJobsParams {\n /** One status or several (a job matching any of them). An unknown status is a 400. */\n status?: JobStatus | readonly JobStatus[]\n /** Exact match on `input_data.type` — the route that created the job\n * (`\"llm-structured\"`, `\"video-analysis\"`, …). One value or several (up to 20). */\n type?: string | readonly string[]\n /** Exact match on `input_data.origin` — the client app that sent it. */\n origin?: string\n /** The surface that created the job (`Job.source`): `\"mcp\"` lists what your\n * MCP clients (Claude, ChatGPT, …) made. One value or several. */\n source?: JobSource | readonly JobSource[]\n /** `true`: only jobs you favorited; `false`: only jobs you did not. */\n favorited?: boolean\n /** Your rating: `\"good\"`, `\"bad\"`, any (`\"rated\"`) or none (`\"unrated\"`). */\n rating?: JobRatingFilter\n /** The jobs of one generate-image batch (`count` / `models`) — its `batchId`. */\n batchId?: string\n /**\n * The jobs a session of this project holds now — or, `\"none\"`, a session\n * with no project. Each job once, however many sessions hold it. Not with\n * `inSession: false`.\n */\n projectId?: string | \"none\"\n /**\n * `false`: the jobs no session holds now (General), whatever session they\n * were born in. `true`: the jobs at least one session holds.\n */\n inSession?: boolean\n /** `true`: each job carries `sessions`, the sessions that hold it now. */\n withSessions?: boolean\n /** Page size, 1–100 (server default 50). */\n limit?: number\n /** The previous page's `next`. */\n cursor?: string\n}\n\n/** What {@link JobsResource.rate} answers: the job's rating now. */\nexport interface RateJobResult {\n data: { jobId: string; rating: JobRating | null }\n}\n\n/**\n * {@link JobsResource.fromUpload}'s body: an image edited in the browser,\n * already stored with `client.uploads.upload`, and what it was made from.\n */\nexport interface JobFromUploadParams {\n /** The edited image's `assetId`, as `client.uploads.upload` returned it. */\n assetId: string\n /** The job whose image was edited. */\n sourceJobId?: string\n /** Which of `sourceJobId`'s images was edited, when it made several. Default: its `imageUrl`. */\n sourceImageUrl?: string\n /** The upload that was edited, when the source is not a job. Not with `sourceJobId`. */\n sourceAssetId?: string\n /** The editor's design state, stored with `client.uploads.uploadJson`, so the edit can be reopened. */\n editStateUrl?: string\n /** The generation session to file the job in. */\n sessionId?: string\n}\n\n/** A list filter value as the query string carries it: several are comma-separated. */\nfunction listParam(value: string | readonly string[] | undefined): string | undefined {\n if (value === undefined) return undefined\n return typeof value === \"string\" ? value : value.join(\",\")\n}\n\nexport interface ListJobsPage {\n /**\n * A page may hold fewer than `limit` rows — even none — and still carry a\n * `next`. Page on `next`, never on `data.length`.\n */\n data: Job[]\n /** Pass back as `cursor` for the next page; `null` on the last one. */\n next: string | null\n}\n\n/**\n * Lean job status returned by `GET /v1/jobs/:id/status`. Skips the\n * `input_data` JSONB and cost columns. Its `output_data` still goes\n * through the server-only nested-field redaction. Intended for poll loops that\n * only need progress/output/error, and when the job finished.\n */\nexport interface JobStatusResult {\n id: string\n status: JobStatus\n progress?: number\n output_data?: unknown\n error_message?: string | null\n /** PR9: see {@link Job.error_hint}. */\n error_hint?: JobErrorHint | null\n /** PR9: see {@link Job.credit_status}. */\n credit_status?: CreditStatus | null\n /** When the job finished, on the server's clock; null until it has. */\n completed_at?: string | null\n}\n\nexport class JobsResource {\n constructor(private client: NodaroClientCore) {}\n\n /** Get a single job by ID. */\n get(id: string): Promise<{ data: Job }> {\n return this.client.request(\"GET\", `/v1/jobs/${encodeURIComponent(id)}`)\n }\n\n /**\n * Your jobs, newest first (`GET /v1/jobs`), cursor-paginated. `type` and\n * `origin` filter on the job's own `input_data`, which every job carries —\n * a client app lists its runs with `{ type: \"llm-structured\", origin: \"studio\" }`.\n * `status` and `type` take one value or a list; `favorited` and `rating`\n * filter on your own marks, `source` on the surface that created the job.\n * `projectId` / `inSession` filter on where the job is now (a project's\n * sessions, or General), and `withSessions` says which sessions each job is\n * in — for \"everything in this project\". Every filter combines with AND,\n * and each job carries `favorited` and `rating`. A deleted job is never\n * listed: read the Trash with `client.trash.list()`.\n */\n list(params: ListJobsParams = {}): Promise<ListJobsPage> {\n const { status, type, source, inSession, withSessions, ...rest } = params\n return this.client.request<ListJobsPage>(\"GET\", \"/v1/jobs\", {\n query: {\n ...rest,\n status: listParam(status),\n type: listParam(type),\n source: listParam(source),\n inSession: inSession === undefined ? undefined : String(inSession),\n withSessions: withSessions ? \"true\" : undefined,\n },\n })\n }\n\n /**\n * Record an image edited in the browser as a job (`POST /v1/jobs/from-upload`):\n * a completed, private, free `image-edit` job whose `output_data.imageUrl` is\n * the upload, linked to its source in `input_data` (`sourceJobId` /\n * `sourceAssetId`, `sourceImageUrl`). It then lists, rates, favorites and\n * files into a session like any image job. One upload is one job: the same\n * `assetId` again returns the same job. OAuth apps need the `assets:write`\n * scope. A deployment's job policy can refuse the image ({@link JobBlockedError}).\n */\n fromUpload(params: JobFromUploadParams): Promise<{ data: Job }> {\n return this.client.request<{ data: Job }>(\"POST\", \"/v1/jobs/from-upload\", { body: params })\n }\n\n /**\n * Rate one of your jobs `\"good\"` or `\"bad\"`, or clear the rating with\n * `null` (`PUT /v1/jobs/:id/rating`). Only the job's owner rates it — anyone\n * else's job is a `NotFoundError`. The rating reads back as `Job.rating`\n * and filters {@link list}. OAuth apps need the `assets:write` scope.\n */\n rate(id: string, rating: JobRating | null): Promise<RateJobResult> {\n return this.client.request<RateJobResult>(\"PUT\", `/v1/jobs/${encodeURIComponent(id)}/rating`, {\n body: { rating },\n })\n }\n\n /**\n * Get the lean status of a single job (poll-loop friendly).\n * Hits `GET /v1/jobs/:id/status` — returns only id/status/progress/\n * output_data/error_message, with far less wire/CPU cost than `get()`.\n * Same auth + ownership semantics as {@link get}.\n */\n getStatus(id: string): Promise<{ data: JobStatusResult }> {\n return this.client.request(\n \"GET\",\n `/v1/jobs/${encodeURIComponent(id)}/status`,\n )\n }\n\n /**\n * Cancel a job. Server route is `POST /v1/jobs/:jobId/cancel`.\n * Refunds any reserved credit holds. A running LLM job priced per model\n * (metered) whose call has already started answers\n * `{ success: true, cancelled: 0, inFlight: true }`: it finishes and is\n * charged only what it used. A job stopped before its call starts is\n * refunded, except for what an earlier attempt of the same job already used\n * (a retry after a billed attempt): that is charged.\n */\n cancel(id: string): Promise<CancelJobResult> {\n return this.client.request(\n \"POST\",\n `/v1/jobs/${encodeURIComponent(id)}/cancel`,\n )\n }\n\n /**\n * Delete a job: it goes to the Trash (`client.trash.list()`). It is left out\n * of every read from then on, stays restorable for 30 days ({@link restore},\n * back into the sessions it was in), and is then deleted for good, as a\n * delete always was (a copy saved to the Library stays). `{ forever: true }` deletes a job already in the Trash for good now;\n * on a live job it fails with a 409 `NodaroError` whose `code` is\n * `\"not_in_trash\"`. Only the job's owner (or an admin) may delete it; a job\n * that is still running keeps running — cancel it first when its worker\n * should stop ({@link cancel}). Nothing is refunded. OAuth apps need the\n * `assets:write` scope.\n */\n delete(id: string, options: { forever?: boolean } = {}): Promise<DeleteJobResult> {\n return this.client.request(\n \"DELETE\",\n `/v1/jobs/${encodeURIComponent(id)}`,\n options.forever ? { query: { forever: \"true\" } } : {},\n )\n }\n\n /**\n * Bring a deleted job back from the Trash: it reappears in every read and in\n * every session it was in. A job that is not in the Trash succeeds too.\n * OAuth apps need the `assets:write` scope.\n */\n restore(id: string): Promise<{ success: true }> {\n return this.client.request(\"POST\", `/v1/jobs/${encodeURIComponent(id)}/restore`)\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/trends.ts"],"names":[],"mappings":"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","file":"chunk-DIF7A5OH.cjs","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 Lite),\n * `resolution_unsupported` (a `resolution` not in the trend's\n * `resolutions`), `too_many_images` (a Nodaro Motion Lite 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 * - 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-pro\"` (Nodaro Motion Pro) or `\"motion-lite\"` (Nodaro Motion Lite).\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-pro\"`. */\n engine?: TrendEngine\n /** Its name to show: \"Nodaro Motion Pro\" or \"Nodaro Motion Lite\". Absent from an older server. */\n engineName?: string\n /**\n * The resolutions a run may ask for, the default first. Nodaro Motion Pro:\n * `[\"480p\", \"720p\", \"1080p\"]`, where 480p is a draft (show it as one);\n * Nodaro Motion Lite: `[\"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 Pro, `\"720p\"` on Nodaro Motion Lite). Another answers 400\n * `resolution_unsupported`.\n */\n resolution?: TrendResolution\n /**\n * A draft run, only at 480p on a Nodaro Motion Pro trend. Default `true` at\n * 480p. `true` on a Nodaro Motion Lite 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-pro\"`. */\n engine?: TrendEngine\n /** Its name to show: \"Nodaro Motion Pro\" or \"Nodaro Motion Lite\". Absent from an older server. */\n engineName?: string\n /** The result, once completed. */\n videoUrl: string | null\n posterUrl: string | null\n creditsCharged: number | null\n /** A Full Quality version: the job id of the run (a draft, or a Nodaro Motion Lite 720p run) it was made from. `null` for any other run. */\n fullQualityOf: string | null\n /**\n * A draft's (or Nodaro Motion Lite 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 * Pro draft (the same take at 1080p) or a Nodaro Motion Lite 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/credits.ts"],"names":[],"mappings":";;;AAuNA,IAAM,iBAAA,GAAoB,EAAA;AAEnB,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,MAAM,OAAA,GAAgC;AACpC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA;AAAA,MAC5B,KAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAA,CAAa,OAAA,GAAqC,EAAC,EAAoC;AACrF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,KAAA,EAAO,0BAAA,EAA4B;AAAA,MACpF,OAAO,EAAE,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA;AAAO,KACvD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAAW,GAAA,EAA0C;AACnD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAA0B,MAAA,EAAQ,yBAAA,EAA2B;AAAA,MAC9E,MAAM,EAAE,MAAA,EAAQ,IAAI,KAAA,CAAM,CAAA,EAAG,iBAAiB,CAAA;AAAE,KACjD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,gBAAA,CAAiB,IAAA,GAAmB,EAAC,EAAoC;AACvE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,MAAA,EAAQ,gCAAA,EAAkC;AAAA,MAC3F,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,iBAAA,CAAkB,KAAA,GAAgC,EAAC,EAAqC;AACtF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAiC,MAAA,EAAQ,iCAAA,EAAmC;AAAA,MAC7F,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,oBAAA,GAAwD;AACtD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgC,MAAA,EAAQ,gCAAgC,CAAA;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUF","file":"chunk-FIMBPKIH.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Authenticated user's credit balance — the shape of `GET /v1/user/credits`'s\n * `data` field. Mirrors the canonical `UserBalance` from the backend billing\n * module (`backend/src/ee/billing/credits.ts`), the source of truth: keep this\n * in sync if that interface changes.\n */\nexport interface UserBalance {\n total: number\n subscription: number\n topup: number\n dailySpent: number\n dailyLimit: number | null\n monthlyAllocation: number\n /** Stored billing tier. Display should prefer `effectiveTier`. */\n tier: string\n /** Derived entitlement tier: \"payg\" when stored-free with purchased credits. */\n effectiveTier: string\n features: Record<string, unknown>\n periodEnd: string | null\n /** Credits earned for app usage (free tier only — earned by running flows). */\n appCreditsAllowance: number\n /** Present when a deployment uses an external shared wallet. Raw credits;\n * null means unavailable. Do not substitute the personal `total` balance. */\n externalWallet?: { available: number | null }\n /**\n * Where the account's free signup grant stands. `\"withheld\"` means the\n * account works but the grant did not land — show the activation path.\n * Absent where the deployment has no signup grant.\n */\n freeGrantState?: FreeGrantState\n /**\n * Present only with `freeGrantState: \"withheld\"`, when the claim showed no\n * browser signals at all: the app may say \"free credits need a regular\n * browser — reload\", because a reload in one judges the account again.\n */\n freeGrantNotice?: \"browser_unmeasured\"\n /**\n * The welcome-credits opt-in. PRESENT only while the deployment has the\n * offer switched on — absent means \"no popup, no banner, no block\".\n */\n welcomeOffer?: WelcomeOfferState\n}\n\n/** Where an account's free signup grant stands. */\nexport type FreeGrantState = \"unclaimed\" | \"granted\" | \"withheld\" | \"revoked\"\n\n/** `UserBalance.welcomeOffer` — what an app needs to show the welcome offer. */\nexport interface WelcomeOfferState {\n /** The one-time popup was already shown (see `markWelcomeOfferSeen`). */\n popupSeen: boolean\n /**\n * The account already has its credits (granted elsewhere) but still owes\n * the email consent: the web apps block creation until it is given — the\n * claim records it.\n */\n consentPending: boolean\n /** The credits a claim grants. Name this figure in the offer; never hard-code it. */\n credits: number\n}\n\n/** What `claimWelcomeOffer` sends: the browser signals plus the app answering. */\nexport interface WelcomeOfferClaimInput extends SignupKeys {\n /**\n * The answering app's slug (lowercase letters, digits and `-`, up to 40\n * characters), recorded with the consent. A value outside that grammar is\n * stored as absent, never refused.\n */\n sourceApp?: string\n /**\n * The interface language the user answered in — the id of a language Nodaro\n * ships, such as `\"en\"`, `\"ja\"` or `\"pt-BR\"`. Matched exactly and\n * case-sensitively: `\"en-US\"` or `\"pt-br\"` is stored as absent, never refused.\n * Recorded with the consent as the language of the LATEST consent: a later\n * \"yes\" in another language replaces it.\n */\n locale?: string\n}\n\n/** The answer of `POST /v1/credits/welcome-offer/claim`. */\nexport interface WelcomeOfferClaimResult {\n /** The email consent is recorded. */\n consent: \"granted\"\n /** Where the free grant stands after the claim — `\"withheld\"` when the abuse gate refused it. */\n grant: FreeGrantState\n /** The grant's figure. A grant tops the subscription credits UP TO it (it is not added on top), so a claim that lands it leaves the subscription credits at no less than this. */\n credits: number\n}\n\n/** The answer of `POST /v1/credits/welcome-offer/seen`. */\nexport interface WelcomeOfferSeenResult {\n ok: true\n}\n\n/**\n * Result of `POST /v1/credits/model-costs` — a batch cost lookup for editor\n * cost previews. `data` maps each priced identifier → its credit cost.\n *\n * Per-model fault isolation (the route runs the lookups under\n * `Promise.allSettled`): identifiers with no pricing row are reported in\n * `missing` (undisplayable until an operator seeds a price) and lookup\n * failures in `errors`, instead of failing the whole batch. Callers typically\n * render `'—'` for any identifier that lands in `missing`. The hard-fail\n * policy still triggers at reservation time when the user actually runs the\n * node — this preview lookup is intentionally lenient.\n */\nexport interface ModelCostsResult {\n data: Record<string, number>\n missing: string[]\n errors: string[]\n /**\n * Why each identifier in `errors` has no price — today only\n * `llm_model_unpriced`: a metered LLM price key whose model has no rate on\n * the lane it would be served on (it cannot be run). Omitted when `errors`\n * is empty.\n */\n errorCodes?: Record<string, ModelCostError>\n /**\n * The identifiers in `data` whose figure is an ESTIMATE, not a fixed price:\n * metered LLM price keys (`llmPriceKey` from `@nodaro/shared/llm-models`,\n * `<feature>:<model>[:direct]`), charged on what the call actually uses.\n * Show these as `≈N`. Omitted when there are none, so a batch of fixed ids\n * answers exactly as it always has. A metered key that lands in `missing`\n * instead means the server is not pricing LLM calls per model — show no\n * price for it.\n */\n estimates?: string[]\n}\n\n/** {@link ModelCostsResult.errorCodes} — why an identifier has no price. */\nexport interface ModelCostError {\n /** Today only `llm_model_unpriced`; open to codes a newer server may add. */\n code: \"llm_model_unpriced\" | (string & {})\n /** A sentence to show the user. */\n message: string\n}\n\n/**\n * Result of `GET /v1/credits/model-cost?model=<id>` (`data`): the credits one\n * identifier is charged. `estimate: true` marks a metered LLM price key's\n * figure as an estimate (show it as `≈N`); such an answer is never cached.\n */\nexport interface ModelCostResult {\n model: string\n creditCost: number\n estimate?: true\n}\n\n/**\n * The two browser signals a claim reports — SHA-256 hex digests computed by\n * `@nodaro/sdk/signup-signals` (`collectSignupKeys`). Either may be absent:\n * a browser that cannot be measured still claims, and the server treats the\n * absence as what it is. Any other value is stored as absent server-side.\n */\nexport interface SignupKeys {\n browserKey?: string\n deviceKey?: string\n}\n\n/** The answer of `POST /v1/credits/claim-signup-grant`. */\nexport interface SignupGrantClaimResult {\n /** Where the account's free grant stands after this call. */\n state: FreeGrantState\n /** True only when THIS call moved the credits — a repeat is `false` with the same state. */\n granted: boolean\n}\n\n/**\n * One row of `GET /v1/credits/transactions` — a run's reservation and how it\n * settled.\n *\n * Which amount to read: a run first HOLDS credits and is then settled — charged\n * all or part of the hold, the rest returned. `credits_charged` is what the run\n * cost (`null` while the hold is still open, `0` once refunded); sum it for\n * spend. `credits_reserved` is the hold, which never changes. `credits_used`\n * is the same hold under its original name, kept for existing integrations.\n */\nexport interface CreditTransaction {\n id: string\n /** ISO-8601. Also the paging cursor: pass the last row's value as `cursor`. */\n created_at: string\n /** Settled charge; `null` while `status` is `\"reserved\"`, `0` when refunded. */\n credits_charged: number | null\n /** The hold taken when the run was accepted. */\n credits_reserved: number | null\n /** The hold, under its original name. Prefer `credits_charged`. */\n credits_used: number | null\n /** The model or operation the credits were for. */\n action: string | null\n provider: string | null\n /** Reservation lifecycle: `\"reserved\"`, `\"committed\"` or `\"refunded\"`. */\n status: string | null\n /** The caller's own billing details for the run (model, which pool paid, …). */\n metadata: Record<string, unknown>\n /** `\"workspace\"` when a class or team budget paid, not your balance. */\n payer: \"user\" | \"workspace\"\n workspaceId: string | null\n}\n\n/** A page of {@link CreditTransaction}s, newest first. */\nexport interface CreditTransactionsPage {\n data: CreditTransaction[]\n /** Pass as `cursor` for the next page; `null` when there are no more rows. */\n nextCursor: string | null\n}\n\nexport interface CreditTransactionsOptions {\n /** Rows per page, 1–50 (server default 20). */\n limit?: number\n /** The `nextCursor` of the previous page. */\n cursor?: string\n}\n\n/** Max identifiers per `model-costs` batch — the route caps the request at 50. */\nconst MODEL_COSTS_LIMIT = 50\n\nexport class CreditsResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * `GET /v1/user/credits` → the authenticated user's credit balance and tier\n * info. Throws `UnauthorizedError` (401) when signed out, and the SDK's\n * other typed errors on the usual statuses.\n */\n async balance(): Promise<UserBalance> {\n const res = await this.client.request<{ data: UserBalance }>(\n \"GET\",\n \"/v1/user/credits\",\n )\n return res.data\n }\n\n /**\n * `GET /v1/credits/transactions` → one page of the caller's credit\n * transactions, newest first, with `nextCursor` for the next page (`null`\n * at the end). Read `credits_charged` for what each run cost — it can be\n * below the hold (`credits_reserved`) and is `null` while the hold is open.\n * Cloud-only route: a self-hosted instance answers 404.\n */\n transactions(options: CreditTransactionsOptions = {}): Promise<CreditTransactionsPage> {\n return this.client.request<CreditTransactionsPage>(\"GET\", \"/v1/credits/transactions\", {\n query: { limit: options.limit, cursor: options.cursor },\n })\n }\n\n /**\n * `POST /v1/credits/model-costs` → per-identifier credit cost, for editor\n * cost previews. Capped at the first {@link MODEL_COSTS_LIMIT} identifiers\n * (the route's request limit). Preserves the `{ data, missing, errors }`\n * fault-isolation shape verbatim, plus `estimates` / `errorCodes` when a\n * metered LLM price key was asked (see {@link ModelCostsResult}).\n */\n modelCosts(ids: string[]): Promise<ModelCostsResult> {\n return this.client.request<ModelCostsResult>(\"POST\", \"/v1/credits/model-costs\", {\n body: { models: ids.slice(0, MODEL_COSTS_LIMIT) },\n })\n }\n\n /**\n * `POST /v1/credits/claim-signup-grant` → claims the account's free signup\n * grant, once, with the browser signals of the page making the claim.\n *\n * Call it once per page load as soon as the session is known, and never\n * wait on it for rendering: it is idempotent (a decided account answers\n * with its state and `granted: false`), it costs the user nothing when it\n * fails (the next load asks again), and the signals are what make the\n * claim worth sending — collect them with `collectSignupKeys()` from\n * `@nodaro/sdk/signup-signals` and pass whatever came back, `{}` included.\n * Cloud-only route: a self-hosted instance answers 404.\n */\n claimSignupGrant(keys: SignupKeys = {}): Promise<SignupGrantClaimResult> {\n return this.client.request<SignupGrantClaimResult>(\"POST\", \"/v1/credits/claim-signup-grant\", {\n body: keys,\n })\n }\n\n /**\n * `POST /v1/credits/welcome-offer/claim` → the user's \"yes\" to the welcome\n * offer, as ONE server action: the marketing-email consent is recorded,\n * then the free signup grant is claimed with that consent. The abuse gate\n * still decides — consent is necessary, not sufficient — so read `grant`:\n * `\"granted\"` (the grant has landed: this call topped the subscription credits up to `credits` — not added on top — or it had landed before), `\"withheld\"` (refused), `\"revoked\"` (taken back earlier; nothing moves), `\"unclaimed\"` (nothing moved — treat as a failure and let the user retry).\n * Pass the browser signals from `collectSignupKeys()` and your app's slug.\n *\n * **Browser session only.** Usable from a first-party web app signed in\n * with a Supabase session (the consent is a person's choice in a browser);\n * an API key or OAuth app token is refused with 403. 409\n * `welcome_offer_disabled` while the deployment has the offer off — show\n * it only when `balance().welcomeOffer` is present. Cloud-only route: a\n * self-hosted instance answers 404.\n */\n claimWelcomeOffer(input: WelcomeOfferClaimInput = {}): Promise<WelcomeOfferClaimResult> {\n return this.client.request<WelcomeOfferClaimResult>(\"POST\", \"/v1/credits/welcome-offer/claim\", {\n body: input,\n })\n }\n\n /**\n * `POST /v1/credits/welcome-offer/seen` → records that the one-time welcome\n * popup was shown, so `balance().welcomeOffer.popupSeen` turns true on every\n * app. Idempotent (the first stamp wins); a failed call only means the popup\n * may show once more, so callers usually ignore errors.\n *\n * **Browser session only**, like {@link claimWelcomeOffer}: 403 for an API\n * key or OAuth app token, 409 `welcome_offer_disabled` while the offer is\n * off, 404 on a self-hosted instance.\n */\n markWelcomeOfferSeen(): Promise<WelcomeOfferSeenResult> {\n return this.client.request<WelcomeOfferSeenResult>(\"POST\", \"/v1/credits/welcome-offer/seen\")\n }\n\n // NOTE: no `estimate(...)` helper. The backend exposes\n // `POST /v1/credits/estimate-workflow` (body `{ nodes }` → `{ data: {\n // totalCredits, nodeCount, estimate?: true } }` — `estimate` when a metered\n // LLM node's estimate is in the total), but no consumer has a settled shape for it\n // yet (studio pre-checks `balance >= Σ modelCosts` client-side rather than\n // calling an estimate endpoint). Adding it now would be inventing an API\n // surface ahead of a real caller, so it's deliberately omitted — add it when\n // a consumer needs it, shaped to that need.\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/cast-swap.ts"],"names":[],"mappings":";;;AAidO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,GAAA,CAAI,QAAwB,OAAA,EAA6E;AACvG,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,eAAA,EAAiB;AAAA,MAClD,IAAA,EAAM,MAAA;AAAA,MACN,GAAI,OAAA,EAAS,cAAA,GAAiB,EAAE,OAAA,EAAS,EAAE,iBAAA,EAAmB,OAAA,CAAQ,cAAA,EAAe,EAAE,GAAI;AAAC,KAC7F,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,MAAA,EAAmD;AAC1D,IAAA,OAAO,IAAA,CAAK,OAAO,OAAA,CAAQ,MAAA,EAAQ,0BAA0B,EAAE,IAAA,EAAM,QAAQ,CAAA;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,oBAAoB,KAAA,EAAqD;AACvE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,CAAA,cAAA,EAAiB,kBAAA,CAAmB,KAAK,CAAC,CAAA,sBAAA,CAAA,EAA0B,EAAE,IAAA,EAAM,IAAI,CAAA;AAAA,EACrH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,WAAA,CAAY,KAAA,EAAe,MAAA,GAAoC,EAAC,EAAuC;AACrG,IAAA,MAAM,IAAA,GAAO,OAAO,SAAA,KAAc,MAAA,GAAY,EAAE,SAAA,EAAW,MAAA,CAAO,SAAA,EAAU,GAAI,EAAC;AACjF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,CAAA,cAAA,EAAiB,kBAAA,CAAmB,KAAK,CAAC,CAAA,aAAA,CAAA,EAAiB,EAAE,IAAA,EAAM,CAAA;AAAA,EACxG;AACF","file":"chunk-GDDV5U32.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Nodaro Motion (the `castSwap` resource; routes `/v1/cast-swap`) — replace\n * the people, pets, products and/or the location in a short video with your\n * own references, keeping its motion, framing, timing and sound (the clip's\n * audio is kept, and the result is the clip's length).\n *\n * Before the video, each person, pet and product role's photos are turned\n * into one clean reference picture by an image model. This costs extra\n * credits (`breakdown.references` in the estimate), charged when it runs, even\n * if the video then fails; it adds about a minute, falls back to your own\n * photos if it fails, and is turned off with `prepareReferences: false`.\n * Locations are not prepared.\n *\n * Engines: a run is made on Nodaro Motion Pro (`engine` left out, or\n * `\"motion-pro\"`) or Nodaro Motion Lite (`\"motion-lite\"`). Lite renders 720p\n * (its default) or 1080p and has no draft; it makes a new take guided by the\n * clip and pictures, so its Full Quality is a new take at 1080p rather than\n * the same take re-rendered (`CastSwapFullQualityEstimate.kind`).\n *\n * How the clip is prepared for the video model (for one, whether faces are\n * hidden first, and whether a refused clip is retried) is set by Nodaro for\n * each engine; a run cannot change it. `faceMasking` is no longer read\n * (deprecated: accepted and ignored).\n *\n * Cloud edition only: `/v1/cast-swap` is served by the cloud plugin and 404s\n * on self-hosted installs. `run()` answers 202 with a job id; follow it with\n * `client.jobs.get(jobId)` / `client.jobs.getStatus(jobId)` like any other job.\n *\n * Repeats: pass `{ idempotencyKey }` to `run()` and it is sent as the\n * `Idempotency-Key` header. A repeat with the same key within the credit\n * guard's dedup window (about 10 s) answers 200 `{ jobId, deduped: true }` with\n * an `X-Dedup-Hit` header; a later one answers 202 with the first run's\n * `jobId`. Nothing new is started or charged.\n *\n * Refusals (thrown as a `NodaroError` subclass; body `{ error: { code, message } }`), before\n * anything is charged: 400 `validation_error` (the body: no role with images,\n * no reference and no `prePrompt` or `postPrompt`, more roles of a kind\n * than allowed, more than 30 images in all, `draft: true` above 480p, a\n * `prePrompt` or `postPrompt` over 4,000 characters); on Nodaro Motion Lite,\n * `draft_unsupported` (`draft: true`), `resolution_unsupported` (480p),\n * `output_format_unsupported` (a MOV edit output) and `too_many_images` (more\n * than 10 images in the run, across every role and fixed reference);\n * `raw_reference_token` (also in a reference's `line`),\n * `invalid_role_mention` (also any `{role:N}` in a reference's `line`),\n * `unsafe_url`, `video_unreadable`, `clip_out_of_range` (4–30 s),\n * `preview_out_of_range`, `no_roles_in_preview` (a preview with every role\n * `inPreview: false`, always refused even with a prompt, or with no role kept\n * in it that has images and no `prePrompt` or `postPrompt`), `prompt_too_long`\n * (the prompt would build too long: a `{role:N}` reads as the role's name when\n * it sends no images; Nodaro Motion Lite's cap is 8,000 characters); 401; 402 `insufficient_credits` (`run()` only); 404\n * (a `sessionId` you may not use, `run()` only); 422 `job_blocked` (`run()`\n * only); 503 `host_too_old` (both); 503 `estimate_unavailable` (`estimate()`\n * only).\n */\n\n/**\n * The engine a Nodaro Motion run is made on, by its public id. Left out =\n * `\"motion-pro\"`.\n *\n * - `\"motion-pro\"` (Nodaro Motion Pro): 480p (the default, a draft unless\n * `draft: false`), 720p or 1080p; Full Quality re-renders a draft's own take\n * at 1080p.\n * - `\"motion-lite\"` (Nodaro Motion Lite): 720p (the default) or 1080p, never a\n * draft (`draft: true` answers 400 `draft_unsupported`); Full Quality makes a\n * new take at 1080p from a completed 720p run's inputs. A preview is still\n * 4 s from `previewStart`.\n */\nexport type CastSwapEngine = \"motion-pro\" | \"motion-lite\"\n\n/**\n * The engine ids an older client sent (`\"seedance-2-5\"` = `\"motion-pro\"`,\n * `\"kling-4\"` = `\"motion-lite\"`). Still accepted in a request; never returned.\n * @deprecated use {@link CastSwapEngine}.\n */\nexport type CastSwapEngineLegacyId = \"seedance-2-5\" | \"kling-4\"\n\n/**\n * Where a Nodaro Motion Lite run cuts the clip to whole seconds (Lite renders\n * whole seconds; asked for more than the clip, it would stretch the motion):\n * - `\"cut-end\"` (the default): drop the fraction of a second at its end;\n * - `\"cut-start\"`: drop it at its start.\n */\nexport type CastSwapMotionLength = \"cut-end\" | \"cut-start\"\n\n/** @deprecated use {@link CastSwapMotionLength}. */\nexport type CastSwapKlingLength = CastSwapMotionLength\n\n/** Whether a swapped subject takes this trait from the references (`\"new\"`) or keeps the video's (`\"original\"`). */\nexport type CastSwapTraitSource = \"new\" | \"original\"\n\n/**\n * Preview-only settings, on every role kind; a full run (`preview` not `true`)\n * ignores both.\n */\nexport interface CastSwapRolePreviewSettings {\n /** The name used instead of `name` in a preview. Same rules as `name` (1–200 characters). */\n previewName?: string\n /**\n * `false` leaves this role out of the preview: its reference images are not\n * sent, not prepared and not charged, and a `{role:N}` naming it reads as its\n * `previewName`, else its `name`. Default `true`. A preview with every role\n * left out (always refused, even with a prompt), or with no role kept in it\n * that has images and no pre / post prompt, answers 400 `no_roles_in_preview`.\n */\n inPreview?: boolean\n}\n\n/** A person in the video to recast. */\nexport interface CastSwapPersonRole extends CastSwapRolePreviewSettings {\n kind: \"person\"\n /** How the person is told apart in the video, e.g. `\"the woman that attacks\"`. */\n name: string\n /** 0–3 images of ONE person. Empty or `null` keeps the original person. */\n images: string[] | null\n /** Body from the references or the video. Default `\"new\"`. */\n body?: CastSwapTraitSource\n /** Outfit from the references or the video. Default `\"new\"`. */\n outfit?: CastSwapTraitSource\n /** Hairstyle and haircut from the references or the video. Default `\"new\"`. */\n hair?: CastSwapTraitSource\n}\n\n/** The place the video happens in. */\nexport interface CastSwapLocationRole extends CastSwapRolePreviewSettings {\n kind: \"location\"\n /** How the place is named. Default `\"the setting\"`. */\n name?: string\n /** 0–3 images of ONE place. Empty or `null` keeps the original place. */\n images: string[] | null\n}\n\n/** An animal in the video to recast. */\nexport interface CastSwapPetRole extends CastSwapRolePreviewSettings {\n kind: \"pet\"\n /** How the animal is told apart in the video, e.g. `\"the dog\"`. */\n name: string\n /** 0–3 images of ONE animal. Empty or `null` keeps the original animal. */\n images: string[] | null\n /** Body (shape, size and build) from the references or the video. Default `\"new\"`. */\n body?: CastSwapTraitSource\n /** Coat (fur, colors and markings) from the references or the video. Default `\"new\"`. */\n coat?: CastSwapTraitSource\n}\n\n/** An object in the video to recast. */\nexport interface CastSwapProductRole extends CastSwapRolePreviewSettings {\n kind: \"product\"\n /** How the object is told apart in the video, e.g. `\"the can\"`. */\n name: string\n /** 0–3 images of ONE product. Empty or `null` keeps the original object. */\n images: string[] | null\n /** Shape and proportions from the references or the video. Default `\"new\"`. */\n shape?: CastSwapTraitSource\n /** Finish (colors, materials, label and markings) from the references or the video. Default `\"new\"`. */\n finish?: CastSwapTraitSource\n}\n\nexport type CastSwapRole = CastSwapPersonRole | CastSwapLocationRole | CastSwapPetRole | CastSwapProductRole\n\n/**\n * A fixed reference: 1–3 images of ONE thing that no role replaces, and the\n * line that says what it is (`\"the glass she drinks from\"`). Sent as given,\n * never prepared; its images count toward the 30.\n */\nexport interface CastSwapReference {\n /** 1–3 http(s) image URLs of one thing. */\n images: string[]\n /**\n * What it is, 1–500 characters. `{image}` in the line stands for this\n * reference's first image (`\"Use {image} for the sky\"`); a line without it\n * gets it appended. A `{role:N}` or a raw `{image:` / `{video:` / `{audio:`\n * token answers 400.\n */\n line: string\n}\n\n/**\n * @deprecated Nodaro sets this per engine; a run's `faceMasking` is ignored.\n */\nexport interface CastSwapFaceMaskingZone {\n leftPct?: number\n rightPct?: number\n topPct?: number\n bottomPct?: number\n}\n\n/**\n * @deprecated Nodaro sets how the clip is prepared for the video model, per\n * engine; a run cannot change it. A `faceMasking` sent is accepted and\n * ignored (no error), so older code keeps working unchanged.\n */\nexport interface CastSwapFaceMasking {\n mode: \"off\" | \"always\" | \"auto\"\n fill?: \"blur\" | \"depth\"\n minFaceHeightPct?: number\n maxFaceHeightPct?: number\n zone?: CastSwapFaceMaskingZone | null\n blurStrengthPct?: number\n threshold?: number\n}\n\n/** The body of `POST /v1/cast-swap` and `POST /v1/cast-swap/estimate`. */\nexport interface CastSwapParams {\n /** The source clip, 4–30 seconds. */\n videoUrl: string\n /**\n * Who and what to swap: at most 6 person roles, 1 location role, 3 pet roles\n * and 3 product roles, with at most 30 images in all. A role with no images\n * is skipped (that subject or place stays as in the video). At least one role\n * with images, or a `prePrompt` or `postPrompt`, is required.\n */\n roles: CastSwapRole[]\n /**\n * Fixed references (at most 5): things the video should show that no role\n * replaces, each with the line that names it. Their images count toward the\n * 30. A reference alone is enough for a run (no role needs images).\n */\n references?: CastSwapReference[]\n /**\n * Your own words placed at the start of the prompt, before the role\n * instructions; at most 4,000 characters. Mention a role as `{role:N}`, as in\n * `postPrompt`; the same 400 refusals apply (an N outside 1..`roles.length`,\n * a raw `{image:` / `{video:` / `{audio:` token).\n */\n prePrompt?: string\n /**\n * Extra instructions in your own words, at most 4,000 characters. Mention a\n * role as `{role:N}`, where N is its 1-based position in `roles`\n * (`{role:1}` is `roles[0]`; empty roles count).\n *\n * The route answers 400, before charging, for an N outside 1..`roles.length`,\n * for a raw `{image:` / `{video:` / `{audio:` token in the pre or post prompt\n * or a role's `name`, for a clip outside 4–30 s, and for a preview past the clip's\n * end (`previewStart + 4` > its length).\n */\n postPrompt?: string\n /** `true` runs on 4 seconds of the clip, starting at `previewStart`: a cheap check of the roles and choices. */\n preview?: boolean\n /** Where the preview starts, in seconds. Default 0. */\n previewStart?: number\n /**\n * The engine (see {@link CastSwapEngine}). Default `\"motion-pro\"`. The job\n * records it in `input_data.engine`, and a completed job's output in its\n * `engine`. The ids an older client sent ({@link CastSwapEngineLegacyId})\n * are still accepted.\n */\n engine?: CastSwapEngine | CastSwapEngineLegacyId\n /**\n * Nodaro Motion Lite only (Pro ignores it): which end of the clip is cut to\n * whole seconds (see {@link CastSwapMotionLength}). Default `\"cut-end\"`.\n */\n motionLength?: CastSwapMotionLength\n /** @deprecated use {@link motionLength} (still accepted). */\n klingLength?: CastSwapKlingLength\n /**\n * The output resolution. Nodaro Motion Pro: `\"480p\"` (default), `\"720p\"` or\n * `\"1080p\"`. Nodaro Motion Lite: `\"720p\"` (default) or `\"1080p\"`; `\"480p\"`\n * answers 400.\n */\n resolution?: \"480p\" | \"720p\" | \"1080p\"\n /**\n * Render as a draft (Nodaro Motion Pro only). Defaults to `true` at 480p and\n * `false` above it; `true` with `\"720p\"` or `\"1080p\"` answers 400 (draft is\n * 480p only). Nodaro Motion Lite has no draft: `true` answers 400\n * `draft_unsupported`.\n */\n draft?: boolean\n /**\n * Turn each person, pet and product role's photos into one clean reference\n * picture before the video (extra credits, shown in `breakdown.references`;\n * about a minute longer; falls back to your own photos if it fails).\n * Default `true`.\n */\n prepareReferences?: boolean\n /**\n * Ignored: Nodaro sets how the clip is prepared, per engine. Accepted (no\n * error) so older code keeps working.\n * @deprecated leave it out.\n */\n faceMasking?: CastSwapFaceMasking\n /**\n * `\"draft\"` means 480p draft.\n * @deprecated use `resolution` + `draft`.\n */\n quality?: \"draft\"\n /** A generation session to file the result in. */\n sessionId?: string\n /** The client app that sent the run (stored on the job as `input_data.origin`). */\n origin?: string\n}\n\n/** What a run would cost, in credits. */\nexport interface CastSwapEstimate {\n /**\n * The engine the run would use, by its public id (absent from an older\n * server = `\"motion-pro\"`).\n */\n engine?: CastSwapEngine\n /**\n * What is held when the run starts. The run is charged what it actually\n * costs, which is expected to be `expectedCredits`.\n */\n totalCredits: number\n /**\n * The charge the run is expected to come to. Never above `totalCredits`\n * (on Nodaro Motion Lite it equals it: Lite renders exactly the length\n * asked for, so the hold is what it settles at). Absent from an older\n * server: read `totalCredits` then.\n */\n expectedCredits?: number\n /**\n * The parts of the quote, by name, for display only:\n * - `base`: the run itself (its base fee). A server before Cast Swap 0.396.0\n * names it `castSwap` instead: read `base`, else `castSwap`;\n * - `references`: preparing the reference pictures (0 when\n * `prepareReferences` is off or there is nothing to prepare);\n * - `motion`: what the video holds (on Nodaro Motion Lite, what it costs);\n * - `motionExpected`: what the video is expected to be charged, never above\n * `motion` (Nodaro Motion Pro);\n * - `privacy`: preparing the clip, when the engine's setting does. In both\n * totals when it always runs; outside both when it is the retry\n * (`privacyCredits`).\n *\n * `motion` and `motionExpected` are alternatives, so the parts sum to more\n * than either total: read `totalCredits` and `expectedCredits`, never the\n * sum. Other parts may appear.\n */\n breakdown: {\n base?: number\n /** @deprecated read {@link base} (an older server answered this name for the same part). */\n castSwap?: number\n references: number\n motion?: number\n motionExpected?: number\n privacy?: number\n } & Record<string, number>\n /**\n * The extra credits charged only if the video model refuses the clip and\n * the run is retried (Nodaro's setting for the engine decides whether it\n * does). Not in `totalCredits` or `expectedCredits`; `run()` needs credits\n * for both the hold and this (else 402).\n */\n privacyCredits?: number\n /** @deprecated read {@link privacyCredits} (an older server answered this name). */\n maskingCredits?: number\n /**\n * `true` when the video is made at Auto length (every Nodaro Motion Pro\n * run): the final charge is expected to come out lower than `totalCredits`,\n * at about `expectedCredits`. `false` on Nodaro Motion Lite.\n */\n settlesLower: boolean\n}\n\n/** Per-call options for {@link CastSwapResource.run}. */\nexport interface CastSwapRunOptions {\n /**\n * Sent as the `Idempotency-Key` header, unchanged (the SDK checks nothing;\n * an empty string sends no header, like no key).\n * The server takes 8–200 characters and answers 400 for any other length.\n * Reuse the same value when retrying the same run: within about 10 s the\n * repeat answers 200 `{ jobId, deduped: true }`, later 202 with the first\n * run's `jobId`. Nothing new is started or charged.\n */\n idempotencyKey?: string\n}\n\n/**\n * What a Full Quality version would be: `\"same-take\"` re-renders a Nodaro\n * Motion Pro draft's own take at 1080p; `\"new-take\"` makes a new Nodaro Motion Lite take at\n * 1080p from a 720p run's inputs (it will not match the 720p run frame for\n * frame). The 720p run or draft stays either way.\n */\nexport type CastSwapFullQualityKind = \"same-take\" | \"new-take\"\n\n/**\n * What a Full Quality version of a run would cost\n * (`POST /v1/cast-swap/:jobId/full-quality/estimate`): a Nodaro Motion Pro\n * draft, or a completed Nodaro Motion Lite run at 720p.\n */\nexport interface CastSwapFullQualityEstimate {\n /**\n * Which Full Quality this is ({@link CastSwapFullQualityKind}), so it can\n * be worded right. Absent from an older server: `\"same-take\"`.\n */\n kind?: CastSwapFullQualityKind\n /** The quote in credits: `breakdown.base` plus the 1080p render (same take) or the new take. */\n totalCredits: number\n /**\n * The parts of the quote, by name, for display only: `base` (the Full\n * Quality job's own steps; a server before Cast Swap 0.396.0 names it\n * `castSwap` instead: read `base`, else `castSwap`), and `motionHd` (a same\n * take: the 1080p render, 0 when an earlier try of this draft already\n * finished that render, which is then reused) or `motion` (a new take).\n */\n breakdown: {\n base?: number\n /** @deprecated read {@link base} (an older server answered this name for the same part). */\n castSwap?: number\n motionHd?: number\n motion?: number\n } & Record<string, number>\n /** ISO time until which the run can be finished at Full Quality (7 days after it completed). */\n availableUntil: string\n}\n\n/** Options for {@link CastSwapResource.fullQuality}. */\nexport interface CastSwapFullQualityParams {\n /** A generation session to file the Full Quality result in. */\n sessionId?: string\n}\n\n/** The Full Quality job (`POST /v1/cast-swap/:jobId/full-quality`). */\nexport interface CastSwapFullQualityResult {\n jobId: string\n /** `true` when this run's Full Quality was already running or done: that job is answered, nothing new is started or charged. */\n existing?: boolean\n}\n\n/**\n * The `code` of a Full Quality refusal (`NodaroError.code`), all before\n * anything is charged:\n * - `unauthorized` (401): no authenticated user;\n * - `not_found` (404): not your job, or not a Nodaro Motion run; also a\n * `sessionId` you may not use;\n * - `not_completed` (409): the run has not finished;\n * - `not_a_draft` (409): the run is neither a Nodaro Motion Pro draft nor a\n * Nodaro Motion Lite run at 720p (a Lite run at 1080p is already Full Quality);\n * - `draft_expired` (410): more than 7 days after the draft completed, or\n * the draft can no longer be finished at Full Quality;\n * - `full_quality_pending` (409, `details.retryable: true`): not final — an\n * earlier try's 1080p render of this draft is being reviewed, or has not\n * started yet; try again later (once the review ends). The only refusal\n * that ends on its own;\n * - `full_quality_unavailable` (409): final — nothing kept to re-render, no\n * readable completion time, or the 1080p finalize refuses this draft; on\n * the start only, also \"failed too many times\" (every try failed) or no\n * try can be started for this run;\n * - `insufficient_credits` (402), or in a workspace `budget_exceeded`,\n * `member_cap_exceeded` or `user_allowance_exceeded`;\n * - `job_blocked` (422): the request was blocked by the job policy;\n * - `job_read_failed` (503, retryable), `estimate_unavailable` (503, try\n * again shortly), `host_too_old` (503).\n *\n * Open-ended: a code not listed here may still arrive.\n */\nexport type CastSwapFullQualityRefusalCode =\n | \"unauthorized\"\n | \"not_found\"\n | \"not_completed\"\n | \"not_a_draft\"\n | \"draft_expired\"\n | \"full_quality_pending\"\n | \"full_quality_unavailable\"\n | \"insufficient_credits\"\n | \"budget_exceeded\"\n | \"member_cap_exceeded\"\n | \"user_allowance_exceeded\"\n | \"job_blocked\"\n | \"job_read_failed\"\n | \"estimate_unavailable\"\n | \"host_too_old\"\n | (string & {})\n\nexport class CastSwapResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * Start a Nodaro Motion run (`POST /v1/cast-swap`, 202 `{ jobId }`). Returns the\n * job to follow. With `options.idempotencyKey` (sent as the\n * `Idempotency-Key` header; 8–200 characters, else 400) a repeat with the\n * same key within about 10 s answers 200 `{ jobId, deduped: true }`, a later\n * one 202 with the first run's `jobId`; nothing new starts.\n */\n run(params: CastSwapParams, options?: CastSwapRunOptions): Promise<{ jobId: string; deduped?: boolean }> {\n return this.client.request(\"POST\", \"/v1/cast-swap\", {\n body: params,\n ...(options?.idempotencyKey ? { headers: { \"Idempotency-Key\": options.idempotencyKey } } : {}),\n })\n }\n\n /** Quote a run before starting it (`POST /v1/cast-swap/estimate`); spends nothing. */\n estimate(params: CastSwapParams): Promise<CastSwapEstimate> {\n return this.client.request(\"POST\", \"/v1/cast-swap/estimate\", { body: params })\n }\n\n /**\n * Quote finishing a run at Full Quality (`POST\n * /v1/cast-swap/:jobId/full-quality/estimate`); spends nothing. `jobId` is a\n * finished Nodaro Motion Pro DRAFT run, or a finished Nodaro Motion Lite\n * run at 720p (a Nodaro Motion or trend run); the quote's `kind` says whether it is the same take\n * re-rendered or a new take. The quote\n * does not look for a Full Quality already running: when one is,\n * {@link fullQuality} answers it (`existing: true`) and charges nothing.\n * Refusals: {@link CastSwapFullQualityRefusalCode}.\n */\n fullQualityEstimate(jobId: string): Promise<CastSwapFullQualityEstimate> {\n return this.client.request(\"POST\", `/v1/cast-swap/${encodeURIComponent(jobId)}/full-quality/estimate`, { body: {} })\n }\n\n /**\n * Finish a run at Full Quality (`POST /v1/cast-swap/:jobId/full-quality`):\n * at 1080p, with the clip's audio, as a new job linked to the run — a\n * Nodaro Motion Pro draft's own take re-rendered, or a new Nodaro Motion Lite take from a\n * 720p run's inputs (the quote's `kind`). SPENDS CREDITS: quote it first with\n * {@link fullQualityEstimate}. Answers 202 `{ jobId }`, or 200\n * `{ jobId, existing: true }` when this run's Full Quality is already\n * running or done (one per run; a failed one may be asked again). Follow\n * the job with `client.jobs.get(jobId)`. No `Idempotency-Key` is taken: the\n * server keys the job by the draft. The new job keeps the draft's origin.\n * Refusals: {@link CastSwapFullQualityRefusalCode}.\n */\n fullQuality(jobId: string, params: CastSwapFullQualityParams = {}): Promise<CastSwapFullQualityResult> {\n const body = params.sessionId !== undefined ? { sessionId: params.sessionId } : {}\n return this.client.request(\"POST\", `/v1/cast-swap/${encodeURIComponent(jobId)}/full-quality`, { body })\n }\n}\n"]}
@@ -1,50 +0,0 @@
1
- // src/resources/billing.ts
2
- var BillingResource = class {
3
- constructor(client) {
4
- this.client = client;
5
- }
6
- client;
7
- /**
8
- * `GET /v1/billing/plans` → the plans and credit packs on sale, with their
9
- * prices. Public: works with no credential, the same for every caller.
10
- * Nodaro Cloud only — a self-hosted instance answers 404. Throws on `403
11
- * self_serve_disabled` (this deployment does not sell to its users) and on
12
- * `503` (`stripe_not_configured`, or prices briefly unavailable).
13
- */
14
- plans() {
15
- return this.client.request("GET", "/v1/billing/plans");
16
- }
17
- /**
18
- * `GET /v1/billing/subscription` → the account's latest subscription
19
- * (`null` when it never subscribed) and the tier its entitlements follow.
20
- * Match `effectiveTier` to a {@link BillingPlan} `id` to mark the current
21
- * plan (no match: pay-as-you-go, or a tier not on sale). Needs a signed-in
22
- * session, a personal API token without a workflow limit, or an app's OAuth
23
- * token. Nodaro Cloud only — a self-hosted instance answers 404.
24
- */
25
- subscription() {
26
- return this.client.request("GET", "/v1/billing/subscription");
27
- }
28
- /**
29
- * `GET /v1/billing/surface` → how this deployment bills: whether it meters
30
- * at all (`providerId !== "none"`) and in which unit costs display. Public,
31
- * every edition.
32
- */
33
- async surface() {
34
- const res = await this.client.request("GET", "/v1/billing/surface");
35
- return res.data;
36
- }
37
- /**
38
- * `GET /v1/billing/account` → the caller's plan and balance as the
39
- * deployment's billing provider reports them, in its display unit. `null`
40
- * when the provider could not answer — not a zero balance. Every edition.
41
- */
42
- async account() {
43
- const res = await this.client.request("GET", "/v1/billing/account");
44
- return res.data;
45
- }
46
- };
47
-
48
- export { BillingResource };
49
- //# sourceMappingURL=chunk-J4WFSKY6.js.map
50
- //# sourceMappingURL=chunk-J4WFSKY6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/billing.ts"],"names":[],"mappings":";AA6KO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,KAAA,GAAqC;AACnC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAA4B,KAAA,EAAO,mBAAmB,CAAA;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,YAAA,GAAmD;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAmC,KAAA,EAAO,0BAA0B,CAAA;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAA,GAAmC;AACvC,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAkC,OAAO,qBAAqB,CAAA;AAC5F,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAA,GAAiD;AACrD,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgD,OAAO,qBAAqB,CAAA;AAC1G,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AACF","file":"chunk-J4WFSKY6.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Plans and prices, the subscription, and the billing surface — the shapes of\n * the `/v1/billing/*` routes an app may call. Mirrors the backend routes\n * (`backend/src/ee/routes/billing.ts`, `backend/src/routes/billing-surface.ts`),\n * the source of truth: keep these in sync if they change.\n *\n * Buying, changing the plan and the payment settings stay in the Nodaro app:\n * those routes refuse API and app tokens, so they have no method here.\n */\n\n/** A sum of money in its currency's major unit (`12` = 12.00), with its ISO-4217 code (`\"USD\"`). */\nexport interface BillingMoney {\n amount: number\n currency: string\n}\n\n/** The credits a price grants, and how often. */\nexport interface BillingPriceCredits {\n amount: number\n per: \"month\" | \"year\"\n}\n\n/** A monthly price, and what it grants. */\nexport interface BillingMonthlyPrice extends BillingMoney {\n /** `null` when the price does not state it. */\n credits: BillingPriceCredits | null\n}\n\n/** A yearly price, what it comes to per month, and what it grants. */\nexport interface BillingYearlyPrice extends BillingMoney {\n /** The yearly amount spread over the months it pays for; `null` when that cannot be said. */\n perMonth: number | null\n /** What the price grants — the plan's credits every month, as a monthly price; `null` when the price does not state it. */\n credits: BillingPriceCredits | null\n}\n\nexport interface BillingPlanPrices {\n /** `null` = Stripe cannot sell this price right now: show it as unavailable, never as free. */\n monthly: BillingMonthlyPrice | null\n yearly: BillingYearlyPrice | null\n}\n\n/** One plan on sale. */\nexport interface BillingPlan {\n /** `\"free\"`, `\"basic\"`, `\"standard\"`, … — the stable id. */\n id: string\n name: string\n /** `null` for the free plan: there is nothing to pay. */\n prices: BillingPlanPrices | null\n /** The free plan's one-time grant at sign-up; `null` on a paid plan, whose credits come with each price. */\n signupCredits: number | null\n /** How many nodes of one workflow run at once. */\n parallelism: number\n /** Storage for your media, in bytes. */\n storageBytes: number\n}\n\n/** A credit pack: this many credits for this price. */\nexport interface BillingTopUp {\n id: string\n credits: number\n /** `null` when the pack's price cannot be stated right now: show it as unavailable. */\n price: BillingMoney | null\n}\n\n/** The answer of `GET /v1/billing/plans`. */\nexport interface BillingPlansResult {\n /** The free plan first, then each paid plan from the smallest. */\n data: BillingPlan[]\n /** The credit packs, from the smallest. */\n topUps: BillingTopUp[]\n /** Any whole amount in this range can be loaded as credits, at the same rate as the packs. */\n topUpRange: { min: number; max: number; currency: string }\n}\n\n/** The account's latest subscription record. */\nexport interface BillingSubscription {\n id: string\n stripe_subscription_id: string | null\n /** The plan id: `\"basic\"`, `\"standard\"`, … */\n tier: string\n /** `\"active\"`, `\"trialing\"`, `\"past_due\"`, `\"paused\"`, `\"canceled\"`, `\"incomplete\"` or `\"expired\"`. */\n status: string\n stripe_price_id: string | null\n /** ISO-8601. */\n current_period_start: string | null\n /** ISO-8601: when the current period ends (and renews, unless cancelled). */\n current_period_end: string | null\n /** True when the subscription ends at `current_period_end` instead of renewing. */\n cancel_at_period_end: boolean\n /** ISO-8601: when a scheduled cancellation takes effect. */\n cancel_at: string | null\n /** ISO-8601: when it was cancelled. */\n canceled_at: string | null\n}\n\n/** The answer of `GET /v1/billing/subscription`. */\nexport interface BillingSubscriptionResult {\n /** `null` when the account never subscribed. */\n data: BillingSubscription | null\n /**\n * The tier the account's entitlements follow: usually a plan id from\n * `plans()` (`\"free\"` included); `\"payg\"` — no subscription, but credits\n * bought (every model, no watermark); or a tier that is not on sale, such\n * as `\"enterprise\"`.\n */\n effectiveTier: string\n}\n\n/** How this deployment bills, the same for every caller (`GET /v1/billing/surface`). */\nexport interface BillingSurface {\n /** The billing-surface contract version (an integer). */\n contract: number\n /** The registered billing provider; `\"none\"` = this deployment meters nothing. */\n providerId: string\n /** The unit costs display in by default (`\"credits\"`, `\"usd\"`, or a deployment's own label). */\n displayUnit: string\n canReport: boolean\n canQuote: boolean\n canAccount: boolean\n /** Whether a cost view belongs on this deployment. */\n mountCostTab: boolean\n /** One billing account pays for every user here. */\n deploymentPayer: boolean\n}\n\n/** One row of {@link BillingAccountSummary.byCategory}. */\nexport interface BillingUsageCategory {\n category: string\n count: number\n /** Spend in the display unit; `null` = unavailable, never 0. */\n amount: number | null\n spent: BillingMoney | null\n}\n\n/**\n * The caller's account as the deployment's billing provider sees it\n * (`GET /v1/billing/account`). Money and amounts are `null` when the provider\n * could not say: show that distinctly, never as 0. A field the provider leaves\n * out is a concept it does not have.\n */\nexport interface BillingAccountSummary {\n /** A plain string; `\"unknown\"` is a real answer. */\n plan: string\n balance: number | null\n dailyAllowance: number | null\n unit: string\n /** `\"external_wallet\"` when the balance comes from a wallet outside Nodaro. */\n balanceSource?: \"external_wallet\"\n /** ISO-8601 start of the current usage period. */\n periodStart?: string | null\n /** Generations this period. */\n generations?: number | null\n /** Money spent this period. */\n spent?: BillingMoney | null\n /** Pay-as-you-go state. */\n payg?: {\n enabled: boolean\n reserve: number | null\n rate: { creditsPerUnit: number; currency: string } | null\n monthlyCap: BillingMoney | null\n } | null\n /** The daily cap and when it resets; prefer it over `dailyAllowance` when present. `limit: 0` means blocked. */\n daily?: { limit: number; used: number; remaining: number; resetsAt: string } | null\n /** Money value of the pay-as-you-go reserve. */\n reserveValue?: BillingMoney | null\n byCategory?: BillingUsageCategory[] | null\n /** The total the balance is drawn from, where the provider grants each user an allocation. */\n allocated?: number | null\n}\n\nexport class BillingResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * `GET /v1/billing/plans` → the plans and credit packs on sale, with their\n * prices. Public: works with no credential, the same for every caller.\n * Nodaro Cloud only — a self-hosted instance answers 404. Throws on `403\n * self_serve_disabled` (this deployment does not sell to its users) and on\n * `503` (`stripe_not_configured`, or prices briefly unavailable).\n */\n plans(): Promise<BillingPlansResult> {\n return this.client.request<BillingPlansResult>(\"GET\", \"/v1/billing/plans\")\n }\n\n /**\n * `GET /v1/billing/subscription` → the account's latest subscription\n * (`null` when it never subscribed) and the tier its entitlements follow.\n * Match `effectiveTier` to a {@link BillingPlan} `id` to mark the current\n * plan (no match: pay-as-you-go, or a tier not on sale). Needs a signed-in\n * session, a personal API token without a workflow limit, or an app's OAuth\n * token. Nodaro Cloud only — a self-hosted instance answers 404.\n */\n subscription(): Promise<BillingSubscriptionResult> {\n return this.client.request<BillingSubscriptionResult>(\"GET\", \"/v1/billing/subscription\")\n }\n\n /**\n * `GET /v1/billing/surface` → how this deployment bills: whether it meters\n * at all (`providerId !== \"none\"`) and in which unit costs display. Public,\n * every edition.\n */\n async surface(): Promise<BillingSurface> {\n const res = await this.client.request<{ data: BillingSurface }>(\"GET\", \"/v1/billing/surface\")\n return res.data\n }\n\n /**\n * `GET /v1/billing/account` → the caller's plan and balance as the\n * deployment's billing provider reports them, in its display unit. `null`\n * when the provider could not answer — not a zero balance. Every edition.\n */\n async account(): Promise<BillingAccountSummary | null> {\n const res = await this.client.request<{ data: BillingAccountSummary | null }>(\"GET\", \"/v1/billing/account\")\n return res.data\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/node-presets.ts"],"names":[],"mappings":"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","file":"chunk-JTAW54UM.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport type { FactoryPreset } from \"@nodaro/prompts/presets\"\n\n/**\n * A user's saved custom preset (`GET /v1/node-presets`). Mirrors the backend's\n * camelCase row shape. `data` is captured node config — merge it into a node's\n * data when building/running a workflow to \"apply\" the preset.\n */\nexport interface NodePreset {\n id: string\n nodeType: string\n name: string\n description?: string\n data: Record<string, unknown>\n groupId?: string\n tags: string[]\n sortOrder: number\n /**\n * How many connected reference images the preset works from (0 = none),\n * counted from the `{image:N}` tokens in its prompt. Absent from servers that\n * predate it.\n */\n minReferences?: number\n /** `minReferences > 0` — the preset works from a connected photo. */\n needsImage?: boolean\n /** The example picture stored with the preset, or null. */\n previewUrl?: string | null\n createdAt: string\n updatedAt: string\n}\n\n/** A user's preset folder/section (`GET /v1/node-preset-groups`). */\nexport interface NodePresetGroup {\n id: string\n nodeType: string\n name: string\n kind: \"folder\" | \"section\"\n sortOrder: number\n createdAt: string\n updatedAt: string\n}\n\n/**\n * One built-in preset as `GET /v1/node-presets/factory` serves it: the\n * catalog entry plus what a client needs to present it.\n */\nexport interface FactoryPresetEntry extends FactoryPreset {\n /**\n * How many connected reference images the preset works from (0 = none):\n * Reference Sheet, Portrait Transformations, Edits and every template using\n * `{image:N}` need at least one. Absent from servers that predate it.\n */\n minReferences?: number\n /** `minReferences > 0` — the preset works from a connected photo. */\n needsImage?: boolean\n /**\n * An example picture of what the preset makes — an absolute URL on the\n * server you asked — or null while the preset has none.\n */\n previewUrl?: string | null\n}\n\n/**\n * Result of `GET /v1/node-presets/factory` — the built-in (factory) catalog for\n * a node type.\n */\nexport interface FactoryPresetsResult {\n data: FactoryPresetEntry[]\n}\n\n/** `POST /v1/node-presets` body. */\nexport interface CreateNodePresetInput {\n /** The node type the preset configures, e.g. `\"generate-image\"`. */\n nodeType: string\n /** Unique per node type (case-insensitive); a duplicate answers 409 `name_taken`. */\n name: string\n description?: string\n /** Node config (≤ 64 KB). Runtime keys (results, job ids, labels) are stripped. */\n data: Record<string, unknown>\n /** One of your preset groups, or null for none. */\n groupId?: string | null\n tags?: string[]\n sortOrder?: number\n /**\n * An example picture: an image stored on this platform (an upload or a\n * generated result) that already exists. Anything else answers 400.\n */\n previewUrl?: string | null\n}\n\n/** `PATCH /v1/node-presets/:id` body — every field optional; `previewUrl: null` removes the picture. */\nexport type UpdateNodePresetInput = Partial<Omit<CreateNodePresetInput, \"nodeType\">>\n\n/** One entry of `POST /v1/node-presets/import` (the `presets export` envelope's entries fit). */\nexport interface ImportNodePresetInput {\n nodeType: string\n name: string\n description?: string\n data: Record<string, unknown>\n previewUrl?: string | null\n}\n\n/** `POST /v1/node-presets/reorder` body. */\nexport interface ReorderNodePresetsInput {\n groups?: Array<{ id: string; sortOrder: number }>\n /** `groupId` moves the preset into one of your groups (null = out of any group). */\n presets?: Array<{ id: string; groupId?: string | null; sortOrder: number }>\n}\n\n/** A favorite: a factory preset id (`\"generate-image/character-board\"`) or one of your preset ids. */\nexport interface NodePresetFavoriteInput {\n nodeType: string\n presetId: string\n}\n\n/** `POST /v1/node-preset-groups` body. */\nexport interface CreateNodePresetGroupInput {\n nodeType: string\n name: string\n /** A collapsible `\"folder\"` (default) or a flat `\"section\"` header. */\n kind?: \"folder\" | \"section\"\n sortOrder?: number\n}\n\n/** `PATCH /v1/node-preset-groups/:id` body. */\nexport interface UpdateNodePresetGroupInput {\n name?: string\n sortOrder?: number\n}\n\n/**\n * Node presets — reusable, named node configurations: your own presets and\n * folders, your favorites, and the built-in factory catalog.\n *\n * To *use* a preset, take its `data` and merge it into a node's config when you\n * create/update a workflow. `needsImage` / `minReferences` say how many photos\n * it works from; `previewUrl` is an example picture (null when there is none).\n *\n * The factory catalog (`listFactory`) is public: no token needed. Reads of\n * your own library need the `presets:read` scope for an OAuth app token;\n * writes (create, update, delete, import, reorder, favorites, groups) need\n * `assets:write`. Your own API key needs neither — but a key limited to some\n * workflows cannot reach your own presets (it reads the factory catalog as\n * anyone can).\n */\nexport class PresetsResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * `GET /v1/node-presets` → your custom presets, newest first. Pass `nodeType`\n * (e.g. `\"generate-image\"`) to filter to one node type.\n */\n async list(nodeType?: string): Promise<NodePreset[]> {\n const qs = nodeType ? `?nodeType=${encodeURIComponent(nodeType)}` : \"\"\n const res = await this.client.request<{ data: NodePreset[] }>(\"GET\", `/v1/node-presets${qs}`)\n return res.data\n }\n\n /**\n * `GET /v1/node-preset-groups` → your preset folders/sections, in display\n * order. Pass `nodeType` to filter to one node type.\n */\n async listGroups(nodeType?: string): Promise<NodePresetGroup[]> {\n const qs = nodeType ? `?nodeType=${encodeURIComponent(nodeType)}` : \"\"\n const res = await this.client.request<{ data: NodePresetGroup[] }>(\"GET\", `/v1/node-preset-groups${qs}`)\n return res.data\n }\n\n /**\n * `GET /v1/node-presets/factory` → the built-in catalog for `nodeType`. These\n * ship with the app (no account needed to exist), so they're a good starting\n * point for \"what configs are available\".\n *\n * Login is optional: the catalog is public, so this works on a client with\n * no credential at all (`auth: { getToken: async () => null }`) — show the\n * names, example pictures (`previewUrl`) and photo counts (`minReferences`,\n * `needsImage`) to a visitor who is not signed in, and ask for a login only\n * when they generate. Your OWN presets (`list()`) still need a token.\n */\n listFactory(nodeType: string): Promise<FactoryPresetsResult> {\n return this.client.request<FactoryPresetsResult>(\n \"GET\",\n `/v1/node-presets/factory?nodeType=${encodeURIComponent(nodeType)}`,\n )\n }\n\n /** `POST /v1/node-presets` → the new preset. */\n async create(input: CreateNodePresetInput): Promise<NodePreset> {\n const res = await this.client.request<{ data: NodePreset }>(\"POST\", \"/v1/node-presets\", { body: input })\n return res.data\n }\n\n /**\n * `PATCH /v1/node-presets/:id` → the updated preset. Pass the `updatedAt` you\n * read as `expectedUpdatedAt` to refuse (409 `conflict`) a change made\n * elsewhere since.\n */\n async update(id: string, patch: UpdateNodePresetInput, opts: { expectedUpdatedAt?: string } = {}): Promise<NodePreset> {\n const body = opts.expectedUpdatedAt ? { ...patch, expectedUpdatedAt: opts.expectedUpdatedAt } : patch\n const res = await this.client.request<{ data: NodePreset }>(\"PATCH\", `/v1/node-presets/${encodeURIComponent(id)}`, { body })\n return res.data\n }\n\n /** `DELETE /v1/node-presets/:id` (404 when it is not yours). `expectedUpdatedAt` as on `update`. */\n async delete(id: string, opts: { expectedUpdatedAt?: string } = {}): Promise<{ success: true }> {\n const res = await this.client.request<{ data: { success: true } }>(\"DELETE\", `/v1/node-presets/${encodeURIComponent(id)}`, {\n query: { expectedUpdatedAt: opts.expectedUpdatedAt },\n })\n return res.data\n }\n\n /**\n * `POST /v1/node-presets/import` — up to 500 presets in one call. A name you\n * already use gets an \"(imported N)\" suffix; a preset whose data is empty\n * after stripping runtime keys is skipped.\n */\n async import(presets: ImportNodePresetInput[]): Promise<{ imported: number }> {\n const res = await this.client.request<{ data: { imported: number } }>(\"POST\", \"/v1/node-presets/import\", { body: { presets } })\n return res.data\n }\n\n /** `POST /v1/node-presets/reorder` — positions (and group membership) for many presets and groups at once. */\n async reorder(input: ReorderNodePresetsInput): Promise<{ ok: true }> {\n const res = await this.client.request<{ data: { ok: true } }>(\"POST\", \"/v1/node-presets/reorder\", { body: input })\n return res.data\n }\n\n /** `GET /v1/node-presets/favorites` → your favorite preset ids for `nodeType`, newest first. */\n async favorites(nodeType: string): Promise<string[]> {\n const res = await this.client.request<{ data: string[] }>(\"GET\", \"/v1/node-presets/favorites\", { query: { nodeType } })\n return res.data\n }\n\n /** `POST /v1/node-presets/favorites` — idempotent. */\n async addFavorite(input: NodePresetFavoriteInput): Promise<{ success: true }> {\n const res = await this.client.request<{ data: { success: true } }>(\"POST\", \"/v1/node-presets/favorites\", { body: input })\n return res.data\n }\n\n /** `DELETE /v1/node-presets/favorites` — idempotent. */\n async removeFavorite(input: NodePresetFavoriteInput): Promise<{ success: true }> {\n const res = await this.client.request<{ data: { success: true } }>(\"DELETE\", \"/v1/node-presets/favorites\", {\n query: { nodeType: input.nodeType, presetId: input.presetId },\n })\n return res.data\n }\n\n /** `POST /v1/node-preset-groups` → the new folder or section. */\n async createGroup(input: CreateNodePresetGroupInput): Promise<NodePresetGroup> {\n const res = await this.client.request<{ data: NodePresetGroup }>(\"POST\", \"/v1/node-preset-groups\", { body: input })\n return res.data\n }\n\n /** `PATCH /v1/node-preset-groups/:id` → the renamed or moved group. */\n async updateGroup(id: string, patch: UpdateNodePresetGroupInput): Promise<NodePresetGroup> {\n const res = await this.client.request<{ data: NodePresetGroup }>(\"PATCH\", `/v1/node-preset-groups/${encodeURIComponent(id)}`, {\n body: patch,\n })\n return res.data\n }\n\n /** `DELETE /v1/node-preset-groups/:id` — its presets move out of the group; none are deleted. */\n async deleteGroup(id: string): Promise<{ success: true }> {\n const res = await this.client.request<{ data: { success: true } }>(\"DELETE\", `/v1/node-preset-groups/${encodeURIComponent(id)}`)\n return res.data\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/trash.ts"],"names":[],"mappings":";AAyDO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA,EAGpB,IAAA,CAAK,MAAA,GAA0B,EAAC,EAAuB;AACrD,IAAA,MAAM,QAAgC,EAAC;AACvC,IAAA,IAAI,MAAA,CAAO,SAAA,EAAW,KAAA,CAAM,SAAA,GAAY,MAAA,CAAO,SAAA;AAC/C,IAAA,IAAI,MAAA,CAAO,IAAA,EAAM,KAAA,CAAM,IAAA,GAAO,MAAA,CAAO,IAAA;AACrC,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,MAAA,EAAW,KAAA,CAAM,OAAO,OAAO,MAAA,CAAO,IAAA,KAAS,QAAA,GAAW,MAAA,CAAO,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAK,GAAG,CAAA;AAChH,IAAA,IAAI,OAAO,KAAA,KAAU,MAAA,QAAiB,KAAA,GAAQ,MAAA,CAAO,OAAO,KAAK,CAAA;AACjE,IAAA,IAAI,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,MAAA,GAAS,MAAA,CAAO,MAAA;AACzC,IAAA,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,WAAA,EAAa,EAAE,OAAO,CAAA;AAAA,EAC1D;AACF","file":"chunk-KBHDUHKM.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport type { Job } from \"./jobs.js\"\n\n/** The session a removed item was removed from. */\nexport interface TrashSession {\n id: string\n title: string | null\n projectId: string | null\n}\n\n/**\n * One entry of the Trash: an item removed from a session, or a deleted job.\n * Narrow on `kind`.\n *\n * - `removed`: kept until restored (`client.sessions.restoreItem(session.id, job.id)`)\n * or deleted for good (`client.sessions.removeItem(session.id, job.id, { forever: true })`).\n * - `deleted`: kept until `purgeAt` (30 days after `deletedAt`), then deleted for\n * good (a copy saved to the Library stays); restore with `client.jobs.restore(job.id)`, or delete\n * now with `client.jobs.delete(job.id, { forever: true })`.\n *\n * A removed item whose job was also deleted is listed once, as the deleted job.\n */\nexport type TrashEntry =\n | { kind: \"removed\"; removedAt: string; session: TrashSession; job: Job }\n | { kind: \"deleted\"; deletedAt: string; purgeAt: string; job: Job }\n\nexport interface ListTrashParams {\n /**\n * A project's id: its sessions' removed items, and deleted jobs a session of\n * it held. `\"none\"`: the sessions with no project, and General.\n */\n projectId?: string | \"none\"\n /**\n * A creation tool (e.g. `\"create-image\"`): what a session of that tool holds\n * or held. A job no session ever held has no tool and is left out — filter by\n * `type` alone to include it.\n */\n tool?: string\n /**\n * The job's `input_data.type` (`\"generate-image\"`, …), like `client.jobs.list`.\n * One value or several (up to 20). Every filter combines with AND.\n */\n type?: string | readonly string[]\n /** Page size, 1–100 (server default 50). */\n limit?: number\n /** The previous page's `next`. */\n cursor?: string\n}\n\nexport interface TrashPage {\n /** A page may hold fewer than `limit` entries and still carry a `next`; page on `next`. */\n data: TrashEntry[]\n /** Pass back as `cursor` for the next page; `null` on the last one. */\n next: string | null\n}\n\n/** Your Trash (`GET /v1/trash`): one entry beside General in a creation tool's sessions list. */\nexport class TrashResource {\n constructor(private client: NodaroClientCore) {}\n\n /** One newest-first page of the Trash, on your side of the workspace line. */\n list(params: ListTrashParams = {}): Promise<TrashPage> {\n const query: Record<string, string> = {}\n if (params.projectId) query.projectId = params.projectId\n if (params.tool) query.tool = params.tool\n if (params.type !== undefined) query.type = typeof params.type === \"string\" ? params.type : params.type.join(\",\")\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/trash\", { query })\n }\n}\n"]}
@@ -1,52 +0,0 @@
1
- 'use strict';
2
-
3
- // src/resources/billing.ts
4
- var BillingResource = class {
5
- constructor(client) {
6
- this.client = client;
7
- }
8
- client;
9
- /**
10
- * `GET /v1/billing/plans` → the plans and credit packs on sale, with their
11
- * prices. Public: works with no credential, the same for every caller.
12
- * Nodaro Cloud only — a self-hosted instance answers 404. Throws on `403
13
- * self_serve_disabled` (this deployment does not sell to its users) and on
14
- * `503` (`stripe_not_configured`, or prices briefly unavailable).
15
- */
16
- plans() {
17
- return this.client.request("GET", "/v1/billing/plans");
18
- }
19
- /**
20
- * `GET /v1/billing/subscription` → the account's latest subscription
21
- * (`null` when it never subscribed) and the tier its entitlements follow.
22
- * Match `effectiveTier` to a {@link BillingPlan} `id` to mark the current
23
- * plan (no match: pay-as-you-go, or a tier not on sale). Needs a signed-in
24
- * session, a personal API token without a workflow limit, or an app's OAuth
25
- * token. Nodaro Cloud only — a self-hosted instance answers 404.
26
- */
27
- subscription() {
28
- return this.client.request("GET", "/v1/billing/subscription");
29
- }
30
- /**
31
- * `GET /v1/billing/surface` → how this deployment bills: whether it meters
32
- * at all (`providerId !== "none"`) and in which unit costs display. Public,
33
- * every edition.
34
- */
35
- async surface() {
36
- const res = await this.client.request("GET", "/v1/billing/surface");
37
- return res.data;
38
- }
39
- /**
40
- * `GET /v1/billing/account` → the caller's plan and balance as the
41
- * deployment's billing provider reports them, in its display unit. `null`
42
- * when the provider could not answer — not a zero balance. Every edition.
43
- */
44
- async account() {
45
- const res = await this.client.request("GET", "/v1/billing/account");
46
- return res.data;
47
- }
48
- };
49
-
50
- exports.BillingResource = BillingResource;
51
- //# sourceMappingURL=chunk-NGKODS3J.cjs.map
52
- //# sourceMappingURL=chunk-NGKODS3J.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/billing.ts"],"names":[],"mappings":";;;AA6KO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,KAAA,GAAqC;AACnC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAA4B,KAAA,EAAO,mBAAmB,CAAA;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,YAAA,GAAmD;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAmC,KAAA,EAAO,0BAA0B,CAAA;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAA,GAAmC;AACvC,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAkC,OAAO,qBAAqB,CAAA;AAC5F,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAA,GAAiD;AACrD,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAgD,OAAO,qBAAqB,CAAA;AAC1G,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AACF","file":"chunk-NGKODS3J.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Plans and prices, the subscription, and the billing surface — the shapes of\n * the `/v1/billing/*` routes an app may call. Mirrors the backend routes\n * (`backend/src/ee/routes/billing.ts`, `backend/src/routes/billing-surface.ts`),\n * the source of truth: keep these in sync if they change.\n *\n * Buying, changing the plan and the payment settings stay in the Nodaro app:\n * those routes refuse API and app tokens, so they have no method here.\n */\n\n/** A sum of money in its currency's major unit (`12` = 12.00), with its ISO-4217 code (`\"USD\"`). */\nexport interface BillingMoney {\n amount: number\n currency: string\n}\n\n/** The credits a price grants, and how often. */\nexport interface BillingPriceCredits {\n amount: number\n per: \"month\" | \"year\"\n}\n\n/** A monthly price, and what it grants. */\nexport interface BillingMonthlyPrice extends BillingMoney {\n /** `null` when the price does not state it. */\n credits: BillingPriceCredits | null\n}\n\n/** A yearly price, what it comes to per month, and what it grants. */\nexport interface BillingYearlyPrice extends BillingMoney {\n /** The yearly amount spread over the months it pays for; `null` when that cannot be said. */\n perMonth: number | null\n /** What the price grants — the plan's credits every month, as a monthly price; `null` when the price does not state it. */\n credits: BillingPriceCredits | null\n}\n\nexport interface BillingPlanPrices {\n /** `null` = Stripe cannot sell this price right now: show it as unavailable, never as free. */\n monthly: BillingMonthlyPrice | null\n yearly: BillingYearlyPrice | null\n}\n\n/** One plan on sale. */\nexport interface BillingPlan {\n /** `\"free\"`, `\"basic\"`, `\"standard\"`, … — the stable id. */\n id: string\n name: string\n /** `null` for the free plan: there is nothing to pay. */\n prices: BillingPlanPrices | null\n /** The free plan's one-time grant at sign-up; `null` on a paid plan, whose credits come with each price. */\n signupCredits: number | null\n /** How many nodes of one workflow run at once. */\n parallelism: number\n /** Storage for your media, in bytes. */\n storageBytes: number\n}\n\n/** A credit pack: this many credits for this price. */\nexport interface BillingTopUp {\n id: string\n credits: number\n /** `null` when the pack's price cannot be stated right now: show it as unavailable. */\n price: BillingMoney | null\n}\n\n/** The answer of `GET /v1/billing/plans`. */\nexport interface BillingPlansResult {\n /** The free plan first, then each paid plan from the smallest. */\n data: BillingPlan[]\n /** The credit packs, from the smallest. */\n topUps: BillingTopUp[]\n /** Any whole amount in this range can be loaded as credits, at the same rate as the packs. */\n topUpRange: { min: number; max: number; currency: string }\n}\n\n/** The account's latest subscription record. */\nexport interface BillingSubscription {\n id: string\n stripe_subscription_id: string | null\n /** The plan id: `\"basic\"`, `\"standard\"`, … */\n tier: string\n /** `\"active\"`, `\"trialing\"`, `\"past_due\"`, `\"paused\"`, `\"canceled\"`, `\"incomplete\"` or `\"expired\"`. */\n status: string\n stripe_price_id: string | null\n /** ISO-8601. */\n current_period_start: string | null\n /** ISO-8601: when the current period ends (and renews, unless cancelled). */\n current_period_end: string | null\n /** True when the subscription ends at `current_period_end` instead of renewing. */\n cancel_at_period_end: boolean\n /** ISO-8601: when a scheduled cancellation takes effect. */\n cancel_at: string | null\n /** ISO-8601: when it was cancelled. */\n canceled_at: string | null\n}\n\n/** The answer of `GET /v1/billing/subscription`. */\nexport interface BillingSubscriptionResult {\n /** `null` when the account never subscribed. */\n data: BillingSubscription | null\n /**\n * The tier the account's entitlements follow: usually a plan id from\n * `plans()` (`\"free\"` included); `\"payg\"` — no subscription, but credits\n * bought (every model, no watermark); or a tier that is not on sale, such\n * as `\"enterprise\"`.\n */\n effectiveTier: string\n}\n\n/** How this deployment bills, the same for every caller (`GET /v1/billing/surface`). */\nexport interface BillingSurface {\n /** The billing-surface contract version (an integer). */\n contract: number\n /** The registered billing provider; `\"none\"` = this deployment meters nothing. */\n providerId: string\n /** The unit costs display in by default (`\"credits\"`, `\"usd\"`, or a deployment's own label). */\n displayUnit: string\n canReport: boolean\n canQuote: boolean\n canAccount: boolean\n /** Whether a cost view belongs on this deployment. */\n mountCostTab: boolean\n /** One billing account pays for every user here. */\n deploymentPayer: boolean\n}\n\n/** One row of {@link BillingAccountSummary.byCategory}. */\nexport interface BillingUsageCategory {\n category: string\n count: number\n /** Spend in the display unit; `null` = unavailable, never 0. */\n amount: number | null\n spent: BillingMoney | null\n}\n\n/**\n * The caller's account as the deployment's billing provider sees it\n * (`GET /v1/billing/account`). Money and amounts are `null` when the provider\n * could not say: show that distinctly, never as 0. A field the provider leaves\n * out is a concept it does not have.\n */\nexport interface BillingAccountSummary {\n /** A plain string; `\"unknown\"` is a real answer. */\n plan: string\n balance: number | null\n dailyAllowance: number | null\n unit: string\n /** `\"external_wallet\"` when the balance comes from a wallet outside Nodaro. */\n balanceSource?: \"external_wallet\"\n /** ISO-8601 start of the current usage period. */\n periodStart?: string | null\n /** Generations this period. */\n generations?: number | null\n /** Money spent this period. */\n spent?: BillingMoney | null\n /** Pay-as-you-go state. */\n payg?: {\n enabled: boolean\n reserve: number | null\n rate: { creditsPerUnit: number; currency: string } | null\n monthlyCap: BillingMoney | null\n } | null\n /** The daily cap and when it resets; prefer it over `dailyAllowance` when present. `limit: 0` means blocked. */\n daily?: { limit: number; used: number; remaining: number; resetsAt: string } | null\n /** Money value of the pay-as-you-go reserve. */\n reserveValue?: BillingMoney | null\n byCategory?: BillingUsageCategory[] | null\n /** The total the balance is drawn from, where the provider grants each user an allocation. */\n allocated?: number | null\n}\n\nexport class BillingResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * `GET /v1/billing/plans` → the plans and credit packs on sale, with their\n * prices. Public: works with no credential, the same for every caller.\n * Nodaro Cloud only — a self-hosted instance answers 404. Throws on `403\n * self_serve_disabled` (this deployment does not sell to its users) and on\n * `503` (`stripe_not_configured`, or prices briefly unavailable).\n */\n plans(): Promise<BillingPlansResult> {\n return this.client.request<BillingPlansResult>(\"GET\", \"/v1/billing/plans\")\n }\n\n /**\n * `GET /v1/billing/subscription` → the account's latest subscription\n * (`null` when it never subscribed) and the tier its entitlements follow.\n * Match `effectiveTier` to a {@link BillingPlan} `id` to mark the current\n * plan (no match: pay-as-you-go, or a tier not on sale). Needs a signed-in\n * session, a personal API token without a workflow limit, or an app's OAuth\n * token. Nodaro Cloud only — a self-hosted instance answers 404.\n */\n subscription(): Promise<BillingSubscriptionResult> {\n return this.client.request<BillingSubscriptionResult>(\"GET\", \"/v1/billing/subscription\")\n }\n\n /**\n * `GET /v1/billing/surface` → how this deployment bills: whether it meters\n * at all (`providerId !== \"none\"`) and in which unit costs display. Public,\n * every edition.\n */\n async surface(): Promise<BillingSurface> {\n const res = await this.client.request<{ data: BillingSurface }>(\"GET\", \"/v1/billing/surface\")\n return res.data\n }\n\n /**\n * `GET /v1/billing/account` → the caller's plan and balance as the\n * deployment's billing provider reports them, in its display unit. `null`\n * when the provider could not answer — not a zero balance. Every edition.\n */\n async account(): Promise<BillingAccountSummary | null> {\n const res = await this.client.request<{ data: BillingAccountSummary | null }>(\"GET\", \"/v1/billing/account\")\n return res.data\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/cast-swap.ts"],"names":[],"mappings":";AAidO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,GAAA,CAAI,QAAwB,OAAA,EAA6E;AACvG,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,eAAA,EAAiB;AAAA,MAClD,IAAA,EAAM,MAAA;AAAA,MACN,GAAI,OAAA,EAAS,cAAA,GAAiB,EAAE,OAAA,EAAS,EAAE,iBAAA,EAAmB,OAAA,CAAQ,cAAA,EAAe,EAAE,GAAI;AAAC,KAC7F,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,MAAA,EAAmD;AAC1D,IAAA,OAAO,IAAA,CAAK,OAAO,OAAA,CAAQ,MAAA,EAAQ,0BAA0B,EAAE,IAAA,EAAM,QAAQ,CAAA;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,oBAAoB,KAAA,EAAqD;AACvE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,CAAA,cAAA,EAAiB,kBAAA,CAAmB,KAAK,CAAC,CAAA,sBAAA,CAAA,EAA0B,EAAE,IAAA,EAAM,IAAI,CAAA;AAAA,EACrH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,WAAA,CAAY,KAAA,EAAe,MAAA,GAAoC,EAAC,EAAuC;AACrG,IAAA,MAAM,IAAA,GAAO,OAAO,SAAA,KAAc,MAAA,GAAY,EAAE,SAAA,EAAW,MAAA,CAAO,SAAA,EAAU,GAAI,EAAC;AACjF,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,CAAA,cAAA,EAAiB,kBAAA,CAAmB,KAAK,CAAC,CAAA,aAAA,CAAA,EAAiB,EAAE,IAAA,EAAM,CAAA;AAAA,EACxG;AACF","file":"chunk-OWG3XAHQ.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * Nodaro Motion (the `castSwap` resource; routes `/v1/cast-swap`) — replace\n * the people, pets, products and/or the location in a short video with your\n * own references, keeping its motion, framing, timing and sound (the clip's\n * audio is kept, and the result is the clip's length).\n *\n * Before the video, each person, pet and product role's photos are turned\n * into one clean reference picture by an image model. This costs extra\n * credits (`breakdown.references` in the estimate), charged when it runs, even\n * if the video then fails; it adds about a minute, falls back to your own\n * photos if it fails, and is turned off with `prepareReferences: false`.\n * Locations are not prepared.\n *\n * Engines: a run is made on Nodaro Motion Pro (`engine` left out, or\n * `\"motion-pro\"`) or Nodaro Motion Lite (`\"motion-lite\"`). Lite renders 720p\n * (its default) or 1080p and has no draft; it makes a new take guided by the\n * clip and pictures, so its Full Quality is a new take at 1080p rather than\n * the same take re-rendered (`CastSwapFullQualityEstimate.kind`).\n *\n * How the clip is prepared for the video model (for one, whether faces are\n * hidden first, and whether a refused clip is retried) is set by Nodaro for\n * each engine; a run cannot change it. `faceMasking` is no longer read\n * (deprecated: accepted and ignored).\n *\n * Cloud edition only: `/v1/cast-swap` is served by the cloud plugin and 404s\n * on self-hosted installs. `run()` answers 202 with a job id; follow it with\n * `client.jobs.get(jobId)` / `client.jobs.getStatus(jobId)` like any other job.\n *\n * Repeats: pass `{ idempotencyKey }` to `run()` and it is sent as the\n * `Idempotency-Key` header. A repeat with the same key within the credit\n * guard's dedup window (about 10 s) answers 200 `{ jobId, deduped: true }` with\n * an `X-Dedup-Hit` header; a later one answers 202 with the first run's\n * `jobId`. Nothing new is started or charged.\n *\n * Refusals (thrown as a `NodaroError` subclass; body `{ error: { code, message } }`), before\n * anything is charged: 400 `validation_error` (the body: no role with images,\n * no reference and no `prePrompt` or `postPrompt`, more roles of a kind\n * than allowed, more than 30 images in all, `draft: true` above 480p, a\n * `prePrompt` or `postPrompt` over 4,000 characters); on Nodaro Motion Lite,\n * `draft_unsupported` (`draft: true`), `resolution_unsupported` (480p),\n * `output_format_unsupported` (a MOV edit output) and `too_many_images` (more\n * than 10 images in the run, across every role and fixed reference);\n * `raw_reference_token` (also in a reference's `line`),\n * `invalid_role_mention` (also any `{role:N}` in a reference's `line`),\n * `unsafe_url`, `video_unreadable`, `clip_out_of_range` (4–30 s),\n * `preview_out_of_range`, `no_roles_in_preview` (a preview with every role\n * `inPreview: false`, always refused even with a prompt, or with no role kept\n * in it that has images and no `prePrompt` or `postPrompt`), `prompt_too_long`\n * (the prompt would build too long: a `{role:N}` reads as the role's name when\n * it sends no images; Nodaro Motion Lite's cap is 8,000 characters); 401; 402 `insufficient_credits` (`run()` only); 404\n * (a `sessionId` you may not use, `run()` only); 422 `job_blocked` (`run()`\n * only); 503 `host_too_old` (both); 503 `estimate_unavailable` (`estimate()`\n * only).\n */\n\n/**\n * The engine a Nodaro Motion run is made on, by its public id. Left out =\n * `\"motion-pro\"`.\n *\n * - `\"motion-pro\"` (Nodaro Motion Pro): 480p (the default, a draft unless\n * `draft: false`), 720p or 1080p; Full Quality re-renders a draft's own take\n * at 1080p.\n * - `\"motion-lite\"` (Nodaro Motion Lite): 720p (the default) or 1080p, never a\n * draft (`draft: true` answers 400 `draft_unsupported`); Full Quality makes a\n * new take at 1080p from a completed 720p run's inputs. A preview is still\n * 4 s from `previewStart`.\n */\nexport type CastSwapEngine = \"motion-pro\" | \"motion-lite\"\n\n/**\n * The engine ids an older client sent (`\"seedance-2-5\"` = `\"motion-pro\"`,\n * `\"kling-4\"` = `\"motion-lite\"`). Still accepted in a request; never returned.\n * @deprecated use {@link CastSwapEngine}.\n */\nexport type CastSwapEngineLegacyId = \"seedance-2-5\" | \"kling-4\"\n\n/**\n * Where a Nodaro Motion Lite run cuts the clip to whole seconds (Lite renders\n * whole seconds; asked for more than the clip, it would stretch the motion):\n * - `\"cut-end\"` (the default): drop the fraction of a second at its end;\n * - `\"cut-start\"`: drop it at its start.\n */\nexport type CastSwapMotionLength = \"cut-end\" | \"cut-start\"\n\n/** @deprecated use {@link CastSwapMotionLength}. */\nexport type CastSwapKlingLength = CastSwapMotionLength\n\n/** Whether a swapped subject takes this trait from the references (`\"new\"`) or keeps the video's (`\"original\"`). */\nexport type CastSwapTraitSource = \"new\" | \"original\"\n\n/**\n * Preview-only settings, on every role kind; a full run (`preview` not `true`)\n * ignores both.\n */\nexport interface CastSwapRolePreviewSettings {\n /** The name used instead of `name` in a preview. Same rules as `name` (1–200 characters). */\n previewName?: string\n /**\n * `false` leaves this role out of the preview: its reference images are not\n * sent, not prepared and not charged, and a `{role:N}` naming it reads as its\n * `previewName`, else its `name`. Default `true`. A preview with every role\n * left out (always refused, even with a prompt), or with no role kept in it\n * that has images and no pre / post prompt, answers 400 `no_roles_in_preview`.\n */\n inPreview?: boolean\n}\n\n/** A person in the video to recast. */\nexport interface CastSwapPersonRole extends CastSwapRolePreviewSettings {\n kind: \"person\"\n /** How the person is told apart in the video, e.g. `\"the woman that attacks\"`. */\n name: string\n /** 0–3 images of ONE person. Empty or `null` keeps the original person. */\n images: string[] | null\n /** Body from the references or the video. Default `\"new\"`. */\n body?: CastSwapTraitSource\n /** Outfit from the references or the video. Default `\"new\"`. */\n outfit?: CastSwapTraitSource\n /** Hairstyle and haircut from the references or the video. Default `\"new\"`. */\n hair?: CastSwapTraitSource\n}\n\n/** The place the video happens in. */\nexport interface CastSwapLocationRole extends CastSwapRolePreviewSettings {\n kind: \"location\"\n /** How the place is named. Default `\"the setting\"`. */\n name?: string\n /** 0–3 images of ONE place. Empty or `null` keeps the original place. */\n images: string[] | null\n}\n\n/** An animal in the video to recast. */\nexport interface CastSwapPetRole extends CastSwapRolePreviewSettings {\n kind: \"pet\"\n /** How the animal is told apart in the video, e.g. `\"the dog\"`. */\n name: string\n /** 0–3 images of ONE animal. Empty or `null` keeps the original animal. */\n images: string[] | null\n /** Body (shape, size and build) from the references or the video. Default `\"new\"`. */\n body?: CastSwapTraitSource\n /** Coat (fur, colors and markings) from the references or the video. Default `\"new\"`. */\n coat?: CastSwapTraitSource\n}\n\n/** An object in the video to recast. */\nexport interface CastSwapProductRole extends CastSwapRolePreviewSettings {\n kind: \"product\"\n /** How the object is told apart in the video, e.g. `\"the can\"`. */\n name: string\n /** 0–3 images of ONE product. Empty or `null` keeps the original object. */\n images: string[] | null\n /** Shape and proportions from the references or the video. Default `\"new\"`. */\n shape?: CastSwapTraitSource\n /** Finish (colors, materials, label and markings) from the references or the video. Default `\"new\"`. */\n finish?: CastSwapTraitSource\n}\n\nexport type CastSwapRole = CastSwapPersonRole | CastSwapLocationRole | CastSwapPetRole | CastSwapProductRole\n\n/**\n * A fixed reference: 1–3 images of ONE thing that no role replaces, and the\n * line that says what it is (`\"the glass she drinks from\"`). Sent as given,\n * never prepared; its images count toward the 30.\n */\nexport interface CastSwapReference {\n /** 1–3 http(s) image URLs of one thing. */\n images: string[]\n /**\n * What it is, 1–500 characters. `{image}` in the line stands for this\n * reference's first image (`\"Use {image} for the sky\"`); a line without it\n * gets it appended. A `{role:N}` or a raw `{image:` / `{video:` / `{audio:`\n * token answers 400.\n */\n line: string\n}\n\n/**\n * @deprecated Nodaro sets this per engine; a run's `faceMasking` is ignored.\n */\nexport interface CastSwapFaceMaskingZone {\n leftPct?: number\n rightPct?: number\n topPct?: number\n bottomPct?: number\n}\n\n/**\n * @deprecated Nodaro sets how the clip is prepared for the video model, per\n * engine; a run cannot change it. A `faceMasking` sent is accepted and\n * ignored (no error), so older code keeps working unchanged.\n */\nexport interface CastSwapFaceMasking {\n mode: \"off\" | \"always\" | \"auto\"\n fill?: \"blur\" | \"depth\"\n minFaceHeightPct?: number\n maxFaceHeightPct?: number\n zone?: CastSwapFaceMaskingZone | null\n blurStrengthPct?: number\n threshold?: number\n}\n\n/** The body of `POST /v1/cast-swap` and `POST /v1/cast-swap/estimate`. */\nexport interface CastSwapParams {\n /** The source clip, 4–30 seconds. */\n videoUrl: string\n /**\n * Who and what to swap: at most 6 person roles, 1 location role, 3 pet roles\n * and 3 product roles, with at most 30 images in all. A role with no images\n * is skipped (that subject or place stays as in the video). At least one role\n * with images, or a `prePrompt` or `postPrompt`, is required.\n */\n roles: CastSwapRole[]\n /**\n * Fixed references (at most 5): things the video should show that no role\n * replaces, each with the line that names it. Their images count toward the\n * 30. A reference alone is enough for a run (no role needs images).\n */\n references?: CastSwapReference[]\n /**\n * Your own words placed at the start of the prompt, before the role\n * instructions; at most 4,000 characters. Mention a role as `{role:N}`, as in\n * `postPrompt`; the same 400 refusals apply (an N outside 1..`roles.length`,\n * a raw `{image:` / `{video:` / `{audio:` token).\n */\n prePrompt?: string\n /**\n * Extra instructions in your own words, at most 4,000 characters. Mention a\n * role as `{role:N}`, where N is its 1-based position in `roles`\n * (`{role:1}` is `roles[0]`; empty roles count).\n *\n * The route answers 400, before charging, for an N outside 1..`roles.length`,\n * for a raw `{image:` / `{video:` / `{audio:` token in the pre or post prompt\n * or a role's `name`, for a clip outside 4–30 s, and for a preview past the clip's\n * end (`previewStart + 4` > its length).\n */\n postPrompt?: string\n /** `true` runs on 4 seconds of the clip, starting at `previewStart`: a cheap check of the roles and choices. */\n preview?: boolean\n /** Where the preview starts, in seconds. Default 0. */\n previewStart?: number\n /**\n * The engine (see {@link CastSwapEngine}). Default `\"motion-pro\"`. The job\n * records it in `input_data.engine`, and a completed job's output in its\n * `engine`. The ids an older client sent ({@link CastSwapEngineLegacyId})\n * are still accepted.\n */\n engine?: CastSwapEngine | CastSwapEngineLegacyId\n /**\n * Nodaro Motion Lite only (Pro ignores it): which end of the clip is cut to\n * whole seconds (see {@link CastSwapMotionLength}). Default `\"cut-end\"`.\n */\n motionLength?: CastSwapMotionLength\n /** @deprecated use {@link motionLength} (still accepted). */\n klingLength?: CastSwapKlingLength\n /**\n * The output resolution. Nodaro Motion Pro: `\"480p\"` (default), `\"720p\"` or\n * `\"1080p\"`. Nodaro Motion Lite: `\"720p\"` (default) or `\"1080p\"`; `\"480p\"`\n * answers 400.\n */\n resolution?: \"480p\" | \"720p\" | \"1080p\"\n /**\n * Render as a draft (Nodaro Motion Pro only). Defaults to `true` at 480p and\n * `false` above it; `true` with `\"720p\"` or `\"1080p\"` answers 400 (draft is\n * 480p only). Nodaro Motion Lite has no draft: `true` answers 400\n * `draft_unsupported`.\n */\n draft?: boolean\n /**\n * Turn each person, pet and product role's photos into one clean reference\n * picture before the video (extra credits, shown in `breakdown.references`;\n * about a minute longer; falls back to your own photos if it fails).\n * Default `true`.\n */\n prepareReferences?: boolean\n /**\n * Ignored: Nodaro sets how the clip is prepared, per engine. Accepted (no\n * error) so older code keeps working.\n * @deprecated leave it out.\n */\n faceMasking?: CastSwapFaceMasking\n /**\n * `\"draft\"` means 480p draft.\n * @deprecated use `resolution` + `draft`.\n */\n quality?: \"draft\"\n /** A generation session to file the result in. */\n sessionId?: string\n /** The client app that sent the run (stored on the job as `input_data.origin`). */\n origin?: string\n}\n\n/** What a run would cost, in credits. */\nexport interface CastSwapEstimate {\n /**\n * The engine the run would use, by its public id (absent from an older\n * server = `\"motion-pro\"`).\n */\n engine?: CastSwapEngine\n /**\n * What is held when the run starts. The run is charged what it actually\n * costs, which is expected to be `expectedCredits`.\n */\n totalCredits: number\n /**\n * The charge the run is expected to come to. Never above `totalCredits`\n * (on Nodaro Motion Lite it equals it: Lite renders exactly the length\n * asked for, so the hold is what it settles at). Absent from an older\n * server: read `totalCredits` then.\n */\n expectedCredits?: number\n /**\n * The parts of the quote, by name, for display only:\n * - `base`: the run itself (its base fee). A server before Cast Swap 0.396.0\n * names it `castSwap` instead: read `base`, else `castSwap`;\n * - `references`: preparing the reference pictures (0 when\n * `prepareReferences` is off or there is nothing to prepare);\n * - `motion`: what the video holds (on Nodaro Motion Lite, what it costs);\n * - `motionExpected`: what the video is expected to be charged, never above\n * `motion` (Nodaro Motion Pro);\n * - `privacy`: preparing the clip, when the engine's setting does. In both\n * totals when it always runs; outside both when it is the retry\n * (`privacyCredits`).\n *\n * `motion` and `motionExpected` are alternatives, so the parts sum to more\n * than either total: read `totalCredits` and `expectedCredits`, never the\n * sum. Other parts may appear.\n */\n breakdown: {\n base?: number\n /** @deprecated read {@link base} (an older server answered this name for the same part). */\n castSwap?: number\n references: number\n motion?: number\n motionExpected?: number\n privacy?: number\n } & Record<string, number>\n /**\n * The extra credits charged only if the video model refuses the clip and\n * the run is retried (Nodaro's setting for the engine decides whether it\n * does). Not in `totalCredits` or `expectedCredits`; `run()` needs credits\n * for both the hold and this (else 402).\n */\n privacyCredits?: number\n /** @deprecated read {@link privacyCredits} (an older server answered this name). */\n maskingCredits?: number\n /**\n * `true` when the video is made at Auto length (every Nodaro Motion Pro\n * run): the final charge is expected to come out lower than `totalCredits`,\n * at about `expectedCredits`. `false` on Nodaro Motion Lite.\n */\n settlesLower: boolean\n}\n\n/** Per-call options for {@link CastSwapResource.run}. */\nexport interface CastSwapRunOptions {\n /**\n * Sent as the `Idempotency-Key` header, unchanged (the SDK checks nothing;\n * an empty string sends no header, like no key).\n * The server takes 8–200 characters and answers 400 for any other length.\n * Reuse the same value when retrying the same run: within about 10 s the\n * repeat answers 200 `{ jobId, deduped: true }`, later 202 with the first\n * run's `jobId`. Nothing new is started or charged.\n */\n idempotencyKey?: string\n}\n\n/**\n * What a Full Quality version would be: `\"same-take\"` re-renders a Nodaro\n * Motion Pro draft's own take at 1080p; `\"new-take\"` makes a new Nodaro Motion Lite take at\n * 1080p from a 720p run's inputs (it will not match the 720p run frame for\n * frame). The 720p run or draft stays either way.\n */\nexport type CastSwapFullQualityKind = \"same-take\" | \"new-take\"\n\n/**\n * What a Full Quality version of a run would cost\n * (`POST /v1/cast-swap/:jobId/full-quality/estimate`): a Nodaro Motion Pro\n * draft, or a completed Nodaro Motion Lite run at 720p.\n */\nexport interface CastSwapFullQualityEstimate {\n /**\n * Which Full Quality this is ({@link CastSwapFullQualityKind}), so it can\n * be worded right. Absent from an older server: `\"same-take\"`.\n */\n kind?: CastSwapFullQualityKind\n /** The quote in credits: `breakdown.base` plus the 1080p render (same take) or the new take. */\n totalCredits: number\n /**\n * The parts of the quote, by name, for display only: `base` (the Full\n * Quality job's own steps; a server before Cast Swap 0.396.0 names it\n * `castSwap` instead: read `base`, else `castSwap`), and `motionHd` (a same\n * take: the 1080p render, 0 when an earlier try of this draft already\n * finished that render, which is then reused) or `motion` (a new take).\n */\n breakdown: {\n base?: number\n /** @deprecated read {@link base} (an older server answered this name for the same part). */\n castSwap?: number\n motionHd?: number\n motion?: number\n } & Record<string, number>\n /** ISO time until which the run can be finished at Full Quality (7 days after it completed). */\n availableUntil: string\n}\n\n/** Options for {@link CastSwapResource.fullQuality}. */\nexport interface CastSwapFullQualityParams {\n /** A generation session to file the Full Quality result in. */\n sessionId?: string\n}\n\n/** The Full Quality job (`POST /v1/cast-swap/:jobId/full-quality`). */\nexport interface CastSwapFullQualityResult {\n jobId: string\n /** `true` when this run's Full Quality was already running or done: that job is answered, nothing new is started or charged. */\n existing?: boolean\n}\n\n/**\n * The `code` of a Full Quality refusal (`NodaroError.code`), all before\n * anything is charged:\n * - `unauthorized` (401): no authenticated user;\n * - `not_found` (404): not your job, or not a Nodaro Motion run; also a\n * `sessionId` you may not use;\n * - `not_completed` (409): the run has not finished;\n * - `not_a_draft` (409): the run is neither a Nodaro Motion Pro draft nor a\n * Nodaro Motion Lite run at 720p (a Lite run at 1080p is already Full Quality);\n * - `draft_expired` (410): more than 7 days after the draft completed, or\n * the draft can no longer be finished at Full Quality;\n * - `full_quality_pending` (409, `details.retryable: true`): not final — an\n * earlier try's 1080p render of this draft is being reviewed, or has not\n * started yet; try again later (once the review ends). The only refusal\n * that ends on its own;\n * - `full_quality_unavailable` (409): final — nothing kept to re-render, no\n * readable completion time, or the 1080p finalize refuses this draft; on\n * the start only, also \"failed too many times\" (every try failed) or no\n * try can be started for this run;\n * - `insufficient_credits` (402), or in a workspace `budget_exceeded`,\n * `member_cap_exceeded` or `user_allowance_exceeded`;\n * - `job_blocked` (422): the request was blocked by the job policy;\n * - `job_read_failed` (503, retryable), `estimate_unavailable` (503, try\n * again shortly), `host_too_old` (503).\n *\n * Open-ended: a code not listed here may still arrive.\n */\nexport type CastSwapFullQualityRefusalCode =\n | \"unauthorized\"\n | \"not_found\"\n | \"not_completed\"\n | \"not_a_draft\"\n | \"draft_expired\"\n | \"full_quality_pending\"\n | \"full_quality_unavailable\"\n | \"insufficient_credits\"\n | \"budget_exceeded\"\n | \"member_cap_exceeded\"\n | \"user_allowance_exceeded\"\n | \"job_blocked\"\n | \"job_read_failed\"\n | \"estimate_unavailable\"\n | \"host_too_old\"\n | (string & {})\n\nexport class CastSwapResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * Start a Nodaro Motion run (`POST /v1/cast-swap`, 202 `{ jobId }`). Returns the\n * job to follow. With `options.idempotencyKey` (sent as the\n * `Idempotency-Key` header; 8–200 characters, else 400) a repeat with the\n * same key within about 10 s answers 200 `{ jobId, deduped: true }`, a later\n * one 202 with the first run's `jobId`; nothing new starts.\n */\n run(params: CastSwapParams, options?: CastSwapRunOptions): Promise<{ jobId: string; deduped?: boolean }> {\n return this.client.request(\"POST\", \"/v1/cast-swap\", {\n body: params,\n ...(options?.idempotencyKey ? { headers: { \"Idempotency-Key\": options.idempotencyKey } } : {}),\n })\n }\n\n /** Quote a run before starting it (`POST /v1/cast-swap/estimate`); spends nothing. */\n estimate(params: CastSwapParams): Promise<CastSwapEstimate> {\n return this.client.request(\"POST\", \"/v1/cast-swap/estimate\", { body: params })\n }\n\n /**\n * Quote finishing a run at Full Quality (`POST\n * /v1/cast-swap/:jobId/full-quality/estimate`); spends nothing. `jobId` is a\n * finished Nodaro Motion Pro DRAFT run, or a finished Nodaro Motion Lite\n * run at 720p (a Nodaro Motion or trend run); the quote's `kind` says whether it is the same take\n * re-rendered or a new take. The quote\n * does not look for a Full Quality already running: when one is,\n * {@link fullQuality} answers it (`existing: true`) and charges nothing.\n * Refusals: {@link CastSwapFullQualityRefusalCode}.\n */\n fullQualityEstimate(jobId: string): Promise<CastSwapFullQualityEstimate> {\n return this.client.request(\"POST\", `/v1/cast-swap/${encodeURIComponent(jobId)}/full-quality/estimate`, { body: {} })\n }\n\n /**\n * Finish a run at Full Quality (`POST /v1/cast-swap/:jobId/full-quality`):\n * at 1080p, with the clip's audio, as a new job linked to the run — a\n * Nodaro Motion Pro draft's own take re-rendered, or a new Nodaro Motion Lite take from a\n * 720p run's inputs (the quote's `kind`). SPENDS CREDITS: quote it first with\n * {@link fullQualityEstimate}. Answers 202 `{ jobId }`, or 200\n * `{ jobId, existing: true }` when this run's Full Quality is already\n * running or done (one per run; a failed one may be asked again). Follow\n * the job with `client.jobs.get(jobId)`. No `Idempotency-Key` is taken: the\n * server keys the job by the draft. The new job keeps the draft's origin.\n * Refusals: {@link CastSwapFullQualityRefusalCode}.\n */\n fullQuality(jobId: string, params: CastSwapFullQualityParams = {}): Promise<CastSwapFullQualityResult> {\n const body = params.sessionId !== undefined ? { sessionId: params.sessionId } : {}\n return this.client.request(\"POST\", `/v1/cast-swap/${encodeURIComponent(jobId)}/full-quality`, { body })\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resources/jobs.ts"],"names":[],"mappings":"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","file":"chunk-PQHZUWSL.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * A job's lifecycle status. Hand-copied mirror of `JOB_STATUSES` in the\n * platform's `backend/src/lib/job-status.ts` — nothing links the two at build\n * time, so a new member has to be added here in the same change.\n *\n * `pending_review` is IN-FLIGHT, NOT TERMINAL: a job policy registered by the\n * deployment held the output for a human reviewer. Treat it like `processing`\n * in a `switch` — keep waiting, do not re-run — and it resolves to `completed`\n * (approved), `failed` (rejected, carrying an `error_hint` with\n * `kind: \"policy-block\"`) or `cancelled` (a held job is cancellable like any\n * in-flight job). `credit_status` reads `\"reserved\"` for the whole hold. Only\n * appears on deployments that register a job policy; `runAndWait` throws\n * {@link JobHeldError} on the first held tick rather than waiting it out.\n */\nexport type JobStatus =\n | \"pending\"\n | \"queued\"\n | \"processing\"\n | \"pending_review\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\"\n\n/**\n * A PROVIDER's verdict: the model's own safety filter refused the output.\n * `class` distinguishes a deterministic block (copyright/likeness — retrying\n * the same request never helps) from `safety`, whose filter is known to be\n * non-deterministic for some models: `retried` says whether the worker\n * already spent its one extra attempt, and `suggestedProvider` — present\n * only when the catalog declares a fallback for the model that failed — is a\n * real model id the SAME prompt/references can be retried on.\n */\nexport type SafetyBlockHint = {\n kind: \"safety-block\"\n class: \"copyright\" | \"likeness\" | \"safety\"\n retried: boolean\n suggestedProvider?: string\n /**\n * Present when the provider refused the INPUT VIDEO itself — not the prompt,\n * a reference image or the output. Sending a different clip is the fix.\n */\n subject?: \"input_video\"\n}\n\n/**\n * A NODARO-side verdict, not a provider's: a job policy registered by the\n * deployment rejected the request (`hookPoint: \"request\"` — nothing ran, and\n * the call itself failed with {@link JobBlockedError}) or the finished output\n * (`hookPoint: \"result\"` — the result was blocked and never published).\n *\n * `reason` is USER-SAFE BY CONTRACT — the platform guarantees the policy's\n * machine text (scores, labels) never reaches it — so show it to the person\n * who made the request as-is. `policyId` identifies the deployment's policy,\n * not a Nodaro product feature. Only occurs on deployments that register a\n * job policy.\n */\nexport type PolicyBlockHint = {\n kind: \"policy-block\"\n policyId: string\n reason: string\n hookPoint: \"request\" | \"result\"\n}\n\n/**\n * The job ran out of credits partway: a step that reserves its own credits\n * (for example, the video a Cast Swap or trend run dispatches) was refused —\n * the balance could not cover it, or a plan or workspace cap was reached. The\n * job failed and its credits were refunded; ask the person to top up (or raise\n * the cap), then run it again.\n *\n * `code` is the refusal's own code, a lowercase snake_case token — for\n * example `insufficient_credits`, `free_video_limit`, `instance_cap_reached`,\n * `budget_exceeded`, `member_cap_exceeded` or `user_allowance_exceeded`. Treat\n * an unknown code like `insufficient_credits`.\n */\nexport type CreditsHint = {\n kind: \"credits\"\n code: string\n}\n\n/**\n * The provider refused the job's input video without naming a content-policy\n * class (its message is about the file, not a policy). Treat it like any\n * other failure — no fallback model is offered — except that the input to\n * change is known: send a different clip.\n */\nexport type InputRefusedHint = {\n kind: \"input-refused\"\n subject: \"input_video\"\n}\n\n/**\n * A job's `jobs.error_hint` (migration 376) — a user-safe, machine-readable\n * failure verdict, so a caller can act on it without parsing `error_message`\n * prose. One column, several verdict sources; `kind` is the discriminant.\n *\n * HAND-COPIED MIRROR of `ErrorHint` in the platform's\n * `backend/src/lib/safety-block.ts` (which names this file in its own\n * comment). Nothing links the two at build time: a new arm there has to be\n * pasted here verbatim in the same change, with a changeset.\n * `packages/client/dist/index.d.ts` is REBUILT, never hand-edited.\n */\nexport type JobErrorHint = SafetyBlockHint | PolicyBlockHint | CreditsHint | InputRefusedHint\n\n/**\n * The billing lifecycle of a job's credit reservation — `usage_logs.status`\n * for the job's `usage_log_id`. `null` when the job has no usage log (e.g. a\n * free run) or hasn't reserved credits yet.\n */\nexport type CreditStatus = \"reserved\" | \"committed\" | \"refunded\"\n\n/** A job owner's rating of their job — set with {@link JobsResource.rate}. */\nexport type JobRating = \"good\" | \"bad\"\n\n/**\n * The `rating` filter of {@link JobsResource.list}: one rating, any rating\n * (`\"rated\"`) or none (`\"unrated\"`).\n */\nexport type JobRatingFilter = JobRating | \"rated\" | \"unrated\"\n\n/**\n * Job record returned to non-admin SDK consumers. Field names are snake_case\n * to match the wire format (per the OpenAPI fix in Phase 1).\n *\n * Sensitive fields stripped server-side for non-admin callers:\n * `provider`, `provider_cost`, `display_cost`, `credits_actual`. USD\n * pricing is admin-only across api/sdk/mcp — non-admin consumers see\n * only the `credits` abstraction. Server-only nested job data (including\n * Recast's private remux base) is stripped for every caller, including admins.\n */\nexport interface Job {\n id: string\n status: JobStatus\n progress: number\n user_id: string\n input_data: unknown\n output_data: unknown\n error_message: string | null\n /** Present on a failed job the worker classified as a provider\n * content-policy block (`kind: \"safety-block\"`) or that a deployment's job\n * policy rejected (`kind: \"policy-block\"`); absent/null otherwise.\n * User-safe by construction — narrow on `kind` before reading the rest. */\n error_hint?: JobErrorHint | null\n /** PR9: the job's credit reservation lifecycle, derived server-side from\n * `usage_logs.status`. `null` when there is no usage log to report. Never\n * changes the meaning of `credits` (the reserved amount) below. */\n credit_status?: CreditStatus | null\n /** The credits HELD when the job was accepted. Never changes — a job can\n * settle below it; read {@link Job.credits_charged} for what it cost. */\n credits: number | null\n /**\n * What the job actually cost once its hold settled. Can be lower than\n * `credits` (the unused part goes back to the balance), `0` when the hold was\n * refunded, `null` while the job is still running or when it reserved\n * nothing of its own. Sum this — not `credits` — to total spend. Returned by\n * `get()` and `list()`.\n */\n credits_charged?: number | null\n job_type: string | null\n created_at: string\n started_at: string | null\n completed_at: string | null\n /**\n * Provenance: which kind of caller created the job — one of\n * `\"internal\" | \"mcp\" | \"app\" | \"cli\" | \"sdk\" | \"extension\" | \"web\" | \"api\"`.\n * `source_detail` narrows it (origin host, `extension/<name>` label,\n * `sdk/<version>`, MCP client name, developer-app id). Descriptive only;\n * lets a library view label or filter media by origin.\n */\n source?: string | null\n source_detail?: string | null\n /** The generation session this job was born in (the `sessionId` sent with\n * the generation request), or null. See `client.sessions`. */\n session_id?: string | null\n /**\n * With `list({ withSessions: true })`: every session that holds the job now,\n * each linkable — empty when it is in no session (General). Absent otherwise.\n */\n sessions?: JobSessionRef[]\n /**\n * When the job was deleted, on a job read from the Trash\n * (`client.trash.list()`). A deleted job is left out of every other read, so\n * this is otherwise absent or `null`.\n */\n deleted_at?: string | null\n /**\n * Whether the job's owner favorited it (`client.gallery.toggleFavorite`).\n * Returned by `get()` and `list()`. Absent from an older server, and when\n * the server could not read it for a moment — treat absent as unknown,\n * never as `false`.\n */\n favorited?: boolean\n /**\n * The owner's rating of the job ({@link JobsResource.rate}), or `null` when\n * unrated. Returned by `get()` and `list()`; absent like `favorited`.\n */\n rating?: JobRating | null\n}\n\nexport interface CancelJobResult {\n success: true\n cancelled: number\n /**\n * `true` when the job could not be stopped because its provider call is\n * already running — a metered LLM job's call, an external provider task. It\n * finishes and is charged only what it used.\n */\n inFlight?: boolean\n}\n\n/**\n * The kind of caller that created a job (`Job.source`). Hand-copied mirror of\n * `JOB_SOURCES` in the platform's `backend/src/lib/job-source.ts`.\n */\nexport type JobSource = \"internal\" | \"mcp\" | \"app\" | \"cli\" | \"sdk\" | \"extension\" | \"web\" | \"api\"\n\nexport interface DeleteJobResult {\n success: true\n}\n\n/** A session that holds a job now (`Job.sessions`). */\nexport interface JobSessionRef {\n id: string\n title: string | null\n /** The session's project, or `null` when it has none. */\n projectId: string | null\n}\n\nexport interface ListJobsParams {\n /** One status or several (a job matching any of them). An unknown status is a 400. */\n status?: JobStatus | readonly JobStatus[]\n /** Exact match on `input_data.type` — the route that created the job\n * (`\"llm-structured\"`, `\"video-analysis\"`, …). One value or several (up to 20). */\n type?: string | readonly string[]\n /** Exact match on `input_data.origin` — the client app that sent it. */\n origin?: string\n /** The surface that created the job (`Job.source`): `\"mcp\"` lists what your\n * MCP clients (Claude, ChatGPT, …) made. One value or several. */\n source?: JobSource | readonly JobSource[]\n /** `true`: only jobs you favorited; `false`: only jobs you did not. */\n favorited?: boolean\n /** Your rating: `\"good\"`, `\"bad\"`, any (`\"rated\"`) or none (`\"unrated\"`). */\n rating?: JobRatingFilter\n /** The jobs of one generate-image batch (`count` / `models`) — its `batchId`. */\n batchId?: string\n /**\n * The jobs a session of this project holds now — or, `\"none\"`, a session\n * with no project. Each job once, however many sessions hold it. Not with\n * `inSession: false`.\n */\n projectId?: string | \"none\"\n /**\n * `false`: the jobs no session holds now (General), whatever session they\n * were born in. `true`: the jobs at least one session holds.\n */\n inSession?: boolean\n /** `true`: each job carries `sessions`, the sessions that hold it now. */\n withSessions?: boolean\n /** Page size, 1–100 (server default 50). */\n limit?: number\n /** The previous page's `next`. */\n cursor?: string\n}\n\n/** What {@link JobsResource.rate} answers: the job's rating now. */\nexport interface RateJobResult {\n data: { jobId: string; rating: JobRating | null }\n}\n\n/**\n * {@link JobsResource.fromUpload}'s body: an image edited in the browser,\n * already stored with `client.uploads.upload`, and what it was made from.\n */\nexport interface JobFromUploadParams {\n /** The edited image's `assetId`, as `client.uploads.upload` returned it. */\n assetId: string\n /** The job whose image was edited. */\n sourceJobId?: string\n /** Which of `sourceJobId`'s images was edited, when it made several. Default: its `imageUrl`. */\n sourceImageUrl?: string\n /** The upload that was edited, when the source is not a job. Not with `sourceJobId`. */\n sourceAssetId?: string\n /** The editor's design state, stored with `client.uploads.uploadJson`, so the edit can be reopened. */\n editStateUrl?: string\n /** The generation session to file the job in. */\n sessionId?: string\n}\n\n/** A list filter value as the query string carries it: several are comma-separated. */\nfunction listParam(value: string | readonly string[] | undefined): string | undefined {\n if (value === undefined) return undefined\n return typeof value === \"string\" ? value : value.join(\",\")\n}\n\nexport interface ListJobsPage {\n /**\n * A page may hold fewer than `limit` rows — even none — and still carry a\n * `next`. Page on `next`, never on `data.length`.\n */\n data: Job[]\n /** Pass back as `cursor` for the next page; `null` on the last one. */\n next: string | null\n}\n\n/**\n * Lean job status returned by `GET /v1/jobs/:id/status`. Skips the\n * `input_data` JSONB and cost columns. Its `output_data` still goes\n * through the server-only nested-field redaction. Intended for poll loops that\n * only need progress/output/error, and when the job finished.\n */\nexport interface JobStatusResult {\n id: string\n status: JobStatus\n progress?: number\n output_data?: unknown\n error_message?: string | null\n /** PR9: see {@link Job.error_hint}. */\n error_hint?: JobErrorHint | null\n /** PR9: see {@link Job.credit_status}. */\n credit_status?: CreditStatus | null\n /** When the job finished, on the server's clock; null until it has. */\n completed_at?: string | null\n}\n\nexport class JobsResource {\n constructor(private client: NodaroClientCore) {}\n\n /** Get a single job by ID. */\n get(id: string): Promise<{ data: Job }> {\n return this.client.request(\"GET\", `/v1/jobs/${encodeURIComponent(id)}`)\n }\n\n /**\n * Your jobs, newest first (`GET /v1/jobs`), cursor-paginated. `type` and\n * `origin` filter on the job's own `input_data`, which every job carries —\n * a client app lists its runs with `{ type: \"llm-structured\", origin: \"studio\" }`.\n * `status` and `type` take one value or a list; `favorited` and `rating`\n * filter on your own marks, `source` on the surface that created the job.\n * `projectId` / `inSession` filter on where the job is now (a project's\n * sessions, or General), and `withSessions` says which sessions each job is\n * in — for \"everything in this project\". Every filter combines with AND,\n * and each job carries `favorited` and `rating`. A deleted job is never\n * listed: read the Trash with `client.trash.list()`.\n */\n list(params: ListJobsParams = {}): Promise<ListJobsPage> {\n const { status, type, source, inSession, withSessions, ...rest } = params\n return this.client.request<ListJobsPage>(\"GET\", \"/v1/jobs\", {\n query: {\n ...rest,\n status: listParam(status),\n type: listParam(type),\n source: listParam(source),\n inSession: inSession === undefined ? undefined : String(inSession),\n withSessions: withSessions ? \"true\" : undefined,\n },\n })\n }\n\n /**\n * Record an image edited in the browser as a job (`POST /v1/jobs/from-upload`):\n * a completed, private, free `image-edit` job whose `output_data.imageUrl` is\n * the upload, linked to its source in `input_data` (`sourceJobId` /\n * `sourceAssetId`, `sourceImageUrl`). It then lists, rates, favorites and\n * files into a session like any image job. One upload is one job: the same\n * `assetId` again returns the same job. OAuth apps need the `assets:write`\n * scope. A deployment's job policy can refuse the image ({@link JobBlockedError}).\n */\n fromUpload(params: JobFromUploadParams): Promise<{ data: Job }> {\n return this.client.request<{ data: Job }>(\"POST\", \"/v1/jobs/from-upload\", { body: params })\n }\n\n /**\n * Rate one of your jobs `\"good\"` or `\"bad\"`, or clear the rating with\n * `null` (`PUT /v1/jobs/:id/rating`). Only the job's owner rates it — anyone\n * else's job is a `NotFoundError`. The rating reads back as `Job.rating`\n * and filters {@link list}. OAuth apps need the `assets:write` scope.\n */\n rate(id: string, rating: JobRating | null): Promise<RateJobResult> {\n return this.client.request<RateJobResult>(\"PUT\", `/v1/jobs/${encodeURIComponent(id)}/rating`, {\n body: { rating },\n })\n }\n\n /**\n * Get the lean status of a single job (poll-loop friendly).\n * Hits `GET /v1/jobs/:id/status` — returns only id/status/progress/\n * output_data/error_message, with far less wire/CPU cost than `get()`.\n * Same auth + ownership semantics as {@link get}.\n */\n getStatus(id: string): Promise<{ data: JobStatusResult }> {\n return this.client.request(\n \"GET\",\n `/v1/jobs/${encodeURIComponent(id)}/status`,\n )\n }\n\n /**\n * Cancel a job. Server route is `POST /v1/jobs/:jobId/cancel`.\n * Refunds any reserved credit holds. A running LLM job priced per model\n * (metered) whose call has already started answers\n * `{ success: true, cancelled: 0, inFlight: true }`: it finishes and is\n * charged only what it used. A job stopped before its call starts is\n * refunded, except for what an earlier attempt of the same job already used\n * (a retry after a billed attempt): that is charged.\n */\n cancel(id: string): Promise<CancelJobResult> {\n return this.client.request(\n \"POST\",\n `/v1/jobs/${encodeURIComponent(id)}/cancel`,\n )\n }\n\n /**\n * Delete a job: it goes to the Trash (`client.trash.list()`). It is left out\n * of every read from then on, stays restorable for 30 days ({@link restore},\n * back into the sessions it was in), and is then deleted for good, as a\n * delete always was (a copy saved to the Library stays). `{ forever: true }` deletes a job already in the Trash for good now;\n * on a live job it fails with a 409 `NodaroError` whose `code` is\n * `\"not_in_trash\"`. Only the job's owner (or an admin) may delete it; a job\n * that is still running keeps running — cancel it first when its worker\n * should stop ({@link cancel}). Nothing is refunded. OAuth apps need the\n * `assets:write` scope.\n */\n delete(id: string, options: { forever?: boolean } = {}): Promise<DeleteJobResult> {\n return this.client.request(\n \"DELETE\",\n `/v1/jobs/${encodeURIComponent(id)}`,\n options.forever ? { query: { forever: \"true\" } } : {},\n )\n }\n\n /**\n * Bring a deleted job back from the Trash: it reappears in every read and in\n * every session it was in. A job that is not in the Trash succeeds too.\n * OAuth apps need the `assets:write` scope.\n */\n restore(id: string): Promise<{ success: true }> {\n return this.client.request(\"POST\", `/v1/jobs/${encodeURIComponent(id)}/restore`)\n }\n}\n"]}