@nodaro/sdk 2.33.0 → 2.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GKQSA24K.cjs → chunk-3WNEIBTH.cjs} +43 -35
- package/dist/chunk-3WNEIBTH.cjs.map +1 -0
- package/dist/{chunk-JMOKD6ZP.js → chunk-7CUWAVWB.js} +3 -3
- package/dist/{chunk-JMOKD6ZP.js.map → chunk-7CUWAVWB.js.map} +1 -1
- package/dist/{chunk-E5BM4BLO.js → chunk-7IWVDWSP.js} +20 -3
- package/dist/chunk-7IWVDWSP.js.map +1 -0
- package/dist/{chunk-RHJRLXKY.cjs → chunk-7XOK277W.cjs} +9 -9
- package/dist/{chunk-RHJRLXKY.cjs.map → chunk-7XOK277W.cjs.map} +1 -1
- package/dist/{chunk-3QBH2VE2.cjs → chunk-A7QSOD2S.cjs} +20 -3
- package/dist/chunk-A7QSOD2S.cjs.map +1 -0
- package/dist/{chunk-VV4TZG5S.js → chunk-AUKHMT53.js} +3 -3
- package/dist/chunk-AUKHMT53.js.map +1 -0
- package/dist/{chunk-JSCC2N7N.js → chunk-AVOQ5DG4.js} +29 -21
- package/dist/chunk-AVOQ5DG4.js.map +1 -0
- package/dist/{chunk-I6CXC345.js → chunk-D2ILCSOQ.js} +3 -3
- package/dist/{chunk-I6CXC345.js.map → chunk-D2ILCSOQ.js.map} +1 -1
- package/dist/{chunk-4NLVBYMW.js → chunk-DQAYIVVS.js} +42 -8
- package/dist/chunk-DQAYIVVS.js.map +1 -0
- package/dist/chunk-ED3ZOX3M.js +54 -0
- package/dist/chunk-ED3ZOX3M.js.map +1 -0
- package/dist/{chunk-5ALNFATQ.cjs → chunk-EK672FFM.cjs} +2 -2
- package/dist/chunk-EK672FFM.cjs.map +1 -0
- package/dist/{chunk-OXQKE5EG.cjs → chunk-ETX3TLNB.cjs} +43 -9
- package/dist/{chunk-4NLVBYMW.js.map → chunk-ETX3TLNB.cjs.map} +1 -1
- package/dist/{chunk-BRV7Z4GD.cjs → chunk-FHVNTS4E.cjs} +2 -2
- package/dist/{chunk-BRV7Z4GD.cjs.map → chunk-FHVNTS4E.cjs.map} +1 -1
- package/dist/{chunk-N6GLZOE4.cjs → chunk-FM2KM5TY.cjs} +52 -10
- package/dist/chunk-FM2KM5TY.cjs.map +1 -0
- package/dist/{chunk-53SXNN3A.js → chunk-FQNCIP6K.js} +2 -2
- package/dist/chunk-FQNCIP6K.js.map +1 -0
- package/dist/{chunk-KNMW722T.cjs → chunk-FRQJNGJS.cjs} +5 -5
- package/dist/{chunk-KNMW722T.cjs.map → chunk-FRQJNGJS.cjs.map} +1 -1
- package/dist/{chunk-4CXB7RA6.js → chunk-IGMWFEFB.js} +6 -4
- package/dist/chunk-IGMWFEFB.js.map +1 -0
- package/dist/{chunk-56NJIP7P.cjs → chunk-IISVYX5J.cjs} +5 -5
- package/dist/{chunk-56NJIP7P.cjs.map → chunk-IISVYX5J.cjs.map} +1 -1
- package/dist/{chunk-6OGGJQVA.js → chunk-ILG5DHQQ.js} +2 -2
- package/dist/chunk-ILG5DHQQ.js.map +1 -0
- package/dist/{chunk-ZQQABQMH.cjs → chunk-JTOEHQQJ.cjs} +12 -2
- package/dist/chunk-JTOEHQQJ.cjs.map +1 -0
- package/dist/{chunk-ZXJQSFIA.js → chunk-JUON5NXA.js} +2 -2
- package/dist/{chunk-ZXJQSFIA.js.map → chunk-JUON5NXA.js.map} +1 -1
- package/dist/chunk-KARJ7P7Y.js +3 -0
- package/dist/chunk-KARJ7P7Y.js.map +1 -0
- package/dist/{chunk-JGS6YBDW.js → chunk-L6T44A3G.js} +51 -11
- package/dist/chunk-L6T44A3G.js.map +1 -0
- package/dist/{chunk-U4T3Z3K7.cjs → chunk-NKH2UFSD.cjs} +5 -5
- package/dist/chunk-NKH2UFSD.cjs.map +1 -0
- package/dist/{chunk-E2ZXHS6E.js → chunk-OIPPTBW4.js} +2 -2
- package/dist/chunk-OIPPTBW4.js.map +1 -0
- package/dist/{chunk-DKEYSJCE.cjs → chunk-OIYMTGYN.cjs} +2 -2
- package/dist/chunk-OIYMTGYN.cjs.map +1 -0
- package/dist/{chunk-NLQQHJ25.cjs → chunk-OSMKIRKI.cjs} +4 -4
- package/dist/{chunk-NLQQHJ25.cjs.map → chunk-OSMKIRKI.cjs.map} +1 -1
- package/dist/chunk-PG2GBS3K.cjs +4 -0
- package/dist/chunk-PG2GBS3K.cjs.map +1 -0
- package/dist/{chunk-GB724HW2.cjs → chunk-PQ7OKM3E.cjs} +6 -4
- package/dist/chunk-PQ7OKM3E.cjs.map +1 -0
- package/dist/{chunk-Y5CYFNNW.js → chunk-Q6HY2Q5V.js} +4 -4
- package/dist/{chunk-Y5CYFNNW.js.map → chunk-Q6HY2Q5V.js.map} +1 -1
- package/dist/chunk-QGOEQGAE.js +102 -0
- package/dist/chunk-QGOEQGAE.js.map +1 -0
- package/dist/{chunk-B7RG6AUQ.cjs → chunk-QZHRXPN5.cjs} +22 -22
- package/dist/chunk-QZHRXPN5.cjs.map +1 -0
- package/dist/chunk-RQSGS7ZK.cjs +4 -0
- package/dist/chunk-RQSGS7ZK.cjs.map +1 -0
- package/dist/{chunk-KGKCAGRD.cjs → chunk-TCLNSVYA.cjs} +2 -2
- package/dist/chunk-TCLNSVYA.cjs.map +1 -0
- package/dist/chunk-U2B7YQ2N.cjs +56 -0
- package/dist/chunk-U2B7YQ2N.cjs.map +1 -0
- package/dist/chunk-VCEYLJ2Q.cjs +104 -0
- package/dist/chunk-VCEYLJ2Q.cjs.map +1 -0
- package/dist/{chunk-VGWOIWRQ.cjs → chunk-VHSMOBU7.cjs} +2 -2
- package/dist/{chunk-VGWOIWRQ.cjs.map → chunk-VHSMOBU7.cjs.map} +1 -1
- package/dist/{chunk-T3SYREY4.js → chunk-WY4QRJZW.js} +12 -2
- package/dist/chunk-WY4QRJZW.js.map +1 -0
- package/dist/chunk-YCVRQZT3.js +3 -0
- package/dist/chunk-YCVRQZT3.js.map +1 -0
- package/dist/{chunk-DPSMVOM3.js → chunk-YSZM3N7Q.js} +2 -2
- package/dist/chunk-YSZM3N7Q.js.map +1 -0
- package/dist/{chunk-5A7J24LN.js → chunk-Z7UZ3FD3.js} +3 -3
- package/dist/{chunk-5A7J24LN.js.map → chunk-Z7UZ3FD3.js.map} +1 -1
- package/dist/{chunk-6ECW23GA.js → chunk-ZDSC6KBT.js} +3 -3
- package/dist/chunk-ZDSC6KBT.js.map +1 -0
- package/dist/entries/apps.cjs +2 -2
- package/dist/entries/apps.d.cts +4 -0
- package/dist/entries/apps.d.ts +4 -0
- package/dist/entries/apps.js +1 -1
- package/dist/entries/cast-swap.cjs +13 -0
- package/dist/entries/cast-swap.cjs.map +1 -0
- package/dist/entries/cast-swap.d.cts +380 -0
- package/dist/entries/cast-swap.d.ts +380 -0
- package/dist/entries/cast-swap.js +4 -0
- package/dist/entries/cast-swap.js.map +1 -0
- package/dist/entries/characters.d.cts +2 -2
- package/dist/entries/characters.d.ts +2 -2
- package/dist/entries/client.cjs +29 -27
- package/dist/entries/client.d.cts +8 -2
- package/dist/entries/client.d.ts +8 -2
- package/dist/entries/client.js +24 -22
- package/dist/entries/copilot.cjs +4 -4
- package/dist/entries/copilot.js +3 -3
- package/dist/entries/core.cjs +6 -6
- package/dist/entries/core.js +2 -2
- package/dist/entries/creatures.d.cts +2 -2
- package/dist/entries/creatures.d.ts +2 -2
- package/dist/entries/credits.cjs +2 -2
- package/dist/entries/credits.d.cts +37 -2
- package/dist/entries/credits.d.ts +37 -2
- package/dist/entries/credits.js +1 -1
- package/dist/entries/edit.cjs +7 -7
- package/dist/entries/edit.d.cts +3 -0
- package/dist/entries/edit.d.ts +3 -0
- package/dist/entries/edit.js +2 -2
- package/dist/entries/errors.cjs +30 -22
- package/dist/entries/errors.d.cts +42 -4
- package/dist/entries/errors.d.ts +42 -4
- package/dist/entries/errors.js +1 -1
- package/dist/entries/jobs.cjs +2 -2
- package/dist/entries/jobs.d.cts +76 -4
- package/dist/entries/jobs.d.ts +76 -4
- package/dist/entries/jobs.js +1 -1
- package/dist/entries/locations.d.cts +2 -2
- package/dist/entries/locations.d.ts +2 -2
- package/dist/entries/media.cjs +4 -4
- package/dist/entries/media.d.cts +39 -5
- package/dist/entries/media.d.ts +39 -5
- package/dist/entries/media.js +3 -3
- package/dist/entries/nodes.cjs +3 -3
- package/dist/entries/nodes.d.cts +2 -2
- package/dist/entries/nodes.d.ts +2 -2
- package/dist/entries/nodes.js +2 -2
- package/dist/entries/objects.d.cts +2 -2
- package/dist/entries/objects.d.ts +2 -2
- package/dist/entries/prompt-helper.cjs +2 -2
- package/dist/entries/prompt-helper.d.cts +3 -1
- package/dist/entries/prompt-helper.d.ts +3 -1
- package/dist/entries/prompt-helper.js +1 -1
- package/dist/entries/scene3d.d.cts +3 -3
- package/dist/entries/scene3d.d.ts +3 -3
- package/dist/entries/studio.cjs +5 -5
- package/dist/entries/studio.d.cts +2 -2
- package/dist/entries/studio.d.ts +2 -2
- package/dist/entries/studio.js +2 -2
- package/dist/entries/templates.cjs +2 -2
- package/dist/entries/templates.d.cts +4 -0
- package/dist/entries/templates.d.ts +4 -0
- package/dist/entries/templates.js +1 -1
- package/dist/entries/trends.cjs +13 -0
- package/dist/entries/trends.cjs.map +1 -0
- package/dist/entries/trends.d.cts +295 -0
- package/dist/entries/trends.d.ts +295 -0
- package/dist/entries/trends.js +4 -0
- package/dist/entries/trends.js.map +1 -0
- package/dist/entries/tutorials.cjs +2 -2
- package/dist/entries/tutorials.d.cts +4 -0
- package/dist/entries/tutorials.d.ts +4 -0
- package/dist/entries/tutorials.js +1 -1
- package/dist/entries/uploads.cjs +2 -2
- package/dist/entries/uploads.d.cts +14 -1
- package/dist/entries/uploads.d.ts +14 -1
- package/dist/entries/uploads.js +1 -1
- package/dist/entries/workflows.cjs +2 -2
- package/dist/entries/workflows.d.cts +6 -0
- package/dist/entries/workflows.d.ts +6 -0
- package/dist/entries/workflows.js +1 -1
- package/dist/index.cjs +158 -138
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +33 -29
- package/dist/{nodes-C-wrO6Sg.d.ts → nodes-9WpYNWtS.d.ts} +35 -1
- package/dist/{nodes-J8ByUN8z.d.cts → nodes-BDUk5Aej.d.cts} +35 -1
- package/dist/{studio-productions-vbCNSbtf.d.ts → studio-productions-DtTE8mdl.d.ts} +35 -1
- package/dist/{studio-productions-DUHvdSvk.d.cts → studio-productions-r2-VZsKj.d.cts} +35 -1
- package/package.json +12 -2
- package/dist/chunk-3QBH2VE2.cjs.map +0 -1
- package/dist/chunk-4CXB7RA6.js.map +0 -1
- package/dist/chunk-53SXNN3A.js.map +0 -1
- package/dist/chunk-5ALNFATQ.cjs.map +0 -1
- package/dist/chunk-6ECW23GA.js.map +0 -1
- package/dist/chunk-6OGGJQVA.js.map +0 -1
- package/dist/chunk-B7RG6AUQ.cjs.map +0 -1
- package/dist/chunk-DKEYSJCE.cjs.map +0 -1
- package/dist/chunk-DPSMVOM3.js.map +0 -1
- package/dist/chunk-E2ZXHS6E.js.map +0 -1
- package/dist/chunk-E5BM4BLO.js.map +0 -1
- package/dist/chunk-GB724HW2.cjs.map +0 -1
- package/dist/chunk-GKQSA24K.cjs.map +0 -1
- package/dist/chunk-JGS6YBDW.js.map +0 -1
- package/dist/chunk-JSCC2N7N.js.map +0 -1
- package/dist/chunk-KGKCAGRD.cjs.map +0 -1
- package/dist/chunk-N6GLZOE4.cjs.map +0 -1
- package/dist/chunk-OXQKE5EG.cjs.map +0 -1
- package/dist/chunk-T3SYREY4.js.map +0 -1
- package/dist/chunk-U4T3Z3K7.cjs.map +0 -1
- package/dist/chunk-VV4TZG5S.js.map +0 -1
- package/dist/chunk-ZQQABQMH.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resources/studio-productions.ts","../src/resources/studio.ts"],"names":["res","StudioPreviewUnavailable","StudioPreviewAppliedError"],"mappings":"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","file":"chunk-NKH2UFSD.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport { StudioPreviewAppliedError, StudioPreviewUnavailable } from \"../errors.js\"\n\n/**\n * Studio productions — `/v1/studio/productions`.\n *\n * A production is a Nodaro workflow whose `settings.studio` holds the shots:\n * each shot a framed still, an optional animated clip, and the plan, looks,\n * cast bindings and voice that made them. These routes are the platform's own\n * reader and writer of that document, so an agent, a script and the studio app\n * are looking at ONE production rather than three opinions about one row.\n *\n * Two rules run through the whole surface:\n *\n * - **Every change is an OPERATION** ({@link StudioProductionsResource.ops}),\n * addressed by stable key — a shot id, a role slug, a result's job id or url\n * — and never by position. That is what lets two writers hold the same\n * production open: a batch composed against a slightly older version still\n * applies correctly to the newest one, and the response says whether it was\n * rebased.\n * - **Generation is run-then-poll.** A still or clip run returns job ids and\n * records a marker on the production; the results land when anyone — your\n * poll, the app, a later {@link StudioProductionsResource.reconcile} —\n * reports the finished job. Nothing waits minutes inside a request.\n *\n * Reads are `detail: \"summary\"` by default (counts and the active urls);\n * `detail: \"full\"` adds every result with the context that regenerates it.\n *\n * **The ENVELOPES are typed here; the production DOCUMENT is not.** A view, a\n * summary, a shot and an operation are open JSON on this surface\n * ({@link StudioProduction}, `ops: unknown[]`) — their field-level types ship\n * with the studio app, which is the one consumer that narrows them, and a\n * second spelling of that vocabulary in the SDK would drift from it the first\n * time either grew. Everything a caller branches on — `version`, `rebased`,\n * `receipts`, `warnings`, a quote's `credits`, a run's `jobIds` — is typed.\n *\n * Cloud edition only: these routes 404 on deployments that do not serve them.\n * Feature-detect once with `list()` — a deployment without them 404s that too,\n * where a deployment with them answers an empty page.\n */\n\n/**\n * A production, a summary, a shot, a receipt's target: open JSON.\n *\n * Deliberately not narrowed here — see the note at the top of this module.\n */\nexport type StudioProduction = Record<string, unknown>\n\n/** How much of a production a read returns. */\nexport type StudioProductionDetail = \"summary\" | \"full\"\n\n/** One thing wrong with a plan, addressed at the field that is wrong. */\nexport interface StudioPlanIssue {\n path: string\n message: string\n hint?: string\n}\n\n/** `GET …/skill` — the authoring skill, rendered at request time from what is live. */\nexport interface StudioSkillResponse {\n /** The authoring guide. */\n skill: string\n /** Every picker, model and enum, in full. */\n catalog: string\n /** The strict JSON Schema a plan is validated against. */\n schema: Record<string, unknown>\n /** The operating guide: the tool map, the loops, the rules. */\n operating: string\n /** The versions the three were rendered from — bare semver, no package names. */\n generatedFrom: { prompts: string; shared: string; codec: string }\n}\n\nexport interface StudioValidatePlanResponse {\n valid: boolean\n errors: StudioPlanIssue[]\n warnings: StudioPlanIssue[]\n summary?: {\n name?: string\n scenes: number\n shots: number\n cast: number\n /** Cast entries that matched a row in the caller's library. */\n bound: number\n }\n}\n\nexport interface StudioListProductionsResponse {\n data: StudioProduction[]\n nextCursor?: string\n}\n\n/** What a create or an import did, in the words a receipt would use. */\nexport interface StudioImportSummary {\n shotsAdded: number\n castEnrolled: number\n /** Cast entries that resolved to a row in the caller's library. */\n castBound: number\n}\n\nexport interface StudioProductionResponse {\n production: StudioProduction\n warnings?: StudioPlanIssue[]\n summary?: StudioImportSummary\n}\n\nexport interface StudioCreateProductionRequest {\n name?: string\n plan?: Record<string, unknown>\n}\n\n/** The caller's own retry token — 8–128 chars of `A-Za-z0-9_.:-`, never derived. */\nexport type StudioClientRequestId = string\n\n/**\n * A batch of operations, applied atomically.\n *\n * `ops` is `unknown[]`: the operation union is the studio app's vocabulary and\n * the route validates it, refusing the whole batch with the offending index.\n * The served operating guide is the runtime reference for what an operation\n * looks like.\n */\nexport interface StudioOpsRequest {\n /** Applied in order. At most 100 per request. */\n ops: unknown[]\n /**\n * The version the batch was composed against. INFORMATIONAL by default: the\n * operations address by stable KEY, so a batch built on a slightly older\n * document still applies exactly to the newest one, and the response says\n * `rebased: true`. Omitting it means \"apply to whatever is there\".\n */\n baseVersion?: number\n /**\n * Refuse to rebase. With `strict: true` a `baseVersion` older than the stored\n * one is a 409 `workflow_conflict` carrying the current production, instead\n * of a rebased apply — for the rare \"replace exactly what I read\" intent.\n */\n strict?: boolean\n /** Your own token for this batch, so a transport retry is not applied twice. */\n clientRequestId?: StudioClientRequestId\n}\n\n/**\n * What else an operation touched — the work that now has to follow it.\n *\n * Reported by the operations whose effect reaches past the thing they name: a\n * frame review re-opens every frame derived from it and every shot bound to\n * those, a sequence fork mints a whole parallel set. Both lists are always\n * present and either may be empty; ids, never counts, because the caller's job\n * is to go and refresh THOSE.\n */\nexport interface StudioOpsImpact {\n /** Planned frames the operation reaches, the named one first. */\n keyframeIds: string[]\n /** Shots bound to any of those frames. */\n shotIds: string[]\n}\n\n/**\n * A sentence as data: the id of the template a client words it from, and the\n * values that fill the template's slots.\n *\n * It travels beside the English wording, never instead of it. A client that\n * words the sentence in the person's language builds it from `template` and\n * `vars`, and shows the English wording whenever it has no wording for\n * `template` or `vars` does not fill it — so a template id it has never heard of\n * costs nothing. A template id is permanent: a change to its slots or its\n * meaning ships as a new id.\n */\nexport interface StudioOpsText {\n /** The template's id. */\n template: string\n /** What fills the template's slots, by slot name. */\n vars: Record<string, string | number>\n}\n\n/**\n * One line of what an operation did, in the words a change log would use.\n *\n * Past tense, the target named the way the user sees it, the side effect in a\n * trailing parenthetical: `Deleted take 2 of Shot 1 (in the bin).`\n */\nexport interface StudioOpsReceipt {\n /** The operation this line reports, by name. */\n op: string\n /** Past tense, naming the target the way a person would. */\n summary: string\n /**\n * `summary` as data, for a client that words receipts in the person's\n * language. `summary` is always present and always English; read `detail`\n * only when you have wording for its template, and show `summary` when you do\n * not. Absent when the studio service that answered predates it.\n */\n detail?: StudioOpsText\n /** Ids the operation MINTED — never ids the caller supplied. */\n ids?: string[]\n /**\n * The dependency scope, where the operation reports one — absent for the\n * many that touch only what they name. The route projects a receipt ONCE for\n * both answers, so this reads the same on an apply and on a preview.\n */\n impact?: StudioOpsImpact\n}\n\n/** What a batch produced. Adopt `production` whole; do not merge into it. */\nexport interface StudioOpsResponse {\n /** The canonical production AFTER the batch. */\n production: StudioProduction\n /** The version the batch produced. Carry it as the next `baseVersion`. */\n version: number\n /** True when the batch was applied to a newer document than it named. */\n rebased: boolean\n /** One per operation, in order. */\n receipts: StudioOpsReceipt[]\n /**\n * Things worth saying that are not failures — an operation that changed\n * nothing, a rename that rewrote four prompts. A warning never rejects a\n * batch; a refusal does.\n */\n warnings: string[]\n /**\n * `warnings` as data, by position: entry `i` words `warnings[i]`, and `null`\n * means that warning has no wording but the English one. The service sends it\n * only when it is exactly as long as `warnings`, so treat a list of any other\n * length as absent. Absent too when the studio service that answered predates\n * it.\n */\n warningDetails?: Array<StudioOpsText | null>\n}\n\n/**\n * An operation's confirmation class — the vocabulary's own four letters.\n *\n * `S` safe · `D` delete · `P` changes who can reach the work · `$` spends\n * credits. The table that assigns them ships with the production codec and is\n * pinned complete over the operation union there; this is the wire's spelling\n * of its values, not a second table.\n */\nexport type StudioOpClass = \"S\" | \"D\" | \"P\" | \"$\"\n\n/**\n * One line of what an operation WOULD do, for a person deciding whether to let\n * it. Everything {@link StudioOpsReceipt} says — the same summary, the same ids\n * and the same {@link StudioOpsImpact}, read in the conditional — plus the two\n * things the decision turns on and only a preview needs: which kind of change\n * it is, and whether it can be taken back.\n */\nexport interface StudioOpsDryRunReceipt extends StudioOpsReceipt {\n /**\n * The confirmation class, read out of the operation vocabulary's own table:\n * `S` safe, `D` deletes, `P` changes who can reach the work, `$` spends\n * credits. Narrow rather than open, because it is what a caller branches on\n * — a rule like \"never a `D` without asking\" is only writable if the\n * compiler knows the letters.\n */\n class: StudioOpClass\n /**\n * Present, and always `true`, when this operation put something in the bin\n * that can be brought back. ABSENT is the honest answer for everything else,\n * including a delete that destroys: there is no entry to name, so there is no\n * promise to make. Read it as `restorable ?? false`, never as `!restorable`\n * meaning \"kept\".\n */\n restorable?: true\n}\n\n/**\n * What a batch WOULD do. Nothing was written: there is no production here and\n * no new version, because none was produced.\n */\nexport interface StudioOpsDryRunResponse {\n /** Always present, and the marker the two-request protocol reads. */\n dryRun: true\n /** The version the preview was computed against. */\n version: number\n /** One per operation, in order. */\n receipts: StudioOpsDryRunReceipt[]\n /** The same things an apply would have said. */\n warnings: string[]\n /** The same data behind them an apply would have sent: see {@link StudioOpsResponse.warningDetails}. */\n warningDetails?: Array<StudioOpsText | null>\n}\n\n/** What landing the finished jobs did. */\nexport interface StudioReconcileResponse {\n /** Job ids whose media is now on the production. */\n landed: string[]\n /** Job ids still running — their markers stay; call again later. */\n pending: string[]\n /** Job ids that failed or were cancelled; their markers are cleared. */\n failed: string[]\n /** One line per landing that had something to say. */\n warnings: string[]\n /** The production after the landings. */\n production: StudioProduction\n /** Its version — unchanged when nothing landed. */\n version: number\n}\n\n/** `POST …/:id/describe` — a Director run that will land as scenes. */\nexport interface StudioDescribeRequest {\n brief: string\n llmModel: string\n /** `\"append\"` adds the drafted scenes; `\"replace\"` rewrites the film. */\n mode?: \"append\" | \"replace\"\n label?: string\n clientRequestId?: StudioClientRequestId\n}\n\n/**\n * `POST …/:id/generate` — one framing or directing run for one shot.\n *\n * `overrides` is what the CALL owns over what the shot's plan says: the model,\n * the prose, the aspect, the direction ids. Open JSON, because the request\n * builders' own option types are the contract and they ship with the studio.\n */\nexport interface StudioGenerateRequest {\n kind: \"still\" | \"clip\"\n shotId: string\n /** Candidates to fan out (stills); the builder clamps it to the catalog's range. */\n count?: number\n /**\n * The directing input mode for a clip; absent ⇒ derived from the scene's saved\n * inputs (a pinned end frame included). `start` sends ONLY the start frame;\n * `start-end` sends the start and the end frame.\n */\n mode?: \"start\" | \"start-end\" | \"references\"\n /** Price the run and return; nothing is submitted and nothing is written. */\n dryRun?: boolean\n clientRequestId?: StudioClientRequestId\n overrides?: Record<string, unknown>\n}\n\n/** Which video route a directing run went to — chosen from the inputs, never asked for. */\nexport type StudioVideoLane = \"generate-video\" | \"text-to-video\"\n\n/** `dryRun: true` — what this run would cost, and nothing else. */\nexport interface StudioGenerateEstimate {\n dryRun: true\n provider: string\n count: number\n /** `null` when the model is unpriced or the instance runs without credits. */\n credits: number | null\n lane?: StudioVideoLane\n}\n\n/**\n * A started (or already-started) run.\n *\n * `deduped` means the same `clientRequestId` came back and the job ids were\n * answered from the production's own markers — nothing was submitted and\n * nothing was charged, so `production` is absent.\n */\nexport interface StudioGenerateResponse {\n jobIds: string[]\n lane?: StudioVideoLane\n deduped?: true\n production?: StudioProduction\n receipts?: StudioOpsReceipt[]\n warnings?: string[]\n}\n\n/** `POST …/:id/frame` — a still pulled out of the shot's clip. */\nexport interface StudioFrameRequest {\n shotId: string\n mode?: \"first\" | \"last\" | \"timestamp\"\n /** Seconds, when `mode` is `\"timestamp\"`. */\n timestamp?: number\n /** Where the grabbed image lands. `\"new-shot\"` inserts a shot after this one. */\n target?: \"new-shot\" | \"start-frame\" | \"end-frame\" | \"still\"\n clientRequestId?: StudioClientRequestId\n}\n\n/** `POST …/:id/voice` — synthesize a shot's spoken line and record it. */\nexport interface StudioVoiceRequest {\n shotId: string\n text: string\n voiceId?: string\n voiceType?: \"premade\" | \"custom\" | \"library\"\n ttsProvider?: string\n /** The delivery levers; the text-to-speech route owns their bounds. */\n delivery?: Record<string, number>\n clientRequestId?: StudioClientRequestId\n}\n\n/** `POST …/:id/revoice` — recast the voices of the shot's active clip. */\nexport interface StudioRevoiceRequest {\n shotId: string\n /** The speaker-ordered recast plan, forwarded to the voice route verbatim. */\n plan: Record<string, unknown>\n clientRequestId?: StudioClientRequestId\n}\n\n/** `POST …/:id/music` — the film's soundtrack. */\nexport interface StudioMusicRequest {\n prompt: string\n duration?: number\n instrumental?: boolean\n vocalGender?: string\n model?: string\n clientRequestId?: StudioClientRequestId\n}\n\n/**\n * What a route that finished its work answers with: the production as it now\n * stands, plus whatever that route produced.\n *\n * `jobId` is present on the routes that only STARTED something (a revoice, a\n * soundtrack, a draft) — the result lands through its marker.\n */\nexport interface StudioMediaResponse {\n production: StudioProduction\n /** The grabbed frame's url, on the frame route. */\n url?: string\n jobId?: string\n receipts?: StudioOpsReceipt[]\n warnings?: string[]\n}\n\n/**\n * `GET …/:id/export-plan` — the ordered steps an export would run.\n *\n * The caller runs them with the verbs that already exist; a route that chained\n * three minutes-long jobs is the long-lived request every client times out on.\n */\nexport interface StudioExportPlanResponse {\n /** False when the production has fewer than two clips — nothing to stitch. */\n canExport: boolean\n steps: StudioExportStep[]\n /** The step whose output IS the film, or `null` when nothing can run. */\n resultStepId: string | null\n /** Total credits, or `null` when ANY step is unpriced (a partial sum understates). */\n estimate: number | null\n /** The credit models with no price, each named once, in step order. */\n unpriced: string[]\n}\n\nexport interface StudioExportStep {\n /** Stable within one plan and derived from the production, never minted. */\n id: string\n label: string\n /** The node this step runs — `merge-video-audio`, `combine-videos`, `video-upscale`. */\n node: string\n creditModel: string\n credits: number | null\n params: Record<string, unknown>\n}\n\n/** `POST …/:id/clone` — a copy, which always starts private and visible. */\nexport interface StudioCloneRequest {\n name?: string\n}\n\n/** Either arm of a generation reply: the quote, or the run that started. */\nexport type StudioGenerateResult = StudioGenerateEstimate | StudioGenerateResponse\n\n/** True when a generation reply is the `dryRun` QUOTE rather than a started run. */\nexport function isStudioGenerateEstimate(\n result: StudioGenerateResult,\n): result is StudioGenerateEstimate {\n return (result as StudioGenerateEstimate).dryRun === true\n}\n\n/** What a `generateStill` / `generateClip` call decides, beyond `kind` and the shot. */\nexport type StudioGenerateOptions = Omit<StudioGenerateRequest, \"kind\" | \"shotId\">\n\n/**\n * A route that only STARTED something — a draft, a revoice, a soundtrack. Its\n * `jobId` is always there, which is what makes `const { jobId } = await …` and\n * a poll the honest next line.\n *\n * `StudioMediaResponse` leaves `jobId` optional because the routes that FINISH\n * their work (a frame, a voiceover) share the shape and have no job to name.\n */\nexport type StudioJobStartedResponse = StudioMediaResponse & { jobId: string }\n\nexport interface ListStudioProductionsOptions {\n limit?: number\n /** From a previous page's `nextCursor`. */\n cursor?: string\n /** Include the rows the dashboard hides. Off by default. */\n includeArchived?: boolean\n}\n\nexport interface GetProductionOptions {\n detail?: StudioProductionDetail\n /** Read ONE shot — the cheap way to re-read after a generation. */\n shotId?: string\n}\n\nexport interface StudioExportPlanOptions {\n /** Plan the 4K finish too. Expensive, so it is always explicit. */\n upscale?: boolean\n}\n\n/** `client.studio.productions` — see the note at the top of this module. */\nexport class StudioProductionsResource {\n constructor(private client: NodaroClientCore) {}\n\n private path(productionId: string, suffix = \"\"): string {\n return `/v1/studio/productions/${encodeURIComponent(productionId)}${suffix}`\n }\n\n // ── reads ────────────────────────────────────────────────────────────────\n\n /**\n * The authoring guide, the full catalog, the plan's JSON Schema and the\n * operating guide — rendered server-side from the version that is live, so\n * they describe the platform you are actually talking to. Free.\n */\n async skill(): Promise<StudioSkillResponse> {\n const res = await this.client.request<{ data: StudioSkillResponse }>(\n \"GET\",\n \"/v1/studio/productions/skill\",\n )\n return res.data\n }\n\n /**\n * Check a plan before it becomes anything. Free, persists nothing, and\n * resolves cast names against YOUR library — loop on `errors` until\n * `valid: true`, then `create({ plan })`.\n */\n async validatePlan(plan: Record<string, unknown>): Promise<StudioValidatePlanResponse> {\n const res = await this.client.request<{ data: StudioValidatePlanResponse }>(\n \"POST\",\n \"/v1/studio/productions/validate\",\n { body: { plan } },\n )\n return res.data\n }\n\n /** Your productions, newest first. Archived rows are hidden unless asked for. */\n async list(opts: ListStudioProductionsOptions = {}): Promise<StudioListProductionsResponse> {\n const res = await this.client.request<{ data: StudioListProductionsResponse }>(\n \"GET\",\n \"/v1/studio/productions\",\n {\n query: {\n limit: opts.limit,\n cursor: opts.cursor,\n includeArchived: opts.includeArchived,\n },\n },\n )\n return res.data\n }\n\n /**\n * One production. A pure read — it never lands a finished job, so call\n * {@link reconcile} first if you are waiting on one.\n */\n async get(\n productionId: string,\n opts: GetProductionOptions = {},\n ): Promise<StudioProduction> {\n const res = await this.client.request<{ data: { production: StudioProduction } }>(\n \"GET\",\n this.path(productionId),\n { query: { detail: opts.detail, shot_id: opts.shotId } },\n )\n return res.data.production\n }\n\n /**\n * The ordered steps that assemble the film, with what they will cost. Run\n * them yourself with the ordinary verbs — nothing here blocks for minutes,\n * and nothing here spends.\n */\n async exportPlan(\n productionId: string,\n opts: StudioExportPlanOptions = {},\n ): Promise<StudioExportPlanResponse> {\n const res = await this.client.request<{ data: StudioExportPlanResponse }>(\n \"GET\",\n this.path(productionId, \"/export-plan\"),\n { query: { upscale: opts.upscale } },\n )\n return res.data\n }\n\n // ── writes ───────────────────────────────────────────────────────────────\n\n /** A new production, optionally landed from a plan in the same call. */\n async create(input: StudioCreateProductionRequest = {}): Promise<StudioProductionResponse> {\n const res = await this.client.request<{ data: StudioProductionResponse }>(\n \"POST\",\n \"/v1/studio/productions\",\n { body: input },\n )\n return res.data\n }\n\n /**\n * Apply a batch of operations. Atomic: one bad operation refuses the whole\n * batch with a `StudioOpError` naming its index, and nothing is written. On\n * success adopt `production` wholesale and carry `version` forward as the\n * next `baseVersion`.\n *\n * `dryRun: true` — the LITERAL, which is what selects the preview reply —\n * asks what the batch would do, so a person can approve an assistant's edits\n * before they land. Spell it in THIS call's own object literal: a flag passed\n * through a variable widens `true` to `boolean`, which selects the apply\n * overload while the runtime still takes the preview path.\n *\n * A preview is TWO requests, deliberately. The route's body is parsed in\n * strip mode, so a deployment that predates the preview drops the flag and\n * APPLIES the batch: a caller that sent its batch and read the answer\n * afterwards would already have written it. So the flag is proved on an EMPTY\n * batch first and the real batch goes only when that answer carries the\n * marker. An older deployment either answers the apply shape — this throws\n * `StudioPreviewUnavailable`, and the batch never leaves — or refuses the\n * empty batch with its own error, which is passed through as it stands.\n *\n * The ping proves the deployment that answered IT, and a fleet mid-rollout\n * can serve the second request from another one. So the second answer is\n * checked too: when the batch came back APPLIED, this throws\n * `StudioPreviewAppliedError`, which carries that write. The SDK cannot\n * un-send a request; what it can do is refuse to call the result a preview.\n *\n * An apply stays exactly one request.\n */\n ops(\n productionId: string,\n input: StudioOpsRequest & { dryRun: true },\n ): Promise<StudioOpsDryRunResponse>\n ops(productionId: string, input: StudioOpsRequest): Promise<StudioOpsResponse>\n async ops(\n productionId: string,\n input: StudioOpsRequest & { dryRun?: true },\n ): Promise<StudioOpsResponse | StudioOpsDryRunResponse> {\n if (input.dryRun !== true) {\n const res = await this.client.request<{ data: StudioOpsResponse }>(\n \"POST\",\n this.path(productionId, \"/ops\"),\n { body: input },\n )\n return res.data\n }\n\n // The ping carries nothing of the caller's batch, because an old deployment\n // takes whatever it is sent at face value; an empty batch with no\n // `clientRequestId` spends neither the caller's operations nor the retry\n // token that would make a real batch unrepeatable.\n const ping = await this.client.request<{ data?: { dryRun?: unknown } } | undefined>(\n \"POST\",\n this.path(productionId, \"/ops\"),\n { body: { ops: [], dryRun: true } },\n )\n // Read through: an older deployment can answer no envelope at all, or a 204\n // that `request` reads as `undefined`. Absent is a \"no\" like any other, and\n // the caller who catches `NodaroError` should not get a `TypeError`.\n if (ping?.data?.dryRun !== true) throw new StudioPreviewUnavailable()\n\n const res = await this.client.request<\n { data?: StudioOpsDryRunResponse | StudioOpsResponse } | undefined\n >(\"POST\", this.path(productionId, \"/ops\"), { body: input })\n // The answer that matters most: this request may have landed on a different\n // deployment from the ping, and an applied batch is not a preview.\n const data = res?.data\n if ((data as { dryRun?: unknown } | undefined)?.dryRun !== true) {\n throw new StudioPreviewAppliedError(data as StudioOpsResponse)\n }\n return data as StudioOpsDryRunResponse\n }\n\n /**\n * Land every generation that has finished since you last looked, and report\n * what is still running. The one call that turns finished jobs into results\n * without a browser open. It writes only when something actually landed.\n */\n async reconcile(productionId: string): Promise<StudioReconcileResponse> {\n const res = await this.client.request<{ data: StudioReconcileResponse }>(\n \"POST\",\n this.path(productionId, \"/reconcile\"),\n )\n return res.data\n }\n\n /** Add a plan's scenes to a production that already exists. */\n async importPlan(\n productionId: string,\n plan: Record<string, unknown>,\n opts: { mode?: \"append\" } = {},\n ): Promise<StudioProductionResponse> {\n const res = await this.client.request<{ data: StudioProductionResponse }>(\n \"POST\",\n this.path(productionId, \"/import\"),\n { body: { plan, mode: opts.mode ?? \"append\" } },\n )\n return res.data\n }\n\n /**\n * Turn a brief into scenes. The run is a platform job: poll it, and its\n * scenes land through {@link reconcile}. The production comes back with the\n * run recorded on it as a pending draft.\n */\n async describe(\n productionId: string,\n input: StudioDescribeRequest,\n ): Promise<StudioJobStartedResponse> {\n const res = await this.client.request<{ data: StudioJobStartedResponse }>(\n \"POST\",\n this.path(productionId, \"/describe\"),\n { body: input },\n )\n return res.data\n }\n\n // ── generation ───────────────────────────────────────────────────────────\n\n /**\n * Frame or animate one shot.\n *\n * The request is assembled server-side from the shot's own plan, looks and\n * bound references, so a scripted run and a press of the button in the app\n * produce the same media; `overrides` changes THIS run without changing the\n * shot. Prefer {@link generateStill} / {@link generateClip}, which say which\n * kind they mean.\n *\n * `dryRun: true` returns the quote and writes nothing — narrow the reply with\n * {@link isStudioGenerateEstimate}.\n */\n generate(\n productionId: string,\n input: StudioGenerateRequest & { dryRun: true },\n ): Promise<StudioGenerateEstimate>\n generate(\n productionId: string,\n input: StudioGenerateRequest,\n ): Promise<StudioGenerateResult>\n async generate(\n productionId: string,\n input: StudioGenerateRequest,\n ): Promise<StudioGenerateResult> {\n const res = await this.client.request<{ data: StudioGenerateResult }>(\n \"POST\",\n this.path(productionId, \"/generate\"),\n { body: input },\n )\n return res.data\n }\n\n /**\n * Frame a shot: submit `count` candidates and record a pending marker for\n * each. Results ACCUMULATE — a generate never replaces what is there.\n *\n * `clientRequestId` makes a retry safe: the same token answers with the jobs\n * the first call started (`deduped: true`) and submits nothing. Never retry a\n * spend without it.\n */\n generateStill(\n productionId: string,\n shotId: string,\n opts: StudioGenerateOptions & { dryRun: true },\n ): Promise<StudioGenerateEstimate>\n generateStill(\n productionId: string,\n shotId: string,\n opts?: StudioGenerateOptions,\n ): Promise<StudioGenerateResult>\n generateStill(\n productionId: string,\n shotId: string,\n opts: StudioGenerateOptions = {},\n ): Promise<StudioGenerateResult> {\n return this.generate(productionId, { ...opts, kind: \"still\", shotId })\n }\n\n /**\n * Animate a shot. The LANE is chosen from the shot's inputs (a start frame,\n * references, a prompt) — never passed in — and reported back as `lane`.\n */\n generateClip(\n productionId: string,\n shotId: string,\n opts: StudioGenerateOptions & { dryRun: true },\n ): Promise<StudioGenerateEstimate>\n generateClip(\n productionId: string,\n shotId: string,\n opts?: StudioGenerateOptions,\n ): Promise<StudioGenerateResult>\n generateClip(\n productionId: string,\n shotId: string,\n opts: StudioGenerateOptions = {},\n ): Promise<StudioGenerateResult> {\n return this.generate(productionId, { ...opts, kind: \"clip\", shotId })\n }\n\n // ── media on one shot ────────────────────────────────────────────────────\n\n /**\n * Extract a frame from a shot's active clip and put it where `target` says —\n * a new shot after this one (the default), this shot's sticky start or end\n * frame, or another still of the same shot. Seconds, not minutes: this one\n * waits for the job and answers with the changed production.\n */\n async frame(\n productionId: string,\n input: StudioFrameRequest,\n ): Promise<StudioMediaResponse> {\n const res = await this.client.request<{ data: StudioMediaResponse }>(\n \"POST\",\n this.path(productionId, \"/frame\"),\n { body: input },\n )\n return res.data\n }\n\n /** Synthesize a shot's spoken line and record it. Waits for the job. */\n async voice(\n productionId: string,\n input: StudioVoiceRequest,\n ): Promise<StudioMediaResponse> {\n const res = await this.client.request<{ data: StudioMediaResponse }>(\n \"POST\",\n this.path(productionId, \"/voice\"),\n { body: input },\n )\n return res.data\n }\n\n /**\n * Recast the voices of a shot's active clip. Minutes of work, so it answers\n * `jobId` and the new clip lands as a result through its own marker.\n */\n async revoice(\n productionId: string,\n input: StudioRevoiceRequest,\n ): Promise<StudioJobStartedResponse> {\n const res = await this.client.request<{ data: StudioJobStartedResponse }>(\n \"POST\",\n this.path(productionId, \"/revoice\"),\n { body: input },\n )\n return res.data\n }\n\n /** Score the film. The finished track lands through its pending marker. */\n async music(\n productionId: string,\n input: StudioMusicRequest,\n ): Promise<StudioJobStartedResponse> {\n const res = await this.client.request<{ data: StudioJobStartedResponse }>(\n \"POST\",\n this.path(productionId, \"/music\"),\n { body: input },\n )\n return res.data\n }\n\n // ── audience and copies ──────────────────────────────────────────────────\n\n /**\n * Open the share-by-link read. Deliberately its own route rather than an\n * operation: who may see the work is decided by the owner, never as a side\n * effect of a batch that was editing something else.\n */\n async share(productionId: string): Promise<StudioProduction> {\n const res = await this.client.request<{ data: StudioMediaResponse }>(\n \"POST\",\n this.path(productionId, \"/share\"),\n { body: { shared: true } },\n )\n return res.data.production\n }\n\n /** Close it again. Sharing is opt-in AND reversible. */\n async unshare(productionId: string): Promise<StudioProduction> {\n const res = await this.client.request<{ data: StudioMediaResponse }>(\n \"POST\",\n this.path(productionId, \"/unshare\"),\n )\n return res.data.production\n }\n\n /**\n * Copy a production you own or can see into your own Studio project. The\n * copy starts PRIVATE and visible — sharing and archiving never travel — and\n * it is copied through YOUR view of the source, so somebody else's bin does\n * not come with it.\n */\n async clone(\n productionId: string,\n input: StudioCloneRequest = {},\n ): Promise<StudioProduction> {\n const res = await this.client.request<{ data: { production: StudioProduction } }>(\n \"POST\",\n this.path(productionId, \"/clone\"),\n { body: input },\n )\n return res.data.production\n }\n}\n\n","import type { NodaroClientCore } from \"../core.js\"\nimport { StudioProductionsResource } from \"./studio-productions.js\"\nexport * from \"./studio-productions.js\"\n\n/** Public response/transport contracts only; document validation and planning\n * remain in the private Studio codec on the server. */\nexport type StudioDocumentJson = Record<string, unknown>\n\nexport interface StudioProductionCapabilities {\n planVersions: number[]\n operations: {\n readKeyframes: boolean\n saveEditorState?: boolean\n editableSharedCopies?: boolean\n revisionedSharing?: boolean\n cloneLinkedProductions?: boolean\n importPlannedBundles?: boolean\n importLinkedBundles?: boolean\n appendPlannedBundles?: boolean\n appendLinkedBundles?: boolean\n editKeyframes: boolean\n generateKeyframes: boolean\n acceptKeyframes: boolean\n rejectKeyframes?: boolean\n editSequencePlans?: boolean\n generateLinkedClips: boolean\n retakeLinkedClips?: boolean\n /** The host keeps a scene's empty media slots. */\n mediaSlots?: boolean\n /** The host keeps a clip's inputs on its clip slot. */\n clipSlotInputs?: boolean\n }\n sourceFrameReferences: boolean\n automaticAcceptance: false\n unattendedGeneration: false\n}\n\nexport interface StudioKeyframeRecord {\n id: string\n label: string\n revision: number\n nodeId: string\n parentKeyframeId?: string\n previewResultKey: string | null\n previewUrl: string | null\n acceptedResultKey: string | null\n acceptedUrl: string | null\n count: number\n pending: Array<{ jobId: string; startedAt: string }>\n /** Owner-only working state. Recorded acceptance is not an execution grant. */\n plan?: StudioDocumentJson\n acceptance?: StudioDocumentJson\n results?: StudioDocumentJson[]\n}\n\nexport interface StudioProductionRecord {\n id: string\n name: string\n version: number\n updatedAt: string\n thumbnailUrl: string | null\n shared: boolean\n archived: boolean\n requiredCapabilities?: string[]\n keyframes?: StudioKeyframeRecord[]\n sequences?: StudioDocumentJson[]\n shots: StudioDocumentJson[]\n pending: { stills: number; clips: number; keyframes?: number; music: boolean; draft: StudioDocumentJson | null }\n [field: string]: unknown\n}\n\nexport interface StudioProductionReply {\n production: StudioProductionRecord\n capabilities?: StudioProductionCapabilities\n warnings?: unknown[]\n [field: string]: unknown\n}\n\nexport interface StudioEditInput {\n ops: ReadonlyArray<{ op: string; [field: string]: unknown }>\n baseVersion?: number\n strict?: boolean\n clientRequestId?: string\n}\n\nexport interface StudioKeyframeGenerationInput {\n keyframeId: string\n expectedRevision: number\n clientRequestId?: string\n overrides?: StudioDocumentJson\n}\n\nexport interface StudioKeyframeAcceptanceInput {\n keyframeId: string\n expectedRevision: number\n resultKey: string\n expectedAcceptedResultKey: string | null\n requirementChecks: Array<{ requirementId: string; outcome: \"pass\" | \"waived\" }>\n waivedReason?: string\n}\n\nexport interface StudioShotGenerationInput {\n kind: \"still\" | \"clip\"\n shotId: string\n clientRequestId?: string\n count?: number\n mode?: \"start\" | \"start-end\" | \"references\"\n dryRun?: boolean\n /** Linked clips only: require the exact inputs returned by a reviewed quote. */\n expectedInputHash?: string\n /** Retake a native linked take using its original request and retained endpoints. */\n retakeResultKey?: string\n overrides?: StudioDocumentJson\n}\n\nexport type StudioGenerationReply = {\n jobIds: string[]\n production?: StudioProductionRecord\n deduped?: true\n lane?: string\n [field: string]: unknown\n} | {\n dryRun: true; provider: string; count: number; credits: number | null; lane?: string\n /** Linked-clip quotes include normalized settings and accepted endpoint pins. */\n inputHash?: string\n endpointPins?: StudioDocumentJson\n creditIdentifier?: string\n duration?: number\n resolution?: string\n aspectRatio?: string\n sound?: boolean\n}\n\nconst root = \"/v1/studio/productions\"\nconst path = (id: string) => `${root}/${encodeURIComponent(id)}`\n\n/** Cloud plugin routes. Check capabilities before exposing dependency controls.\n * Reads do not reconcile jobs, submit media or accept generated candidates. */\nexport class StudioResource {\n readonly productions: StudioProductionsResource\n\n constructor(private readonly client: NodaroClientCore) {\n this.productions = new StudioProductionsResource(client)\n }\n\n capabilities(): Promise<{ data: StudioProductionCapabilities }> {\n return this.client.request(\"GET\", `${root}/capabilities`)\n }\n\n skill(): Promise<{ data: StudioDocumentJson }> {\n return this.client.request(\"GET\", `${root}/skill`)\n }\n\n list(options: { limit?: number; cursor?: string; includeArchived?: boolean } = {}): Promise<{ data: {\n data: Array<{ id: string; name: string; version: number; updatedAt: string; thumbnailUrl: string | null; shared: boolean; archived: boolean; shotCount: number }>\n nextCursor?: string\n } }> {\n return this.client.request(\"GET\", root, { query: options })\n }\n\n get(id: string, options: { detail?: \"summary\" | \"full\"; shotId?: string } = {}): Promise<{ data: StudioProductionReply }> {\n return this.client.request(\"GET\", path(id), { query: { detail: options.detail, shot_id: options.shotId } })\n }\n\n validatePlan(plan: StudioDocumentJson): Promise<{ data: { valid: boolean; errors: StudioDocumentJson[]; warnings: StudioDocumentJson[]; summary?: StudioDocumentJson } }> {\n return this.client.request(\"POST\", `${root}/validate`, { body: { plan } })\n }\n\n create(input: { name?: string; plan?: StudioDocumentJson }): Promise<{ data: StudioProductionReply }> {\n return this.client.request(\"POST\", root, { body: input })\n }\n\n /** Import a portable document through the compatible server writer.\n * Retained frame media requires an authorized source; acceptance never transfers. */\n importBundle(input: { bundle: StudioDocumentJson; projectId?: string }): Promise<{ data: StudioProductionReply }> {\n return this.client.request(\"POST\", `${root}/import-bundle`, { body: input })\n }\n\n /** Append a bundle's scenes and frame closure under an exact destination revision. */\n appendBundle(id: string, input: { bundle: StudioDocumentJson; expectedVersion: number;\n afterShotId?: string; applyFilm?: boolean }): Promise<{ data: {\n production: StudioProductionRecord; importedShotIds: string[]; importedKeyframeIds: string[];\n } }> {\n return this.client.request(\"POST\", `${path(id)}/import-bundle`, { body: input })\n }\n\n /** Copy a saved production. Linked copies retain verified inputs, remap\n * dependencies and require fresh frame acceptance; they start no media jobs. */\n clone(id: string, input: { name?: string; projectId?: string; expectedVersion?: number } = {}): Promise<{ data: StudioProductionReply }> {\n return this.client.request(\"POST\", `${path(id)}/clone`, { body: input })\n }\n\n edit(id: string, input: StudioEditInput): Promise<{ data: StudioProductionReply & { version: number; rebased: boolean; receipts: StudioDocumentJson[] } }> {\n return this.client.request(\"POST\", `${path(id)}/ops`, { body: input })\n }\n\n /** Audience-authorized sharing; an expected revision prevents publication\n * of concurrent edits the caller has not reviewed. */\n setShared(id: string, input: { shared: boolean; allowEditableCopy?: boolean; expectedVersion?: number }): Promise<{ data: StudioProductionReply }> {\n return this.client.request(\"POST\", `${path(id)}/share`, { body: input })\n }\n\n /** Save ordinary editor fields against the loaded revision. Protected frame\n * and job state can only change through their dedicated semantic actions. */\n saveEditorState(id: string, input: { expectedVersion: number; graph: object; clientRequestId?: string }) {\n return this.edit(id, {\n baseVersion: input.expectedVersion, strict: true,\n ...(input.clientRequestId ? { clientRequestId: input.clientRequestId } : {}),\n ops: [{ op: \"save_editor_state\", expectedVersion: input.expectedVersion, graph: input.graph }],\n })\n }\n\n generateKeyframe(id: string, input: StudioKeyframeGenerationInput): Promise<{ data: Exclude<StudioGenerationReply, { dryRun: true }> }> {\n return this.client.request(\"POST\", `${path(id)}/generate`, { body: { ...input, kind: \"keyframe\" } })\n }\n\n generateShot(id: string, input: StudioShotGenerationInput): Promise<{ data: StudioGenerationReply }> {\n return this.client.request(\"POST\", `${path(id)}/generate`, { body: input })\n }\n\n /** A separate explicit review action; generation never calls this method. */\n acceptKeyframe(id: string, input: StudioKeyframeAcceptanceInput,\n concurrency: Pick<StudioEditInput, \"baseVersion\" | \"strict\" | \"clientRequestId\"> = {}) {\n return this.edit(id, { ...concurrency, ops: [{ ...input, op: \"accept_keyframe_result\" }] })\n }\n\n reconcile(id: string): Promise<{ data: StudioProductionReply & { landed: string[]; pending: string[]; failed: string[]; version: number } }> {\n return this.client.request(\"POST\", `${path(id)}/reconcile`, { body: {} })\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resources/prompt-helper.ts"],"names":[],"mappings":";AAuEO,IAAM,uBAAN,MAA2B;AAAA,EAChC,YAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B,MAAA;AAAA,EAEpB,QAAQ,KAAA,EAA6C;AACnD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,0BAAA,EAA4B,EAAE,IAAA,EAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,GAAG,KAAA,IAAS,CAAA;AAAA,EAC1G;AAAA,EAEA,SAAS,KAAA,EAA6C;AACpD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,0BAAA,EAA4B,EAAE,IAAA,EAAM,EAAE,MAAA,EAAQ,UAAA,EAAY,GAAG,KAAA,IAAS,CAAA;AAAA,EAC3G;AAAA,EAEA,QAAQ,KAAA,EAA4C;AAClD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,MAAA,EAAQ,0BAAA,EAA4B,EAAE,IAAA,EAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,GAAG,KAAA,IAAS,CAAA;AAAA,EAC1G;AACF","file":"chunk-OIPPTBW4.js","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport type { WizardQuestion, WizardSelection, RecommendedModel, WizardNodeContext } from \"@nodaro/prompts/doctrine\"\nexport type { WizardQuestion, WizardOption, WizardSelection, RecommendedModel, WizardNodeContext } from \"@nodaro/prompts/doctrine\"\n\n// Re-export the canonical wizard types (SSOT in @nodaro/shared).\n\ninterface CommonInput {\n nodeType: string\n provider?: string\n style?: string\n aspectRatio?: string\n duration?: number\n llmModel?: string\n /** Reasoning effort level (model-dependent). */\n reasoningEffort?: string\n /**\n * Gemini and Claude models. Runs the request on the provider's own API so\n * `temperature`, `maxTokens` and the full reasoning range actually take\n * effect — on the default lane those levers are not reliably honoured.\n * The call is charged its actual usage at the vendor's direct rate (the price key ends `:direct`), so\n * it usually costs more than the default lane; where the server prices LLM calls by tier instead, it\n * bills one credit rung up (a premium model moves to premium-direct). A model without a direct lane returns\n * `400 advanced_mode_unsupported`.\n */\n advancedMode?: boolean\n /** Sampling temperature. Only honoured when `advancedMode` is true. */\n temperature?: number\n /** Output token cap. Only honoured when `advancedMode` is true. */\n maxTokens?: number\n nodeContext?: WizardNodeContext\n userPreference?: string\n /** Associates this call with a workflow execution. Read server-side before Zod. */\n workflowId?: string\n}\n\nexport interface AnalyzeInput extends CommonInput {\n /** The user's rough idea. Omit to build questions from scratch. */\n prompt?: string\n}\nexport interface AnalyzeResult {\n jobId: string\n questions: WizardQuestion[]\n}\n\nexport interface GenerateInput extends CommonInput {\n /** The chosen answers from analyze. */\n selections: WizardSelection[]\n /** The user's original rough idea, woven into the generated prompt. */\n originalPrompt?: string\n}\n\nexport interface EnhanceInput extends CommonInput {\n /** The rough idea to improve one-shot. Omit to build from scratch. */\n prompt?: string\n}\n\nexport interface PromptResult {\n jobId: string\n prompt: string\n recommendedModel?: RecommendedModel\n}\n\n/**\n * AI Prompt Wizard — help write/improve prompts for generation nodes.\n *\n * - `analyze` -> guided questions, `generate` -> prompt from selections (the\n * 2-step human flow), or `enhance` -> one-shot \"improve this prompt\".\n *\n * All three delegate to `POST /v1/prompt-helper/wizard`. Throws `NodaroError`\n * on 4xx/5xx (e.g. `validation_error`, `malformed_response`).\n */\nexport class PromptHelperResource {\n constructor(private client: NodaroClientCore) {}\n\n analyze(input: AnalyzeInput): Promise<AnalyzeResult> {\n return this.client.request(\"POST\", \"/v1/prompt-helper/wizard\", { body: { action: \"analyze\", ...input } })\n }\n\n generate(input: GenerateInput): Promise<PromptResult> {\n return this.client.request(\"POST\", \"/v1/prompt-helper/wizard\", { body: { action: \"generate\", ...input } })\n }\n\n enhance(input: EnhanceInput): Promise<PromptResult> {\n return this.client.request(\"POST\", \"/v1/prompt-helper/wizard\", { body: { action: \"enhance\", ...input } })\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resources/apps.ts"],"names":[],"mappings":"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","file":"chunk-OIYMTGYN.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\n\n/**\n * A published app — a workflow wrapped in a curated input/output presentation.\n * Returned by the public `/v1/apps/browse` endpoint.\n */\nexport interface PublishedApp {\n id: string\n slug: string\n name: string\n description?: string | null\n creatorId: string\n creatorName?: string | null\n thumbnailUrl?: string | null\n category?: string | null\n isFeatured?: boolean\n runCount?: number\n /** The listed price per run; when `perMinuteCredits` is above 0, its fixed part. */\n estimatedCredits?: number | null\n /** The listed price holds a metered LLM call's ESTIMATE (its model's typical\n * call, charged its actual usage per token): show it `~N`. Absent for a\n * fixed price, and on a deployment running the tier-pricing rollback. */\n estimatedCreditsIsEstimate?: true\n /**\n * Credits per minute of the recording the app is given, when its price\n * follows that recording's length: the price is `estimatedCredits` plus\n * this per minute. 0 or absent when the price does not depend on a\n * recording's length.\n */\n perMinuteCredits?: number\n /**\n * Credits for each item beyond the creator's saved items of a list input\n * the app's user fills: the price is the listed one plus this per further\n * item. 0 or absent when the app has no such input.\n */\n perItemCredits?: number\n /**\n * The listed price is a range: `estimatedCredits` (with `perMinuteCredits` /\n * `perItemCredits`) is \"from\" — the app's saved settings — and these are\n * \"up to\": the most a run can cost over the choices the app's user has (every\n * exposed field, at the creator's restricted values where set). Present only\n * when they differ from \"from\"; absent on an app published before the range\n * existed until its next publish.\n */\n maxEstimatedCredits?: number\n maxPerMinuteCredits?: number\n maxPerItemCredits?: number\n createdAt: string\n updatedAt: string\n}\n\nexport interface ListAppsParams {\n /** Substring search across app name + description. */\n search?: string\n /** Page size; backend caps at 50. */\n limit?: number\n /** Cursor token returned by the previous page. */\n cursor?: string\n /** Filter to a single category slug. */\n category?: string\n}\n\nexport interface ListAppsResult {\n data: PublishedApp[]\n nextCursor?: string | null\n}\n\n/**\n * App detail — includes the input schema (required + optional fields end users\n * fill in) and the output mapping (which workflow nodes produce which display\n * cards).\n */\nexport interface PublishedAppDetail extends PublishedApp {\n inputSchema: Record<string, unknown>\n outputs: Array<{ nodeId: string; label: string; type: string }>\n /**\n * The app run alone: the listed price less its Render final part (run and\n * charged separately) — fixed, per minute of the recording and per further\n * list item. Absent from a server that predates them; read the listed\n * `estimatedCredits` / `perMinuteCredits` / `perItemCredits` instead.\n */\n runEstimatedCredits?: number\n runPerMinuteCredits?: number\n runPerItemCredits?: number\n /**\n * Whether this server charges Edit Plan per started minute of the recording\n * (otherwise in 15/30/60/90/120/180-minute steps). Absent from a server that\n * predates it: read it as the steps.\n */\n editPlanPerMinute?: boolean\n}\n\nexport interface AppRunResult {\n /** The execution-id that was started — poll via client.executions.get(). */\n executionId: string\n status: \"pending\" | \"running\"\n /** App-run id (distinct from executionId — used by listRuns/getRun). */\n runId?: string\n}\n\nexport interface AppRun {\n id: string\n appSlug: string\n executionId: string\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"cancelled\"\n inputs: Record<string, unknown>\n outputs?: Array<{ nodeId: string; type: string; url?: string; text?: string }>\n startedAt: string\n finishedAt?: string | null\n}\n\nexport interface ListAppRunsParams {\n limit?: number\n cursor?: string\n}\n\n/**\n * Result of a soft-delete (archive) operation. The run is moved to the user's\n * archive in the Nodaro UI; restoration and permanent deletion are UI-only by\n * design — SDK / MCP / API delete callers can't accidentally destroy data.\n */\nexport interface DeleteAppRunResult {\n success: true\n archived: true\n}\n\n/** Advanced per-run options for `apps.run`. */\nexport interface RunAppOptions {\n /**\n * Raw node-data overrides keyed by node id → field → value, merged onto the\n * app's workflow for THIS run only. Reaches fields that are NOT exposed as app\n * inputs — e.g. `{ [nodeId]: { promptPrefix: \"…\", promptSuffix: \"…\" } }` to\n * wrap the end user's prompt with text they never see.\n */\n inputOverrides?: Record<string, Record<string, unknown>>\n}\n\n/** Options for `apps.resume` — the Retry of a failed app run. */\nexport interface ResumeAppRunOptions {\n /** Answer what the Retry would run and charge, and start nothing. */\n dryRun?: boolean\n /**\n * The app version you expect the Retry to run — the one starting over would\n * run. Absent: the app's latest. A run of another version is refused with\n * `app_run_version_changed`; send the run's own version (the refusal's\n * `runVersion`) to continue on it anyway.\n */\n version?: number\n}\n\n/** One step a Retry runs again. */\nexport interface AppRunResumeStep {\n nodeId: string\n type: string\n /** The node's label, or `null` when it has none. */\n label: string | null\n}\n\n/** What a Retry would do (`apps.resume(…, { dryRun: true })`); nothing is started. */\nexport interface AppRunResumePlan {\n runId: string\n /** The failed execution the Retry continues. */\n continuesExecutionId: string\n /** The app version the Retry runs (the run's own). */\n appVersion: number | null\n /** Every node the Retry runs, in graph order. */\n nodeIds: string[]\n /** The steps that run again. */\n steps: AppRunResumeStep[]\n /** What the Retry is expected to charge, the creator's fee included; `null` without credits. */\n estimatedCredits: number | null\n /** Clips handed back at no cost. */\n reusedClips: number\n /** Whether your balance and your app credits cover what the Retry holds while it runs; `null` without credits. */\n sufficient: boolean | null\n /** Your spendable balance; `null` without credits. */\n available: number | null\n /** Your app credits, present only when they are what stops the Retry (it would be refused `insufficient_app_credits`). */\n appCreditsAllowance?: number\n}\n\n/**\n * A Retry that started. `deduped: true` when a concurrent double press reached\n * the same Retry; a second call made while the first is pending is refused\n * with `already_running` (`AlreadyRunningError`) instead.\n */\nexport interface AppRunResumeResult {\n /** The Retry's execution — the run now points at it. Poll via client.executions.get(). */\n executionId: string\n runId: string\n status: \"pending\"\n /** Every node the Retry runs. */\n nodeIds: string[]\n /** `true` when a concurrent press had already started this Retry. */\n deduped?: true\n}\n\n/**\n * The `code` the server gives an `apps.resume` refusal. Nothing is written or\n * billed when a Retry is refused. How each arrives:\n * - `app_run_version_changed` as `AppRunVersionChangedError`;\n * - `already_running` as `AlreadyRunningError` (with the live `executionId`);\n * - `not_found` and `continuation_not_found` as `NotFoundError`, whose `code`\n * is always `not_found` (the server's message is kept);\n * - `insufficient_credits` and `insufficient_app_credits` as\n * `InsufficientCreditsError`, whose `code` is always `insufficient_credits`\n * (the server's message says which);\n * - the rest as `NodaroError` carrying this code.\n */\nexport type AppRunResumeRefusalCode =\n /** No such run of yours under this app. */\n | \"not_found\"\n /** The run's execution is not yours. */\n | \"continuation_not_found\"\n /** A malformed run id or body, or a component's run (continued by the run that called it). */\n | \"validation_error\"\n /** The app version the run ran is gone: start the run over. */\n | \"app_run_version_unavailable\"\n /** The app is on another version than the run ran: start over, or send `version` = the run's. */\n | \"app_run_version_changed\"\n /** The run's execution ran another workflow, or another version of it (rare). */\n | \"continuation_workflow_mismatch\"\n | \"continuation_version_mismatch\"\n /** The run did not fail (it completed or was stopped): start it over. */\n | \"continuation_not_failed\"\n /** The run is still running, a Retry of it is pending, or it was started again elsewhere. */\n | \"already_running\"\n /** Nothing is left to retry: every step completed (409), or the app runs no step (400). */\n | \"continuation_subset_required\"\n /** An input the run applied sets a field the app does not let a runner set. */\n | \"locked_field\"\n /**\n * The Retry would stop at a Preview with nobody to review it: retry it from the app page.\n * (The same refusal arrives under the other preview refusal codes when a review-on use-case\n * node holds the run; `isPreviewRefusalCode` in `@nodaro/shared/nodes` asks for all of them.)\n */\n | \"preview_review_required\"\n /** Your app credits cannot cover it. */\n | \"insufficient_app_credits\"\n /** Your balance cannot cover it. */\n | \"insufficient_credits\"\n /** Billing is briefly unavailable for a workspace run (503): try again shortly. */\n | \"billing_unavailable\"\n\nexport class AppsResource {\n constructor(private client: NodaroClientCore) {}\n\n /** List published apps. Public — no auth required for community apps. */\n list(params: ListAppsParams = {}): Promise<ListAppsResult> {\n const qs = new URLSearchParams()\n if (params.search) qs.set(\"search\", params.search)\n if (params.limit !== undefined) qs.set(\"limit\", String(params.limit))\n if (params.cursor) qs.set(\"cursor\", params.cursor)\n if (params.category) qs.set(\"category\", params.category)\n const query = qs.toString()\n return this.client.request(\"GET\", `/v1/apps/browse${query ? `?${query}` : \"\"}`)\n }\n\n /** Get one app's metadata + input schema by slug. */\n get(slug: string): Promise<{ data: PublishedAppDetail }> {\n return this.client.request(\"GET\", `/v1/app/${encodeURIComponent(slug)}`)\n }\n\n /**\n * Trigger an app run with the given input values. The keys in `inputs` must\n * match the app's input-schema field names (see `get(slug).inputSchema`).\n * `opts.inputOverrides` adds raw per-node overrides (see {@link RunAppOptions}).\n * Returns the execution-id for status polling via client.executions.get().\n */\n run(\n slug: string,\n inputs: Record<string, unknown> = {},\n opts: RunAppOptions = {},\n ): Promise<AppRunResult> {\n return this.client.request(\"POST\", `/v1/app/${encodeURIComponent(slug)}/run`, {\n body: { inputs, ...(opts.inputOverrides ? { inputOverrides: opts.inputOverrides } : {}) },\n })\n }\n\n /** List past runs for an app (the caller must own the app or the runs). */\n listRuns(slug: string, params: ListAppRunsParams = {}): Promise<{ data: AppRun[]; nextCursor?: string | null }> {\n const qs = new URLSearchParams()\n if (params.limit !== undefined) qs.set(\"limit\", String(params.limit))\n if (params.cursor) qs.set(\"cursor\", params.cursor)\n const query = qs.toString()\n return this.client.request(\n \"GET\",\n `/v1/app/${encodeURIComponent(slug)}/runs${query ? `?${query}` : \"\"}`,\n )\n }\n\n /** Get one app-run by id. */\n getRun(slug: string, runId: string): Promise<{ data: AppRun }> {\n return this.client.request(\n \"GET\",\n `/v1/app/${encodeURIComponent(slug)}/runs/${encodeURIComponent(runId)}`,\n )\n }\n\n /**\n * Retry a FAILED app run from where it stopped: only the steps it did not\n * complete run, and only those are charged; every step it completed hands\n * on its result. It runs the version the run ran, with the inputs the run\n * applied (it takes none of its own). The run's execution moves to the\n * Retry's.\n *\n * `{ dryRun: true }` answers what the Retry would run and charge\n * ({@link AppRunResumePlan}) and starts nothing — show it before a Retry.\n * Refusals are listed in {@link AppRunResumeRefusalCode}; to run with other\n * inputs or on the app's current version, start over with\n * `run(slug, inputs)` instead.\n *\n * @param slug The published app's slug.\n * @param runId The run's UUID (`AppRunResult.runId`, `AppRun.id`).\n */\n resume(slug: string, runId: string, opts: ResumeAppRunOptions & { dryRun: true }): Promise<{ data: AppRunResumePlan }>\n resume(slug: string, runId: string, opts?: ResumeAppRunOptions & { dryRun?: false }): Promise<AppRunResumeResult>\n resume(slug: string, runId: string, opts?: ResumeAppRunOptions): Promise<{ data: AppRunResumePlan } | AppRunResumeResult>\n resume(slug: string, runId: string, opts: ResumeAppRunOptions = {}): Promise<{ data: AppRunResumePlan } | AppRunResumeResult> {\n return this.client.request(\n \"POST\",\n `/v1/app/${encodeURIComponent(slug)}/runs/${encodeURIComponent(runId)}/resume`,\n {\n body: {\n ...(opts.dryRun ? { dryRun: true } : {}),\n ...(opts.version !== undefined ? { version: opts.version } : {}),\n },\n },\n )\n }\n\n /**\n * Archive (soft-delete) a published-app run. The run is hidden from the\n * default run list and can be restored or permanently deleted from the\n * archive view at https://app.nodaro.ai/archived-runs.\n *\n * @param slug The published app's slug (the last path segment of its URL).\n * @param runId The run's UUID.\n */\n deleteRun(slug: string, runId: string): Promise<DeleteAppRunResult> {\n return this.client.request(\n \"DELETE\",\n `/v1/app/${encodeURIComponent(slug)}/runs/${encodeURIComponent(runId)}`,\n )\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkFRQJNGJS_cjs = require('./chunk-FRQJNGJS.cjs');
|
|
4
4
|
|
|
5
5
|
// src/resources/copilot.ts
|
|
6
6
|
var COPILOT_FRAME_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -93,12 +93,12 @@ var CopilotResource = class {
|
|
|
93
93
|
}),
|
|
94
94
|
signal: opts.signal
|
|
95
95
|
});
|
|
96
|
-
for await (const value of
|
|
96
|
+
for await (const value of chunkFRQJNGJS_cjs.readSseStream(res, { label: "copilot stream" })) {
|
|
97
97
|
if (isCopilotFrame(value)) yield value;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
exports.CopilotResource = CopilotResource;
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
104
|
-
//# sourceMappingURL=chunk-
|
|
103
|
+
//# sourceMappingURL=chunk-OSMKIRKI.cjs.map
|
|
104
|
+
//# sourceMappingURL=chunk-OSMKIRKI.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resources/copilot.ts"],"names":["readSseStream"],"mappings":"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","file":"chunk-NLQQHJ25.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport { readSseStream } from \"../sse.js\"\n\n/**\n * The assistant's conversation surface (`/v1/copilot/*`) — threads, and one\n * turn as a live stream.\n *\n * A thread is the conversation; a TURN is one message and everything the\n * assistant does in answer to it. `stream` is the only method that spends: it\n * POSTs the message and yields the turn's frames as they arrive, so a caller\n * renders prose, tool activity and proposals live instead of waiting for a\n * finished answer.\n *\n * Two shapes here are deliberately TOLERANT of a deployment that predates a\n * field, because the SDK ships ahead of every instance it talks to:\n * - `surface` is optional on both {@link CopilotThread} and the `metadata`\n * frame — absent is a value (\"this deployment does not say\"), never a\n * default this package invents.\n * - An event kind this package does not model is IGNORED, not thrown: a\n * newer server may emit frames a caller was built before.\n */\n\n/** How this thread runs what it proposes: ask first, or run within a budget. */\nexport type CopilotRunMode = \"ask\" | \"auto\"\n\n/** The thread's model rung. Absent from a server that predates it. */\nexport type CopilotModelTier = \"economy\" | \"standard\" | \"premium\"\n\n/**\n * Which product the conversation belongs to.\n *\n * NO deployment sends this today — nothing in the platform emits it yet — and\n * it is typed here so that the day one does, a caller reads what the server\n * said. That is why it is OPTIONAL everywhere it appears: absent is a value\n * (\"this deployment does not say\"), never a default this package invents.\n */\nexport type CopilotSurface = \"studio\" | \"workflow\"\n\nexport interface CopilotThread {\n id: string\n workflowId: string\n runMode: CopilotRunMode\n /** See {@link CopilotSurface} — absent means the server did not say. */\n surface?: CopilotSurface\n /** Whether this thread may build nodes that publish to connected accounts. */\n allowPublishing?: boolean\n modelTier?: CopilotModelTier\n autoRunLimitCredits: number\n userTurnCount: number\n lastMessageAt: string | null\n createdAt: string\n /** Only on {@link CopilotResource.get} — derived from a live turn. */\n status?: \"running\" | \"idle\"\n activeTurnId?: string | null\n}\n\n/** The workflow a thread was opened on, as `create` answers it. */\nexport interface CopilotThreadWorkflow {\n id: string\n projectId: string\n name: string\n version: number\n}\n\nexport type CopilotMessagePart =\n | { kind: \"text\"; text: string }\n | { kind: \"tool_call\"; id: string; name: string; label: string; status: \"finished\" | \"failed\" }\n\nexport interface CopilotMessage {\n id: string\n seq: number\n turnId: string\n role: \"user\" | \"assistant\"\n createdAt: string\n parts: CopilotMessagePart[]\n}\n\n/** Open a thread on an existing workflow, or on one this call creates. */\nexport interface CreateCopilotThreadInput {\n /** The workflow to work on. Give this OR `prompt`. */\n workflowId?: string\n /** Start from nothing: the server creates a workflow seeded by this prompt. */\n prompt?: string\n /** Name for a workflow created by `prompt` (defaults to the prompt's head). */\n name?: string\n}\n\nexport interface CopilotStreamOptions {\n /** The user's turn. */\n message: string\n /** The workflow version this message was composed against — a newer one 409s. */\n baseVersion?: number\n /** The composer's echo of the thread's rung; the thread row stays authoritative. */\n tier?: CopilotModelTier\n /**\n * Ends the stream. The client's request timeout is NOT applied to a turn.\n *\n * Aborting REJECTS the iteration — with the platform's raw `AbortError`, not\n * a `NodaroError` — whether it fires before the first frame or between two of\n * them. Wrap the loop in `try`/`catch` if a cancelled turn is an ordinary\n * ending in your UI. (`media.downloadVideoProgress` behaves the same way.)\n */\n signal?: AbortSignal\n}\n\nexport interface CopilotToolCallFrameData {\n id: string\n name: string\n label: string\n status: \"started\" | \"finished\" | \"failed\"\n summary?: string\n}\n\nexport interface CopilotWorkflowUpdateFrameData {\n workflowId: string\n version: number\n updatedAt?: string\n note?: string | null\n addedNodeIds: string[]\n updatedNodeIds: string[]\n removedNodeIds: string[]\n addedNodeTypes: string[]\n nodeCount: number\n edgeCount: number\n adjustments: string[]\n}\n\n/** A file the turn wired onto a node — named on the run card, never counted. */\nexport interface CopilotWiredAsset {\n id: string\n kind: string\n filename: string\n nodeId: string\n}\n\nexport interface CopilotRunProposalNode {\n id: string\n type: string\n graphVersion: number\n label: string\n}\n\nexport interface CopilotRunProposalFrameData {\n workflowId: string\n addedNodeTypes: string[]\n wiredAssets?: CopilotWiredAsset[]\n note: string | null\n /** Present when ONE node was proposed rather than the whole graph. */\n node?: CopilotRunProposalNode | null\n}\n\n/** A workflow the turn created — a different one from the open canvas. */\nexport interface CopilotWorkflowCreatedFrameData {\n workflowId: string\n name: string\n projectId: string\n}\n\n/** One thing the assistant saved to memory this turn. */\nexport interface CopilotMemorySavedFrameData {\n id: string\n content: string\n}\n\nexport interface CopilotMetadataFrameData {\n threadId: string\n turnId: string\n jobId: string\n model: string\n baseVersion: number | null\n runMode: CopilotRunMode\n autoRunLimitCredits: number\n allowPublishing?: boolean\n modelTier?: CopilotModelTier\n /** See {@link CopilotSurface} — absent means the server did not say. */\n surface?: CopilotSurface\n}\n\n/**\n * One frame of a turn.\n *\n * The union covers the frame KINDS; a frame's `data` is passed through\n * untouched, so a field this package does not model still reaches the caller\n * rather than being stripped on the way.\n */\nexport type CopilotStreamFrame =\n | { type: \"metadata\"; data: CopilotMetadataFrameData }\n | { type: \"token\"; data: { text: string } }\n | { type: \"tool_call\"; data: CopilotToolCallFrameData }\n | { type: \"workflow_updated\"; data: CopilotWorkflowUpdateFrameData }\n | { type: \"workflow_created\"; data: CopilotWorkflowCreatedFrameData }\n | { type: \"run_proposed\"; data: CopilotRunProposalFrameData }\n | { type: \"memory_saved\"; data: CopilotMemorySavedFrameData }\n | { type: \"usage\"; data: { inputTokens: number; outputTokens: number; cacheReadTokens: number; creditsCharged: number | null } }\n | { type: \"done\"; data: { turnId: string; messageId: string | null; status: \"completed\" | \"capped\" | \"cancelled\"; finalVersion: number | null } }\n | { type: \"error\"; data: { code: string; message: string } }\n\n/** The kinds above, as the wire spells them — the whole of what `stream` yields. */\nconst COPILOT_FRAME_TYPES: ReadonlySet<string> = new Set([\n \"metadata\",\n \"token\",\n \"tool_call\",\n \"workflow_updated\",\n \"workflow_created\",\n \"run_proposed\",\n \"memory_saved\",\n \"usage\",\n \"done\",\n \"error\",\n])\n\nfunction isCopilotFrame(value: unknown): value is CopilotStreamFrame {\n if (typeof value !== \"object\" || value === null) return false\n const type = (value as { type?: unknown }).type\n return typeof type === \"string\" && COPILOT_FRAME_TYPES.has(type)\n}\n\nexport class CopilotResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * Open a conversation (`POST /v1/copilot/threads`) — on `workflowId`, or on\n * a workflow the server creates from `prompt`. Re-opening a workflow that\n * already has an active thread answers THAT thread rather than a second one.\n */\n create(input: CreateCopilotThreadInput): Promise<{ data: { thread: CopilotThread; workflow: CopilotThreadWorkflow } }> {\n return this.client.request(\"POST\", \"/v1/copilot/threads\", { body: input })\n }\n\n /**\n * The active thread for a workflow (`GET /v1/copilot/threads?workflowId=…`),\n * or `null` when the user has none open on it.\n */\n list(params: { workflowId: string }): Promise<{ data: { thread: CopilotThread | null } }> {\n return this.client.request(\"GET\", \"/v1/copilot/threads\", { query: { workflowId: params.workflowId } })\n }\n\n /**\n * One thread with its messages (`GET /v1/copilot/threads/:id`). `after` reads\n * only what followed that sequence number (the panel's catch-up); `limit`\n * caps the page at the server's own ceiling.\n */\n get(\n id: string,\n opts: { after?: number; limit?: number } = {},\n ): Promise<{ data: { thread: CopilotThread; messages: CopilotMessage[] } }> {\n return this.client.request(\"GET\", `/v1/copilot/threads/${encodeURIComponent(id)}`, {\n query: { ...(opts.after !== undefined ? { after: opts.after } : {}), ...(opts.limit !== undefined ? { limit: opts.limit } : {}) },\n })\n }\n\n /**\n * Close a conversation (`DELETE /v1/copilot/threads/:id`). Archival, not\n * deletion — the messages stay readable. Refused while a turn is running.\n */\n archive(id: string): Promise<{ data: { archived: true } }> {\n return this.client.request(\"DELETE\", `/v1/copilot/threads/${encodeURIComponent(id)}`)\n }\n\n /**\n * Stop the running turn (`POST /v1/copilot/threads/:id/cancel`). Answers the\n * turn it asked to stop; the turn's own stream ends with a `done` frame.\n */\n cancel(id: string): Promise<{ data: { cancelling: true; turnId: string } }> {\n return this.client.request(\"POST\", `/v1/copilot/threads/${encodeURIComponent(id)}/cancel`)\n }\n\n /**\n * Send one message and iterate the turn's frames\n * (`POST /v1/copilot/threads/:id/messages`, server-sent events).\n *\n * Deliberately NOT routed through `client.request`: that path arms an abort\n * timer around the whole exchange, and a turn legitimately runs for minutes —\n * inheriting `timeoutMs` would cut the assistant off mid-answer. The caller\n * owns the lifetime instead: pass `signal` to end the stream, or stop\n * iterating (the reader cancels the body, which ends the request).\n *\n * A frame kind this package does not model is skipped rather than thrown, so\n * a newer server cannot break an older caller.\n */\n async *stream(threadId: string, opts: CopilotStreamOptions): AsyncGenerator<CopilotStreamFrame, void, undefined> {\n const url = `${this.client.baseUrl}/v1/copilot/threads/${encodeURIComponent(threadId)}/messages`\n const token = await this.client.auth.getToken()\n const res = await this.client.fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n },\n body: JSON.stringify({\n message: opts.message,\n ...(opts.baseVersion !== undefined ? { baseVersion: opts.baseVersion } : {}),\n ...(opts.tier ? { tier: opts.tier } : {}),\n }),\n signal: opts.signal,\n })\n for await (const value of readSseStream<unknown>(res, { label: \"copilot stream\" })) {\n if (isCopilotFrame(value)) yield value\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/resources/copilot.ts"],"names":["readSseStream"],"mappings":"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","file":"chunk-OSMKIRKI.cjs","sourcesContent":["import type { NodaroClientCore } from \"../core.js\"\nimport { readSseStream } from \"../sse.js\"\n\n/**\n * The assistant's conversation surface (`/v1/copilot/*`) — threads, and one\n * turn as a live stream.\n *\n * A thread is the conversation; a TURN is one message and everything the\n * assistant does in answer to it. `stream` is the only method that spends: it\n * POSTs the message and yields the turn's frames as they arrive, so a caller\n * renders prose, tool activity and proposals live instead of waiting for a\n * finished answer.\n *\n * Two shapes here are deliberately TOLERANT of a deployment that predates a\n * field, because the SDK ships ahead of every instance it talks to:\n * - `surface` is optional on both {@link CopilotThread} and the `metadata`\n * frame — absent is a value (\"this deployment does not say\"), never a\n * default this package invents.\n * - An event kind this package does not model is IGNORED, not thrown: a\n * newer server may emit frames a caller was built before.\n */\n\n/** How this thread runs what it proposes: ask first, or run within a budget. */\nexport type CopilotRunMode = \"ask\" | \"auto\"\n\n/** The thread's model rung. Absent from a server that predates it. */\nexport type CopilotModelTier = \"economy\" | \"standard\" | \"premium\"\n\n/**\n * Which product the conversation belongs to.\n *\n * NO deployment sends this today — nothing in the platform emits it yet — and\n * it is typed here so that the day one does, a caller reads what the server\n * said. That is why it is OPTIONAL everywhere it appears: absent is a value\n * (\"this deployment does not say\"), never a default this package invents.\n */\nexport type CopilotSurface = \"studio\" | \"workflow\"\n\nexport interface CopilotThread {\n id: string\n workflowId: string\n runMode: CopilotRunMode\n /** See {@link CopilotSurface} — absent means the server did not say. */\n surface?: CopilotSurface\n /** Whether this thread may build nodes that publish to connected accounts. */\n allowPublishing?: boolean\n modelTier?: CopilotModelTier\n autoRunLimitCredits: number\n userTurnCount: number\n lastMessageAt: string | null\n createdAt: string\n /** Only on {@link CopilotResource.get} — derived from a live turn. */\n status?: \"running\" | \"idle\"\n activeTurnId?: string | null\n}\n\n/** The workflow a thread was opened on, as `create` answers it. */\nexport interface CopilotThreadWorkflow {\n id: string\n projectId: string\n name: string\n version: number\n}\n\nexport type CopilotMessagePart =\n | { kind: \"text\"; text: string }\n | { kind: \"tool_call\"; id: string; name: string; label: string; status: \"finished\" | \"failed\" }\n\nexport interface CopilotMessage {\n id: string\n seq: number\n turnId: string\n role: \"user\" | \"assistant\"\n createdAt: string\n parts: CopilotMessagePart[]\n}\n\n/** Open a thread on an existing workflow, or on one this call creates. */\nexport interface CreateCopilotThreadInput {\n /** The workflow to work on. Give this OR `prompt`. */\n workflowId?: string\n /** Start from nothing: the server creates a workflow seeded by this prompt. */\n prompt?: string\n /** Name for a workflow created by `prompt` (defaults to the prompt's head). */\n name?: string\n}\n\nexport interface CopilotStreamOptions {\n /** The user's turn. */\n message: string\n /** The workflow version this message was composed against — a newer one 409s. */\n baseVersion?: number\n /** The composer's echo of the thread's rung; the thread row stays authoritative. */\n tier?: CopilotModelTier\n /**\n * Ends the stream. The client's request timeout is NOT applied to a turn.\n *\n * Aborting REJECTS the iteration — with the platform's raw `AbortError`, not\n * a `NodaroError` — whether it fires before the first frame or between two of\n * them. Wrap the loop in `try`/`catch` if a cancelled turn is an ordinary\n * ending in your UI. (`media.downloadVideoProgress` behaves the same way.)\n */\n signal?: AbortSignal\n}\n\nexport interface CopilotToolCallFrameData {\n id: string\n name: string\n label: string\n status: \"started\" | \"finished\" | \"failed\"\n summary?: string\n}\n\nexport interface CopilotWorkflowUpdateFrameData {\n workflowId: string\n version: number\n updatedAt?: string\n note?: string | null\n addedNodeIds: string[]\n updatedNodeIds: string[]\n removedNodeIds: string[]\n addedNodeTypes: string[]\n nodeCount: number\n edgeCount: number\n adjustments: string[]\n}\n\n/** A file the turn wired onto a node — named on the run card, never counted. */\nexport interface CopilotWiredAsset {\n id: string\n kind: string\n filename: string\n nodeId: string\n}\n\nexport interface CopilotRunProposalNode {\n id: string\n type: string\n graphVersion: number\n label: string\n}\n\nexport interface CopilotRunProposalFrameData {\n workflowId: string\n addedNodeTypes: string[]\n wiredAssets?: CopilotWiredAsset[]\n note: string | null\n /** Present when ONE node was proposed rather than the whole graph. */\n node?: CopilotRunProposalNode | null\n}\n\n/** A workflow the turn created — a different one from the open canvas. */\nexport interface CopilotWorkflowCreatedFrameData {\n workflowId: string\n name: string\n projectId: string\n}\n\n/** One thing the assistant saved to memory this turn. */\nexport interface CopilotMemorySavedFrameData {\n id: string\n content: string\n}\n\nexport interface CopilotMetadataFrameData {\n threadId: string\n turnId: string\n jobId: string\n model: string\n baseVersion: number | null\n runMode: CopilotRunMode\n autoRunLimitCredits: number\n allowPublishing?: boolean\n modelTier?: CopilotModelTier\n /** See {@link CopilotSurface} — absent means the server did not say. */\n surface?: CopilotSurface\n}\n\n/**\n * One frame of a turn.\n *\n * The union covers the frame KINDS; a frame's `data` is passed through\n * untouched, so a field this package does not model still reaches the caller\n * rather than being stripped on the way.\n */\nexport type CopilotStreamFrame =\n | { type: \"metadata\"; data: CopilotMetadataFrameData }\n | { type: \"token\"; data: { text: string } }\n | { type: \"tool_call\"; data: CopilotToolCallFrameData }\n | { type: \"workflow_updated\"; data: CopilotWorkflowUpdateFrameData }\n | { type: \"workflow_created\"; data: CopilotWorkflowCreatedFrameData }\n | { type: \"run_proposed\"; data: CopilotRunProposalFrameData }\n | { type: \"memory_saved\"; data: CopilotMemorySavedFrameData }\n | { type: \"usage\"; data: { inputTokens: number; outputTokens: number; cacheReadTokens: number; creditsCharged: number | null } }\n | { type: \"done\"; data: { turnId: string; messageId: string | null; status: \"completed\" | \"capped\" | \"cancelled\"; finalVersion: number | null } }\n | { type: \"error\"; data: { code: string; message: string } }\n\n/** The kinds above, as the wire spells them — the whole of what `stream` yields. */\nconst COPILOT_FRAME_TYPES: ReadonlySet<string> = new Set([\n \"metadata\",\n \"token\",\n \"tool_call\",\n \"workflow_updated\",\n \"workflow_created\",\n \"run_proposed\",\n \"memory_saved\",\n \"usage\",\n \"done\",\n \"error\",\n])\n\nfunction isCopilotFrame(value: unknown): value is CopilotStreamFrame {\n if (typeof value !== \"object\" || value === null) return false\n const type = (value as { type?: unknown }).type\n return typeof type === \"string\" && COPILOT_FRAME_TYPES.has(type)\n}\n\nexport class CopilotResource {\n constructor(private client: NodaroClientCore) {}\n\n /**\n * Open a conversation (`POST /v1/copilot/threads`) — on `workflowId`, or on\n * a workflow the server creates from `prompt`. Re-opening a workflow that\n * already has an active thread answers THAT thread rather than a second one.\n */\n create(input: CreateCopilotThreadInput): Promise<{ data: { thread: CopilotThread; workflow: CopilotThreadWorkflow } }> {\n return this.client.request(\"POST\", \"/v1/copilot/threads\", { body: input })\n }\n\n /**\n * The active thread for a workflow (`GET /v1/copilot/threads?workflowId=…`),\n * or `null` when the user has none open on it.\n */\n list(params: { workflowId: string }): Promise<{ data: { thread: CopilotThread | null } }> {\n return this.client.request(\"GET\", \"/v1/copilot/threads\", { query: { workflowId: params.workflowId } })\n }\n\n /**\n * One thread with its messages (`GET /v1/copilot/threads/:id`). `after` reads\n * only what followed that sequence number (the panel's catch-up); `limit`\n * caps the page at the server's own ceiling.\n */\n get(\n id: string,\n opts: { after?: number; limit?: number } = {},\n ): Promise<{ data: { thread: CopilotThread; messages: CopilotMessage[] } }> {\n return this.client.request(\"GET\", `/v1/copilot/threads/${encodeURIComponent(id)}`, {\n query: { ...(opts.after !== undefined ? { after: opts.after } : {}), ...(opts.limit !== undefined ? { limit: opts.limit } : {}) },\n })\n }\n\n /**\n * Close a conversation (`DELETE /v1/copilot/threads/:id`). Archival, not\n * deletion — the messages stay readable. Refused while a turn is running.\n */\n archive(id: string): Promise<{ data: { archived: true } }> {\n return this.client.request(\"DELETE\", `/v1/copilot/threads/${encodeURIComponent(id)}`)\n }\n\n /**\n * Stop the running turn (`POST /v1/copilot/threads/:id/cancel`). Answers the\n * turn it asked to stop; the turn's own stream ends with a `done` frame.\n */\n cancel(id: string): Promise<{ data: { cancelling: true; turnId: string } }> {\n return this.client.request(\"POST\", `/v1/copilot/threads/${encodeURIComponent(id)}/cancel`)\n }\n\n /**\n * Send one message and iterate the turn's frames\n * (`POST /v1/copilot/threads/:id/messages`, server-sent events).\n *\n * Deliberately NOT routed through `client.request`: that path arms an abort\n * timer around the whole exchange, and a turn legitimately runs for minutes —\n * inheriting `timeoutMs` would cut the assistant off mid-answer. The caller\n * owns the lifetime instead: pass `signal` to end the stream, or stop\n * iterating (the reader cancels the body, which ends the request).\n *\n * A frame kind this package does not model is skipped rather than thrown, so\n * a newer server cannot break an older caller.\n */\n async *stream(threadId: string, opts: CopilotStreamOptions): AsyncGenerator<CopilotStreamFrame, void, undefined> {\n const url = `${this.client.baseUrl}/v1/copilot/threads/${encodeURIComponent(threadId)}/messages`\n const token = await this.client.auth.getToken()\n const res = await this.client.fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n },\n body: JSON.stringify({\n message: opts.message,\n ...(opts.baseVersion !== undefined ? { baseVersion: opts.baseVersion } : {}),\n ...(opts.tier ? { tier: opts.tier } : {}),\n }),\n signal: opts.signal,\n })\n for await (const value of readSseStream<unknown>(res, { label: \"copilot stream\" })) {\n if (isCopilotFrame(value)) yield value\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-PG2GBS3K.cjs"}
|
|
@@ -35,7 +35,8 @@ var CreditsResource = class {
|
|
|
35
35
|
* `POST /v1/credits/model-costs` → per-identifier credit cost, for editor
|
|
36
36
|
* cost previews. Capped at the first {@link MODEL_COSTS_LIMIT} identifiers
|
|
37
37
|
* (the route's request limit). Preserves the `{ data, missing, errors }`
|
|
38
|
-
* fault-isolation shape verbatim
|
|
38
|
+
* fault-isolation shape verbatim, plus `estimates` / `errorCodes` when a
|
|
39
|
+
* metered LLM price key was asked (see {@link ModelCostsResult}).
|
|
39
40
|
*/
|
|
40
41
|
modelCosts(ids) {
|
|
41
42
|
return this.client.request("POST", "/v1/credits/model-costs", {
|
|
@@ -94,7 +95,8 @@ var CreditsResource = class {
|
|
|
94
95
|
}
|
|
95
96
|
// NOTE: no `estimate(...)` helper. The backend exposes
|
|
96
97
|
// `POST /v1/credits/estimate-workflow` (body `{ nodes }` → `{ data: {
|
|
97
|
-
// totalCredits, nodeCount } }`
|
|
98
|
+
// totalCredits, nodeCount, estimate?: true } }` — `estimate` when a metered
|
|
99
|
+
// LLM node's estimate is in the total), but no consumer has a settled shape for it
|
|
98
100
|
// yet (studio pre-checks `balance >= Σ modelCosts` client-side rather than
|
|
99
101
|
// calling an estimate endpoint). Adding it now would be inventing an API
|
|
100
102
|
// surface ahead of a real caller, so it's deliberately omitted — add it when
|
|
@@ -102,5 +104,5 @@ var CreditsResource = class {
|
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
exports.CreditsResource = CreditsResource;
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
//# sourceMappingURL=chunk-PQ7OKM3E.cjs.map
|
|
108
|
+
//# sourceMappingURL=chunk-PQ7OKM3E.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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-PQ7OKM3E.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,4 +1,4 @@
|
|
|
1
|
-
import { throwFromResponse } from './chunk-
|
|
1
|
+
import { throwFromResponse } from './chunk-L6T44A3G.js';
|
|
2
2
|
import { WORKSPACE_HEADER } from '@nodaro/shared/organizations';
|
|
3
3
|
|
|
4
4
|
// src/binary-response.ts
|
|
@@ -34,7 +34,7 @@ async function readBinaryResponse(response, maxBytes) {
|
|
|
34
34
|
}
|
|
35
35
|
return bytes.buffer;
|
|
36
36
|
}
|
|
37
|
-
var SDK_VERSION = "2.
|
|
37
|
+
var SDK_VERSION = "2.38.0" ;
|
|
38
38
|
var CLIENT_HEADER = "X-Nodaro-Client";
|
|
39
39
|
var isBrowser = () => typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
40
40
|
var NodaroClientCore = class {
|
|
@@ -224,5 +224,5 @@ function supabaseAuth(supabase) {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
export { CallbackAuth, NodaroClientCore, StaticTokenAuth, supabaseAuth };
|
|
227
|
-
//# sourceMappingURL=chunk-
|
|
228
|
-
//# sourceMappingURL=chunk-
|
|
227
|
+
//# sourceMappingURL=chunk-Q6HY2Q5V.js.map
|
|
228
|
+
//# sourceMappingURL=chunk-Q6HY2Q5V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/binary-response.ts","../src/core.ts","../src/auth.ts"],"names":[],"mappings":"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","file":"chunk-Y5CYFNNW.js","sourcesContent":["/** Bound decoded response bytes even when Content-Length is absent or compressed. */\nexport async function readBinaryResponse(response: Response, maxBytes: number): Promise<ArrayBuffer> {\n const advertised = response.headers.get(\"content-length\")\n if (advertised !== null && (!/^\\d+$/.test(advertised) || Number(advertised) > maxBytes)) {\n await response.body?.cancel()\n throw new Error(\"Binary response exceeds its declared size limit\")\n }\n if (!response.body) return new ArrayBuffer(0)\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let length = 0\n try {\n while (true) {\n const next = await reader.read()\n if (next.done) break\n length += next.value.byteLength\n if (length > maxBytes) {\n await reader.cancel()\n throw new Error(\"Binary response exceeds its declared size limit\")\n }\n chunks.push(next.value)\n }\n } finally { reader.releaseLock() }\n const bytes = new Uint8Array(length)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength }\n return bytes.buffer\n}\n","/**\n * The client core: configuration, auth, the request transport and the\n * account-identity calls — everything a resource needs, and none of the\n * resources themselves.\n *\n * {@link NodaroClient} (`createClient`) extends it with every resource. An app\n * that uses only a few resources can extend it with just those, so it loads\n * (and its tests depend on) only the resource modules it actually uses:\n *\n * class AppClient extends NodaroClientCore {\n * readonly jobs = new JobsResource(this)\n * readonly nodes = new NodesResource(this) // needs `jobs` on the same client\n * }\n *\n * A resource that calls another one through the client says so in its\n * constructor type (`NodesResource` takes a core that also has `jobs`), so a\n * client missing it fails to compile rather than at run time.\n */\nimport { readBinaryResponse } from \"./binary-response.js\"\nimport { throwFromResponse } from \"./errors.js\"\nimport type { Auth } from \"./auth.js\"\nimport type { DeveloperAppKind, DeveloperAppScope } from \"./resources/developer-apps.js\"\nimport { WORKSPACE_HEADER, type MeOrganizations } from \"@nodaro/shared/organizations\"\n\n/** Replaced at build time by tsup `define` from package.json. The fallback\n * keeps `tsx`/vitest runs of the source working, where no define applies. */\ndeclare const __SDK_VERSION__: string | undefined\nconst SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\"\n\n/**\n * Identifies the calling package to the backend, which records it as the job's\n * provenance (`jobs.source` / `source_detail`) so an operator can tell a CLI\n * run from an SDK integration from a browser session.\n *\n * Only `sdk/…` and `cli/…` are honoured server-side; anything else is ignored\n * rather than trusted, since this is an unauthenticated header.\n */\nexport const CLIENT_HEADER = \"X-Nodaro-Client\"\n\n/**\n * In a browser the header is redundant AND a liability, so it is not sent.\n *\n * Redundant: the browser already sends `Origin`, which names the actual product\n * (`studio.nodaro.ai`) rather than merely the library it used — and the backend\n * deliberately prefers Origin over this header, so a browser-sent value is\n * discarded on arrival.\n *\n * A liability: `X-Nodaro-Client` is not a CORS-safelisted request header, so a\n * browser sending it requires an exact match in the server's\n * `Access-Control-Allow-Headers`. Any deployment whose backend predates that\n * allowlist entry would fail the PREFLIGHT — breaking every API call from the\n * app, not just its provenance. Self-hosted and lagging deployments make that a\n * real risk, and nothing in a browser app would reveal the cause.\n *\n * An EXPLICIT `clientLabel` is always sent regardless: a caller that names\n * itself has opted in deliberately, and that intent outranks this default.\n */\nconst isBrowser = (): boolean => typeof window !== \"undefined\" && typeof window.document !== \"undefined\"\n\nexport interface ClientOptions {\n /** Backend base URL, e.g. \"https://nodaro.example.com\" or empty string for same-origin. */\n baseUrl: string\n /** Auth provider. Use StaticTokenAuth, supabaseAuth, or CallbackAuth. */\n auth: Auth\n /** Optional fetch override (for tests or custom transports). */\n fetch?: typeof fetch\n /** Default request timeout in ms. Default 60s. */\n timeoutMs?: number\n /**\n * Overrides the `X-Nodaro-Client` label. Defaults to `sdk/<version>`.\n *\n * Exists for `@nodaro/cli`, which is a wrapper AROUND this SDK: without an\n * override every CLI invocation would be recorded as an SDK call and the two\n * surfaces could never be told apart.\n */\n clientLabel?: string\n /**\n * The workspace every request acts in — sent as `X-Nodaro-Workspace`.\n *\n * It selects which workspace a LIST reads from and where a CREATE lands.\n * It never authorizes: reading, updating, deleting or running an\n * identified object is decided by that object's own workspace, so a\n * forgotten header cannot hide work and a forged one cannot reach anyone\n * else's. Omit it to work in the caller's personal space.\n *\n * Prefer {@link NodaroClient.withWorkspace} for anything short-lived — one\n * client per workspace, no shared mutable selection to get wrong.\n */\n workspaceId?: string\n}\n\ninterface RequestOptions {\n body?: unknown\n query?: Record<string, string | number | boolean | undefined>\n headers?: Record<string, string>\n signal?: AbortSignal\n}\n\n/**\n * The authenticated user's canonical identity (`GET /v1/me`). A token-\n * introspection primitive: any valid bearer token (first-party Supabase JWT or\n * a developer-app OAuth token) resolves to its owner's identity. Mirrors the\n * `profiles` identity columns server-side — the route is the source of truth.\n */\nexport interface UserIdentity {\n /** Nodaro user id (= the Supabase auth user id). */\n readonly id: string\n readonly email: string\n /** Human-readable display name (from `profiles.full_name`); `null` if unset. */\n readonly displayName: string | null\n /** Avatar URL; `null` if unset. */\n readonly avatarUrl: string | null\n /** Subscription tier (e.g. \"free\", \"pro\"). */\n readonly tier: string\n /**\n * Whether the user holds an admin role. DESCRIPTIVE only — use it to decide\n * whether to render admin surfaces without capability-probing an admin\n * endpoint; every admin API stays enforced server-side regardless.\n */\n readonly isAdmin: boolean\n}\n\n/**\n * An app the user let into their account through OAuth (`GET\n * /v1/me/connected-apps`): a developer app, an AI assistant's MCP connector, or\n * a self-hosted install. One entry per grant still in force.\n */\nexport interface ConnectedApp {\n /** The grant's id — what {@link NodaroClient.revokeConnectedApp} takes. */\n readonly authorizationId: string\n /** The app's name; `null` if it has none. A `dynamic_mcp` client chose its own. */\n readonly name: string | null\n /**\n * How the app came to exist: `user` (a developer app someone registered),\n * `first_party_mcp`, `dynamic_mcp` (an MCP client that registered itself) or\n * `community_instance` (a self-hosted install).\n */\n readonly kind: DeveloperAppKind | (string & {})\n readonly homepageUrl: string | null\n /** The scopes this grant holds. */\n readonly scopes: readonly DeveloperAppScope[]\n /** When the user granted access (ISO 8601). */\n readonly connectedAt: string\n /** The latest use of any token issued under the grant; `null` if never used. */\n readonly lastUsedAt: string | null\n}\n\nexport class NodaroClientCore {\n readonly baseUrl: string\n readonly auth: Auth\n readonly timeoutMs: number\n /** Value sent as `X-Nodaro-Client`; recorded by the backend as job provenance. */\n readonly clientLabel: string\n /** Whether to actually send it — see the note on {@link CLIENT_HEADER}. */\n private readonly sendClientHeader: boolean\n private readonly fetchOverride: typeof fetch | undefined\n\n /**\n * Resolved lazily so consumers can swap `globalThis.fetch` after the\n * client has been constructed (e.g. test mocks). Always rebound to the\n * global object — native fetch throws \"Illegal invocation\" when its\n * `this` is anything else.\n */\n get fetch(): typeof fetch {\n return this.fetchOverride ?? globalThis.fetch.bind(globalThis)\n }\n\n /** The workspace this client acts in; undefined = the personal space. */\n readonly workspaceId: string | undefined\n\n constructor(opts: ClientOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/$/, \"\") // strip trailing slash\n this.auth = opts.auth\n this.fetchOverride = opts.fetch\n this.timeoutMs = opts.timeoutMs ?? 60_000\n this.clientLabel = opts.clientLabel ?? `sdk/${SDK_VERSION}`\n this.workspaceId = opts.workspaceId\n // Explicit label = deliberate opt-in, always sent. The default is suppressed\n // in browsers — see the note on CLIENT_HEADER.\n this.sendClientHeader = opts.clientLabel !== undefined || !isBrowser()\n }\n\n /**\n * The transport: build the request, send it, throw the typed error on a\n * non-2xx via {@link throwFromResponse}, then read the body via `read` — all\n * inside the abort timer, so `timeoutMs` bounds the body download too (a\n * stalled CSV stream aborts, it does not hang forever). Both the JSON path\n * ({@link request}) and the text path ({@link requestText}) go through here.\n */\n private async send<T>(\n method: string,\n path: string,\n options: RequestOptions,\n read: (res: Response) => Promise<T>,\n ): Promise<T> {\n const url = this.buildUrl(path, options.query)\n\n const token = await this.auth.getToken()\n // A `FormData` body is a multipart upload: let the runtime set\n // `Content-Type: multipart/form-data; boundary=…` itself (a manual JSON\n // content-type corrupts the boundary), and send the body as-is rather than\n // JSON-stringifying it. Every other body stays JSON, exactly as before.\n const isFormData =\n typeof FormData !== \"undefined\" && options.body instanceof FormData\n const headers: Record<string, string> = {\n ...(isFormData ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.sendClientHeader ? { [CLIENT_HEADER]: this.clientLabel } : {}),\n // Before per-request headers: a resource that needs to reach outside\n // this client's workspace says so explicitly and wins.\n ...(this.workspaceId ? { [WORKSPACE_HEADER]: this.workspaceId } : {}),\n ...(options.headers ?? {}),\n }\n if (token) headers[\"Authorization\"] = `Bearer ${token}`\n\n const ac = new AbortController()\n const timeoutId = setTimeout(() => ac.abort(), this.timeoutMs)\n const abort = () => ac.abort(options.signal?.reason)\n options.signal?.addEventListener(\"abort\", abort, { once: true })\n if (options.signal?.aborted) abort()\n\n try {\n ac.signal.throwIfAborted()\n const res = await this.fetch(url, {\n method,\n headers,\n body:\n options.body === undefined\n ? undefined\n : isFormData\n ? (options.body as FormData)\n : JSON.stringify(options.body),\n signal: ac.signal,\n })\n\n if (!res.ok) {\n let errBody: Record<string, unknown> = {}\n try {\n errBody = await res.json() as Record<string, unknown>\n } catch {\n // Empty/non-JSON body — fall through with empty errBody\n }\n throwFromResponse(res.status, errBody)\n }\n\n return await read(res)\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener(\"abort\", abort)\n }\n }\n\n /** Authenticated, bounded binary reads through the normal timeout/error transport. */\n async requestBytes(method: string, path: string, options: RequestOptions & { maxBytes: number }): Promise<ArrayBuffer> {\n if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1) throw new Error(\"maxBytes must be a positive integer\")\n return this.send(method, path, options, (response) => readBinaryResponse(response, options.maxBytes))\n }\n\n async request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n return this.send(method, path, options, async (res) =>\n // 204 No Content\n res.status === 204 ? (undefined as T) : ((await res.json()) as T),\n )\n }\n\n /**\n * Like {@link request}, for endpoints that answer text (CSV exports). Errors\n * are still the JSON envelope and throw the same typed errors, and the read is\n * bounded by the same timeout as the request.\n */\n async requestText(method: string, path: string, options: RequestOptions = {}): Promise<string> {\n return this.send(method, path, options, (res) => res.text())\n }\n\n /**\n * `GET /v1/me` → the authenticated user's identity (see {@link UserIdentity}).\n * Unwraps the `{ data }` envelope. Throws `UnauthorizedError` (401) when the\n * token is missing/invalid, and the SDK's other typed errors as usual.\n *\n * On an instance with organizations it also carries what the caller belongs\n * to. THREE states, and collapsing them is wrong in a way users feel: the\n * organization fields ABSENT means this instance has no organizations at\n * all; present and empty means the account belongs to none; and\n * `organizationsUnavailable` means the lookup failed — keep whatever\n * selection you already had rather than concluding the person was removed\n * from everything.\n */\n async me(): Promise<UserIdentity & MeOrganizations> {\n const res = await this.request<{ data: UserIdentity & MeOrganizations }>(\"GET\", \"/v1/me\")\n return res.data\n }\n\n /**\n * `GET /v1/me/connected-apps` → every app with access to the signed-in\n * user's account (see {@link ConnectedApp}), newest grant first.\n *\n * **A signed-in browser session only.** The route answers a Supabase\n * session token (`supabaseAuth(…)`, `createSharedSupabaseClient`, or a\n * `StaticTokenAuth` holding one) and refuses every other credential with\n * `UnauthorizedError` (\"Sign in required\"): an app token or an API key must\n * not list the grants it lives under. Build your \"Connected apps\" settings\n * screen on this; a server-side integration cannot call it.\n */\n async connectedApps(): Promise<ConnectedApp[]> {\n const res = await this.request<{ apps: ConnectedApp[] }>(\"GET\", \"/v1/me/connected-apps\")\n return res.apps\n }\n\n /**\n * `POST /v1/me/connected-apps/:authorizationId/revoke` — ends an app's\n * access: the grant and every token issued under it, at once (the app's next\n * call is a 401). Resolves when done.\n *\n * A signed-in browser session only, like {@link connectedApps}. Throws\n * `NotFoundError` when the grant is not the user's or is already revoked.\n */\n async revokeConnectedApp(authorizationId: string): Promise<void> {\n await this.request(\"POST\", `/v1/me/connected-apps/${encodeURIComponent(authorizationId)}/revoke`)\n }\n\n private buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n const base = this.baseUrl || (typeof window !== \"undefined\" ? window.location.origin : \"http://placeholder\")\n const url = new URL(path, base)\n // If baseUrl was empty, strip the placeholder origin\n const fullUrl = this.baseUrl ? url.toString() : url.pathname + url.search\n if (query) {\n const u = new URL(this.baseUrl ? fullUrl : fullUrl, base)\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) u.searchParams.set(k, String(v))\n }\n return this.baseUrl ? u.toString() : u.pathname + u.search\n }\n return fullUrl\n }\n}\n","/**\n * Auth provides the token used for `Authorization: Bearer <token>` on each request.\n * Implementations:\n * - StaticTokenAuth — fixed string (server-side, OAuth access token, API token)\n * - CallbackAuth — caller-supplied async function (BYO logic)\n * - supabaseAuth — pulls JWT from a Supabase client live (browser frontends)\n */\nexport interface Auth {\n /** Returns the current Bearer token, or null if not authenticated. */\n getToken(): Promise<string | null>\n}\n\nexport class StaticTokenAuth implements Auth {\n constructor(private token: string) {}\n async getToken() {\n return this.token\n }\n}\n\nexport class CallbackAuth implements Auth {\n constructor(private fn: () => string | null | Promise<string | null>) {}\n async getToken() {\n return this.fn()\n }\n}\n\ninterface SupabaseLikeClient {\n auth: {\n getSession(): Promise<{ data: { session: { access_token: string } | null } }>\n }\n}\n\n/** Pulls a JWT from a Supabase v2 client. Caller supplies their own supabase. */\nexport function supabaseAuth(supabase: SupabaseLikeClient): Auth {\n return {\n async getToken() {\n const { data } = await supabase.auth.getSession()\n return data.session?.access_token ?? null\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/binary-response.ts","../src/core.ts","../src/auth.ts"],"names":[],"mappings":"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","file":"chunk-Q6HY2Q5V.js","sourcesContent":["/** Bound decoded response bytes even when Content-Length is absent or compressed. */\nexport async function readBinaryResponse(response: Response, maxBytes: number): Promise<ArrayBuffer> {\n const advertised = response.headers.get(\"content-length\")\n if (advertised !== null && (!/^\\d+$/.test(advertised) || Number(advertised) > maxBytes)) {\n await response.body?.cancel()\n throw new Error(\"Binary response exceeds its declared size limit\")\n }\n if (!response.body) return new ArrayBuffer(0)\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let length = 0\n try {\n while (true) {\n const next = await reader.read()\n if (next.done) break\n length += next.value.byteLength\n if (length > maxBytes) {\n await reader.cancel()\n throw new Error(\"Binary response exceeds its declared size limit\")\n }\n chunks.push(next.value)\n }\n } finally { reader.releaseLock() }\n const bytes = new Uint8Array(length)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength }\n return bytes.buffer\n}\n","/**\n * The client core: configuration, auth, the request transport and the\n * account-identity calls — everything a resource needs, and none of the\n * resources themselves.\n *\n * {@link NodaroClient} (`createClient`) extends it with every resource. An app\n * that uses only a few resources can extend it with just those, so it loads\n * (and its tests depend on) only the resource modules it actually uses:\n *\n * class AppClient extends NodaroClientCore {\n * readonly jobs = new JobsResource(this)\n * readonly nodes = new NodesResource(this) // needs `jobs` on the same client\n * }\n *\n * A resource that calls another one through the client says so in its\n * constructor type (`NodesResource` takes a core that also has `jobs`), so a\n * client missing it fails to compile rather than at run time.\n */\nimport { readBinaryResponse } from \"./binary-response.js\"\nimport { throwFromResponse } from \"./errors.js\"\nimport type { Auth } from \"./auth.js\"\nimport type { DeveloperAppKind, DeveloperAppScope } from \"./resources/developer-apps.js\"\nimport { WORKSPACE_HEADER, type MeOrganizations } from \"@nodaro/shared/organizations\"\n\n/** Replaced at build time by tsup `define` from package.json. The fallback\n * keeps `tsx`/vitest runs of the source working, where no define applies. */\ndeclare const __SDK_VERSION__: string | undefined\nconst SDK_VERSION = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\"\n\n/**\n * Identifies the calling package to the backend, which records it as the job's\n * provenance (`jobs.source` / `source_detail`) so an operator can tell a CLI\n * run from an SDK integration from a browser session.\n *\n * Only `sdk/…` and `cli/…` are honoured server-side; anything else is ignored\n * rather than trusted, since this is an unauthenticated header.\n */\nexport const CLIENT_HEADER = \"X-Nodaro-Client\"\n\n/**\n * In a browser the header is redundant AND a liability, so it is not sent.\n *\n * Redundant: the browser already sends `Origin`, which names the actual product\n * (`studio.nodaro.ai`) rather than merely the library it used — and the backend\n * deliberately prefers Origin over this header, so a browser-sent value is\n * discarded on arrival.\n *\n * A liability: `X-Nodaro-Client` is not a CORS-safelisted request header, so a\n * browser sending it requires an exact match in the server's\n * `Access-Control-Allow-Headers`. Any deployment whose backend predates that\n * allowlist entry would fail the PREFLIGHT — breaking every API call from the\n * app, not just its provenance. Self-hosted and lagging deployments make that a\n * real risk, and nothing in a browser app would reveal the cause.\n *\n * An EXPLICIT `clientLabel` is always sent regardless: a caller that names\n * itself has opted in deliberately, and that intent outranks this default.\n */\nconst isBrowser = (): boolean => typeof window !== \"undefined\" && typeof window.document !== \"undefined\"\n\nexport interface ClientOptions {\n /** Backend base URL, e.g. \"https://nodaro.example.com\" or empty string for same-origin. */\n baseUrl: string\n /** Auth provider. Use StaticTokenAuth, supabaseAuth, or CallbackAuth. */\n auth: Auth\n /** Optional fetch override (for tests or custom transports). */\n fetch?: typeof fetch\n /** Default request timeout in ms. Default 60s. */\n timeoutMs?: number\n /**\n * Overrides the `X-Nodaro-Client` label. Defaults to `sdk/<version>`.\n *\n * Exists for `@nodaro/cli`, which is a wrapper AROUND this SDK: without an\n * override every CLI invocation would be recorded as an SDK call and the two\n * surfaces could never be told apart.\n */\n clientLabel?: string\n /**\n * The workspace every request acts in — sent as `X-Nodaro-Workspace`.\n *\n * It selects which workspace a LIST reads from and where a CREATE lands.\n * It never authorizes: reading, updating, deleting or running an\n * identified object is decided by that object's own workspace, so a\n * forgotten header cannot hide work and a forged one cannot reach anyone\n * else's. Omit it to work in the caller's personal space.\n *\n * Prefer {@link NodaroClient.withWorkspace} for anything short-lived — one\n * client per workspace, no shared mutable selection to get wrong.\n */\n workspaceId?: string\n}\n\ninterface RequestOptions {\n body?: unknown\n query?: Record<string, string | number | boolean | undefined>\n headers?: Record<string, string>\n signal?: AbortSignal\n}\n\n/**\n * The authenticated user's canonical identity (`GET /v1/me`). A token-\n * introspection primitive: any valid bearer token (first-party Supabase JWT or\n * a developer-app OAuth token) resolves to its owner's identity. Mirrors the\n * `profiles` identity columns server-side — the route is the source of truth.\n */\nexport interface UserIdentity {\n /** Nodaro user id (= the Supabase auth user id). */\n readonly id: string\n readonly email: string\n /** Human-readable display name (from `profiles.full_name`); `null` if unset. */\n readonly displayName: string | null\n /** Avatar URL; `null` if unset. */\n readonly avatarUrl: string | null\n /** Subscription tier (e.g. \"free\", \"pro\"). */\n readonly tier: string\n /**\n * Whether the user holds an admin role. DESCRIPTIVE only — use it to decide\n * whether to render admin surfaces without capability-probing an admin\n * endpoint; every admin API stays enforced server-side regardless.\n */\n readonly isAdmin: boolean\n}\n\n/**\n * An app the user let into their account through OAuth (`GET\n * /v1/me/connected-apps`): a developer app, an AI assistant's MCP connector, or\n * a self-hosted install. One entry per grant still in force.\n */\nexport interface ConnectedApp {\n /** The grant's id — what {@link NodaroClient.revokeConnectedApp} takes. */\n readonly authorizationId: string\n /** The app's name; `null` if it has none. A `dynamic_mcp` client chose its own. */\n readonly name: string | null\n /**\n * How the app came to exist: `user` (a developer app someone registered),\n * `first_party_mcp`, `dynamic_mcp` (an MCP client that registered itself) or\n * `community_instance` (a self-hosted install).\n */\n readonly kind: DeveloperAppKind | (string & {})\n readonly homepageUrl: string | null\n /** The scopes this grant holds. */\n readonly scopes: readonly DeveloperAppScope[]\n /** When the user granted access (ISO 8601). */\n readonly connectedAt: string\n /** The latest use of any token issued under the grant; `null` if never used. */\n readonly lastUsedAt: string | null\n}\n\nexport class NodaroClientCore {\n readonly baseUrl: string\n readonly auth: Auth\n readonly timeoutMs: number\n /** Value sent as `X-Nodaro-Client`; recorded by the backend as job provenance. */\n readonly clientLabel: string\n /** Whether to actually send it — see the note on {@link CLIENT_HEADER}. */\n private readonly sendClientHeader: boolean\n private readonly fetchOverride: typeof fetch | undefined\n\n /**\n * Resolved lazily so consumers can swap `globalThis.fetch` after the\n * client has been constructed (e.g. test mocks). Always rebound to the\n * global object — native fetch throws \"Illegal invocation\" when its\n * `this` is anything else.\n */\n get fetch(): typeof fetch {\n return this.fetchOverride ?? globalThis.fetch.bind(globalThis)\n }\n\n /** The workspace this client acts in; undefined = the personal space. */\n readonly workspaceId: string | undefined\n\n constructor(opts: ClientOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/$/, \"\") // strip trailing slash\n this.auth = opts.auth\n this.fetchOverride = opts.fetch\n this.timeoutMs = opts.timeoutMs ?? 60_000\n this.clientLabel = opts.clientLabel ?? `sdk/${SDK_VERSION}`\n this.workspaceId = opts.workspaceId\n // Explicit label = deliberate opt-in, always sent. The default is suppressed\n // in browsers — see the note on CLIENT_HEADER.\n this.sendClientHeader = opts.clientLabel !== undefined || !isBrowser()\n }\n\n /**\n * The transport: build the request, send it, throw the typed error on a\n * non-2xx via {@link throwFromResponse}, then read the body via `read` — all\n * inside the abort timer, so `timeoutMs` bounds the body download too (a\n * stalled CSV stream aborts, it does not hang forever). Both the JSON path\n * ({@link request}) and the text path ({@link requestText}) go through here.\n */\n private async send<T>(\n method: string,\n path: string,\n options: RequestOptions,\n read: (res: Response) => Promise<T>,\n ): Promise<T> {\n const url = this.buildUrl(path, options.query)\n\n const token = await this.auth.getToken()\n // A `FormData` body is a multipart upload: let the runtime set\n // `Content-Type: multipart/form-data; boundary=…` itself (a manual JSON\n // content-type corrupts the boundary), and send the body as-is rather than\n // JSON-stringifying it. Every other body stays JSON, exactly as before.\n const isFormData =\n typeof FormData !== \"undefined\" && options.body instanceof FormData\n const headers: Record<string, string> = {\n ...(isFormData ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.sendClientHeader ? { [CLIENT_HEADER]: this.clientLabel } : {}),\n // Before per-request headers: a resource that needs to reach outside\n // this client's workspace says so explicitly and wins.\n ...(this.workspaceId ? { [WORKSPACE_HEADER]: this.workspaceId } : {}),\n ...(options.headers ?? {}),\n }\n if (token) headers[\"Authorization\"] = `Bearer ${token}`\n\n const ac = new AbortController()\n const timeoutId = setTimeout(() => ac.abort(), this.timeoutMs)\n const abort = () => ac.abort(options.signal?.reason)\n options.signal?.addEventListener(\"abort\", abort, { once: true })\n if (options.signal?.aborted) abort()\n\n try {\n ac.signal.throwIfAborted()\n const res = await this.fetch(url, {\n method,\n headers,\n body:\n options.body === undefined\n ? undefined\n : isFormData\n ? (options.body as FormData)\n : JSON.stringify(options.body),\n signal: ac.signal,\n })\n\n if (!res.ok) {\n let errBody: Record<string, unknown> = {}\n try {\n errBody = await res.json() as Record<string, unknown>\n } catch {\n // Empty/non-JSON body — fall through with empty errBody\n }\n throwFromResponse(res.status, errBody)\n }\n\n return await read(res)\n } finally {\n clearTimeout(timeoutId)\n options.signal?.removeEventListener(\"abort\", abort)\n }\n }\n\n /** Authenticated, bounded binary reads through the normal timeout/error transport. */\n async requestBytes(method: string, path: string, options: RequestOptions & { maxBytes: number }): Promise<ArrayBuffer> {\n if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1) throw new Error(\"maxBytes must be a positive integer\")\n return this.send(method, path, options, (response) => readBinaryResponse(response, options.maxBytes))\n }\n\n async request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n return this.send(method, path, options, async (res) =>\n // 204 No Content\n res.status === 204 ? (undefined as T) : ((await res.json()) as T),\n )\n }\n\n /**\n * Like {@link request}, for endpoints that answer text (CSV exports). Errors\n * are still the JSON envelope and throw the same typed errors, and the read is\n * bounded by the same timeout as the request.\n */\n async requestText(method: string, path: string, options: RequestOptions = {}): Promise<string> {\n return this.send(method, path, options, (res) => res.text())\n }\n\n /**\n * `GET /v1/me` → the authenticated user's identity (see {@link UserIdentity}).\n * Unwraps the `{ data }` envelope. Throws `UnauthorizedError` (401) when the\n * token is missing/invalid, and the SDK's other typed errors as usual.\n *\n * On an instance with organizations it also carries what the caller belongs\n * to. THREE states, and collapsing them is wrong in a way users feel: the\n * organization fields ABSENT means this instance has no organizations at\n * all; present and empty means the account belongs to none; and\n * `organizationsUnavailable` means the lookup failed — keep whatever\n * selection you already had rather than concluding the person was removed\n * from everything.\n */\n async me(): Promise<UserIdentity & MeOrganizations> {\n const res = await this.request<{ data: UserIdentity & MeOrganizations }>(\"GET\", \"/v1/me\")\n return res.data\n }\n\n /**\n * `GET /v1/me/connected-apps` → every app with access to the signed-in\n * user's account (see {@link ConnectedApp}), newest grant first.\n *\n * **A signed-in browser session only.** The route answers a Supabase\n * session token (`supabaseAuth(…)`, `createSharedSupabaseClient`, or a\n * `StaticTokenAuth` holding one) and refuses every other credential with\n * `UnauthorizedError` (\"Sign in required\"): an app token or an API key must\n * not list the grants it lives under. Build your \"Connected apps\" settings\n * screen on this; a server-side integration cannot call it.\n */\n async connectedApps(): Promise<ConnectedApp[]> {\n const res = await this.request<{ apps: ConnectedApp[] }>(\"GET\", \"/v1/me/connected-apps\")\n return res.apps\n }\n\n /**\n * `POST /v1/me/connected-apps/:authorizationId/revoke` — ends an app's\n * access: the grant and every token issued under it, at once (the app's next\n * call is a 401). Resolves when done.\n *\n * A signed-in browser session only, like {@link connectedApps}. Throws\n * `NotFoundError` when the grant is not the user's or is already revoked.\n */\n async revokeConnectedApp(authorizationId: string): Promise<void> {\n await this.request(\"POST\", `/v1/me/connected-apps/${encodeURIComponent(authorizationId)}/revoke`)\n }\n\n private buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>): string {\n const base = this.baseUrl || (typeof window !== \"undefined\" ? window.location.origin : \"http://placeholder\")\n const url = new URL(path, base)\n // If baseUrl was empty, strip the placeholder origin\n const fullUrl = this.baseUrl ? url.toString() : url.pathname + url.search\n if (query) {\n const u = new URL(this.baseUrl ? fullUrl : fullUrl, base)\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) u.searchParams.set(k, String(v))\n }\n return this.baseUrl ? u.toString() : u.pathname + u.search\n }\n return fullUrl\n }\n}\n","/**\n * Auth provides the token used for `Authorization: Bearer <token>` on each request.\n * Implementations:\n * - StaticTokenAuth — fixed string (server-side, OAuth access token, API token)\n * - CallbackAuth — caller-supplied async function (BYO logic)\n * - supabaseAuth — pulls JWT from a Supabase client live (browser frontends)\n */\nexport interface Auth {\n /** Returns the current Bearer token, or null if not authenticated. */\n getToken(): Promise<string | null>\n}\n\nexport class StaticTokenAuth implements Auth {\n constructor(private token: string) {}\n async getToken() {\n return this.token\n }\n}\n\nexport class CallbackAuth implements Auth {\n constructor(private fn: () => string | null | Promise<string | null>) {}\n async getToken() {\n return this.fn()\n }\n}\n\ninterface SupabaseLikeClient {\n auth: {\n getSession(): Promise<{ data: { session: { access_token: string } | null } }>\n }\n}\n\n/** Pulls a JWT from a Supabase v2 client. Caller supplies their own supabase. */\nexport function supabaseAuth(supabase: SupabaseLikeClient): Auth {\n return {\n async getToken() {\n const { data } = await supabase.auth.getSession()\n return data.session?.access_token ?? null\n },\n }\n}\n"]}
|