@picsart/ai-sdk 5.40.0 → 6.0.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/index.js CHANGED
@@ -1,31 +1,6 @@
1
+ import { __commonJS, __toESM } from './chunk-4VNS5WPM.js';
1
2
  import { deflateSync, inflateSync } from 'fflate';
2
3
 
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __commonJS = (cb, mod) => function __require() {
10
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- __defProp(target, "default", { value: mod, enumerable: true }) ,
26
- mod
27
- ));
28
-
29
4
  // ../../node_modules/@picsart/pa-model-pricing-sdk/build/lib/errors/ModelPricingClientError.js
30
5
  var require_ModelPricingClientError = __commonJS({
31
6
  "../../node_modules/@picsart/pa-model-pricing-sdk/build/lib/errors/ModelPricingClientError.js"(exports) {
@@ -441,192 +416,6 @@ var require_build = __commonJS({
441
416
  }
442
417
  });
443
418
 
444
- // src/core/errors.ts
445
- var ApiError = class extends Error {
446
- /** HTTP status, or the synthesized equivalent for non-HTTP failures. */
447
- status;
448
- /** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
449
- code;
450
- /** Alias of {@link code}, named after the platform's own error field. */
451
- reason;
452
- constructor(message, init) {
453
- super(message);
454
- this.name = "ApiError";
455
- this.status = init.status;
456
- this.code = init.code;
457
- this.reason = init.code;
458
- }
459
- };
460
- var CODE_BY_STATUS = {
461
- 400: "bad_request",
462
- 401: "unauthorized",
463
- 402: "payment_required",
464
- 403: "forbidden",
465
- 404: "not_found",
466
- 408: "timeout",
467
- 409: "conflict",
468
- 413: "payload_too_large",
469
- 422: "unprocessable_entity",
470
- 429: "rate_limited",
471
- 500: "server_error",
472
- 502: "bad_gateway",
473
- 503: "service_unavailable",
474
- 504: "gateway_timeout"
475
- };
476
- function codeForStatus(status) {
477
- return CODE_BY_STATUS[status] ?? (status >= 500 ? "server_error" : `http_${status}`);
478
- }
479
- async function readErrorBody(res) {
480
- let text = "";
481
- try {
482
- text = await res.text();
483
- } catch {
484
- return { text: "" };
485
- }
486
- try {
487
- const parsed = JSON.parse(text);
488
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
489
- return { text, json: parsed };
490
- }
491
- } catch {
492
- }
493
- return { text };
494
- }
495
- function reasonFrom(json, status) {
496
- const raw = json?.reason ?? json?.code;
497
- return typeof raw === "string" && raw.length > 0 ? raw : codeForStatus(status);
498
- }
499
-
500
- // src/core/workflow.ts
501
- var DEFAULT_POLL_INTERVAL_MS = 2e3;
502
- var DEFAULT_MAX_ATTEMPTS = 300;
503
- var sleepDefault = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
504
- function getNested(raw, path) {
505
- let current = raw;
506
- for (const key of path) {
507
- if (!current || typeof current !== "object") return void 0;
508
- current = current[key];
509
- }
510
- return current;
511
- }
512
- function pickFirst(raw, paths) {
513
- for (const path of paths) {
514
- const value = getNested(raw, path);
515
- if (value !== void 0) return value;
516
- }
517
- return void 0;
518
- }
519
- function normalizeStatus(status) {
520
- if (typeof status !== "string") return "UNKNOWN";
521
- const s = status.toUpperCase();
522
- if (s === "ACCEPTED") return "ACCEPTED";
523
- if (s === "IN_PROGRESS" || s === "PENDING" || s === "RUNNING") return "IN_PROGRESS";
524
- if (s === "COMPLETED" || s === "SUCCESS") return "COMPLETED";
525
- if (s === "FAILED" || s === "ERROR") return "FAILED";
526
- if (s === "CANCELED" || s === "CANCELLED") return "CANCELED";
527
- return "UNKNOWN";
528
- }
529
- function parseWorkflowStatus(handle, raw) {
530
- const statusRaw = pickFirst(raw, [["response", "status"], ["status"]]);
531
- const status = normalizeStatus(statusRaw);
532
- const result = pickFirst(raw, [["response", "result"], ["result"]]);
533
- const usageRaw = pickFirst(raw, [["response", "usage"], ["usage"]]);
534
- const usage = usageRaw && typeof usageRaw === "object" && (typeof usageRaw.credits === "number" || Array.isArray(usageRaw.details)) ? usageRaw : void 0;
535
- const errorRaw = pickFirst(raw, [["response", "error"], ["response", "message"], ["error"], ["message"], ["reason"]]);
536
- const reasonRaw = pickFirst(raw, [["response", "reason"], ["reason"]]);
537
- const statusCodeRaw = pickFirst(raw, [["response", "statusCode"], ["statusCode"]]);
538
- const progressRaw = pickFirst(raw, [["response", "progress"], ["progress"]]);
539
- const progress = progressRaw && typeof progressRaw === "object" ? {
540
- percent: typeof progressRaw.percent === "number" ? progressRaw.percent : void 0,
541
- estimatedSecondsLeft: typeof progressRaw.estimatedSecondsLeft === "number" ? progressRaw.estimatedSecondsLeft : void 0
542
- } : void 0;
543
- return {
544
- handle,
545
- status,
546
- result,
547
- error: typeof errorRaw === "string" ? errorRaw : void 0,
548
- reason: typeof reasonRaw === "string" ? reasonRaw : void 0,
549
- statusCode: typeof statusCodeRaw === "number" ? statusCodeRaw : void 0,
550
- progress,
551
- usage,
552
- raw
553
- };
554
- }
555
- function isTerminal(status) {
556
- return status === "COMPLETED" || status === "FAILED" || status === "CANCELED";
557
- }
558
- function createWorkflowClient(transport, options = {}) {
559
- const parseStatus = options.parseStatus ?? parseWorkflowStatus;
560
- const sleep2 = options.sleep ?? sleepDefault;
561
- const defaultPollIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
562
- const defaultMaxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
563
- const submit = async (request) => {
564
- if (!transport.submit) {
565
- throw new ApiError("Transport does not support submit (execute-only transport)", {
566
- status: 400,
567
- code: "unsupported_transport"
568
- });
569
- }
570
- return transport.submit(request);
571
- };
572
- const status = async (handle, signal) => {
573
- if (!transport.status) {
574
- throw new ApiError("Transport does not support status (execute-only transport)", {
575
- status: 400,
576
- code: "unsupported_transport"
577
- });
578
- }
579
- const raw = await transport.status(handle, signal);
580
- return parseStatus(handle, raw);
581
- };
582
- const result = async (handle, pollOptions = {}) => {
583
- const intervalMs = pollOptions.intervalMs ?? defaultPollIntervalMs;
584
- const maxAttempts = pollOptions.maxAttempts ?? defaultMaxAttempts;
585
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
586
- if (pollOptions.signal?.aborted) {
587
- throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
588
- }
589
- const next = await status(handle, pollOptions.signal);
590
- if (isTerminal(next.status)) return next;
591
- await sleep2(intervalMs);
592
- }
593
- throw new ApiError(
594
- `Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
595
- { status: 408, code: "timeout" }
596
- );
597
- };
598
- const run = async (request, runOptions = {}) => {
599
- const runMode = runOptions.mode;
600
- const useExecute = runMode === "sync" || runMode === void 0 && !transport.submit;
601
- if (useExecute) {
602
- const raw = await transport.execute(request);
603
- const syntheticHandle = { workflow: request.workflow, id: "sync" };
604
- const parsed = parseStatus(syntheticHandle, raw);
605
- return parsed.status === "UNKNOWN" ? { ...parsed, status: "COMPLETED" } : parsed;
606
- }
607
- const handle = await submit(request);
608
- return result(handle, runOptions);
609
- };
610
- const subscribe = async function* (handle, subscribeOptions = {}) {
611
- const intervalMs = subscribeOptions.intervalMs ?? defaultPollIntervalMs;
612
- const maxAttempts = subscribeOptions.maxAttempts ?? defaultMaxAttempts;
613
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
614
- if (subscribeOptions.signal?.aborted) {
615
- throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
616
- }
617
- const next = await status(handle, subscribeOptions.signal);
618
- yield next;
619
- if (isTerminal(next.status)) return next;
620
- await sleep2(intervalMs);
621
- }
622
- throw new ApiError(
623
- `Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
624
- { status: 408, code: "timeout" }
625
- );
626
- };
627
- return { submit, status, result, run, subscribe };
628
- }
629
-
630
419
  // src/core/descriptors/utils.ts
