@omni-commerce/cli 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ function parseArgv(argv) {
50
50
  continue;
51
51
  }
52
52
  const next = argv[index + 1];
53
- if (!next || next.startsWith("-") && next !== "-" || GLOBAL_TRUE_FLAGS.has(stripped)) {
53
+ if (next === void 0 || next.startsWith("-") && next !== "-" || GLOBAL_TRUE_FLAGS.has(stripped)) {
54
54
  flags[stripped] = true;
55
55
  continue;
56
56
  }
@@ -78,13 +78,13 @@ import { execFile } from "child_process";
78
78
  import { promisify } from "util";
79
79
  var executeFile = promisify(execFile);
80
80
  async function openBrowser(url) {
81
- const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32.exe" : "xdg-open";
81
+ const command2 = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32.exe" : "xdg-open";
82
82
  const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
83
- await executeFile(command, args, { timeout: 1e4 });
83
+ await executeFile(command2, args, { timeout: 1e4 });
84
84
  }
85
85
 
86
86
  // src/version.ts
87
- var CLI_VERSION = "0.10.0";
87
+ var CLI_VERSION = "0.11.0";
88
88
 
89
89
  // src/browser-login.ts
90
90
  function loginOrigin(baseUrl) {
@@ -261,14 +261,14 @@ var UPLOAD_CONTENT_TYPES = {
261
261
  ".heic": "image/heic",
262
262
  ".heif": "image/heif"
263
263
  };
264
- async function uploadBody(filePath, fields) {
264
+ async function uploadBody(filePath, fields2) {
265
265
  const form = new FormData();
266
266
  const type = UPLOAD_CONTENT_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
267
267
  form.append(
268
268
  "file",
269
269
  new File([await readFile2(filePath)], basename(filePath), { type })
270
270
  );
271
- for (const [key, value] of Object.entries(fields)) {
271
+ for (const [key, value] of Object.entries(fields2)) {
272
272
  if (value === void 0 || value === null) continue;
273
273
  form.append(
274
274
  key,
@@ -283,8 +283,8 @@ function stringValue(value, flag) {
283
283
  }
284
284
  return value;
285
285
  }
286
- async function executeCommand(client, command, request) {
287
- if (command.resourcePath[0] === "jobs" && command.methodName === "wait") {
286
+ async function executeCommand(client, command2, request) {
287
+ if (command2.resourcePath[0] === "jobs" && command2.methodName === "wait") {
288
288
  const jobId = stringValue(request.jobId, "job-id");
289
289
  const organizationId = typeof request.organizationId === "string" ? request.organizationId : void 0;
290
290
  return client.jobs.wait({ jobId, organizationId });
@@ -292,7 +292,8 @@ async function executeCommand(client, command, request) {
292
292
  const pathValues = {};
293
293
  const query = {};
294
294
  const consumed = /* @__PURE__ */ new Set();
295
- for (const flag of command.flags) {
295
+ for (const flag of command2.flags) {
296
+ if (flag.source === "body") continue;
296
297
  const value = request[flag.name];
297
298
  if (value === void 0) continue;
298
299
  consumed.add(flag.name);
@@ -306,20 +307,20 @@ async function executeCommand(client, command, request) {
306
307
  for (const [key, value] of Object.entries(request)) {
307
308
  if (!consumed.has(key)) body[key] = value;
308
309
  }
309
- if (command.acceptsFile) {
310
- const { file, ...fields } = body;
310
+ if (command2.acceptsFile) {
311
+ const { file, ...fields2 } = body;
311
312
  return client.request({
312
- method: command.httpMethod,
313
- path: interpolatePath(command.path, pathValues),
313
+ method: command2.httpMethod,
314
+ path: interpolatePath(command2.path, pathValues),
314
315
  query: Object.keys(query).length > 0 ? query : void 0,
315
- body: await uploadBody(stringValue(file, "file"), fields)
316
+ body: await uploadBody(stringValue(file, "file"), fields2)
316
317
  });
317
318
  }
318
319
  return client.request({
319
- method: command.httpMethod,
320
- path: interpolatePath(command.path, pathValues),
320
+ method: command2.httpMethod,
321
+ path: interpolatePath(command2.path, pathValues),
321
322
  query: Object.keys(query).length > 0 ? query : void 0,
322
- body: command.acceptsJsonBody && Object.keys(body).length > 0 ? body : void 0
323
+ body: command2.acceptsJsonBody && Object.keys(body).length > 0 ? body : void 0
323
324
  });
324
325
  }
325
326
 
@@ -1408,6 +1409,18 @@ var COMMANDS = [
1408
1409
  required: false,
1409
1410
  source: "query"
1410
1411
  },
1412
+ {
1413
+ name: "marketplace",
1414
+ flag: "marketplace",
1415
+ required: false,
1416
+ source: "query"
1417
+ },
1418
+ {
1419
+ name: "store",
1420
+ flag: "store",
1421
+ required: false,
1422
+ source: "query"
1423
+ },
1411
1424
  {
1412
1425
  name: "stage",
1413
1426
  flag: "stage",
@@ -1480,6 +1493,25 @@ var COMMANDS = [
1480
1493
  acceptsFile: false,
1481
1494
  bodyRequired: false
1482
1495
  },
1496
+ {
1497
+ trail: ["campaigns", "update"],
1498
+ resourcePath: ["campaigns"],
1499
+ methodName: "update",
1500
+ httpMethod: "PATCH",
1501
+ path: "/api/v1/campaigns/{campaignId}",
1502
+ summary: "Change Campaign Details",
1503
+ flags: [
1504
+ {
1505
+ name: "campaignId",
1506
+ flag: "campaign-id",
1507
+ required: true,
1508
+ source: "path"
1509
+ }
1510
+ ],
1511
+ acceptsJsonBody: true,
1512
+ acceptsFile: false,
1513
+ bodyRequired: true
1514
+ },
1483
1515
  {
1484
1516
  trail: ["campaigns", "replace"],
1485
1517
  resourcePath: ["campaigns"],
@@ -1518,6 +1550,148 @@ var COMMANDS = [
1518
1550
  acceptsFile: false,
1519
1551
  bodyRequired: true
1520
1552
  },
1553
+ {
1554
+ trail: ["campaigns", "get-analytics"],
1555
+ resourcePath: ["campaigns"],
1556
+ methodName: "getAnalytics",
1557
+ httpMethod: "GET",
1558
+ path: "/api/v1/campaigns/{campaignId}/analytics",
1559
+ summary: "Get Campaign Analytics",
1560
+ flags: [
1561
+ {
1562
+ name: "campaignId",
1563
+ flag: "campaign-id",
1564
+ required: true,
1565
+ source: "path"
1566
+ },
1567
+ {
1568
+ name: "organizationId",
1569
+ flag: "organization-id",
1570
+ required: false,
1571
+ source: "query"
1572
+ },
1573
+ {
1574
+ name: "analyticsWindow",
1575
+ flag: "analytics-window",
1576
+ required: false,
1577
+ source: "query"
1578
+ }
1579
+ ],
1580
+ acceptsJsonBody: false,
1581
+ acceptsFile: false,
1582
+ bodyRequired: false
1583
+ },
1584
+ {
1585
+ trail: ["campaigns", "get-notifications"],
1586
+ resourcePath: ["campaigns"],
1587
+ methodName: "getNotifications",
1588
+ httpMethod: "GET",
1589
+ path: "/api/v1/campaigns/{campaignId}/notifications",
1590
+ summary: "Get Campaign Notifications",
1591
+ flags: [
1592
+ {
1593
+ name: "campaignId",
1594
+ flag: "campaign-id",
1595
+ required: true,
1596
+ source: "path"
1597
+ },
1598
+ {
1599
+ name: "organizationId",
1600
+ flag: "organization-id",
1601
+ required: false,
1602
+ source: "query"
1603
+ }
1604
+ ],
1605
+ acceptsJsonBody: false,
1606
+ acceptsFile: false,
1607
+ bodyRequired: false
1608
+ },
1609
+ {
1610
+ trail: ["campaigns", "set-notifications"],
1611
+ resourcePath: ["campaigns"],
1612
+ methodName: "setNotifications",
1613
+ httpMethod: "PUT",
1614
+ path: "/api/v1/campaigns/{campaignId}/notifications",
1615
+ summary: "Set Campaign Notifications",
1616
+ flags: [
1617
+ {
1618
+ name: "campaignId",
1619
+ flag: "campaign-id",
1620
+ required: true,
1621
+ source: "path"
1622
+ }
1623
+ ],
1624
+ acceptsJsonBody: true,
1625
+ acceptsFile: false,
1626
+ bodyRequired: true
1627
+ },
1628
+ {
1629
+ trail: ["campaigns", "list-offers"],
1630
+ resourcePath: ["campaigns"],
1631
+ methodName: "listOffers",
1632
+ httpMethod: "GET",
1633
+ path: "/api/v1/campaigns/{campaignId}/offers",
1634
+ summary: "List Campaign Offers",
1635
+ flags: [
1636
+ {
1637
+ name: "campaignId",
1638
+ flag: "campaign-id",
1639
+ required: true,
1640
+ source: "path"
1641
+ },
1642
+ {
1643
+ name: "kind",
1644
+ flag: "kind",
1645
+ required: false,
1646
+ source: "query"
1647
+ },
1648
+ {
1649
+ name: "window",
1650
+ flag: "window",
1651
+ required: false,
1652
+ source: "query"
1653
+ },
1654
+ {
1655
+ name: "review",
1656
+ flag: "review",
1657
+ required: false,
1658
+ source: "query"
1659
+ },
1660
+ {
1661
+ name: "offset",
1662
+ flag: "offset",
1663
+ required: false,
1664
+ source: "query"
1665
+ },
1666
+ {
1667
+ name: "limit",
1668
+ flag: "limit",
1669
+ required: false,
1670
+ source: "query"
1671
+ },
1672
+ {
1673
+ name: "organizationId",
1674
+ flag: "organization-id",
1675
+ required: false,
1676
+ source: "query"
1677
+ }
1678
+ ],
1679
+ acceptsJsonBody: false,
1680
+ acceptsFile: false,
1681
+ bodyRequired: false
1682
+ },
1683
+ {
1684
+ trail: ["campaigns", "act-batch"],
1685
+ resourcePath: ["campaigns"],
1686
+ methodName: "actBatch",
1687
+ httpMethod: "POST",
1688
+ path: "/api/v1/campaigns/actions",
1689
+ summary: "Act on Campaigns",
1690
+ flags: [],
1691
+ acceptsJsonBody: true,
1692
+ acceptsFile: false,
1693
+ bodyRequired: true
1694
+ },
1521
1695
  {
1522
1696
  trail: ["campaigns", "archive"],
1523
1697
  resourcePath: ["campaigns"],
@@ -1542,6 +1716,18 @@ var COMMANDS = [
1542
1716
  acceptsFile: false,
1543
1717
  bodyRequired: true
1544
1718
  },
1719
+ {
1720
+ trail: ["campaigns", "import-briefs"],
1721
+ resourcePath: ["campaigns"],
1722
+ methodName: "importBriefs",
1723
+ httpMethod: "POST",
1724
+ path: "/api/v1/campaigns/import",
1725
+ summary: "Import Campaign Workbooks",
1726
+ flags: [],
1727
+ acceptsJsonBody: true,
1728
+ acceptsFile: false,
1729
+ bodyRequired: true
1730
+ },
1545
1731
  {
1546
1732
  trail: ["campaigns", "get-options"],
1547
1733
  resourcePath: ["campaigns"],
@@ -1561,6 +1747,30 @@ var COMMANDS = [
1561
1747
  acceptsFile: false,
1562
1748
  bodyRequired: false
1563
1749
  },
1750
+ {
1751
+ trail: ["campaigns", "resolve-promotion-options"],
1752
+ resourcePath: ["campaigns"],
1753
+ methodName: "resolvePromotionOptions",
1754
+ httpMethod: "POST",
1755
+ path: "/api/v1/campaigns/promotion-options",
1756
+ summary: "Resolve Campaign Promotion Selection",
1757
+ flags: [],
1758
+ acceptsJsonBody: true,
1759
+ acceptsFile: false,
1760
+ bodyRequired: true
1761
+ },
1762
+ {
1763
+ trail: ["campaigns", "validate"],
1764
+ resourcePath: ["campaigns"],
1765
+ methodName: "validate",
1766
+ httpMethod: "POST",
1767
+ path: "/api/v1/campaigns/validate",
1768
+ summary: "Validate Campaigns",
1769
+ flags: [],
1770
+ acceptsJsonBody: true,
1771
+ acceptsFile: false,
1772
+ bodyRequired: true
1773
+ },
1564
1774
  {
1565
1775
  trail: ["catalog", "list-items"],
1566
1776
  resourcePath: ["catalog"],
@@ -2942,6 +3152,12 @@ var COMMANDS = [
2942
3152
  flag: "include-summary",
2943
3153
  required: false,
2944
3154
  source: "query"
3155
+ },
3156
+ {
3157
+ name: "detail",
3158
+ flag: "detail",
3159
+ required: false,
3160
+ source: "query"
2945
3161
  }
2946
3162
  ],
2947
3163
  acceptsJsonBody: false,
@@ -3407,6 +3623,12 @@ var COMMANDS = [
3407
3623
  required: false,
3408
3624
  source: "query"
3409
3625
  },
3626
+ {
3627
+ name: "campaign",
3628
+ flag: "campaign",
3629
+ required: false,
3630
+ source: "query"
3631
+ },
3410
3632
  {
3411
3633
  name: "q",
3412
3634
  flag: "q",
@@ -3547,6 +3769,25 @@ var COMMANDS = [
3547
3769
  acceptsFile: false,
3548
3770
  bodyRequired: true
3549
3771
  },
3772
+ {
3773
+ trail: ["promotions", "preview"],
3774
+ resourcePath: ["promotions"],
3775
+ methodName: "preview",
3776
+ httpMethod: "POST",
3777
+ path: "/api/v1/promotions/{promotionId}/preview",
3778
+ summary: "Preview Promotion Prices",
3779
+ flags: [
3780
+ {
3781
+ name: "promotionId",
3782
+ flag: "promotion-id",
3783
+ required: true,
3784
+ source: "path"
3785
+ }
3786
+ ],
3787
+ acceptsJsonBody: true,
3788
+ acceptsFile: false,
3789
+ bodyRequired: true
3790
+ },
3550
3791
  {
3551
3792
  trail: ["promotions", "remove-products"],
3552
3793
  resourcePath: ["promotions"],
@@ -3647,6 +3888,122 @@ var COMMANDS = [
3647
3888
  acceptsFile: false,
3648
3889
  bodyRequired: true
3649
3890
  },
3891
+ {
3892
+ trail: ["promotions", "create-batch"],
3893
+ resourcePath: ["promotions"],
3894
+ methodName: "createBatch",
3895
+ httpMethod: "POST",
3896
+ path: "/api/v1/promotions/batch",
3897
+ summary: "Create Promotions with Products",
3898
+ flags: [],
3899
+ acceptsJsonBody: true,
3900
+ acceptsFile: false,
3901
+ bodyRequired: true
3902
+ },
3903
+ {
3904
+ trail: ["promotions", "get-campaign-options"],
3905
+ resourcePath: ["promotions"],
3906
+ methodName: "getCampaignOptions",
3907
+ httpMethod: "GET",
3908
+ path: "/api/v1/promotions/campaign-options",
3909
+ summary: "Find Promotion Campaigns",
3910
+ flags: [
3911
+ {
3912
+ name: "organizationId",
3913
+ flag: "organization-id",
3914
+ required: false,
3915
+ source: "query"
3916
+ },
3917
+ {
3918
+ name: "q",
3919
+ flag: "q",
3920
+ required: false,
3921
+ source: "query"
3922
+ }
3923
+ ],
3924
+ acceptsJsonBody: false,
3925
+ acceptsFile: false,
3926
+ bodyRequired: false
3927
+ },
3928
+ {
3929
+ trail: ["promotions", "get-capabilities"],
3930
+ resourcePath: ["promotions"],
3931
+ methodName: "getCapabilities",
3932
+ httpMethod: "GET",
3933
+ path: "/api/v1/promotions/capabilities",
3934
+ summary: "Get Promotion Marketplace Capabilities",
3935
+ flags: [
3936
+ {
3937
+ name: "organizationId",
3938
+ flag: "organization-id",
3939
+ required: false,
3940
+ source: "query"
3941
+ }
3942
+ ],
3943
+ acceptsJsonBody: false,
3944
+ acceptsFile: false,
3945
+ bodyRequired: false
3946
+ },
3947
+ {
3948
+ trail: ["promotions", "delete-batch"],
3949
+ resourcePath: ["promotions"],
3950
+ methodName: "deleteBatch",
3951
+ httpMethod: "POST",
3952
+ path: "/api/v1/promotions/delete",
3953
+ summary: "Delete Promotions",
3954
+ flags: [],
3955
+ acceptsJsonBody: true,
3956
+ acceptsFile: false,
3957
+ bodyRequired: true
3958
+ },
3959
+ {
3960
+ trail: ["promotions", "get-delivery"],
3961
+ resourcePath: ["promotions"],
3962
+ methodName: "getDelivery",
3963
+ httpMethod: "POST",
3964
+ path: "/api/v1/promotions/delivery",
3965
+ summary: "Read Promotion Delivery",
3966
+ flags: [],
3967
+ acceptsJsonBody: true,
3968
+ acceptsFile: false,
3969
+ bodyRequired: true
3970
+ },
3971
+ {
3972
+ trail: ["promotions", "get-product-options"],
3973
+ resourcePath: ["promotions"],
3974
+ methodName: "getProductOptions",
3975
+ httpMethod: "POST",
3976
+ path: "/api/v1/promotions/product-options",
3977
+ summary: "Find Eligible Promotion Products",
3978
+ flags: [],
3979
+ acceptsJsonBody: true,
3980
+ acceptsFile: false,
3981
+ bodyRequired: true
3982
+ },
3983
+ {
3984
+ trail: ["promotions", "set-statuses"],
3985
+ resourcePath: ["promotions"],
3986
+ methodName: "setStatuses",
3987
+ httpMethod: "POST",
3988
+ path: "/api/v1/promotions/status",
3989
+ summary: "Set Promotion Statuses",
3990
+ flags: [],
3991
+ acceptsJsonBody: true,
3992
+ acceptsFile: false,
3993
+ bodyRequired: true
3994
+ },
3995
+ {
3996
+ trail: ["promotions", "validate"],
3997
+ resourcePath: ["promotions"],
3998
+ methodName: "validate",
3999
+ httpMethod: "POST",
4000
+ path: "/api/v1/promotions/validate",
4001
+ summary: "Validate Promotions",
4002
+ flags: [],
4003
+ acceptsJsonBody: true,
4004
+ acceptsFile: false,
4005
+ bodyRequired: true
4006
+ },
3650
4007
  {
3651
4008
  trail: ["returns", "list"],
3652
4009
  resourcePath: ["returns"],
@@ -4880,10 +5237,1064 @@ var COMMANDS = [
4880
5237
  }
4881
5238
  ];
4882
5239
 
5240
+ // src/generated/campaign-inputs.ts
5241
+ var CAMPAIGN_INPUTS = {
5242
+ patch: {
5243
+ type: "object",
5244
+ properties: {
5245
+ expectedRevision: {
5246
+ type: "integer",
5247
+ exclusiveMinimum: 0,
5248
+ maximum: 9007199254740991
5249
+ },
5250
+ name: {
5251
+ type: "string",
5252
+ minLength: 2,
5253
+ maxLength: 160
5254
+ },
5255
+ objective: {
5256
+ type: "string",
5257
+ maxLength: 8e3
5258
+ },
5259
+ brand: {
5260
+ type: "string",
5261
+ maxLength: 160
5262
+ },
5263
+ ownerId: {
5264
+ type: "string",
5265
+ minLength: 1,
5266
+ maxLength: 160,
5267
+ description: "Organization member ID or eligible oauth:<clientId> actor from campaign options."
5268
+ },
5269
+ approverId: {
5270
+ type: "string",
5271
+ minLength: 1,
5272
+ maxLength: 160,
5273
+ description: "Explicit named approver: organization member ID or eligible oauth:<clientId> actor from campaign options. Other users and API clients cannot approve on this actor's behalf."
5274
+ },
5275
+ timezone: {
5276
+ type: "string"
5277
+ },
5278
+ stores: {
5279
+ maxItems: 100,
5280
+ type: "array",
5281
+ items: {
5282
+ type: "object",
5283
+ properties: {
5284
+ marketplace: {
5285
+ type: "string",
5286
+ enum: [
5287
+ "shopee",
5288
+ "lazada",
5289
+ "tiktok",
5290
+ "shopify",
5291
+ "whatsapp",
5292
+ "zalora",
5293
+ "amazon",
5294
+ "ebay"
5295
+ ]
5296
+ },
5297
+ country: {
5298
+ type: "string",
5299
+ minLength: 1,
5300
+ maxLength: 80
5301
+ },
5302
+ storeId: {
5303
+ type: "string",
5304
+ minLength: 1,
5305
+ maxLength: 160
5306
+ }
5307
+ },
5308
+ required: ["marketplace", "country", "storeId"],
5309
+ additionalProperties: false
5310
+ }
5311
+ },
5312
+ serviceRequest: {
5313
+ type: "string",
5314
+ maxLength: 300
5315
+ },
5316
+ priority: {
5317
+ type: "string",
5318
+ enum: ["standard", "priority", "super_priority"]
5319
+ },
5320
+ documentIds: {
5321
+ maxItems: 100,
5322
+ type: "array",
5323
+ items: {
5324
+ type: "string",
5325
+ minLength: 1,
5326
+ maxLength: 160
5327
+ }
5328
+ },
5329
+ reviewDueAt: {
5330
+ anyOf: [
5331
+ {
5332
+ type: "string",
5333
+ format: "date-time",
5334
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5335
+ },
5336
+ {
5337
+ type: "null"
5338
+ }
5339
+ ]
5340
+ },
5341
+ sourceCurrency: {
5342
+ type: "string",
5343
+ pattern: "^[A-Z]{3}$"
5344
+ },
5345
+ sourceMarketplace: {
5346
+ type: "string",
5347
+ enum: [
5348
+ "shopee",
5349
+ "lazada",
5350
+ "tiktok",
5351
+ "shopify",
5352
+ "whatsapp",
5353
+ "zalora",
5354
+ "amazon",
5355
+ "ebay"
5356
+ ]
5357
+ },
5358
+ sourceCountry: {
5359
+ type: "string",
5360
+ maxLength: 80
5361
+ },
5362
+ syncMode: {
5363
+ type: "string",
5364
+ enum: ["offer_windows", "campaign_start"]
5365
+ },
5366
+ cleanupNotes: {
5367
+ type: "string",
5368
+ maxLength: 8e3
5369
+ },
5370
+ offers: {
5371
+ description: "Add or update promotions by stable offer id. Existing IDs patch only supplied fields; new IDs require windowId and promotion terms. Omitted offers stay unchanged.",
5372
+ maxItems: 500,
5373
+ type: "array",
5374
+ items: {
5375
+ type: "object",
5376
+ properties: {
5377
+ id: {
5378
+ type: "string",
5379
+ minLength: 1,
5380
+ maxLength: 160
5381
+ },
5382
+ windowId: {
5383
+ type: "string",
5384
+ minLength: 1,
5385
+ maxLength: 160
5386
+ },
5387
+ promotionId: {
5388
+ description: "Existing draft promotion attached to this window; the campaign controls its live dates.",
5389
+ type: "string",
5390
+ format: "uuid",
5391
+ pattern: "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
5392
+ },
5393
+ productIds: {
5394
+ maxItems: 200,
5395
+ type: "array",
5396
+ items: {
5397
+ type: "string",
5398
+ format: "uuid",
5399
+ pattern: "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
5400
+ }
5401
+ },
5402
+ reviewNotes: {
5403
+ type: "string",
5404
+ maxLength: 8e3
5405
+ },
5406
+ excludedReason: {
5407
+ type: "string",
5408
+ maxLength: 2e3
5409
+ },
5410
+ name: {
5411
+ type: "string",
5412
+ minLength: 1,
5413
+ maxLength: 200
5414
+ },
5415
+ description: {
5416
+ anyOf: [
5417
+ {
5418
+ type: "string",
5419
+ maxLength: 2e3
5420
+ },
5421
+ {
5422
+ type: "null"
5423
+ }
5424
+ ]
5425
+ },
5426
+ kind: {
5427
+ description: "Canonical OmniCommerce promotion mechanic: product discount, flash sale, voucher, gift with purchase, Buy-X-Get-Y, bundle deal, or Lazada-only free shipping and early bird price.",
5428
+ type: "string",
5429
+ enum: [
5430
+ "product_discount",
5431
+ "flash_sale",
5432
+ "voucher",
5433
+ "gift_with_purchase",
5434
+ "buy_x_get_y",
5435
+ "bundle_deal",
5436
+ "free_shipping",
5437
+ "early_bird_price"
5438
+ ]
5439
+ },
5440
+ salesChannels: {
5441
+ minItems: 1,
5442
+ type: "array",
5443
+ items: {
5444
+ type: "string",
5445
+ enum: ["pdp", "live"]
5446
+ }
5447
+ },
5448
+ discountType: {
5449
+ type: "string",
5450
+ enum: ["percentage", "fixed_amount"]
5451
+ },
5452
+ discountValue: {
5453
+ type: "number",
5454
+ minimum: 0
5455
+ },
5456
+ storeDiscounts: {
5457
+ description: "Patch discount terms for exact target store keys; unnamed stores remain unchanged. Null discountValue restores shared campaign terms for that store.",
5458
+ maxItems: 100,
5459
+ type: "array",
5460
+ items: {
5461
+ type: "object",
5462
+ properties: {
5463
+ storeKey: {
5464
+ type: "string",
5465
+ minLength: 1,
5466
+ maxLength: 255
5467
+ },
5468
+ discountType: {
5469
+ type: "string",
5470
+ enum: ["percentage", "fixed_amount"]
5471
+ },
5472
+ discountValue: {
5473
+ anyOf: [
5474
+ {
5475
+ type: "number",
5476
+ minimum: 0
5477
+ },
5478
+ {
5479
+ type: "null"
5480
+ }
5481
+ ]
5482
+ }
5483
+ },
5484
+ required: ["storeKey", "discountType", "discountValue"],
5485
+ additionalProperties: false
5486
+ }
5487
+ },
5488
+ targetStores: {
5489
+ description: "Up to 100 connected stores for create (required) or update. Copy marketplace, country, and storeId from get_promotions targetStores or find_products marketplaceDetails. Do not concatenate a storeKeys string.",
5490
+ minItems: 1,
5491
+ maxItems: 100,
5492
+ type: "array",
5493
+ items: {
5494
+ type: "object",
5495
+ properties: {
5496
+ marketplace: {
5497
+ type: "string",
5498
+ enum: [
5499
+ "lazada",
5500
+ "shopee",
5501
+ "tiktok",
5502
+ "shopify",
5503
+ "amazon",
5504
+ "zalora",
5505
+ "ebay"
5506
+ ]
5507
+ },
5508
+ country: {
5509
+ type: "string",
5510
+ minLength: 1,
5511
+ maxLength: 80
5512
+ },
5513
+ storeId: {
5514
+ type: "string",
5515
+ minLength: 1,
5516
+ maxLength: 120
5517
+ }
5518
+ },
5519
+ required: ["marketplace", "country", "storeId"],
5520
+ additionalProperties: false
5521
+ }
5522
+ },
5523
+ minSalePrice: {
5524
+ anyOf: [
5525
+ {
5526
+ type: "number",
5527
+ minimum: 0
5528
+ },
5529
+ {
5530
+ type: "null"
5531
+ }
5532
+ ]
5533
+ },
5534
+ maxDiscountAmount: {
5535
+ anyOf: [
5536
+ {
5537
+ type: "number",
5538
+ minimum: 0
5539
+ },
5540
+ {
5541
+ type: "null"
5542
+ }
5543
+ ]
5544
+ },
5545
+ minSpend: {
5546
+ anyOf: [
5547
+ {
5548
+ type: "number",
5549
+ minimum: 0
5550
+ },
5551
+ {
5552
+ type: "null"
5553
+ }
5554
+ ]
5555
+ },
5556
+ voucherCode: {
5557
+ anyOf: [
5558
+ {
5559
+ type: "string",
5560
+ minLength: 1,
5561
+ maxLength: 100
5562
+ },
5563
+ {
5564
+ type: "null"
5565
+ }
5566
+ ]
5567
+ },
5568
+ claimStartsAt: {
5569
+ anyOf: [
5570
+ {
5571
+ type: "string",
5572
+ format: "date-time",
5573
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5574
+ },
5575
+ {
5576
+ type: "null"
5577
+ }
5578
+ ]
5579
+ },
5580
+ totalUsageLimit: {
5581
+ anyOf: [
5582
+ {
5583
+ type: "integer",
5584
+ exclusiveMinimum: 0,
5585
+ maximum: 9007199254740991
5586
+ },
5587
+ {
5588
+ type: "null"
5589
+ }
5590
+ ]
5591
+ },
5592
+ perBuyerLimit: {
5593
+ anyOf: [
5594
+ {
5595
+ type: "integer",
5596
+ exclusiveMinimum: 0,
5597
+ maximum: 9007199254740991
5598
+ },
5599
+ {
5600
+ type: "null"
5601
+ }
5602
+ ]
5603
+ },
5604
+ voucherType: {
5605
+ description: "Voucher subtype from get_promotions capabilities.voucherTypes. Browser AOP types (delivery=browser_aop) are writable Seller Center vouchers. Omit browserAutomation.aopId to use the built-in marketplace AOP.",
5606
+ anyOf: [
5607
+ {
5608
+ type: "string",
5609
+ enum: [
5610
+ "shopee_shop",
5611
+ "shopee_product",
5612
+ "shopee_private",
5613
+ "shopee_live",
5614
+ "shopee_video",
5615
+ "shopee_new_buyer",
5616
+ "shopee_repeat_buyer",
5617
+ "shopee_campaign",
5618
+ "shopee_follow_prize",
5619
+ "shopee_exclusive_streamer",
5620
+ "shopee_welcome",
5621
+ "shopee_member_repeat_buyer",
5622
+ "shopee_exclusive",
5623
+ "shopee_redemption",
5624
+ "shopee_exclusive_creator",
5625
+ "lazada_store_follower",
5626
+ "lazada_regular",
5627
+ "lazada_store_new_buyer",
5628
+ "lazada_live",
5629
+ "lazada_offline",
5630
+ "lazada_co_funded",
5631
+ "lazada_welcome",
5632
+ "lazada_linked_member",
5633
+ "lazada_new_member",
5634
+ "lazada_member_exclusive",
5635
+ "lazada_points_redeemable",
5636
+ "tiktok_seller_coupon",
5637
+ "shopify_discount_code",
5638
+ "amazon_promotion_code",
5639
+ "zalora_seller_voucher",
5640
+ "ebay_coded_coupon"
5641
+ ]
5642
+ },
5643
+ {
5644
+ type: "null"
5645
+ }
5646
+ ]
5647
+ },
5648
+ voucherCampaign: {
5649
+ description: "Campaign voucher targeting: exact campaignId/sessionId from Seller Center and termsAccepted only after the seller reviewed participation obligations. Required for shopee_campaign; nomination creates a draft awaiting marketplace review.",
5650
+ type: "object",
5651
+ properties: {
5652
+ campaignId: {
5653
+ type: "string",
5654
+ pattern: "^[1-9]\\d{0,39}$"
5655
+ },
5656
+ sessionId: {
5657
+ type: "string",
5658
+ pattern: "^[1-9]\\d{0,39}$"
5659
+ },
5660
+ termsAccepted: {
5661
+ type: "boolean"
5662
+ }
5663
+ },
5664
+ required: ["campaignId", "sessionId", "termsAccepted"],
5665
+ additionalProperties: false
5666
+ },
5667
+ browserAutomation: {
5668
+ anyOf: [
5669
+ {
5670
+ type: "object",
5671
+ properties: {
5672
+ aopId: {
5673
+ type: "string",
5674
+ minLength: 1,
5675
+ maxLength: 160
5676
+ },
5677
+ variableValues: {
5678
+ type: "object",
5679
+ propertyNames: {
5680
+ type: "string",
5681
+ minLength: 1,
5682
+ maxLength: 160
5683
+ },
5684
+ additionalProperties: {
5685
+ type: "string",
5686
+ maxLength: 8e3
5687
+ }
5688
+ },
5689
+ instructions: {
5690
+ type: "string",
5691
+ maxLength: 8e3
5692
+ }
5693
+ },
5694
+ additionalProperties: false
5695
+ },
5696
+ {
5697
+ type: "null"
5698
+ }
5699
+ ]
5700
+ },
5701
+ voucherScope: {
5702
+ description: "shop = all products, products = selected assigned products. Repeat Buyer defaults to shop.",
5703
+ type: "string",
5704
+ enum: ["products", "shop"]
5705
+ },
5706
+ voucherReward: {
5707
+ description: "Seller Center reward type. coins_cashback is a percentage greater than 3 and does not reduce salePrice.",
5708
+ type: "string",
5709
+ enum: ["discount", "coins_cashback"]
5710
+ },
5711
+ voucherAudience: {
5712
+ type: "string",
5713
+ enum: ["general", "shop_follow"]
5714
+ },
5715
+ repeatPurchaseMinCount: {
5716
+ description: "Shopee Repeat Buyer: minimum completed shop orders, 1 or 2. Required for shopee_repeat_buyer.",
5717
+ type: "integer",
5718
+ minimum: 1,
5719
+ maximum: 999
5720
+ },
5721
+ repeatPurchaseLookbackDays: {
5722
+ description: "Shopee Repeat Buyer: lookback window in days, one of 30, 90, 180 or 365. Required for shopee_repeat_buyer.",
5723
+ type: "integer",
5724
+ minimum: 1,
5725
+ maximum: 365
5726
+ },
5727
+ smartVoucher: {
5728
+ description: "Shopee Smart Voucher toggle. Used by New Buyer and similar portal types.",
5729
+ type: "boolean"
5730
+ },
5731
+ followPrizeExpiryDays: {
5732
+ description: "Shopee Follow Prize: days after claim until expiry. Seller Center is 7.",
5733
+ type: "integer",
5734
+ minimum: 1,
5735
+ maximum: 30
5736
+ },
5737
+ videoDisplayMode: {
5738
+ description: "Shopee Video display: shopee_video = all Shopee Video; creator_video = targeted creator videos only.",
5739
+ type: "string",
5740
+ enum: ["shopee_video", "creator_video"]
5741
+ },
5742
+ exclusiveCreatorUsernames: {
5743
+ description: "Exclusive Creator Voucher: up to 15 Shopee usernames.",
5744
+ maxItems: 15,
5745
+ type: "array",
5746
+ items: {
5747
+ type: "string",
5748
+ minLength: 1,
5749
+ maxLength: 64
5750
+ }
5751
+ },
5752
+ exclusiveStreamerUsernames: {
5753
+ description: "Exclusive Streamer Voucher: up to 15 Shopee usernames.",
5754
+ maxItems: 15,
5755
+ type: "array",
5756
+ items: {
5757
+ type: "string",
5758
+ minLength: 1,
5759
+ maxLength: 64
5760
+ }
5761
+ },
5762
+ voucherDisplayChannels: {
5763
+ description: 'Shopee display setting. ["all"] = display on all pages; [] = hidden/share by code.',
5764
+ maxItems: 4,
5765
+ type: "array",
5766
+ items: {
5767
+ type: "string",
5768
+ enum: [
5769
+ "all",
5770
+ "feed",
5771
+ "live",
5772
+ "order_page",
5773
+ "followers",
5774
+ "offline"
5775
+ ]
5776
+ }
5777
+ },
5778
+ bundlePriceType: {
5779
+ type: "string",
5780
+ enum: ["discount", "fixed_price"]
5781
+ },
5782
+ bundleTiers: {
5783
+ maxItems: 2,
5784
+ type: "array",
5785
+ items: {
5786
+ type: "object",
5787
+ properties: {
5788
+ minimumQuantity: {
5789
+ type: "integer",
5790
+ minimum: 2,
5791
+ maximum: 3e3
5792
+ },
5793
+ discountValue: {
5794
+ type: "number",
5795
+ exclusiveMinimum: 0
5796
+ }
5797
+ },
5798
+ required: ["minimumQuantity", "discountValue"]
5799
+ }
5800
+ },
5801
+ thresholdType: {
5802
+ type: "string",
5803
+ enum: ["item_quantity", "order_amount"]
5804
+ },
5805
+ thresholdValue: {
5806
+ type: "number",
5807
+ exclusiveMinimum: 0
5808
+ },
5809
+ giftType: {
5810
+ type: "string",
5811
+ enum: ["gift", "sample"]
5812
+ },
5813
+ giftQuantity: {
5814
+ type: "integer",
5815
+ exclusiveMinimum: 0,
5816
+ maximum: 999
5817
+ },
5818
+ gifts: {
5819
+ minItems: 1,
5820
+ maxItems: 20,
5821
+ type: "array",
5822
+ items: {
5823
+ type: "object",
5824
+ properties: {
5825
+ productId: {
5826
+ type: "string",
5827
+ format: "uuid",
5828
+ pattern: "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
5829
+ },
5830
+ variantId: {
5831
+ anyOf: [
5832
+ {
5833
+ type: "string",
5834
+ format: "uuid",
5835
+ pattern: "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
5836
+ },
5837
+ {
5838
+ type: "null"
5839
+ }
5840
+ ]
5841
+ },
5842
+ totalClaimLimit: {
5843
+ type: "integer",
5844
+ minimum: 1,
5845
+ maximum: 99999
5846
+ }
5847
+ },
5848
+ required: ["productId", "totalClaimLimit"],
5849
+ additionalProperties: false
5850
+ }
5851
+ },
5852
+ buyQuantity: {
5853
+ type: "integer",
5854
+ exclusiveMinimum: 0,
5855
+ maximum: 999
5856
+ },
5857
+ getQuantity: {
5858
+ type: "integer",
5859
+ exclusiveMinimum: 0,
5860
+ maximum: 999
5861
+ },
5862
+ benefitProductIds: {
5863
+ minItems: 1,
5864
+ maxItems: 200,
5865
+ type: "array",
5866
+ items: {
5867
+ type: "string",
5868
+ format: "uuid",
5869
+ pattern: "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
5870
+ }
5871
+ },
5872
+ benefitType: {
5873
+ type: "string",
5874
+ enum: ["free", "percentage", "fixed_amount"]
5875
+ },
5876
+ benefitValue: {
5877
+ type: "number",
5878
+ minimum: 0
5879
+ },
5880
+ maxApplicationsPerOrder: {
5881
+ type: "integer",
5882
+ exclusiveMinimum: 0,
5883
+ maximum: 999
5884
+ },
5885
+ bundleMinimumQuantity: {
5886
+ type: "integer",
5887
+ minimum: 2,
5888
+ maximum: 3e3
5889
+ },
5890
+ shippingDeliveryOption: {
5891
+ description: "Free shipping delivery option. Lazada: STANDARD (default), ECONOMY, EXPRESS, SELLER_OWN_FLEET or P2P, as offered by the store.",
5892
+ type: "string",
5893
+ pattern: "^[A-Z0-9_]{1,40}$"
5894
+ },
5895
+ shippingBudget: {
5896
+ description: "Free shipping: total shipping subsidy budget in the store currency. Omit for an unlimited budget.",
5897
+ type: "number",
5898
+ exclusiveMinimum: 0
5899
+ }
5900
+ },
5901
+ required: ["id"],
5902
+ additionalProperties: false
5903
+ }
5904
+ },
5905
+ windows: {
5906
+ description: "Add or update windows by stable id. Existing IDs patch only supplied fields; new IDs require name, startsAt and endsAt. Omitted windows stay unchanged.",
5907
+ maxItems: 100,
5908
+ type: "array",
5909
+ items: {
5910
+ type: "object",
5911
+ properties: {
5912
+ id: {
5913
+ type: "string",
5914
+ minLength: 1,
5915
+ maxLength: 160
5916
+ },
5917
+ name: {
5918
+ type: "string",
5919
+ minLength: 1,
5920
+ maxLength: 160
5921
+ },
5922
+ startsAt: {
5923
+ anyOf: [
5924
+ {
5925
+ type: "string",
5926
+ format: "date-time",
5927
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5928
+ },
5929
+ {
5930
+ type: "null"
5931
+ }
5932
+ ]
5933
+ },
5934
+ endsAt: {
5935
+ anyOf: [
5936
+ {
5937
+ type: "string",
5938
+ format: "date-time",
5939
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5940
+ },
5941
+ {
5942
+ type: "null"
5943
+ }
5944
+ ]
5945
+ },
5946
+ preparesAt: {
5947
+ anyOf: [
5948
+ {
5949
+ type: "string",
5950
+ format: "date-time",
5951
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5952
+ },
5953
+ {
5954
+ type: "null"
5955
+ }
5956
+ ]
5957
+ },
5958
+ reviewNotes: {
5959
+ type: "string",
5960
+ maxLength: 8e3
5961
+ }
5962
+ },
5963
+ required: ["id"],
5964
+ additionalProperties: false
5965
+ }
5966
+ },
5967
+ removeWindowIds: {
5968
+ description: "Delete these windows. Move or remove their promotions in the same call. Maximum 100 IDs.",
5969
+ maxItems: 100,
5970
+ type: "array",
5971
+ items: {
5972
+ type: "string",
5973
+ minLength: 1,
5974
+ maxLength: 160
5975
+ }
5976
+ },
5977
+ removeOfferIds: {
5978
+ description: "Delete these draft promotion offers. Maximum 500 IDs.",
5979
+ maxItems: 500,
5980
+ type: "array",
5981
+ items: {
5982
+ type: "string",
5983
+ minLength: 1,
5984
+ maxLength: 160
5985
+ }
5986
+ },
5987
+ organizationId: {
5988
+ description: "Required for session auth. Optional for an API key, which already identifies the organization.",
5989
+ type: "string",
5990
+ minLength: 1,
5991
+ maxLength: 120
5992
+ },
5993
+ expectedPendingVersion: {
5994
+ anyOf: [
5995
+ {
5996
+ type: "integer",
5997
+ exclusiveMinimum: 0,
5998
+ maximum: 9007199254740991
5999
+ },
6000
+ {
6001
+ type: "null"
6002
+ }
6003
+ ]
6004
+ }
6005
+ },
6006
+ required: ["expectedRevision"],
6007
+ additionalProperties: false
6008
+ },
6009
+ action: {
6010
+ type: "object",
6011
+ properties: {
6012
+ action: {
6013
+ type: "string",
6014
+ enum: [
6015
+ "approve",
6016
+ "run",
6017
+ "approve_and_run",
6018
+ "end",
6019
+ "resume",
6020
+ "retry",
6021
+ "approve_change",
6022
+ "discard_change"
6023
+ ]
6024
+ },
6025
+ expectedRevision: {
6026
+ type: "integer",
6027
+ exclusiveMinimum: 0,
6028
+ maximum: 9007199254740991
6029
+ },
6030
+ expectedPendingVersion: {
6031
+ type: "integer",
6032
+ exclusiveMinimum: 0,
6033
+ maximum: 9007199254740991
6034
+ },
6035
+ organizationId: {
6036
+ description: "Required for session auth. Optional for an API key, which already identifies the organization.",
6037
+ type: "string",
6038
+ minLength: 1,
6039
+ maxLength: 120
6040
+ }
6041
+ },
6042
+ required: ["action", "expectedRevision"],
6043
+ additionalProperties: false
6044
+ }
6045
+ };
6046
+
6047
+ // src/body-flags.ts
6048
+ var kebab = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
6049
+ function nonNull(schema) {
6050
+ return schema.anyOf?.find((item) => item.type !== "null") ?? schema;
6051
+ }
6052
+ function nullable(schema) {
6053
+ return schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null") || Boolean(schema.anyOf?.some(nullable));
6054
+ }
6055
+ function bodyFlagNames(schema, prefix = "") {
6056
+ schema = nonNull(schema);
6057
+ if (schema.properties)
6058
+ return Object.entries(schema.properties).flatMap(
6059
+ ([name, child]) => bodyFlagNames(child, `${prefix ? `${prefix}.` : ""}${kebab(name)}`)
6060
+ );
6061
+ if (schema.items && nonNull(schema.items).properties)
6062
+ return bodyFlagNames(schema.items, `${prefix}.<index>`);
6063
+ if (typeof schema.additionalProperties === "object")
6064
+ return bodyFlagNames(schema.additionalProperties, `${prefix}.<key>`);
6065
+ return [prefix];
6066
+ }
6067
+ function scalar(value, schema, flag) {
6068
+ if (value === "null" && nullable(schema)) return null;
6069
+ schema = nonNull(schema);
6070
+ const type = Array.isArray(schema.type) ? schema.type.find((item) => item !== "null") : schema.type;
6071
+ let result = value;
6072
+ if (type === "array" && schema.items) {
6073
+ if (typeof value !== "string")
6074
+ throw new Error(`--${flag} requires comma-separated values`);
6075
+ result = value === "" ? [] : value.split(",").map((item) => scalar(item.trim(), schema.items, flag));
6076
+ const count = result.length;
6077
+ if (count < (schema.minItems ?? 0) || count > (schema.maxItems ?? Infinity))
6078
+ throw new Error(`--${flag} has an invalid number of values`);
6079
+ } else if (type === "boolean") {
6080
+ if (![true, false, "true", "false"].includes(value))
6081
+ throw new Error(`--${flag} requires true or false`);
6082
+ result = value === true || value === "true";
6083
+ } else if (type === "integer" || type === "number") {
6084
+ const number = typeof value === "string" && value.trim() ? Number(value) : NaN;
6085
+ if (!Number.isFinite(number) || type === "integer" && !Number.isSafeInteger(number) || number < (schema.minimum ?? -Infinity) || number > (schema.maximum ?? Infinity) || schema.exclusiveMinimum !== void 0 && number <= schema.exclusiveMinimum)
6086
+ throw new Error(`--${flag} requires a valid ${type}`);
6087
+ result = number;
6088
+ } else if (typeof value !== "string" || type === "object") {
6089
+ throw new Error(
6090
+ `--${flag} requires a value; use dotted fields for objects`
6091
+ );
6092
+ }
6093
+ if (schema.enum && !schema.enum.includes(result))
6094
+ throw new Error(
6095
+ `--${flag}${flag.endsWith(".marketplace") ? `: ${String(result)} is not supported for this store target; value` : ""} must be one of: ${schema.enum.join(", ")}`
6096
+ );
6097
+ if (typeof result === "string" && (result.length < (schema.minLength ?? 0) || result.length > (schema.maxLength ?? Infinity) || schema.pattern && !new RegExp(schema.pattern).test(result) || schema.format === "date-time" && (!/T.*(?:Z|[+-]\d{2}:\d{2})$/.test(result) || !Number.isFinite(Date.parse(result)))))
6098
+ throw new Error(
6099
+ `--${flag} has an invalid value${schema.format ? ` (${schema.format})` : ""}`
6100
+ );
6101
+ return result;
6102
+ }
6103
+ function assignFlag(target, schema, parts, value, flag) {
6104
+ schema = nonNull(schema);
6105
+ const [part, ...rest] = parts;
6106
+ if (["__proto__", "constructor", "prototype"].includes(part))
6107
+ throw new Error(`Unknown flag --${flag}`);
6108
+ const key = schema.items ? /^(0|[1-9][0-9]*)$/.test(part) ? Number(part) : void 0 : Object.keys(schema.properties ?? {}).find(
6109
+ (name) => kebab(name) === part
6110
+ ) ?? (typeof schema.additionalProperties === "object" ? part : void 0);
6111
+ const child = schema.items ?? (typeof key === "string" ? schema.properties?.[key] ?? schema.additionalProperties : void 0);
6112
+ if (key === void 0 || !child || typeof child !== "object" || typeof key === "number" && key >= (schema.maxItems ?? 500))
6113
+ throw new Error(`Unknown or out-of-bounds flag --${flag}`);
6114
+ const object = target;
6115
+ if (!rest.length) {
6116
+ object[key] = scalar(value, child, flag);
6117
+ return;
6118
+ }
6119
+ const shape = nonNull(child);
6120
+ if (!shape.properties && !shape.items && typeof shape.additionalProperties !== "object")
6121
+ throw new Error(`Unknown flag --${flag}`);
6122
+ object[key] ??= shape.items ? [] : {};
6123
+ if (typeof object[key] !== "object" || object[key] === null)
6124
+ throw new Error(`Conflicting flag --${flag}`);
6125
+ assignFlag(object[key], child, rest, value, flag);
6126
+ }
6127
+ function validateRequired(value, schema, prefix = "") {
6128
+ if (value === null) return;
6129
+ schema = nonNull(schema);
6130
+ if (Array.isArray(value) && schema.items) {
6131
+ for (let index = 0; index < value.length; index++) {
6132
+ if (!(index in value))
6133
+ throw new Error(`--${prefix} indices must be consecutive from 0`);
6134
+ validateRequired(value[index], schema.items, `${prefix}.${index}`);
6135
+ }
6136
+ } else if (value && typeof value === "object") {
6137
+ const object = value;
6138
+ for (const key of schema.required ?? [])
6139
+ if (object[key] === void 0)
6140
+ throw new Error(
6141
+ `Missing required flag --${prefix ? `${prefix}.` : ""}${kebab(key)}`
6142
+ );
6143
+ for (const [key, child] of Object.entries(schema.properties ?? {}))
6144
+ if (object[key] !== void 0)
6145
+ validateRequired(
6146
+ object[key],
6147
+ child,
6148
+ `${prefix ? `${prefix}.` : ""}${kebab(key)}`
6149
+ );
6150
+ }
6151
+ }
6152
+ var GLOBAL_FLAGS = /* @__PURE__ */ new Set([
6153
+ "api-key",
6154
+ "base-url",
6155
+ "timeout",
6156
+ "format",
6157
+ "version",
6158
+ "v"
6159
+ ]);
6160
+ function parseBodyFlags(flags, schema) {
6161
+ const result = {};
6162
+ for (const [flag, value] of Object.entries(flags)) {
6163
+ if (GLOBAL_FLAGS.has(flag)) continue;
6164
+ assignFlag(result, schema, flag.split("."), value, flag);
6165
+ }
6166
+ validateRequired(result, schema);
6167
+ return result;
6168
+ }
6169
+
6170
+ // src/campaign-commands.ts
6171
+ var patch = CAMPAIGN_INPUTS.patch;
6172
+ var fields = patch.properties;
6173
+ var guards = Object.fromEntries(
6174
+ ["organizationId", "expectedRevision", "expectedPendingVersion"].map(
6175
+ (key) => [key, fields[key]]
6176
+ )
6177
+ );
6178
+ var scope = { campaignId: { type: "string" } };
6179
+ function command(name, summary, schema, options = {}) {
6180
+ const inputSchema = {
6181
+ ...schema,
6182
+ properties: { ...scope, ...schema.properties },
6183
+ required: ["campaignId", ...schema.required ?? []]
6184
+ };
6185
+ return {
6186
+ trail: ["campaigns", name],
6187
+ resourcePath: ["campaigns"],
6188
+ methodName: name,
6189
+ httpMethod: options.action ? "POST" : "PATCH",
6190
+ path: `/api/v1/campaigns/{campaignId}${options.action ? "/actions" : ""}`,
6191
+ summary,
6192
+ flags: bodyFlagNames(inputSchema).map((flag) => ({
6193
+ name: flag === "campaign-id" ? "campaignId" : flag,
6194
+ flag,
6195
+ required: inputSchema.required.some((key) => kebab(key) === flag),
6196
+ source: flag === "campaign-id" ? "path" : "body"
6197
+ })),
6198
+ acceptsJsonBody: true,
6199
+ acceptsFile: false,
6200
+ bodyRequired: false,
6201
+ inputSchema,
6202
+ ...options
6203
+ };
6204
+ }
6205
+ var CAMPAIGN_COMMANDS = [
6206
+ command(
6207
+ "update",
6208
+ "Edit campaign fields, windows and offers. Omitted values stay unchanged; running edits are proposals.",
6209
+ patch
6210
+ ),
6211
+ ...["offers", "windows"].flatMap((target) => {
6212
+ const { id, ...properties } = fields[target].items.properties;
6213
+ const ids = target === "offers" ? "offerIds" : "windowIds";
6214
+ const selection = {
6215
+ type: "array",
6216
+ items: id,
6217
+ minItems: 1,
6218
+ maxItems: fields[target].maxItems
6219
+ };
6220
+ return [
6221
+ command(
6222
+ `update-${target}`,
6223
+ `Add or update ${target} by stable IDs with shared changes. Running edits are proposals.`,
6224
+ {
6225
+ type: "object",
6226
+ properties: { ...guards, [ids]: selection, ...properties },
6227
+ required: ["expectedRevision", ids]
6228
+ },
6229
+ { target }
6230
+ ),
6231
+ command(
6232
+ `remove-${target}`,
6233
+ `Remove selected ${target}. Move or remove offers before removing their window. Running edits are proposals.`,
6234
+ {
6235
+ type: "object",
6236
+ properties: { ...guards, [ids]: selection },
6237
+ required: ["expectedRevision", ids]
6238
+ },
6239
+ { target: target === "offers" ? "removeOfferIds" : "removeWindowIds" }
6240
+ )
6241
+ ];
6242
+ }),
6243
+ ...["approve_change", "discard_change"].map(
6244
+ (action) => command(
6245
+ action.replaceAll("_", "-"),
6246
+ action === "approve_change" ? "Approve the reviewed live proposal as its named approver and queue delivery." : "Discard the reviewed live proposal.",
6247
+ {
6248
+ type: "object",
6249
+ properties: {
6250
+ ...guards,
6251
+ expectedPendingVersion: CAMPAIGN_INPUTS.action.properties.expectedPendingVersion
6252
+ },
6253
+ required: ["expectedRevision", "expectedPendingVersion"]
6254
+ },
6255
+ { action }
6256
+ )
6257
+ )
6258
+ ];
6259
+ function isCampaignCommand(command2) {
6260
+ return "inputSchema" in command2;
6261
+ }
6262
+ function campaignRequest(command2, flags) {
6263
+ const input2 = parseBodyFlags(flags, command2.inputSchema);
6264
+ if (command2.action) return { ...input2, action: command2.action };
6265
+ if (!command2.target) {
6266
+ if (!Object.keys(input2).some(
6267
+ (key) => key !== "campaignId" && !(key in guards)
6268
+ ))
6269
+ throw new Error("Supply at least one field to change.");
6270
+ return input2;
6271
+ }
6272
+ const {
6273
+ campaignId,
6274
+ organizationId,
6275
+ expectedRevision,
6276
+ expectedPendingVersion,
6277
+ offerIds,
6278
+ windowIds,
6279
+ ...changes
6280
+ } = input2;
6281
+ const ids = [...new Set(offerIds ?? windowIds)];
6282
+ const removing = command2.target === "removeOfferIds" || command2.target === "removeWindowIds";
6283
+ if (!removing && !Object.keys(changes).length)
6284
+ throw new Error("Supply at least one field to change.");
6285
+ return {
6286
+ campaignId,
6287
+ organizationId,
6288
+ expectedRevision,
6289
+ expectedPendingVersion,
6290
+ [command2.target]: removing ? ids : ids.map((id) => ({ id, ...changes }))
6291
+ };
6292
+ }
6293
+
4883
6294
  // src/help.ts
4884
6295
  function printRootHelp(commands) {
4885
6296
  const groups = [
4886
- ...new Set(commands.map((command) => command.trail[0]))
6297
+ ...new Set(commands.map((command2) => command2.trail[0]))
4887
6298
  ].sort();
4888
6299
  return `OmniCommerce CLI
4889
6300
 
@@ -4923,33 +6334,42 @@ function printAuthHelp() {
4923
6334
  omni auth logout Remove saved credentials
4924
6335
  `;
4925
6336
  }
4926
- function printCommandHelp(command) {
4927
- const flags = command.flags.map((flag) => {
6337
+ function printCommandHelp(command2) {
6338
+ const flags = command2.flags.map((flag) => {
4928
6339
  const req = flag.required ? "required" : "optional";
4929
6340
  return ` --${flag.flag} <${flag.name}> ${req} ${flag.source}`;
4930
6341
  }).join("\n");
4931
- const jsonLine = `${command.acceptsFile ? " --file <path> required file to upload\n" : ""}${command.acceptsJsonBody ? ` --json <object|-> ${command.bodyRequired && !command.acceptsFile ? "required" : "optional"} body
6342
+ const jsonLine = `${command2.acceptsFile ? " --file <path> required file to upload\n" : ""}${command2.acceptsJsonBody && !isCampaignCommand(command2) ? ` --json <object|-> ${command2.bodyRequired && !command2.acceptsFile ? "required" : "optional"} body
4932
6343
  ` : ""}`;
4933
- return `omni ${command.trail.join(" ")}
6344
+ return `omni ${command2.trail.join(" ")}
4934
6345
 
4935
- ${command.summary}
6346
+ ${command2.summary}
4936
6347
 
4937
- ${command.httpMethod} ${command.path}
6348
+ ${command2.httpMethod} ${command2.path}
4938
6349
 
4939
6350
  Flags:
4940
6351
  ${flags || " (none)"}${jsonLine ? `
4941
6352
  ${jsonLine}` : ""}
6353
+ ${isCampaignCommand(command2) ? "\nArrays use comma-separated values (empty string clears). Object arrays use .0., .1., ... in flag names. Nullable fields accept null. Times require ISO 8601 with an explicit offset. Use --schema for types and allowed values.\n" : ""}
4942
6354
  `;
4943
6355
  }
4944
6356
  function matchCommands(commands, trail) {
4945
6357
  const prefix = commands.filter(
4946
- (command) => trail.every((token, index) => command.trail[index] === token)
6358
+ (command2) => trail.every((token, index) => command2.trail[index] === token)
4947
6359
  );
4948
- const exact = prefix.find((command) => command.trail.length === trail.length);
6360
+ const exact = prefix.find((command2) => command2.trail.length === trail.length);
4949
6361
  return { exact, prefix };
4950
6362
  }
4951
6363
 
4952
6364
  // src/run.ts
6365
+ var CLI_COMMANDS = [
6366
+ ...COMMANDS.filter(
6367
+ (command2) => !CAMPAIGN_COMMANDS.some(
6368
+ (custom) => custom.trail.join(" ") === command2.trail.join(" ")
6369
+ )
6370
+ ),
6371
+ ...CAMPAIGN_COMMANDS
6372
+ ];
4953
6373
  async function readStdin() {
4954
6374
  const chunks = [];
4955
6375
  for await (const chunk of input) {
@@ -4966,9 +6386,9 @@ async function parseJsonBody(raw) {
4966
6386
  }
4967
6387
  return parsed;
4968
6388
  }
4969
- function buildRequest(command, flags, body) {
6389
+ function buildRequest(command2, flags, body) {
4970
6390
  const request = { ...body };
4971
- for (const flag of command.flags) {
6391
+ for (const flag of command2.flags) {
4972
6392
  const value = flagString(flags, flag.flag);
4973
6393
  if (value === void 0) {
4974
6394
  if (flag.required) {
@@ -5037,19 +6457,19 @@ async function runCli(argv, options) {
5037
6457
  return handleAuth(parsed.trail, parsed.flags, options);
5038
6458
  }
5039
6459
  if (parsed.trail.length === 0) {
5040
- process.stdout.write(printRootHelp(COMMANDS));
6460
+ process.stdout.write(printRootHelp(CLI_COMMANDS));
5041
6461
  return parsed.help ? 0 : 1;
5042
6462
  }
5043
- const matched = matchCommands(COMMANDS, parsed.trail);
6463
+ const matched = matchCommands(CLI_COMMANDS, parsed.trail);
5044
6464
  if (parsed.help && !matched.exact) {
5045
6465
  if (matched.prefix.length === 0) {
5046
6466
  process.stderr.write(`Unknown command: ${parsed.trail.join(" ")}
5047
6467
  `);
5048
- process.stdout.write(printRootHelp(COMMANDS));
6468
+ process.stdout.write(printRootHelp(CLI_COMMANDS));
5049
6469
  return 1;
5050
6470
  }
5051
6471
  const unique = [
5052
- ...new Set(matched.prefix.map((command2) => command2.trail.join(" ")))
6472
+ ...new Set(matched.prefix.map((command3) => command3.trail.join(" ")))
5053
6473
  ];
5054
6474
  process.stdout.write(
5055
6475
  `omni ${parsed.trail.join(" ")}
@@ -5060,38 +6480,42 @@ ${unique.map((line) => ` omni ${line}`).join("\n")}
5060
6480
  );
5061
6481
  return 0;
5062
6482
  }
5063
- const command = matched.exact;
5064
- if (!command) {
6483
+ const command2 = matched.exact;
6484
+ if (!command2) {
5065
6485
  process.stderr.write(`Unknown command: ${parsed.trail.join(" ")}
5066
6486
  `);
5067
- process.stdout.write(printRootHelp(COMMANDS));
6487
+ process.stdout.write(printRootHelp(CLI_COMMANDS));
5068
6488
  return 1;
5069
6489
  }
5070
6490
  if (parsed.help) {
5071
- process.stdout.write(printCommandHelp(command));
6491
+ process.stdout.write(printCommandHelp(command2));
5072
6492
  return 0;
5073
6493
  }
5074
6494
  if (parsed.schema) {
5075
- process.stdout.write(`${JSON.stringify(command, null, 2)}
6495
+ process.stdout.write(`${JSON.stringify(command2, null, 2)}
5076
6496
  `);
5077
6497
  return 0;
5078
6498
  }
5079
6499
  const body = await parseJsonBody(flagString(parsed.flags, "json"));
5080
- const filePath = command.acceptsFile ? flagString(parsed.flags, "file") : void 0;
5081
- if (command.acceptsFile && !filePath) {
5082
- throw new Error(`omni ${command.trail.join(" ")} requires --file <path>`);
6500
+ const filePath = command2.acceptsFile ? flagString(parsed.flags, "file") : void 0;
6501
+ if (command2.acceptsFile && !filePath) {
6502
+ throw new Error(`omni ${command2.trail.join(" ")} requires --file <path>`);
5083
6503
  }
5084
- if (command.bodyRequired && !body && !filePath) {
5085
- throw new Error(`omni ${command.trail.join(" ")} requires --json`);
6504
+ if (command2.bodyRequired && !body && !filePath) {
6505
+ throw new Error(`omni ${command2.trail.join(" ")} requires --json`);
5086
6506
  }
5087
- const request = buildRequest(command, parsed.flags, body);
6507
+ if (isCampaignCommand(command2) && body)
6508
+ throw new Error(
6509
+ "Use campaign field flags for this command; --json is available on campaigns replace and actions."
6510
+ );
6511
+ const request = isCampaignCommand(command2) ? campaignRequest(command2, parsed.flags) : buildRequest(command2, parsed.flags, body);
5088
6512
  if (filePath) request.file = filePath;
5089
6513
  if (parsed.dryRun) {
5090
6514
  process.stdout.write(
5091
6515
  `${JSON.stringify(
5092
6516
  {
5093
- method: command.httpMethod,
5094
- path: command.path,
6517
+ method: command2.httpMethod,
6518
+ path: command2.path,
5095
6519
  request
5096
6520
  },
5097
6521
  null,
@@ -5112,7 +6536,7 @@ ${unique.map((line) => ` omni ${line}`).join("\n")}
5112
6536
  return 1;
5113
6537
  }
5114
6538
  const timeoutRaw = flagString(parsed.flags, "timeout");
5115
- const timeoutInSeconds = timeoutRaw ? Number(timeoutRaw) : command.path === "/api/v1/agents/general/invoke" ? 780 : void 0;
6539
+ const timeoutInSeconds = timeoutRaw ? Number(timeoutRaw) : command2.path === "/api/v1/agents/general/invoke" ? 780 : void 0;
5116
6540
  const client = new OmniClient({
5117
6541
  apiKey: credentials.apiKey,
5118
6542
  baseUrl: credentials.baseUrl,
@@ -5120,7 +6544,7 @@ ${unique.map((line) => ` omni ${line}`).join("\n")}
5120
6544
  fetch: options?.fetch
5121
6545
  });
5122
6546
  try {
5123
- const result = await executeCommand(client, command, request);
6547
+ const result = await executeCommand(client, command2, request);
5124
6548
  const format = resolveFormat(
5125
6549
  flagString(parsed.flags, "format"),
5126
6550
  options?.stdoutIsTty ?? Boolean(process.stdout.isTTY)