@feastalytics/cli 0.1.2 → 0.1.4

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/cli.js CHANGED
@@ -8656,146 +8656,6 @@ function errorMessage(error) {
8656
8656
  // src/main.ts
8657
8657
  import fs2 from "fs";
8658
8658
 
8659
- // src/browserLogin.ts
8660
- import { spawn } from "child_process";
8661
- import { createHash, randomBytes } from "crypto";
8662
- import http from "http";
8663
- var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
8664
- function base64url(buffer) {
8665
- return buffer.toString("base64url");
8666
- }
8667
- function tabHtml(title, body) {
8668
- return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
8669
- <style>body{font-family:-apple-system,system-ui,sans-serif;background:#0f0f0f;color:#f5f5f5;
8670
- display:flex;min-height:100vh;align-items:center;justify-content:center;margin:0}
8671
- .card{text-align:center;padding:2rem 3rem}.t{font-size:1.4rem;font-weight:600;margin-bottom:.5rem}
8672
- .b{color:#a1a1a1}</style></head><body><div class="card">
8673
- <div class="t">${title}</div><div class="b">${body}</div></div></body></html>`;
8674
- }
8675
- function openBrowser(url) {
8676
- let command;
8677
- let args;
8678
- if (process.platform === "darwin") {
8679
- command = "open";
8680
- args = [url];
8681
- } else if (process.platform === "win32") {
8682
- command = "cmd";
8683
- args = ["/c", "start", "", url];
8684
- } else {
8685
- command = "xdg-open";
8686
- args = [url];
8687
- }
8688
- try {
8689
- const child = spawn(command, args, { stdio: "ignore", detached: true });
8690
- child.on("error", () => {
8691
- });
8692
- child.unref();
8693
- } catch {
8694
- }
8695
- }
8696
- function startLoopback(expectedState) {
8697
- return new Promise((resolveOuter, rejectOuter) => {
8698
- let resolveCode = () => {
8699
- };
8700
- let rejectCode = () => {
8701
- };
8702
- const waitForCode = new Promise((resolve, reject) => {
8703
- resolveCode = resolve;
8704
- rejectCode = reject;
8705
- });
8706
- waitForCode.catch(() => {
8707
- });
8708
- const server = http.createServer((req, res) => {
8709
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
8710
- if (url.pathname !== "/callback") {
8711
- res.writeHead(404);
8712
- res.end();
8713
- return;
8714
- }
8715
- const error = url.searchParams.get("error");
8716
- if (error != null) {
8717
- res.writeHead(200, { "Content-Type": "text/html" });
8718
- res.end(tabHtml("Authorization declined", "You can close this tab."));
8719
- rejectCode(new Error(`Authorization declined: ${error}`));
8720
- return;
8721
- }
8722
- if (url.searchParams.get("state") !== expectedState) {
8723
- res.writeHead(400, { "Content-Type": "text/html" });
8724
- res.end(
8725
- tabHtml("Invalid request", "State mismatch \u2014 retry from the CLI.")
8726
- );
8727
- rejectCode(new Error("State mismatch \u2014 aborting (possible CSRF)"));
8728
- return;
8729
- }
8730
- const code = url.searchParams.get("code");
8731
- if (code == null || code.length === 0) {
8732
- res.writeHead(400);
8733
- res.end();
8734
- rejectCode(new Error("Authorization callback did not include a code"));
8735
- return;
8736
- }
8737
- res.writeHead(200, {
8738
- "Content-Type": "text/html",
8739
- Connection: "close"
8740
- });
8741
- res.end(
8742
- tabHtml(
8743
- "You're all set \u2713",
8744
- "The feast CLI is authorized. Close this tab and return to your terminal."
8745
- )
8746
- );
8747
- resolveCode(code);
8748
- });
8749
- const timeout = setTimeout(() => {
8750
- rejectCode(
8751
- new Error("Timed out waiting for browser authorization (5 minutes)")
8752
- );
8753
- }, CALLBACK_TIMEOUT_MS);
8754
- const close = () => {
8755
- clearTimeout(timeout);
8756
- server.closeAllConnections?.();
8757
- server.close();
8758
- };
8759
- server.on("error", rejectOuter);
8760
- server.listen(0, "127.0.0.1", () => {
8761
- server.unref();
8762
- const address = server.address();
8763
- const port = typeof address === "object" && address != null ? address.port : 0;
8764
- resolveOuter({ port, waitForCode, close });
8765
- });
8766
- });
8767
- }
8768
- async function browserLogin() {
8769
- const codeVerifier = base64url(randomBytes(32));
8770
- const codeChallenge = base64url(
8771
- createHash("sha256").update(codeVerifier).digest()
8772
- );
8773
- const state = base64url(randomBytes(16));
8774
- const { port, waitForCode, close } = await startLoopback(state);
8775
- try {
8776
- const authUrl = `${getWebBaseUrl()}/oauth?port=${port}&state=${encodeURIComponent(state)}&challenge=${encodeURIComponent(codeChallenge)}`;
8777
- console.error("Opening your browser to authorize the feast CLI...");
8778
- console.error(`If it doesn't open, visit:
8779
- ${authUrl}
8780
- `);
8781
- openBrowser(authUrl);
8782
- const code = await waitForCode;
8783
- const client = createHttpCaller({});
8784
- const response = await client.api.auth.exchangeAuthCode.mutate({
8785
- code,
8786
- codeVerifier
8787
- });
8788
- const tokens = response?.tokens;
8789
- if (tokens?.accessToken?.jwtToken == null) {
8790
- throw new Error("Authorization failed: empty token response");
8791
- }
8792
- saveTokens(tokens);
8793
- return tokens;
8794
- } finally {
8795
- close();
8796
- }
8797
- }
8798
-
8799
8659
  // src/generated/manifest.ts
8800
8660
  var CLI_MANIFEST = {
8801
8661
  "awsAccountId": "891817334910",
@@ -10807,6 +10667,37 @@ var CLI_MANIFEST = {
10807
10667
  "$schema": "http://json-schema.org/draft-07/schema#"
10808
10668
  }
10809
10669
  },
10670
+ {
10671
+ "id": "createMembersProgramReward",
10672
+ "domain": "membersProgram",
10673
+ "description": "Create a members program reward. The name is matched against the organization's catalog items: an exact match is reused (and its staff instructions updated when you pass staffInstructions), otherwise a new catalog item is created. Pass pointsCost to make the reward redeemable by guests spending points; omit it for a reward that is granted by an automation instead. A reward with no awardReward automation and no pointsCost can never reach a guest, so pair it with an automation via createAutomation.",
10674
+ "needsApproval": false,
10675
+ "type": "mutation",
10676
+ "path": [
10677
+ "api",
10678
+ "membersProgram",
10679
+ "createReward"
10680
+ ],
10681
+ "inputJsonSchema": {
10682
+ "type": "object",
10683
+ "properties": {
10684
+ "name": {
10685
+ "type": "string"
10686
+ },
10687
+ "staffInstructions": {
10688
+ "type": "string"
10689
+ },
10690
+ "pointsCost": {
10691
+ "type": "number"
10692
+ }
10693
+ },
10694
+ "required": [
10695
+ "name"
10696
+ ],
10697
+ "additionalProperties": false,
10698
+ "$schema": "http://json-schema.org/draft-07/schema#"
10699
+ }
10700
+ },
10810
10701
  {
10811
10702
  "id": "deleteAutomationFlow",
10812
10703
  "domain": "automations",
@@ -10879,7 +10770,8 @@ var CLI_MANIFEST = {
10879
10770
  "funnel",
10880
10771
  "organizationFilePublic",
10881
10772
  "organizationFilePrivate",
10882
- "creativeLibrary"
10773
+ "creativeLibrary",
10774
+ "creativeLibraryBroll"
10883
10775
  ]
10884
10776
  },
10885
10777
  "key": {
@@ -11360,7 +11252,7 @@ var CLI_MANIFEST = {
11360
11252
  {
11361
11253
  "id": "getMediaUploadUrl",
11362
11254
  "domain": "core",
11363
- "description": "Get a presigned S3 upload URL for a media file in a given scope (passBuilder, funnel, organizationFilePublic, organizationFilePrivate, creativeLibrary). Set cropped to store a cropped variant under the scope's cropped/ subfolder. PUT the file bytes to the returned presignedUrl, then reference the returned url/key.",
11255
+ "description": "Get a presigned S3 upload URL for a media file in a given scope (passBuilder, funnel, organizationFilePublic, organizationFilePrivate, creativeLibrary, creativeLibraryBroll). Set cropped to store a cropped variant under the scope's cropped/ subfolder. PUT the file bytes to the returned presignedUrl, then reference the returned url/key.",
11364
11256
  "needsApproval": false,
11365
11257
  "type": "mutation",
11366
11258
  "path": [
@@ -11378,7 +11270,8 @@ var CLI_MANIFEST = {
11378
11270
  "funnel",
11379
11271
  "organizationFilePublic",
11380
11272
  "organizationFilePrivate",
11381
- "creativeLibrary"
11273
+ "creativeLibrary",
11274
+ "creativeLibraryBroll"
11382
11275
  ]
11383
11276
  },
11384
11277
  "fileName": {
@@ -11581,7 +11474,7 @@ var CLI_MANIFEST = {
11581
11474
  {
11582
11475
  "id": "listMedia",
11583
11476
  "domain": "core",
11584
- "description": "List uploaded media files for the organization across one or more scopes (passBuilder, funnel, organizationFilePublic, organizationFilePrivate, creativeLibrary). Each file is tagged with its scope and a canDelete flag. Set mediaOnly to return only image/video files. Set cropped to list the cropped variants (each scope's cropped/ subfolder) instead of the base files.",
11477
+ "description": "List uploaded media files for the organization across one or more scopes (passBuilder, funnel, organizationFilePublic, organizationFilePrivate, creativeLibrary, creativeLibraryBroll). Each file is tagged with its scope and a canDelete flag. Set mediaOnly to return only image/video files. Set cropped to list the cropped variants (each scope's cropped/ subfolder) instead of the base files.",
11585
11478
  "needsApproval": false,
11586
11479
  "type": "query",
11587
11480
  "path": [
@@ -11601,7 +11494,8 @@ var CLI_MANIFEST = {
11601
11494
  "funnel",
11602
11495
  "organizationFilePublic",
11603
11496
  "organizationFilePrivate",
11604
- "creativeLibrary"
11497
+ "creativeLibrary",
11498
+ "creativeLibraryBroll"
11605
11499
  ]
11606
11500
  },
11607
11501
  "minItems": 1
@@ -11620,6 +11514,25 @@ var CLI_MANIFEST = {
11620
11514
  "$schema": "http://json-schema.org/draft-07/schema#"
11621
11515
  }
11622
11516
  },
11517
+ {
11518
+ "id": "listMembersProgramRewards",
11519
+ "domain": "membersProgram",
11520
+ "description": "List the organization's members program rewards, each with its catalog item name, staff instructions, and points cost. A reward with a pointsCost is redeemable by guests spending points; one without is granted by an automation. To find orphaned rewards \u2014 rewards nothing ever awards \u2014 call listAutomations and check which itemIds appear in an awardReward action.",
11521
+ "needsApproval": false,
11522
+ "type": "query",
11523
+ "path": [
11524
+ "api",
11525
+ "membersProgram",
11526
+ "listRewards"
11527
+ ],
11528
+ "inputJsonSchema": {
11529
+ "type": "object",
11530
+ "properties": {},
11531
+ "additionalProperties": false,
11532
+ "default": {},
11533
+ "$schema": "http://json-schema.org/draft-07/schema#"
11534
+ }
11535
+ },
11623
11536
  {
11624
11537
  "id": "loadCurrentOrganization",
11625
11538
  "domain": "core",
@@ -11734,6 +11647,32 @@ var CLI_MANIFEST = {
11734
11647
  "$schema": "http://json-schema.org/draft-07/schema#"
11735
11648
  }
11736
11649
  },
11650
+ {
11651
+ "id": "previewFunnelDraft",
11652
+ "domain": "funnel",
11653
+ "description": "Render every screen of a funnel draft to a PDF (one screen per page, mobile viewport) and return a short-lived download URL. Open drafts render with their staged edits applied; promoted drafts render the live funnel. Fetch the returned URL with web_fetch to visually inspect the screens.",
11654
+ "needsApproval": false,
11655
+ "type": "mutation",
11656
+ "path": [
11657
+ "api",
11658
+ "layoutEngine",
11659
+ "drafts",
11660
+ "preview"
11661
+ ],
11662
+ "inputJsonSchema": {
11663
+ "type": "object",
11664
+ "properties": {
11665
+ "draftId": {
11666
+ "type": "string"
11667
+ }
11668
+ },
11669
+ "required": [
11670
+ "draftId"
11671
+ ],
11672
+ "additionalProperties": false,
11673
+ "$schema": "http://json-schema.org/draft-07/schema#"
11674
+ }
11675
+ },
11737
11676
  {
11738
11677
  "id": "queryData",
11739
11678
  "domain": "data",
@@ -11912,10 +11851,29 @@ Filter leaves are one column each, written as the column name prefixed with $, c
11912
11851
  "$schema": "http://json-schema.org/draft-07/schema#"
11913
11852
  }
11914
11853
  },
11854
+ {
11855
+ "id": "readPassConfiguration",
11856
+ "domain": "passBuilder",
11857
+ "description": "Read the organization's live wallet pass configuration: sections (the pass content fields), features, locations, metadata, and the current version number. Returns null when the organization has never saved a pass configuration. Read this before changing the pass \u2014 savePassConfiguration is a full-document save, so the returned document is the starting point you modify.",
11858
+ "needsApproval": false,
11859
+ "type": "query",
11860
+ "path": [
11861
+ "api",
11862
+ "passBuilder",
11863
+ "get"
11864
+ ],
11865
+ "inputJsonSchema": {
11866
+ "type": "object",
11867
+ "properties": {},
11868
+ "additionalProperties": false,
11869
+ "default": {},
11870
+ "$schema": "http://json-schema.org/draft-07/schema#"
11871
+ }
11872
+ },
11915
11873
  {
11916
11874
  "id": "saveFunnelEdits",
11917
11875
  "domain": "funnel",
11918
- "description": "Apply funnel edits to production and save. Accepts either a draftId (applies the draft's staged edits, then marks it promoted) or an inline edits array. This writes to the live funnel.",
11876
+ "description": "Apply funnel edits to production and save. Accepts either a draftId (applies the draft's staged edits, creates any screens staged with stageFunnelScreen, then marks it promoted) or an inline edits array. This writes to the live funnel.",
11919
11877
  "needsApproval": true,
11920
11878
  "type": "mutation",
11921
11879
  "path": [
@@ -13683,52 +13641,1473 @@ Filter leaves are one column each, written as the column name prefixed with $, c
13683
13641
  }
13684
13642
  },
13685
13643
  {
13686
- "id": "searchUsers",
13687
- "domain": "membersProgram",
13688
- "description": "Search and explore members (loyalty guests) and their activity. Returns a page of the most recent user events, one per member, each carrying the member's serialNumber plus event details (type, time, and related object). Filters: query (free-text name search), eventTypes (e.g. sentText, receivedText, scan, order, rewardAwarded, rewardRedeemed, checkout, and the *Attribution types), progressMinBound/progressMaxBound (visit-count range), isUnread=true (members with unanswered inbound texts), orderBy (ASC|DESC by event time). Paginate with limit (default 100) and cursor (the `cursor` returned by the previous call; undefined cursor means no more pages).",
13689
- "needsApproval": false,
13690
- "type": "query",
13644
+ "id": "savePassConfiguration",
13645
+ "domain": "passBuilder",
13646
+ "description": "Save the organization's wallet pass configuration. This is a full-document save, not a patch: send the complete configuration, so the normal flow is readPassConfiguration, modify the returned document, then save it back. Anything you omit is dropped. Each save appends a new version rather than editing the current one, and when the sections, features, or locations differ from the previous version every pass already in a guest's wallet is re-pushed with the new design.",
13647
+ "needsApproval": true,
13648
+ "type": "mutation",
13691
13649
  "path": [
13692
13650
  "api",
13693
- "loyalty",
13694
- "app",
13695
- "searchUsersV3"
13651
+ "passBuilder",
13652
+ "create"
13696
13653
  ],
13697
13654
  "inputJsonSchema": {
13698
13655
  "type": "object",
13699
13656
  "properties": {
13700
- "cursor": {
13701
- "type": "number"
13702
- },
13703
- "eventTypes": {
13704
- "type": "array",
13705
- "items": {
13706
- "type": "string",
13707
- "enum": [
13708
- "sentText",
13709
- "receivedText",
13710
- "scan",
13711
- "order",
13712
- "passCreated",
13713
- "passUpdated",
13714
- "passRegistered",
13715
- "passDeleted",
13716
- "rewardAwarded",
13717
- "rewardRedeemed",
13718
- "rewardExpiration",
13719
- "fbAttribution",
13720
- "influencerAttribution",
13721
- "tiktokAttribution",
13722
- "googleAttribution",
13723
- "miscAttribution",
13724
- "referralAttribution",
13725
- "checkout",
13726
- "customerInference",
13727
- "reservationCreated"
13728
- ]
13729
- }
13730
- },
13731
- "limit": {
13657
+ "sections": {
13658
+ "type": "object",
13659
+ "properties": {
13660
+ "headerFields": {
13661
+ "type": "array",
13662
+ "items": {
13663
+ "type": "object",
13664
+ "properties": {
13665
+ "key": {
13666
+ "type": "string"
13667
+ },
13668
+ "label": {
13669
+ "type": "string"
13670
+ },
13671
+ "valueRules": {
13672
+ "type": "array",
13673
+ "items": {
13674
+ "type": "object",
13675
+ "properties": {
13676
+ "ruleId": {
13677
+ "type": "string"
13678
+ },
13679
+ "conditions": {
13680
+ "type": "array",
13681
+ "items": {
13682
+ "anyOf": [
13683
+ {
13684
+ "type": "object",
13685
+ "properties": {
13686
+ "type": {
13687
+ "type": "string",
13688
+ "const": "event"
13689
+ },
13690
+ "event": {
13691
+ "type": "object",
13692
+ "properties": {
13693
+ "event": {
13694
+ "anyOf": [
13695
+ {
13696
+ "type": "object",
13697
+ "properties": {
13698
+ "type": {
13699
+ "type": "string",
13700
+ "const": "receiveAutomation"
13701
+ },
13702
+ "receiveAutomation": {
13703
+ "type": "object",
13704
+ "properties": {
13705
+ "automationId": {
13706
+ "type": "string"
13707
+ }
13708
+ },
13709
+ "additionalProperties": false
13710
+ }
13711
+ },
13712
+ "required": [
13713
+ "type",
13714
+ "receiveAutomation"
13715
+ ],
13716
+ "additionalProperties": false
13717
+ },
13718
+ {
13719
+ "type": "object",
13720
+ "properties": {
13721
+ "type": {
13722
+ "type": "string",
13723
+ "const": "reply"
13724
+ },
13725
+ "reply": {
13726
+ "type": "object",
13727
+ "properties": {
13728
+ "automationId": {
13729
+ "type": "string"
13730
+ },
13731
+ "keywords": {
13732
+ "type": "array",
13733
+ "items": {
13734
+ "type": "string"
13735
+ }
13736
+ }
13737
+ },
13738
+ "additionalProperties": false
13739
+ }
13740
+ },
13741
+ "required": [
13742
+ "type",
13743
+ "reply"
13744
+ ],
13745
+ "additionalProperties": false
13746
+ },
13747
+ {
13748
+ "type": "object",
13749
+ "properties": {
13750
+ "type": {
13751
+ "type": "string",
13752
+ "const": "signUp"
13753
+ }
13754
+ },
13755
+ "required": [
13756
+ "type"
13757
+ ],
13758
+ "additionalProperties": false
13759
+ },
13760
+ {
13761
+ "type": "object",
13762
+ "properties": {
13763
+ "type": {
13764
+ "type": "string",
13765
+ "const": "importedCustomer"
13766
+ }
13767
+ },
13768
+ "required": [
13769
+ "type"
13770
+ ],
13771
+ "additionalProperties": false
13772
+ },
13773
+ {
13774
+ "type": "object",
13775
+ "properties": {
13776
+ "type": {
13777
+ "type": "string",
13778
+ "const": "addPass"
13779
+ }
13780
+ },
13781
+ "required": [
13782
+ "type"
13783
+ ],
13784
+ "additionalProperties": false
13785
+ },
13786
+ {
13787
+ "type": "object",
13788
+ "properties": {
13789
+ "type": {
13790
+ "type": "string",
13791
+ "const": "visit"
13792
+ },
13793
+ "visit": {
13794
+ "type": "object",
13795
+ "properties": {
13796
+ "locationId": {
13797
+ "type": "string"
13798
+ }
13799
+ },
13800
+ "additionalProperties": false
13801
+ }
13802
+ },
13803
+ "required": [
13804
+ "type"
13805
+ ],
13806
+ "additionalProperties": false
13807
+ },
13808
+ {
13809
+ "type": "object",
13810
+ "properties": {
13811
+ "type": {
13812
+ "type": "string",
13813
+ "const": "viewCampaign"
13814
+ },
13815
+ "viewCampaign": {
13816
+ "type": "object",
13817
+ "properties": {
13818
+ "campaignId": {
13819
+ "type": "string"
13820
+ }
13821
+ },
13822
+ "additionalProperties": false
13823
+ }
13824
+ },
13825
+ "required": [
13826
+ "type",
13827
+ "viewCampaign"
13828
+ ],
13829
+ "additionalProperties": false
13830
+ },
13831
+ {
13832
+ "type": "object",
13833
+ "properties": {
13834
+ "type": {
13835
+ "type": "string",
13836
+ "const": "offerRedemption"
13837
+ },
13838
+ "offerRedemption": {
13839
+ "type": "object",
13840
+ "properties": {
13841
+ "campaignId": {
13842
+ "type": "string"
13843
+ },
13844
+ "promoId": {
13845
+ "type": "string"
13846
+ },
13847
+ "offerId": {
13848
+ "type": "string"
13849
+ }
13850
+ },
13851
+ "additionalProperties": false
13852
+ }
13853
+ },
13854
+ "required": [
13855
+ "type"
13856
+ ],
13857
+ "additionalProperties": false
13858
+ },
13859
+ {
13860
+ "type": "object",
13861
+ "properties": {
13862
+ "type": {
13863
+ "type": "string",
13864
+ "const": "offerExpiration"
13865
+ },
13866
+ "offerExpiration": {
13867
+ "type": "object",
13868
+ "properties": {
13869
+ "campaignId": {
13870
+ "type": "string"
13871
+ },
13872
+ "promoId": {
13873
+ "type": "string"
13874
+ },
13875
+ "offerId": {
13876
+ "type": "string"
13877
+ }
13878
+ },
13879
+ "additionalProperties": false
13880
+ }
13881
+ },
13882
+ "required": [
13883
+ "type",
13884
+ "offerExpiration"
13885
+ ],
13886
+ "additionalProperties": false
13887
+ },
13888
+ {
13889
+ "type": "object",
13890
+ "properties": {
13891
+ "type": {
13892
+ "type": "string",
13893
+ "const": "checkout"
13894
+ },
13895
+ "checkout": {
13896
+ "type": "object",
13897
+ "properties": {
13898
+ "promoId": {
13899
+ "type": "string"
13900
+ },
13901
+ "campaignId": {
13902
+ "type": "string"
13903
+ }
13904
+ },
13905
+ "additionalProperties": false
13906
+ }
13907
+ },
13908
+ "required": [
13909
+ "type",
13910
+ "checkout"
13911
+ ],
13912
+ "additionalProperties": false
13913
+ },
13914
+ {
13915
+ "type": "object",
13916
+ "properties": {
13917
+ "type": {
13918
+ "type": "string",
13919
+ "const": "buttonClick"
13920
+ },
13921
+ "buttonClick": {
13922
+ "type": "object",
13923
+ "properties": {
13924
+ "buttonId": {
13925
+ "type": "string"
13926
+ },
13927
+ "eventName": {
13928
+ "type": "string"
13929
+ }
13930
+ },
13931
+ "required": [
13932
+ "buttonId",
13933
+ "eventName"
13934
+ ],
13935
+ "additionalProperties": false
13936
+ }
13937
+ },
13938
+ "required": [
13939
+ "type",
13940
+ "buttonClick"
13941
+ ],
13942
+ "additionalProperties": false
13943
+ },
13944
+ {
13945
+ "type": "object",
13946
+ "properties": {
13947
+ "type": {
13948
+ "type": "string",
13949
+ "const": "formSubmission"
13950
+ }
13951
+ },
13952
+ "required": [
13953
+ "type"
13954
+ ],
13955
+ "additionalProperties": false
13956
+ },
13957
+ {
13958
+ "type": "object",
13959
+ "properties": {
13960
+ "type": {
13961
+ "type": "string",
13962
+ "const": "invalidScan"
13963
+ }
13964
+ },
13965
+ "required": [
13966
+ "type"
13967
+ ],
13968
+ "additionalProperties": false
13969
+ },
13970
+ {
13971
+ "type": "object",
13972
+ "properties": {
13973
+ "type": {
13974
+ "type": "string",
13975
+ "const": "gotReferred"
13976
+ }
13977
+ },
13978
+ "required": [
13979
+ "type"
13980
+ ],
13981
+ "additionalProperties": false
13982
+ },
13983
+ {
13984
+ "type": "object",
13985
+ "properties": {
13986
+ "type": {
13987
+ "type": "string",
13988
+ "const": "referred"
13989
+ }
13990
+ },
13991
+ "required": [
13992
+ "type"
13993
+ ],
13994
+ "additionalProperties": false
13995
+ },
13996
+ {
13997
+ "type": "object",
13998
+ "properties": {
13999
+ "type": {
14000
+ "type": "string",
14001
+ "const": "subscriptionRenewal"
14002
+ },
14003
+ "subscriptionRenewal": {
14004
+ "type": "object",
14005
+ "properties": {
14006
+ "campaignId": {
14007
+ "type": "string"
14008
+ }
14009
+ },
14010
+ "additionalProperties": false
14011
+ }
14012
+ },
14013
+ "required": [
14014
+ "type"
14015
+ ],
14016
+ "additionalProperties": false
14017
+ },
14018
+ {
14019
+ "type": "object",
14020
+ "properties": {
14021
+ "type": {
14022
+ "type": "string",
14023
+ "const": "pickupDate"
14024
+ },
14025
+ "pickupDate": {
14026
+ "type": "object",
14027
+ "properties": {
14028
+ "campaignId": {
14029
+ "type": "string"
14030
+ }
14031
+ },
14032
+ "additionalProperties": false
14033
+ }
14034
+ },
14035
+ "required": [
14036
+ "type"
14037
+ ],
14038
+ "additionalProperties": false
14039
+ }
14040
+ ]
14041
+ },
14042
+ "occur": {
14043
+ "anyOf": [
14044
+ {
14045
+ "type": "object",
14046
+ "properties": {
14047
+ "match": {
14048
+ "type": "string",
14049
+ "enum": [
14050
+ "GTE",
14051
+ "LTE",
14052
+ "EQ"
14053
+ ]
14054
+ },
14055
+ "duration": {
14056
+ "type": "number",
14057
+ "description": "Duration in milliseconds"
14058
+ }
14059
+ },
14060
+ "required": [
14061
+ "match",
14062
+ "duration"
14063
+ ],
14064
+ "additionalProperties": false,
14065
+ "description": "Time-based occur. Use match (GTE/LTE/EQ) and duration (ms). No 'relative' type."
14066
+ },
14067
+ {
14068
+ "type": "object",
14069
+ "properties": {
14070
+ "type": {
14071
+ "type": "string",
14072
+ "const": "today"
14073
+ }
14074
+ },
14075
+ "required": [
14076
+ "type"
14077
+ ],
14078
+ "additionalProperties": false
14079
+ }
14080
+ ]
14081
+ }
14082
+ },
14083
+ "required": [
14084
+ "event"
14085
+ ],
14086
+ "additionalProperties": false,
14087
+ "description": "Nested: event.event is the AutomationEvent, event.occur is optional. NOT flat event/occur at top level."
14088
+ }
14089
+ },
14090
+ "required": [
14091
+ "type",
14092
+ "event"
14093
+ ],
14094
+ "additionalProperties": false
14095
+ },
14096
+ {
14097
+ "type": "object",
14098
+ "properties": {
14099
+ "type": {
14100
+ "type": "string",
14101
+ "const": "operatingSystem"
14102
+ },
14103
+ "operatingSystem": {
14104
+ "type": "object",
14105
+ "properties": {
14106
+ "os": {
14107
+ "type": "string",
14108
+ "enum": [
14109
+ "ios",
14110
+ "android"
14111
+ ]
14112
+ }
14113
+ },
14114
+ "required": [
14115
+ "os"
14116
+ ],
14117
+ "additionalProperties": false
14118
+ }
14119
+ },
14120
+ "required": [
14121
+ "type",
14122
+ "operatingSystem"
14123
+ ],
14124
+ "additionalProperties": false
14125
+ },
14126
+ {
14127
+ "type": "object",
14128
+ "properties": {
14129
+ "type": {
14130
+ "type": "string",
14131
+ "const": "and"
14132
+ },
14133
+ "conditions": {
14134
+ "type": "array",
14135
+ "items": {
14136
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14137
+ }
14138
+ }
14139
+ },
14140
+ "required": [
14141
+ "type",
14142
+ "conditions"
14143
+ ],
14144
+ "additionalProperties": false
14145
+ },
14146
+ {
14147
+ "type": "object",
14148
+ "properties": {
14149
+ "type": {
14150
+ "type": "string",
14151
+ "const": "or"
14152
+ },
14153
+ "conditions": {
14154
+ "type": "array",
14155
+ "items": {
14156
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14157
+ }
14158
+ }
14159
+ },
14160
+ "required": [
14161
+ "type",
14162
+ "conditions"
14163
+ ],
14164
+ "additionalProperties": false
14165
+ },
14166
+ {
14167
+ "type": "object",
14168
+ "properties": {
14169
+ "type": {
14170
+ "type": "string",
14171
+ "const": "not"
14172
+ },
14173
+ "condition": {
14174
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14175
+ }
14176
+ },
14177
+ "required": [
14178
+ "type",
14179
+ "condition"
14180
+ ],
14181
+ "additionalProperties": false
14182
+ },
14183
+ {
14184
+ "type": "object",
14185
+ "properties": {
14186
+ "type": {
14187
+ "type": "string",
14188
+ "const": "progress"
14189
+ },
14190
+ "progress": {
14191
+ "anyOf": [
14192
+ {
14193
+ "type": "object",
14194
+ "properties": {
14195
+ "type": {
14196
+ "type": "string",
14197
+ "const": "divisible"
14198
+ },
14199
+ "divisible": {
14200
+ "type": "object",
14201
+ "properties": {
14202
+ "offset": {
14203
+ "type": "number"
14204
+ },
14205
+ "step": {
14206
+ "type": "number"
14207
+ }
14208
+ },
14209
+ "required": [
14210
+ "offset",
14211
+ "step"
14212
+ ],
14213
+ "additionalProperties": false
14214
+ }
14215
+ },
14216
+ "required": [
14217
+ "type",
14218
+ "divisible"
14219
+ ],
14220
+ "additionalProperties": false
14221
+ },
14222
+ {
14223
+ "type": "object",
14224
+ "properties": {
14225
+ "type": {
14226
+ "type": "string",
14227
+ "const": "match"
14228
+ },
14229
+ "match": {
14230
+ "type": "object",
14231
+ "properties": {
14232
+ "match": {
14233
+ "type": "string",
14234
+ "enum": [
14235
+ "GTE",
14236
+ "LTE",
14237
+ "EQ"
14238
+ ]
14239
+ },
14240
+ "value": {
14241
+ "type": "number"
14242
+ }
14243
+ },
14244
+ "required": [
14245
+ "match",
14246
+ "value"
14247
+ ],
14248
+ "additionalProperties": false
14249
+ }
14250
+ },
14251
+ "required": [
14252
+ "type",
14253
+ "match"
14254
+ ],
14255
+ "additionalProperties": false
14256
+ }
14257
+ ]
14258
+ }
14259
+ },
14260
+ "required": [
14261
+ "type",
14262
+ "progress"
14263
+ ],
14264
+ "additionalProperties": false
14265
+ },
14266
+ {
14267
+ "type": "object",
14268
+ "properties": {
14269
+ "type": {
14270
+ "type": "string",
14271
+ "const": "passSubType"
14272
+ },
14273
+ "passSubType": {
14274
+ "type": "object",
14275
+ "properties": {
14276
+ "identifier": {
14277
+ "type": "string"
14278
+ }
14279
+ },
14280
+ "required": [
14281
+ "identifier"
14282
+ ],
14283
+ "additionalProperties": false
14284
+ }
14285
+ },
14286
+ "required": [
14287
+ "type",
14288
+ "passSubType"
14289
+ ],
14290
+ "additionalProperties": false
14291
+ },
14292
+ {
14293
+ "type": "object",
14294
+ "properties": {
14295
+ "type": {
14296
+ "type": "string",
14297
+ "const": "cohort"
14298
+ },
14299
+ "cohort": {
14300
+ "anyOf": [
14301
+ {
14302
+ "type": "object",
14303
+ "properties": {
14304
+ "type": {
14305
+ "type": "string",
14306
+ "const": "specific"
14307
+ },
14308
+ "specific": {
14309
+ "type": "object",
14310
+ "properties": {
14311
+ "cohort": {
14312
+ "type": "string"
14313
+ },
14314
+ "step": {
14315
+ "type": "string"
14316
+ }
14317
+ },
14318
+ "required": [
14319
+ "cohort",
14320
+ "step"
14321
+ ],
14322
+ "additionalProperties": false
14323
+ }
14324
+ },
14325
+ "required": [
14326
+ "type",
14327
+ "specific"
14328
+ ],
14329
+ "additionalProperties": false
14330
+ },
14331
+ {
14332
+ "type": "object",
14333
+ "properties": {
14334
+ "type": {
14335
+ "type": "string",
14336
+ "const": "none"
14337
+ }
14338
+ },
14339
+ "required": [
14340
+ "type"
14341
+ ],
14342
+ "additionalProperties": false
14343
+ }
14344
+ ]
14345
+ }
14346
+ },
14347
+ "required": [
14348
+ "type",
14349
+ "cohort"
14350
+ ],
14351
+ "additionalProperties": false
14352
+ },
14353
+ {
14354
+ "type": "object",
14355
+ "properties": {
14356
+ "type": {
14357
+ "type": "string",
14358
+ "const": "dayOfWeek"
14359
+ },
14360
+ "dayOfWeek": {
14361
+ "type": "object",
14362
+ "properties": {
14363
+ "localDayOfWeek": {
14364
+ "type": "number",
14365
+ "minimum": 0,
14366
+ "maximum": 6
14367
+ }
14368
+ },
14369
+ "required": [
14370
+ "localDayOfWeek"
14371
+ ],
14372
+ "additionalProperties": false
14373
+ }
14374
+ },
14375
+ "required": [
14376
+ "type",
14377
+ "dayOfWeek"
14378
+ ],
14379
+ "additionalProperties": false
14380
+ },
14381
+ {
14382
+ "type": "object",
14383
+ "properties": {
14384
+ "type": {
14385
+ "type": "string",
14386
+ "const": "hasActiveSubscription"
14387
+ }
14388
+ },
14389
+ "required": [
14390
+ "type"
14391
+ ],
14392
+ "additionalProperties": false
14393
+ },
14394
+ {
14395
+ "type": "object",
14396
+ "properties": {
14397
+ "type": {
14398
+ "type": "string",
14399
+ "const": "customProperty"
14400
+ },
14401
+ "customProperty": {
14402
+ "anyOf": [
14403
+ {
14404
+ "type": "object",
14405
+ "properties": {
14406
+ "propertyId": {
14407
+ "type": "string"
14408
+ },
14409
+ "propertyType": {
14410
+ "type": "string",
14411
+ "const": "number"
14412
+ },
14413
+ "number": {
14414
+ "type": "object",
14415
+ "properties": {
14416
+ "operator": {
14417
+ "type": "string",
14418
+ "enum": [
14419
+ "GTE",
14420
+ "LTE",
14421
+ "EQ",
14422
+ "GT",
14423
+ "LT"
14424
+ ]
14425
+ },
14426
+ "value": {
14427
+ "type": "number"
14428
+ }
14429
+ },
14430
+ "required": [
14431
+ "operator",
14432
+ "value"
14433
+ ],
14434
+ "additionalProperties": false
14435
+ }
14436
+ },
14437
+ "required": [
14438
+ "propertyId",
14439
+ "propertyType",
14440
+ "number"
14441
+ ],
14442
+ "additionalProperties": false
14443
+ },
14444
+ {
14445
+ "type": "object",
14446
+ "properties": {
14447
+ "propertyId": {
14448
+ "type": "string"
14449
+ },
14450
+ "propertyType": {
14451
+ "type": "string",
14452
+ "const": "dayOfYear"
14453
+ },
14454
+ "dayOfYear": {
14455
+ "anyOf": [
14456
+ {
14457
+ "type": "object",
14458
+ "properties": {
14459
+ "type": {
14460
+ "type": "string",
14461
+ "const": "relative"
14462
+ },
14463
+ "relative": {
14464
+ "type": "object",
14465
+ "properties": {
14466
+ "direction": {
14467
+ "type": "string",
14468
+ "enum": [
14469
+ "since",
14470
+ "until"
14471
+ ]
14472
+ },
14473
+ "operator": {
14474
+ "type": "string",
14475
+ "enum": [
14476
+ "GTE",
14477
+ "LTE",
14478
+ "EQ",
14479
+ "GT",
14480
+ "LT"
14481
+ ]
14482
+ },
14483
+ "durationMs": {
14484
+ "type": "number"
14485
+ }
14486
+ },
14487
+ "required": [
14488
+ "direction",
14489
+ "operator",
14490
+ "durationMs"
14491
+ ],
14492
+ "additionalProperties": false
14493
+ }
14494
+ },
14495
+ "required": [
14496
+ "type",
14497
+ "relative"
14498
+ ],
14499
+ "additionalProperties": false
14500
+ }
14501
+ ]
14502
+ }
14503
+ },
14504
+ "required": [
14505
+ "propertyId",
14506
+ "propertyType",
14507
+ "dayOfYear"
14508
+ ],
14509
+ "additionalProperties": false
14510
+ },
14511
+ {
14512
+ "type": "object",
14513
+ "properties": {
14514
+ "propertyId": {
14515
+ "type": "string"
14516
+ },
14517
+ "propertyType": {
14518
+ "type": "string",
14519
+ "const": "date"
14520
+ },
14521
+ "date": {
14522
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items/anyOf/10/properties/customProperty/anyOf/1/properties/dayOfYear"
14523
+ }
14524
+ },
14525
+ "required": [
14526
+ "propertyId",
14527
+ "propertyType",
14528
+ "date"
14529
+ ],
14530
+ "additionalProperties": false
14531
+ },
14532
+ {
14533
+ "type": "object",
14534
+ "properties": {
14535
+ "propertyId": {
14536
+ "type": "string"
14537
+ },
14538
+ "propertyType": {
14539
+ "type": "string",
14540
+ "const": "boolean"
14541
+ },
14542
+ "boolean": {
14543
+ "type": "object",
14544
+ "properties": {
14545
+ "value": {
14546
+ "type": "boolean"
14547
+ }
14548
+ },
14549
+ "required": [
14550
+ "value"
14551
+ ],
14552
+ "additionalProperties": false
14553
+ }
14554
+ },
14555
+ "required": [
14556
+ "propertyId",
14557
+ "propertyType",
14558
+ "boolean"
14559
+ ],
14560
+ "additionalProperties": false
14561
+ },
14562
+ {
14563
+ "type": "object",
14564
+ "properties": {
14565
+ "propertyId": {
14566
+ "type": "string"
14567
+ },
14568
+ "propertyType": {
14569
+ "type": "string",
14570
+ "const": "string"
14571
+ },
14572
+ "string": {
14573
+ "type": "object",
14574
+ "properties": {
14575
+ "operator": {
14576
+ "type": "string",
14577
+ "enum": [
14578
+ "EQ",
14579
+ "NEQ",
14580
+ "CONTAINS",
14581
+ "STARTS_WITH",
14582
+ "ENDS_WITH"
14583
+ ]
14584
+ },
14585
+ "value": {
14586
+ "type": "string"
14587
+ }
14588
+ },
14589
+ "required": [
14590
+ "operator",
14591
+ "value"
14592
+ ],
14593
+ "additionalProperties": false
14594
+ }
14595
+ },
14596
+ "required": [
14597
+ "propertyId",
14598
+ "propertyType",
14599
+ "string"
14600
+ ],
14601
+ "additionalProperties": false
14602
+ }
14603
+ ]
14604
+ }
14605
+ },
14606
+ "required": [
14607
+ "type",
14608
+ "customProperty"
14609
+ ],
14610
+ "additionalProperties": false
14611
+ }
14612
+ ]
14613
+ }
14614
+ },
14615
+ "value": {
14616
+ "anyOf": [
14617
+ {
14618
+ "type": "object",
14619
+ "properties": {
14620
+ "type": {
14621
+ "type": "string",
14622
+ "const": "property"
14623
+ },
14624
+ "property": {
14625
+ "anyOf": [
14626
+ {
14627
+ "type": "object",
14628
+ "properties": {
14629
+ "type": {
14630
+ "type": "string",
14631
+ "const": "name"
14632
+ },
14633
+ "name": {
14634
+ "type": "object",
14635
+ "properties": {},
14636
+ "additionalProperties": false
14637
+ }
14638
+ },
14639
+ "required": [
14640
+ "type",
14641
+ "name"
14642
+ ],
14643
+ "additionalProperties": false
14644
+ },
14645
+ {
14646
+ "type": "object",
14647
+ "properties": {
14648
+ "type": {
14649
+ "type": "string",
14650
+ "const": "phoneNumber"
14651
+ },
14652
+ "phoneNumber": {
14653
+ "type": "object",
14654
+ "properties": {},
14655
+ "additionalProperties": false
14656
+ }
14657
+ },
14658
+ "required": [
14659
+ "type",
14660
+ "phoneNumber"
14661
+ ],
14662
+ "additionalProperties": false
14663
+ },
14664
+ {
14665
+ "type": "object",
14666
+ "properties": {
14667
+ "type": {
14668
+ "type": "string",
14669
+ "const": "progress"
14670
+ },
14671
+ "progress": {
14672
+ "type": "object",
14673
+ "properties": {},
14674
+ "additionalProperties": false
14675
+ }
14676
+ },
14677
+ "required": [
14678
+ "type",
14679
+ "progress"
14680
+ ],
14681
+ "additionalProperties": false
14682
+ },
14683
+ {
14684
+ "type": "object",
14685
+ "properties": {
14686
+ "type": {
14687
+ "type": "string",
14688
+ "const": "memberNumber"
14689
+ },
14690
+ "memberNumber": {
14691
+ "type": "object",
14692
+ "properties": {},
14693
+ "additionalProperties": false
14694
+ }
14695
+ },
14696
+ "required": [
14697
+ "type",
14698
+ "memberNumber"
14699
+ ],
14700
+ "additionalProperties": false
14701
+ },
14702
+ {
14703
+ "type": "object",
14704
+ "properties": {
14705
+ "type": {
14706
+ "type": "string",
14707
+ "const": "lastVisit"
14708
+ },
14709
+ "lastVisit": {
14710
+ "type": "object",
14711
+ "properties": {},
14712
+ "additionalProperties": false
14713
+ }
14714
+ },
14715
+ "required": [
14716
+ "type",
14717
+ "lastVisit"
14718
+ ],
14719
+ "additionalProperties": false
14720
+ }
14721
+ ]
14722
+ }
14723
+ },
14724
+ "required": [
14725
+ "type",
14726
+ "property"
14727
+ ],
14728
+ "additionalProperties": false
14729
+ },
14730
+ {
14731
+ "type": "object",
14732
+ "properties": {
14733
+ "type": {
14734
+ "type": "string",
14735
+ "const": "expression"
14736
+ },
14737
+ "expression": {
14738
+ "anyOf": [
14739
+ {
14740
+ "type": "object",
14741
+ "properties": {
14742
+ "type": {
14743
+ "type": "string",
14744
+ "const": "string"
14745
+ },
14746
+ "string": {
14747
+ "type": "object",
14748
+ "properties": {
14749
+ "text": {
14750
+ "type": "string"
14751
+ }
14752
+ },
14753
+ "required": [
14754
+ "text"
14755
+ ],
14756
+ "additionalProperties": false
14757
+ }
14758
+ },
14759
+ "required": [
14760
+ "type",
14761
+ "string"
14762
+ ],
14763
+ "additionalProperties": false
14764
+ }
14765
+ ]
14766
+ }
14767
+ },
14768
+ "required": [
14769
+ "type",
14770
+ "expression"
14771
+ ],
14772
+ "additionalProperties": false
14773
+ }
14774
+ ]
14775
+ }
14776
+ },
14777
+ "required": [
14778
+ "ruleId",
14779
+ "conditions",
14780
+ "value"
14781
+ ],
14782
+ "additionalProperties": false
14783
+ }
14784
+ },
14785
+ "alignment": {
14786
+ "type": "string",
14787
+ "enum": [
14788
+ "left",
14789
+ "center",
14790
+ "right"
14791
+ ]
14792
+ }
14793
+ },
14794
+ "required": [
14795
+ "key",
14796
+ "label",
14797
+ "valueRules"
14798
+ ],
14799
+ "additionalProperties": false
14800
+ }
14801
+ },
14802
+ "auxiliaryFields": {
14803
+ "type": "array",
14804
+ "items": {
14805
+ "$ref": "#/properties/sections/properties/headerFields/items"
14806
+ }
14807
+ },
14808
+ "secondaryFields": {
14809
+ "type": "array",
14810
+ "items": {
14811
+ "$ref": "#/properties/sections/properties/headerFields/items"
14812
+ }
14813
+ },
14814
+ "logoFile": {
14815
+ "type": "object",
14816
+ "properties": {
14817
+ "conditionalRules": {
14818
+ "type": "array",
14819
+ "items": {
14820
+ "type": "object",
14821
+ "properties": {
14822
+ "ruleId": {
14823
+ "type": "string"
14824
+ },
14825
+ "conditions": {
14826
+ "type": "array",
14827
+ "items": {
14828
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14829
+ }
14830
+ },
14831
+ "file": {
14832
+ "type": "string"
14833
+ },
14834
+ "originalFile": {
14835
+ "type": "string"
14836
+ },
14837
+ "crop": {
14838
+ "type": "object",
14839
+ "properties": {
14840
+ "cropPosition": {
14841
+ "type": "object",
14842
+ "properties": {
14843
+ "x": {
14844
+ "type": "number"
14845
+ },
14846
+ "y": {
14847
+ "type": "number"
14848
+ }
14849
+ },
14850
+ "required": [
14851
+ "x",
14852
+ "y"
14853
+ ],
14854
+ "additionalProperties": false
14855
+ },
14856
+ "zoom": {
14857
+ "type": "number"
14858
+ },
14859
+ "croppedAreaPixels": {
14860
+ "type": "object",
14861
+ "properties": {
14862
+ "x": {
14863
+ "type": "number"
14864
+ },
14865
+ "y": {
14866
+ "type": "number"
14867
+ },
14868
+ "width": {
14869
+ "type": "number"
14870
+ },
14871
+ "height": {
14872
+ "type": "number"
14873
+ }
14874
+ },
14875
+ "required": [
14876
+ "x",
14877
+ "y",
14878
+ "width",
14879
+ "height"
14880
+ ],
14881
+ "additionalProperties": false
14882
+ }
14883
+ },
14884
+ "required": [
14885
+ "cropPosition",
14886
+ "zoom",
14887
+ "croppedAreaPixels"
14888
+ ],
14889
+ "additionalProperties": false
14890
+ }
14891
+ },
14892
+ "required": [
14893
+ "ruleId",
14894
+ "conditions",
14895
+ "file"
14896
+ ],
14897
+ "additionalProperties": false
14898
+ }
14899
+ }
14900
+ },
14901
+ "required": [
14902
+ "conditionalRules"
14903
+ ],
14904
+ "additionalProperties": false
14905
+ },
14906
+ "stripFile": {
14907
+ "$ref": "#/properties/sections/properties/logoFile"
14908
+ },
14909
+ "backgroundColor": {
14910
+ "type": "object",
14911
+ "properties": {
14912
+ "conditionalRules": {
14913
+ "type": "array",
14914
+ "items": {
14915
+ "type": "object",
14916
+ "properties": {
14917
+ "ruleId": {
14918
+ "type": "string"
14919
+ },
14920
+ "conditions": {
14921
+ "type": "array",
14922
+ "items": {
14923
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14924
+ }
14925
+ },
14926
+ "color": {
14927
+ "type": "string"
14928
+ }
14929
+ },
14930
+ "required": [
14931
+ "ruleId",
14932
+ "conditions",
14933
+ "color"
14934
+ ],
14935
+ "additionalProperties": false
14936
+ }
14937
+ }
14938
+ },
14939
+ "required": [
14940
+ "conditionalRules"
14941
+ ],
14942
+ "additionalProperties": false
14943
+ },
14944
+ "labelColor": {
14945
+ "$ref": "#/properties/sections/properties/backgroundColor"
14946
+ }
14947
+ },
14948
+ "required": [
14949
+ "headerFields",
14950
+ "auxiliaryFields",
14951
+ "secondaryFields",
14952
+ "logoFile",
14953
+ "stripFile",
14954
+ "backgroundColor",
14955
+ "labelColor"
14956
+ ],
14957
+ "additionalProperties": false
14958
+ },
14959
+ "features": {
14960
+ "type": "object",
14961
+ "properties": {
14962
+ "renderRewards": {
14963
+ "type": "boolean"
14964
+ },
14965
+ "campaignTakeover": {
14966
+ "type": "boolean"
14967
+ },
14968
+ "rewardLabelRules": {
14969
+ "type": "array",
14970
+ "items": {
14971
+ "type": "object",
14972
+ "properties": {
14973
+ "ruleId": {
14974
+ "type": "string"
14975
+ },
14976
+ "conditions": {
14977
+ "type": "array",
14978
+ "items": {
14979
+ "$ref": "#/properties/sections/properties/headerFields/items/properties/valueRules/items/properties/conditions/items"
14980
+ }
14981
+ },
14982
+ "label": {
14983
+ "type": "string"
14984
+ }
14985
+ },
14986
+ "required": [
14987
+ "ruleId",
14988
+ "conditions",
14989
+ "label"
14990
+ ],
14991
+ "additionalProperties": false
14992
+ }
14993
+ }
14994
+ },
14995
+ "required": [
14996
+ "renderRewards",
14997
+ "campaignTakeover"
14998
+ ],
14999
+ "additionalProperties": false
15000
+ },
15001
+ "locations": {
15002
+ "type": "object",
15003
+ "properties": {
15004
+ "locations": {
15005
+ "type": "array",
15006
+ "items": {
15007
+ "type": "object",
15008
+ "properties": {
15009
+ "locationId": {
15010
+ "type": "string"
15011
+ },
15012
+ "latitude": {
15013
+ "type": "number"
15014
+ },
15015
+ "longitude": {
15016
+ "type": "number"
15017
+ },
15018
+ "altitude": {
15019
+ "type": "number"
15020
+ },
15021
+ "relevantText": {
15022
+ "type": "string"
15023
+ }
15024
+ },
15025
+ "required": [
15026
+ "latitude",
15027
+ "longitude"
15028
+ ],
15029
+ "additionalProperties": false
15030
+ }
15031
+ }
15032
+ },
15033
+ "required": [
15034
+ "locations"
15035
+ ],
15036
+ "additionalProperties": false
15037
+ },
15038
+ "metadata": {
15039
+ "type": "object",
15040
+ "properties": {
15041
+ "name": {
15042
+ "type": "string"
15043
+ },
15044
+ "description": {
15045
+ "type": "string"
15046
+ }
15047
+ },
15048
+ "required": [
15049
+ "name",
15050
+ "description"
15051
+ ],
15052
+ "additionalProperties": false
15053
+ }
15054
+ },
15055
+ "required": [
15056
+ "sections",
15057
+ "features",
15058
+ "metadata"
15059
+ ],
15060
+ "additionalProperties": false,
15061
+ "$schema": "http://json-schema.org/draft-07/schema#"
15062
+ }
15063
+ },
15064
+ {
15065
+ "id": "searchUsers",
15066
+ "domain": "membersProgram",
15067
+ "description": "Search and explore members (loyalty guests) and their activity. Returns a page of the most recent user events, one per member, each carrying the member's serialNumber plus event details (type, time, and related object). Filters: query (free-text name search), eventTypes (e.g. sentText, receivedText, scan, order, rewardAwarded, rewardRedeemed, checkout, and the *Attribution types), progressMinBound/progressMaxBound (visit-count range), isUnread=true (members with unanswered inbound texts), orderBy (ASC|DESC by event time). Paginate with limit (default 100) and cursor (the `cursor` returned by the previous call; undefined cursor means no more pages).",
15068
+ "needsApproval": false,
15069
+ "type": "query",
15070
+ "path": [
15071
+ "api",
15072
+ "loyalty",
15073
+ "app",
15074
+ "searchUsersV3"
15075
+ ],
15076
+ "inputJsonSchema": {
15077
+ "type": "object",
15078
+ "properties": {
15079
+ "cursor": {
15080
+ "type": "number"
15081
+ },
15082
+ "eventTypes": {
15083
+ "type": "array",
15084
+ "items": {
15085
+ "type": "string",
15086
+ "enum": [
15087
+ "sentText",
15088
+ "receivedText",
15089
+ "scan",
15090
+ "order",
15091
+ "passCreated",
15092
+ "passUpdated",
15093
+ "passRegistered",
15094
+ "passDeleted",
15095
+ "rewardAwarded",
15096
+ "rewardRedeemed",
15097
+ "rewardExpiration",
15098
+ "fbAttribution",
15099
+ "influencerAttribution",
15100
+ "tiktokAttribution",
15101
+ "googleAttribution",
15102
+ "miscAttribution",
15103
+ "referralAttribution",
15104
+ "checkout",
15105
+ "customerInference",
15106
+ "reservationCreated"
15107
+ ]
15108
+ }
15109
+ },
15110
+ "limit": {
13732
15111
  "type": "number"
13733
15112
  },
13734
15113
  "query": {
@@ -17292,6 +18671,45 @@ Filter leaves are one column each, written as the column name prefixed with $, c
17292
18671
  "$schema": "http://json-schema.org/draft-07/schema#"
17293
18672
  }
17294
18673
  },
18674
+ {
18675
+ "id": "stageFunnelScreen",
18676
+ "domain": "funnel",
18677
+ "description": "Stage a new, empty funnel screen onto a funnel draft. The screen only exists on the draft until saveFunnelEdits promotes it, and can be filled in with stageFunnelEdit using the returned screenId. On a campaign draft the screen is campaign-scoped unless campaignScoped is false.",
18678
+ "needsApproval": false,
18679
+ "type": "mutation",
18680
+ "path": [
18681
+ "api",
18682
+ "layoutEngine",
18683
+ "drafts",
18684
+ "stageScreenCreate"
18685
+ ],
18686
+ "inputJsonSchema": {
18687
+ "type": "object",
18688
+ "properties": {
18689
+ "draftId": {
18690
+ "type": "string"
18691
+ },
18692
+ "title": {
18693
+ "type": "string"
18694
+ },
18695
+ "description": {
18696
+ "type": "string"
18697
+ },
18698
+ "screenId": {
18699
+ "type": "string"
18700
+ },
18701
+ "campaignScoped": {
18702
+ "type": "boolean"
18703
+ }
18704
+ },
18705
+ "required": [
18706
+ "draftId",
18707
+ "title"
18708
+ ],
18709
+ "additionalProperties": false,
18710
+ "$schema": "http://json-schema.org/draft-07/schema#"
18711
+ }
18712
+ },
17295
18713
  {
17296
18714
  "id": "updateAutomationFlow",
17297
18715
  "domain": "automations",
@@ -18265,6 +19683,234 @@ ${available}`
18265
19683
  };
18266
19684
  }
18267
19685
 
19686
+ // src/actingContext.ts
19687
+ function environmentAccessToken() {
19688
+ const token = process.env.FEAST_ACCESS_TOKEN;
19689
+ return token != null && token.length > 0 ? token : void 0;
19690
+ }
19691
+ function firstNonEmpty(...values) {
19692
+ for (const value of values) {
19693
+ if (value != null && value.length > 0) {
19694
+ return value;
19695
+ }
19696
+ }
19697
+ return void 0;
19698
+ }
19699
+ function roleLabelFromArn(arn) {
19700
+ return arn.split("/").pop()?.split(".").pop() ?? arn;
19701
+ }
19702
+ function buildPreferredRole(organizationId, role) {
19703
+ if (role.startsWith("arn:")) {
19704
+ return role;
19705
+ }
19706
+ if (!CLI_MANIFEST.organizationRoles.includes(role)) {
19707
+ throw new Error(
19708
+ `Unknown role "${role}". Expected one of ${CLI_MANIFEST.organizationRoles.join(", ")}, or a full role ARN.`
19709
+ );
19710
+ }
19711
+ return formRoleArn(organizationId, role);
19712
+ }
19713
+ function resolvePinned(pinned, flag, variableName, flagName) {
19714
+ if (pinned == null) {
19715
+ return flag;
19716
+ }
19717
+ if (flag != null && flag !== pinned) {
19718
+ throw new Error(
19719
+ `${variableName} pins this session to ${pinned}, so ${flagName} ${flag} is not allowed.`
19720
+ );
19721
+ }
19722
+ return pinned;
19723
+ }
19724
+ function resolveFromEnvironment(accessToken, orgFlag, roleFlag) {
19725
+ const organizationId = resolvePinned(
19726
+ firstNonEmpty(process.env.FEAST_ORGANIZATION_ID),
19727
+ orgFlag,
19728
+ "FEAST_ORGANIZATION_ID",
19729
+ "--org"
19730
+ );
19731
+ if (organizationId == null) {
19732
+ throw new Error(
19733
+ "FEAST_ACCESS_TOKEN is set, so the organization cannot be read from the token. Pass --org <organizationId> or set FEAST_ORGANIZATION_ID."
19734
+ );
19735
+ }
19736
+ const role = resolvePinned(
19737
+ firstNonEmpty(process.env.FEAST_PREFERRED_ROLE),
19738
+ roleFlag,
19739
+ "FEAST_PREFERRED_ROLE",
19740
+ "--role"
19741
+ );
19742
+ if (role == null) {
19743
+ throw new Error(
19744
+ "FEAST_ACCESS_TOKEN is set, so the role cannot be read from the token. Pass --role <role> or set FEAST_PREFERRED_ROLE."
19745
+ );
19746
+ }
19747
+ const preferredRole = buildPreferredRole(organizationId, role);
19748
+ return {
19749
+ accessToken,
19750
+ organizationId,
19751
+ preferredRole,
19752
+ roleLabel: roleLabelFromArn(preferredRole),
19753
+ source: "environment"
19754
+ };
19755
+ }
19756
+ async function resolveActingContext(orgFlag, roleFlag) {
19757
+ const environmentToken = environmentAccessToken();
19758
+ if (environmentToken != null) {
19759
+ return resolveFromEnvironment(environmentToken, orgFlag, roleFlag);
19760
+ }
19761
+ const tokens = await ensureFreshTokens();
19762
+ const accessToken = tokens.accessToken.jwtToken;
19763
+ const organizationId = resolveOrganizationId(accessToken, orgFlag);
19764
+ const resolved = resolvePreferredRole(accessToken, organizationId);
19765
+ return {
19766
+ accessToken,
19767
+ organizationId,
19768
+ preferredRole: resolved.arn,
19769
+ roleLabel: resolved.orgRole.role,
19770
+ source: "credentials"
19771
+ };
19772
+ }
19773
+
19774
+ // src/browserLogin.ts
19775
+ import { spawn } from "child_process";
19776
+ import { createHash, randomBytes } from "crypto";
19777
+ import http from "http";
19778
+ var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
19779
+ function base64url(buffer) {
19780
+ return buffer.toString("base64url");
19781
+ }
19782
+ function tabHtml(title, body) {
19783
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
19784
+ <style>body{font-family:-apple-system,system-ui,sans-serif;background:#0f0f0f;color:#f5f5f5;
19785
+ display:flex;min-height:100vh;align-items:center;justify-content:center;margin:0}
19786
+ .card{text-align:center;padding:2rem 3rem}.t{font-size:1.4rem;font-weight:600;margin-bottom:.5rem}
19787
+ .b{color:#a1a1a1}</style></head><body><div class="card">
19788
+ <div class="t">${title}</div><div class="b">${body}</div></div></body></html>`;
19789
+ }
19790
+ function openBrowser(url) {
19791
+ let command;
19792
+ let args;
19793
+ if (process.platform === "darwin") {
19794
+ command = "open";
19795
+ args = [url];
19796
+ } else if (process.platform === "win32") {
19797
+ command = "cmd";
19798
+ args = ["/c", "start", "", url];
19799
+ } else {
19800
+ command = "xdg-open";
19801
+ args = [url];
19802
+ }
19803
+ try {
19804
+ const child = spawn(command, args, { stdio: "ignore", detached: true });
19805
+ child.on("error", () => {
19806
+ });
19807
+ child.unref();
19808
+ } catch {
19809
+ }
19810
+ }
19811
+ function startLoopback(expectedState) {
19812
+ return new Promise((resolveOuter, rejectOuter) => {
19813
+ let resolveCode = () => {
19814
+ };
19815
+ let rejectCode = () => {
19816
+ };
19817
+ const waitForCode = new Promise((resolve, reject) => {
19818
+ resolveCode = resolve;
19819
+ rejectCode = reject;
19820
+ });
19821
+ waitForCode.catch(() => {
19822
+ });
19823
+ const server = http.createServer((req, res) => {
19824
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
19825
+ if (url.pathname !== "/callback") {
19826
+ res.writeHead(404);
19827
+ res.end();
19828
+ return;
19829
+ }
19830
+ const error = url.searchParams.get("error");
19831
+ if (error != null) {
19832
+ res.writeHead(200, { "Content-Type": "text/html" });
19833
+ res.end(tabHtml("Authorization declined", "You can close this tab."));
19834
+ rejectCode(new Error(`Authorization declined: ${error}`));
19835
+ return;
19836
+ }
19837
+ if (url.searchParams.get("state") !== expectedState) {
19838
+ res.writeHead(400, { "Content-Type": "text/html" });
19839
+ res.end(
19840
+ tabHtml("Invalid request", "State mismatch \u2014 retry from the CLI.")
19841
+ );
19842
+ rejectCode(new Error("State mismatch \u2014 aborting (possible CSRF)"));
19843
+ return;
19844
+ }
19845
+ const code = url.searchParams.get("code");
19846
+ if (code == null || code.length === 0) {
19847
+ res.writeHead(400);
19848
+ res.end();
19849
+ rejectCode(new Error("Authorization callback did not include a code"));
19850
+ return;
19851
+ }
19852
+ res.writeHead(200, {
19853
+ "Content-Type": "text/html",
19854
+ Connection: "close"
19855
+ });
19856
+ res.end(
19857
+ tabHtml(
19858
+ "You're all set \u2713",
19859
+ "The feast CLI is authorized. Close this tab and return to your terminal."
19860
+ )
19861
+ );
19862
+ resolveCode(code);
19863
+ });
19864
+ const timeout = setTimeout(() => {
19865
+ rejectCode(
19866
+ new Error("Timed out waiting for browser authorization (5 minutes)")
19867
+ );
19868
+ }, CALLBACK_TIMEOUT_MS);
19869
+ const close = () => {
19870
+ clearTimeout(timeout);
19871
+ server.closeAllConnections?.();
19872
+ server.close();
19873
+ };
19874
+ server.on("error", rejectOuter);
19875
+ server.listen(0, "127.0.0.1", () => {
19876
+ server.unref();
19877
+ const address = server.address();
19878
+ const port = typeof address === "object" && address != null ? address.port : 0;
19879
+ resolveOuter({ port, waitForCode, close });
19880
+ });
19881
+ });
19882
+ }
19883
+ async function browserLogin() {
19884
+ const codeVerifier = base64url(randomBytes(32));
19885
+ const codeChallenge = base64url(
19886
+ createHash("sha256").update(codeVerifier).digest()
19887
+ );
19888
+ const state = base64url(randomBytes(16));
19889
+ const { port, waitForCode, close } = await startLoopback(state);
19890
+ try {
19891
+ const authUrl = `${getWebBaseUrl()}/oauth?port=${port}&state=${encodeURIComponent(state)}&challenge=${encodeURIComponent(codeChallenge)}`;
19892
+ console.error("Opening your browser to authorize the feast CLI...");
19893
+ console.error(`If it doesn't open, visit:
19894
+ ${authUrl}
19895
+ `);
19896
+ openBrowser(authUrl);
19897
+ const code = await waitForCode;
19898
+ const client = createHttpCaller({});
19899
+ const response = await client.api.auth.exchangeAuthCode.mutate({
19900
+ code,
19901
+ codeVerifier
19902
+ });
19903
+ const tokens = response?.tokens;
19904
+ if (tokens?.accessToken?.jwtToken == null) {
19905
+ throw new Error("Authorization failed: empty token response");
19906
+ }
19907
+ saveTokens(tokens);
19908
+ return tokens;
19909
+ } finally {
19910
+ close();
19911
+ }
19912
+ }
19913
+
18268
19914
  // src/prompt.ts
18269
19915
  import readline from "readline";
18270
19916
  function promptText(question) {
@@ -18349,6 +19995,7 @@ Commands:
18349
19995
 
18350
19996
  Call options:
18351
19997
  --org <organizationId> Organization to act on (required unless you belong to exactly one)
19998
+ --role <role|roleArn> Role to act as, e.g. OWNER (required when FEAST_ACCESS_TOKEN is set)
18352
19999
  --input <json> Tool input as a JSON string (default: {})
18353
20000
  --input-file <path> Tool input from a JSON file
18354
20001
  --yes Skip the confirmation prompt for mutations
@@ -18356,11 +20003,18 @@ Call options:
18356
20003
  Environment:
18357
20004
  FEAST_API_URL Override the API base URL (default: production)
18358
20005
  FEAST_WEB_URL Override the web app base URL for browser login (default: production)
18359
- FEAST_NO_UPDATE_CHECK Disable the daily check for a newer published version`;
20006
+ FEAST_NO_UPDATE_CHECK Disable the daily check for a newer published version
20007
+
20008
+ Non-interactive credentials (for sandboxed agents):
20009
+ FEAST_ACCESS_TOKEN Access token to send verbatim. When set, no token is read
20010
+ from disk and none is refreshed, and the token is never
20011
+ decoded \u2014 so the organization and role must be supplied too.
20012
+ FEAST_ORGANIZATION_ID Pins the organization. --org must match it or be omitted.
20013
+ FEAST_PREFERRED_ROLE Pins the role. --role must match it or be omitted.`;
18360
20014
  function parseArgs(argv) {
18361
20015
  const positional = [];
18362
20016
  const flags = {};
18363
- const valueFlags = /* @__PURE__ */ new Set(["org", "input", "input-file", "domain"]);
20017
+ const valueFlags = /* @__PURE__ */ new Set(["org", "role", "input", "input-file", "domain"]);
18364
20018
  for (let i = 0; i < argv.length; i++) {
18365
20019
  const arg = argv[i];
18366
20020
  if (!arg.startsWith("--")) {
@@ -18382,6 +20036,11 @@ function parseArgs(argv) {
18382
20036
  return { positional, flags };
18383
20037
  }
18384
20038
  async function commandLogin(args) {
20039
+ if (environmentAccessToken() != null) {
20040
+ throw new Error(
20041
+ "FEAST_ACCESS_TOKEN is set, so this CLI is using credentials from the environment. Unset it to log in and store tokens on disk."
20042
+ );
20043
+ }
18385
20044
  if (args.flags["password"] === true) {
18386
20045
  const username = args.positional[0] ?? await promptText("Username: ");
18387
20046
  if (username.length === 0) {
@@ -18402,7 +20061,20 @@ async function commandLogin(args) {
18402
20061
  console.info(`Logged in as ${payload.username ?? payload.sub}`);
18403
20062
  console.info(`Tokens stored in ${getCredentialsPath()}`);
18404
20063
  }
18405
- async function commandWhoami() {
20064
+ async function commandWhoami(args) {
20065
+ if (environmentAccessToken() != null) {
20066
+ const acting = await resolveActingContext(
20067
+ args.flags["org"],
20068
+ args.flags["role"]
20069
+ );
20070
+ console.info("Credentials: FEAST_ACCESS_TOKEN (environment)");
20071
+ console.info(`Organization: ${acting.organizationId}`);
20072
+ console.info(`Role: ${acting.roleLabel}`);
20073
+ console.info(
20074
+ "The token is opaque to this CLI, so the user and their other organizations cannot be listed."
20075
+ );
20076
+ return;
20077
+ }
18406
20078
  const tokens = await ensureFreshTokens();
18407
20079
  const payload = decodeJwtPayload(tokens.accessToken.jwtToken);
18408
20080
  const orgRoles = listOrgRoles(tokens.accessToken.jwtToken);
@@ -18544,25 +20216,20 @@ ${issues.join("\n")}
18544
20216
  Run: feast describe ${tool.id}`
18545
20217
  );
18546
20218
  }
18547
- const tokens = await ensureFreshTokens();
18548
- const isWrite = tool.type === "mutation" || tool.needsApproval;
18549
- const organizationId = resolveOrganizationId(
18550
- tokens.accessToken.jwtToken,
18551
- args.flags["org"]
18552
- );
18553
- const resolved = resolvePreferredRole(
18554
- tokens.accessToken.jwtToken,
18555
- organizationId
20219
+ const acting = await resolveActingContext(
20220
+ args.flags["org"],
20221
+ args.flags["role"]
18556
20222
  );
20223
+ const isWrite = tool.type === "mutation" || tool.needsApproval;
18557
20224
  console.error(
18558
- `Acting on organization ${organizationId} as ${resolved.orgRole.role}`
20225
+ `Acting on organization ${acting.organizationId} as ${acting.roleLabel}`
18559
20226
  );
18560
20227
  const client = createHttpCaller({
18561
- accessToken: tokens.accessToken.jwtToken,
18562
- preferredRole: resolved.arn
20228
+ accessToken: acting.accessToken,
20229
+ preferredRole: acting.preferredRole
18563
20230
  });
18564
20231
  if (isWrite) {
18565
- const organization = await verifyOrganization(client, organizationId);
20232
+ const organization = await verifyOrganization(client, acting.organizationId);
18566
20233
  if (args.flags["yes"] !== true) {
18567
20234
  const orgLabel = organization.name != null ? `"${organization.name}" (${organization.id})` : organization.id;
18568
20235
  const approved = await promptConfirm(
@@ -18590,7 +20257,7 @@ async function runCli(argv) {
18590
20257
  console.info("Logged out");
18591
20258
  break;
18592
20259
  case "whoami":
18593
- await commandWhoami();
20260
+ await commandWhoami(args);
18594
20261
  break;
18595
20262
  case "tools":
18596
20263
  commandTools(args);