631
420
  function extractDefaults(params2) {
632
421
  const defaults = {};
@@ -851,11 +640,47 @@ function transferValues(newParams, prev) {
851
640
  return ctx;
852
641
  }
853
642
 
643
+ // src/core/errors.ts
644
+ var ApiError = class extends Error {
645
+ /** HTTP status, or the synthesized equivalent for non-HTTP failures. */
646
+ status;
647
+ /** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
648
+ code;
649
+ /** Alias of {@link code}, named after the platform's own error field. */
650
+ reason;
651
+ constructor(message, init) {
652
+ super(message);
653
+ this.name = "ApiError";
654
+ this.status = init.status;
655
+ this.code = init.code;
656
+ this.reason = init.code;
657
+ }
658
+ };
659
+ var CODE_BY_STATUS = {
660
+ 400: "bad_request",
661
+ 401: "unauthorized",
662
+ 402: "payment_required",
663
+ 403: "forbidden",
664
+ 404: "not_found",
665
+ 408: "timeout",
666
+ 409: "conflict",
667
+ 413: "payload_too_large",
668
+ 422: "unprocessable_entity",
669
+ 429: "rate_limited",
670
+ 500: "server_error",
671
+ 502: "bad_gateway",
672
+ 503: "service_unavailable",
673
+ 504: "gateway_timeout"
674
+ };
675
+ function codeForStatus(status) {
676
+ return CODE_BY_STATUS[status] ?? (status >= 500 ? "server_error" : `http_${status}`);
677
+ }
678
+
854
679
  // src/core/visibility.ts
855
680
  var DEFAULT_VISIBLE_RELEASES = ["production", "general-availability"];
856
681
  var releaseOf = (m) => m.release ?? "production";
857
682
  function isVisibleForReleases(m, releases = DEFAULT_VISIBLE_RELEASES) {
858
- if (m.disabled || m.deprecated) return false;
683
+ if (m.deprecated) return false;
859
684
  return releases.includes(releaseOf(m));
860
685
  }
861
686
 
@@ -1290,7 +1115,6 @@ function defineModels(provider, configs) {
1290
1115
  if (c.pollOptions !== void 0) model.pollOptions = c.pollOptions;
1291
1116
  if (c.badge !== void 0) model.badge = c.badge;
1292
1117
  if (c.addedAt !== void 0) model.addedAt = c.addedAt;
1293
- if (c.disabled !== void 0) model.disabled = c.disabled;
1294
1118
  if (c.deprecated !== void 0) model.deprecated = c.deprecated;
1295
1119
  if (c.release !== void 0) model.release = c.release;
1296
1120
  if (c.modelId !== void 0) model.modelId = c.modelId;
@@ -1523,23 +1347,6 @@ var klingOmniAdvancedParams = {
1523
1347
  };
1524
1348
 
1525
1349
  // src/vendors/catalog/kling/index.ts
1526
- var KLING_DUAL_IMAGE_EFFECTS = /* @__PURE__ */ new Set([
1527
- "pet_skateboard",
1528
- "daily_ootd",
1529
- "toss_run",
1530
- "switch_to_silk",
1531
- "studio_look",
1532
- "french_elegance",
1533
- "finger_swipe",
1534
- "smooth_transition",
1535
- "kiss_pro",
1536
- "snow_night_kiss",
1537
- "eternal_kiss",
1538
- "cheers_2026",
1539
- "fight_pro",
1540
- "hug_pro",
1541
- "heart_gesture_pro"
1542
- ]);
1543
1350
  var V3_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1544
1351
  var V26_DURATIONS = [5, 10];
1545
1352
  var KLING_IMAGE_AR = ["16:9", "9:16", "1:1", "21:9", "4:3", "3:2", "2:3", "3:4"];
@@ -1893,7 +1700,7 @@ var { MODELS } = defineModels("kling", [
1893
1700
  id: "kling-elements",
1894
1701
  name: "Kling Elements",
1895
1702
  addedAt: "2026-05-11",
1896
- disabled: true,
1703
+ release: "preview",
1897
1704
  // pending backend toolId + pricing confirmation
1898
1705
  workflow: "kling-elements",
1899
1706
  estimatedTime: 30,
@@ -2232,10 +2039,27 @@ var buildKlingElementsPayload = (input) => {
2232
2039
  ...input.elementVoiceId ? { element_voice_id: input.elementVoiceId } : {}
2233
2040
  };
2234
2041
  };
2042
+ var DUAL_IMAGE_EFFECTS = /* @__PURE__ */ new Set([
2043
+ "pet_skateboard",
2044
+ "daily_ootd",
2045
+ "toss_run",
2046
+ "switch_to_silk",
2047
+ "studio_look",
2048
+ "french_elegance",
2049
+ "finger_swipe",
2050
+ "smooth_transition",
2051
+ "kiss_pro",
2052
+ "snow_night_kiss",
2053
+ "eternal_kiss",
2054
+ "cheers_2026",
2055
+ "fight_pro",
2056
+ "hug_pro",
2057
+ "heart_gesture_pro"
2058
+ ]);
2235
2059
  var buildKlingVideoEffectsPayload = (input) => {
2236
- const scene = input.templateId ?? input.style;
2060
+ const scene = input.templateId;
2237
2061
  const catalogItem = getHydratedCatalog({ workflow: "kling/v1/catalog/templates" })?.items.find((item) => item.id === scene);
2238
- const slots = typeof catalogItem?.meta?.imageSlots === "number" ? catalogItem.meta.imageSlots : scene && KLING_DUAL_IMAGE_EFFECTS.has(scene) ? 2 : 1;
2062
+ const slots = typeof catalogItem?.meta?.imageSlots === "number" ? catalogItem.meta.imageSlots : scene && DUAL_IMAGE_EFFECTS.has(scene) ? 2 : 1;
2239
2063
  const uploaded = input.imageUrls?.length ?? 0;
2240
2064
  if (uploaded < slots) {
2241
2065
  throw new ApiError(`Kling Video Effects: the "${scene}" effect requires ${slots} image${slots > 1 ? "s" : ""} (got ${uploaded}).`, { status: 400, code: "validation_error" });
@@ -4734,8 +4558,8 @@ var GEMINI_DEFAULT_VOICE_ID = "Kore";
4734
4558
  var DEFAULT_GROK_VOICE_ID = "eve";
4735
4559
  var ASYNC_DEFAULT_VOICE_ID = "cca0e076-b350-4966-b570-4c2fca50b525";
4736
4560
  var SEEDAUDIO_DEFAULT_VOICE_ID = "en_male_tim_uranus_bigtts";
4737
- function getVoiceById(id, extra) {
4738
- return [...extra ?? [], ...getHydratedVoices()].find((v) => v.id === id);
4561
+ function getVoiceById(id) {
4562
+ return getHydratedVoices().find((v) => v.id === id);
4739
4563
  }
4740
4564
 
4741
4565
  // src/vendors/catalog/seedaudio.ts
@@ -6566,7 +6390,7 @@ var { MODELS: MODELS23 } = defineModels("elevenlabs", [
6566
6390
  estimatedTime: 15,
6567
6391
  mode: "audio",
6568
6392
  inputType: "tts",
6569
- disabled: true,
6393
+ release: "preview",
6570
6394
  description: "Remix voice characteristics by describing the desired vocal style.",
6571
6395
  features: [feat("Voice Design", "characteristic"), feat("Remix", "characteristic")],
6572
6396
  paramConfig: {
@@ -6735,9 +6559,6 @@ var { MODELS: MODELS24 } = defineModels("heygen", [
6735
6559
  ]);
6736
6560
 
6737
6561
  // src/vendors/catalog/minimax.ts
6738
- var buildMinimaxTTSPayload = (ctx) => ({
6739
- text: ctx.prompt
6740
- });
6741
6562
  var buildMinimaxMusicPayload = (ctx) => ({
6742
6563
  prompt: ctx.prompt,
6743
6564
  ...ctx.lyricsPrompt ? { lyrics: ctx.lyricsPrompt } : {},
@@ -6788,25 +6609,9 @@ var h3MaxConstraints = [
6788
6609
  } }
6789
6610
  ];
6790
6611
  var { MODELS: MODELS25 } = defineModels("minimax", [
6791
- {
6792
- id: "minimax-02-hd",
6793
- name: "MiniMax 02 HD",
6794
- modelId: "minimax-02-hd",
6795
- addedAt: "2026-02-06",
6796
- workflow: "minimax-tts",
6797
- buildPayload: buildMinimaxTTSPayload,
6798
- estimatedTime: 15,
6799
- mode: "audio",
6800
- inputType: "tts",
6801
- disabled: true,
6802
- // Backend workflow not deployed
6803
- description: "HD voice synthesis with rich tonal depth and consistent delivery.",
6804
- features: [feat("Consistent", "characteristic"), feat("Cinematic", "characteristic")],
6805
- paramConfig: {
6806
- ...params.language(true),
6807
- ...params.prompt({ maxLength: 150 })
6808
- }
6809
- },
6612
+ // minimax-02-hd (minimax-tts) was removed in 6.0: its backend workflow was
6613
+ // never deployed anywhere, so no generation ever existed to resolve — nothing
6614
+ // to deprecate. Re-add as a fresh entry if MiniMax TTS ever ships.
6810
6615
  {
6811
6616
  id: "minimax-music-v2",
6812
6617
  name: "MiniMax Music v2",
@@ -9371,7 +9176,7 @@ var ALL_MODELS = [
9371
9176
  ...MODELS36,
9372
9177
  ...MODELS37
9373
9178
  ];
9374
- var getModelsByMode = (mode, includeDisabled = false) => ALL_MODELS.filter((m) => m.mode === mode && (includeDisabled || isVisibleForReleases(m)));
9179
+ var getModelsByMode = (mode, includeHidden = false) => ALL_MODELS.filter((m) => m.mode === mode && (includeHidden || isVisibleForReleases(m)));
9375
9180
 
9376
9181
  // src/core/contracts.ts
9377
9182
  function requireObject(value, message) {
@@ -9416,9 +9221,6 @@ function createModelContract(model) {
9416
9221
  output: buildOutputSchema(model)
9417
9222
  };
9418
9223
  }
9419
- function validateModelInput(model, input) {
9420
- return createModelContract(model).input.parse(input);
9421
- }
9422
9224
  var _contracts = null;
9423
9225
  function ensureContracts() {
9424
9226
  if (!_contracts) {
@@ -9457,10 +9259,7 @@ function throwIfErrorResult(result, modelName) {
9457
9259
  function extractSyncResult(raw) {
9458
9260
  if (!raw || typeof raw !== "object") return raw;
9459
9261
  const data = raw;
9460
- const syncResult = data.response?.result ?? data.result;
9461
- const sr = syncResult;
9462
- const imgs = sr && Array.isArray(sr.images) ? sr.images : null;
9463
- return imgs?.length ? imgs[0] : syncResult;
9262
+ return data.response?.result ?? data.result;
9464
9263
  }
9465
9264
  var extractUrl = (result) => {
9466
9265
  if (Array.isArray(result)) return extractUrl(result[0]);
@@ -9586,27 +9385,55 @@ var extractText = (result) => {
9586
9385
  }
9587
9386
  return void 0;
9588
9387
  };
9388
+ var RESULT_ARRAY_KEYS = ["items", "images", "imageUrls", "urls", "data", "previews"];
9589
9389
  var extractAllResults = (result) => {
9590
9390
  if (!result || typeof result !== "object") return void 0;
9591
9391
  const obj = result;
9592
- if (Array.isArray(obj.items) && obj.items.length > 1) {
9593
- const items = [];
9594
- for (const item of obj.items) {
9595
- if (item && typeof item === "object") {
9596
- const it = item;
9597
- const url = typeof it.url === "string" ? it.url : void 0;
9598
- if (url) {
9599
- items.push({
9600
- url,
9601
- exploreImageId: typeof it.image_id === "string" ? it.image_id : void 0
9602
- });
9603
- }
9604
- }
9392
+ let arr;
9393
+ for (const key of RESULT_ARRAY_KEYS) {
9394
+ const candidate = obj[key];
9395
+ if (Array.isArray(candidate) && candidate.length > 1) {
9396
+ arr = candidate;
9397
+ break;
9605
9398
  }
9606
- if (items.length > 0) return items;
9607
9399
  }
9608
- return void 0;
9400
+ if (!arr) return void 0;
9401
+ const items = [];
9402
+ for (const entry of arr) {
9403
+ if (typeof entry === "string") {
9404
+ items.push({ url: entry });
9405
+ continue;
9406
+ }
9407
+ if (entry && typeof entry === "object") {
9408
+ const it = entry;
9409
+ if (typeof it.url === "string") items.push({ url: it.url, source: it });
9410
+ }
9411
+ }
9412
+ return items.length > 0 ? items : void 0;
9609
9413
  };
9414
+ function buildItemMetadata(parsed, item, index, provider) {
9415
+ const meta = {};
9416
+ const top = parsed && typeof parsed === "object" ? parsed : void 0;
9417
+ const it = item && typeof item === "object" && !Array.isArray(item) ? item : void 0;
9418
+ if (provider === "recraft" && typeof it?.image_id === "string") meta.exploreImageId = it.image_id;
9419
+ if (provider === "elevenlabs" && typeof it?.generated_voice_id === "string") meta.generatedVoiceId = it.generated_voice_id;
9420
+ if (typeof top?.seed === "number") meta.seed = top.seed;
9421
+ if (Array.isArray(top?.has_nsfw_concepts) && typeof top.has_nsfw_concepts[index] === "boolean") {
9422
+ meta.nsfw = top.has_nsfw_concepts[index];
9423
+ }
9424
+ if (typeof it?.width === "number") meta.width = it.width;
9425
+ if (typeof it?.height === "number") meta.height = it.height;
9426
+ if (typeof it?.content_type === "string") meta.contentType = it.content_type;
9427
+ const video = top?.video && typeof top.video === "object" ? top.video : void 0;
9428
+ if (video) {
9429
+ if (typeof video.duration === "number") meta.duration = video.duration;
9430
+ if (typeof video.fps === "number") meta.fps = video.fps;
9431
+ if (typeof video.file_size === "number") meta.fileSize = video.file_size;
9432
+ if (meta.width === void 0 && typeof video.width === "number") meta.width = video.width;
9433
+ if (meta.height === void 0 && typeof video.height === "number") meta.height = video.height;
9434
+ }
9435
+ return Object.keys(meta).length > 0 ? meta : void 0;
9436
+ }
9610
9437
  function toCompletedStatus(handle, result, raw, usage) {
9611
9438
  return {
9612
9439
  handle,
@@ -9659,1132 +9486,1792 @@ function resolveModel(id) {
9659
9486
  return found;
9660
9487
  }
9661
9488
 
9662
- // src/client/transport.ts
9663
- var GATEWAY_HEADERS = {
9664
- "platform": "api",
9665
- "X-Touchpoint": "sdk"
9489
+ // ../../node_modules/@picsart/workflows-client/dist/index.mjs
9490
+ var logger_default = {
9491
+ error: (...args) => {
9492
+ console.error(...args);
9493
+ },
9494
+ warn: (...args) => {
9495
+ console.debug(...args);
9496
+ },
9497
+ info: (...args) => {
9498
+ console.info(...args);
9499
+ },
9500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9501
+ debug: (...args) => {
9502
+ console.debug(...args);
9503
+ }
9666
9504
  };
9667
- function resolveFetch(config) {
9668
- if (config.fetch) return config.fetch;
9669
- if (config.apiKey) {
9670
- const token = config.apiKey.replace(/^Bearer\s+/i, "");
9671
- return (url, init) => {
9672
- const headers = new Headers(init?.headers);
9673
- headers.set("Authorization", `Bearer ${token}`);
9674
- for (const [name, value] of Object.entries(GATEWAY_HEADERS)) {
9675
- if (!headers.has(name)) headers.set(name, value);
9505
+ var ExecutionMode = /* @__PURE__ */ ((ExecutionMode2) => {
9506
+ ExecutionMode2["ASYNC"] = "ASYNC";
9507
+ ExecutionMode2["SYNC"] = "SYNC";
9508
+ ExecutionMode2["STREAM"] = "STREAM";
9509
+ ExecutionMode2["SOCKET"] = "SOCKET";
9510
+ return ExecutionMode2;
9511
+ })(ExecutionMode || {});
9512
+ var STREAM_EVENT_NAME = "task.stream";
9513
+ var normalizeWorkflowName = (workflow) => (workflow || "").replace(/-/g, "_").toLowerCase();
9514
+ var taskChannel = (workflow, taskId) => `workflows:${normalizeWorkflowName(workflow)}:${taskId}`;
9515
+ var workflowChannel = (workflow) => `workflows:${normalizeWorkflowName(workflow)}:all`;
9516
+ var DEFAULT_REASON = "unknown_error";
9517
+ var DEFAULT_MESSAGE = "Unknown error";
9518
+ var NON_JSON_BODY_MESSAGE = "Non json response was returned from server";
9519
+ var asRecord = (value) => value && typeof value === "object" ? value : void 0;
9520
+ var asText = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
9521
+ var asStatusCode = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
9522
+ var WorkflowsError = class _WorkflowsError extends Error {
9523
+ constructor(init) {
9524
+ super(init.message);
9525
+ this.name = this.constructor.name;
9526
+ this.reason = init.reason;
9527
+ this.httpStatusCode = init.httpStatusCode;
9528
+ }
9529
+ /**
9530
+ * Builds an error from an already-parsed error payload — a `FailedResult` off a stream/socket
9531
+ * event, or any body with `reason` / `message` / `statusCode`. Each field falls back to
9532
+ * `fallback` individually, so a payload that carries only a message still keeps the caller's
9533
+ * reason and status.
9534
+ */
9535
+ static fromBody(body, fallback) {
9536
+ const parsed = asRecord(body);
9537
+ return new _WorkflowsError({
9538
+ reason: asText(parsed?.reason) || fallback.reason,
9539
+ message: asText(parsed?.message) || fallback.message,
9540
+ httpStatusCode: asStatusCode(parsed?.statusCode) ?? fallback.httpStatusCode
9541
+ });
9542
+ }
9543
+ /**
9544
+ * Builds an error from a non-ok `Response`. The status always comes from the response itself;
9545
+ * `reason` and `message` come from the JSON body when it has them, otherwise from `fallback`
9546
+ * (a body that isn't JSON at all is reported as such rather than throwing a parse error).
9547
+ */
9548
+ static async fromResponse(response, fallback) {
9549
+ let body;
9550
+ try {
9551
+ body = await response.json();
9552
+ } catch {
9553
+ body = void 0;
9554
+ }
9555
+ const parsed = asRecord(body);
9556
+ return new _WorkflowsError({
9557
+ reason: asText(parsed?.reason) || fallback?.reason || DEFAULT_REASON,
9558
+ message: asText(parsed?.message) || fallback?.message || (parsed ? DEFAULT_MESSAGE : NON_JSON_BODY_MESSAGE),
9559
+ httpStatusCode: response.status
9560
+ });
9561
+ }
9562
+ /** Wraps an unexpected local throw (anything that isn't already a WorkflowsError). */
9563
+ static fromUnknown(error) {
9564
+ return new _WorkflowsError({
9565
+ reason: DEFAULT_REASON,
9566
+ message: (error instanceof Error ? error.message : asText(error)) || DEFAULT_MESSAGE
9567
+ });
9568
+ }
9569
+ };
9570
+ var WorkflowResultUpdateAware = class {
9571
+ constructor(onProgressFn, onPartialResultFn, onEventFn) {
9572
+ this.onProgressFn = onProgressFn;
9573
+ this.onPartialResultFn = onPartialResultFn;
9574
+ this.onEventFn = onEventFn;
9575
+ }
9576
+ async onUpdate(response) {
9577
+ if (!response.updated) return;
9578
+ await this.deliverEvents(response);
9579
+ if (response.status !== "IN_PROGRESS") return;
9580
+ const newUpdated = new Date(response.updated);
9581
+ if (newUpdated?.getTime() !== this.updated?.getTime()) {
9582
+ this.updated = newUpdated;
9583
+ await this.onPartialResultFn?.(response);
9584
+ if (response.progress) {
9585
+ await this.onProgressFn?.(response.progress);
9676
9586
  }
9677
- return globalThis.fetch(url, { ...init, headers });
9678
- };
9587
+ }
9679
9588
  }
9680
- throw new Error("createClient config requires either `fetch` or `apiKey`.");
9681
- }
9682
- function buildTransport(config) {
9683
- const apiUrl = config.apiUrl;
9684
- const f = resolveFetch(config);
9685
- const jsonPost = async (url, body, signal) => f(url, {
9686
- method: "POST",
9687
- headers: { "Content-Type": "application/json" },
9688
- body: JSON.stringify(body),
9689
- signal
9690
- });
9691
- return {
9692
- async submit(request) {
9693
- const res = await jsonPost(
9694
- `${apiUrl}/workflows/${request.workflow}/submit`,
9695
- { params: request.payload },
9696
- request.signal
9697
- );
9698
- const { text, json } = await readErrorBody(res);
9699
- if (!res.ok) {
9700
- const detail = json ? json.message ?? JSON.stringify(json) : text;
9701
- throw new ApiError(`Submit failed (${res.status}): ${detail}`, {
9702
- status: res.status,
9703
- code: reasonFrom(json, res.status)
9704
- });
9589
+ async deliverEvents(response) {
9590
+ if (!response.events?.length || !this.onEventFn) return;
9591
+ let startIdx = 0;
9592
+ if (this._lastEventId) {
9593
+ const lastSeenIdx = response.events.findIndex((e) => e.id === this._lastEventId);
9594
+ if (lastSeenIdx !== -1) {
9595
+ startIdx = lastSeenIdx + 1;
9705
9596
  }
9706
- const response = json?.response;
9707
- const id = response?.id ?? json?.id;
9708
- if (!id) {
9709
- throw new ApiError(`No task id in response: ${json ? JSON.stringify(json) : text}`, {
9710
- status: 502,
9711
- code: "invalid_response"
9712
- });
9713
- }
9714
- return { workflow: request.workflow, id: String(id) };
9715
- },
9716
- async status(handle, signal) {
9717
- const res = await f(`${apiUrl}/workflows/${handle.workflow}/${handle.id}/result`, { signal });
9718
- if (!res.ok) {
9719
- const { text, json } = await readErrorBody(res);
9720
- const detail = json ? json.message ?? text : text;
9721
- throw new ApiError(`Status check failed (${res.status}): ${detail}`, {
9722
- status: res.status,
9723
- code: reasonFrom(json, res.status)
9597
+ }
9598
+ const newEvents = response.events.slice(startIdx);
9599
+ for (const event of newEvents) {
9600
+ await this.onEventFn(event);
9601
+ }
9602
+ if (newEvents.length > 0) {
9603
+ this._lastEventId = newEvents[newEvents.length - 1].id;
9604
+ }
9605
+ }
9606
+ };
9607
+ var bearer = (token) => token.startsWith("Bearer ") ? token : `Bearer ${token}`;
9608
+ var WorkflowsSocket = class {
9609
+ constructor(config) {
9610
+ this.config = config;
9611
+ this.ownsSocket = false;
9612
+ this.channelRefs = /* @__PURE__ */ new WeakMap();
9613
+ }
9614
+ // Watch already-submitted work live, as an async iterable of the raw StreamSocketMessage the gateway
9615
+ // pushes. Iterate with `for await` and switch on `msg.type` (EventTypes.* or an `event.<custom>`
9616
+ // string), reading `msg.payload`. With a taskId, watch that one task and end after its COMPLETED /
9617
+ // FAILED; without, watch every task of the workflow until stopped. A lost socket session (or the
9618
+ // passed AbortSignal) is THROWN out of the loop; `break` also stops watching — the `finally` tears
9619
+ // everything down.
9620
+ async *subscribe(options) {
9621
+ if (!this.config.socket && !this.config.socketConnection) {
9622
+ throw new WorkflowsError({
9623
+ httpStatusCode: 400,
9624
+ reason: "invalid_state",
9625
+ message: "subscribe() requires either `socket` or `socketConnection` on the client."
9626
+ });
9627
+ }
9628
+ if (options.signal?.aborted) throw new DOMException("Aborted", "AbortError");
9629
+ const channel = options.taskId ? taskChannel(options.name, options.taskId) : workflowChannel(options.name);
9630
+ this.assertValidChannel(channel);
9631
+ const socket = await this.resolveSocket();
9632
+ if (!socket) return;
9633
+ if (options.signal?.aborted) throw new DOMException("Aborted", "AbortError");
9634
+ const workflow = normalizeWorkflowName(options.name);
9635
+ const queue = [];
9636
+ let wake;
9637
+ const push = (item) => {
9638
+ queue.push(item);
9639
+ const w = wake;
9640
+ wake = void 0;
9641
+ w?.();
9642
+ };
9643
+ const handler = (message) => {
9644
+ if (!message) return;
9645
+ const mine = options.taskId ? message.taskId === options.taskId : message.workflow === workflow;
9646
+ if (mine) push({ msg: message });
9647
+ };
9648
+ socket.on(STREAM_EVENT_NAME, handler);
9649
+ const streamEnded = (detail) => push({ error: new WorkflowsError({
9650
+ httpStatusCode: 503,
9651
+ reason: "socket_connection_lost",
9652
+ message: `Lost the socket connection (${detail}); the event stream ended.`
9653
+ }) });
9654
+ const detachReconnect = this.onSessionLost(socket, () => streamEnded("the session could not be recovered on reconnect"));
9655
+ const detachAbandoned = this.onConnectAbandoned(socket, (err) => streamEnded(
9656
+ `socket.io stopped reconnecting after "${err instanceof Error ? err.message : String(err)}"`
9657
+ ));
9658
+ const detachDisconnect = this.onDisconnected(socket, (reason) => streamEnded(`"${reason}"`));
9659
+ const onAbort = () => push({ error: new DOMException("Aborted", "AbortError") });
9660
+ options.signal?.addEventListener("abort", onAbort);
9661
+ this.joinChannel(socket, channel);
9662
+ try {
9663
+ while (true) {
9664
+ while (queue.length) {
9665
+ const item = queue.shift();
9666
+ if ("error" in item) throw item.error;
9667
+ yield item.msg;
9668
+ if (options.taskId && (item.msg.type === "task.completed" || item.msg.type === "task.failed")) return;
9669
+ }
9670
+ await new Promise((resolve) => {
9671
+ wake = resolve;
9724
9672
  });
9725
9673
  }
9726
- return res.json();
9727
- },
9728
- async execute(request) {
9729
- const res = await jsonPost(
9730
- `${apiUrl}/workflows/${request.workflow}/execute`,
9731
- { params: request.payload },
9732
- request.signal
9733
- );
9734
- if (!res.ok) {
9735
- const { text, json } = await readErrorBody(res);
9736
- const detail = json ? json.message ?? text : text;
9737
- throw new ApiError(`Execute failed (${res.status}): ${detail}`, {
9738
- status: res.status,
9739
- code: reasonFrom(json, res.status)
9740
- });
9674
+ } finally {
9675
+ options.signal?.removeEventListener("abort", onAbort);
9676
+ this.leaveChannel(socket, channel);
9677
+ socket.off(STREAM_EVENT_NAME, handler);
9678
+ detachReconnect();
9679
+ detachAbandoned();
9680
+ detachDisconnect();
9681
+ }
9682
+ }
9683
+ // Opens the socket (create + connect for socketConnection; return an injected one). Idempotent —
9684
+ // resolveSocket memoizes, so repeated calls reuse the same connection.
9685
+ connect() {
9686
+ return this.resolveSocket();
9687
+ }
9688
+ // Disconnects the socket ONLY if this class created it (socketConnection); an injected socket is
9689
+ // left for the caller to manage. Safe to call more than once.
9690
+ async disconnect() {
9691
+ if (!this.ownsSocket || !this.socketPromise) return;
9692
+ const socket = await this.socketPromise.catch(() => void 0);
9693
+ socket?.disconnect();
9694
+ this.socketPromise = void 0;
9695
+ this.ownsSocket = false;
9696
+ }
9697
+ // Backs run({ mode: ExecutionMode.SOCKET }). Joins the workflow channel and starts listening BEFORE
9698
+ // submitting, so no early event is missed — the per-task channel can't be joined until submit mints
9699
+ // the taskId. Events seen before we know our taskId are buffered, then matched once we have it (the
9700
+ // workflow room carries sibling tasks, so the taskId filter is load-bearing).
9701
+ // `markSeen` is fired with the taskId the moment a terminal (COMPLETED/FAILED) event is received — the
9702
+ // socket-mode equivalent of the polling client fetching /result, which is what disabled the task's
9703
+ // pending notification server-side. Fired for both terminal outcomes (matching the old poll, which
9704
+ // disabled on COMPLETED and FAILED alike) and ONLY on a real terminal event — never on a lost session,
9705
+ // abort, or connect error, so an unconsumed result still triggers the async fallback notification.
9706
+ // It's best-effort: runTask fires it without awaiting and swallows any rejection, since a failed disable
9707
+ // only costs a redundant fallback notification and must never fail the run.
9708
+ async runTask(workflowName, submit, executionOptions, markSeen) {
9709
+ const signal = executionOptions?.abortSignal;
9710
+ await this.ensureSocketReady(workflowName, signal);
9711
+ const { onProgress, onPartialResult, onEvent } = executionOptions ?? {};
9712
+ const ac = new AbortController();
9713
+ const forwardAbort = () => ac.abort();
9714
+ signal?.addEventListener("abort", forwardAbort);
9715
+ const iter = this.subscribe({ name: workflowName, signal: ac.signal })[Symbol.asyncIterator]();
9716
+ const firstPull = iter.next();
9717
+ firstPull.catch(() => {
9718
+ });
9719
+ try {
9720
+ const taskId = await submit();
9721
+ for (let pull = firstPull; ; pull = iter.next()) {
9722
+ const { value: message, done } = await pull;
9723
+ if (done) break;
9724
+ if (message.taskId !== taskId) continue;
9725
+ switch (message.type) {
9726
+ case "task.completed":
9727
+ void Promise.resolve(markSeen?.(taskId)).catch(() => void 0);
9728
+ return {
9729
+ result: message.payload?.result,
9730
+ usage: message.payload?.usage,
9731
+ status: "COMPLETED"
9732
+ /* COMPLETED */
9733
+ // a FAILED message throws below instead
9734
+ };
9735
+ case "task.failed":
9736
+ void Promise.resolve(markSeen?.(taskId)).catch(() => void 0);
9737
+ throw this.failureError(message.payload?.result ?? message.payload);
9738
+ case "task.metrics":
9739
+ await onProgress?.(message.payload);
9740
+ break;
9741
+ case "task.partial-result":
9742
+ await onPartialResult?.({ status: "IN_PROGRESS", result: message.payload });
9743
+ break;
9744
+ default:
9745
+ if (message.type.startsWith("event.")) {
9746
+ await onEvent?.({ ...message.payload, type: message.type.replace(/^event\./, "") });
9747
+ }
9748
+ }
9741
9749
  }
9742
- return res.json();
9743
- },
9744
- async options(workflow, payload) {
9750
+ throw new WorkflowsError({
9751
+ httpStatusCode: 500,
9752
+ reason: "socket_stream_ended",
9753
+ message: "Socket stream ended before the task completed."
9754
+ });
9755
+ } finally {
9756
+ signal?.removeEventListener("abort", forwardAbort);
9757
+ ac.abort();
9758
+ await iter.return?.();
9759
+ }
9760
+ }
9761
+ // Pre-submit gate for runTask: validate the channel and get the socket connected before any work is
9762
+ // submitted. The socket itself isn't returned — subscribe() re-resolves it — this only proves it's
9763
+ // reachable and honors an abort that fired before (or during) connecting, since addEventListener('abort')
9764
+ // never fires for an already-aborted signal.
9765
+ async ensureSocketReady(workflowName, signal) {
9766
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
9767
+ const channel = workflowChannel(workflowName);
9768
+ this.assertValidChannel(channel);
9769
+ const socket = await this.resolveSocket();
9770
+ if (!socket) {
9771
+ throw new WorkflowsError({
9772
+ httpStatusCode: 400,
9773
+ reason: "invalid_state",
9774
+ message: "ExecutionMode.SOCKET requires either `socket` or `socketConnection` on the client."
9775
+ });
9776
+ }
9777
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
9778
+ }
9779
+ // Fails a run/subscription when a reconnect couldn't be recovered. The gateway has Connection State
9780
+ // Recovery: a reconnect within its window restores our room and replays missed events, so there's
9781
+ // nothing to do; past the window `recovered` is false and the stream is gone — and we don't refetch
9782
+ // from the DB. We only wire this after the socket is connected, so any 'connect' here is a reconnect.
9783
+ // Returns a function to detach the listener on teardown.
9784
+ onSessionLost(socket, onLost) {
9785
+ const listener = () => {
9786
+ if (!socket.recovered) onLost();
9787
+ };
9788
+ socket.on("connect", listener);
9789
+ return () => socket.off("connect", listener);
9790
+ }
9791
+ // The other half of onSessionLost, for the case it can't see: a connect attempt that FAILS rather than
9792
+ // succeeding — typically a reconnect whose handshake the gateway rejects (an expired token). No
9793
+ // 'connect' event ever fires then, so onSessionLost stays silent and the watcher parks forever on a
9794
+ // socket that will never deliver again. `socket.active` says whether socket.io intends to keep trying:
9795
+ // still true while it retries a transient transport failure (a blip Connection State Recovery papers
9796
+ // over — ignore it, or we'd fail runs that were about to resume), false once it has given up. Only
9797
+ // that second case is terminal, and it covers BOTH a server-side rejection (never retried) and
9798
+ // reconnectionAttempts running out, which is why the caller reports the error rather than a cause.
9799
+ // Returns a detach function.
9800
+ onConnectAbandoned(socket, onAbandoned) {
9801
+ const listener = (err) => {
9802
+ if (socket.active === false) onAbandoned(err);
9803
+ };
9804
+ socket.on("connect_error", listener);
9805
+ return () => socket.off("connect_error", listener);
9806
+ }
9807
+ // The third and last way this socket can go quiet for good: it disconnects and nothing is coming back
9808
+ // — `client.disconnect()` called while a watch is live ('io client disconnect'), or the gateway closing
9809
+ // us out ('io server disconnect'). Neither produces a 'connect' or a 'connect_error', so without this
9810
+ // the watcher parks forever on a socket that is simply gone. `active` splits the cases here too, as
9811
+ // socket.io documents: still true for a transport close or ping timeout, which it retries and CSR
9812
+ // papers over; false once the connection was closed for good. Returns a detach function.
9813
+ onDisconnected(socket, onGone) {
9814
+ const listener = (reason) => {
9815
+ if (socket.active === false) onGone(reason);
9816
+ };
9817
+ socket.on("disconnect", listener);
9818
+ return () => socket.off("disconnect", listener);
9819
+ }
9820
+ resolveSocket() {
9821
+ if (this.socketPromise) return this.socketPromise;
9822
+ const { socket, socketConnection } = this.config;
9823
+ if (!socket && !socketConnection) return Promise.resolve(void 0);
9824
+ const promise = (async () => {
9825
+ const resolved = socket ?? await this.createSocket(socketConnection);
9826
+ this.ownsSocket = !socket;
9745
9827
  try {
9746
- const res = await jsonPost(`${apiUrl}/workflows/${workflow}/options`, { params: payload });
9747
- if (!res.ok) return null;
9748
- const data = await res.json();
9749
- const response = data.response;
9750
- const credits = response?.credits;
9751
- return typeof credits === "number" ? credits : null;
9752
- } catch {
9753
- return null;
9828
+ await this.whenConnected(resolved);
9829
+ } catch (err) {
9830
+ if (!socket) {
9831
+ resolved.disconnect();
9832
+ this.ownsSocket = false;
9833
+ }
9834
+ throw err;
9754
9835
  }
9836
+ return resolved;
9837
+ })();
9838
+ promise.catch(() => {
9839
+ if (this.socketPromise === promise) this.socketPromise = void 0;
9840
+ });
9841
+ this.socketPromise = promise;
9842
+ return promise;
9843
+ }
9844
+ // Resolves once the socket's transport is up; rejects if the connection fails (so callers surface an
9845
+ // error instead of hanging). connect() is idempotent, so driving it is safe whether the socket is
9846
+ // already connecting (autoConnect) or was created with autoConnect:false.
9847
+ whenConnected(socket) {
9848
+ if (socket.connected) return Promise.resolve();
9849
+ return new Promise((resolve, reject) => {
9850
+ const cleanup = () => {
9851
+ socket.off("connect", onConnect);
9852
+ socket.off("connect_error", onError);
9853
+ };
9854
+ const onConnect = () => {
9855
+ cleanup();
9856
+ resolve();
9857
+ };
9858
+ const onError = (err) => {
9859
+ cleanup();
9860
+ reject(err instanceof Error ? err : new Error(`socket connection failed: ${String(err)}`));
9861
+ };
9862
+ socket.on("connect", onConnect);
9863
+ socket.on("connect_error", onError);
9864
+ socket.connect();
9865
+ });
9866
+ }
9867
+ // socket.io-client is an optional peer dependency, imported on demand so non-socket consumers
9868
+ // never load it (and SSR never connects).
9869
+ async createSocket(conn) {
9870
+ let io;
9871
+ try {
9872
+ ({ io } = await import('./esm-debug-3SQICTIF.js'));
9873
+ } catch {
9874
+ throw new WorkflowsError({
9875
+ httpStatusCode: 400,
9876
+ reason: "invalid_state",
9877
+ message: 'socketConnection requires the optional peer dependency "socket.io-client" to be installed.'
9878
+ });
9755
9879
  }
9756
- };
9757
- }
9758
- function isClientConfig(input) {
9759
- return "fetch" in input && typeof input.fetch === "function" || "apiKey" in input && typeof input.apiKey === "string";
9760
- }
9761
-
9762
- // src/client/prepare.ts
9763
- function resolvePayloadBuild(model, ctx) {
9764
- const hasImages = Array.isArray(ctx.imageUrls) && ctx.imageUrls.length > 0 || !!ctx.startFrame || !!ctx.endFrame;
9765
- return {
9766
- hasImages,
9767
- workflow: hasImages && model.editWorkflow ? model.editWorkflow : model.workflow,
9768
- buildPayload: hasImages && model.buildEditPayload ? model.buildEditPayload : model.buildPayload ?? ((ctx2) => ({ prompt: ctx2.prompt }))
9769
- };
9770
- }
9771
- function prepareRequest(model, params2) {
9772
- const ctx = { ...params2 };
9773
- const contract = getModelContract(model.id);
9774
- const validatedCtx = contract ? contract.input.parse(ctx) : ctx;
9775
- const resolved = resolvePayloadBuild(model, validatedCtx);
9776
- const payload = resolved.buildPayload(validatedCtx);
9777
- return { ctx, workflow: resolved.workflow, payload, contract };
9778
- }
9779
- function throwIfTerminalFailure(completed, model) {
9780
- if (completed.status === "FAILED") {
9781
- throw new ApiError(`${model.name} failed: ${completed.error ?? "unknown error"}`, {
9782
- status: completed.statusCode ?? 502,
9783
- code: completed.reason ?? "generation_failed"
9880
+ return io(conn.url, {
9881
+ path: conn.path ?? "/socket-gateway",
9882
+ transports: conn.transports ?? ["websocket"],
9883
+ auth: this.buildAuth(conn)
9784
9884
  });
9785
9885
  }
9786
- if (completed.status === "CANCELED") {
9787
- throw new ApiError(`${model.name} was canceled`, { status: 499, code: "canceled" });
9886
+ // The handshake auth payload, in socket.io's FUNCTION form — socket.io re-invokes it per (re)connect
9887
+ // attempt (Socket#onopen), which is the whole point: a plain object would be snapshotted once and
9888
+ // replayed on every reconnect, so it would go stale along with the token it captured.
9889
+ buildAuth(conn) {
9890
+ const { getToken } = conn;
9891
+ return (cb) => {
9892
+ void Promise.resolve().then(getToken).then((fresh) => cb({ token: bearer(fresh) })).catch((err) => {
9893
+ logger_default.error("workflows.socket - socketConnection.getToken failed; the handshake will be refused", err);
9894
+ cb({ token: "" });
9895
+ });
9896
+ };
9788
9897
  }
9789
- }
9790
- function parseResult(completed, model, contract) {
9791
- throwIfTerminalFailure(completed, model);
9792
- throwIfErrorResult(completed.result, model.name);
9793
- const parsed = contract?.output ? contract.output.parse(completed.result) : completed.result;
9794
- const multiItems = extractAllResults(parsed);
9795
- if (multiItems?.length) {
9796
- const results = multiItems.map((item) => ({
9797
- url: item.url,
9798
- metadata: item.exploreImageId ? { exploreImageId: item.exploreImageId } : void 0
9799
- }));
9800
- return { url: results[0].url, results, model: model.id, handle: completed.handle, raw: parsed, usage: completed.usage };
9898
+ // The gateway only accepts channels shaped `workflows:SEG:SEG` (SEG = letters, digits, _, - or /). We build
9899
+ // the channel from `name`/`taskId`, so validate it here to fail fast on a bad name instead of
9900
+ // silently never joining a room. Task names are slash-delimited paths (e.g. /v1/videos/text-to-video),
9901
+ // so `/` is allowed. (A channel that passes this but the gateway still refuses is left to hang — expected.)
9902
+ assertValidChannel(channel) {
9903
+ if (!/^workflows:[a-zA-Z0-9_/-]+:[a-zA-Z0-9_/-]+$/.test(channel)) {
9904
+ throw new WorkflowsError({
9905
+ httpStatusCode: 400,
9906
+ reason: "invalid_channel",
9907
+ message: `Invalid channel "${channel}" \u2014 name and taskId may only contain letters, digits, "_", "-" or "/".`
9908
+ });
9909
+ }
9801
9910
  }
9802
- const url = extractUrl(parsed);
9803
- if (!url) {
9804
- throw new ApiError(`${model.name}: unexpected response \u2014 no result URL`, {
9805
- status: 502,
9806
- code: "invalid_response"
9807
- });
9911
+ // Room membership. Every joiner emits its OWN `subscribe` (idempotent at the gateway); the ref-count
9912
+ // is used solely to emit `unsubscribe` ONCE, when the last watcher leaves, so one watcher's teardown
9913
+ // never drops a room a sibling still needs. Fire-and-forget: we validate the channel locally, so
9914
+ // there's no ack to act on.
9915
+ joinChannel(socket, channel) {
9916
+ let refs = this.channelRefs.get(socket);
9917
+ if (!refs) {
9918
+ refs = /* @__PURE__ */ new Map();
9919
+ this.channelRefs.set(socket, refs);
9920
+ }
9921
+ refs.set(channel, (refs.get(channel) ?? 0) + 1);
9922
+ socket.emit("subscribe", { channels: [channel] });
9923
+ }
9924
+ leaveChannel(socket, channel) {
9925
+ const refs = this.channelRefs.get(socket);
9926
+ if (!refs) return;
9927
+ const count = (refs.get(channel) ?? 0) - 1;
9928
+ if (count <= 0) {
9929
+ refs.delete(channel);
9930
+ socket.emit("unsubscribe", { channels: [channel] });
9931
+ } else {
9932
+ refs.set(channel, count);
9933
+ }
9808
9934
  }
9809
- return { url, results: [{ url }], model: model.id, handle: completed.handle, raw: parsed, usage: completed.usage };
9810
- }
9811
- function parseTextResult(completed, model) {
9812
- throwIfTerminalFailure(completed, model);
9813
- throwIfErrorResult(completed.result, model.name);
9814
- throwIfErrorResult(completed.raw, model.name);
9815
- const text = extractText(completed.result) ?? extractText(completed.raw);
9816
- if (text == null) {
9817
- throw new ApiError(`${model.name}: unexpected response \u2014 no text`, {
9818
- status: 502,
9819
- code: "invalid_response"
9935
+ // The Error a task failure maps to: the gateway's own reason/message/statusCode when it sent them,
9936
+ // otherwise a generic 500 failure.
9937
+ failureError(failure) {
9938
+ return WorkflowsError.fromBody(failure, {
9939
+ httpStatusCode: 500,
9940
+ reason: "workflow_failed",
9941
+ message: "Workflow failed"
9820
9942
  });
9821
9943
  }
9822
- return { text, model: model.id, handle: completed.handle, raw: completed.raw ?? completed.result, usage: completed.usage };
9823
- }
9824
-
9825
- // src/core/limits.ts
9826
- var MAX_DRIVE_PROMPT_LENGTH = 18e3;
9827
-
9828
- // src/client/drive.ts
9829
- var USER_REACTION_ATTR = "userReaction";
9830
- function inferResourceType(mode) {
9831
- if (mode === "video") return "VIDEO";
9832
- if (mode === "audio") return "AUDIO";
9833
- return "PHOTO";
9834
- }
9835
- function buildFilename(prompt, mode) {
9836
- const shortId = String(Date.now()).slice(-6);
9837
- const ext = mode === "video" ? "mp4" : mode === "audio" ? "mp3" : "png";
9838
- if (!prompt) return `ai-generation-${shortId}.${ext}`;
9839
- const slug = prompt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
9840
- return `${slug}-${shortId}.${ext}`;
9841
- }
9842
- function inferMediaType(file) {
9843
- const name = String(file.name || "");
9844
- if (/\.(mp3|wav|ogg|aac|flac|m4a)$/i.test(name)) return "audio";
9845
- if (/\.(mp4|webm|mov|avi|mkv|m4v|wmv)$/i.test(name)) return "video";
9846
- const contentType = file.contentType ?? file.content;
9847
- const resourceType = String(contentType?.resourceType || "").toUpperCase();
9848
- if (resourceType === "VIDEO") return "video";
9849
- if (resourceType === "AUDIO") return "audio";
9850
- return "image";
9851
- }
9852
- function contentResourceTypes(type) {
9853
- if (type === "image") return "PHOTO";
9854
- if (type === "video") return "VIDEO";
9855
- if (type === "audio") return "AUDIO";
9856
- return "PHOTO,VIDEO,AUDIO";
9857
- }
9858
- function normalizeUrl(raw) {
9859
- if (typeof raw !== "string") return void 0;
9860
- return raw.trim() || void 0;
9861
- }
9862
- function parseAttributes(raw) {
9863
- const map = {};
9864
- if (!raw || typeof raw !== "object") return map;
9865
- if (Array.isArray(raw)) {
9866
- for (const a of raw) {
9867
- map[a.property] = String(a.value);
9868
- }
9869
- } else {
9870
- for (const [k, v] of Object.entries(raw)) {
9871
- map[k] = String(v);
9944
+ };
9945
+ async function* decodeSSE(stream) {
9946
+ for await (const chunk of readSSE(stream)) {
9947
+ const lines = chunk.split("\n");
9948
+ const sseData = {};
9949
+ for (const line of lines) {
9950
+ if (line.startsWith("data:")) {
9951
+ const data = line.replace(/^data:\s*/, "");
9952
+ if (data === "[DONE]") {
9953
+ return;
9954
+ }
9955
+ try {
9956
+ sseData.data = JSON.parse(data);
9957
+ } catch (err) {
9958
+ logger_default.warn(
9959
+ `Failed to parse data JSON from OpenAI event stream: - ${data}, err=${JSON.stringify(err)}`
9960
+ );
9961
+ }
9962
+ }
9872
9963
  }
9964
+ yield sseData;
9873
9965
  }
9874
- return map;
9875
- }
9876
- function parseReaction(value) {
9877
- return value === "like" || value === "dislike" ? value : void 0;
9878
9966
  }
9879
- function parseJsonAttr(raw) {
9880
- if (!raw) return void 0;
9967
+ async function* readSSE(stream) {
9968
+ const reader = stream.getReader();
9969
+ let buffer = new Uint8Array();
9970
+ const decoder = new TextDecoder("utf-8");
9881
9971
  try {
9882
- const value = JSON.parse(raw);
9883
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
9884
- } catch {
9885
- return void 0;
9972
+ while (true) {
9973
+ const { value, done } = await reader.read();
9974
+ if (done) break;
9975
+ const tmp = new Uint8Array(buffer.length + value.length);
9976
+ tmp.set(buffer);
9977
+ tmp.set(value, buffer.length);
9978
+ buffer = tmp;
9979
+ let index;
9980
+ while ((index = findDoubleNewlineIndex(buffer)) !== -1) {
9981
+ const slice = buffer.subarray(0, index);
9982
+ yield decoder.decode(slice);
9983
+ buffer = buffer.subarray(index);
9984
+ }
9985
+ }
9986
+ if (buffer.length > 0) {
9987
+ yield decoder.decode(buffer);
9988
+ }
9989
+ } finally {
9990
+ reader.releaseLock();
9886
9991
  }
9887
9992
  }
9888
- var asString = (v) => typeof v === "string" && v.trim() ? v : void 0;
9889
- var asStringArray = (v) => Array.isArray(v) && v.length && v.every((x) => typeof x === "string") ? v : void 0;
9890
- function toSdkPayload(params2) {
9891
- const p2 = { prompt: String(params2.prompt ?? "").slice(0, MAX_DRIVE_PROMPT_LENGTH) };
9892
- for (const [key, value] of Object.entries(params2)) {
9893
- if (key === "prompt") continue;
9894
- if (value === void 0 || value === null || value === "") continue;
9895
- p2[key] = value;
9993
+ function findDoubleNewlineIndex(buffer) {
9994
+ const newline = 10;
9995
+ const carriage = 13;
9996
+ for (let i = 0; i < buffer.length - 1; i++) {
9997
+ if (buffer[i] === newline && buffer[i + 1] === newline) {
9998
+ return i + 2;
9999
+ }
10000
+ if (buffer[i] === carriage && buffer[i + 1] === carriage) {
10001
+ return i + 2;
10002
+ }
10003
+ if (buffer[i] === carriage && buffer[i + 1] === newline && i + 3 < buffer.length && buffer[i + 2] === carriage && buffer[i + 3] === newline) {
10004
+ return i + 4;
10005
+ }
9896
10006
  }
9897
- return p2;
10007
+ return -1;
9898
10008
  }
9899
- function buildGenerationAttributes(input) {
9900
- const attrs = {
9901
- model: input.modelId,
9902
- aiSDKPayload: JSON.stringify(toSdkPayload(input.params))
9903
- };
9904
- if (input.app) {
9905
- attrs.appId = input.app.id;
9906
- attrs.appType = input.app.type;
10009
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10010
+ var DEFAULT_POLLING_INTERVAL = 300;
10011
+ var DEFAULT_RETRIES_COUNT = 1e3;
10012
+ var NETWORK_RETRIES_COUNT = 10;
10013
+ var MAX_POLLING_BACKOFF = 5e3;
10014
+ var WorkflowsClient = class {
10015
+ constructor(options) {
10016
+ this.defaultHeaders = {
10017
+ Accept: "application/json",
10018
+ "Content-Type": "application/json"
10019
+ };
10020
+ this.terminalStatuses = [
10021
+ "COMPLETED",
10022
+ "FAILED"
10023
+ /* FAILED */
10024
+ ];
10025
+ this.clientOptions = options || {};
10026
+ this.clientOptions.baseUrl = this.clientOptions.baseUrl || "https://api.picsart.com/";
10027
+ if (!this.clientOptions.baseUrl.endsWith("/")) this.clientOptions.baseUrl += "/";
10028
+ if (this.clientOptions.apiKey) {
10029
+ this.clientOptions.apiKey = this.clientOptions.apiKey.replace("Bearer ", "");
10030
+ }
10031
+ if (this.clientOptions.identityToken) {
10032
+ this.clientOptions.identityToken = this.clientOptions.identityToken.replace("Bearer ", "");
10033
+ }
10034
+ this.workflowsApiBaseUrl = `${this.clientOptions.baseUrl}workflows`;
10035
+ this.sockets = new WorkflowsSocket({
10036
+ socket: this.clientOptions.socket,
10037
+ socketConnection: this.clientOptions.socketConnection && {
10038
+ ...this.clientOptions.socketConnection,
10039
+ url: this.clientOptions.socketConnection.url || this.clientOptions.baseUrl
10040
+ }
10041
+ });
10042
+ if (this.clientOptions.socket || this.clientOptions.socketConnection) {
10043
+ void Promise.resolve().then(() => this.sockets.connect()).catch(() => void 0);
10044
+ }
9907
10045
  }
9908
- return attrs;
9909
- }
9910
- function toMediaItem(file) {
9911
- const url = normalizeUrl(file.sourceUrl);
9912
- if (!url || String(file.name || "").startsWith("__")) return null;
9913
- const preview = file.preview;
9914
- return {
9915
- uid: String(file.uid ?? ""),
9916
- url,
9917
- name: String(file.name || ""),
9918
- type: inferMediaType(file),
9919
- previewUrl: normalizeUrl(preview?.url),
9920
- timestamp: Number(file.updatedAt ?? file.createdAt ?? 0)
9921
- };
9922
- }
9923
- function toDetailedItem(file) {
9924
- const base2 = toMediaItem(file);
9925
- if (!base2) return null;
9926
- const attrs = parseAttributes(file.attributes);
9927
- let extras = {};
9928
- if (attrs.textScript) {
10046
+ /**
10047
+ * Runs a workflow end-to-end and resolves with its result.
10048
+ *
10049
+ * A workflow that has an entry in `WorkflowTypes` (from `@picsart/workflows-types`) is
10050
+ * type-checked against it: `params` must match the workflow's input and the result comes back
10051
+ * typed, with no type argument to pass. Every other workflow is left unconstrained — declare
10052
+ * the result yourself with `run<MyResult>(name, params)`.
10053
+ *
10054
+ * The execution mode is taken from remote settings when available, otherwise from
10055
+ * `executionOptions.mode`, defaulting to async (submit + polling). Supported modes:
10056
+ * sync (single HTTP call), stream (SSE, requires `onEvent`), socket (result pushed
10057
+ * over the socket), and async (submit + polling).
10058
+ *
10059
+ * @typeParam R - Shape of the result, for a workflow that has no `WorkflowTypes` entry.
10060
+ * Passing it explicitly also opts a mapped workflow out of its types.
10061
+ * @param name - Workflow name.
10062
+ * @param params - Workflow input, typed per the workflow definition when there is one.
10063
+ * @param executionOptions - Mode, callbacks (`onAccepted`, `onProgress`, `onPartialResult`,
10064
+ * `onEvent`), polling tuning, headers, and abort signal.
10065
+ * @returns The workflow result and usage info.
10066
+ * @throws {WorkflowsError} On a failed request (`httpStatusCode` carries the HTTP status),
10067
+ * invalid arguments, or an unexpected failure.
10068
+ */
10069
+ async run(name, params2, executionOptions) {
9929
10070
  try {
9930
- extras = JSON.parse(attrs.textScript);
9931
- } catch {
10071
+ const remoteSettings = await this.getApiSettings(
10072
+ name,
10073
+ executionOptions?.remoteSettingName
10074
+ );
10075
+ const executionMode = remoteSettings.executionMode || executionOptions?.mode || "ASYNC";
10076
+ if (executionMode === "SYNC") {
10077
+ return this.executeTaskSync(name, params2, executionOptions);
10078
+ }
10079
+ if (executionMode === "STREAM") {
10080
+ return this.executeTaskStream(name, params2, executionOptions);
10081
+ }
10082
+ if (executionMode === "SOCKET") {
10083
+ const submit = async () => {
10084
+ try {
10085
+ const id = await this.postTask(name, params2, executionOptions);
10086
+ await executionOptions?.onAccepted?.(id);
10087
+ return id;
10088
+ } catch (err) {
10089
+ throw this.wrapError(name, err);
10090
+ }
10091
+ };
10092
+ return this.sockets.runTask(
10093
+ name,
10094
+ submit,
10095
+ executionOptions,
10096
+ (taskId2) => this.disableNotification(taskId2, { headers: executionOptions?.headers })
10097
+ );
10098
+ }
10099
+ const taskId = await this.postTask(name, params2, executionOptions);
10100
+ await executionOptions?.onAccepted?.(taskId);
10101
+ return this.runPolling(name, taskId, executionOptions);
10102
+ } catch (err) {
10103
+ throw this.wrapError(name, err);
9932
10104
  }
9933
10105
  }
9934
- return {
9935
- ...base2,
9936
- createdAt: file.createdAt,
9937
- model: attrs.model,
9938
- prompt: attrs.prompt || void 0,
9939
- service: attrs.service,
9940
- subType: attrs.subType,
9941
- duration: attrs.duration,
9942
- userReaction: parseReaction(attrs[USER_REACTION_ATTR]),
9943
- referenceImageUrls: extras.referenceImageUrls,
9944
- referenceVideoUrl: extras.referenceVideoUrl,
9945
- referenceAudioUrl: extras.referenceAudioUrl,
9946
- aspectRatio: extras.aspectRatio,
9947
- resolution: extras.resolution,
9948
- quality: extras.quality
9949
- };
9950
- }
9951
- var LEGACY_TOOL_APP = {
9952
- "ai-playground": { appId: "com.picsart.ai-playground", appType: "miniapp" }
9953
- };
9954
- function adaptLegacyGeneration(attrs) {
9955
- let extras = {};
9956
- if (attrs.textScript) {
10106
+ /**
10107
+ * Submits a task WITHOUT waiting for its result — the standalone counterpart of {@link run}.
10108
+ * Consume the result later with {@link runPolling} or {@link subscribe} (`{ name, taskId }`).
10109
+ *
10110
+ * Only the submission-related execution options apply here (`headers`, `notificationConfig`,
10111
+ * `remoteSettingName`); result-consumption options (mode, callbacks, polling) belong to the consumer.
10112
+ *
10113
+ * @param name - Workflow name.
10114
+ * @param params - Workflow input parameters.
10115
+ * @param executionOptions - Submission-related options only.
10116
+ * @returns The taskId of the submitted task.
10117
+ * @throws {WorkflowsError} On a failed request (`httpStatusCode` carries the HTTP status)
10118
+ * or an unexpected failure.
10119
+ */
10120
+ async submit(name, params2, executionOptions) {
9957
10121
  try {
9958
- extras = JSON.parse(attrs.textScript);
9959
- } catch {
10122
+ return await this.postTask(name, params2, executionOptions);
10123
+ } catch (err) {
10124
+ throw this.wrapError(name, err);
9960
10125
  }
9961
10126
  }
9962
- const aiSDKPayload = { prompt: attrs.prompt || "" };
9963
- const aspectRatio = asString(extras.aspectRatio);
9964
- if (aspectRatio) aiSDKPayload.aspectRatio = aspectRatio;
9965
- const resolution = asString(extras.resolution);
9966
- if (resolution) aiSDKPayload.resolution = resolution;
9967
- const duration = extras.duration ?? attrs.duration;
9968
- if (duration != null && duration !== "") aiSDKPayload.duration = Number(duration);
9969
- const imageUrls = asStringArray(extras.referenceImageUrls);
9970
- if (imageUrls) aiSDKPayload.imageUrls = imageUrls;
9971
- const videoUrl = asString(extras.referenceVideoUrl);
9972
- if (videoUrl) aiSDKPayload.videoUrl = videoUrl;
9973
- const audioUrl = asString(extras.referenceAudioUrl);
9974
- if (audioUrl) aiSDKPayload.audioUrl = audioUrl;
9975
- const startFrame = asString(extras.startFrame);
9976
- if (startFrame) aiSDKPayload.startFrame = startFrame;
9977
- const endFrame = asString(extras.endFrame);
9978
- if (endFrame) aiSDKPayload.endFrame = endFrame;
9979
- const quality = asString(extras.quality);
9980
- if (quality) aiSDKPayload.quality = quality;
9981
- const style = asString(extras.style);
9982
- if (style) aiSDKPayload.style = style;
9983
- const iterateModel = asString(extras.iterateModel);
9984
- if (iterateModel) aiSDKPayload.iterateModel = iterateModel;
9985
- const exploreImageId = asString(extras.exploreImageId);
9986
- if (exploreImageId) aiSDKPayload.exploreImageId = exploreImageId;
9987
- const app = attrs.tool ? LEGACY_TOOL_APP[attrs.tool] : void 0;
9988
- return {
9989
- appId: app?.appId,
9990
- appType: app?.appType,
9991
- model: attrs.model || void 0,
9992
- aiSDKPayload,
9993
- userReaction: parseReaction(attrs[USER_REACTION_ATTR])
9994
- };
9995
- }
9996
- function parseGeneration(file) {
9997
- const attrs = parseAttributes(file.attributes);
9998
- if (!attrs.aiSDKPayload) {
9999
- return adaptLegacyGeneration(attrs);
10000
- }
10001
- return {
10002
- appId: attrs.appId || void 0,
10003
- appType: attrs.appType === "native" || attrs.appType === "miniapp" ? attrs.appType : void 0,
10004
- model: attrs.model || void 0,
10005
- aiSDKPayload: parseJsonAttr(attrs.aiSDKPayload),
10006
- userReaction: parseReaction(attrs[USER_REACTION_ATTR])
10007
- };
10008
- }
10009
- function createDriveClient(f, apiUrl, rootFolderName) {
10010
- let cachedRootUid = null;
10011
- let rootPromise = null;
10012
- const jsonPost = async (path, body) => f(`${apiUrl}${path}`, {
10013
- method: "POST",
10014
- headers: { "Content-Type": "application/json" },
10015
- body: JSON.stringify(body)
10016
- });
10017
- const jsonGet = async (path) => f(`${apiUrl}${path}`);
10018
- async function findFolderByPath(name) {
10127
+ /**
10128
+ * Fetches the options a workflow offers for the given input — what the adapter resolves for THIS
10129
+ * caller (subscription tier, country, the `x-config-id` CMS card), which is why it is read at call
10130
+ * time rather than described by the workflow's types.
10131
+ *
10132
+ * @param name - Workflow name, including the version when the workflow has one (`pipelineName/v1`).
10133
+ * @param params - Workflow input to resolve the options for; defaults to `{}` for the common case
10134
+ * of asking before anything is chosen.
10135
+ * @param requestOptions - `remoteSettingName` to resolve the `x-config-id` under a name other
10136
+ * than the workflow's own.
10137
+ * @returns The options payload — the envelope's `response`, unwrapped.
10138
+ * @throws {WorkflowsError} On a failed request; `httpStatusCode` carries the HTTP status.
10139
+ */
10140
+ async options(name, params2 = {}, requestOptions) {
10019
10141
  try {
10020
- const res = await jsonGet(`/cloud-storage/v1/me/files-by-path?path=${encodeURIComponent(name)}`);
10021
- if (!res.ok) return null;
10022
- const data = await res.json();
10023
- if (data.status !== "success") return null;
10024
- const response = data.response;
10025
- const file = Array.isArray(response) ? response[0] : response;
10026
- return file?.uid ?? null;
10027
- } catch {
10028
- return null;
10142
+ const remoteSettings = await this.getApiSettings(name, requestOptions?.remoteSettingName);
10143
+ const response = await this._fetch(`${this.workflowsApiBaseUrl}/${name}/options`, {
10144
+ method: "POST",
10145
+ headers: this.requestHeaders(remoteSettings.configId),
10146
+ body: JSON.stringify({ params: params2 })
10147
+ });
10148
+ const json = await this.toSuccessResponse(response);
10149
+ return json.response;
10150
+ } catch (err) {
10151
+ throw this.wrapError(name, err);
10029
10152
  }
10030
10153
  }
10031
- async function findFolderInList(name, parentUid) {
10032
- try {
10033
- const params2 = parentUid ? `parentFolderUid=${parentUid}&fileTypes=FOLDER&limit=100` : `fileTypes=FOLDER&limit=100`;
10034
- const res = await jsonGet(`/cloud-storage/v1/me/files?${params2}`);
10035
- if (!res.ok) return null;
10036
- const data = await res.json();
10037
- const response = data.response;
10038
- const files = Array.isArray(response) ? response : [];
10039
- const match = files.find((f2) => String(f2.name || "").toLowerCase() === name.toLowerCase());
10040
- return match?.uid ?? null;
10041
- } catch {
10042
- return null;
10154
+ async postTask(taskName, command, executionOptions) {
10155
+ const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10156
+ const response = await this._fetch(`${this.workflowsApiBaseUrl}/${taskName}/submit`, {
10157
+ method: "POST",
10158
+ headers: this.requestHeaders(remoteSettings.configId, executionOptions?.headers),
10159
+ body: JSON.stringify({
10160
+ params: command,
10161
+ notification: executionOptions?.notificationConfig
10162
+ })
10163
+ });
10164
+ const json = await this.toSuccessResponse(response);
10165
+ return json.response.id;
10166
+ }
10167
+ /**
10168
+ * Polls an already-submitted task until it reaches a terminal status (COMPLETED/FAILED)
10169
+ * and resolves with its result. Progress and partial-result callbacks from
10170
+ * `executionOptions` are invoked on each update.
10171
+ *
10172
+ * A poll that never reaches the server (dropped wifi, DNS failure, a reset connection) does not
10173
+ * end the run — the task keeps going server-side, so polling backs off and retries, giving up
10174
+ * only once the drops outlast the retry budget. Anything the server did answer, and any abort,
10175
+ * still fails immediately.
10176
+ *
10177
+ * @typeParam R - Shape of the workflow result.
10178
+ * @param taskName - Workflow name.
10179
+ * @param taskId - Task id returned by {@link submit} (or `onAccepted`).
10180
+ * @param executionOptions - `pollingInterval` (default 300ms), `retriesCount` (default 1000),
10181
+ * callbacks and abort signal.
10182
+ * @returns The workflow result and usage info.
10183
+ * @throws {WorkflowsError} With `httpStatusCode` 408 when the retry budget is exhausted
10184
+ * before the task completes, or the connection error when the connection never came back.
10185
+ */
10186
+ async runPolling(taskName, taskId, executionOptions) {
10187
+ const pollingInterval = executionOptions?.pollingInterval || DEFAULT_POLLING_INTERVAL;
10188
+ let retriesCounter = executionOptions?.retriesCount || DEFAULT_RETRIES_COUNT;
10189
+ let pollingResponse;
10190
+ let networkFailures = 0;
10191
+ let lastNetworkError;
10192
+ const progressAware = new WorkflowResultUpdateAware(
10193
+ executionOptions?.onProgress,
10194
+ executionOptions?.onPartialResult,
10195
+ executionOptions?.onEvent
10196
+ );
10197
+ do {
10198
+ if (executionOptions?.abortSignal?.aborted) throw new DOMException("Aborted", "AbortError");
10199
+ await sleep(this.pollingDelay(pollingInterval, networkFailures));
10200
+ retriesCounter--;
10201
+ try {
10202
+ pollingResponse = await this.getResult(taskName, taskId);
10203
+ networkFailures = 0;
10204
+ lastNetworkError = void 0;
10205
+ } catch (err) {
10206
+ if (!this.isConnectionError(err)) throw err;
10207
+ if (++networkFailures > NETWORK_RETRIES_COUNT) throw this.connectionError(err);
10208
+ lastNetworkError = err;
10209
+ logger_default.warn(
10210
+ `workflows.runPolling - poll ${networkFailures}/${NETWORK_RETRIES_COUNT} of ${taskName}/${taskId} did not reach the server, retrying`,
10211
+ err
10212
+ );
10213
+ continue;
10214
+ }
10215
+ await progressAware.onUpdate(pollingResponse.response);
10216
+ } while (retriesCounter > 0 && !this.isTerminal(pollingResponse));
10217
+ if (lastNetworkError) throw this.connectionError(lastNetworkError);
10218
+ if (!this.isTerminal(pollingResponse) || !pollingResponse?.response.result) {
10219
+ throw new WorkflowsError({
10220
+ httpStatusCode: 408,
10221
+ reason: "client_timeout",
10222
+ message: "Polling timeout reached. Consider increasing polling interval or retries count from execution options."
10223
+ });
10043
10224
  }
10225
+ return pollingResponse.response;
10044
10226
  }
10045
- async function createFolder(name, parentUid) {
10227
+ // Raw transport rejections (a TypeError from fetch) become the client's own error on the way out,
10228
+ // keeping what fetch said but labelling it for callers. No httpStatusCode: the server never answered.
10229
+ connectionError(error) {
10230
+ return new WorkflowsError({
10231
+ reason: "connection_error",
10232
+ message: error?.message || "The request did not reach the server"
10233
+ });
10234
+ }
10235
+ isTerminal(response) {
10236
+ return !!response && this.terminalStatuses.includes(response.response.status);
10237
+ }
10238
+ /**
10239
+ * Whether a failed poll never got an answer from the server — the connection dropped, DNS failed,
10240
+ * the request was reset. Classified by what the failure is NOT, so it holds in a browser
10241
+ * (`TypeError: Failed to fetch`) and in Node (`TypeError: fetch failed`) alike: anything the
10242
+ * server answered carries an `httpStatusCode`, and an abort is the caller's own doing.
10243
+ */
10244
+ isConnectionError(error) {
10245
+ const name = error?.name;
10246
+ if (name === "AbortError" || name === "TimeoutError") return false;
10247
+ if (error instanceof WorkflowsError) return error.httpStatusCode === void 0;
10248
+ return true;
10249
+ }
10250
+ // Back off while the connection is down instead of hammering a dead radio — never below the
10251
+ // caller's own interval, never above MAX_POLLING_BACKOFF.
10252
+ pollingDelay(interval, consecutiveFailures) {
10253
+ if (consecutiveFailures === 0) return interval;
10254
+ return Math.min(interval * 2 ** consecutiveFailures, Math.max(interval, MAX_POLLING_BACKOFF));
10255
+ }
10256
+ /**
10257
+ * Fetches the CURRENT state of an already-submitted task with a single request — no polling, no
10258
+ * waiting. Returns the task as it stands, so read `status` (and `progress`) to know what you got:
10259
+ * `result` may still be empty or partial while the task is not COMPLETED. Use {@link runPolling}
10260
+ * or {@link subscribe} to wait for a terminal status instead.
10261
+ *
10262
+ * @typeParam R - Shape of the workflow result.
10263
+ * @param taskName - Workflow name.
10264
+ * @param taskId - Task id returned by {@link submit} (or `onAccepted`).
10265
+ * @returns The task record as it stands at the moment of the call.
10266
+ * @throws {WorkflowsError} On a failed request (`httpStatusCode` carries the HTTP status)
10267
+ * or an unexpected failure.
10268
+ */
10269
+ async result(taskName, taskId) {
10046
10270
  try {
10047
- const body = { name };
10048
- if (parentUid) body.parentFolderUid = parentUid;
10049
- const res = await jsonPost("/cloud-storage/v1/me/folders", body);
10050
- if (!res.ok) return null;
10051
- const data = await res.json();
10052
- const response = data.response;
10053
- return response?.uid ?? null;
10054
- } catch {
10055
- return null;
10271
+ const response = await this.getResult(taskName, taskId);
10272
+ return response.response;
10273
+ } catch (err) {
10274
+ throw this.wrapError(taskName, err);
10056
10275
  }
10057
10276
  }
10058
- async function resolveRootFolder() {
10059
- const byPath = await findFolderByPath(rootFolderName);
10060
- if (byPath) return byPath;
10061
- const inList = await findFolderInList(rootFolderName);
10062
- if (inList) return inList;
10063
- const recheck = await findFolderByPath(rootFolderName);
10064
- if (recheck) return recheck;
10065
- return createFolder(rootFolderName);
10066
- }
10067
- async function ensureRootFolder() {
10068
- if (cachedRootUid) return cachedRootUid;
10069
- if (!rootPromise) {
10070
- rootPromise = resolveRootFolder().then((uid) => {
10071
- cachedRootUid = uid;
10072
- rootPromise = null;
10073
- return uid;
10074
- }).catch((err) => {
10075
- setTimeout(() => {
10076
- rootPromise = null;
10077
- }, 1e4);
10078
- throw err;
10277
+ /**
10278
+ * Watches already-submitted work LIVE over the socket (no re-submit), as an async iterable
10279
+ * of the raw `StreamSocketMessage` the gateway pushes — iterate with `for await` and switch
10280
+ * on `msg.type`. With a `taskId` it watches that task (ending after its COMPLETED/FAILED);
10281
+ * without it, it watches EVERY task of the workflow until stopped. A lost session throws out
10282
+ * of the loop; `break` (or an AbortSignal in options) stops watching.
10283
+ *
10284
+ * @param options - Subscription target: `name` (required), optional `taskId` and abort signal.
10285
+ * @returns Async iterable of socket messages for the subscribed workflow/task.
10286
+ * @throws {WorkflowsError} If `options.name` is missing.
10287
+ */
10288
+ subscribe(options) {
10289
+ if (!options.name) {
10290
+ throw new WorkflowsError({
10291
+ httpStatusCode: 400,
10292
+ reason: "INVALID_ARGUMENTS",
10293
+ message: "subscribe() requires `name`."
10079
10294
  });
10080
10295
  }
10081
- return rootPromise;
10296
+ return this.sockets.subscribe(options);
10082
10297
  }
10083
- async function fetchFolders(parentUid) {
10298
+ /**
10299
+ * Closes the socket the client created from `socketConnection`. No-op for an injected
10300
+ * `socket` (the caller owns that one). Safe to call more than once.
10301
+ */
10302
+ async disconnect() {
10303
+ return this.sockets.disconnect();
10304
+ }
10305
+ /**
10306
+ * Marks a task's notification as seen so it is no longer surfaced to the user.
10307
+ * Called automatically after socket-mode runs; call it manually when consuming
10308
+ * results yourself (e.g. after {@link submit} + {@link subscribe}).
10309
+ *
10310
+ * @param taskId - Task id whose notification should be dismissed.
10311
+ * @param options - Optional extra request headers.
10312
+ * @throws {WorkflowsError} On a failed request; `httpStatusCode` carries the HTTP status.
10313
+ */
10314
+ async disableNotification(taskId, options) {
10084
10315
  try {
10085
- const params2 = parentUid ? `parentFolderUid=${parentUid}&fileTypes=FOLDER&limit=100` : `fileTypes=FOLDER&limit=100`;
10086
- const res = await jsonGet(`/cloud-storage/v1/me/files?${params2}`);
10087
- if (!res.ok) return [];
10088
- const data = await res.json();
10089
- const files = Array.isArray(data.response) ? data.response : [];
10090
- return files.filter((f2) => f2.uid && f2.name).map((f2) => ({ name: String(f2.name), uid: String(f2.uid) }));
10091
- } catch {
10092
- return [];
10316
+ const url = `${this.clientOptions.baseUrl}workflow-notifications/${taskId}/seen-status`;
10317
+ const response = await this._fetch(url, { method: "PATCH", headers: options?.headers });
10318
+ await this.throwIfError(response);
10319
+ } catch (err) {
10320
+ throw this.wrapError("disableNotification", err);
10093
10321
  }
10094
10322
  }
10095
- async function fetchMedia(opts) {
10096
- try {
10097
- const endpoint = opts.folderUid ? "/cloud-storage/v1/me/files" : "/cloud-storage/v1/me/flattened-files";
10098
- const params2 = [
10099
- opts.folderUid ? `parentFolderUid=${opts.folderUid}` : "",
10100
- "limit=100",
10101
- "sortType=UPDATED",
10102
- "sortOrder=DESC",
10103
- "fileTypes=FILE",
10104
- `contentResourceTypes=${contentResourceTypes(opts.type)}`
10105
- ].filter(Boolean).join("&");
10106
- const res = await jsonGet(`${endpoint}?${params2}`);
10107
- if (!res.ok) return [];
10108
- const data = await res.json();
10109
- return Array.isArray(data.response) ? data.response : [];
10110
- } catch {
10111
- return [];
10323
+ async executeTaskSync(taskName, command, executionOptions) {
10324
+ const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10325
+ const response = await this._fetch(
10326
+ `${this.workflowsApiBaseUrl}/${taskName}/execute`,
10327
+ {
10328
+ signal: executionOptions?.abortSignal,
10329
+ method: "POST",
10330
+ headers: this.requestHeaders(remoteSettings.configId, executionOptions?.headers),
10331
+ body: JSON.stringify({ params: command })
10332
+ }
10333
+ );
10334
+ const successResponse = await this.toSuccessResponse(response);
10335
+ return successResponse.response;
10336
+ }
10337
+ async getResult(taskName, taskId) {
10338
+ const response = await this._fetch(`${this.workflowsApiBaseUrl}/${taskName}/${taskId}/result`, {
10339
+ method: "GET"
10340
+ });
10341
+ return this.toSuccessResponse(response);
10342
+ }
10343
+ async executeTaskStream(taskName, command, executionOptions) {
10344
+ const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10345
+ const onEvent = executionOptions?.onEvent;
10346
+ if (!onEvent) {
10347
+ throw new WorkflowsError({
10348
+ httpStatusCode: 400,
10349
+ reason: "INVALID_ARGUMENTS",
10350
+ message: "onEvent is required for streaming"
10351
+ });
10352
+ }
10353
+ const streamHeaders = this.requestHeaders(remoteSettings.configId, executionOptions?.headers);
10354
+ streamHeaders.set("Accept", "text/event-stream");
10355
+ const response = await this._fetch(`${this.workflowsApiBaseUrl}/${taskName}/stream`, {
10356
+ signal: executionOptions?.abortSignal,
10357
+ method: "POST",
10358
+ headers: streamHeaders,
10359
+ body: JSON.stringify({ params: command })
10360
+ });
10361
+ await this.throwIfError(response);
10362
+ if (!response.body) {
10363
+ throw new WorkflowsError({
10364
+ httpStatusCode: 500,
10365
+ reason: "invalid_response",
10366
+ message: "No response body"
10367
+ });
10368
+ }
10369
+ let completedEvent = {};
10370
+ for await (const event of decodeSSE(response.body)) {
10371
+ if (executionOptions?.abortSignal?.aborted) break;
10372
+ const data = event.data;
10373
+ if (data.type.startsWith("event.")) {
10374
+ await onEvent({
10375
+ ...data,
10376
+ type: data.type.replace(/^event\.\s*/, "")
10377
+ });
10378
+ }
10379
+ if (data.type === "task.partial-result") {
10380
+ await executionOptions.onPartialResult?.({
10381
+ status: "IN_PROGRESS",
10382
+ result: data.result
10383
+ });
10384
+ }
10385
+ if (data.type === "task.failed") {
10386
+ throw WorkflowsError.fromBody(data.result, {
10387
+ httpStatusCode: 500,
10388
+ reason: "workflow_failed",
10389
+ message: "Workflow failed"
10390
+ });
10391
+ }
10392
+ if (data.type === "task.completed") {
10393
+ completedEvent = data;
10394
+ }
10112
10395
  }
10396
+ return {
10397
+ result: completedEvent.result,
10398
+ usage: completedEvent.usage,
10399
+ status: "COMPLETED"
10400
+ /* COMPLETED */
10401
+ // the loop only leaves the FAILED branch by throwing
10402
+ };
10113
10403
  }
10114
- async function fetchFileByUid(fileUid) {
10404
+ /**
10405
+ * Fetches the execution history of a workflow, paginated.
10406
+ *
10407
+ * @typeParam R - Shape of each execution's result in the history entries.
10408
+ * @param taskName - Workflow name to fetch history for.
10409
+ * @param offset - Pagination offset (default 0).
10410
+ * @param limit - Page size (default 10).
10411
+ * @param isGrouped - When true, fetches the grouped history endpoint.
10412
+ * @returns The history page for the workflow.
10413
+ * @throws {WorkflowsError} On a failed request (`httpStatusCode` carries the HTTP status)
10414
+ * or an unexpected failure.
10415
+ */
10416
+ async executionsHistory(taskName, offset = 0, limit = 10, isGrouped = false) {
10115
10417
  try {
10116
- const res = await jsonGet(`/drive/v1/files/${fileUid}`);
10117
- if (!res.ok) return null;
10118
- const data = await res.json();
10119
- const file = data.response;
10120
- return file && typeof file === "object" && !Array.isArray(file) ? file : null;
10418
+ const grouped = isGrouped ? "/grouped" : "";
10419
+ const url = `${this.clientOptions.baseUrl}workflows-history${grouped}?name=${taskName}&limit=${limit}&offset=${offset}`;
10420
+ const res = await this._fetch(url);
10421
+ return this.toSuccessResponse(res);
10422
+ } catch (err) {
10423
+ throw this.wrapError("requestHistory", err);
10424
+ }
10425
+ }
10426
+ async toSuccessResponse(response) {
10427
+ await this.throwIfError(response);
10428
+ const body = await response.text();
10429
+ try {
10430
+ return JSON.parse(body);
10121
10431
  } catch {
10122
- return null;
10432
+ throw new WorkflowsError({
10433
+ httpStatusCode: response.status,
10434
+ reason: "invalid_response",
10435
+ message: "Non json response was returned from server"
10436
+ });
10123
10437
  }
10124
10438
  }
10125
- async function setReaction(fileUid, reaction) {
10439
+ async throwIfError(response) {
10440
+ if (response.ok) return;
10441
+ throw await WorkflowsError.fromResponse(response, { reason: "request_failed" });
10442
+ }
10443
+ async getApiSettings(name, remoteSettingName) {
10444
+ if (!this.clientOptions.getRemoteSettings) return {};
10445
+ const settingName = remoteSettingName || `${name.replace(/-/g, "_").toLowerCase()}_api`;
10126
10446
  try {
10127
- const res = await f(`${apiUrl}/drive/v1/files/${fileUid}`, {
10128
- method: "PATCH",
10129
- headers: { "Content-Type": "application/json" },
10130
- body: JSON.stringify({ attributes: { [USER_REACTION_ATTR]: reaction } })
10447
+ const apiSetting = await this.clientOptions.getRemoteSettings(
10448
+ settingName,
10449
+ "miniapp"
10450
+ );
10451
+ return {
10452
+ configId: apiSetting?.configId || "",
10453
+ executionMode: apiSetting?.executionMode
10454
+ };
10455
+ } catch (err) {
10456
+ logger_default.error(
10457
+ `workflows.getConfigId - failed when fetching remoteSettings: settingName=${settingName}`,
10458
+ err
10459
+ );
10460
+ return {};
10461
+ }
10462
+ }
10463
+ wrapError(actionName, error) {
10464
+ if (error instanceof WorkflowsError || error instanceof DOMException) {
10465
+ return error;
10466
+ }
10467
+ logger_default.error(`WorkflowsError - ${actionName} failed`, error);
10468
+ return WorkflowsError.fromUnknown(error);
10469
+ }
10470
+ // Per-call headers plus the resolved config id, with the call's own value winning — again via
10471
+ // Headers, so every HeadersInit shape survives.
10472
+ requestHeaders(configId, headers) {
10473
+ const merged = new Headers(headers);
10474
+ if (!merged.has("x-config-id")) merged.set("x-config-id", configId || "");
10475
+ return merged;
10476
+ }
10477
+ buildRequestHeaders(initHeaders) {
10478
+ const headers = new Headers(initHeaders);
10479
+ const optionHeaders = new Headers(this.defaultHeaders);
10480
+ for (const [key, value] of new Headers(this.clientOptions.headers).entries()) {
10481
+ optionHeaders.set(key, value);
10482
+ }
10483
+ for (const [key, value] of optionHeaders.entries()) {
10484
+ if (!headers.has(key)) {
10485
+ headers.set(key, value);
10486
+ }
10487
+ }
10488
+ if (this.clientOptions.apiKey) {
10489
+ headers.set("Authorization", `Bearer ${this.clientOptions.apiKey}`);
10490
+ }
10491
+ if (this.clientOptions.identityToken) {
10492
+ headers.set("x-app-authorization", `Bearer ${this.clientOptions.identityToken}`);
10493
+ }
10494
+ return headers;
10495
+ }
10496
+ async _fetch(input, init) {
10497
+ const headers = this.buildRequestHeaders(init?.headers);
10498
+ const requestInit = {
10499
+ ...init,
10500
+ headers
10501
+ };
10502
+ if (this.clientOptions.fetch) {
10503
+ return this.clientOptions.fetch(input, {
10504
+ ...requestInit,
10505
+ // return headers as a plain object for easier handling in custom fetch
10506
+ headers: Object.fromEntries(headers.entries())
10507
+ });
10508
+ }
10509
+ if (!headers.has("Authorization") && !headers.has("x-app-authorization")) {
10510
+ throw new WorkflowsError({
10511
+ httpStatusCode: 400,
10512
+ reason: "invalid_state",
10513
+ message: "apiKey is not provided"
10514
+ });
10515
+ }
10516
+ return fetch(input, requestInit);
10517
+ }
10518
+ };
10519
+ var WorkflowsClient_default = WorkflowsClient;
10520
+
10521
+ // src/client/workflows-error.ts
10522
+ var GENERIC_REASONS = /* @__PURE__ */ new Set(["request_failed", "unknown_error"]);
10523
+ function isWorkflowsError(err) {
10524
+ if (err instanceof WorkflowsError) return true;
10525
+ const e = err;
10526
+ return err instanceof Error && typeof e.reason === "string" && /WorkflowsError$/.test(e.name ?? "");
10527
+ }
10528
+ function toApiError(err, workflow, id) {
10529
+ if (err instanceof ApiError) return err;
10530
+ if (err instanceof DOMException && err.name === "AbortError") return err;
10531
+ const e = err;
10532
+ if (isWorkflowsError(err)) {
10533
+ if (e.reason === "client_timeout") {
10534
+ return new ApiError(`Timed out waiting for workflow ${workflow}${id ? `:${id}` : ""}`, {
10535
+ status: 408,
10536
+ code: "timeout"
10131
10537
  });
10132
- return res.ok;
10133
- } catch {
10134
- return false;
10135
10538
  }
10539
+ const status = e.httpStatusCode ?? 502;
10540
+ return new ApiError(e.message ?? "Request failed", {
10541
+ status,
10542
+ code: e.reason && !GENERIC_REASONS.has(e.reason) ? e.reason : codeForStatus(status)
10543
+ });
10544
+ }
10545
+ return new ApiError(err instanceof Error ? err.message : String(err), {
10546
+ status: 502,
10547
+ code: "generation_failed"
10548
+ });
10549
+ }
10550
+
10551
+ // src/client/transport.ts
10552
+ var GATEWAY_HEADERS = {
10553
+ "platform": "api",
10554
+ "X-Touchpoint": "sdk"
10555
+ };
10556
+ function maybeFetch(config) {
10557
+ if (config.fetch) return config.fetch;
10558
+ if (config.apiKey) {
10559
+ const token = config.apiKey.replace(/^Bearer\s+/i, "");
10560
+ return (url, init) => {
10561
+ const headers = new Headers(init?.headers);
10562
+ headers.set("Authorization", `Bearer ${token}`);
10563
+ for (const [name, value] of Object.entries(GATEWAY_HEADERS)) {
10564
+ if (!headers.has(name)) headers.set(name, value);
10565
+ }
10566
+ return globalThis.fetch(url, { ...init, headers });
10567
+ };
10136
10568
  }
10569
+ return null;
10570
+ }
10571
+ function createWorkflowsClient(apiUrl, authedFetch) {
10572
+ return new WorkflowsClient_default({
10573
+ baseUrl: apiUrl,
10574
+ // AuthenticatedFetch takes a string url; the client's fetch type accepts
10575
+ // URL/Request inputs too. Normalize without losing the Request's own url,
10576
+ // method, headers, or body (the client passes plain string urls today,
10577
+ // but the contract allows more).
10578
+ fetch: (input, init) => {
10579
+ if (input instanceof Request) {
10580
+ return authedFetch(input.url, init ?? {
10581
+ method: input.method,
10582
+ headers: input.headers,
10583
+ body: input.body,
10584
+ signal: input.signal
10585
+ });
10586
+ }
10587
+ return authedFetch(typeof input === "string" ? input : input.toString(), init);
10588
+ }
10589
+ });
10590
+ }
10591
+ function buildTransport(wc) {
10592
+ const asResult = (res) => ({
10593
+ result: res.result,
10594
+ usage: res.usage,
10595
+ raw: res.result
10596
+ });
10137
10597
  return {
10138
- /**
10139
- * Ensure a subfolder exists inside the root folder.
10140
- * Creates both root and subfolder if needed. Returns the folder reference.
10141
- * Call with no argument to just ensure the root folder exists.
10142
- */
10143
- async ensureFolder(subfolder) {
10144
- const rootUid = await ensureRootFolder();
10145
- if (!rootUid) return null;
10146
- if (!subfolder) {
10147
- return { name: rootFolderName, uid: rootUid };
10598
+ async execute(request) {
10599
+ try {
10600
+ const res = await wc.run(request.workflow, request.payload, {
10601
+ mode: ExecutionMode.SYNC,
10602
+ abortSignal: request.signal
10603
+ });
10604
+ return asResult(res);
10605
+ } catch (err) {
10606
+ throw toApiError(err, request.workflow);
10148
10607
  }
10149
- const existingUid = await findFolderInList(subfolder, rootUid);
10150
- if (existingUid) return { name: subfolder, uid: existingUid };
10151
- const newUid = await createFolder(subfolder, rootUid);
10152
- if (!newUid) return null;
10153
- return { name: subfolder, uid: newUid };
10154
- },
10155
- /** List subfolders inside the root folder (boards). */
10156
- async folders() {
10157
- const rootUid = await ensureRootFolder();
10158
- if (!rootUid) return [];
10159
- return fetchFolders(rootUid);
10160
10608
  },
10161
- /** List top-level Drive folders + root subfolders, deduplicated. */
10162
- async allFolders() {
10163
- const rootUid = await ensureRootFolder();
10164
- const [rootLevel, subfolders] = await Promise.all([
10165
- fetchFolders(),
10166
- rootUid ? fetchFolders(rootUid) : Promise.resolve([])
10167
- ]);
10168
- const seen = /* @__PURE__ */ new Set();
10169
- const merged = [];
10170
- for (const folder of [...rootLevel, ...subfolders]) {
10171
- if (seen.has(folder.uid)) continue;
10172
- seen.add(folder.uid);
10173
- merged.push(folder);
10609
+ async submit(request) {
10610
+ try {
10611
+ const id = await wc.submit(request.workflow, request.payload, { abortSignal: request.signal });
10612
+ if (!id) {
10613
+ throw new ApiError("No task id in response", { status: 502, code: "invalid_response" });
10614
+ }
10615
+ return id;
10616
+ } catch (err) {
10617
+ throw toApiError(err, request.workflow);
10174
10618
  }
10175
- return merged;
10176
10619
  },
10177
- /** Find a folder by name (case-insensitive) across root and subfolders. */
10178
- async findFolder(name) {
10179
- if (name.toLowerCase() === rootFolderName.toLowerCase()) {
10180
- const uid = await ensureRootFolder();
10181
- return uid ? { name: rootFolderName, uid } : null;
10620
+ async poll(handle, options) {
10621
+ try {
10622
+ const res = await wc.runPolling(handle.workflow, handle.id, {
10623
+ pollingInterval: options?.intervalMs,
10624
+ retriesCount: options?.maxAttempts,
10625
+ abortSignal: options?.signal,
10626
+ onProgress: options?.onProgress
10627
+ });
10628
+ return asResult(res);
10629
+ } catch (err) {
10630
+ throw toApiError(err, handle.workflow, handle.id);
10182
10631
  }
10183
- const rootUid = await ensureRootFolder();
10184
- const [rootLevel, subfolders] = await Promise.all([
10185
- fetchFolders(),
10186
- rootUid ? fetchFolders(rootUid) : Promise.resolve([])
10187
- ]);
10188
- const lowerName = name.toLowerCase();
10189
- return [...rootLevel, ...subfolders].find((f2) => f2.name.toLowerCase() === lowerName) ?? null;
10190
10632
  },
10191
- /**
10192
- * List media items. When no folder is given, lists across all folders (flattened).
10193
- * Optionally filter by media type (sent to backend, not client-side).
10194
- */
10195
- async list(options) {
10196
- const folderUid = options?.folder?.uid ?? void 0;
10197
- const files = await fetchMedia({ folderUid, type: options?.type });
10198
- const items = [];
10199
- for (const file of files) {
10200
- const item = toMediaItem(file);
10201
- if (item) items.push(item);
10633
+ async status(handle, signal) {
10634
+ if (signal?.aborted) {
10635
+ throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
10202
10636
  }
10203
- return items;
10204
- },
10205
- /**
10206
- * List media items with full generation metadata (model, prompt, params, etc.).
10207
- * Same options as list() — folder and type filter.
10208
- */
10209
- async listDetailed(options) {
10210
- const folderUid = options?.folder?.uid ?? void 0;
10211
- const files = await fetchMedia({ folderUid, type: options?.type });
10212
- const items = [];
10213
- for (const file of files) {
10214
- const item = toDetailedItem(file);
10215
- if (item) items.push(item);
10637
+ try {
10638
+ return asResult(await wc.result(handle.workflow, handle.id));
10639
+ } catch (err) {
10640
+ throw toApiError(err, handle.workflow, handle.id);
10216
10641
  }
10217
- return items;
10218
10642
  },
10219
- async getGeneration(fileUid) {
10220
- const file = await fetchFileByUid(fileUid);
10221
- return file ? parseGeneration(file) : null;
10222
- },
10223
- /** Save a file to Drive. Returns save result or null on failure. */
10224
- async save(params2, folder) {
10225
- const targetUid = folder?.uid ?? await ensureRootFolder();
10226
- if (!targetUid) return null;
10227
- const targetFolder = folder ?? { name: rootFolderName, uid: targetUid };
10228
- const body = {
10229
- name: params2.name,
10230
- sourceUrl: params2.url,
10231
- parentFolderUid: targetUid,
10232
- content: {
10233
- type: "STANDALONE",
10234
- resourceType: params2.resourceType,
10235
- sourcePlatform: "WEB"
10236
- },
10237
- preview: {
10238
- url: params2.previewUrl || params2.url,
10239
- width: 1024,
10240
- height: 1024
10241
- },
10242
- attributes: Object.entries(params2.attributes ?? {}).map(([property, value]) => ({
10243
- property,
10244
- value
10245
- }))
10246
- };
10643
+ async options(workflow, payload) {
10247
10644
  try {
10248
- let res = await jsonPost("/cloud-storage/v1/me/files", body);
10249
- if (res.status === 400) {
10250
- const text = await res.text();
10251
- if (text.includes("restricted_keywords")) {
10252
- const ext = params2.name.split(".").pop() || "png";
10253
- body.name = `ai-generation-${Date.now()}.${ext}`;
10254
- res = await jsonPost("/cloud-storage/v1/me/files", body);
10255
- } else {
10256
- return null;
10257
- }
10258
- }
10259
- if (!res.ok) return null;
10260
- const data = await res.json();
10261
- const file = data.response;
10262
- const uid = file?.uid;
10263
- if (!uid) return null;
10264
- return { uid, folder: targetFolder };
10645
+ const res = await wc.options(workflow, payload);
10646
+ return typeof res?.credits === "number" ? res.credits : null;
10265
10647
  } catch {
10266
10648
  return null;
10267
10649
  }
10268
- },
10269
- /** Build standard save params from a generation result. */
10270
- buildSaveParams(url, modelId, modelName, mode, prompt) {
10271
- return {
10272
- url,
10273
- name: buildFilename(prompt, mode),
10274
- resourceType: inferResourceType(mode),
10275
- attributes: {
10276
- tool: "ai-sdk",
10277
- model: modelId,
10278
- prompt: prompt || "",
10279
- service: modelName
10280
- }
10281
- };
10282
- },
10283
- async addReaction(fileUid, reaction) {
10284
- return setReaction(fileUid, reaction);
10285
- },
10286
- async removeReaction(fileUid) {
10287
- return setReaction(fileUid, null);
10288
10650
  }
10289
10651
  };
10290
10652
  }
10291
10653
 
10292
- // ../../node_modules/@picsart/workflows-client/dist/index.mjs
10293
- var logger_default = {
10294
- error: (...args) => {
10295
- console.error(...args);
10296
- },
10297
- warn: (...args) => {
10298
- console.debug(...args);
10299
- },
10300
- info: (...args) => {
10301
- console.info(...args);
10302
- },
10303
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10304
- debug: (...args) => {
10305
- console.debug(...args);
10306
- }
10307
- };
10308
- var ExecutionMode = /* @__PURE__ */ ((ExecutionMode2) => {
10309
- ExecutionMode2["ASYNC"] = "ASYNC";
10310
- ExecutionMode2["SYNC"] = "SYNC";
10311
- ExecutionMode2["STREAM"] = "STREAM";
10312
- return ExecutionMode2;
10313
- })(ExecutionMode || {});
10314
- var WorkflowsServerError = class extends Error {
10315
- constructor(message) {
10316
- super(`WorkflowsServerError: ${message}`);
10317
- this.name = this.constructor.name;
10318
- }
10319
- };
10320
- var WorkflowsClientError = class extends Error {
10321
- constructor(action, status, responseBody) {
10322
- super(
10323
- `WorkflowsClientError: [${status}] ${action} failed: ${responseBody.reason} - ${responseBody.message}`
10324
- );
10325
- this.name = this.constructor.name;
10326
- this.status = status;
10327
- this.details = responseBody;
10328
- }
10329
- };
10330
- var WorkflowsUnknownError = class extends Error {
10331
- constructor(message) {
10332
- super(`WorkflowsUnknownError: ${message}`);
10333
- this.name = this.constructor.name;
10654
+ // src/client/prepare.ts
10655
+ function resolvePayloadBuild(model, ctx) {
10656
+ const hasImages = Array.isArray(ctx.imageUrls) && ctx.imageUrls.length > 0 || !!ctx.startFrame || !!ctx.endFrame;
10657
+ return {
10658
+ hasImages,
10659
+ workflow: hasImages && model.editWorkflow ? model.editWorkflow : model.workflow,
10660
+ buildPayload: hasImages && model.buildEditPayload ? model.buildEditPayload : model.buildPayload ?? ((ctx2) => ({ prompt: ctx2.prompt }))
10661
+ };
10662
+ }
10663
+ function prepareRequest(model, params2) {
10664
+ const ctx = { ...params2 };
10665
+ const contract = getModelContract(model.id);
10666
+ const validatedCtx = contract ? contract.input.parse(ctx) : ctx;
10667
+ const resolved = resolvePayloadBuild(model, validatedCtx);
10668
+ const payload = resolved.buildPayload(validatedCtx);
10669
+ return { ctx, workflow: resolved.workflow, payload, contract };
10670
+ }
10671
+ function parseResult(completed, model, contract) {
10672
+ throwIfErrorResult(completed.result, model.name);
10673
+ const parsed = contract?.output ? contract.output.parse(completed.result) : completed.result;
10674
+ const multiItems = extractAllResults(parsed);
10675
+ if (multiItems?.length) {
10676
+ const items2 = multiItems.map((item, i) => ({
10677
+ url: item.url,
10678
+ metadata: buildItemMetadata(parsed, item.source, i, model.provider)
10679
+ }));
10680
+ return {
10681
+ url: items2[0].url,
10682
+ items: items2,
10683
+ results: items2,
10684
+ // Sync executions have no job id (nothing to poll) — omit rather than ''.
10685
+ ...completed.handle.id ? { generationId: completed.handle.id } : {},
10686
+ usage: completed.usage
10687
+ };
10334
10688
  }
10335
- };
10336
- var WorkflowResultUpdateAware = class {
10337
- constructor(onProgressFn, onPartialResultFn, onEventFn) {
10338
- this.onProgressFn = onProgressFn;
10339
- this.onPartialResultFn = onPartialResultFn;
10340
- this.onEventFn = onEventFn;
10689
+ const url = extractUrl(parsed);
10690
+ if (!url) {
10691
+ throw new ApiError(`${model.name}: unexpected response \u2014 no result URL`, {
10692
+ status: 502,
10693
+ code: "invalid_response"
10694
+ });
10341
10695
  }
10342
- async onUpdate(response) {
10343
- if (!response.updated) return;
10344
- await this.deliverEvents(response);
10345
- if (response.status !== "IN_PROGRESS") return;
10346
- const newUpdated = new Date(response.updated);
10347
- if (newUpdated?.getTime() !== this.updated?.getTime()) {
10348
- this.updated = newUpdated;
10349
- await this.onPartialResultFn?.(response);
10350
- if (response.progress) {
10351
- await this.onProgressFn?.(response.progress);
10352
- }
10696
+ const obj = parsed && typeof parsed === "object" ? parsed : void 0;
10697
+ let source = parsed;
10698
+ for (const key of ["images", "items", "imageUrls", "data", "previews"]) {
10699
+ const arr = obj?.[key];
10700
+ if (Array.isArray(arr) && arr.length > 0) {
10701
+ source = arr[0];
10702
+ break;
10353
10703
  }
10354
10704
  }
10355
- async deliverEvents(response) {
10356
- if (!response.events?.length || !this.onEventFn) return;
10357
- let startIdx = 0;
10358
- if (this._lastEventId) {
10359
- const lastSeenIdx = response.events.findIndex((e) => e.id === this._lastEventId);
10360
- if (lastSeenIdx !== -1) {
10361
- startIdx = lastSeenIdx + 1;
10362
- }
10363
- }
10364
- const newEvents = response.events.slice(startIdx);
10365
- for (const event of newEvents) {
10366
- await this.onEventFn(event);
10367
- }
10368
- if (newEvents.length > 0) {
10369
- this._lastEventId = newEvents[newEvents.length - 1].id;
10370
- }
10705
+ const items = [{ url, metadata: buildItemMetadata(parsed, source, 0, model.provider) }];
10706
+ return {
10707
+ url,
10708
+ items,
10709
+ results: items,
10710
+ // Sync executions have no job id (nothing to poll) — omit rather than ''.
10711
+ ...completed.handle.id ? { generationId: completed.handle.id } : {},
10712
+ usage: completed.usage
10713
+ };
10714
+ }
10715
+ function parseTextResult(completed, model) {
10716
+ throwIfErrorResult(completed.result, model.name);
10717
+ throwIfErrorResult(completed.raw, model.name);
10718
+ const text = extractText(completed.result) ?? extractText(completed.raw);
10719
+ if (text == null) {
10720
+ throw new ApiError(`${model.name}: unexpected response \u2014 no text`, {
10721
+ status: 502,
10722
+ code: "invalid_response"
10723
+ });
10371
10724
  }
10372
- };
10373
- async function* decodeSSE(stream) {
10374
- for await (const chunk of readSSE(stream)) {
10375
- const lines = chunk.split("\n");
10376
- const sseData = {};
10377
- for (const line of lines) {
10378
- if (line.startsWith("data:")) {
10379
- const data = line.replace(/^data:\s*/, "");
10380
- if (data === "[DONE]") {
10381
- return;
10382
- }
10383
- try {
10384
- sseData.data = JSON.parse(data);
10385
- } catch (err) {
10386
- logger_default.warn(
10387
- `Failed to parse data JSON from OpenAI event stream: - ${data}, err=${JSON.stringify(err)}`
10388
- );
10389
- }
10390
- }
10725
+ return { text, model: model.id, raw: completed.raw ?? completed.result, usage: completed.usage };
10726
+ }
10727
+
10728
+ // src/core/limits.ts
10729
+ var MAX_DRIVE_PROMPT_LENGTH = 18e3;
10730
+
10731
+ // src/client/drive.ts
10732
+ var USER_REACTION_ATTR = "userReaction";
10733
+ function inferResourceType(mode) {
10734
+ if (mode === "video") return "VIDEO";
10735
+ if (mode === "audio") return "AUDIO";
10736
+ return "PHOTO";
10737
+ }
10738
+ function buildFilename(prompt, mode) {
10739
+ const shortId = String(Date.now()).slice(-6);
10740
+ const ext = mode === "video" ? "mp4" : mode === "audio" ? "mp3" : "png";
10741
+ if (!prompt) return `ai-generation-${shortId}.${ext}`;
10742
+ const slug = prompt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
10743
+ return `${slug}-${shortId}.${ext}`;
10744
+ }
10745
+ function inferMediaType(file) {
10746
+ const name = String(file.name || "");
10747
+ if (/\.(mp3|wav|ogg|aac|flac|m4a)$/i.test(name)) return "audio";
10748
+ if (/\.(mp4|webm|mov|avi|mkv|m4v|wmv)$/i.test(name)) return "video";
10749
+ const contentType = file.contentType ?? file.content;
10750
+ const resourceType = String(contentType?.resourceType || "").toUpperCase();
10751
+ if (resourceType === "VIDEO") return "video";
10752
+ if (resourceType === "AUDIO") return "audio";
10753
+ return "image";
10754
+ }
10755
+ function contentResourceTypes(type) {
10756
+ if (type === "image") return "PHOTO";
10757
+ if (type === "video") return "VIDEO";
10758
+ if (type === "audio") return "AUDIO";
10759
+ return "PHOTO,VIDEO,AUDIO";
10760
+ }
10761
+ function normalizeUrl(raw) {
10762
+ if (typeof raw !== "string") return void 0;
10763
+ return raw.trim() || void 0;
10764
+ }
10765
+ function parseAttributes(raw) {
10766
+ const map = {};
10767
+ if (!raw || typeof raw !== "object") return map;
10768
+ if (Array.isArray(raw)) {
10769
+ for (const a of raw) {
10770
+ map[a.property] = String(a.value);
10771
+ }
10772
+ } else {
10773
+ for (const [k, v] of Object.entries(raw)) {
10774
+ map[k] = String(v);
10391
10775
  }
10392
- yield sseData;
10393
10776
  }
10777
+ return map;
10394
10778
  }
10395
- async function* readSSE(stream) {
10396
- const reader = stream.getReader();
10397
- let buffer = new Uint8Array();
10398
- const decoder = new TextDecoder("utf-8");
10779
+ function parseReaction(value) {
10780
+ return value === "like" || value === "dislike" ? value : void 0;
10781
+ }
10782
+ function parseJsonAttr(raw) {
10783
+ if (!raw) return void 0;
10399
10784
  try {
10400
- while (true) {
10401
- const { value, done } = await reader.read();
10402
- if (done) break;
10403
- const tmp = new Uint8Array(buffer.length + value.length);
10404
- tmp.set(buffer);
10405
- tmp.set(value, buffer.length);
10406
- buffer = tmp;
10407
- let index;
10408
- while ((index = findDoubleNewlineIndex(buffer)) !== -1) {
10409
- const slice = buffer.subarray(0, index);
10410
- yield decoder.decode(slice);
10411
- buffer = buffer.subarray(index);
10412
- }
10413
- }
10414
- if (buffer.length > 0) {
10415
- yield decoder.decode(buffer);
10416
- }
10417
- } finally {
10418
- reader.releaseLock();
10785
+ const value = JSON.parse(raw);
10786
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
10787
+ } catch {
10788
+ return void 0;
10419
10789
  }
10420
10790
  }
10421
- function findDoubleNewlineIndex(buffer) {
10422
- const newline = 10;
10423
- const carriage = 13;
10424
- for (let i = 0; i < buffer.length - 1; i++) {
10425
- if (buffer[i] === newline && buffer[i + 1] === newline) {
10426
- return i + 2;
10427
- }
10428
- if (buffer[i] === carriage && buffer[i + 1] === carriage) {
10429
- return i + 2;
10430
- }
10431
- if (buffer[i] === carriage && buffer[i + 1] === newline && i + 3 < buffer.length && buffer[i + 2] === carriage && buffer[i + 3] === newline) {
10432
- return i + 4;
10433
- }
10791
+ var asString = (v) => typeof v === "string" && v.trim() ? v : void 0;
10792
+ var asStringArray = (v) => Array.isArray(v) && v.length && v.every((x) => typeof x === "string") ? v : void 0;
10793
+ function toSdkPayload(params2) {
10794
+ const p2 = { prompt: String(params2.prompt ?? "").slice(0, MAX_DRIVE_PROMPT_LENGTH) };
10795
+ for (const [key, value] of Object.entries(params2)) {
10796
+ if (key === "prompt") continue;
10797
+ if (value === void 0 || value === null || value === "") continue;
10798
+ p2[key] = value;
10434
10799
  }
10435
- return -1;
10800
+ return p2;
10436
10801
  }
10437
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10438
- var DEFAULT_POLLING_INTERVAL = 300;
10439
- var DEFAULT_RETRIES_COUNT = 1e3;
10440
- var WorkflowsClient = class {
10441
- constructor(options) {
10442
- this.defaultHeaders = {
10443
- Accept: "application/json",
10444
- "Content-Type": "application/json"
10445
- };
10446
- this.terminalStatuses = [
10447
- "COMPLETED",
10448
- "FAILED"
10449
- /* FAILED */
10450
- ];
10451
- this.options = options || {};
10452
- this.options.baseUrl = this.options.baseUrl || "https://api.picsart.com/";
10453
- if (!this.options.baseUrl.endsWith("/")) this.options.baseUrl += "/";
10454
- if (this.options.apiKey) {
10455
- this.options.apiKey = this.options.apiKey.replace("Bearer ", "");
10456
- }
10457
- if (this.options.identityToken) {
10458
- this.options.identityToken = this.options.identityToken.replace("Bearer ", "");
10459
- }
10460
- this.workflowsApiBaseUrl = `${this.options.baseUrl}workflows`;
10802
+ function buildGenerationAttributes(input) {
10803
+ const attrs = {
10804
+ model: input.modelId,
10805
+ aiSDKPayload: JSON.stringify(toSdkPayload(input.params))
10806
+ };
10807
+ if (input.app) {
10808
+ attrs.appId = input.app.id;
10809
+ attrs.appType = input.app.type;
10461
10810
  }
10462
- async run(name, params2, executionOptions) {
10811
+ return attrs;
10812
+ }
10813
+ function toMediaItem(file) {
10814
+ const url = normalizeUrl(file.sourceUrl);
10815
+ if (!url || String(file.name || "").startsWith("__")) return null;
10816
+ const preview = file.preview;
10817
+ return {
10818
+ uid: String(file.uid ?? ""),
10819
+ url,
10820
+ name: String(file.name || ""),
10821
+ type: inferMediaType(file),
10822
+ previewUrl: normalizeUrl(preview?.url),
10823
+ timestamp: Number(file.updatedAt ?? file.createdAt ?? 0)
10824
+ };
10825
+ }
10826
+ function toDetailedItem(file) {
10827
+ const base2 = toMediaItem(file);
10828
+ if (!base2) return null;
10829
+ const attrs = parseAttributes(file.attributes);
10830
+ let extras = {};
10831
+ if (attrs.textScript) {
10463
10832
  try {
10464
- const remoteSettings = await this.getApiSettings(
10465
- name,
10466
- executionOptions?.remoteSettingName
10467
- );
10468
- const executionMode = remoteSettings.executionMode || executionOptions?.mode || "ASYNC";
10469
- if (executionMode === "SYNC") {
10470
- return this.executeTaskSync(name, params2, executionOptions);
10471
- }
10472
- if (executionMode === "STREAM") {
10473
- return this.executeTaskStream(name, params2, executionOptions);
10474
- }
10475
- const taskId = await this.postTask(name, params2, executionOptions);
10476
- await executionOptions?.onAccepted?.(taskId);
10477
- return this.runPolling(name, taskId, executionOptions);
10478
- } catch (err) {
10479
- throw this.wrapError(name, err);
10833
+ extras = JSON.parse(attrs.textScript);
10834
+ } catch {
10480
10835
  }
10481
10836
  }
10482
- async runTypeSafe(name, params2, executionOptions) {
10483
- return this.run(name, params2, executionOptions);
10837
+ return {
10838
+ ...base2,
10839
+ createdAt: file.createdAt,
10840
+ model: attrs.model,
10841
+ prompt: attrs.prompt || void 0,
10842
+ service: attrs.service,
10843
+ subType: attrs.subType,
10844
+ duration: attrs.duration,
10845
+ userReaction: parseReaction(attrs[USER_REACTION_ATTR]),
10846
+ referenceImageUrls: extras.referenceImageUrls,
10847
+ referenceVideoUrl: extras.referenceVideoUrl,
10848
+ referenceAudioUrl: extras.referenceAudioUrl,
10849
+ aspectRatio: extras.aspectRatio,
10850
+ resolution: extras.resolution,
10851
+ quality: extras.quality
10852
+ };
10853
+ }
10854
+ var LEGACY_TOOL_APP = {
10855
+ "ai-playground": { appId: "com.picsart.ai-playground", appType: "miniapp" }
10856
+ };
10857
+ function adaptLegacyGeneration(attrs) {
10858
+ let extras = {};
10859
+ if (attrs.textScript) {
10860
+ try {
10861
+ extras = JSON.parse(attrs.textScript);
10862
+ } catch {
10863
+ }
10484
10864
  }
10485
- async postTask(taskName, command, executionOptions) {
10486
- const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10487
- const response = await this._fetch(`${this.workflowsApiBaseUrl}/${taskName}/submit`, {
10488
- method: "POST",
10489
- headers: {
10490
- "x-config-id": remoteSettings.configId || "",
10491
- ...executionOptions?.headers
10492
- },
10493
- body: JSON.stringify({
10494
- params: command,
10495
- notification: executionOptions?.notificationConfig
10496
- })
10497
- });
10498
- const json = await this.toSuccessResponse(response, taskName);
10499
- return json.response.id;
10865
+ const aiSDKPayload = { prompt: attrs.prompt || "" };
10866
+ const aspectRatio = asString(extras.aspectRatio);
10867
+ if (aspectRatio) aiSDKPayload.aspectRatio = aspectRatio;
10868
+ const resolution = asString(extras.resolution);
10869
+ if (resolution) aiSDKPayload.resolution = resolution;
10870
+ const duration = extras.duration ?? attrs.duration;
10871
+ if (duration != null && duration !== "") aiSDKPayload.duration = Number(duration);
10872
+ const imageUrls = asStringArray(extras.referenceImageUrls);
10873
+ if (imageUrls) aiSDKPayload.imageUrls = imageUrls;
10874
+ const videoUrl = asString(extras.referenceVideoUrl);
10875
+ if (videoUrl) aiSDKPayload.videoUrl = videoUrl;
10876
+ const audioUrl = asString(extras.referenceAudioUrl);
10877
+ if (audioUrl) aiSDKPayload.audioUrl = audioUrl;
10878
+ const startFrame = asString(extras.startFrame);
10879
+ if (startFrame) aiSDKPayload.startFrame = startFrame;
10880
+ const endFrame = asString(extras.endFrame);
10881
+ if (endFrame) aiSDKPayload.endFrame = endFrame;
10882
+ const quality = asString(extras.quality);
10883
+ if (quality) aiSDKPayload.quality = quality;
10884
+ const style = asString(extras.style);
10885
+ if (style) aiSDKPayload.style = style;
10886
+ const iterateModel = asString(extras.iterateModel);
10887
+ if (iterateModel) aiSDKPayload.iterateModel = iterateModel;
10888
+ const exploreImageId = asString(extras.exploreImageId);
10889
+ if (exploreImageId) aiSDKPayload.exploreImageId = exploreImageId;
10890
+ const app = attrs.tool ? LEGACY_TOOL_APP[attrs.tool] : void 0;
10891
+ return {
10892
+ appId: app?.appId,
10893
+ appType: app?.appType,
10894
+ model: attrs.model || void 0,
10895
+ aiSDKPayload,
10896
+ userReaction: parseReaction(attrs[USER_REACTION_ATTR])
10897
+ };
10898
+ }
10899
+ function parseGeneration(file) {
10900
+ const attrs = parseAttributes(file.attributes);
10901
+ if (!attrs.aiSDKPayload) {
10902
+ return adaptLegacyGeneration(attrs);
10500
10903
  }
10501
- async runPolling(taskName, taskId, executionOptions) {
10502
- let retriesCounter = executionOptions?.retriesCount || DEFAULT_RETRIES_COUNT;
10503
- let pollingResponse;
10504
- const progressAware = new WorkflowResultUpdateAware(
10505
- executionOptions?.onProgress,
10506
- executionOptions?.onPartialResult,
10507
- executionOptions?.onEvent
10508
- );
10509
- do {
10510
- await sleep(executionOptions?.pollingInterval || DEFAULT_POLLING_INTERVAL);
10511
- pollingResponse = await this.getResult(taskName, taskId, executionOptions?.abortSignal, executionOptions?.headers);
10512
- await progressAware.onUpdate(pollingResponse.response);
10513
- retriesCounter--;
10514
- } while (retriesCounter > 0 && !this.terminalStatuses.includes(pollingResponse.response.status));
10515
- if (!this.terminalStatuses.includes(pollingResponse.response.status) || !pollingResponse.response.result) {
10516
- throw new WorkflowsClientError(taskName, 408, {
10517
- status: "error",
10518
- reason: "client_timeout",
10519
- message: "Polling timeout reached. Consider increasing polling interval or retries count from execution options. "
10520
- });
10904
+ return {
10905
+ appId: attrs.appId || void 0,
10906
+ appType: attrs.appType === "native" || attrs.appType === "miniapp" ? attrs.appType : void 0,
10907
+ model: attrs.model || void 0,
10908
+ aiSDKPayload: parseJsonAttr(attrs.aiSDKPayload),
10909
+ userReaction: parseReaction(attrs[USER_REACTION_ATTR])
10910
+ };
10911
+ }
10912
+ function createDriveClient(f, apiUrl, rootFolderName) {
10913
+ let cachedRootUid = null;
10914
+ let rootPromise = null;
10915
+ const jsonPost = async (path, body) => f(`${apiUrl}${path}`, {
10916
+ method: "POST",
10917
+ headers: { "Content-Type": "application/json" },
10918
+ body: JSON.stringify(body)
10919
+ });
10920
+ const jsonGet = async (path) => f(`${apiUrl}${path}`);
10921
+ async function findFolderByPath(name) {
10922
+ try {
10923
+ const res = await jsonGet(`/cloud-storage/v1/me/files-by-path?path=${encodeURIComponent(name)}`);
10924
+ if (!res.ok) return null;
10925
+ const data = await res.json();
10926
+ if (data.status !== "success") return null;
10927
+ const response = data.response;
10928
+ const file = Array.isArray(response) ? response[0] : response;
10929
+ return file?.uid ?? null;
10930
+ } catch {
10931
+ return null;
10521
10932
  }
10522
- return {
10523
- result: pollingResponse.response.result,
10524
- usage: pollingResponse.response.usage
10525
- };
10526
10933
  }
10527
- async executeTaskSync(taskName, command, executionOptions) {
10528
- const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10529
- const response = await this._fetch(
10530
- `${this.workflowsApiBaseUrl}/${taskName}/execute`,
10531
- {
10532
- signal: executionOptions?.abortSignal,
10533
- method: "POST",
10534
- headers: {
10535
- "x-config-id": remoteSettings.configId || "",
10536
- ...executionOptions?.headers
10537
- },
10538
- body: JSON.stringify({ params: command })
10539
- }
10540
- );
10541
- const successResponse = await this.toSuccessResponse(
10542
- response,
10543
- taskName
10544
- );
10545
- return {
10546
- result: successResponse.response.result,
10547
- usage: successResponse.response.usage
10548
- };
10934
+ async function findFolderInList(name, parentUid) {
10935
+ try {
10936
+ const params2 = parentUid ? `parentFolderUid=${parentUid}&fileTypes=FOLDER&limit=100` : `fileTypes=FOLDER&limit=100`;
10937
+ const res = await jsonGet(`/cloud-storage/v1/me/files?${params2}`);
10938
+ if (!res.ok) return null;
10939
+ const data = await res.json();
10940
+ const response = data.response;
10941
+ const files = Array.isArray(response) ? response : [];
10942
+ const match = files.find((f2) => String(f2.name || "").toLowerCase() === name.toLowerCase());
10943
+ return match?.uid ?? null;
10944
+ } catch {
10945
+ return null;
10946
+ }
10549
10947
  }
10550
- async getResult(taskName, taskId, abortSignal, headers) {
10551
- const response = await this._fetch(
10552
- `${this.workflowsApiBaseUrl}/${taskName}/${taskId}/result`,
10553
- {
10554
- method: "GET",
10555
- headers: {
10556
- ...headers
10557
- },
10558
- signal: abortSignal
10559
- }
10560
- );
10561
- return this.toSuccessResponse(response, taskName);
10562
- }
10563
- async executeTaskStream(taskName, command, executionOptions) {
10564
- const remoteSettings = await this.getApiSettings(taskName, executionOptions?.remoteSettingName);
10565
- const onEvent = executionOptions?.onEvent;
10566
- const actionName = `Executing ${taskName} task in stream mode`;
10567
- if (!onEvent) {
10568
- throw new WorkflowsClientError(actionName, 400, {
10569
- message: "onEvent is required for streaming",
10570
- status: "error",
10571
- reason: "INVALID_ARGUMENTS"
10572
- });
10573
- }
10574
- const response = await this._fetch(`${this.workflowsApiBaseUrl}/${taskName}/stream`, {
10575
- signal: executionOptions?.abortSignal,
10576
- method: "POST",
10577
- headers: {
10578
- "x-config-id": remoteSettings.configId || "",
10579
- ...executionOptions?.headers,
10580
- Accept: "text/event-stream"
10581
- },
10582
- body: JSON.stringify({ params: command })
10583
- });
10584
- await this.throwIfError(response, actionName);
10585
- if (!response.body) throw new WorkflowsServerError("No response body");
10586
- let completedEvent = {};
10587
- for await (const event of decodeSSE(response.body)) {
10588
- if (executionOptions?.abortSignal?.aborted) break;
10589
- const data = event.data;
10590
- if (data.type.startsWith("event.")) {
10591
- await onEvent({
10592
- ...data,
10593
- type: data.type.replace(/^event\.\s*/, "")
10594
- });
10595
- }
10596
- if (data.type === "task.partial-result") {
10597
- await executionOptions.onPartialResult?.({
10598
- status: "IN_PROGRESS",
10599
- result: data.result
10600
- });
10601
- }
10602
- if (data.type === "task.failed") {
10603
- const failedResult = data.result;
10604
- const statusCode = failedResult.statusCode;
10605
- if (statusCode >= 500) {
10606
- throw new WorkflowsServerError(`[${statusCode}] - ${actionName} failed with message ${failedResult.message}.`);
10607
- }
10608
- if (statusCode >= 400) {
10609
- throw new WorkflowsClientError(actionName, statusCode, failedResult);
10610
- }
10611
- throw new WorkflowsUnknownError(failedResult.message || failedResult.reason);
10612
- }
10613
- if (data.type === "task.completed") {
10614
- completedEvent = data;
10615
- }
10616
- }
10617
- return {
10618
- result: completedEvent.result,
10619
- usage: completedEvent.usage
10620
- };
10621
- }
10622
- async executionsHistory(taskName, offset = 0, limit = 10, isGrouped = false) {
10948
+ async function createFolder(name, parentUid) {
10623
10949
  try {
10624
- const grouped = isGrouped ? "/grouped" : "";
10625
- const url = `${this.options.baseUrl}workflows-history${grouped}?name=${taskName}&limit=${limit}&offset=${offset}`;
10626
- const res = await this._fetch(url);
10627
- return this.toSuccessResponse(res, "requestHistory");
10628
- } catch (err) {
10629
- throw this.wrapError("requestHistory", err);
10950
+ const body = { name };
10951
+ if (parentUid) body.parentFolderUid = parentUid;
10952
+ const res = await jsonPost("/cloud-storage/v1/me/folders", body);
10953
+ if (!res.ok) return null;
10954
+ const data = await res.json();
10955
+ const response = data.response;
10956
+ return response?.uid ?? null;
10957
+ } catch {
10958
+ return null;
10630
10959
  }
10631
10960
  }
10632
- async toSuccessResponse(response, actionName) {
10633
- await this.throwIfError(response, actionName);
10634
- return await response.json();
10961
+ async function resolveRootFolder() {
10962
+ const byPath = await findFolderByPath(rootFolderName);
10963
+ if (byPath) return byPath;
10964
+ const inList = await findFolderInList(rootFolderName);
10965
+ if (inList) return inList;
10966
+ const recheck = await findFolderByPath(rootFolderName);
10967
+ if (recheck) return recheck;
10968
+ return createFolder(rootFolderName);
10635
10969
  }
10636
- async throwIfError(response, actionName) {
10637
- if (response.status >= 500) {
10638
- let message;
10639
- try {
10640
- const errorResponse = await response.json();
10641
- message = errorResponse.message || errorResponse.reason || "Unknown error";
10642
- } catch (err) {
10643
- message = "Non json response was returned from server";
10644
- }
10645
- throw new WorkflowsServerError(`[${response.status}] - ${actionName} failed with message: ${message}.`);
10646
- }
10647
- if (!response.ok) {
10648
- throw new WorkflowsClientError(actionName, response.status, await response.json());
10970
+ async function ensureRootFolder() {
10971
+ if (cachedRootUid) return cachedRootUid;
10972
+ if (!rootPromise) {
10973
+ rootPromise = resolveRootFolder().then((uid) => {
10974
+ cachedRootUid = uid;
10975
+ rootPromise = null;
10976
+ return uid;
10977
+ }).catch((err) => {
10978
+ setTimeout(() => {
10979
+ rootPromise = null;
10980
+ }, 1e4);
10981
+ throw err;
10982
+ });
10649
10983
  }
10984
+ return rootPromise;
10650
10985
  }
10651
- async getApiSettings(name, remoteSettingName) {
10652
- if (!this.options.getRemoteSettings) return {};
10653
- const settingName = remoteSettingName || `${name.replace(/-/g, "_").toLowerCase()}_api`;
10986
+ async function fetchFolders(parentUid) {
10654
10987
  try {
10655
- const apiSetting = await this.options.getRemoteSettings(
10656
- settingName,
10657
- "miniapp"
10658
- );
10659
- return {
10660
- configId: apiSetting?.configId || "",
10661
- executionMode: apiSetting?.executionMode
10662
- };
10663
- } catch (err) {
10664
- logger_default.error(
10665
- `workflows.getConfigId - failed when fetching remoteSettings: settingName=${settingName}`,
10666
- err
10667
- );
10668
- return {};
10988
+ const params2 = parentUid ? `parentFolderUid=${parentUid}&fileTypes=FOLDER&limit=100` : `fileTypes=FOLDER&limit=100`;
10989
+ const res = await jsonGet(`/cloud-storage/v1/me/files?${params2}`);
10990
+ if (!res.ok) return [];
10991
+ const data = await res.json();
10992
+ const files = Array.isArray(data.response) ? data.response : [];
10993
+ return files.filter((f2) => f2.uid && f2.name).map((f2) => ({ name: String(f2.name), uid: String(f2.uid) }));
10994
+ } catch {
10995
+ return [];
10669
10996
  }
10670
10997
  }
10671
- wrapError(actionName, error) {
10672
- if (error instanceof WorkflowsClientError || error instanceof WorkflowsServerError || error instanceof DOMException) {
10673
- return error;
10998
+ async function fetchMedia(opts) {
10999
+ try {
11000
+ const endpoint = opts.folderUid ? "/cloud-storage/v1/me/files" : "/cloud-storage/v1/me/flattened-files";
11001
+ const params2 = [
11002
+ opts.folderUid ? `parentFolderUid=${opts.folderUid}` : "",
11003
+ "limit=100",
11004
+ "sortType=UPDATED",
11005
+ "sortOrder=DESC",
11006
+ "fileTypes=FILE",
11007
+ `contentResourceTypes=${contentResourceTypes(opts.type)}`
11008
+ ].filter(Boolean).join("&");
11009
+ const res = await jsonGet(`${endpoint}?${params2}`);
11010
+ if (!res.ok) return [];
11011
+ const data = await res.json();
11012
+ return Array.isArray(data.response) ? data.response : [];
11013
+ } catch {
11014
+ return [];
10674
11015
  }
10675
- logger_default.error(`PluggableAPIUnknownError - ${actionName} failed`, error);
10676
- return new WorkflowsUnknownError(
10677
- `workflows.${actionName} failed - ${error.message}`
10678
- );
10679
11016
  }
10680
- buildRequestHeaders(initHeaders) {
10681
- const headers = new Headers(initHeaders);
10682
- const optionHeaders = new Headers({
10683
- ...this.defaultHeaders,
10684
- ...this.options.headers
10685
- });
10686
- for (const [key, value] of optionHeaders.entries()) {
10687
- if (!headers.has(key)) {
10688
- headers.set(key, value);
10689
- }
10690
- }
10691
- if (this.options.apiKey) {
10692
- headers.set("Authorization", `Bearer ${this.options.apiKey}`);
10693
- }
10694
- if (this.options.identityToken) {
10695
- headers.set("x-app-authorization", `Bearer ${this.options.identityToken}`);
11017
+ async function fetchFileByUid(fileUid) {
11018
+ try {
11019
+ const res = await jsonGet(`/drive/v1/files/${fileUid}`);
11020
+ if (!res.ok) return null;
11021
+ const data = await res.json();
11022
+ const file = data.response;
11023
+ return file && typeof file === "object" && !Array.isArray(file) ? file : null;
11024
+ } catch {
11025
+ return null;
10696
11026
  }
10697
- return headers;
10698
11027
  }
10699
- async _fetch(input, init) {
10700
- const headers = this.buildRequestHeaders(init?.headers);
10701
- const requestInit = {
10702
- ...init,
10703
- headers
10704
- };
10705
- if (this.options.fetch) {
10706
- return this.options.fetch(input, {
10707
- ...requestInit,
10708
- // return headers as a plain object for easier handling in custom fetch
10709
- headers: Object.fromEntries(headers.entries())
11028
+ async function setReaction(fileUid, reaction) {
11029
+ try {
11030
+ const res = await f(`${apiUrl}/drive/v1/files/${fileUid}`, {
11031
+ method: "PATCH",
11032
+ headers: { "Content-Type": "application/json" },
11033
+ body: JSON.stringify({ attributes: { [USER_REACTION_ATTR]: reaction } })
10710
11034
  });
11035
+ return res.ok;
11036
+ } catch {
11037
+ return false;
10711
11038
  }
10712
- if (!headers.has("Authorization") && !headers.has("x-app-authorization")) {
10713
- throw new Error("apiKey is not provided");
10714
- }
10715
- return fetch(input, requestInit);
10716
11039
  }
10717
- };
10718
- var WorkflowsClient_default = WorkflowsClient;
10719
-
10720
- // src/client/apis.ts
10721
- function createApis(config) {
10722
- const f = config ? resolveFetch(config) : null;
10723
- const client = config && f ? new WorkflowsClient_default({
10724
- baseUrl: config.apiUrl,
10725
- fetch: (input, init) => f(typeof input === "string" ? input : input.toString(), init)
10726
- }) : null;
10727
11040
  return {
10728
- async run(api, payload, options) {
10729
- if (!client) {
10730
- throw new Error("ai.apis requires a client created with a ClientConfig (authenticated fetch).");
11041
+ /**
11042
+ * Ensure a subfolder exists inside the root folder.
11043
+ * Creates both root and subfolder if needed. Returns the folder reference.
11044
+ * Call with no argument to just ensure the root folder exists.
11045
+ */
11046
+ async ensureFolder(subfolder) {
11047
+ const rootUid = await ensureRootFolder();
11048
+ if (!rootUid) return null;
11049
+ if (!subfolder) {
11050
+ return { name: rootFolderName, uid: rootUid };
10731
11051
  }
10732
- const forwarded = { ...options ?? {} };
10733
- delete forwarded.remoteSettingName;
10734
- delete forwarded.onPartialResult;
10735
- delete forwarded.notificationConfig;
10736
- return client.run(api, payload, forwarded);
10737
- }
10738
- // The public conditional-typed signature lives on ApisClient; the runtime
10739
- // impl is uniform, so we assert the shape here.
10740
- };
10741
- }
10742
-
10743
- // src/client/catalogs.ts
10744
- var DEFAULT_LIMIT = 100;
10745
- var MIN_TTL_SECONDS = 60;
10746
- var copyPage = (page) => ({
10747
- items: [...page.items],
10748
- nextCursor: page.nextCursor
10749
- });
10750
- var abortError = (signal) => signal.reason ?? new DOMException("The catalog load was aborted.", "AbortError");
10751
- function abortable(promise, signal) {
10752
- if (!signal) return promise;
10753
- if (signal.aborted) return Promise.reject(abortError(signal));
10754
- return new Promise((resolve, reject) => {
10755
- const onAbort = () => reject(abortError(signal));
10756
- signal.addEventListener("abort", onAbort, { once: true });
10757
- const settle = () => signal.removeEventListener("abort", onAbort);
10758
- promise.then(
10759
- (value) => {
10760
- settle();
10761
- resolve(value);
10762
- },
10763
- (err) => {
10764
- settle();
10765
- reject(err);
11052
+ const existingUid = await findFolderInList(subfolder, rootUid);
11053
+ if (existingUid) return { name: subfolder, uid: existingUid };
11054
+ const newUid = await createFolder(subfolder, rootUid);
11055
+ if (!newUid) return null;
11056
+ return { name: subfolder, uid: newUid };
11057
+ },
11058
+ /** List subfolders inside the root folder (boards). */
11059
+ async folders() {
11060
+ const rootUid = await ensureRootFolder();
11061
+ if (!rootUid) return [];
11062
+ return fetchFolders(rootUid);
11063
+ },
11064
+ /** List top-level Drive folders + root subfolders, deduplicated. */
11065
+ async allFolders() {
11066
+ const rootUid = await ensureRootFolder();
11067
+ const [rootLevel, subfolders] = await Promise.all([
11068
+ fetchFolders(),
11069
+ rootUid ? fetchFolders(rootUid) : Promise.resolve([])
11070
+ ]);
11071
+ const seen = /* @__PURE__ */ new Set();
11072
+ const merged = [];
11073
+ for (const folder of [...rootLevel, ...subfolders]) {
11074
+ if (seen.has(folder.uid)) continue;
11075
+ seen.add(folder.uid);
11076
+ merged.push(folder);
10766
11077
  }
10767
- );
10768
- });
10769
- }
10770
- function createCatalogs(transport, options) {
10771
- const stores = /* @__PURE__ */ new Map();
10772
- const inflight = /* @__PURE__ */ new Map();
10773
- const keyOf2 = (s) => `${s.workflow} ${s.modelId ?? ""}`;
10774
- async function fetchPage(workflow, query) {
10775
- const payload = {};
10776
- if (query.modelId) payload.modelId = query.modelId;
10777
- if (query.cursor) payload.cursor = query.cursor;
10778
- if (query.limit) payload.limit = query.limit;
10779
- const raw = await transport.execute({ workflow, payload });
10780
- const container = raw?.response ?? raw;
10781
- if (raw?.status === "error" || container?.status === "FAILED") {
10782
- const message = container?.message ?? container?.error ?? raw?.message;
10783
- throw new Error(`${workflow} failed${message ? `: ${String(message)}` : ""}`);
10784
- }
10785
- const result = container?.result;
11078
+ return merged;
11079
+ },
11080
+ /** Find a folder by name (case-insensitive) across root and subfolders. */
11081
+ async findFolder(name) {
11082
+ if (name.toLowerCase() === rootFolderName.toLowerCase()) {
11083
+ const uid = await ensureRootFolder();
11084
+ return uid ? { name: rootFolderName, uid } : null;
11085
+ }
11086
+ const rootUid = await ensureRootFolder();
11087
+ const [rootLevel, subfolders] = await Promise.all([
11088
+ fetchFolders(),
11089
+ rootUid ? fetchFolders(rootUid) : Promise.resolve([])
11090
+ ]);
11091
+ const lowerName = name.toLowerCase();
11092
+ return [...rootLevel, ...subfolders].find((f2) => f2.name.toLowerCase() === lowerName) ?? null;
11093
+ },
11094
+ /**
11095
+ * List media items. When no folder is given, lists across all folders (flattened).
11096
+ * Optionally filter by media type (sent to backend, not client-side).
11097
+ */
11098
+ async list(options) {
11099
+ const folderUid = options?.folder?.uid ?? void 0;
11100
+ const files = await fetchMedia({ folderUid, type: options?.type });
11101
+ const items = [];
11102
+ for (const file of files) {
11103
+ const item = toMediaItem(file);
11104
+ if (item) items.push(item);
11105
+ }
11106
+ return items;
11107
+ },
11108
+ /**
11109
+ * List media items with full generation metadata (model, prompt, params, etc.).
11110
+ * Same options as list() — folder and type filter.
11111
+ */
11112
+ async listDetailed(options) {
11113
+ const folderUid = options?.folder?.uid ?? void 0;
11114
+ const files = await fetchMedia({ folderUid, type: options?.type });
11115
+ const items = [];
11116
+ for (const file of files) {
11117
+ const item = toDetailedItem(file);
11118
+ if (item) items.push(item);
11119
+ }
11120
+ return items;
11121
+ },
11122
+ async getGeneration(fileUid) {
11123
+ const file = await fetchFileByUid(fileUid);
11124
+ return file ? parseGeneration(file) : null;
11125
+ },
11126
+ /** Save a file to Drive. Returns save result or null on failure. */
11127
+ async save(params2, folder) {
11128
+ const targetUid = folder?.uid ?? await ensureRootFolder();
11129
+ if (!targetUid) return null;
11130
+ const targetFolder = folder ?? { name: rootFolderName, uid: targetUid };
11131
+ const body = {
11132
+ name: params2.name,
11133
+ sourceUrl: params2.url,
11134
+ parentFolderUid: targetUid,
11135
+ content: {
11136
+ type: "STANDALONE",
11137
+ resourceType: params2.resourceType,
11138
+ sourcePlatform: "WEB"
11139
+ },
11140
+ preview: {
11141
+ url: params2.previewUrl || params2.url,
11142
+ width: 1024,
11143
+ height: 1024
11144
+ },
11145
+ attributes: Object.entries(params2.attributes ?? {}).map(([property, value]) => ({
11146
+ property,
11147
+ value
11148
+ }))
11149
+ };
11150
+ try {
11151
+ let res = await jsonPost("/cloud-storage/v1/me/files", body);
11152
+ if (res.status === 400) {
11153
+ const text = await res.text();
11154
+ if (text.includes("restricted_keywords")) {
11155
+ const ext = params2.name.split(".").pop() || "png";
11156
+ body.name = `ai-generation-${Date.now()}.${ext}`;
11157
+ res = await jsonPost("/cloud-storage/v1/me/files", body);
11158
+ } else {
11159
+ return null;
11160
+ }
11161
+ }
11162
+ if (!res.ok) return null;
11163
+ const data = await res.json();
11164
+ const file = data.response;
11165
+ const uid = file?.uid;
11166
+ if (!uid) return null;
11167
+ return { uid, folder: targetFolder };
11168
+ } catch {
11169
+ return null;
11170
+ }
11171
+ },
11172
+ /** Build standard save params from a generation result. */
11173
+ buildSaveParams(url, modelId, modelName, mode, prompt) {
11174
+ return {
11175
+ url,
11176
+ name: buildFilename(prompt, mode),
11177
+ resourceType: inferResourceType(mode),
11178
+ attributes: {
11179
+ tool: "ai-sdk",
11180
+ model: modelId,
11181
+ prompt: prompt || "",
11182
+ service: modelName
11183
+ }
11184
+ };
11185
+ },
11186
+ async addReaction(fileUid, reaction) {
11187
+ return setReaction(fileUid, reaction);
11188
+ },
11189
+ async removeReaction(fileUid) {
11190
+ return setReaction(fileUid, null);
11191
+ }
11192
+ };
11193
+ }
11194
+
11195
+ // src/client/apis.ts
11196
+ function createApis(client) {
11197
+ return {
11198
+ async run(api, payload, options) {
11199
+ if (!client) {
11200
+ throw new ApiError(
11201
+ "`ai.apis` requires `apiUrl` plus `fetch` or `apiKey` on createClient \u2014 the workflows APIs are not served by a custom transport.",
11202
+ { status: 400, code: "unsupported_transport" }
11203
+ );
11204
+ }
11205
+ const forwarded = { ...options ?? {} };
11206
+ delete forwarded.remoteSettingName;
11207
+ delete forwarded.onPartialResult;
11208
+ delete forwarded.notificationConfig;
11209
+ try {
11210
+ return await client.run(api, payload, forwarded);
11211
+ } catch (err) {
11212
+ throw toApiError(err, api);
11213
+ }
11214
+ }
11215
+ // The public conditional-typed signature lives on ApisClient; the runtime
11216
+ // impl is uniform, so we assert the shape here.
11217
+ };
11218
+ }
11219
+
11220
+ // src/client/catalogs.ts
11221
+ var DEFAULT_LIMIT = 100;
11222
+ var MIN_TTL_SECONDS = 60;
11223
+ var copyPage = (page) => ({
11224
+ items: [...page.items],
11225
+ nextCursor: page.nextCursor
11226
+ });
11227
+ var abortError = (signal) => signal.reason ?? new DOMException("The catalog load was aborted.", "AbortError");
11228
+ function abortable(promise, signal) {
11229
+ if (!signal) return promise;
11230
+ if (signal.aborted) return Promise.reject(abortError(signal));
11231
+ return new Promise((resolve, reject) => {
11232
+ const onAbort = () => reject(abortError(signal));
11233
+ signal.addEventListener("abort", onAbort, { once: true });
11234
+ const settle = () => signal.removeEventListener("abort", onAbort);
11235
+ promise.then(
11236
+ (value) => {
11237
+ settle();
11238
+ resolve(value);
11239
+ },
11240
+ (err) => {
11241
+ settle();
11242
+ reject(err);
11243
+ }
11244
+ );
11245
+ });
11246
+ }
11247
+ function createCatalogs(transport, options) {
11248
+ const stores = /* @__PURE__ */ new Map();
11249
+ const inflight = /* @__PURE__ */ new Map();
11250
+ const keyOf2 = (s) => `${s.workflow} ${s.modelId ?? ""}`;
11251
+ async function fetchPage(workflow, query) {
11252
+ const payload = {};
11253
+ if (query.modelId) payload.modelId = query.modelId;
11254
+ if (query.cursor) payload.cursor = query.cursor;
11255
+ if (query.limit) payload.limit = query.limit;
11256
+ let res;
11257
+ try {
11258
+ res = await transport.execute({ workflow, payload });
11259
+ } catch (err) {
11260
+ if (err instanceof ApiError || err instanceof DOMException && err.name === "AbortError") throw err;
11261
+ throw new ApiError(`${workflow} failed: ${err instanceof Error ? err.message : String(err)}`, {
11262
+ status: 502,
11263
+ code: "bad_gateway"
11264
+ });
11265
+ }
11266
+ const body = res.result ?? res.raw;
11267
+ const container = body?.response ?? body;
11268
+ const result = container?.result ?? container;
11269
+ throwIfErrorResult(result, workflow);
10786
11270
  if (!result || !Array.isArray(result.items)) {
10787
- throw new Error(`${workflow} returned no catalog result`);
11271
+ throw new ApiError(`${workflow} returned no catalog result`, {
11272
+ status: 502,
11273
+ code: "invalid_response"
11274
+ });
10788
11275
  }
10789
11276
  return { ...result, nextCursor: result.nextCursor ?? null };
10790
11277
  }
@@ -10876,6 +11363,13 @@ function createCatalogs(transport, options) {
10876
11363
  return client;
10877
11364
  }
10878
11365
 
11366
+ // src/client/types.ts
11367
+ var GenerationEventType = {
11368
+ Progress: "generation.progress",
11369
+ Completed: "generation.completed",
11370
+ Failed: "generation.failed"
11371
+ };
11372
+
10879
11373
  // src/client/index.ts
10880
11374
  var MODE_POLL_DEFAULTS = {
10881
11375
  video: { intervalMs: 2e3, maxAttempts: 1800 },
@@ -10895,30 +11389,90 @@ function resolvePollOptions(model, overrides) {
10895
11389
  return resolved;
10896
11390
  }
10897
11391
  function createClient(config) {
10898
- const isConfig = isClientConfig(config);
10899
- const transport = isConfig ? buildTransport(config) : config;
10900
- const client = createWorkflowClient(transport, { pollingIntervalMs: 2e3 });
10901
- const supportsSubmit = typeof transport.submit === "function";
10902
- const apis = createApis(isConfig ? config : null);
10903
- const catalogs = createCatalogs(transport, isConfig ? config.catalogs : void 0);
10904
- const inputsTransformationConfig = isConfig ? config.inputsTransformation : void 0;
10905
- const driveConfig = isConfig ? config.drive : void 0;
10906
- const driveClient = isConfig && driveConfig ? createDriveClient(resolveFetch(config), config.apiUrl, driveConfig.folder) : null;
11392
+ const authedFetch = maybeFetch(config);
11393
+ const wc = config.apiUrl && authedFetch ? createWorkflowsClient(config.apiUrl, authedFetch) : null;
11394
+ function buildDefaultTransport() {
11395
+ if (wc) return buildTransport(wc);
11396
+ throw new Error(config.apiUrl ? "createClient config requires either `fetch` or `apiKey` (or a custom `transport`)." : "createClient config requires `apiUrl` (or a custom `transport`).");
11397
+ }
11398
+ const transport = config.transport ?? buildDefaultTransport();
11399
+ const supportsAsync = typeof transport.submit === "function" && typeof transport.poll === "function";
11400
+ const apis = createApis(wc);
11401
+ const inputsTransformationConfig = config.inputsTransformation;
11402
+ const catalogs = createCatalogs(transport, config.catalogs);
11403
+ const driveConfig = config.drive;
11404
+ const driveApiUrl = config.apiUrl;
11405
+ if (driveConfig && !(authedFetch && driveApiUrl)) {
11406
+ throw new Error("createClient `drive` requires `apiUrl` plus `fetch` or `apiKey` \u2014 Drive is a REST surface, not served by a custom transport.");
11407
+ }
11408
+ const driveClient = driveConfig && authedFetch && driveApiUrl ? createDriveClient(authedFetch, driveApiUrl, driveConfig.folder) : null;
11409
+ function toApiError2(err, signal) {
11410
+ if (err instanceof ApiError) return err;
11411
+ if (err instanceof DOMException && err.name === "AbortError") {
11412
+ if (signal?.aborted) {
11413
+ return new ApiError("Operation aborted", { status: 499, code: "aborted" });
11414
+ }
11415
+ throw err;
11416
+ }
11417
+ return new ApiError(err instanceof Error ? err.message : String(err), {
11418
+ status: 502,
11419
+ code: "generation_failed"
11420
+ });
11421
+ }
11422
+ function unsupportedTransport(capability) {
11423
+ return new ApiError(`Transport does not support ${capability} (execute-only transport)`, {
11424
+ status: 400,
11425
+ code: "unsupported_transport"
11426
+ });
11427
+ }
11428
+ function asCompleted(handle, res) {
11429
+ return toCompletedStatus(handle, res.result, res.raw ?? res.result, res.usage);
11430
+ }
11431
+ function pollJob(handle, poll, onProgress) {
11432
+ if (!transport.poll) return Promise.reject(unsupportedTransport("polling"));
11433
+ return transport.poll(handle, { ...poll, onProgress });
11434
+ }
11435
+ function assertAsyncLifecycle() {
11436
+ if (!transport.submit) throw unsupportedTransport("submit");
11437
+ if (!transport.poll) throw unsupportedTransport("polling");
11438
+ }
11439
+ function assertMediaModel(model) {
11440
+ if (model.mode === "text") {
11441
+ throw new ApiError(`${model.name} is a text model \u2014 use generateText() instead.`, {
11442
+ status: 400,
11443
+ code: "wrong_model_mode"
11444
+ });
11445
+ }
11446
+ }
11447
+ async function submitJob(workflow, payload, signal) {
11448
+ if (signal?.aborted) {
11449
+ throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
11450
+ }
11451
+ if (!transport.submit) throw unsupportedTransport("submit");
11452
+ const id = await transport.submit({ workflow, payload, signal });
11453
+ if (!id) {
11454
+ throw new ApiError("No task id in response", { status: 502, code: "invalid_response" });
11455
+ }
11456
+ return id;
11457
+ }
10907
11458
  async function executeModel(model, workflow, payload, options) {
10908
11459
  const signal = options?.signal;
10909
- if (model.syncExecute || !supportsSubmit) {
10910
- const syncResponse = await client.run(
10911
- { workflow, payload, signal },
10912
- { mode: "sync" }
10913
- );
10914
- return toCompletedStatus(
10915
- syncResponse.handle,
10916
- extractSyncResult(syncResponse.raw),
10917
- syncResponse.raw,
10918
- syncResponse.usage
10919
- );
11460
+ try {
11461
+ if (model.syncExecute || !supportsAsync) {
11462
+ const res2 = await transport.execute({ workflow, payload, signal });
11463
+ return toCompletedStatus(
11464
+ { workflow, id: "" },
11465
+ extractSyncResult(res2.result) ?? res2.result,
11466
+ res2.raw ?? res2.result,
11467
+ res2.usage
11468
+ );
11469
+ }
11470
+ const id = await submitJob(workflow, payload, signal);
11471
+ const res = await pollJob({ workflow, id }, resolvePollOptions(model, options));
11472
+ return asCompleted({ workflow, id }, res);
11473
+ } catch (err) {
11474
+ throw toApiError2(err, signal);
10920
11475
  }
10921
- return client.run({ workflow, payload, signal }, resolvePollOptions(model, options));
10922
11476
  }
10923
11477
  function buildDrivePayloadOptions(model, params2, options) {
10924
11478
  const explicit = options?.drive;
@@ -10950,24 +11504,33 @@ function createClient(config) {
10950
11504
  }
10951
11505
  };
10952
11506
  }
11507
+ async function resolveJobHandle(model, generationId, signal) {
11508
+ const primary = { workflow: model.workflow, id: generationId };
11509
+ if (!model.editWorkflow || !transport.status) return primary;
11510
+ try {
11511
+ await transport.status(primary, signal);
11512
+ return primary;
11513
+ } catch (err) {
11514
+ if (err instanceof ApiError && err.status === 404) {
11515
+ return { workflow: model.editWorkflow, id: generationId };
11516
+ }
11517
+ throw err;
11518
+ }
11519
+ }
10953
11520
  return {
10954
11521
  // ── Simple path ──────────────────────────────────────────────────
10955
11522
  /**
10956
11523
  * Generate content using a model.
10957
11524
  *
10958
11525
  * Validates input, builds the vendor payload, picks the right workflow,
10959
- * submits the job, polls to completion, and returns the result URL.
11526
+ * submits the job, polls to completion, and returns the parsed result
11527
+ * (`items[]` with per-item URLs and promoted vendor metadata).
10960
11528
  * If drive options are provided (or DriveConfig is set), the backend
10961
11529
  * saves the result to Picsart Drive.
10962
11530
  */
10963
11531
  async generate(model, params2, options) {
10964
11532
  const resolved = resolveModel(model);
10965
- if (resolved.mode === "text") {
10966
- throw new ApiError(`${resolved.name} is a text model \u2014 use generateText() instead.`, {
10967
- status: 400,
10968
- code: "wrong_model_mode"
10969
- });
10970
- }
11533
+ assertMediaModel(resolved);
10971
11534
  const { workflow, payload, contract } = prepareRequest(resolved, params2);
10972
11535
  const drive = buildDrivePayloadOptions(resolved, params2, options);
10973
11536
  const finalPayload = injectPayloadOptions(payload, drive, options?.inputsTransformation);
@@ -10994,13 +11557,6 @@ function createClient(config) {
10994
11557
  const completed = await executeModel(resolved, workflow, payload, options);
10995
11558
  return parseTextResult(completed, resolved);
10996
11559
  },
10997
- /** @deprecated Use `getCredits()` instead. */
10998
- async estimate(model, params2) {
10999
- if (!transport.options) return null;
11000
- const resolved = resolveModel(model);
11001
- const { workflow, payload } = prepareRequest(resolved, params2);
11002
- return await transport.options(workflow, payload) ?? null;
11003
- },
11004
11560
  /**
11005
11561
  * Get exact credit cost for a model with specific parameters.
11006
11562
  * Calls the backend /options endpoint for real-time pricing.
@@ -11019,61 +11575,114 @@ function createClient(config) {
11019
11575
  return payload;
11020
11576
  },
11021
11577
  // ── Advanced lifecycle ────────────────────────────────────────────
11022
- /** Submit a generation job and get a handle back. */
11578
+ /** Submit a generation job and get its generation id back. Media models
11579
+ * only — text models have no async lifecycle (`result()`/`subscribe()`
11580
+ * reject them). Pass the id to `result(model, id)` / `subscribe(model, id)`. */
11023
11581
  async submit(model, params2, options) {
11024
11582
  const resolved = resolveModel(model);
11583
+ assertMediaModel(resolved);
11584
+ assertAsyncLifecycle();
11025
11585
  const { workflow, payload } = prepareRequest(resolved, params2);
11026
11586
  const drive = buildDrivePayloadOptions(resolved, params2, options);
11027
11587
  const finalPayload = injectPayloadOptions(payload, drive, options?.inputsTransformation);
11028
- return client.submit({ workflow, payload: finalPayload, signal: options?.signal });
11029
- },
11030
- /** Check the current status of a submitted job. */
11031
- async status(handle, signal) {
11032
- return client.status(handle, signal);
11588
+ try {
11589
+ return await submitJob(workflow, finalPayload, options?.signal);
11590
+ } catch (err) {
11591
+ throw toApiError2(err, options?.signal);
11592
+ }
11033
11593
  },
11034
11594
  /** Poll a submitted job until it completes and return the parsed result. */
11035
- async result(handle, model, options) {
11595
+ async result(model, generationId, options) {
11036
11596
  const resolved = resolveModel(model);
11597
+ assertMediaModel(resolved);
11037
11598
  const contract = getModelContract(resolved.id);
11038
- const completed = await client.result(handle, resolvePollOptions(resolved, options));
11599
+ assertAsyncLifecycle();
11600
+ const handle = await resolveJobHandle(resolved, generationId, options?.signal);
11601
+ let completed;
11602
+ try {
11603
+ completed = asCompleted(handle, await pollJob(handle, resolvePollOptions(resolved, options)));
11604
+ } catch (err) {
11605
+ throw toApiError2(err, options?.signal);
11606
+ }
11039
11607
  return parseResult(completed, resolved, contract);
11040
11608
  },
11041
11609
  /**
11042
- * Subscribe to live status updates for a submitted job.
11610
+ * Subscribe to live updates for a submitted job. Yields one
11611
+ * {@link GenerationEvent} per poll: `generation.progress` while running,
11612
+ * then a single terminal `generation.completed` (with the parsed result)
11613
+ * or `generation.failed` (with the {@link ApiError} `result()` would have
11614
+ * thrown). Failures arrive as events, not exceptions.
11043
11615
  *
11044
11616
  * ```ts
11045
- * const handle = await ai.submit(Models.Flux2Pro, { prompt: 'a cat' });
11046
- * for await (const update of ai.subscribe(handle)) {
11047
- * console.log(update.status, update.progress);
11617
+ * const id = await ai.submit(Models.Flux2Pro, { prompt: 'a cat' });
11618
+ * for await (const e of ai.subscribe(Models.Flux2Pro, id)) {
11619
+ * if (e.type === 'generation.progress') console.log(e.progress?.percent);
11620
+ * if (e.type === 'generation.completed') console.log(e.result.url);
11621
+ * if (e.type === 'generation.failed') console.error(e.error.message);
11048
11622
  * }
11049
11623
  * ```
11050
11624
  */
11051
- subscribe(handle, options) {
11052
- return client.subscribe(handle, options);
11053
- },
11054
- // ── Raw workflow access ──────────────────────────────────────────
11055
- /**
11056
- * Run a raw workflow (not tied to a model).
11057
- * @deprecated Use `apis.run()` instead.
11058
- */
11059
- async runWorkflow(workflow, payload, options) {
11060
- const done = await client.run(
11061
- { workflow, payload, signal: options?.signal },
11062
- options
11063
- );
11064
- if (done.status === "FAILED" || done.status === "CANCELED") {
11065
- throw new ApiError(done.error ?? `${workflow} failed with status ${done.status}`, {
11066
- status: done.statusCode ?? (done.status === "CANCELED" ? 499 : 502),
11067
- code: done.reason ?? (done.status === "CANCELED" ? "canceled" : "generation_failed")
11068
- });
11069
- }
11070
- if (done.result === void 0) {
11071
- throw new ApiError(`${workflow} completed but returned no result`, {
11072
- status: 502,
11073
- code: "invalid_response"
11625
+ subscribe(model, generationId, options) {
11626
+ const resolved = resolveModel(model);
11627
+ assertMediaModel(resolved);
11628
+ assertAsyncLifecycle();
11629
+ const contract = getModelContract(resolved.id);
11630
+ return (async function* () {
11631
+ let handle;
11632
+ try {
11633
+ handle = await resolveJobHandle(resolved, generationId, options?.signal);
11634
+ } catch (err) {
11635
+ yield { type: "generation.failed", error: toApiError2(err, options?.signal) };
11636
+ return;
11637
+ }
11638
+ const poll = new AbortController();
11639
+ if (options?.signal) {
11640
+ if (options.signal.aborted) poll.abort();
11641
+ else options.signal.addEventListener("abort", () => poll.abort(), { once: true });
11642
+ }
11643
+ const queue = [];
11644
+ let wake;
11645
+ let waker = new Promise((resolve) => {
11646
+ wake = () => resolve(null);
11074
11647
  });
11075
- }
11076
- return done.result;
11648
+ const done = pollJob(
11649
+ handle,
11650
+ { ...resolvePollOptions(resolved, options), signal: poll.signal },
11651
+ (p2) => {
11652
+ queue.push(p2);
11653
+ wake();
11654
+ }
11655
+ ).then(
11656
+ (res) => ({ ok: true, res }),
11657
+ (err) => ({ ok: false, err })
11658
+ );
11659
+ try {
11660
+ for (; ; ) {
11661
+ while (queue.length) yield { type: "generation.progress", progress: queue.shift() };
11662
+ const raced = await Promise.race([done, waker]);
11663
+ if (raced === null) {
11664
+ waker = new Promise((resolve) => {
11665
+ wake = () => resolve(null);
11666
+ });
11667
+ continue;
11668
+ }
11669
+ while (queue.length) yield { type: "generation.progress", progress: queue.shift() };
11670
+ if (!raced.ok) {
11671
+ yield { type: "generation.failed", error: toApiError2(raced.err, options?.signal) };
11672
+ return;
11673
+ }
11674
+ const completed = asCompleted(handle, raced.res);
11675
+ try {
11676
+ yield { type: "generation.completed", result: parseResult(completed, resolved, contract) };
11677
+ } catch (err) {
11678
+ yield { type: "generation.failed", error: toApiError2(err, options?.signal) };
11679
+ }
11680
+ return;
11681
+ }
11682
+ } finally {
11683
+ poll.abort();
11684
+ }
11685
+ })();
11077
11686
  },
11078
11687
  // ── apis (direct, low-level API access) ───────────────────────────
11079
11688
  /** Direct, low-level access to the Picsart model APIs. See `./apis.ts`. */
@@ -11087,446 +11696,6 @@ function createClient(config) {
11087
11696
  };
11088
11697
  }
11089
11698
 
11090
- // src/core/constraints.ts
11091
- function normalize(r) {
11092
- if ("disabled" in r) return { kind: "disabled", reason: r.reason };
11093
- return { kind: "allowed", allowed: r.allowed, reason: r.reason };
11094
- }
11095
- function matchOperator(op, actual) {
11096
- if ("exists" in op) {
11097
- const has = actual != null && (!Array.isArray(actual) || actual.length > 0) && (typeof actual !== "string" || actual.length > 0);
11098
- return op.exists ? has : !has;
11099
- }
11100
- if ("is" in op) return actual === op.is;
11101
- return false;
11102
- }
11103
- function matchCondition(when, values) {
11104
- return Object.entries(when).every(
11105
- ([key, op]) => matchOperator(op, values[key])
11106
- );
11107
- }
11108
- function merge(prev, next) {
11109
- if (!prev) return next;
11110
- if (prev.kind === "disabled" || next.kind === "disabled") {
11111
- return { kind: "disabled", reason: next.kind === "disabled" ? next.reason : prev.kind === "disabled" ? prev.reason : void 0 };
11112
- }
11113
- const allowed = new Set(next.allowed.map(String));
11114
- return {
11115
- kind: "allowed",
11116
- allowed: prev.allowed.filter((o) => allowed.has(String(o))),
11117
- reason: next.reason ?? prev.reason
11118
- };
11119
- }
11120
- function evaluateConstraints(constraints, values) {
11121
- const effects = /* @__PURE__ */ new Map();
11122
- if (!constraints?.length) return effects;
11123
- for (const rule of constraints) {
11124
- if (!matchCondition(rule.when, values)) continue;
11125
- for (const [key, restriction] of Object.entries(rule.then)) {
11126
- effects.set(key, merge(effects.get(key), normalize(restriction)));
11127
- }
11128
- }
11129
- return effects;
11130
- }
11131
-
11132
- // src/core/descriptors/pricing.ts
11133
- var import_pa_model_pricing_sdk = __toESM(require_build());
11134
- var _client = null;
11135
- var _byModel = null;
11136
- var _loadPromise = null;
11137
- function configurePricing(options) {
11138
- _client = new import_pa_model_pricing_sdk.ModelPricingClient(options);
11139
- _byModel = null;
11140
- _loadPromise = null;
11141
- }
11142
- function loadPricing() {
11143
- if (_byModel) return Promise.resolve();
11144
- if (!_client) {
11145
- return Promise.reject(new Error(
11146
- "loadPricing(): not configured. Call catalog.pricing.configure({ baseUrl, fetch }) first."
11147
- ));
11148
- }
11149
- if (!_loadPromise) {
11150
- const client = _client;
11151
- _loadPromise = client.init().then(() => {
11152
- const byModel = /* @__PURE__ */ new Map();
11153
- for (const entry of client.getModelPricing()) {
11154
- const id = entry.metadata.modelId;
11155
- const list = byModel.get(id);
11156
- if (list) list.push(entry);
11157
- else byModel.set(id, [entry]);
11158
- }
11159
- _byModel = byModel;
11160
- }).catch((err) => {
11161
- _loadPromise = null;
11162
- throw err;
11163
- });
11164
- }
11165
- return _loadPromise;
11166
- }
11167
- function isPricingLoaded() {
11168
- return _byModel !== null;
11169
- }
11170
- function getCreditsForModel(modelId, ctx) {
11171
- if (!_byModel) return null;
11172
- let entries = _byModel.get(modelId);
11173
- if (!entries || entries.length === 0) return null;
11174
- if (ctx) {
11175
- entries = entries.filter((e) => {
11176
- if (ctx.generateAudio !== void 0 && e.metadata.audio !== ctx.generateAudio) return false;
11177
- if (ctx.resolution !== void 0 && e.metadata.quality !== ctx.resolution) return false;
11178
- return true;
11179
- });
11180
- if (entries.length === 0) return null;
11181
- }
11182
- let min = Infinity;
11183
- let max = -Infinity;
11184
- let unit = entries[0].unit;
11185
- for (const e of entries) {
11186
- if (e.credits < min) min = e.credits;
11187
- if (e.credits > max) max = e.credits;
11188
- if (e.unit !== unit) unit = void 0;
11189
- }
11190
- const tiers = entries.map((e) => ({
11191
- credits: e.credits,
11192
- unit: e.unit,
11193
- quality: e.metadata.quality || void 0,
11194
- audio: e.metadata.audio,
11195
- useCase: e.metadata.useCase
11196
- }));
11197
- return unit ? { min, max, unit, tiers } : { min, max, tiers };
11198
- }
11199
-
11200
- // src/core/descriptors/model-accessor.ts
11201
- function withHydration(entry, flat) {
11202
- if (entry.descriptor.kind !== "catalog") return flat;
11203
- const hydrated = getHydratedCatalog(entry.descriptor.source);
11204
- if (!hydrated) return flat;
11205
- return { ...flat, catalogOptions: hydrated.catalogOptions };
11206
- }
11207
- var ModelParamsAccessorImpl = class {
11208
- def;
11209
- constructor(def) {
11210
- this.def = def;
11211
- }
11212
- param(key) {
11213
- const entry = this.def.paramConfig[key];
11214
- if (!entry) return void 0;
11215
- const { descriptor, ...meta } = entry;
11216
- return withHydration(entry, { ...meta, ...descriptor });
11217
- }
11218
- hasParam(key) {
11219
- return key in this.def.paramConfig;
11220
- }
11221
- all() {
11222
- return Object.entries(this.def.paramConfig).map(
11223
- ([key, entry]) => {
11224
- const { descriptor, ...meta } = entry;
11225
- return withHydration(entry, { key, ...meta, ...descriptor });
11226
- }
11227
- );
11228
- }
11229
- // Kind-narrowed accessors
11230
- enum(key) {
11231
- return this.narrow(key, "enum");
11232
- }
11233
- catalog(key) {
11234
- return this.narrow(key, "catalog");
11235
- }
11236
- range(key) {
11237
- return this.narrow(key, "range");
11238
- }
11239
- boolean(key) {
11240
- return this.narrow(key, "boolean");
11241
- }
11242
- text(key) {
11243
- return this.narrow(key, "text");
11244
- }
11245
- file(key) {
11246
- return this.narrow(key, "file");
11247
- }
11248
- // Well-known shorthands
11249
- prompt() {
11250
- return this.narrow("prompt", "text");
11251
- }
11252
- aspectRatio() {
11253
- return this.narrow("aspectRatio", "enum");
11254
- }
11255
- /** Enum on fixed-option models, range where the vendor accepts every value
11256
- * in a span — callers narrow on `.kind`. */
11257
- duration() {
11258
- const entry = this.param("duration");
11259
- if (!entry || entry.kind !== "enum" && entry.kind !== "range") return void 0;
11260
- return entry;
11261
- }
11262
- resolution() {
11263
- return this.narrow("resolution", "enum");
11264
- }
11265
- generateAudio() {
11266
- return this.narrow("generateAudio", "boolean");
11267
- }
11268
- startFrame() {
11269
- return this.narrow("startFrame", "file");
11270
- }
11271
- endFrame() {
11272
- return this.narrow("endFrame", "file");
11273
- }
11274
- // Absorbed from Models namespace
11275
- hasFileInput() {
11276
- return Object.values(this.def.paramConfig).some((e) => e.descriptor.kind === "file");
11277
- }
11278
- getDefault(key) {
11279
- const entry = this.def.paramConfig[key];
11280
- if (!entry) return void 0;
11281
- const d = entry.descriptor;
11282
- return "default" in d ? d.default : void 0;
11283
- }
11284
- getDefaults() {
11285
- return extractDefaults(this.def.paramConfig);
11286
- }
11287
- /** @deprecated Use `enum(key)` instead. */
11288
- getEnumOptions(key) {
11289
- const entry = this.def.paramConfig[key];
11290
- if (!entry || entry.descriptor.kind !== "enum") return null;
11291
- return entry.descriptor.options.map((o) => o.id);
11292
- }
11293
- toSchema() {
11294
- return descriptorsToSchema(this.def.paramConfig);
11295
- }
11296
- transferValues(prev) {
11297
- return transferValues(this.def.paramConfig, prev);
11298
- }
11299
- narrow(key, kind) {
11300
- const entry = this.param(key);
11301
- if (!entry || entry.kind !== kind) return void 0;
11302
- return entry;
11303
- }
11304
- };
11305
- var ConstrainedParamsAccessor = class {
11306
- inner;
11307
- effects;
11308
- constructor(inner, effects) {
11309
- this.inner = inner;
11310
- this.effects = effects;
11311
- }
11312
- // ── Decorated accessors ──────────────────────────────────────────
11313
- enum(key) {
11314
- return this.applyEnum(key, this.inner.enum(key));
11315
- }
11316
- catalog(key) {
11317
- return this.applyEntry(key, this.inner.catalog(key));
11318
- }
11319
- range(key) {
11320
- return this.applyEntry(key, this.inner.range(key));
11321
- }
11322
- boolean(key) {
11323
- return this.applyEntry(key, this.inner.boolean(key));
11324
- }
11325
- text(key) {
11326
- return this.applyEntry(key, this.inner.text(key));
11327
- }
11328
- file(key) {
11329
- return this.applyEntry(key, this.inner.file(key));
11330
- }
11331
- prompt() {
11332
- return this.applyEntry("prompt", this.inner.prompt());
11333
- }
11334
- aspectRatio() {
11335
- return this.applyEnum("aspectRatio", this.inner.aspectRatio());
11336
- }
11337
- duration() {
11338
- const entry = this.inner.duration();
11339
- return entry?.kind === "enum" ? this.applyEnum("duration", entry) : this.applyEntry("duration", entry);
11340
- }
11341
- resolution() {
11342
- return this.applyEnum("resolution", this.inner.resolution());
11343
- }
11344
- generateAudio() {
11345
- return this.applyEntry("generateAudio", this.inner.generateAudio());
11346
- }
11347
- startFrame() {
11348
- return this.applyEntry("startFrame", this.inner.startFrame());
11349
- }
11350
- endFrame() {
11351
- return this.applyEntry("endFrame", this.inner.endFrame());
11352
- }
11353
- all() {
11354
- return this.inner.all().map((e) => {
11355
- const r = this.effects.get(e.key);
11356
- if (!r) return e;
11357
- if (e.kind === "enum") return this.decorateEnumFlat(e, r);
11358
- if (r.kind === "disabled") return { ...e, disabled: true, disabledReason: r.reason };
11359
- return e;
11360
- });
11361
- }
11362
- // ── Pass-through delegates ───────────────────────────────────────
11363
- param(key) {
11364
- return this.inner.param(key);
11365
- }
11366
- hasParam(key) {
11367
- return this.inner.hasParam(key);
11368
- }
11369
- hasFileInput() {
11370
- return this.inner.hasFileInput();
11371
- }
11372
- getDefault(key) {
11373
- return this.inner.getDefault(key);
11374
- }
11375
- getDefaults() {
11376
- return this.inner.getDefaults();
11377
- }
11378
- getEnumOptions(key) {
11379
- return this.inner.getEnumOptions(key);
11380
- }
11381
- toSchema() {
11382
- return this.inner.toSchema();
11383
- }
11384
- transferValues(prev) {
11385
- return this.inner.transferValues(prev);
11386
- }
11387
- // ── Private helpers ──────────────────────────────────────────────
11388
- applyEntry(key, entry) {
11389
- if (!entry) return void 0;
11390
- const r = this.effects.get(key);
11391
- if (!r) return entry;
11392
- if (r.kind === "disabled") return { ...entry, disabled: true, disabledReason: r.reason };
11393
- return entry;
11394
- }
11395
- applyEnum(key, entry) {
11396
- if (!entry) return void 0;
11397
- const r = this.effects.get(key);
11398
- if (!r) return entry;
11399
- if (r.kind === "disabled") {
11400
- const options2 = entry.options.map((opt) => ({ ...opt, disabled: true, disabledReason: r.reason }));
11401
- return { ...entry, options: options2, disabled: true, disabledReason: r.reason };
11402
- }
11403
- const allowed = new Set(r.allowed.map(String));
11404
- const options = entry.options.map(
11405
- (opt) => allowed.has(String(opt.id)) ? opt : { ...opt, disabled: true, disabledReason: r.reason }
11406
- );
11407
- return { ...entry, options };
11408
- }
11409
- decorateEnumFlat(entry, r) {
11410
- if (entry.kind !== "enum") return entry;
11411
- if (r.kind === "disabled") {
11412
- const options2 = entry.options.map((opt) => ({
11413
- ...opt,
11414
- disabled: true,
11415
- disabledReason: r.reason
11416
- }));
11417
- return { ...entry, options: options2, disabled: true, disabledReason: r.reason };
11418
- }
11419
- const allowed = new Set(r.allowed.map(String));
11420
- const options = entry.options.map(
11421
- (opt) => allowed.has(String(opt.id)) ? opt : { ...opt, disabled: true, disabledReason: r.reason }
11422
- );
11423
- return { ...entry, options };
11424
- }
11425
- };
11426
- var ModelMetaImpl = class {
11427
- mode;
11428
- inputType;
11429
- description;
11430
- features;
11431
- badges;
11432
- provider;
11433
- addedAt;
11434
- release;
11435
- constructor(def) {
11436
- this.mode = def.mode;
11437
- this.inputType = def.inputType;
11438
- this.description = def.description;
11439
- this.features = def.features;
11440
- this.badges = def.badge ?? [];
11441
- this.release = def.release ?? "production";
11442
- this.provider = {
11443
- id: def.provider,
11444
- name: def.providerName,
11445
- color: def.providerColor,
11446
- label: def.providerLabel
11447
- };
11448
- this.addedAt = def.addedAt ?? null;
11449
- }
11450
- };
11451
- var ModelDescriptorImpl = class {
11452
- id;
11453
- name;
11454
- api;
11455
- def;
11456
- _params;
11457
- _meta;
11458
- constructor(def) {
11459
- this.id = def.id;
11460
- this.name = def.name;
11461
- this.api = { workflow: def.workflow, editWorkflow: def.editWorkflow };
11462
- this.def = def;
11463
- }
11464
- params() {
11465
- return this._params ??= new ModelParamsAccessorImpl(this.def);
11466
- }
11467
- paramsFor(values) {
11468
- const inner = this.params();
11469
- const effects = evaluateConstraints(this.def.constraints, values);
11470
- if (!effects.size) return inner;
11471
- return new ConstrainedParamsAccessor(inner, effects);
11472
- }
11473
- validate(input) {
11474
- if (!input || typeof input !== "object" || Array.isArray(input)) {
11475
- return { valid: false, errors: [`Invalid input for model "${this.def.id}"`] };
11476
- }
11477
- try {
11478
- validateAll(this.def.paramConfig, input);
11479
- return { valid: true };
11480
- } catch (err) {
11481
- return { valid: false, errors: [err instanceof Error ? err.message : String(err)] };
11482
- }
11483
- }
11484
- meta() {
11485
- return this._meta ??= new ModelMetaImpl(this.def);
11486
- }
11487
- getCreditsInfo(ctx) {
11488
- if (this.def.modelId) {
11489
- const byModelId = getCreditsForModel(this.def.modelId, ctx);
11490
- if (byModelId) return byModelId;
11491
- }
11492
- return getCreditsForModel(this.def.id, ctx);
11493
- }
11494
- };
11495
- function _model(id) {
11496
- return new ModelDescriptorImpl(resolveModel(id));
11497
- }
11498
- function _all(filter = {}) {
11499
- const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
11500
- return ALL_MODELS.filter((m) => isVisibleForReleases(m, releases)).map((m) => new ModelDescriptorImpl(m));
11501
- }
11502
- function _find(filter) {
11503
- const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
11504
- return ALL_MODELS.filter((m) => {
11505
- if (!isVisibleForReleases(m, releases)) return false;
11506
- if (filter.output && m.mode !== filter.output) return false;
11507
- if (filter.provider && m.provider !== filter.provider) return false;
11508
- return true;
11509
- }).map((m) => new ModelDescriptorImpl(m));
11510
- }
11511
- function _search(query, filter = {}) {
11512
- const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
11513
- const q = query.toLowerCase();
11514
- return ALL_MODELS.filter(
11515
- (m) => isVisibleForReleases(m, releases) && (m.id.toLowerCase().includes(q) || m.name.toLowerCase().includes(q) || m.provider.toLowerCase().includes(q))
11516
- ).map((m) => new ModelDescriptorImpl(m));
11517
- }
11518
- var Model = _model;
11519
- var catalog = {
11520
- all: _all,
11521
- find: _find,
11522
- search: _search,
11523
- pricing: {
11524
- configure: configurePricing,
11525
- load: loadPricing,
11526
- isLoaded: isPricingLoaded
11527
- }
11528
- };
11529
-
11530
11699
  // src/generated/model-constants.ts
11531
11700
  var AsyncFlashV1 = "async-flash-v1";
11532
11701
  var BytedanceOmnihumanV15 = "bytedance-omnihuman-v1.5";
@@ -11643,7 +11812,6 @@ var LumaUni1Max = "luma-uni-1-max";
11643
11812
  var Lyria3Clip = "lyria-3-clip";
11644
11813
  var Lyria3Pro = "lyria-3-pro";
11645
11814
  var Lyria35 = "lyria-3.5";
11646
- var Minimax02Hd = "minimax-02-hd";
11647
11815
  var MinimaxH3 = "minimax-h3";
11648
11816
  var MinimaxH3Max = "minimax-h3-max";
11649
11817
  var MinimaxH3MaxCameraControls = "minimax-h3-max-camera-controls";
@@ -11869,7 +12037,6 @@ var Models = {
11869
12037
  Lyria3Clip,
11870
12038
  Lyria3Pro,
11871
12039
  Lyria35,
11872
- Minimax02Hd,
11873
12040
  MinimaxH3,
11874
12041
  MinimaxH3Max,
11875
12042
  MinimaxH3MaxCameraControls,
@@ -11978,39 +12145,437 @@ var Models = {
11978
12145
  Wan27T2v,
11979
12146
  Wan27VideoEdit,
11980
12147
  Wan30Video,
11981
- Wan30VideoPrime,
11982
- /** @deprecated Use the `catalog` accessor (`catalog.all()` / `catalog.find({ output, provider })`) instead. */
11983
- list(filter) {
11984
- if (!filter) return [...ALL_MODELS];
11985
- return ALL_MODELS.filter((m) => {
11986
- if (filter.mode && m.mode !== filter.mode) return false;
11987
- if (filter.provider && m.provider !== filter.provider) return false;
12148
+ Wan30VideoPrime
12149
+ };
12150
+
12151
+ // src/core/constraints.ts
12152
+ function normalize(r) {
12153
+ if ("disabled" in r) return { kind: "disabled", reason: r.reason };
12154
+ return { kind: "allowed", allowed: r.allowed, reason: r.reason };
12155
+ }
12156
+ function matchOperator(op, actual) {
12157
+ if ("exists" in op) {
12158
+ const has = actual != null && (!Array.isArray(actual) || actual.length > 0) && (typeof actual !== "string" || actual.length > 0);
12159
+ return op.exists ? has : !has;
12160
+ }
12161
+ if ("is" in op) return actual === op.is;
12162
+ return false;
12163
+ }
12164
+ function matchCondition(when, values) {
12165
+ return Object.entries(when).every(
12166
+ ([key, op]) => matchOperator(op, values[key])
12167
+ );
12168
+ }
12169
+ function merge(prev, next) {
12170
+ if (!prev) return next;
12171
+ if (prev.kind === "disabled" || next.kind === "disabled") {
12172
+ return { kind: "disabled", reason: next.kind === "disabled" ? next.reason : prev.kind === "disabled" ? prev.reason : void 0 };
12173
+ }
12174
+ const allowed = new Set(next.allowed.map(String));
12175
+ return {
12176
+ kind: "allowed",
12177
+ allowed: prev.allowed.filter((o) => allowed.has(String(o))),
12178
+ reason: next.reason ?? prev.reason
12179
+ };
12180
+ }
12181
+ function evaluateConstraints(constraints, values) {
12182
+ const effects = /* @__PURE__ */ new Map();
12183
+ if (!constraints?.length) return effects;
12184
+ for (const rule of constraints) {
12185
+ if (!matchCondition(rule.when, values)) continue;
12186
+ for (const [key, restriction] of Object.entries(rule.then)) {
12187
+ effects.set(key, merge(effects.get(key), normalize(restriction)));
12188
+ }
12189
+ }
12190
+ return effects;
12191
+ }
12192
+
12193
+ // src/core/descriptors/pricing.ts
12194
+ var import_pa_model_pricing_sdk = __toESM(require_build(), 1);
12195
+ var _client = null;
12196
+ var _byModel = null;
12197
+ var _loadPromise = null;
12198
+ function configurePricing(options) {
12199
+ _client = new import_pa_model_pricing_sdk.ModelPricingClient(options);
12200
+ _byModel = null;
12201
+ _loadPromise = null;
12202
+ }
12203
+ function loadPricing() {
12204
+ if (_byModel) return Promise.resolve();
12205
+ if (!_client) {
12206
+ return Promise.reject(new Error(
12207
+ "loadPricing(): not configured. Call catalog.pricing.configure({ baseUrl, fetch }) first."
12208
+ ));
12209
+ }
12210
+ if (!_loadPromise) {
12211
+ const client = _client;
12212
+ _loadPromise = client.init().then(() => {
12213
+ const byModel = /* @__PURE__ */ new Map();
12214
+ for (const entry of client.getModelPricing()) {
12215
+ const id = entry.metadata.modelId;
12216
+ const list = byModel.get(id);
12217
+ if (list) list.push(entry);
12218
+ else byModel.set(id, [entry]);
12219
+ }
12220
+ _byModel = byModel;
12221
+ }).catch((err) => {
12222
+ _loadPromise = null;
12223
+ throw err;
12224
+ });
12225
+ }
12226
+ return _loadPromise;
12227
+ }
12228
+ function isPricingLoaded() {
12229
+ return _byModel !== null;
12230
+ }
12231
+ function getCreditsForModel(modelId, ctx) {
12232
+ if (!_byModel) return null;
12233
+ let entries = _byModel.get(modelId);
12234
+ if (!entries || entries.length === 0) return null;
12235
+ if (ctx) {
12236
+ entries = entries.filter((e) => {
12237
+ if (ctx.generateAudio !== void 0 && e.metadata.audio !== ctx.generateAudio) return false;
12238
+ if (ctx.resolution !== void 0 && e.metadata.quality !== ctx.resolution) return false;
11988
12239
  return true;
11989
12240
  });
11990
- },
11991
- /** @deprecated Use `Model(id).validate(input)` instead. */
11992
- validate(model, input) {
12241
+ if (entries.length === 0) return null;
12242
+ }
12243
+ let min = Infinity;
12244
+ let max = -Infinity;
12245
+ let unit = entries[0].unit;
12246
+ for (const e of entries) {
12247
+ if (e.credits < min) min = e.credits;
12248
+ if (e.credits > max) max = e.credits;
12249
+ if (e.unit !== unit) unit = void 0;
12250
+ }
12251
+ const tiers = entries.map((e) => ({
12252
+ credits: e.credits,
12253
+ unit: e.unit,
12254
+ quality: e.metadata.quality || void 0,
12255
+ audio: e.metadata.audio,
12256
+ useCase: e.metadata.useCase
12257
+ }));
12258
+ return unit ? { min, max, unit, tiers } : { min, max, tiers };
12259
+ }
12260
+
12261
+ // src/core/descriptors/model-accessor.ts
12262
+ function withHydration(entry, flat) {
12263
+ if (entry.descriptor.kind !== "catalog") return flat;
12264
+ const hydrated = getHydratedCatalog(entry.descriptor.source);
12265
+ if (!hydrated) return flat;
12266
+ return { ...flat, catalogOptions: hydrated.catalogOptions };
12267
+ }
12268
+ var ModelParamsAccessorImpl = class {
12269
+ def;
12270
+ constructor(def) {
12271
+ this.def = def;
12272
+ }
12273
+ param(key) {
12274
+ const entry = this.def.paramConfig[key];
12275
+ if (!entry) return void 0;
12276
+ const { descriptor, ...meta } = entry;
12277
+ return withHydration(entry, { ...meta, ...descriptor });
12278
+ }
12279
+ hasParam(key) {
12280
+ return key in this.def.paramConfig;
12281
+ }
12282
+ all() {
12283
+ return Object.entries(this.def.paramConfig).map(
12284
+ ([key, entry]) => {
12285
+ const { descriptor, ...meta } = entry;
12286
+ return withHydration(entry, { key, ...meta, ...descriptor });
12287
+ }
12288
+ );
12289
+ }
12290
+ // Kind-narrowed accessors
12291
+ enum(key) {
12292
+ return this.narrow(key, "enum");
12293
+ }
12294
+ catalog(key) {
12295
+ return this.narrow(key, "catalog");
12296
+ }
12297
+ range(key) {
12298
+ return this.narrow(key, "range");
12299
+ }
12300
+ boolean(key) {
12301
+ return this.narrow(key, "boolean");
12302
+ }
12303
+ text(key) {
12304
+ return this.narrow(key, "text");
12305
+ }
12306
+ file(key) {
12307
+ return this.narrow(key, "file");
12308
+ }
12309
+ // Well-known shorthands
12310
+ prompt() {
12311
+ return this.narrow("prompt", "text");
12312
+ }
12313
+ aspectRatio() {
12314
+ return this.narrow("aspectRatio", "enum");
12315
+ }
12316
+ /** Enum on fixed-option models, range where the vendor accepts every value
12317
+ * in a span — callers narrow on `.kind`. */
12318
+ duration() {
12319
+ const entry = this.param("duration");
12320
+ if (!entry || entry.kind !== "enum" && entry.kind !== "range") return void 0;
12321
+ return entry;
12322
+ }
12323
+ resolution() {
12324
+ return this.narrow("resolution", "enum");
12325
+ }
12326
+ generateAudio() {
12327
+ return this.narrow("generateAudio", "boolean");
12328
+ }
12329
+ startFrame() {
12330
+ return this.narrow("startFrame", "file");
12331
+ }
12332
+ endFrame() {
12333
+ return this.narrow("endFrame", "file");
12334
+ }
12335
+ // Absorbed from Models namespace
12336
+ hasFileInput() {
12337
+ return Object.values(this.def.paramConfig).some((e) => e.descriptor.kind === "file");
12338
+ }
12339
+ getDefault(key) {
12340
+ const entry = this.def.paramConfig[key];
12341
+ if (!entry) return void 0;
12342
+ const d = entry.descriptor;
12343
+ return "default" in d ? d.default : void 0;
12344
+ }
12345
+ getDefaults() {
12346
+ return extractDefaults(this.def.paramConfig);
12347
+ }
12348
+ toSchema() {
12349
+ return descriptorsToSchema(this.def.paramConfig);
12350
+ }
12351
+ transferValues(prev) {
12352
+ return transferValues(this.def.paramConfig, prev);
12353
+ }
12354
+ narrow(key, kind) {
12355
+ const entry = this.param(key);
12356
+ if (!entry || entry.kind !== kind) return void 0;
12357
+ return entry;
12358
+ }
12359
+ };
12360
+ var ConstrainedParamsAccessor = class {
12361
+ inner;
12362
+ effects;
12363
+ constructor(inner, effects) {
12364
+ this.inner = inner;
12365
+ this.effects = effects;
12366
+ }
12367
+ // ── Decorated accessors ──────────────────────────────────────────
12368
+ enum(key) {
12369
+ return this.applyEnum(key, this.inner.enum(key));
12370
+ }
12371
+ catalog(key) {
12372
+ return this.applyEntry(key, this.inner.catalog(key));
12373
+ }
12374
+ range(key) {
12375
+ return this.applyEntry(key, this.inner.range(key));
12376
+ }
12377
+ boolean(key) {
12378
+ return this.applyEntry(key, this.inner.boolean(key));
12379
+ }
12380
+ text(key) {
12381
+ return this.applyEntry(key, this.inner.text(key));
12382
+ }
12383
+ file(key) {
12384
+ return this.applyEntry(key, this.inner.file(key));
12385
+ }
12386
+ prompt() {
12387
+ return this.applyEntry("prompt", this.inner.prompt());
12388
+ }
12389
+ aspectRatio() {
12390
+ return this.applyEnum("aspectRatio", this.inner.aspectRatio());
12391
+ }
12392
+ duration() {
12393
+ const entry = this.inner.duration();
12394
+ return entry?.kind === "enum" ? this.applyEnum("duration", entry) : this.applyEntry("duration", entry);
12395
+ }
12396
+ resolution() {
12397
+ return this.applyEnum("resolution", this.inner.resolution());
12398
+ }
12399
+ generateAudio() {
12400
+ return this.applyEntry("generateAudio", this.inner.generateAudio());
12401
+ }
12402
+ startFrame() {
12403
+ return this.applyEntry("startFrame", this.inner.startFrame());
12404
+ }
12405
+ endFrame() {
12406
+ return this.applyEntry("endFrame", this.inner.endFrame());
12407
+ }
12408
+ all() {
12409
+ return this.inner.all().map((e) => {
12410
+ const r = this.effects.get(e.key);
12411
+ if (!r) return e;
12412
+ if (e.kind === "enum") return this.decorateEnumFlat(e, r);
12413
+ if (r.kind === "disabled") return { ...e, disabled: true, disabledReason: r.reason };
12414
+ return e;
12415
+ });
12416
+ }
12417
+ // ── Pass-through delegates ───────────────────────────────────────
12418
+ param(key) {
12419
+ return this.inner.param(key);
12420
+ }
12421
+ hasParam(key) {
12422
+ return this.inner.hasParam(key);
12423
+ }
12424
+ hasFileInput() {
12425
+ return this.inner.hasFileInput();
12426
+ }
12427
+ getDefault(key) {
12428
+ return this.inner.getDefault(key);
12429
+ }
12430
+ getDefaults() {
12431
+ return this.inner.getDefaults();
12432
+ }
12433
+ toSchema() {
12434
+ return this.inner.toSchema();
12435
+ }
12436
+ transferValues(prev) {
12437
+ return this.inner.transferValues(prev);
12438
+ }
12439
+ // ── Private helpers ──────────────────────────────────────────────
12440
+ applyEntry(key, entry) {
12441
+ if (!entry) return void 0;
12442
+ const r = this.effects.get(key);
12443
+ if (!r) return entry;
12444
+ if (r.kind === "disabled") return { ...entry, disabled: true, disabledReason: r.reason };
12445
+ return entry;
12446
+ }
12447
+ applyEnum(key, entry) {
12448
+ if (!entry) return void 0;
12449
+ const r = this.effects.get(key);
12450
+ if (!r) return entry;
12451
+ if (r.kind === "disabled") {
12452
+ const options2 = entry.options.map((opt) => ({ ...opt, disabled: true, disabledReason: r.reason }));
12453
+ return { ...entry, options: options2, disabled: true, disabledReason: r.reason };
12454
+ }
12455
+ const allowed = new Set(r.allowed.map(String));
12456
+ const options = entry.options.map(
12457
+ (opt) => allowed.has(String(opt.id)) ? opt : { ...opt, disabled: true, disabledReason: r.reason }
12458
+ );
12459
+ return { ...entry, options };
12460
+ }
12461
+ decorateEnumFlat(entry, r) {
12462
+ if (entry.kind !== "enum") return entry;
12463
+ if (r.kind === "disabled") {
12464
+ const options2 = entry.options.map((opt) => ({
12465
+ ...opt,
12466
+ disabled: true,
12467
+ disabledReason: r.reason
12468
+ }));
12469
+ return { ...entry, options: options2, disabled: true, disabledReason: r.reason };
12470
+ }
12471
+ const allowed = new Set(r.allowed.map(String));
12472
+ const options = entry.options.map(
12473
+ (opt) => allowed.has(String(opt.id)) ? opt : { ...opt, disabled: true, disabledReason: r.reason }
12474
+ );
12475
+ return { ...entry, options };
12476
+ }
12477
+ };
12478
+ var ModelMetaImpl = class {
12479
+ mode;
12480
+ inputType;
12481
+ description;
12482
+ features;
12483
+ badges;
12484
+ provider;
12485
+ addedAt;
12486
+ release;
12487
+ constructor(def) {
12488
+ this.mode = def.mode;
12489
+ this.inputType = def.inputType;
12490
+ this.description = def.description;
12491
+ this.features = def.features;
12492
+ this.badges = def.badge ?? [];
12493
+ this.release = def.release ?? "production";
12494
+ this.provider = {
12495
+ id: def.provider,
12496
+ name: def.providerName,
12497
+ color: def.providerColor,
12498
+ label: def.providerLabel
12499
+ };
12500
+ this.addedAt = def.addedAt ?? null;
12501
+ }
12502
+ };
12503
+ var ModelDescriptorImpl = class {
12504
+ id;
12505
+ name;
12506
+ api;
12507
+ def;
12508
+ _params;
12509
+ _meta;
12510
+ constructor(def) {
12511
+ this.id = def.id;
12512
+ this.name = def.name;
12513
+ this.api = { workflow: def.workflow, editWorkflow: def.editWorkflow };
12514
+ this.def = def;
12515
+ }
12516
+ params() {
12517
+ return this._params ??= new ModelParamsAccessorImpl(this.def);
12518
+ }
12519
+ paramsFor(values) {
12520
+ const inner = this.params();
12521
+ const effects = evaluateConstraints(this.def.constraints, values);
12522
+ if (!effects.size) return inner;
12523
+ return new ConstrainedParamsAccessor(inner, effects);
12524
+ }
12525
+ validate(input) {
12526
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
12527
+ return { valid: false, errors: [`Invalid input for model "${this.def.id}"`] };
12528
+ }
11993
12529
  try {
11994
- validateModelInput(resolveModel(model), input);
12530
+ validateAll(this.def.paramConfig, input);
11995
12531
  return { valid: true };
11996
12532
  } catch (err) {
11997
- const message = err instanceof Error ? err.message : String(err);
11998
- return { valid: false, errors: [message] };
12533
+ return { valid: false, errors: [err instanceof Error ? err.message : String(err)] };
11999
12534
  }
12000
- },
12001
- /** @deprecated Use `Model(id).params().toSchema()` instead. */
12002
- toSchema(id) {
12003
- return Model(id).params().toSchema();
12004
- },
12005
- /** @deprecated Use `Model(id).params().file(key)` instead. */
12006
- getFileParam(id, key) {
12007
- const f = Model(id).params().file(key);
12008
- if (!f) return null;
12009
- return { required: f.required ?? false, max: f.array?.max ?? 1, label: f.label, accept: f.accept };
12010
- },
12011
- /** @deprecated Use `Model(id).params().hasParam(key)` instead. */
12012
- hasParam(id, key) {
12013
- return Model(id).params().hasParam(key);
12535
+ }
12536
+ meta() {
12537
+ return this._meta ??= new ModelMetaImpl(this.def);
12538
+ }
12539
+ getCreditsInfo(ctx) {
12540
+ if (this.def.modelId) {
12541
+ const byModelId = getCreditsForModel(this.def.modelId, ctx);
12542
+ if (byModelId) return byModelId;
12543
+ }
12544
+ return getCreditsForModel(this.def.id, ctx);
12545
+ }
12546
+ };
12547
+ function _model(id) {
12548
+ return new ModelDescriptorImpl(resolveModel(id));
12549
+ }
12550
+ function _all(filter = {}) {
12551
+ const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
12552
+ return ALL_MODELS.filter((m) => isVisibleForReleases(m, releases)).map((m) => new ModelDescriptorImpl(m));
12553
+ }
12554
+ function _find(filter) {
12555
+ const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
12556
+ return ALL_MODELS.filter((m) => {
12557
+ if (!isVisibleForReleases(m, releases)) return false;
12558
+ if (filter.output && m.mode !== filter.output) return false;
12559
+ if (filter.provider && m.provider !== filter.provider) return false;
12560
+ return true;
12561
+ }).map((m) => new ModelDescriptorImpl(m));
12562
+ }
12563
+ function _search(query, filter = {}) {
12564
+ const releases = filter.release ?? DEFAULT_VISIBLE_RELEASES;
12565
+ const q = query.toLowerCase();
12566
+ return ALL_MODELS.filter(
12567
+ (m) => isVisibleForReleases(m, releases) && (m.id.toLowerCase().includes(q) || m.name.toLowerCase().includes(q) || m.provider.toLowerCase().includes(q))
12568
+ ).map((m) => new ModelDescriptorImpl(m));
12569
+ }
12570
+ var Model = _model;
12571
+ var catalog = {
12572
+ all: _all,
12573
+ find: _find,
12574
+ search: _search,
12575
+ pricing: {
12576
+ configure: configurePricing,
12577
+ load: loadPricing,
12578
+ isLoaded: isPricingLoaded
12014
12579
  }
12015
12580
  };
12016
12581
  function toBase64Url(bytes) {
@@ -12194,4 +12759,4 @@ function decodeDeepLinkPayload(encoded) {
12194
12759
  return deserializePayload(encoded);
12195
12760
  }
12196
12761
 
12197
- export { ALL_MODELS, ApiError, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, KLING_DUAL_IMAGE_EFFECTS, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
12762
+ export { ALL_MODELS, ApiError, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, GenerationEventType, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };