@drip-apex/apex-cli 0.15.2 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +1102 -36
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7325,7 +7325,7 @@ async function checkForUpdate(current) {
7325
7325
  // package.json
7326
7326
  var package_default = {
7327
7327
  name: "@drip-apex/apex-cli",
7328
- version: "0.15.2",
7328
+ version: "0.15.3",
7329
7329
  license: "MIT",
7330
7330
  description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
7331
7331
  private: false,
@@ -7589,7 +7589,8 @@ function classifyApiError(status4, details) {
7589
7589
  const code = details?.code;
7590
7590
  if (code === "shop_context_required" || code === "org_context_required")
7591
7591
  return EXIT_CODES.validation;
7592
- if (code === "shop_access_denied" || code === "org_access_denied") return EXIT_CODES.auth;
7592
+ if (code === "shop_access_denied" || code === "org_access_denied" || code === "member_suspended")
7593
+ return EXIT_CODES.auth;
7593
7594
  return classifyStatus(status4);
7594
7595
  }
7595
7596
  function confirmationRequired(command, message, retry) {
@@ -7686,7 +7687,7 @@ function errorReason(error) {
7686
7687
  const body5 = error.api?.body;
7687
7688
  const code = body5?.code ?? body5?.error?.code ?? (error.api ? details?.code : void 0);
7688
7689
  if (typeof code === "string" && code) return code;
7689
- if (["shop_context_required", "local_file_error", "validation_failed"].includes(
7690
+ if (["shop_context_required", "local_file_error", "validation_failed", "member_suspended"].includes(
7690
7691
  String(details?.code)
7691
7692
  ))
7692
7693
  return String(details?.code);
@@ -7704,6 +7705,13 @@ function errorReason(error) {
7704
7705
  function errorNextSteps(error) {
7705
7706
  const reason2 = errorReason(error);
7706
7707
  const steps = [];
7708
+ if (reason2 === "member_suspended")
7709
+ return [
7710
+ {
7711
+ command: "contact organization admin",
7712
+ why: "Ask an organization owner or admin to reinstate your membership."
7713
+ }
7714
+ ];
7707
7715
  if (reason2 === "auth_required" || error.api?.status === 401) {
7708
7716
  steps.push({ command: "apex login", why: "Authenticate with a valid API key." });
7709
7717
  if (!error.api)
@@ -8438,6 +8446,7 @@ function listExperiments(creds, opts) {
8438
8446
  if (opts?.includeArchived) params.set("archived", "1");
8439
8447
  if (opts?.view) params.set("view", opts.view);
8440
8448
  if (opts?.fields !== void 0) params.set("fields", opts.fields);
8449
+ if (opts?.url !== void 0) params.set("url", opts.url);
8441
8450
  const qs = params.toString();
8442
8451
  return request(creds, "GET", `/api/v1/experiments${qs ? `?${qs}` : ""}`);
8443
8452
  }
@@ -8485,6 +8494,8 @@ function getExperimentFilesystem(creds, id) {
8485
8494
  function getExperimentResults(creds, id, opts) {
8486
8495
  const params = new URLSearchParams();
8487
8496
  if (opts?.currency) params.set("currency", opts.currency);
8497
+ for (const key of ["segment", "exclude_segment"])
8498
+ if (opts?.[key] !== void 0) params.set(key, opts[key]);
8488
8499
  const qs = params.toString();
8489
8500
  return request(
8490
8501
  creds,
@@ -8538,6 +8549,17 @@ function applyExperimentFilesystem(creds, id, body5) {
8538
8549
  function getExperimentQaRun(creds, id) {
8539
8550
  return request(creds, "GET", `/api/v1/experiments/${id}/qa/run`);
8540
8551
  }
8552
+ function getExperimentQaEvents(creds, id, options = {}) {
8553
+ const params = new URLSearchParams();
8554
+ if (options.session) params.set("qa_session", options.session);
8555
+ if (options.minutes !== void 0) params.set("minutes", String(options.minutes));
8556
+ if (options.type) params.set("type", options.type);
8557
+ return request(
8558
+ creds,
8559
+ "GET",
8560
+ `/api/v1/experiments/${id}/qa/events${params.size ? `?${params}` : ""}`
8561
+ );
8562
+ }
8541
8563
  function updateExperimentQaRun(creds, id, body5) {
8542
8564
  return request(creds, "PATCH", `/api/v1/experiments/${id}/qa/run`, body5);
8543
8565
  }
@@ -8891,6 +8913,12 @@ function createAdminCustomer(creds, body5) {
8891
8913
  function setAdminCustomerCategory(creds, orgId, body5) {
8892
8914
  return request(userCredentials(creds), "PATCH", `/api/v1/admin/customers/${orgId}`, body5);
8893
8915
  }
8916
+ function setAdminCustomerManagedBy(creds, orgId, body5) {
8917
+ return request(userCredentials(creds), "PATCH", `/api/v1/admin/customers/${orgId}`, body5);
8918
+ }
8919
+ function setAdminCustomerWorkspaceType(creds, orgId, body5) {
8920
+ return request(userCredentials(creds), "PATCH", `/api/v1/admin/customers/${orgId}`, body5);
8921
+ }
8894
8922
  function getAdminSettings(creds) {
8895
8923
  return request(userCredentials(creds), "GET", "/api/v1/admin/settings");
8896
8924
  }
@@ -9045,6 +9073,17 @@ function revokeOrgInvite(creds, id) {
9045
9073
  function setOrgMember(creds, id, body5) {
9046
9074
  return request(userCreds(creds), "PATCH", `/api/v1/org/members/${encodeURIComponent(id)}`, body5);
9047
9075
  }
9076
+ function suspendOrgMember(creds, id, reason2) {
9077
+ return request(userCreds(creds), "PATCH", `/api/v1/org/members/${encodeURIComponent(id)}`, {
9078
+ suspended: true,
9079
+ ...reason2 ? { reason: reason2 } : {}
9080
+ });
9081
+ }
9082
+ function reinstateOrgMember(creds, id) {
9083
+ return request(userCreds(creds), "PATCH", `/api/v1/org/members/${encodeURIComponent(id)}`, {
9084
+ suspended: false
9085
+ });
9086
+ }
9048
9087
  function removeOrgMember(creds, id) {
9049
9088
  return request(userCreds(creds), "DELETE", `/api/v1/org/members/${encodeURIComponent(id)}`);
9050
9089
  }
@@ -9188,6 +9227,14 @@ function getOrderJourney(creds, opts) {
9188
9227
  45e3
9189
9228
  );
9190
9229
  }
9230
+ function moveAdminCustomerStores(creds, orgId, body5) {
9231
+ return request(
9232
+ userCredentials(creds),
9233
+ "POST",
9234
+ `/api/v1/admin/customers/${orgId}/move-stores`,
9235
+ body5
9236
+ );
9237
+ }
9191
9238
 
9192
9239
  // src/context.ts
9193
9240
  var import_node_readline = __toESM(require("node:readline"));
@@ -9399,10 +9446,14 @@ async function waitForBrowserLogin(baseUrl, userMode = false) {
9399
9446
  const returnedState = params.get("state");
9400
9447
  const apiKey = params.get("apiKey");
9401
9448
  const error = params.get("error");
9449
+ const code = params.get("code");
9402
9450
  if (error) {
9403
9451
  res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
9404
9452
  res.end("<h1>Apex CLI login failed</h1><p>You can close this window.</p>");
9405
- finish(new Error(error));
9453
+ const details = code ? { code } : void 0;
9454
+ finish(
9455
+ details ? new CliError(error, classifyApiError(400, details), details) : new Error(error)
9456
+ );
9406
9457
  return;
9407
9458
  }
9408
9459
  if (returnedState !== state) {
@@ -9486,6 +9537,7 @@ var loginCommand = new Command("login").description("Authenticate with Apex").op
9486
9537
  try {
9487
9538
  apiKey = await waitForBrowserLogin(baseUrl, true);
9488
9539
  } catch (err) {
9540
+ if (err instanceof CliError) throw err;
9489
9541
  fail(this, err instanceof Error ? err.message : "Browser login failed.");
9490
9542
  }
9491
9543
  }
@@ -9777,13 +9829,18 @@ function buildQaPreviewUrl(experiment2, options = {}) {
9777
9829
  }
9778
9830
 
9779
9831
  // src/lib/qa-preview-url.ts
9780
- function buildQaPreviewUrl2(storeUrl, experimentId, variationId) {
9781
- const url = buildQaPreviewUrl({
9782
- id: experimentId,
9783
- store_url: storeUrl,
9784
- variations: [{ id: variationId, is_control: false }]
9785
- });
9832
+ function buildQaPreviewUrl2(storeUrl, experimentId, variationId, qaSessionId) {
9833
+ const url = buildQaPreviewUrl(
9834
+ {
9835
+ id: experimentId,
9836
+ store_url: storeUrl,
9837
+ variations: [{ id: variationId, is_control: false }]
9838
+ },
9839
+ { qaSessionId }
9840
+ );
9786
9841
  if (!url) throw new TypeError("Invalid QA preview URL");
9842
+ if (qaSessionId && url.searchParams.get("drip_qa_session") !== qaSessionId)
9843
+ url.searchParams.set("drip_qa_session", qaSessionId);
9787
9844
  return url;
9788
9845
  }
9789
9846
 
@@ -10181,6 +10238,755 @@ function createAssetReferenceRewriter(options) {
10181
10238
  return { rewrite, rewriteUnknown };
10182
10239
  }
10183
10240
 
10241
+ // src/generated/experiment-filesystem.schema.json
10242
+ var experiment_filesystem_schema_default = {
10243
+ $id: "https://api.drip-apex.com/schemas/experiment-filesystem-v1.json",
10244
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10245
+ files: {
10246
+ "experiment.json": {
10247
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10248
+ additionalProperties: false,
10249
+ properties: {
10250
+ autoStopOverride: {
10251
+ anyOf: [
10252
+ {},
10253
+ {
10254
+ type: "null"
10255
+ }
10256
+ ]
10257
+ },
10258
+ countingType: {
10259
+ anyOf: [
10260
+ {
10261
+ enum: ["standard", "setup_test", "aa_test", "non_program"],
10262
+ type: "string"
10263
+ },
10264
+ {
10265
+ type: "null"
10266
+ }
10267
+ ]
10268
+ },
10269
+ description: {
10270
+ anyOf: [
10271
+ {
10272
+ type: "string"
10273
+ },
10274
+ {
10275
+ type: "null"
10276
+ }
10277
+ ]
10278
+ },
10279
+ environmentKey: {
10280
+ type: "string"
10281
+ },
10282
+ experimentType: {
10283
+ enum: ["mutation", "redirect"],
10284
+ type: "string"
10285
+ },
10286
+ id: {
10287
+ anyOf: [
10288
+ {
10289
+ type: "string"
10290
+ },
10291
+ {
10292
+ type: "null"
10293
+ }
10294
+ ]
10295
+ },
10296
+ name: {
10297
+ type: "string"
10298
+ },
10299
+ schemaVersion: {
10300
+ const: 1,
10301
+ type: "number"
10302
+ },
10303
+ sequentialTestingEnabled: {
10304
+ type: "boolean"
10305
+ },
10306
+ significanceLevel: {
10307
+ type: "number"
10308
+ },
10309
+ slug: {
10310
+ anyOf: [
10311
+ {
10312
+ type: "string"
10313
+ },
10314
+ {
10315
+ type: "null"
10316
+ }
10317
+ ]
10318
+ },
10319
+ statisticalPower: {
10320
+ type: "number"
10321
+ },
10322
+ storeUrl: {
10323
+ anyOf: [
10324
+ {
10325
+ type: "string"
10326
+ },
10327
+ {
10328
+ type: "null"
10329
+ }
10330
+ ]
10331
+ },
10332
+ testKey: {
10333
+ anyOf: [
10334
+ {
10335
+ type: "string"
10336
+ },
10337
+ {
10338
+ type: "null"
10339
+ }
10340
+ ]
10341
+ },
10342
+ trafficAllocation: {
10343
+ maximum: 1,
10344
+ minimum: 0,
10345
+ type: "number"
10346
+ }
10347
+ },
10348
+ required: ["schemaVersion"],
10349
+ type: "object"
10350
+ },
10351
+ "experiment/index.css": {
10352
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10353
+ contentMediaType: "text/css",
10354
+ type: "string"
10355
+ },
10356
+ "experiment/index.js": {
10357
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10358
+ contentMediaType: "text/javascript",
10359
+ type: "string"
10360
+ },
10361
+ "experiment/reset.js": {
10362
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10363
+ contentMediaType: "text/javascript",
10364
+ type: "string"
10365
+ },
10366
+ "launch.json": {
10367
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10368
+ additionalProperties: false,
10369
+ properties: {
10370
+ endedAt: {
10371
+ anyOf: [
10372
+ {
10373
+ type: "string"
10374
+ },
10375
+ {
10376
+ type: "null"
10377
+ }
10378
+ ]
10379
+ },
10380
+ launchApproval: {
10381
+ anyOf: [
10382
+ {
10383
+ additionalProperties: false,
10384
+ properties: {
10385
+ decidedAt: {
10386
+ anyOf: [
10387
+ {
10388
+ type: "string"
10389
+ },
10390
+ {
10391
+ type: "null"
10392
+ }
10393
+ ]
10394
+ },
10395
+ note: {
10396
+ anyOf: [
10397
+ {
10398
+ type: "string"
10399
+ },
10400
+ {
10401
+ type: "null"
10402
+ }
10403
+ ]
10404
+ },
10405
+ status: {
10406
+ type: "string"
10407
+ }
10408
+ },
10409
+ required: ["status", "decidedAt", "note"],
10410
+ type: "object"
10411
+ },
10412
+ {
10413
+ type: "null"
10414
+ }
10415
+ ]
10416
+ },
10417
+ launchGate: {
10418
+ additionalProperties: false,
10419
+ properties: {
10420
+ qaApproved: {
10421
+ type: "boolean"
10422
+ },
10423
+ treatmentArtifactsApproved: {
10424
+ type: "boolean"
10425
+ }
10426
+ },
10427
+ required: ["qaApproved", "treatmentArtifactsApproved"],
10428
+ type: "object"
10429
+ },
10430
+ readonly: {
10431
+ const: true,
10432
+ type: "boolean"
10433
+ },
10434
+ scheduledEndAt: {
10435
+ anyOf: [
10436
+ {
10437
+ type: "string"
10438
+ },
10439
+ {
10440
+ type: "null"
10441
+ }
10442
+ ]
10443
+ },
10444
+ scheduledStartAt: {
10445
+ anyOf: [
10446
+ {
10447
+ type: "string"
10448
+ },
10449
+ {
10450
+ type: "null"
10451
+ }
10452
+ ]
10453
+ },
10454
+ schemaVersion: {
10455
+ const: 1,
10456
+ type: "number"
10457
+ },
10458
+ startedAt: {
10459
+ anyOf: [
10460
+ {
10461
+ type: "string"
10462
+ },
10463
+ {
10464
+ type: "null"
10465
+ }
10466
+ ]
10467
+ },
10468
+ status: {
10469
+ type: "string"
10470
+ }
10471
+ },
10472
+ readOnly: true,
10473
+ required: [
10474
+ "schemaVersion",
10475
+ "readonly",
10476
+ "status",
10477
+ "scheduledStartAt",
10478
+ "scheduledEndAt",
10479
+ "startedAt",
10480
+ "endedAt",
10481
+ "launchApproval",
10482
+ "launchGate"
10483
+ ],
10484
+ type: "object"
10485
+ },
10486
+ "metrics.json": {
10487
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10488
+ additionalProperties: false,
10489
+ properties: {
10490
+ goals: {
10491
+ items: {
10492
+ additionalProperties: false,
10493
+ properties: {
10494
+ capture: {
10495
+ anyOf: [
10496
+ {},
10497
+ {
10498
+ type: "null"
10499
+ }
10500
+ ]
10501
+ },
10502
+ countOnce: {
10503
+ type: "boolean"
10504
+ },
10505
+ eventName: {
10506
+ anyOf: [
10507
+ {
10508
+ type: "string"
10509
+ },
10510
+ {
10511
+ type: "null"
10512
+ }
10513
+ ]
10514
+ },
10515
+ filter: {
10516
+ anyOf: [
10517
+ {},
10518
+ {
10519
+ type: "null"
10520
+ }
10521
+ ]
10522
+ },
10523
+ id: {
10524
+ anyOf: [
10525
+ {
10526
+ type: "string"
10527
+ },
10528
+ {
10529
+ type: "null"
10530
+ }
10531
+ ]
10532
+ },
10533
+ isPrimary: {
10534
+ type: "boolean"
10535
+ },
10536
+ measurement: {},
10537
+ name: {
10538
+ type: "string"
10539
+ },
10540
+ pageId: {
10541
+ anyOf: [
10542
+ {
10543
+ type: "string"
10544
+ },
10545
+ {
10546
+ type: "null"
10547
+ }
10548
+ ]
10549
+ },
10550
+ selector: {
10551
+ anyOf: [
10552
+ {
10553
+ type: "string"
10554
+ },
10555
+ {
10556
+ type: "null"
10557
+ }
10558
+ ]
10559
+ },
10560
+ textMatch: {
10561
+ anyOf: [
10562
+ {
10563
+ type: "string"
10564
+ },
10565
+ {
10566
+ type: "null"
10567
+ }
10568
+ ]
10569
+ },
10570
+ type: {
10571
+ enum: ["click", "pageview", "custom", "revenue"],
10572
+ type: "string"
10573
+ },
10574
+ urlPattern: {
10575
+ anyOf: [
10576
+ {
10577
+ type: "string"
10578
+ },
10579
+ {
10580
+ type: "null"
10581
+ }
10582
+ ]
10583
+ },
10584
+ valueProperty: {
10585
+ anyOf: [
10586
+ {
10587
+ type: "string"
10588
+ },
10589
+ {
10590
+ type: "null"
10591
+ }
10592
+ ]
10593
+ },
10594
+ valueUnit: {
10595
+ anyOf: [
10596
+ {
10597
+ type: "string"
10598
+ },
10599
+ {
10600
+ type: "null"
10601
+ }
10602
+ ]
10603
+ }
10604
+ },
10605
+ required: ["name", "type"],
10606
+ type: "object"
10607
+ },
10608
+ type: "array"
10609
+ },
10610
+ primaryGoalId: {
10611
+ anyOf: [
10612
+ {
10613
+ type: "string"
10614
+ },
10615
+ {
10616
+ type: "null"
10617
+ }
10618
+ ]
10619
+ },
10620
+ schemaVersion: {
10621
+ const: 1,
10622
+ type: "number"
10623
+ }
10624
+ },
10625
+ required: ["schemaVersion", "primaryGoalId", "goals"],
10626
+ type: "object"
10627
+ },
10628
+ "qa.json": {
10629
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10630
+ additionalProperties: false,
10631
+ properties: {
10632
+ latestRun: {
10633
+ anyOf: [
10634
+ {
10635
+ additionalProperties: false,
10636
+ properties: {
10637
+ approvedAt: {
10638
+ anyOf: [
10639
+ {
10640
+ type: "string"
10641
+ },
10642
+ {
10643
+ type: "null"
10644
+ }
10645
+ ]
10646
+ },
10647
+ completedAt: {
10648
+ anyOf: [
10649
+ {
10650
+ type: "string"
10651
+ },
10652
+ {
10653
+ type: "null"
10654
+ }
10655
+ ]
10656
+ },
10657
+ id: {
10658
+ type: "string"
10659
+ },
10660
+ source: {
10661
+ type: "string"
10662
+ },
10663
+ status: {
10664
+ type: "string"
10665
+ },
10666
+ summary: {
10667
+ anyOf: [
10668
+ {},
10669
+ {
10670
+ type: "null"
10671
+ }
10672
+ ]
10673
+ }
10674
+ },
10675
+ required: ["id", "status", "source", "approvedAt", "completedAt", "summary"],
10676
+ type: "object"
10677
+ },
10678
+ {
10679
+ type: "null"
10680
+ }
10681
+ ]
10682
+ },
10683
+ qaApprovedAt: {
10684
+ anyOf: [
10685
+ {
10686
+ type: "string"
10687
+ },
10688
+ {
10689
+ type: "null"
10690
+ }
10691
+ ]
10692
+ },
10693
+ qaApprovedBy: {
10694
+ anyOf: [
10695
+ {
10696
+ type: "string"
10697
+ },
10698
+ {
10699
+ type: "null"
10700
+ }
10701
+ ]
10702
+ },
10703
+ readonly: {
10704
+ const: true,
10705
+ type: "boolean"
10706
+ },
10707
+ schemaVersion: {
10708
+ const: 1,
10709
+ type: "number"
10710
+ }
10711
+ },
10712
+ readOnly: true,
10713
+ required: ["schemaVersion", "readonly", "qaApprovedAt", "qaApprovedBy", "latestRun"],
10714
+ type: "object"
10715
+ },
10716
+ "targeting.json": {
10717
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10718
+ additionalProperties: {},
10719
+ properties: {
10720
+ authorUrl: {
10721
+ items: {
10722
+ additionalProperties: false,
10723
+ properties: {
10724
+ include: {
10725
+ type: "boolean"
10726
+ },
10727
+ match_type: {
10728
+ enum: ["exact", "contains", "wildcard", "regex"],
10729
+ type: "string"
10730
+ },
10731
+ matchType: {
10732
+ enum: ["exact", "contains", "wildcard", "regex"],
10733
+ type: "string"
10734
+ },
10735
+ pattern: {
10736
+ minLength: 1,
10737
+ type: "string"
10738
+ },
10739
+ type: {
10740
+ enum: ["regex", "simple"],
10741
+ type: "string"
10742
+ }
10743
+ },
10744
+ required: ["type", "pattern"],
10745
+ type: "object"
10746
+ },
10747
+ type: "array"
10748
+ },
10749
+ custom: {},
10750
+ device: {},
10751
+ deviceType: {
10752
+ type: "string"
10753
+ },
10754
+ lifecycle: {},
10755
+ pageRules: {
10756
+ items: {},
10757
+ type: "array"
10758
+ },
10759
+ pages: {
10760
+ items: {},
10761
+ type: "array"
10762
+ },
10763
+ segments: {
10764
+ items: {},
10765
+ type: "array"
10766
+ },
10767
+ url: {
10768
+ items: {
10769
+ additionalProperties: false,
10770
+ properties: {
10771
+ include: {
10772
+ type: "boolean"
10773
+ },
10774
+ match_type: {
10775
+ enum: ["exact", "contains", "wildcard", "regex"],
10776
+ type: "string"
10777
+ },
10778
+ matchType: {
10779
+ enum: ["exact", "contains", "wildcard", "regex"],
10780
+ type: "string"
10781
+ },
10782
+ pattern: {
10783
+ minLength: 1,
10784
+ type: "string"
10785
+ },
10786
+ type: {
10787
+ enum: ["regex", "simple"],
10788
+ type: "string"
10789
+ }
10790
+ },
10791
+ required: ["type", "pattern"],
10792
+ type: "object"
10793
+ },
10794
+ type: "array"
10795
+ }
10796
+ },
10797
+ type: "object"
10798
+ },
10799
+ "variations/*/anti-flicker.json": {
10800
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10801
+ items: {
10802
+ type: "string"
10803
+ },
10804
+ type: "array"
10805
+ },
10806
+ "variations/*/index.css": {
10807
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10808
+ contentMediaType: "text/css",
10809
+ type: "string"
10810
+ },
10811
+ "variations/*/index.js": {
10812
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10813
+ contentMediaType: "text/javascript",
10814
+ type: "string"
10815
+ },
10816
+ "variations/*/mutations.json": {
10817
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10818
+ items: {
10819
+ additionalProperties: {},
10820
+ anyOf: [
10821
+ {
10822
+ required: ["selector"]
10823
+ },
10824
+ {
10825
+ properties: {
10826
+ action: {
10827
+ enum: [
10828
+ "priceTest",
10829
+ "shippingTest",
10830
+ "offerTest",
10831
+ "postPurchaseOffer",
10832
+ "checkoutBlock"
10833
+ ]
10834
+ }
10835
+ },
10836
+ required: ["action"]
10837
+ }
10838
+ ],
10839
+ properties: {
10840
+ action: {
10841
+ type: "string"
10842
+ },
10843
+ selector: {
10844
+ minLength: 1,
10845
+ type: "string"
10846
+ },
10847
+ type: {
10848
+ type: "string"
10849
+ }
10850
+ },
10851
+ type: "object"
10852
+ },
10853
+ type: "array"
10854
+ },
10855
+ "variations/*/reset.js": {
10856
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10857
+ contentMediaType: "text/javascript",
10858
+ type: "string"
10859
+ },
10860
+ "variations/*/url-blocks.json": {
10861
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10862
+ items: {},
10863
+ type: "array"
10864
+ },
10865
+ "variations/*/variation.json": {
10866
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10867
+ additionalProperties: false,
10868
+ properties: {
10869
+ artifactReview: {
10870
+ additionalProperties: false,
10871
+ properties: {
10872
+ hash: {
10873
+ anyOf: [
10874
+ {
10875
+ type: "string"
10876
+ },
10877
+ {
10878
+ type: "null"
10879
+ }
10880
+ ]
10881
+ },
10882
+ reviewedAt: {
10883
+ anyOf: [
10884
+ {
10885
+ type: "string"
10886
+ },
10887
+ {
10888
+ type: "null"
10889
+ }
10890
+ ]
10891
+ },
10892
+ reviewedBy: {
10893
+ anyOf: [
10894
+ {
10895
+ type: "string"
10896
+ },
10897
+ {
10898
+ type: "null"
10899
+ }
10900
+ ]
10901
+ },
10902
+ source: {
10903
+ anyOf: [
10904
+ {
10905
+ type: "string"
10906
+ },
10907
+ {
10908
+ type: "null"
10909
+ }
10910
+ ]
10911
+ },
10912
+ status: {
10913
+ type: "string"
10914
+ }
10915
+ },
10916
+ required: ["status"],
10917
+ type: "object"
10918
+ },
10919
+ codeDeliveryMode: {
10920
+ enum: ["inline", "external"],
10921
+ type: "string"
10922
+ },
10923
+ cssArtifactId: {
10924
+ anyOf: [
10925
+ {
10926
+ type: "string"
10927
+ },
10928
+ {
10929
+ type: "null"
10930
+ }
10931
+ ]
10932
+ },
10933
+ id: {
10934
+ anyOf: [
10935
+ {
10936
+ type: "string"
10937
+ },
10938
+ {
10939
+ type: "null"
10940
+ }
10941
+ ]
10942
+ },
10943
+ isControl: {
10944
+ type: "boolean"
10945
+ },
10946
+ jsArtifactId: {
10947
+ anyOf: [
10948
+ {
10949
+ type: "string"
10950
+ },
10951
+ {
10952
+ type: "null"
10953
+ }
10954
+ ]
10955
+ },
10956
+ name: {
10957
+ type: "string"
10958
+ },
10959
+ redirectUrl: {
10960
+ anyOf: [
10961
+ {
10962
+ type: "string"
10963
+ },
10964
+ {
10965
+ type: "null"
10966
+ }
10967
+ ]
10968
+ },
10969
+ schemaVersion: {
10970
+ const: 1,
10971
+ type: "number"
10972
+ },
10973
+ slug: {
10974
+ type: "string"
10975
+ },
10976
+ weight: {
10977
+ maximum: 1,
10978
+ minimum: 0,
10979
+ type: "number"
10980
+ }
10981
+ },
10982
+ required: ["schemaVersion"],
10983
+ type: "object"
10984
+ }
10985
+ },
10986
+ schemaVersion: 1,
10987
+ source: "experiment-filesystem"
10988
+ };
10989
+
10184
10990
  // src/commands/experiments.ts
10185
10991
  function formatDate(iso) {
10186
10992
  return new Date(iso).toLocaleDateString("en-US", {
@@ -10244,6 +11050,13 @@ function formatRevenue(value, currency) {
10244
11050
  return String(value);
10245
11051
  }
10246
11052
  }
11053
+ function audienceLine(segmentApplied) {
11054
+ if (!segmentApplied || typeof segmentApplied !== "object") return null;
11055
+ const { requested, applied, reason: reason2 } = segmentApplied;
11056
+ if (!requested) return null;
11057
+ const label = `${requested.mode === "exclude" ? "excluding " : ""}${String(requested.dim)}=${String(requested.value)}`;
11058
+ return applied === true ? label : `all visitors (${label} not applied: ${typeof reason2 === "string" ? reason2 : "unknown reason"})`;
11059
+ }
10247
11060
  function printExperimentResults(results2, currency) {
10248
11061
  const summary = summarizeResults(results2);
10249
11062
  const { experiment: experiment2, totals, decision, verdictWithheld, statisticsState, presentedOutcome } = summary;
@@ -10251,6 +11064,8 @@ function printExperimentResults(results2, currency) {
10251
11064
  console.log();
10252
11065
  console.log(source_default.bold(String(experiment2.name ?? experiment2.id ?? "Experiment results")));
10253
11066
  console.log(` Status: ${statusBadge(String(experiment2.status ?? "unknown"))}`);
11067
+ const audience = audienceLine(results2.segment_applied);
11068
+ if (audience) console.log(` Audience: ${audience}`);
10254
11069
  console.log(` Visitors: ${String(totals.visitors ?? 0)}`);
10255
11070
  console.log(` Conversions: ${String(totals.primaryConversions ?? 0)}`);
10256
11071
  console.log(` Revenue: ${formatRevenue(totals.revenue, currency)}`);
@@ -10314,18 +11129,11 @@ function printExperimentResults(results2, currency) {
10314
11129
  }
10315
11130
  console.log();
10316
11131
  }
11132
+ var filesystemPaths = Object.keys(experiment_filesystem_schema_default.files);
10317
11133
  var EXPERIMENT_FILESYSTEM_PATH_SCHEMA = {
10318
- topLevel: ["experiment.json", "targeting.json", "metrics.json", "qa.json", "launch.json"],
10319
- experiment: ["index.css", "index.js", "reset.js"],
10320
- variation: [
10321
- "variation.json",
10322
- "mutations.json",
10323
- "url-blocks.json",
10324
- "index.css",
10325
- "index.js",
10326
- "reset.js",
10327
- "anti-flicker.json"
10328
- ]
11134
+ topLevel: filesystemPaths.filter((entry) => !entry.includes("/")),
11135
+ experiment: filesystemPaths.filter((entry) => entry.startsWith("experiment/")).map((entry) => entry.slice("experiment/".length)),
11136
+ variation: filesystemPaths.filter((entry) => entry.startsWith("variations/*/")).map((entry) => entry.slice("variations/*/".length))
10329
11137
  };
10330
11138
  var SERVER_OWNED_EXPERIMENT_FILES = /* @__PURE__ */ new Set(["qa.json", "launch.json"]);
10331
11139
  var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -10777,20 +11585,31 @@ var list = new Command("list").alias("ls").description(
10777
11585
  "List experiments in summary view by default; use --full for details or --fields for selected columns (archived experiments are hidden by default)"
10778
11586
  ).option(
10779
11587
  "-s, --status <status>",
10780
- "Filter by status (draft|awaiting_client_approval|changes_requested|client_approved|qa|ready_to_launch|running|paused|completed)"
11588
+ "Filter by status (draft|awaiting_client_approval|changes_requested|client_approved|qa|ready_to_launch|running|paused|completed) (applied server-side)"
11589
+ ).option(
11590
+ "--url <absolute-url>",
11591
+ "Filter by effective storefront URL targeting (applied server-side)"
10781
11592
  ).option("-n, --limit <n>", "Max results", "20").option("--include-archived", "Include archived experiments").option("--full", "Return full experiment details instead of the default summary").option(
10782
11593
  "--fields <list>",
10783
11594
  "Comma-separated fields to return and table columns in order (JSON always keeps id)"
10784
11595
  ).action(async function action3(opts) {
10785
11596
  const creds = requireCredentials();
10786
11597
  const spinner = await createSpinner("Fetching experiments...");
10787
- const res = await listExperiments(creds, {
11598
+ const listDefault = () => listExperiments(creds, {
10788
11599
  status: opts.status,
10789
11600
  limit: parseInt(opts.limit, 10),
10790
11601
  includeArchived: opts.includeArchived,
10791
11602
  view: opts.full ? "full" : "summary",
10792
11603
  fields: opts.fields
10793
11604
  });
11605
+ const res = opts.url !== void 0 ? await listExperiments(creds, {
11606
+ status: opts.status,
11607
+ limit: parseInt(opts.limit, 10),
11608
+ includeArchived: opts.includeArchived,
11609
+ view: opts.full ? "full" : "summary",
11610
+ fields: opts.fields,
11611
+ url: opts.url
11612
+ }) : await listDefault();
10794
11613
  if (!res.ok) {
10795
11614
  spinner?.fail("Failed to fetch experiments");
10796
11615
  fail(this, res.error, classifyStatus(res.status), res.errorDetails);
@@ -10928,7 +11747,13 @@ ${JSON.stringify(data.order, null, 2)}`);
10928
11747
  console.log(`
10929
11748
  Definitions: ${data.definitions_url}`);
10930
11749
  }
10931
- var results = new Command("results").description("Read experiment results and timeseries by ID or slug").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--currency <code>", "Currency code for revenue formatting").option("--explain", "Explain raw-event exclusions and residuals").option("--goal <id>", "Goal ID or event key for --explain").option("--order <key>", "Order label trail for --explain").option("--samples", "Show sample identifiers with --explain").action(async function action7(idOrSlug, opts) {
11750
+ var results = new Command("results").description("Read experiment results and timeseries by ID or slug").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--currency <code>", "Currency code for revenue formatting").option("--segment <dim:value>", "Include one audience segment").option("--exclude-segment <dim:value>", "Exclude one audience segment (include wins)").option("--explain", "Explain raw-event exclusions and residuals").option("--goal <id>", "Goal ID or event key for --explain").option("--order <key>", "Order label trail for --explain").option("--samples", "Show sample identifiers with --explain").action(async function action7(idOrSlug, opts) {
11751
+ if (opts.explain && (opts.segment !== void 0 || opts.excludeSegment !== void 0))
11752
+ fail(
11753
+ this,
11754
+ "--segment and --exclude-segment are only available for results, without --explain",
11755
+ EXIT_CODES.validation
11756
+ );
10932
11757
  if (!opts.explain && (opts.goal || opts.order || opts.samples))
10933
11758
  fail(this, "--goal, --order and --samples require --explain", EXIT_CODES.validation);
10934
11759
  const creds = requireCredentials();
@@ -10939,7 +11764,11 @@ var results = new Command("results").description("Read experiment results and ti
10939
11764
  spinner?.fail(`Experiment "${idOrSlug}" not found`);
10940
11765
  fail(this, `Experiment "${idOrSlug}" not found.`, EXIT_CODES.validation);
10941
11766
  }
10942
- const res = opts.explain ? await getExperimentResultsExplain(creds, exp.id, opts) : await getExperimentResults(creds, exp.id, { currency: opts.currency });
11767
+ const res = opts.explain ? await getExperimentResultsExplain(creds, exp.id, opts) : await getExperimentResults(creds, exp.id, {
11768
+ currency: opts.currency,
11769
+ segment: opts.segment,
11770
+ exclude_segment: opts.excludeSegment
11771
+ });
10943
11772
  if (!res.ok || !res.data) {
10944
11773
  spinner?.fail("Failed to fetch experiment results");
10945
11774
  fail(
@@ -11213,7 +12042,7 @@ var create = new Command("create").description(
11213
12042
  []
11214
12043
  ).option(
11215
12044
  "--goal <goal-id|new:fields>",
11216
- "Goal id or new:type=...,selector=...,name=... (repeatable)",
12045
+ "Goal id or new:type=...,selector=...,name=... (repeatable; omitted uses the shop default main goal)",
11217
12046
  collectCreateOption,
11218
12047
  []
11219
12048
  ).option(
@@ -11632,6 +12461,7 @@ var pullFilesystem = new Command("pull").description("Pull an experiment as an e
11632
12461
  output(
11633
12462
  this,
11634
12463
  {
12464
+ schemaVersion: 1,
11635
12465
  folder,
11636
12466
  experimentId: exp.id,
11637
12467
  files: res.data.files.map((file) => import_node_path6.default.join(folder, file.path))
@@ -12460,7 +13290,55 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
12460
13290
  throw error;
12461
13291
  }
12462
13292
  });
12463
- var qa = new Command("qa").description("Manage experiment QA runs from the CLI").addCommand(qaStatus).addCommand(qaCheck).addCommand(qaNotes).addCommand(qaApprove).addCommand(qaReopen).addCommand(qaEvidence);
13293
+ var qa = new Command("qa").description("Manage experiment QA runs from the CLI").addCommand(qaStatus).addCommand(
13294
+ new Command("events").description("List QA-tagged events for an experiment session").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--session <id>", "QA session id (defaults to the latest QA run)").option("--minutes <n>", "Lookback window in minutes", "60").option("--type <type>", "Event type: all, goal, or custom", "all").action(async function(target, opts) {
13295
+ const minutes = Number(opts.minutes);
13296
+ if (!Number.isInteger(minutes) || minutes < 5 || minutes > 1440)
13297
+ fail(this, "--minutes must be an integer between 5 and 1440", EXIT_CODES.validation);
13298
+ if (!["all", "goal", "custom"].includes(opts.type))
13299
+ fail(this, "--type must be all, goal, or custom", EXIT_CODES.validation);
13300
+ const creds = requireCredentials();
13301
+ const experiment2 = { id: (await resolveExperimentForFilesystemOrId(target)).experimentId };
13302
+ let session = opts.session;
13303
+ if (!session) {
13304
+ const status4 = await getExperimentQaRun(creds, experiment2.id);
13305
+ if (!status4.ok || !status4.data)
13306
+ throw new CliError(
13307
+ status4.error ?? "Failed to fetch QA run",
13308
+ classifyStatus(status4.status),
13309
+ status4.errorDetails
13310
+ );
13311
+ session = status4.data.run.id;
13312
+ }
13313
+ const response = await getExperimentQaEvents(creds, experiment2.id, {
13314
+ session,
13315
+ minutes,
13316
+ type: opts.type
13317
+ });
13318
+ if (!response.ok || !response.data)
13319
+ throw new CliError(
13320
+ response.error ?? "Failed to fetch QA events",
13321
+ classifyStatus(response.status),
13322
+ response.errorDetails
13323
+ );
13324
+ output(
13325
+ this,
13326
+ response.data,
13327
+ () => printTable(
13328
+ ["TYPE", "EVENT", "VARIATION", "SESSION", "COUNT", "LAST SEEN", "LAST URL"],
13329
+ response.data.data.map((row) => [
13330
+ row.event_type === "goal" ? "goal" : "custom",
13331
+ row.event_name || row.goal_id,
13332
+ row.variation_id,
13333
+ row.qa_session_id,
13334
+ String(row.events),
13335
+ row.last_event_at ?? "\u2014",
13336
+ row.last_url ?? "\u2014"
13337
+ ])
13338
+ )
13339
+ );
13340
+ })
13341
+ ).addCommand(qaCheck).addCommand(qaNotes).addCommand(qaApprove).addCommand(qaReopen).addCommand(qaEvidence);
12464
13342
  var SCREENSHOT_DEVICES = [
12465
13343
  "desktop_chrome",
12466
13344
  "iphone_15_pro",
@@ -13246,9 +14124,21 @@ var previewCommand = new Command("preview").description(
13246
14124
  'Experiment is missing "store_url". Set it on the experiment or pass --store-url <url>.'
13247
14125
  );
13248
14126
  }
14127
+ const snapshot = await getExperimentFilesystem(creds, exp.id).catch(() => null);
14128
+ let qaRun;
14129
+ if (snapshot?.ok && snapshot.data) {
14130
+ const qaFile = snapshot.data.files.find((file) => file.path === "qa.json");
14131
+ if (qaFile) {
14132
+ try {
14133
+ const run3 = JSON.parse(qaFile.content)?.latestRun;
14134
+ if (run3 && typeof run3.id === "string" && run3.id.trim()) qaRun = run3;
14135
+ } catch {
14136
+ }
14137
+ }
14138
+ }
13249
14139
  let previewUrl;
13250
14140
  try {
13251
- previewUrl = buildQaPreviewUrl2(storeUrl, exp.id, variation.id);
14141
+ previewUrl = buildQaPreviewUrl2(storeUrl, exp.id, variation.id, qaRun?.id);
13252
14142
  } catch {
13253
14143
  fail(
13254
14144
  previewCommand,
@@ -13257,6 +14147,10 @@ var previewCommand = new Command("preview").description(
13257
14147
  }
13258
14148
  const signed = await signedPreviewFromReadiness(creds, exp.id, variation.id, storeUrl);
13259
14149
  const selectedPreviewUrl = signed?.signed_preview_url ?? previewUrl.toString();
14150
+ if (signed?.signed_preview_url) {
14151
+ const signedSessionId = signed.signed_preview_qa_session_id;
14152
+ qaRun = signedSessionId ? qaRun?.id === signedSessionId ? qaRun : { id: signedSessionId } : void 0;
14153
+ }
13260
14154
  const warning = signed?.signed_preview_url ? null : "Test images may not load on this unsigned link. Open Apex \u2192 QA for a signed link.";
13261
14155
  if (warning) console.error(warning);
13262
14156
  output(
@@ -13267,6 +14161,7 @@ var previewCommand = new Command("preview").description(
13267
14161
  previewUrl: selectedPreviewUrl,
13268
14162
  expiresAt: signed?.signed_preview_expires_at ?? null,
13269
14163
  ...warning ? { warning } : {},
14164
+ ...qaRun ? { qa_run: qaRun, qa_session_id: qaRun.id } : {},
13270
14165
  ...!opts?.storeUrl && exp.preview_target ? { previewTarget: exp.preview_target } : {}
13271
14166
  },
13272
14167
  () => {
@@ -20146,7 +21041,8 @@ function createSchemaCommand(program3) {
20146
21041
  mandatoryOptions: "An option's mandatory field is true only when the flag itself must be passed.",
20147
21042
  requiredArguments: "An argument's required field means the positional is mandatory."
20148
21043
  },
20149
- ...commandSchema(program3)
21044
+ ...commandSchema(program3),
21045
+ files: experiment_filesystem_schema_default
20150
21046
  };
20151
21047
  output(this, schema, () => {
20152
21048
  process.stdout.write(`${JSON.stringify(schema, null, 2)}
@@ -20787,8 +21683,15 @@ var skillsCommand = new Command("skills").description("Print or install the Apex
20787
21683
  });
20788
21684
  });
20789
21685
 
21686
+ // ../../ops/org-split/move-plan.mjs
21687
+ var uuid = /^([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)$/;
21688
+ function validateMoveInput(shopIds, toOrgId) {
21689
+ if (typeof toOrgId !== "string" || !uuid.test(toOrgId) || !Array.isArray(shopIds) || !shopIds.length || shopIds.some((id) => typeof id !== "string" || !uuid.test(id))) throw new Error("Choose valid store and destination UUIDs");
21690
+ return { shopIds: [...new Set(shopIds.map((id) => id.toLowerCase()))].sort(), toOrgId: toOrgId.toLowerCase() };
21691
+ }
21692
+
20790
21693
  // src/commands/admin.ts
20791
- var uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
21694
+ var uuid2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
20792
21695
  function switchValue(value) {
20793
21696
  if (value === "on") return true;
20794
21697
  if (value === "off") return false;
@@ -20915,7 +21818,7 @@ var create6 = new Command("create").description("Create an internal, demo or dog
20915
21818
  fail(this, "--name must be 1 to 60 characters", 3);
20916
21819
  if (opts.reason.trim().length < 8 || opts.reason.trim().length > 500)
20917
21820
  fail(this, "--reason must be 8 to 500 characters", 3);
20918
- if (opts.claimShop && !uuid.test(opts.claimShop)) fail(this, "--claim-shop must be a UUID", 3);
21821
+ if (opts.claimShop && !uuid2.test(opts.claimShop)) fail(this, "--claim-shop must be a UUID", 3);
20919
21822
  if (opts.claimShop && (opts.domain || opts.platform))
20920
21823
  fail(this, "Use --claim-shop or --domain with --platform", 3);
20921
21824
  if (!!opts.domain !== !!opts.platform)
@@ -20936,7 +21839,7 @@ var create6 = new Command("create").description("Create an internal, demo or dog
20936
21839
  fail(this, "--owner requires a store for the invitation", 3);
20937
21840
  if (opts.plan !== "self_managed" && (!opts.consultant || !opts.consultantTitle?.trim()))
20938
21841
  fail(this, "Managed plans require --consultant and --consultant-title", 3);
20939
- if (opts.consultant && !uuid.test(opts.consultant))
21842
+ if (opts.consultant && !uuid2.test(opts.consultant))
20940
21843
  fail(this, "--consultant must be a platform access UUID", 3);
20941
21844
  if (!!opts.consultant !== !!opts.consultantTitle?.trim())
20942
21845
  fail(this, "Provide --consultant and --consultant-title together", 3);
@@ -21001,7 +21904,7 @@ var create6 = new Command("create").description("Create an internal, demo or dog
21001
21904
  });
21002
21905
  });
21003
21906
  var setCategory = new Command("set-category").description("Change an existing account's category with an audit reason").argument("<org-id>", "Organization UUID").requiredOption("--category <category>", "internal, demo, dogfood or customer").requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--confirm", "Confirm category change").option("--json", "Output JSON").action(async function(orgId, opts) {
21004
- if (!uuid.test(orgId)) fail(this, "<org-id> must be a UUID", 3);
21907
+ if (!uuid2.test(orgId)) fail(this, "<org-id> must be a UUID", 3);
21005
21908
  if (!["internal", "demo", "dogfood", "customer"].includes(opts.category))
21006
21909
  fail(this, "--category must be internal, demo, dogfood or customer", 3);
21007
21910
  const reason2 = opts.reason.trim();
@@ -21032,14 +21935,137 @@ var setCategory = new Command("set-category").description("Change an existing ac
21032
21935
  printTable(["RESULT"], [[`Customer ${result.customerId} moved to ${result.category}`]]);
21033
21936
  });
21034
21937
  });
21035
- var customers = new Command("customers").description("Create engineering accounts and manage existing account categories").addCommand(create6).addCommand(setCategory);
21938
+ var setManagedBy = new Command("set-managed-by").description("Assign or clear a managing agency with an audit reason").argument("<org-id>", "Organization UUID").option("--by <org-id>", "Managing agency UUID").option("--clear", "Clear the managing agency").requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--confirm", "Confirm manager change").option("--json", "Output JSON").action(async function(orgId, opts) {
21939
+ if (!uuid2.test(orgId)) fail(this, "<org-id> must be a UUID", 3);
21940
+ if (!!opts.by === !!opts.clear) fail(this, "Use exactly one of --by or --clear", 3);
21941
+ if (opts.by && !uuid2.test(opts.by)) fail(this, "--by must be a UUID", 3);
21942
+ const reason2 = opts.reason.trim();
21943
+ if (reason2.length < 8 || reason2.length > 500)
21944
+ fail(this, "--reason must be 8 to 500 characters", 3);
21945
+ if (!opts.confirm) {
21946
+ const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
21947
+ confirmationRequired(
21948
+ this,
21949
+ "--confirm is required to change a managing agency",
21950
+ `apex admin customers set-managed-by ${orgId} ${opts.by ? `--by ${opts.by}` : "--clear"} --reason ${quote(reason2)} --confirm`
21951
+ );
21952
+ }
21953
+ const res = await setAdminCustomerManagedBy(requireUserCredentials(), orgId, {
21954
+ managed_by_org_id: opts.by ?? null,
21955
+ reason: reason2
21956
+ });
21957
+ if (!res.ok || !res.data)
21958
+ fail(
21959
+ this,
21960
+ res.error ?? "Failed to change managing agency",
21961
+ classifyStatus(res.status),
21962
+ res.errorDetails
21963
+ );
21964
+ output(
21965
+ this,
21966
+ res.data,
21967
+ () => printTable(
21968
+ ["CUSTOMER", "MANAGING AGENCY"],
21969
+ [[res.data.data.customerId, res.data.data.managed_by_org_id ?? "None"]]
21970
+ )
21971
+ );
21972
+ });
21973
+ var setWorkspaceType = new Command("set-workspace-type").description("Change a workspace type with an audit reason").argument("<org-id>", "Organization UUID").requiredOption("--type <company|agency>", "Workspace type").requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--confirm", "Confirm workspace type change").option("--json", "Output JSON").action(async function(orgId, opts) {
21974
+ if (!uuid2.test(orgId)) fail(this, "<org-id> must be a UUID", 3);
21975
+ if (opts.type !== "company" && opts.type !== "agency")
21976
+ fail(this, "--type must be company or agency", 3);
21977
+ const reason2 = opts.reason.trim();
21978
+ if (reason2.length < 8 || reason2.length > 500)
21979
+ fail(this, "--reason must be 8 to 500 characters", 3);
21980
+ if (!opts.confirm) {
21981
+ const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
21982
+ confirmationRequired(
21983
+ this,
21984
+ "--confirm is required to change a workspace type",
21985
+ `apex admin customers set-workspace-type ${orgId} --type ${opts.type} --reason ${quote(reason2)} --confirm`
21986
+ );
21987
+ }
21988
+ const res = await setAdminCustomerWorkspaceType(requireUserCredentials(), orgId, {
21989
+ workspace_type: opts.type,
21990
+ reason: reason2
21991
+ });
21992
+ if (!res.ok || !res.data)
21993
+ fail(
21994
+ this,
21995
+ res.error ?? "Failed to change workspace type",
21996
+ classifyStatus(res.status),
21997
+ res.errorDetails
21998
+ );
21999
+ output(
22000
+ this,
22001
+ res.data,
22002
+ () => printTable(
22003
+ ["CUSTOMER", "WORKSPACE TYPE"],
22004
+ [[res.data.data.customerId, res.data.data.workspace_type]]
22005
+ )
22006
+ );
22007
+ });
22008
+ function storeMoveRequest(opts) {
22009
+ const { shopIds, toOrgId } = validateMoveInput(opts.shop, opts.to);
22010
+ const reason2 = opts.reason.trim();
22011
+ if (reason2.length < 8 || reason2.length > 500)
22012
+ throw new Error("--reason must be 8 to 500 characters");
22013
+ return {
22014
+ toOrgId,
22015
+ body: {
22016
+ shop_ids: shopIds,
22017
+ reason: reason2,
22018
+ keep_grants: !!opts.keepGrants,
22019
+ dry_run: !opts.confirm
22020
+ }
22021
+ };
22022
+ }
22023
+ var moveStores = new Command("move-stores").description("Preview or confirm an audited set of store moves").requiredOption("--to <org-id>", "Destination organization UUID").requiredOption(
22024
+ "--shop <shop-id>",
22025
+ "Store UUID, repeat for each store",
22026
+ (value, previous) => [...previous, value],
22027
+ []
22028
+ ).requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--keep-grants", "Preserve explicit store roles and overrides").option("--confirm", "Perform the move; otherwise return a read-only plan").option("--json", "Output JSON").action(async function(opts) {
22029
+ let request2;
22030
+ try {
22031
+ request2 = storeMoveRequest(opts);
22032
+ } catch (error) {
22033
+ fail(this, error instanceof Error ? error.message : "Invalid move request", 3);
22034
+ }
22035
+ const res = await moveAdminCustomerStores(
22036
+ requireUserCredentials(),
22037
+ request2.toOrgId,
22038
+ request2.body
22039
+ );
22040
+ if (!res.ok || !res.data)
22041
+ fail(
22042
+ this,
22043
+ res.error ?? "Failed to move stores",
22044
+ classifyStatus(res.status),
22045
+ res.errorDetails
22046
+ );
22047
+ output(this, res.data, () => {
22048
+ printTable(
22049
+ ["FIELD", "VALUE"],
22050
+ Object.entries(res.data.data).map(([key, value]) => [key, JSON.stringify(value)])
22051
+ );
22052
+ });
22053
+ });
22054
+ var customers = new Command("customers").description("Create engineering accounts and manage existing account categories").addCommand(create6).addCommand(setCategory).addCommand(setManagedBy).addCommand(setWorkspaceType).addCommand(moveStores);
21036
22055
  var adminCommand = new Command("admin").description("Operate allowlisted platform controls").addCommand(settingsGet).addCommand(quarantine).addCommand(shops).addCommand(customers);
21037
22056
 
21038
22057
  // src/commands/membership.ts
21039
22058
  init_source();
21040
22059
  async function run2(c, p, label) {
21041
22060
  const r = await p;
21042
- if (!r.ok) fail(c, r.error ?? `Failed to ${label}`, classifyStatus(r.status), r.errorDetails);
22061
+ if (!r.ok)
22062
+ fail(
22063
+ c,
22064
+ r.error ?? `Failed to ${label}`,
22065
+ classifyStatus(r.status),
22066
+ r.errorDetails,
22067
+ apiFailureOf(r)
22068
+ );
21043
22069
  output(c, r.data ?? {}, () => process.stdout.write(`${JSON.stringify(r.data, null, 2)}
21044
22070
  `));
21045
22071
  }
@@ -21075,7 +22101,27 @@ orgCommand.command("update").option("--name <name>").option("--slug <slug>").opt
21075
22101
  );
21076
22102
  });
21077
22103
  orgCommand.command("members").action(async function() {
21078
- await run2(this, listOrgMembers(requireUserCredentials()), "list members");
22104
+ const r = await listOrgMembers(requireUserCredentials());
22105
+ if (!r.ok)
22106
+ fail(
22107
+ this,
22108
+ r.error ?? "Failed to list members",
22109
+ classifyStatus(r.status),
22110
+ r.errorDetails,
22111
+ apiFailureOf(r)
22112
+ );
22113
+ const rows = r.data?.data ?? [];
22114
+ output(this, r.data ?? {}, () => {
22115
+ printTable(
22116
+ ["ID", "MEMBER", "ROLE", "SUSPENDED"],
22117
+ rows.map((row) => [
22118
+ String(row.id),
22119
+ String(row.email ?? row.name ?? "-"),
22120
+ String(row.role),
22121
+ String(row.suspended_at ?? "-")
22122
+ ])
22123
+ );
22124
+ });
21079
22125
  });
21080
22126
  orgCommand.command("invite").argument("<email>").option("--role <role>", "Alias for --org-role").option("--org-role <role>", "Organization role", "member").addOption(
21081
22127
  new Option(
@@ -21112,6 +22158,26 @@ orgCommand.command("set-role").argument("<memberId>").argument("<role>").option(
21112
22158
  "set role"
21113
22159
  );
21114
22160
  });
22161
+ orgCommand.command("suspend-member").argument("<memberId>").option("--reason <text>").option("-f, --force").action(async function(id, o) {
22162
+ if (!await confirm(
22163
+ this,
22164
+ o.force,
22165
+ `Suspend organization member ${id}`,
22166
+ `apex org suspend-member ${id} --force`
22167
+ ))
22168
+ return;
22169
+ await run2(this, suspendOrgMember(requireUserCredentials(), id, o.reason), "suspend member");
22170
+ });
22171
+ orgCommand.command("reinstate-member").argument("<memberId>").option("-f, --force").action(async function(id, o) {
22172
+ if (!await confirm(
22173
+ this,
22174
+ o.force,
22175
+ `Reinstate organization member ${id}`,
22176
+ `apex org reinstate-member ${id} --force`
22177
+ ))
22178
+ return;
22179
+ await run2(this, reinstateOrgMember(requireUserCredentials(), id), "reinstate member");
22180
+ });
21115
22181
  orgCommand.command("remove-member").argument("<memberId>").option("-f, --force").action(async function(id, o) {
21116
22182
  if (!await confirm(
21117
22183
  this,