@drip-apex/apex-cli 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +40 -3
  2. package/dist/cli.js +1233 -362
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1159,8 +1159,8 @@ var require_command = __commonJS({
1159
1159
  "node_modules/commander/lib/command.js"(exports2) {
1160
1160
  var EventEmitter = require("node:events").EventEmitter;
1161
1161
  var childProcess2 = require("node:child_process");
1162
- var path25 = require("node:path");
1163
- var fs29 = require("node:fs");
1162
+ var path26 = require("node:path");
1163
+ var fs30 = require("node:fs");
1164
1164
  var process16 = require("node:process");
1165
1165
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1166
1166
  var { CommanderError: CommanderError2 } = require_error();
@@ -2141,7 +2141,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2141
2141
  * @param {string} subcommandName
2142
2142
  */
2143
2143
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2144
- if (fs29.existsSync(executableFile)) return;
2144
+ if (fs30.existsSync(executableFile)) return;
2145
2145
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2146
2146
  const executableMissing = `'${executableFile}' does not exist
2147
2147
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2159,11 +2159,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2159
2159
  let launchWithNode = false;
2160
2160
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2161
2161
  function findFile(baseDir, baseName) {
2162
- const localBin = path25.resolve(baseDir, baseName);
2163
- if (fs29.existsSync(localBin)) return localBin;
2164
- if (sourceExt.includes(path25.extname(baseName))) return void 0;
2162
+ const localBin = path26.resolve(baseDir, baseName);
2163
+ if (fs30.existsSync(localBin)) return localBin;
2164
+ if (sourceExt.includes(path26.extname(baseName))) return void 0;
2165
2165
  const foundExt = sourceExt.find(
2166
- (ext) => fs29.existsSync(`${localBin}${ext}`)
2166
+ (ext) => fs30.existsSync(`${localBin}${ext}`)
2167
2167
  );
2168
2168
  if (foundExt) return `${localBin}${foundExt}`;
2169
2169
  return void 0;
@@ -2175,21 +2175,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2175
2175
  if (this._scriptPath) {
2176
2176
  let resolvedScriptPath;
2177
2177
  try {
2178
- resolvedScriptPath = fs29.realpathSync(this._scriptPath);
2178
+ resolvedScriptPath = fs30.realpathSync(this._scriptPath);
2179
2179
  } catch {
2180
2180
  resolvedScriptPath = this._scriptPath;
2181
2181
  }
2182
- executableDir = path25.resolve(
2183
- path25.dirname(resolvedScriptPath),
2182
+ executableDir = path26.resolve(
2183
+ path26.dirname(resolvedScriptPath),
2184
2184
  executableDir
2185
2185
  );
2186
2186
  }
2187
2187
  if (executableDir) {
2188
2188
  let localFile = findFile(executableDir, executableFile);
2189
2189
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2190
- const legacyName = path25.basename(
2190
+ const legacyName = path26.basename(
2191
2191
  this._scriptPath,
2192
- path25.extname(this._scriptPath)
2192
+ path26.extname(this._scriptPath)
2193
2193
  );
2194
2194
  if (legacyName !== this._name) {
2195
2195
  localFile = findFile(
@@ -2200,7 +2200,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2200
2200
  }
2201
2201
  executableFile = localFile || executableFile;
2202
2202
  }
2203
- launchWithNode = sourceExt.includes(path25.extname(executableFile));
2203
+ launchWithNode = sourceExt.includes(path26.extname(executableFile));
2204
2204
  let proc;
2205
2205
  if (process16.platform !== "win32") {
2206
2206
  if (launchWithNode) {
@@ -3047,7 +3047,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3047
3047
  * @return {Command}
3048
3048
  */
3049
3049
  nameFromFilename(filename) {
3050
- this._name = path25.basename(filename, path25.extname(filename));
3050
+ this._name = path26.basename(filename, path26.extname(filename));
3051
3051
  return this;
3052
3052
  }
3053
3053
  /**
@@ -3061,9 +3061,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3061
3061
  * @param {string} [path]
3062
3062
  * @return {(string|null|Command)}
3063
3063
  */
3064
- executableDir(path26) {
3065
- if (path26 === void 0) return this._executableDir;
3066
- this._executableDir = path26;
3064
+ executableDir(path27) {
3065
+ if (path27 === void 0) return this._executableDir;
3066
+ this._executableDir = path27;
3067
3067
  return this;
3068
3068
  }
3069
3069
  /**
@@ -3797,18 +3797,18 @@ var init_source = __esm({
3797
3797
  }
3798
3798
  }
3799
3799
  });
3800
- createStyler = (open2, close, parent) => {
3800
+ createStyler = (open3, close, parent) => {
3801
3801
  let openAll;
3802
3802
  let closeAll;
3803
3803
  if (parent === void 0) {
3804
- openAll = open2;
3804
+ openAll = open3;
3805
3805
  closeAll = close;
3806
3806
  } else {
3807
- openAll = parent.openAll + open2;
3807
+ openAll = parent.openAll + open3;
3808
3808
  closeAll = close + parent.closeAll;
3809
3809
  }
3810
3810
  return {
3811
- open: open2,
3811
+ open: open3,
3812
3812
  close,
3813
3813
  openAll,
3814
3814
  closeAll,
@@ -6158,16 +6158,16 @@ __export(ora_exports, {
6158
6158
  function ora(options) {
6159
6159
  return new Ora(options);
6160
6160
  }
6161
- async function oraPromise(action74, options) {
6162
- const actionIsFunction = typeof action74 === "function";
6163
- const actionIsPromise = typeof action74.then === "function";
6161
+ async function oraPromise(action78, options) {
6162
+ const actionIsFunction = typeof action78 === "function";
6163
+ const actionIsPromise = typeof action78.then === "function";
6164
6164
  if (!actionIsFunction && !actionIsPromise) {
6165
6165
  throw new TypeError("Parameter `action` must be a Function or a Promise");
6166
6166
  }
6167
6167
  const { successText, failText } = typeof options === "object" ? options : { successText: void 0, failText: void 0 };
6168
6168
  const spinner = ora(options).start();
6169
6169
  try {
6170
- const promise = actionIsFunction ? action74(spinner) : action74;
6170
+ const promise = actionIsFunction ? action78(spinner) : action78;
6171
6171
  const result = await promise;
6172
6172
  spinner.succeed(
6173
6173
  successText === void 0 ? void 0 : typeof successText === "string" ? successText : successText(result)
@@ -7107,15 +7107,15 @@ function clientIdentityHeaders() {
7107
7107
  // ../api-client/src/http.ts
7108
7108
  var APEX_API_VERSION_HEADER = "x-apex-api-version";
7109
7109
  var SUPPORTED_APEX_API_VERSION = "1";
7110
- function extractErrorMessage(json, status3) {
7111
- if (!json) return `HTTP ${status3}`;
7110
+ function extractErrorMessage(json, status4) {
7111
+ if (!json) return `HTTP ${status4}`;
7112
7112
  const error = json.error;
7113
7113
  if (typeof error === "string" && error.trim()) return error;
7114
7114
  if (error && typeof error === "object") {
7115
7115
  const message = error.message;
7116
7116
  if (typeof message === "string" && message.trim()) return message;
7117
7117
  }
7118
- return typeof json.message === "string" && json.message.trim() ? json.message : `HTTP ${status3}`;
7118
+ return typeof json.message === "string" && json.message.trim() ? json.message : `HTTP ${status4}`;
7119
7119
  }
7120
7120
  function extractErrorDetails(json) {
7121
7121
  if (!json) return void 0;
@@ -7137,11 +7137,11 @@ function createHttpClient(creds, options) {
7137
7137
  warnedAboutVersionSkew = true;
7138
7138
  warn3(version > 1 ? "CLI is behind the server \u2014 update.\n" : "Server API is older than this CLI supports.\n");
7139
7139
  }
7140
- async function request2(method, path25, body5, requestTimeoutMs, form = false) {
7140
+ async function request2(method, path26, body5, requestTimeoutMs, form = false) {
7141
7141
  const started2 = performance.now();
7142
- const observe = (status3, requestId2, errorCode) => {
7142
+ const observe = (status4, requestId2, errorCode) => {
7143
7143
  try {
7144
- options.onResponse?.({ method, path: path25, status: status3, requestId: requestId2, errorCode: typeof errorCode === "string" ? errorCode : void 0, durationMs: Math.max(0, Math.round(performance.now() - started2)) });
7144
+ options.onResponse?.({ method, path: path26, status: status4, requestId: requestId2, errorCode: typeof errorCode === "string" ? errorCode : void 0, durationMs: Math.max(0, Math.round(performance.now() - started2)) });
7145
7145
  } catch {
7146
7146
  }
7147
7147
  };
@@ -7155,7 +7155,7 @@ function createHttpClient(creds, options) {
7155
7155
  if (creds.resolvedContext?.orgId) headers["X-Apex-Org"] = creds.resolvedContext.orgId;
7156
7156
  if (creds.resolvedContext?.shopId) headers["X-Apex-Shop"] = creds.resolvedContext.shopId;
7157
7157
  }
7158
- const mock = options.transport?.(method, path25, body5, headers);
7158
+ const mock = options.transport?.(method, path26, body5, headers);
7159
7159
  if (mock !== void 0) {
7160
7160
  const version = options.testTransportVersion?.();
7161
7161
  if (version) inspectVersion(new Headers({ [APEX_API_VERSION_HEADER]: version }));
@@ -7164,7 +7164,7 @@ function createHttpClient(creds, options) {
7164
7164
  }
7165
7165
  let response;
7166
7166
  try {
7167
- response = await fetch(`${creds.baseUrl}${path25}`, { method, headers, body: form ? body5 : body5 ? JSON.stringify(body5) : void 0, signal: AbortSignal.timeout(timeoutMs) });
7167
+ response = await fetch(`${creds.baseUrl}${path26}`, { method, headers, body: form ? body5 : body5 ? JSON.stringify(body5) : void 0, signal: AbortSignal.timeout(timeoutMs) });
7168
7168
  } catch (error) {
7169
7169
  const outer = error && typeof error === "object" ? error : void 0;
7170
7170
  const nested = outer?.cause && typeof outer.cause === "object" ? outer.cause : void 0;
@@ -7184,7 +7184,7 @@ function createHttpClient(creds, options) {
7184
7184
  if (!response.ok) return { ok: false, status: response.status, data: json, error: extractErrorMessage(json, response.status), errorDetails: extractErrorDetails(json), requestId, retryAfter: response.headers.get("retry-after"), rateLimit: { limit: response.headers.get("x-ratelimit-limit"), remaining: response.headers.get("x-ratelimit-remaining"), reset: response.headers.get("x-ratelimit-reset") } };
7185
7185
  return { ok: true, status: response.status, data: json };
7186
7186
  }
7187
- return { request: (method, path25, body5, timeoutMs) => request2(method, path25, body5, timeoutMs), requestForm: (method, path25, body5) => request2(method, path25, body5, void 0, true) };
7187
+ return { request: (method, path26, body5, timeoutMs) => request2(method, path26, body5, timeoutMs), requestForm: (method, path26, body5) => request2(method, path26, body5, void 0, true) };
7188
7188
  }
7189
7189
 
7190
7190
  // ../api-client/src/credentials.ts
@@ -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.14.2",
7328
+ version: "0.15.0",
7329
7329
  license: "MIT",
7330
7330
  description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
7331
7331
  private: false,
@@ -7579,18 +7579,18 @@ function commandPath(command) {
7579
7579
  function fail(command, message, exitCode = EXIT_CODES.general, details, api) {
7580
7580
  throw new CliError(message, exitCode, details, api);
7581
7581
  }
7582
- function classifyStatus(status3) {
7583
- if (status3 === 401 || status3 === 403) return EXIT_CODES.auth;
7584
- if (status3 === 409) return EXIT_CODES.conflict;
7585
- if (status3 === 400 || status3 === 413 || status3 === 422) return EXIT_CODES.validation;
7582
+ function classifyStatus(status4) {
7583
+ if (status4 === 401 || status4 === 403) return EXIT_CODES.auth;
7584
+ if (status4 === 409) return EXIT_CODES.conflict;
7585
+ if (status4 === 400 || status4 === 413 || status4 === 422) return EXIT_CODES.validation;
7586
7586
  return EXIT_CODES.general;
7587
7587
  }
7588
- function classifyApiError(status3, details) {
7588
+ 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
7592
  if (code === "shop_access_denied" || code === "org_access_denied") return EXIT_CODES.auth;
7593
- return classifyStatus(status3);
7593
+ return classifyStatus(status4);
7594
7594
  }
7595
7595
  function confirmationRequired(command, message, retry) {
7596
7596
  return fail(command, message, EXIT_CODES.confirmationRequired, { retry });
@@ -7690,12 +7690,12 @@ function errorReason(error) {
7690
7690
  String(details?.code)
7691
7691
  ))
7692
7692
  return String(details?.code);
7693
- const status3 = error.api?.status;
7694
- if (status3 === 401) return "auth_required";
7695
- if (status3 === 403) return "auth_forbidden";
7696
- if (status3 === 404) return "not_found";
7697
- if (status3 === 429) return "rate_limited";
7698
- if (status3 === 409 || error.exitCode === EXIT_CODES.conflict) return "conflict";
7693
+ const status4 = error.api?.status;
7694
+ if (status4 === 401) return "auth_required";
7695
+ if (status4 === 403) return "auth_forbidden";
7696
+ if (status4 === 404) return "not_found";
7697
+ if (status4 === 429) return "rate_limited";
7698
+ if (status4 === 409 || error.exitCode === EXIT_CODES.conflict) return "conflict";
7699
7699
  if (error.exitCode === EXIT_CODES.auth) return "auth_required";
7700
7700
  if (error.exitCode === EXIT_CODES.confirmationRequired) return "confirmation_required";
7701
7701
  if (error.exitCode === EXIT_CODES.validation) return "validation_failed";
@@ -8107,9 +8107,9 @@ var staticRouteSegments = /* @__PURE__ */ new Set([
8107
8107
  "webhooks",
8108
8108
  "workspace"
8109
8109
  ]);
8110
- function normalizeRoute(path25) {
8111
- if (!path25.startsWith("/api/")) return ":route";
8112
- const parts = path25.split(/[?#]/, 1)[0].split("/");
8110
+ function normalizeRoute(path26) {
8111
+ if (!path26.startsWith("/api/")) return ":route";
8112
+ const parts = path26.split(/[?#]/, 1)[0].split("/");
8113
8113
  const files = parts.indexOf("files");
8114
8114
  if (files !== -1 && parts.length > files + 1) parts.splice(files + 1, parts.length, ":id");
8115
8115
  return parts.map((part) => !part || staticRouteSegments.has(part) ? part : ":id").join("/");
@@ -8119,18 +8119,18 @@ function recordApiResponse2(response) {
8119
8119
  if (response.status >= 400 && !firstError)
8120
8120
  firstError = { ...response, path: normalizeRoute(response.path) };
8121
8121
  }
8122
- function classifyError(exitCode, error, status3) {
8122
+ function classifyError(exitCode, error, status4) {
8123
8123
  const detail5 = error && typeof error === "object" ? error : {};
8124
8124
  if (exitCode === 0) return "ok";
8125
8125
  if (detail5.code === "commander.unknownCommand") return "unknown_command";
8126
8126
  if (detail5.code?.startsWith("commander.")) return "validation";
8127
8127
  if (exitCode === 4) return "confirmation_required";
8128
- if (status3 === 401 || status3 === 403 || exitCode === 2) return "auth";
8129
- if (status3 === 404) return "not_found";
8130
- if (status3 === 429) return "rate_limited";
8131
- if (status3 === 409 || exitCode === 5) return "conflict";
8132
- if (status3 && status3 >= 500) return "server";
8133
- if (status3 === 400 || status3 === 422 || exitCode === 3) return "validation";
8128
+ if (status4 === 401 || status4 === 403 || exitCode === 2) return "auth";
8129
+ if (status4 === 404) return "not_found";
8130
+ if (status4 === 429) return "rate_limited";
8131
+ if (status4 === 409 || exitCode === 5) return "conflict";
8132
+ if (status4 && status4 >= 500) return "server";
8133
+ if (status4 === 400 || status4 === 422 || exitCode === 3) return "validation";
8134
8134
  if (Boolean(
8135
8135
  detail5.details?.reason && ["network", "dns", "timeout", "tls", "refused", "reset", "unknown"].includes(
8136
8136
  detail5.details.reason
@@ -8206,6 +8206,63 @@ var import_node_readline2 = __toESM(require("node:readline"));
8206
8206
 
8207
8207
  // src/api.ts
8208
8208
  var import_node_crypto2 = require("node:crypto");
8209
+
8210
+ // ../api-client/src/landing-pages.ts
8211
+ var LANDING_PAGE_MANIFEST_FILE = "apex-landing-page.json";
8212
+ var LANDING_PAGE_CHECKOUT_MARKER = ".apex/landing-page.json";
8213
+ var LANDING_PAGE_CONTEXT_DIR = "context";
8214
+ var LANDING_PAGE_BATCH_FILES = 32;
8215
+ var LANDING_PAGE_BATCH_BYTES = 3 * 1024 * 1024;
8216
+ function isLandingPageCheckoutPath(relativePath) {
8217
+ const segments = relativePath.split("/");
8218
+ if (segments.some((segment) => segment.startsWith(".") || segment === "node_modules")) return false;
8219
+ if (relativePath === LANDING_PAGE_MANIFEST_FILE) return false;
8220
+ if (segments[0] === LANDING_PAGE_CONTEXT_DIR) return false;
8221
+ return true;
8222
+ }
8223
+ function pagePath(id, action78) {
8224
+ return `/api/v1/landing-pages/${encodeURIComponent(id)}${action78 ? `/${action78}` : ""}`;
8225
+ }
8226
+ function landingPagesApi(request2) {
8227
+ const call = async (method, path26, body5) => {
8228
+ const response = await request2(method, path26, body5);
8229
+ return { ...response, data: response.data?.data };
8230
+ };
8231
+ return {
8232
+ create: (body5) => call("POST", "/api/v1/landing-pages", body5),
8233
+ checkout: (id, paths) => call(
8234
+ "GET",
8235
+ paths?.length ? `${pagePath(id, "filesystem")}?paths=${encodeURIComponent(paths.join(","))}` : pagePath(id, "filesystem")
8236
+ ),
8237
+ plan: (id, body5) => call("POST", pagePath(id, "filesystem/plan"), body5),
8238
+ upload: (id, files) => call("POST", pagePath(id, "filesystem/upload"), { files }),
8239
+ apply: (id, body5) => call("POST", pagePath(id, "filesystem/apply"), body5),
8240
+ deploy: (id, body5) => call("POST", pagePath(id, "deploy"), body5),
8241
+ versions: (id) => call("GET", pagePath(id, "versions")),
8242
+ preview: (id, versionNumber) => call("GET", versionNumber ? `${pagePath(id, "preview")}?version=${versionNumber}` : pagePath(id, "preview")),
8243
+ rollback: (id, body5) => call("POST", pagePath(id, "rollback"), body5),
8244
+ experiment: (id, body5) => call("POST", pagePath(id, "experiment"), body5)
8245
+ };
8246
+ }
8247
+ function landingPageBatches(items, size) {
8248
+ const batches = [];
8249
+ let current = [];
8250
+ let bytes = 0;
8251
+ for (const item of items) {
8252
+ const itemBytes = size(item);
8253
+ if (current.length && (current.length >= LANDING_PAGE_BATCH_FILES || bytes + itemBytes > LANDING_PAGE_BATCH_BYTES)) {
8254
+ batches.push(current);
8255
+ current = [];
8256
+ bytes = 0;
8257
+ }
8258
+ current.push(item);
8259
+ bytes += itemBytes;
8260
+ }
8261
+ if (current.length) batches.push(current);
8262
+ return batches;
8263
+ }
8264
+
8265
+ // src/api.ts
8209
8266
  function simulateTriggers(creds, body5) {
8210
8267
  return request(creds, "POST", "/api/v1/tracking/simulate-trigger", body5);
8211
8268
  }
@@ -8229,11 +8286,11 @@ function getUserKeyIdentity(creds) {
8229
8286
  function getAccountContext(creds) {
8230
8287
  return userRequest(creds, "GET", "/api/v1/me/context");
8231
8288
  }
8232
- function userRequest(creds, method, path25, body5) {
8289
+ function userRequest(creds, method, path26, body5) {
8233
8290
  return request(
8234
8291
  { ...creds, apiKey: creds.userApiKey ?? "", baseUrl: creds.userBaseUrl ?? creds.baseUrl },
8235
8292
  method,
8236
- path25,
8293
+ path26,
8237
8294
  body5
8238
8295
  );
8239
8296
  }
@@ -8315,13 +8372,13 @@ function sharedClient(creds) {
8315
8372
  }
8316
8373
  return client;
8317
8374
  }
8318
- async function request(creds, method, path25, body5, requestTimeoutMs) {
8319
- const response = await sharedClient(creds).request(method, path25, body5, requestTimeoutMs);
8375
+ async function request(creds, method, path26, body5, requestTimeoutMs) {
8376
+ const response = await sharedClient(creds).request(method, path26, body5, requestTimeoutMs);
8320
8377
  recordApiResponse(response);
8321
8378
  return response;
8322
8379
  }
8323
- async function requestForm(creds, method, path25, body5) {
8324
- const response = await sharedClient(creds).requestForm(method, path25, body5);
8380
+ async function requestForm(creds, method, path26, body5) {
8381
+ const response = await sharedClient(creds).requestForm(method, path26, body5);
8325
8382
  recordApiResponse(response);
8326
8383
  return response;
8327
8384
  }
@@ -8553,6 +8610,36 @@ function getInstallDomains(creds) {
8553
8610
  function updateInstallDomains(creds, body5) {
8554
8611
  return request(creds, "PATCH", "/api/v1/install-domains", body5);
8555
8612
  }
8613
+ function getLandingPageDomains(creds, filter = {}) {
8614
+ const query = filter.host ? `?host=${encodeURIComponent(filter.host)}` : "";
8615
+ return request(
8616
+ creds,
8617
+ "GET",
8618
+ `/api/v1/landing-pages/domains${query}`
8619
+ );
8620
+ }
8621
+ function createLandingPageDomain(creds, body5) {
8622
+ return request(
8623
+ creds,
8624
+ "POST",
8625
+ "/api/v1/landing-pages/domains",
8626
+ body5
8627
+ );
8628
+ }
8629
+ function verifyLandingPageDomain(creds, id) {
8630
+ return request(
8631
+ creds,
8632
+ "POST",
8633
+ `/api/v1/landing-pages/domains/${encodeURIComponent(id)}/verify`
8634
+ );
8635
+ }
8636
+ function deleteLandingPageDomain(creds, id) {
8637
+ return request(
8638
+ creds,
8639
+ "DELETE",
8640
+ `/api/v1/landing-pages/domains/${encodeURIComponent(id)}`
8641
+ );
8642
+ }
8556
8643
  function getExternalTracking(creds) {
8557
8644
  return request(creds, "GET", "/api/v1/external-tracking");
8558
8645
  }
@@ -8560,18 +8647,18 @@ function updateExternalTracking(creds, body5) {
8560
8647
  return request(creds, "PATCH", "/api/v1/external-tracking", body5);
8561
8648
  }
8562
8649
  function trackingEventProperties(event) {
8563
- let preview = null;
8650
+ let preview2 = null;
8564
8651
  if (typeof event.properties_preview === "string") {
8565
8652
  try {
8566
8653
  const parsed = JSON.parse(event.properties_preview);
8567
8654
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
8568
- preview = parsed;
8655
+ preview2 = parsed;
8569
8656
  }
8570
8657
  } catch {
8571
8658
  }
8572
8659
  }
8573
8660
  const legacy = event.properties && typeof event.properties === "object" && !Array.isArray(event.properties) ? event.properties : null;
8574
- const properties = { ...legacy ?? {}, ...preview ?? {} };
8661
+ const properties = { ...legacy ?? {}, ...preview2 ?? {} };
8575
8662
  if (event.goal_id != null) properties.goalId = event.goal_id;
8576
8663
  return properties;
8577
8664
  }
@@ -8763,11 +8850,11 @@ function requestTargetingResource(creds, method, kind5, id, body5, limit, cursor
8763
8850
  if (limit) params.set("limit", String(limit));
8764
8851
  if (cursor) params.set("cursor", cursor);
8765
8852
  const query = params.toString();
8766
- const path25 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
8767
- return request(creds, method, path25, body5);
8853
+ const path26 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
8854
+ return request(creds, method, path26, body5);
8768
8855
  }
8769
- function managedPath(kind5, id, action74) {
8770
- return `/api/v1/${kind5}${id ? `/${encodeURIComponent(id)}` : ""}${action74 ? `/${action74}` : ""}`;
8856
+ function managedPath(kind5, id, action78) {
8857
+ return `/api/v1/${kind5}${id ? `/${encodeURIComponent(id)}` : ""}${action78 ? `/${action78}` : ""}`;
8771
8858
  }
8772
8859
  function listManagedDeliveries(creds, kind5, limit) {
8773
8860
  const q = limit ? `?limit=${limit}` : "";
@@ -8785,8 +8872,8 @@ function createManagedDelivery(creds, kind5, body5) {
8785
8872
  function updateManagedDelivery(creds, kind5, id, body5) {
8786
8873
  return request(creds, "PATCH", managedPath(kind5, id), body5);
8787
8874
  }
8788
- function transitionManagedDelivery(creds, kind5, id, action74) {
8789
- return request(creds, "POST", managedPath(kind5, id, action74));
8875
+ function transitionManagedDelivery(creds, kind5, id, action78) {
8876
+ return request(creds, "POST", managedPath(kind5, id, action78));
8790
8877
  }
8791
8878
  function listConfigResources(creds, family, opts) {
8792
8879
  const params = new URLSearchParams();
@@ -8798,6 +8885,11 @@ function listConfigResources(creds, family, opts) {
8798
8885
  function getConfigResource(creds, family, id) {
8799
8886
  return request(creds, "GET", `/api/v1/${family}/${encodeURIComponent(id)}`);
8800
8887
  }
8888
+ function landingPages(creds) {
8889
+ return landingPagesApi(
8890
+ (method, path26, body5) => request(creds, method, path26, body5)
8891
+ );
8892
+ }
8801
8893
  function deleteAsset(creds, id) {
8802
8894
  return request(creds, "DELETE", `/api/v1/assets/${encodeURIComponent(id)}`);
8803
8895
  }
@@ -9103,9 +9195,9 @@ var STATUS_COLORS = {
9103
9195
  paused: source_default.yellow,
9104
9196
  completed: source_default.blue
9105
9197
  };
9106
- function statusBadge(status3) {
9107
- const colorize = STATUS_COLORS[status3] ?? source_default.white;
9108
- return colorize(status3.toUpperCase());
9198
+ function statusBadge(status4) {
9199
+ const colorize = STATUS_COLORS[status4] ?? source_default.white;
9200
+ return colorize(status4.toUpperCase());
9109
9201
  }
9110
9202
  function printTable(headers, rows, widths) {
9111
9203
  const colWidths = widths ?? headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
@@ -9284,8 +9376,8 @@ async function waitForBrowserLogin(baseUrl, userMode = false) {
9284
9376
  ${loginUrl.toString()}
9285
9377
  `));
9286
9378
  try {
9287
- const open2 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
9288
- await open2(loginUrl.toString());
9379
+ const open3 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
9380
+ await open3(loginUrl.toString());
9289
9381
  } catch {
9290
9382
  }
9291
9383
  });
@@ -9595,12 +9687,12 @@ function applyQaPreviewParams(url, params) {
9595
9687
  url.search += `${url.search ? "&" : "?"}drip_force=${force}`;
9596
9688
  return url;
9597
9689
  }
9598
- function buildQaPreviewUrl(experiment, options = {}) {
9599
- const variation = options.variationName ? experiment.variations?.find((v) => v.name === options.variationName) : experiment.variations?.find((v) => !v.is_control) ?? experiment.variations?.[0];
9600
- const target = options.storeUrl ?? experiment.store_url;
9690
+ function buildQaPreviewUrl(experiment2, options = {}) {
9691
+ const variation = options.variationName ? experiment2.variations?.find((v) => v.name === options.variationName) : experiment2.variations?.find((v) => !v.is_control) ?? experiment2.variations?.[0];
9692
+ const target = options.storeUrl ?? experiment2.store_url;
9601
9693
  if (!target || !variation) return null;
9602
9694
  try {
9603
- return applyQaPreviewParams(new URL(target), { experimentId: experiment.id, variationId: variation.id, experimentPublicId: experiment.public_id, variationPublicId: variation.public_id, ...options });
9695
+ return applyQaPreviewParams(new URL(target), { experimentId: experiment2.id, variationId: variation.id, experimentPublicId: experiment2.public_id, variationPublicId: variation.public_id, ...options });
9604
9696
  } catch {
9605
9697
  return null;
9606
9698
  }
@@ -9730,10 +9822,10 @@ async function resolveExperimentReference(request2, idOrSlug, variationRef) {
9730
9822
  let res = await request2("GET", idShaped || keyShaped ? `/api/v1/experiments/${encodeURIComponent(reference)}` : slugPath);
9731
9823
  if (!res.ok && res.status === 404 && idShaped) res = await request2("GET", slugPath);
9732
9824
  if (!res.ok) return { ok: false, error: res.error ?? "Experiment not found", status: res.status, response: res };
9733
- const experiment = record(res.data);
9734
- if (typeof experiment.id !== "string") return { ok: false, error: "Experiment has no id" };
9735
- const variations = Array.isArray(experiment.variations) ? experiment.variations.map(record) : [];
9736
- if (variationRef === void 0) return { ok: true, experimentId: experiment.id, variations, experiment };
9825
+ const experiment2 = record(res.data);
9826
+ if (typeof experiment2.id !== "string") return { ok: false, error: "Experiment has no id" };
9827
+ const variations = Array.isArray(experiment2.variations) ? experiment2.variations.map(record) : [];
9828
+ if (variationRef === void 0) return { ok: true, experimentId: experiment2.id, variations, experiment: experiment2 };
9737
9829
  const available = variations.map((v) => String(v.slug ?? v.name)).join(", ") || "none";
9738
9830
  const byIdentity = variations.filter((v) => v.id === variationRef || v.public_id === variationRef || v.slug === variationRef);
9739
9831
  const byName = variations.filter((v) => v.name === variationRef);
@@ -9744,11 +9836,11 @@ async function resolveExperimentReference(request2, idOrSlug, variationRef) {
9744
9836
  };
9745
9837
  const match2 = matches[0];
9746
9838
  if (typeof match2?.id !== "string") return { ok: false, error: `Variation "${variationRef}" not found. Available: ${available}` };
9747
- return { ok: true, experimentId: experiment.id, variationId: match2.id, variations, experiment };
9839
+ return { ok: true, experimentId: experiment2.id, variationId: match2.id, variations, experiment: experiment2 };
9748
9840
  }
9749
9841
  function summarizeResults(payload) {
9750
9842
  const data = record(payload);
9751
- const experiment = record(data.experiment);
9843
+ const experiment2 = record(data.experiment);
9752
9844
  const decision = record(data.decision);
9753
9845
  const outcome = record(data.effective_outcome);
9754
9846
  const commerce = record(data.commerce);
@@ -9762,6 +9854,9 @@ function summarizeResults(payload) {
9762
9854
  if (degradedFlags.commerceUnavailable) missing.add("commerce orders");
9763
9855
  const controlRate = rawArms.find((arm) => arm.isControl === true)?.primaryRate;
9764
9856
  const treatmentCount = rawArms.filter((arm) => arm.isControl !== true).length;
9857
+ const plannedDays = typeof data.planned_days === "number" && Number.isFinite(data.planned_days) && data.planned_days > 0 ? Math.max(1, Math.round(data.planned_days)) : null;
9858
+ const runtimeDay = typeof data.runtime_day === "number" && Number.isInteger(data.runtime_day) && data.runtime_day >= 1 ? data.runtime_day : null;
9859
+ const confidenceIndication = data.confidence_visible === false ? typeof data.confidence_indication === "string" && data.confidence_indication ? data.confidence_indication : runtimeDay !== null ? plannedDays !== null && runtimeDay <= plannedDays ? `Indication \xB7 day ${runtimeDay} of about ${plannedDays}` : `Indication \xB7 day ${runtimeDay}` : null : null;
9765
9860
  const arms = rawArms.map((arm) => {
9766
9861
  const judged = arm.isControl !== true && (decision.treatmentVariationId != null ? arm.variationId === decision.treatmentVariationId : treatmentCount === 1);
9767
9862
  let lift = null;
@@ -9773,7 +9868,7 @@ function summarizeResults(payload) {
9773
9868
  } else if (typeof arm.primaryRate === "number" && typeof controlRate === "number" && controlRate > 0)
9774
9869
  lift = Math.round((arm.primaryRate / controlRate - 1) * 1e4) / 100;
9775
9870
  if (lift == null) missing.add("lift");
9776
- if (judged && confidence == null) missing.add("confidence");
9871
+ if (judged && confidence == null && data.confidence_visible !== false) missing.add("confidence");
9777
9872
  }
9778
9873
  if (arm.visitors == null) missing.add("visitors");
9779
9874
  if (arm.primaryConversions == null) missing.add("conversions");
@@ -9782,8 +9877,8 @@ function summarizeResults(payload) {
9782
9877
  });
9783
9878
  const verdictWithheld = decision.state === "untrustworthy";
9784
9879
  return {
9785
- experiment,
9786
- runWindow: { startedAt: experiment.resultsWindowStartedAt ?? experiment.startedAt, endedAt: experiment.endedAt },
9880
+ experiment: experiment2,
9881
+ runWindow: { startedAt: experiment2.resultsWindowStartedAt ?? experiment2.startedAt, endedAt: experiment2.endedAt },
9787
9882
  primaryGoal: decision.primaryGoalName,
9788
9883
  totals: record(data.totals),
9789
9884
  decision,
@@ -9802,7 +9897,8 @@ function summarizeResults(payload) {
9802
9897
  value
9803
9898
  })) : [],
9804
9899
  metrics: Array.isArray(data.metrics) ? data.metrics.map(record) : [],
9805
- resultsWeekdays: data.results_weekdays
9900
+ resultsWeekdays: data.results_weekdays,
9901
+ confidenceIndication
9806
9902
  };
9807
9903
  }
9808
9904
 
@@ -10072,11 +10168,11 @@ function formatRevenue(value, currency) {
10072
10168
  }
10073
10169
  function printExperimentResults(results2, currency) {
10074
10170
  const summary = summarizeResults(results2);
10075
- const { experiment, totals, decision, verdictWithheld, statisticsState, presentedOutcome } = summary;
10171
+ const { experiment: experiment2, totals, decision, verdictWithheld, statisticsState, presentedOutcome } = summary;
10076
10172
  const outcomeProvenance = summary.outcome.provenance === "manager_decided" ? " (manager decided)" : "";
10077
10173
  console.log();
10078
- console.log(source_default.bold(String(experiment.name ?? experiment.id ?? "Experiment results")));
10079
- console.log(` Status: ${statusBadge(String(experiment.status ?? "unknown"))}`);
10174
+ console.log(source_default.bold(String(experiment2.name ?? experiment2.id ?? "Experiment results")));
10175
+ console.log(` Status: ${statusBadge(String(experiment2.status ?? "unknown"))}`);
10080
10176
  console.log(` Visitors: ${String(totals.visitors ?? 0)}`);
10081
10177
  console.log(` Conversions: ${String(totals.primaryConversions ?? 0)}`);
10082
10178
  console.log(` Revenue: ${formatRevenue(totals.revenue, currency)}`);
@@ -10102,7 +10198,7 @@ function printExperimentResults(results2, currency) {
10102
10198
  }
10103
10199
  console.log(` Stats lift: ${formatPct(decision.lift)}`);
10104
10200
  console.log(
10105
- ` Confidence (${confidenceBasisLabel(decision.basis)}): ${verdictWithheld ? "withheld \u2014 not decision-grade" : formatPct(decision.judgedConfidence)}`
10201
+ ` Confidence (${confidenceBasisLabel(decision.basis)}): ${verdictWithheld ? "withheld \u2014 not decision-grade" : summary.confidenceIndication ?? formatPct(decision.judgedConfidence)}`
10106
10202
  );
10107
10203
  const arms = summary.arms;
10108
10204
  if (arms.length > 0) {
@@ -10239,16 +10335,16 @@ function listExperimentFilesystemFiles(folder, session) {
10239
10335
  const absPath = import_node_path6.default.join(folder, fileName);
10240
10336
  if (!import_node_fs8.default.existsSync(absPath)) continue;
10241
10337
  const content = import_node_fs8.default.readFileSync(absPath, "utf8");
10242
- const sha256 = sha256Text(content);
10338
+ const sha2562 = sha256Text(content);
10243
10339
  if (SERVER_OWNED_EXPERIMENT_FILES.has(fileName)) {
10244
- if (session.files[fileName]?.sha256 !== sha256) {
10340
+ if (session.files[fileName]?.sha256 !== sha2562) {
10245
10341
  console.error(
10246
10342
  `${fileName} is server-owned; local edits are ignored (dashboard flow owns QA state)`
10247
10343
  );
10248
10344
  }
10249
10345
  continue;
10250
10346
  }
10251
- files.push({ path: fileName, content, sha256 });
10347
+ files.push({ path: fileName, content, sha256: sha2562 });
10252
10348
  }
10253
10349
  for (const fileName of EXPERIMENT_FILESYSTEM_PATH_SCHEMA.experiment) {
10254
10350
  const relativePath = `experiment/${fileName}`;
@@ -10314,35 +10410,35 @@ async function rewriteExperimentFilesystemAssets(input) {
10314
10410
  })
10315
10411
  );
10316
10412
  }
10317
- function printFilesystemPlan(plan) {
10318
- if (plan.errors.length > 0) {
10413
+ function printFilesystemPlan(plan2) {
10414
+ if (plan2.errors.length > 0) {
10319
10415
  console.log(source_default.red("\nErrors"));
10320
- plan.errors.forEach((error) => console.log(` - ${error}`));
10416
+ plan2.errors.forEach((error) => console.log(` - ${error}`));
10321
10417
  }
10322
- const compatibilityWarnings = (plan.previewCompatibility ?? []).map(
10418
+ const compatibilityWarnings = (plan2.previewCompatibility ?? []).map(
10323
10419
  (finding) => `${finding.path}:${finding.line}:${finding.column} ${finding.rule}: ${finding.message}. Try: ${finding.alternative}`
10324
10420
  );
10325
10421
  if (compatibilityWarnings.length > 0) {
10326
10422
  console.log(source_default.yellow("\nPreview compatibility"));
10327
10423
  compatibilityWarnings.forEach((warning) => console.log(` - ${warning}`));
10328
10424
  }
10329
- const otherWarnings = plan.warnings.filter((warning) => !compatibilityWarnings.includes(warning));
10425
+ const otherWarnings = plan2.warnings.filter((warning) => !compatibilityWarnings.includes(warning));
10330
10426
  if (otherWarnings.length > 0) {
10331
10427
  console.log(source_default.yellow("\nWarnings"));
10332
10428
  otherWarnings.forEach((warning) => console.log(` - ${warning}`));
10333
10429
  }
10334
- if ((plan.blockingIssues?.length ?? 0) > 0) {
10430
+ if ((plan2.blockingIssues?.length ?? 0) > 0) {
10335
10431
  console.log(source_default.red("\nBlocking issues"));
10336
- (plan.blockingIssues ?? []).forEach((issue) => {
10432
+ (plan2.blockingIssues ?? []).forEach((issue) => {
10337
10433
  const context = [issue.path, issue.variation ? `variation: ${issue.variation}` : null].filter(Boolean).join(", ");
10338
10434
  console.log(` - [${issue.code}] ${issue.message}${context ? ` (${context})` : ""}`);
10339
10435
  });
10340
10436
  }
10341
10437
  console.log(source_default.bold("\nChanges"));
10342
- if (plan.changes.length === 0) {
10438
+ if (plan2.changes.length === 0) {
10343
10439
  console.log(source_default.dim(" No changes."));
10344
10440
  } else {
10345
- plan.changes.forEach((change) => {
10441
+ plan2.changes.forEach((change) => {
10346
10442
  if (change.entity === "experiment.targeting" && Array.isArray(change.detail?.changedKeys)) {
10347
10443
  console.log(` Changed keys: ${change.detail.changedKeys.join(", ")}`);
10348
10444
  }
@@ -10354,9 +10450,9 @@ function printFilesystemPlan(plan) {
10354
10450
  });
10355
10451
  }
10356
10452
  console.log();
10357
- console.log(`QA invalidated: ${plan.invalidatesQa ? source_default.yellow("yes") : "no"}`);
10358
- console.log(`Acknowledgment: ${plan.invalidatesQa ? "--accept-qa-invalidation" : "none"}`);
10359
- console.log(`Precompile: ${plan.precompileRequired ? "yes" : "no"}`);
10453
+ console.log(`QA invalidated: ${plan2.invalidatesQa ? source_default.yellow("yes") : "no"}`);
10454
+ console.log(`Acknowledgment: ${plan2.invalidatesQa ? "--accept-qa-invalidation" : "none"}`);
10455
+ console.log(`Precompile: ${plan2.precompileRequired ? "yes" : "no"}`);
10360
10456
  }
10361
10457
  function blockingIssueSummary(issues) {
10362
10458
  if (!issues?.length) return "Experiment filesystem plan has errors.";
@@ -10380,11 +10476,11 @@ async function getQaPreviewUrls(creds, experimentId) {
10380
10476
  if (!res.ok || !res.data || !storeUrl) {
10381
10477
  return { previewUrls: [], unavailable: true, dashboardUrl };
10382
10478
  }
10383
- const experiment = res.data;
10384
- const previewUrls = (experiment.variations ?? []).filter((variation) => !variation.is_control).map((variation) => ({
10479
+ const experiment2 = res.data;
10480
+ const previewUrls = (experiment2.variations ?? []).filter((variation) => !variation.is_control).map((variation) => ({
10385
10481
  variationId: variation.id,
10386
10482
  variationName: variation.name,
10387
- url: buildQaPreviewUrl2(storeUrl, experiment.id, variation.id).toString()
10483
+ url: buildQaPreviewUrl2(storeUrl, experiment2.id, variation.id).toString()
10388
10484
  }));
10389
10485
  return { previewUrls, unavailable: false, dashboardUrl };
10390
10486
  } catch {
@@ -10398,9 +10494,9 @@ function printQaPreviewUrls(experimentId, result) {
10398
10494
  );
10399
10495
  return;
10400
10496
  }
10401
- for (const preview of result.previewUrls) {
10497
+ for (const preview2 of result.previewUrls) {
10402
10498
  console.log(
10403
- `Preview (interactive, works while draft \u2014 no on-site editing needed): ${preview.url}`
10499
+ `Preview (interactive, works while draft \u2014 no on-site editing needed): ${preview2.url}`
10404
10500
  );
10405
10501
  }
10406
10502
  }
@@ -10618,8 +10714,8 @@ var list = new Command("list").alias("ls").description(
10618
10714
  printTable(
10619
10715
  fields3.map((field) => field.toUpperCase()),
10620
10716
  experiments.map(
10621
- (experiment) => fields3.map((field) => {
10622
- const value = experiment[field];
10717
+ (experiment2) => fields3.map((field) => {
10718
+ const value = experiment2[field];
10623
10719
  return value == null ? "-" : typeof value === "object" ? JSON.stringify(value) : String(value);
10624
10720
  })
10625
10721
  )
@@ -10661,13 +10757,13 @@ var get = new Command("get").description(
10661
10757
  output(this, { ...exp, dashboard_url: dashboardUrl }, () => printExperiment(exp, dashboardUrl));
10662
10758
  });
10663
10759
  var attachIdea = new Command("attach-idea").description("Attach an experiment to an idea").argument("<experiment>", EXPERIMENT_REFERENCE_DESCRIPTION).requiredOption("--idea <key>", "Idea UUID or Apex backlog key").option("--follow-up", "Attach as a follow-up test").action(async function action5(experimentRef, opts) {
10664
- const experiment = await resolveExperiment(experimentRef);
10665
- if (!experiment) fail(this, `Experiment "${experimentRef}" not found.`, EXIT_CODES.validation);
10760
+ const experiment2 = await resolveExperiment(experimentRef);
10761
+ if (!experiment2) fail(this, `Experiment "${experimentRef}" not found.`, EXIT_CODES.validation);
10666
10762
  const classified = classifyIdeaReference(opts.idea);
10667
10763
  if (classified.kind === "invalid") {
10668
10764
  fail(this, "--idea must be a UUID or Apex backlog key", EXIT_CODES.validation);
10669
10765
  }
10670
- const res = await attachExperimentIdea(requireCredentials(), experiment.id, {
10766
+ const res = await attachExperimentIdea(requireCredentials(), experiment2.id, {
10671
10767
  ...classified.kind === "uuid" ? { backlog_item_id: classified.value } : { idea_key: classified.value },
10672
10768
  ...opts.followUp ? { as_follow_up: true } : {}
10673
10769
  });
@@ -10681,9 +10777,9 @@ var attachIdea = new Command("attach-idea").description("Attach an experiment to
10681
10777
  output(this, res.data, () => printSuccess("Idea attached."));
10682
10778
  });
10683
10779
  var detachIdea = new Command("detach-idea").description("Detach an experiment from its idea").argument("<experiment>", EXPERIMENT_REFERENCE_DESCRIPTION).action(async function action6(experimentRef) {
10684
- const experiment = await resolveExperiment(experimentRef);
10685
- if (!experiment) fail(this, `Experiment "${experimentRef}" not found.`, EXIT_CODES.validation);
10686
- const res = await detachExperimentIdea(requireCredentials(), experiment.id);
10780
+ const experiment2 = await resolveExperiment(experimentRef);
10781
+ if (!experiment2) fail(this, `Experiment "${experimentRef}" not found.`, EXIT_CODES.validation);
10782
+ const res = await detachExperimentIdea(requireCredentials(), experiment2.id);
10687
10783
  if (!res.ok)
10688
10784
  fail(
10689
10785
  this,
@@ -11494,9 +11590,9 @@ var experimentStatus = new Command("status").description(
11494
11590
  apiFailureOf(response)
11495
11591
  );
11496
11592
  }
11497
- const experiment = response?.data ?? await resolveExperiment(target);
11498
- if (!experiment) fail(this, "Experiment not found.");
11499
- const snapshot = await getExperimentFilesystem(creds, experiment.id);
11593
+ const experiment2 = response?.data ?? await resolveExperiment(target);
11594
+ if (!experiment2) fail(this, "Experiment not found.");
11595
+ const snapshot = await getExperimentFilesystem(creds, experiment2.id);
11500
11596
  if (!snapshot.ok || !snapshot.data) {
11501
11597
  throw new CliError(
11502
11598
  snapshot.error ?? "Failed to read experiment filesystem",
@@ -11505,7 +11601,7 @@ var experimentStatus = new Command("status").description(
11505
11601
  apiFailureOf(snapshot)
11506
11602
  );
11507
11603
  }
11508
- let versions = null;
11604
+ let versions2 = null;
11509
11605
  if (session) {
11510
11606
  const localFiles = listExperimentFilesystemFiles(candidate, session);
11511
11607
  const localHashes = new Map(localFiles.map((file) => [file.path, file.sha256]));
@@ -11533,24 +11629,24 @@ var experimentStatus = new Command("status").description(
11533
11629
  assets: session.assets ?? {},
11534
11630
  credentials: creds
11535
11631
  });
11536
- const plan = await planExperimentFilesystem(creds, experiment.id, { files: plannedFiles });
11537
- if (!plan.ok || !plan.data) {
11632
+ const plan2 = await planExperimentFilesystem(creds, experiment2.id, { files: plannedFiles });
11633
+ if (!plan2.ok || !plan2.data) {
11538
11634
  throw new CliError(
11539
- plan.error ?? "Failed to read saved state",
11540
- classifyStatus(plan.status),
11541
- plan.errorDetails,
11542
- apiFailureOf(plan)
11635
+ plan2.error ?? "Failed to read saved state",
11636
+ classifyStatus(plan2.status),
11637
+ plan2.errorDetails,
11638
+ apiFailureOf(plan2)
11543
11639
  );
11544
11640
  }
11545
- versions = {
11641
+ versions2 = {
11546
11642
  files,
11547
11643
  saved: {
11548
- would_change: plan.data.changes.length > 0,
11549
- invalidates_qa: plan.data.invalidatesQa,
11550
- blocking_issue_count: plan.data.blockingIssues?.filter((issue) => issue.severity === "blocking").length ?? 0,
11551
- valid: plan.data.ok,
11552
- errors: plan.data.errors,
11553
- warnings: plan.data.warnings
11644
+ would_change: plan2.data.changes.length > 0,
11645
+ invalidates_qa: plan2.data.invalidatesQa,
11646
+ blocking_issue_count: plan2.data.blockingIssues?.filter((issue) => issue.severity === "blocking").length ?? 0,
11647
+ valid: plan2.data.ok,
11648
+ errors: plan2.data.errors,
11649
+ warnings: plan2.data.warnings
11554
11650
  }
11555
11651
  };
11556
11652
  }
@@ -11566,9 +11662,9 @@ var experimentStatus = new Command("status").description(
11566
11662
  }
11567
11663
  const qa3 = {
11568
11664
  ...summarizeHandoverQa(qaSnapshot),
11569
- ready_version_id: experiment.ready_version_id ?? null
11665
+ ready_version_id: experiment2.ready_version_id ?? null
11570
11666
  };
11571
- const shopId = experiment.shop_id ?? creds.resolvedContext?.shopId ?? null;
11667
+ const shopId = experiment2.shop_id ?? creds.resolvedContext?.shopId ?? null;
11572
11668
  let org = {
11573
11669
  id: creds.resolvedContext?.orgId ?? null,
11574
11670
  name: null
@@ -11594,13 +11690,13 @@ var experimentStatus = new Command("status").description(
11594
11690
  "Shop credentials do not expose account context; organization and shop names are unknown."
11595
11691
  );
11596
11692
  const nextSteps = [];
11597
- const ref = shellQuote(session ? target : experiment.id);
11693
+ const ref = shellQuote(session ? target : experiment2.id);
11598
11694
  if (!session)
11599
11695
  nextSteps.push({
11600
11696
  command: `apex experiments pull ${ref} --out <folder>`,
11601
11697
  why: "Pull files to verify the local version."
11602
11698
  });
11603
- if (versions && (versions.files.some((file) => file.state !== "unchanged") || versions.saved.would_change || !versions.saved.valid || versions.saved.blocking_issue_count > 0)) {
11699
+ if (versions2 && (versions2.files.some((file) => file.state !== "unchanged") || versions2.saved.would_change || !versions2.saved.valid || versions2.saved.blocking_issue_count > 0)) {
11604
11700
  nextSteps.push(
11605
11701
  {
11606
11702
  command: `apex experiments plan ${ref} --json`,
@@ -11629,14 +11725,14 @@ var experimentStatus = new Command("status").description(
11629
11725
  why: "Required QA checks are complete; approve after verifying the saved version."
11630
11726
  });
11631
11727
  }
11632
- const locallyChanged = versions !== null && (versions.files.some((file) => file.state !== "unchanged") || versions.saved.would_change || !versions.saved.valid || versions.saved.blocking_issue_count > 0);
11728
+ const locallyChanged = versions2 !== null && (versions2.files.some((file) => file.state !== "unchanged") || versions2.saved.would_change || !versions2.saved.valid || versions2.saved.blocking_issue_count > 0);
11633
11729
  const qaComplete = qa3.required_done !== null && qa3.required_total !== null && qa3.required_done === qa3.required_total && qa3.blockers !== null && qa3.blockers.length === 0;
11634
11730
  const readyVersionKnown = typeof qa3.ready_version_id === "string" && qa3.ready_version_id.length > 0;
11635
11731
  const approvalCurrent = qa3.approval_present && readyVersionKnown && !locallyChanged;
11636
- const lifecycleReady = experiment.status === "ready_to_launch";
11732
+ const lifecycleReady = experiment2.status === "ready_to_launch";
11637
11733
  const readyForHandover = lifecycleReady && approvalCurrent && qaComplete;
11638
11734
  if (approvalCurrent && qaComplete && !lifecycleReady)
11639
- notes.push(`The experiment status is ${experiment.status}; handover needs ready_to_launch.`);
11735
+ notes.push(`The experiment status is ${experiment2.status}; handover needs ready_to_launch.`);
11640
11736
  if (qa3.approval_present && !readyVersionKnown && qaComplete && !locallyChanged)
11641
11737
  nextSteps.push({
11642
11738
  command: `apex experiments qa approve ${ref}`,
@@ -11647,7 +11743,7 @@ var experimentStatus = new Command("status").description(
11647
11743
  command: "launch in the Apex dashboard",
11648
11744
  why: "QA is approved for the saved version. Starting a test is a human decision."
11649
11745
  });
11650
- if (!versions)
11746
+ if (!versions2)
11651
11747
  notes.push(
11652
11748
  "Without a pulled folder, readiness reflects the saved version only; local edits elsewhere are not visible."
11653
11749
  );
@@ -11659,13 +11755,13 @@ var experimentStatus = new Command("status").description(
11659
11755
  base_url: creds.baseUrl
11660
11756
  },
11661
11757
  experiment: {
11662
- id: experiment.id,
11663
- test_key: experiment.test_key ?? null,
11664
- name: experiment.name,
11665
- status: experiment.status,
11666
- dashboard_url: buildDashboardUrl(creds.baseUrl, shopId, experiment.id)
11758
+ id: experiment2.id,
11759
+ test_key: experiment2.test_key ?? null,
11760
+ name: experiment2.name,
11761
+ status: experiment2.status,
11762
+ dashboard_url: buildDashboardUrl(creds.baseUrl, shopId, experiment2.id)
11667
11763
  },
11668
- versions,
11764
+ versions: versions2,
11669
11765
  qa: { ...qa3, approval_current: approvalCurrent },
11670
11766
  next_steps: nextSteps,
11671
11767
  ready_for_handover: readyForHandover,
@@ -11678,16 +11774,16 @@ Credential: ${data.context.credential_kind}
11678
11774
  Base URL: ${creds.baseUrl}`
11679
11775
  );
11680
11776
  console.log(
11681
- `Experiment: ${experiment.test_key ?? experiment.id} / ${experiment.name} (${experiment.status})
11777
+ `Experiment: ${experiment2.test_key ?? experiment2.id} / ${experiment2.name} (${experiment2.status})
11682
11778
  Dashboard: ${data.experiment.dashboard_url ?? "unknown"}`
11683
11779
  );
11684
11780
  console.log("Versions:");
11685
- if (versions) {
11686
- for (const file of versions.files) console.log(` ${file.state}: ${file.path}`);
11781
+ if (versions2) {
11782
+ for (const file of versions2.files) console.log(` ${file.state}: ${file.path}`);
11687
11783
  console.log(
11688
- ` Saved: would change ${versions.saved.would_change}; invalidates QA ${versions.saved.invalidates_qa}; blocking issues ${versions.saved.blocking_issue_count}`
11784
+ ` Saved: would change ${versions2.saved.would_change}; invalidates QA ${versions2.saved.invalidates_qa}; blocking issues ${versions2.saved.blocking_issue_count}`
11689
11785
  );
11690
- for (const message of [...versions.saved.errors, ...versions.saved.warnings])
11786
+ for (const message of [...versions2.saved.errors, ...versions2.saved.warnings])
11691
11787
  console.log(` ${message}`);
11692
11788
  } else console.log(" unknown (no pulled folder)");
11693
11789
  console.log(
@@ -12160,13 +12256,13 @@ function normalizeEvidenceDevice(value) {
12160
12256
  if (normalized === "ipad_pro") return "tablet";
12161
12257
  return normalized;
12162
12258
  }
12163
- function resolveEvidenceVariation(experiment, ref) {
12259
+ function resolveEvidenceVariation(experiment2, ref) {
12164
12260
  if (!ref) return void 0;
12165
- const exact = experiment.variations?.find(
12261
+ const exact = experiment2.variations?.find(
12166
12262
  (variation) => variation.id === ref || variation.slug === ref
12167
12263
  );
12168
12264
  if (exact) return exact.id;
12169
- const matches = (experiment.variations ?? []).filter(
12265
+ const matches = (experiment2.variations ?? []).filter(
12170
12266
  (variation) => variation.name.toLowerCase() === ref.toLowerCase()
12171
12267
  );
12172
12268
  if (matches.length === 1) return matches[0].id;
@@ -12213,9 +12309,9 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
12213
12309
  records: parsed
12214
12310
  });
12215
12311
  } else {
12216
- const experiment = await resolveExperiment(resolved.experimentId);
12217
- if (!experiment) throw new Error("Experiment not found.");
12218
- const variationId = resolveEvidenceVariation(experiment, opts.variation);
12312
+ const experiment2 = await resolveExperiment(resolved.experimentId);
12313
+ if (!experiment2) throw new Error("Experiment not found.");
12314
+ const variationId = resolveEvidenceVariation(experiment2, opts.variation);
12219
12315
  const form = new FormData();
12220
12316
  for (const filePath of opts.file) {
12221
12317
  const absolutePath = import_node_path6.default.resolve(process.cwd(), filePath);
@@ -12270,8 +12366,8 @@ function shellQuote(value) {
12270
12366
  if (value.length > 0 && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value)) return value;
12271
12367
  return `'${value.replaceAll("'", "'\\''")}'`;
12272
12368
  }
12273
- function isTransientResumeStatus(status3) {
12274
- return status3 === 429 || status3 !== void 0 && status3 >= 500;
12369
+ function isTransientResumeStatus(status4) {
12370
+ return status4 === 429 || status4 !== void 0 && status4 >= 500;
12275
12371
  }
12276
12372
  function screenshotTimeoutMessage(job, experimentRef, out, seconds, flags = {}) {
12277
12373
  const progress = job.progress ? ` Last progress: ${job.progress.captured}/${job.progress.total} captured.` : "";
@@ -12299,8 +12395,8 @@ var screenshots = new Command("screenshots").description(
12299
12395
  const explicitJson = process.argv.includes("--json");
12300
12396
  if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)
12301
12397
  fail(this, "--timeout must be a positive number of seconds.", EXIT_CODES.validation);
12302
- const experiment = await resolveExperiment(experimentRef);
12303
- if (!experiment?.variations?.length)
12398
+ const experiment2 = await resolveExperiment(experimentRef);
12399
+ if (!experiment2?.variations?.length)
12304
12400
  fail(this, "Experiment has no variations to capture.", EXIT_CODES.validation);
12305
12401
  const creds = requireCredentials();
12306
12402
  let devices;
@@ -12363,13 +12459,13 @@ var screenshots = new Command("screenshots").description(
12363
12459
  if (!job.experiment_id)
12364
12460
  fail(
12365
12461
  this,
12366
- `Screenshot job ${job.job_id} names no experiment; refusing to resume it under ${experimentRef} (${experiment.id}).`,
12462
+ `Screenshot job ${job.job_id} names no experiment; refusing to resume it under ${experimentRef} (${experiment2.id}).`,
12367
12463
  EXIT_CODES.validation
12368
12464
  );
12369
- if (job.experiment_id !== experiment.id)
12465
+ if (job.experiment_id !== experiment2.id)
12370
12466
  fail(
12371
12467
  this,
12372
- `Screenshot job ${job.job_id} belongs to experiment ${job.experiment_id}, not ${experimentRef} (${experiment.id}).`,
12468
+ `Screenshot job ${job.job_id} belongs to experiment ${job.experiment_id}, not ${experimentRef} (${experiment2.id}).`,
12373
12469
  EXIT_CODES.validation
12374
12470
  );
12375
12471
  const storedDevices = job.devices ?? [];
@@ -12392,7 +12488,7 @@ var screenshots = new Command("screenshots").description(
12392
12488
  `Screenshot job ${job.job_id} stores its results in a legacy shape this CLI cannot download; create a new job instead of resuming it.`,
12393
12489
  EXIT_CODES.validation
12394
12490
  );
12395
- const knownVariationIds = new Set(experiment.variations.map((variation) => variation.id));
12491
+ const knownVariationIds = new Set(experiment2.variations.map((variation) => variation.id));
12396
12492
  const storedVariationIds = job.variation_ids ?? [];
12397
12493
  for (const id of storedVariationIds.filter((id2) => !knownVariationIds.has(id2)))
12398
12494
  console.error(
@@ -12419,15 +12515,15 @@ var screenshots = new Command("screenshots").description(
12419
12515
  EXIT_CODES.validation
12420
12516
  );
12421
12517
  const requested = opts.variations ? commaList(opts.variations) : [];
12422
- const control = experiment.variations.find((variation) => variation.is_control);
12518
+ const control = experiment2.variations.find((variation) => variation.is_control);
12423
12519
  if (!control) fail(this, "Experiment has no control variation.", EXIT_CODES.validation);
12424
12520
  const matchedById = /* @__PURE__ */ new Map();
12425
12521
  const unmatched = [];
12426
12522
  for (const ref of requested) {
12427
- const exactMatch = experiment.variations.find(
12523
+ const exactMatch = experiment2.variations.find(
12428
12524
  (candidate) => ref === candidate.id || ref === candidate.slug
12429
12525
  );
12430
- const nameMatches = exactMatch ? [] : experiment.variations.filter(
12526
+ const nameMatches = exactMatch ? [] : experiment2.variations.filter(
12431
12527
  (candidate) => ref.toLowerCase() === candidate.name.toLowerCase()
12432
12528
  );
12433
12529
  if (nameMatches.length > 1) {
@@ -12446,9 +12542,9 @@ var screenshots = new Command("screenshots").description(
12446
12542
  }
12447
12543
  if (unmatched.length)
12448
12544
  fail(this, `Unknown --variations value(s): ${unmatched.join(", ")}`, EXIT_CODES.validation);
12449
- const selected2 = requested.length ? [...matchedById.values()] : experiment.variations;
12545
+ const selected2 = requested.length ? [...matchedById.values()] : experiment2.variations;
12450
12546
  variationIds = [.../* @__PURE__ */ new Set([control.id, ...selected2.map((variation) => variation.id)])];
12451
- const created = await createExperimentScreenshots(creds, experiment.id, {
12547
+ const created = await createExperimentScreenshots(creds, experiment2.id, {
12452
12548
  devices,
12453
12549
  variation_ids: variationIds
12454
12550
  });
@@ -12533,7 +12629,7 @@ var screenshots = new Command("screenshots").description(
12533
12629
  delayMs = Math.min(Math.round(delayMs * 1.6), 1e4);
12534
12630
  }
12535
12631
  if (opts.attachQa !== false) {
12536
- const attached = await attachExperimentQaEvidence(creds, experiment.id, {
12632
+ const attached = await attachExperimentQaEvidence(creds, experiment2.id, {
12537
12633
  screenshot_job_id: job.job_id
12538
12634
  });
12539
12635
  if (!attached.ok) {
@@ -12560,7 +12656,7 @@ var screenshots = new Command("screenshots").description(
12560
12656
  const rows = [];
12561
12657
  const errors = [];
12562
12658
  const usedFilenames = /* @__PURE__ */ new Set();
12563
- const hasControlVariation = experiment.variations.some((item) => item.is_control);
12659
+ const hasControlVariation = experiment2.variations.some((item) => item.is_control);
12564
12660
  const downloads = [];
12565
12661
  if (Array.isArray(job.results))
12566
12662
  fail(
@@ -12569,7 +12665,7 @@ var screenshots = new Command("screenshots").description(
12569
12665
  EXIT_CODES.validation
12570
12666
  );
12571
12667
  for (const variationId of variationIds) {
12572
- const variation = experiment.variations.find((item) => item.id === variationId);
12668
+ const variation = experiment2.variations.find((item) => item.id === variationId);
12573
12669
  if (!variation) {
12574
12670
  console.error(
12575
12671
  source_default.yellow(
@@ -12585,7 +12681,7 @@ var screenshots = new Command("screenshots").description(
12585
12681
  continue;
12586
12682
  }
12587
12683
  const pairPrefix = opts.labelPairs === false || !hasControlVariation ? "" : variation.is_control ? "before-" : "after-";
12588
- const baseFilename = `${pairPrefix}${safeFilenamePart(experiment.slug ?? experiment.name)}-${safeFilenamePart(variation.slug ?? variation.name)}-${safeFilenamePart(device)}`;
12684
+ const baseFilename = `${pairPrefix}${safeFilenamePart(experiment2.slug ?? experiment2.name)}-${safeFilenamePart(variation.slug ?? variation.name)}-${safeFilenamePart(device)}`;
12589
12685
  let filename = `${baseFilename}.png`;
12590
12686
  if (usedFilenames.has(filename)) {
12591
12687
  const suffix = variation.id.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase();
@@ -12957,8 +13053,8 @@ var previewCommand = new Command("preview").description(
12957
13053
  });
12958
13054
  if (opts.open !== false && !shouldUseJson(previewCommand)) {
12959
13055
  try {
12960
- const open2 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
12961
- await open2(data.preview_url);
13056
+ const open3 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13057
+ await open3(data.preview_url);
12962
13058
  } catch {
12963
13059
  console.error(source_default.dim("Copy the URL above to preview."));
12964
13060
  }
@@ -13059,8 +13155,8 @@ var previewCommand = new Command("preview").description(
13059
13155
  );
13060
13156
  if (opts?.open !== false && !shouldUseJson(previewCommand)) {
13061
13157
  try {
13062
- const open2 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13063
- await open2(previewUrl.toString());
13158
+ const open3 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13159
+ await open3(previewUrl.toString());
13064
13160
  console.log(source_default.dim("Opened in browser."));
13065
13161
  } catch {
13066
13162
  console.log(source_default.dim("Copy the URL above to preview."));
@@ -13092,19 +13188,19 @@ function apexConfigPath(folder) {
13092
13188
  return import_node_path8.default.join(folder, "apex.harness.json");
13093
13189
  }
13094
13190
  function loadHarnessConfig(folder) {
13095
- const experiment = readJson(experimentConfigPath(folder)) ?? {};
13191
+ const experiment2 = readJson(experimentConfigPath(folder)) ?? {};
13096
13192
  const apex = readJson(apexConfigPath(folder));
13097
- if (apex) return { apex, experiment };
13098
- const targetOrigin = experiment.targetOrigin?.replace(/\/+$/, "") ?? "";
13099
- const defaultPath = experiment.defaultPath ?? "/";
13193
+ if (apex) return { apex, experiment: experiment2 };
13194
+ const targetOrigin = experiment2.targetOrigin?.replace(/\/+$/, "") ?? "";
13195
+ const defaultPath = experiment2.defaultPath ?? "/";
13100
13196
  const targetUrl = targetOrigin ? new URL(defaultPath, targetOrigin).toString() : "";
13101
- const id = experiment.id ?? import_node_path8.default.basename(import_node_path8.default.dirname(folder));
13197
+ const id = experiment2.id ?? import_node_path8.default.basename(import_node_path8.default.dirname(folder));
13102
13198
  const variation = import_node_path8.default.basename(folder);
13103
13199
  return {
13104
- experiment,
13200
+ experiment: experiment2,
13105
13201
  apex: {
13106
13202
  experimentSlug: slugify(id, "experiment"),
13107
- experimentName: experiment.name ?? id,
13203
+ experimentName: experiment2.name ?? id,
13108
13204
  variationSlug: slugify(variation, "variant-a"),
13109
13205
  variationName: `Variant ${variation}`,
13110
13206
  targetUrl,
@@ -13112,8 +13208,8 @@ function loadHarnessConfig(folder) {
13112
13208
  }
13113
13209
  };
13114
13210
  }
13115
- function cleanupResetJs(experiment) {
13116
- const cleanupGlobal = experiment.inject?.cleanupGlobal?.trim();
13211
+ function cleanupResetJs(experiment2) {
13212
+ const cleanupGlobal = experiment2.inject?.cleanupGlobal?.trim();
13117
13213
  if (!cleanupGlobal) return null;
13118
13214
  return `if (typeof window[${JSON.stringify(cleanupGlobal)}] === "function") window[${JSON.stringify(cleanupGlobal)}]();`;
13119
13215
  }
@@ -13131,8 +13227,8 @@ function runAbHarness(args, cwd) {
13131
13227
  });
13132
13228
  });
13133
13229
  }
13134
- function artifactPaths(folder, experiment) {
13135
- const variant = experiment.variant;
13230
+ function artifactPaths(folder, experiment2) {
13231
+ const variant = experiment2.variant;
13136
13232
  if (variant?.endsWith(".bundle.js")) {
13137
13233
  const jsPath = import_node_path8.default.join(folder, variant.replace(/\.bundle\.js$/, ".js"));
13138
13234
  const cssPath = import_node_path8.default.join(folder, variant.replace(/\.bundle\.js$/, ".css"));
@@ -13188,7 +13284,7 @@ async function uploadVersion(params) {
13188
13284
  }
13189
13285
  async function createSession(folder) {
13190
13286
  const creds = requireCredentials();
13191
- const { apex, experiment } = loadHarnessConfig(folder);
13287
+ const { apex, experiment: experiment2 } = loadHarnessConfig(folder);
13192
13288
  if (!apex.targetUrl) {
13193
13289
  throw new Error("Missing targetUrl. Run `apex harness init` and set targetUrl.");
13194
13290
  }
@@ -13198,7 +13294,7 @@ async function createSession(folder) {
13198
13294
  variationSlug: apex.variationSlug,
13199
13295
  variationName: apex.variationName,
13200
13296
  targetUrl: apex.targetUrl,
13201
- resetJs: cleanupResetJs(experiment),
13297
+ resetJs: cleanupResetJs(experiment2),
13202
13298
  antiFlickerSelectors: apex.antiFlickerSelectors ?? []
13203
13299
  });
13204
13300
  if (!res.ok)
@@ -13207,7 +13303,7 @@ async function createSession(folder) {
13207
13303
  classifyStatus(res.status),
13208
13304
  res.errorDetails
13209
13305
  );
13210
- return { session: res.data, apex, experiment };
13306
+ return { session: res.data, apex, experiment: experiment2 };
13211
13307
  }
13212
13308
  var harnessCommand = new Command("harness").description(
13213
13309
  "Develop AB Test Harness projects through Apex live QA sessions"
@@ -13257,8 +13353,8 @@ harnessCommand.command("dev").argument("<folder>", "AB Test Harness experiment f
13257
13353
  }
13258
13354
  );
13259
13355
  if (opts.open !== false) {
13260
- const open2 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13261
- await open2(created.session.previewUrl);
13356
+ const open3 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13357
+ await open3(created.session.previewUrl);
13262
13358
  }
13263
13359
  let uploading = false;
13264
13360
  let queued = false;
@@ -13704,12 +13800,12 @@ operatorCommand.command("checkout").argument("<experiment-id>", "Apex experiment
13704
13800
  classifyStatus(expRes.status),
13705
13801
  expRes.errorDetails
13706
13802
  );
13707
- const experiment = expRes.data;
13708
- const variation = resolveVariation(experiment, opts.variation);
13803
+ const experiment2 = expRes.data;
13804
+ const variation = resolveVariation(experiment2, opts.variation);
13709
13805
  const folder = folderPath2(
13710
- opts.out ?? slugify2(`${experiment.name}-${variation.name}`, "apex-operator")
13806
+ opts.out ?? slugify2(`${experiment2.name}-${variation.name}`, "apex-operator")
13711
13807
  );
13712
- const workspace = await getOperatorWorkspace(creds, experiment.id, variation.id);
13808
+ const workspace = await getOperatorWorkspace(creds, experiment2.id, variation.id);
13713
13809
  if (!workspace.ok || !workspace.data)
13714
13810
  throw new CliError(
13715
13811
  workspace.error ?? "Could not load workspace",
@@ -13722,7 +13818,7 @@ operatorCommand.command("checkout").argument("<experiment-id>", "Apex experiment
13722
13818
  folder,
13723
13819
  sessionFromWorkspace({
13724
13820
  credsBaseUrl: creds.baseUrl,
13725
- experimentId: experiment.id,
13821
+ experimentId: experiment2.id,
13726
13822
  variationId: variation.id,
13727
13823
  workspace: workspace.data
13728
13824
  })
@@ -13731,12 +13827,12 @@ operatorCommand.command("checkout").argument("<experiment-id>", "Apex experiment
13731
13827
  this,
13732
13828
  {
13733
13829
  folder,
13734
- experimentId: experiment.id,
13830
+ experimentId: experiment2.id,
13735
13831
  variationId: variation.id,
13736
13832
  files: workspace.data.files.map((file) => file.path)
13737
13833
  },
13738
13834
  () => {
13739
- printSuccess(`Checked out ${experiment.name} / ${variation.name}`);
13835
+ printSuccess(`Checked out ${experiment2.name} / ${variation.name}`);
13740
13836
  console.log(source_default.cyan(folder));
13741
13837
  }
13742
13838
  );
@@ -13811,8 +13907,8 @@ operatorCommand.command("dev").argument("[folder]", "Operator workspace folder",
13811
13907
  }
13812
13908
  );
13813
13909
  if (opts.open !== false) {
13814
- const open2 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13815
- await open2(harnessSession.previewUrl);
13910
+ const open3 = (await Promise.resolve().then(() => (init_open(), open_exports))).default;
13911
+ await open3(harnessSession.previewUrl);
13816
13912
  }
13817
13913
  if (opts.once) return;
13818
13914
  let uploading = false;
@@ -13930,7 +14026,7 @@ var approved = new Command("approved").description("List approved roadmap ideas
13930
14026
  ["ID", "SCORE", "TYPE", "EXPERIMENT", "TITLE"],
13931
14027
  items.map((item) => [
13932
14028
  item.id,
13933
- score(item.computed_score),
14029
+ score(item.apex_score),
13934
14030
  truncate(item.action_type, 14),
13935
14031
  truncate(experimentState(item), 18),
13936
14032
  truncate(item.title, 54)
@@ -14232,8 +14328,8 @@ trackingCommand.command("verify").description("Verify live tracking events for t
14232
14328
  ["TIME", "EVENT", "PROPERTIES"],
14233
14329
  res.data.recent_events.map((event) => {
14234
14330
  const raw = JSON.stringify(trackingEventProperties(event));
14235
- const preview = raw.length > 120 ? `${raw.slice(0, 117)}...` : raw;
14236
- return [String(event.timestamp ?? "-"), String(event.event_type ?? "-"), preview];
14331
+ const preview2 = raw.length > 120 ? `${raw.slice(0, 117)}...` : raw;
14332
+ return [String(event.timestamp ?? "-"), String(event.event_type ?? "-"), preview2];
14237
14333
  })
14238
14334
  );
14239
14335
  }
@@ -15433,8 +15529,8 @@ var import_node_fs18 = __toESM(require("node:fs"));
15433
15529
  var import_node_path15 = __toESM(require("node:path"));
15434
15530
 
15435
15531
  // src/trigger-proposal.ts
15436
- function runtimePath(path25) {
15437
- return path25.replaceAll("[]", ".0");
15532
+ function runtimePath(path26) {
15533
+ return path26.replaceAll("[]", ".0");
15438
15534
  }
15439
15535
  function fieldsFrom(value, prefix = "", descriptorList = false) {
15440
15536
  if (!value || typeof value !== "object") return [];
@@ -15512,9 +15608,9 @@ function candidatesFrom(report) {
15512
15608
  }
15513
15609
  return [root];
15514
15610
  }
15515
- function score2(path25, patterns) {
15611
+ function score2(path26, patterns) {
15516
15612
  return patterns.reduce(
15517
- (total, pattern, index) => total + (pattern.test(path25) ? patterns.length - index : 0),
15613
+ (total, pattern, index) => total + (pattern.test(path26) ? patterns.length - index : 0),
15518
15614
  0
15519
15615
  );
15520
15616
  }
@@ -16089,6 +16185,15 @@ init_source();
16089
16185
  function normalizeDomain(value) {
16090
16186
  return value.trim().replace(/^https?:\/\//i, "").replace(/[/?#].*$/, "").replace(/\.$/, "").toLowerCase();
16091
16187
  }
16188
+ function landingHost(value) {
16189
+ const trimmed = value.trim();
16190
+ try {
16191
+ const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
16192
+ return new URL(withScheme).hostname.toLowerCase().replace(/\.$/, "") || normalizeDomain(trimmed);
16193
+ } catch {
16194
+ return normalizeDomain(trimmed);
16195
+ }
16196
+ }
16092
16197
  function warn2(command, precompile) {
16093
16198
  if (!shouldUseJson(command) && precompile && precompile.status !== "ok")
16094
16199
  console.error(source_default.yellow(`Warning (${precompile.status}): ${precompile.summary}`));
@@ -16143,7 +16248,7 @@ var add = new Command("add").description("Add install domains").argument("<domai
16143
16248
  );
16144
16249
  output(this, data, () => printDomains(data));
16145
16250
  });
16146
- var remove = new Command("remove").description("Remove install domains").argument("<domain...>").action(async function action53(domain) {
16251
+ var removeInstall = new Command("remove").description("Remove install domains").argument("<domain...>").action(async function action53(domain) {
16147
16252
  const removeSet = new Set(domain.map(normalizeDomain));
16148
16253
  const data = await patchWithRetry(
16149
16254
  this,
@@ -16156,7 +16261,194 @@ var set3 = new Command("set").description("Replace install domains").argument("<
16156
16261
  const data = await patchWithRetry(this, () => replacement);
16157
16262
  output(this, data, () => printDomains(data));
16158
16263
  });
16159
- var domainsCommand = new Command("domains").description("Manage install domains").addCommand(list5).addCommand(add).addCommand(remove).addCommand(set3);
16264
+ var configuredInterval = Number(process.env.APEX_CLI_DOMAIN_VERIFY_INTERVAL_MS);
16265
+ var VERIFY_INTERVAL_MS = Number.isFinite(configuredInterval) && configuredInterval > 0 ? configuredInterval : 1e4;
16266
+ var VERIFY_WINDOW_MS = 12e4;
16267
+ function domainFail(command, result, fallback2) {
16268
+ return fail(
16269
+ command,
16270
+ result.error ?? fallback2,
16271
+ classifyStatus(result.status),
16272
+ result.errorDetails
16273
+ );
16274
+ }
16275
+ function printInstructions(domain) {
16276
+ if (!domain.dns_instructions.length) return;
16277
+ console.log("Add these DNS records at the DNS provider:");
16278
+ printTable(
16279
+ ["TYPE", "NAME", "VALUE"],
16280
+ domain.dns_instructions.map((record4) => [record4.type, record4.name, record4.value]),
16281
+ [6, 44, 48]
16282
+ );
16283
+ }
16284
+ function printDomainStatus(domain) {
16285
+ console.log(
16286
+ `${domain.host}: ${domain.status}${domain.status_message ? ` (${domain.status_message})` : ""}`
16287
+ );
16288
+ }
16289
+ async function findLandingDomain(command, host) {
16290
+ const wanted = landingHost(host);
16291
+ const listed = await getLandingPageDomains(requireCredentials(), { host: wanted });
16292
+ if (!listed.ok || !listed.data)
16293
+ domainFail(command, listed, "Failed to load landing-page domains");
16294
+ const row = listed.data.data.find((item) => item.host === wanted);
16295
+ if (!row) fail(command, `No landing-page domain ${wanted} on this shop`, EXIT_CODES.general);
16296
+ return row;
16297
+ }
16298
+ async function verifyOnce(command, id) {
16299
+ const checked = await verifyLandingPageDomain(requireCredentials(), id);
16300
+ if (!checked.ok || !checked.data) domainFail(command, checked, "Failed to verify the domain");
16301
+ return checked.data.data;
16302
+ }
16303
+ var claim = new Command("claim").description("Claim a client hostname for landing pages (CNAME to the Apex edge)").argument("<host>", "hostname the client controls, for example offers.example.com").option("--wait", "check DNS and the certificate every 10 seconds for up to two minutes").action(async function action55(host, opts) {
16304
+ const result = await createLandingPageDomain(requireCredentials(), {
16305
+ host: landingHost(host),
16306
+ mode: "claim"
16307
+ });
16308
+ if (!result.ok || !result.data) domainFail(this, result, "Failed to claim the hostname");
16309
+ let domain = result.data.data;
16310
+ if (!shouldUseJson(this)) {
16311
+ console.log(`CNAME ${domain.host} -> ${domain.dns_target}`);
16312
+ printInstructions(domain);
16313
+ }
16314
+ if (opts.wait) {
16315
+ const deadline = Date.now() + VERIFY_WINDOW_MS;
16316
+ while (domain.status !== "active" && domain.status !== "failed" && Date.now() < deadline) {
16317
+ await new Promise((resolve2) => setTimeout(resolve2, VERIFY_INTERVAL_MS));
16318
+ domain = await verifyOnce(this, domain.id);
16319
+ }
16320
+ }
16321
+ output(this, { data: domain }, () => printDomainStatus(domain));
16322
+ });
16323
+ function parseConfirmPrice(command, raw) {
16324
+ if (!raw) return void 0;
16325
+ const parts = raw.join(" ").trim().split(/\s+/);
16326
+ const [amount, currency] = parts;
16327
+ if (parts.length !== 2 || !amount || !currency || !/^\d+(\.\d{1,2})?$/.test(amount) || !/^[A-Za-z]{3}$/.test(currency)) {
16328
+ fail(
16329
+ command,
16330
+ '--confirm-price takes the quoted amount and currency, for example --confirm-price "10.44 USD"',
16331
+ EXIT_CODES.validation
16332
+ );
16333
+ }
16334
+ return { amount, currency: currency.toUpperCase() };
16335
+ }
16336
+ function describeQuote(quote) {
16337
+ const term = quote.years === 1 ? "1 year" : `${quote.years} years`;
16338
+ return `${quote.domain}: ${quote.total} ${quote.currency} for ${term}, renews at ${quote.renewal_cost} ${quote.currency} a year (auto-renew on, billed to the Drip Cloudflare account)`;
16339
+ }
16340
+ var register = new Command("register").description("Register a new domain on the Drip Cloudflare account (billable, non-refundable)").argument("<name>", "domain to register, for example example-shop.com").option("--years <years>", "registration term in years (1 to 10)", "1").option(
16341
+ "--confirm-price <price...>",
16342
+ 'confirm the quoted total, for example "10.44 USD" (required without a terminal)'
16343
+ ).action(async function action56(name, opts) {
16344
+ const years = Number(opts.years);
16345
+ if (!Number.isInteger(years) || years < 1 || years > 10)
16346
+ fail(this, "--years must be a whole number from 1 to 10", EXIT_CODES.validation);
16347
+ const host = landingHost(name);
16348
+ let confirm3 = parseConfirmPrice(this, opts.confirmPrice);
16349
+ let result = await createLandingPageDomain(requireCredentials(), {
16350
+ host,
16351
+ mode: "register",
16352
+ years,
16353
+ confirm_price: confirm3
16354
+ });
16355
+ const quote = result.data?.quote;
16356
+ if (!result.ok && result.status === 409 && result.errorDetails?.code === "price_confirmation_required" && quote) {
16357
+ if (confirm3) {
16358
+ fail(this, `The price changed: ${describeQuote(quote)}`, EXIT_CODES.conflict, { quote });
16359
+ }
16360
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
16361
+ confirmationRequired(
16362
+ this,
16363
+ `Registering spends money. ${describeQuote(quote)}`,
16364
+ `apex domains register ${host} --years ${years} --confirm-price "${quote.total} ${quote.currency}"`
16365
+ );
16366
+ }
16367
+ const readline3 = await import("node:readline");
16368
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
16369
+ console.error(describeQuote(quote));
16370
+ const answer = await new Promise(
16371
+ (resolve2) => rl.question(
16372
+ source_default.yellow(
16373
+ `Register ${quote.domain} for ${quote.total} ${quote.currency}? This is non-refundable. [y/N] `
16374
+ ),
16375
+ resolve2
16376
+ )
16377
+ );
16378
+ rl.close();
16379
+ if (answer.trim().toLowerCase() !== "y") {
16380
+ output(
16381
+ this,
16382
+ { aborted: true, quote },
16383
+ () => console.log("Aborted. Nothing was registered.")
16384
+ );
16385
+ return;
16386
+ }
16387
+ confirm3 = { amount: quote.total, currency: quote.currency };
16388
+ result = await createLandingPageDomain(requireCredentials(), {
16389
+ host,
16390
+ mode: "register",
16391
+ years,
16392
+ confirm_price: confirm3
16393
+ });
16394
+ }
16395
+ if (!result.ok || !result.data) domainFail(this, result, "Failed to register the domain");
16396
+ const domain = result.data.data;
16397
+ output(this, { data: domain }, () => {
16398
+ const registration = domain.registration;
16399
+ console.log(
16400
+ `Registered ${domain.host}${registration ? ` for ${registration.price} ${registration.currency}` : ""}; status ${domain.status}.`
16401
+ );
16402
+ if (domain.status_message) console.log(domain.status_message);
16403
+ console.log(`Check progress with: apex domains status ${domain.host}`);
16404
+ });
16405
+ });
16406
+ var status = new Command("status").description("List landing-page domains, or check one hostname now").argument("[host]", "check this hostname against Cloudflare and DNS").action(async function action57(host) {
16407
+ if (host) {
16408
+ const row = await findLandingDomain(this, host);
16409
+ if (row.status === "removed")
16410
+ fail(
16411
+ this,
16412
+ `${row.host} was removed, but Cloudflare has not confirmed the hostname delete; run apex domains unclaim ${row.host} again`,
16413
+ EXIT_CODES.general
16414
+ );
16415
+ const domain = await verifyOnce(this, row.id);
16416
+ output(this, { data: domain }, () => {
16417
+ printDomainStatus(domain);
16418
+ if (domain.status !== "active") printInstructions(domain);
16419
+ });
16420
+ return;
16421
+ }
16422
+ const result = await getLandingPageDomains(requireCredentials());
16423
+ if (!result.ok || !result.data) domainFail(this, result, "Failed to load landing-page domains");
16424
+ const rows = result.data.data;
16425
+ output(this, result.data, () => {
16426
+ if (!rows.length) {
16427
+ console.log("No landing-page domains. Claim one with: apex domains claim <host>");
16428
+ return;
16429
+ }
16430
+ printTable(
16431
+ ["HOST", "MODE", "STATUS", "DNS TARGET"],
16432
+ rows.map((row) => [row.host, row.mode, row.status, row.dns_target]),
16433
+ [40, 9, 10, 24]
16434
+ );
16435
+ if (result.data.has_more)
16436
+ console.log(`Showing the newest ${rows.length}; more landing-page domains exist.`);
16437
+ });
16438
+ });
16439
+ var unclaim = new Command("unclaim").description(
16440
+ "Remove a landing-page domain from Apex (a registered domain stays on the Drip account)"
16441
+ ).argument("<host>").action(async function action58(host) {
16442
+ const row = await findLandingDomain(this, host);
16443
+ const result = await deleteLandingPageDomain(requireCredentials(), row.id);
16444
+ if (!result.ok || !result.data) domainFail(this, result, "Failed to remove the domain");
16445
+ const domain = result.data.data;
16446
+ output(this, { data: domain }, () => {
16447
+ console.log(`Removed ${domain.host} from Apex.`);
16448
+ if (domain.status_message) console.log(domain.status_message);
16449
+ });
16450
+ });
16451
+ var domainsCommand = new Command("domains").description("Manage install domains and landing-page domains").addCommand(list5).addCommand(add).addCommand(removeInstall).addCommand(set3).addCommand(claim).addCommand(register).addCommand(status).addCommand(unclaim);
16160
16452
 
16161
16453
  // src/commands/handover.ts
16162
16454
  init_source();
@@ -16243,14 +16535,14 @@ async function patchWithRetry2(command, build) {
16243
16535
  );
16244
16536
  return result.data;
16245
16537
  }
16246
- var get4 = new Command("get").description("Show cross-domain handover settings").action(async function action55() {
16538
+ var get4 = new Command("get").description("Show cross-domain handover settings").action(async function action59() {
16247
16539
  const data = await load5(this);
16248
16540
  output(this, data, () => printSettings(data.externalTracking));
16249
16541
  });
16250
16542
  var set4 = new Command("set").description("Enable cross-domain identity handover").requiredOption("--domain <domain...>", "Bound handover domain(s)").option("--param <param>", "Handover query parameter").option(
16251
16543
  "--signed",
16252
16544
  "Require signed handover tokens (the default; unsigning is a platform-support action)"
16253
- ).action(async function action56(opts) {
16545
+ ).action(async function action60(opts) {
16254
16546
  const domains = opts.domain.map(normalizeDomain2);
16255
16547
  if (domains.some((domain) => !domain))
16256
16548
  fail(
@@ -16270,14 +16562,14 @@ var set4 = new Command("set").description("Enable cross-domain identity handover
16270
16562
  }));
16271
16563
  output(this, data, () => printSettings(data.externalTracking));
16272
16564
  });
16273
- var off2 = new Command("off").description("Disable cross-domain handover").action(async function action57() {
16565
+ var off2 = new Command("off").description("Disable cross-domain handover").action(async function action61() {
16274
16566
  const data = await patchWithRetry2(this, (current) => ({
16275
16567
  externalTracking: { domains: [], signed: true },
16276
16568
  ifRevision: current.revision
16277
16569
  }));
16278
16570
  output(this, data, () => printSettings(data.externalTracking));
16279
16571
  });
16280
- var qa2 = new Command("qa").description("Check static link/form handover parameter carriage").requiredOption("--from <url>", "Page URL to inspect").requiredOption("--to-domain <domain>", "Target domain that should carry the handover parameter").action(async function action58(opts) {
16572
+ var qa2 = new Command("qa").description("Check static link/form handover parameter carriage").requiredOption("--from <url>", "Page URL to inspect").requiredOption("--to-domain <domain>", "Target domain that should carry the handover parameter").action(async function action62(opts) {
16281
16573
  const config = await load5(this);
16282
16574
  const target = normalizeDomain2(opts.toDomain);
16283
16575
  if (!target) fail(this, "--to-domain must be a valid hostname", EXIT_CODES.validation);
@@ -16371,10 +16663,10 @@ var reason = (v) => typeof v === "string" && (/^[a-z][a-z0-9_]{0,79}$/.test(v) |
16371
16663
  v
16372
16664
  ));
16373
16665
  var member = (v, values) => typeof v === "string" && values.includes(v);
16374
- var status = (v) => member(v, ["pass", "fail", "inconclusive"]);
16666
+ var status2 = (v) => member(v, ["pass", "fail", "inconclusive"]);
16375
16667
  var count = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
16376
16668
  function validReport(v) {
16377
- if (!record2(v) || v.schemaVersion !== 1 || v.finalStatisticalContribution !== "not_evaluated" || !record2(v.evaluation) || !status(v.evaluation.status) || !member(v.evaluation.freshness, ["current", "stale", "unknown"]) || !Array.isArray(v.evaluation.stages) || !v.evaluation.stages.every(
16669
+ if (!record2(v) || v.schemaVersion !== 1 || v.finalStatisticalContribution !== "not_evaluated" || !record2(v.evaluation) || !status2(v.evaluation.status) || !member(v.evaluation.freshness, ["current", "stale", "unknown"]) || !Array.isArray(v.evaluation.stages) || !v.evaluation.stages.every(
16378
16670
  (s) => record2(s) && (s.freshness === void 0 || member(s.freshness, ["current", "stale", "unknown"])) && member(s.stage, [
16379
16671
  "consent",
16380
16672
  "assignment",
@@ -16383,7 +16675,7 @@ function validReport(v) {
16383
16675
  "authoritative_fact",
16384
16676
  "attribution",
16385
16677
  "result"
16386
- ]) && status(s.status) && Array.isArray(s.reasonCodes) && s.reasonCodes.every(reason)
16678
+ ]) && status2(s.status) && Array.isArray(s.reasonCodes) && s.reasonCodes.every(reason)
16387
16679
  ))
16388
16680
  return false;
16389
16681
  if (v.receiptOutcomes !== void 0 && (!Array.isArray(v.receiptOutcomes) || v.receiptOutcomes.length > 6856 || !v.receiptOutcomes.every(
@@ -17466,7 +17758,7 @@ var doctorCommand = new Command("doctor").description("Check shop configuration
17466
17758
  ).option("--check <id>", "Run one check (for example purchase_capture)").option(
17467
17759
  "--worker <url>",
17468
17760
  `Apex Worker base URL; defaults to DRIP_WORKER_URL or ${PRODUCTION_WORKER_BASE_URL}`
17469
- ).action(async function action59(options) {
17761
+ ).action(async function action63(options) {
17470
17762
  const data = await runDoctor(options);
17471
17763
  if (options.check && data.checks.length === 0)
17472
17764
  fail(this, `Unknown doctor check: ${options.check}`, EXIT_CODES.validation);
@@ -17488,7 +17780,7 @@ function printDoctorReport(checks) {
17488
17780
  var consentRateCommand = new Command("consent-rate").description("Estimate analytics consent acceptance from visitor series").option(
17489
17781
  "--since <date>",
17490
17782
  "Consent gating start date (YYYY-MM-DD); splits the baseline and gated windows"
17491
- ).action(async function action60(opts) {
17783
+ ).action(async function action64(opts) {
17492
17784
  if (opts.since !== void 0 && parseDay(opts.since) === null)
17493
17785
  fail(this, "--since must be a calendar date in YYYY-MM-DD form", EXIT_CODES.validation);
17494
17786
  const result = await getVisitorSeries(requireCredentials(), { days: VISITOR_SERIES_DAYS });
@@ -17548,7 +17840,7 @@ function analyticsSubcommand(name, description, defaultDays) {
17548
17840
  command.option("--planning-days <n>", "Planning window in days", "180");
17549
17841
  command.option("--limit <n>", "Maximum page rows", "50");
17550
17842
  }
17551
- return command.action(async function action74(opts) {
17843
+ return command.action(async function action78(opts) {
17552
17844
  const result = await getAnalytics(requireCredentials(), name, {
17553
17845
  days: positiveInteger(this, opts.days, "--days"),
17554
17846
  planningDays: positiveInteger(this, opts.planningDays, "--planning-days"),
@@ -17568,7 +17860,7 @@ function urlsCommand() {
17568
17860
  return new Command("urls").description("Show storefront URL inventory from pageview analytics").option("-d, --days <n>", "Analytics window in days", "14").option("--host <host>", "Filter inventory to one host").option("--contains <substring>", "Server-side path substring filter").option(
17569
17861
  "--match <regex>",
17570
17862
  "Client-side regex filter; exhausts inventory pages before filtering"
17571
- ).option("--limit <n>", "Maximum rows to print", "50").action(async function action74(opts) {
17863
+ ).option("--limit <n>", "Maximum rows to print", "50").action(async function action78(opts) {
17572
17864
  const creds = requireCredentials();
17573
17865
  const days = positiveInteger(this, opts.days, "--days") ?? 14;
17574
17866
  const displayLimit = Math.min(200, positiveInteger(this, opts.limit, "--limit") ?? 50);
@@ -17779,10 +18071,10 @@ function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
17779
18071
  const chunks = [];
17780
18072
  let bytes = 0;
17781
18073
  let settled = false;
17782
- const settle = (action74) => {
18074
+ const settle = (action78) => {
17783
18075
  if (settled) return;
17784
18076
  settled = true;
17785
- action74();
18077
+ action78();
17786
18078
  };
17787
18079
  stream.on("data", (chunk) => {
17788
18080
  if (settled) return;
@@ -17824,7 +18116,7 @@ function overLimitMessage(hostname, maxBytes) {
17824
18116
  return `Response from ${hostname} exceeded the ${maxBytes}-byte cap`;
17825
18117
  }
17826
18118
  function responseFromIncoming(res, body5, decoded) {
17827
- const status3 = res.statusCode ?? 0;
18119
+ const status4 = res.statusCode ?? 0;
17828
18120
  const headers = new Headers();
17829
18121
  for (const [key, value] of Object.entries(res.headers)) {
17830
18122
  if (Array.isArray(value)) for (const entry of value) headers.append(key, entry);
@@ -17834,9 +18126,9 @@ function responseFromIncoming(res, body5, decoded) {
17834
18126
  headers.delete("content-encoding");
17835
18127
  headers.delete("content-length");
17836
18128
  }
17837
- const nullBody = status3 === 101 || status3 === 204 || status3 === 205 || status3 === 304;
18129
+ const nullBody = status4 === 101 || status4 === 204 || status4 === 205 || status4 === 304;
17838
18130
  return new Response(nullBody ? null : body5, {
17839
- status: status3,
18131
+ status: status4,
17840
18132
  statusText: res.statusMessage ?? "",
17841
18133
  headers
17842
18134
  });
@@ -17853,11 +18145,11 @@ function pinnedFetch(url, addresses, limits) {
17853
18145
  else callback(null, addresses[0], (0, import_node_net.isIP)(addresses[0]));
17854
18146
  };
17855
18147
  let settled = false;
17856
- const settle = (action74) => {
18148
+ const settle = (action78) => {
17857
18149
  if (settled) return;
17858
18150
  settled = true;
17859
18151
  clearTimeout(deadline);
17860
- action74();
18152
+ action78();
17861
18153
  };
17862
18154
  const req = send3(
17863
18155
  url,
@@ -17868,8 +18160,8 @@ function pinnedFetch(url, addresses, limits) {
17868
18160
  lookup: pinnedLookup
17869
18161
  },
17870
18162
  (res) => {
17871
- const declared = Number.parseInt(res.headers["content-length"] ?? "", 10);
17872
- if (Number.isFinite(declared) && declared > limits.maxBytes && limits.onExceed === "error") {
18163
+ const declared2 = Number.parseInt(res.headers["content-length"] ?? "", 10);
18164
+ if (Number.isFinite(declared2) && declared2 > limits.maxBytes && limits.onExceed === "error") {
17873
18165
  req.destroy();
17874
18166
  settle(() => reject(new BodyLimitError(overLimitMessage(url.hostname, limits.maxBytes))));
17875
18167
  return;
@@ -17984,7 +18276,7 @@ function detectChallenge(html) {
17984
18276
  );
17985
18277
  }
17986
18278
  var LEGACY_SNIPPET_MARKER = /sdk\.drip-apex\.com|events\.drip-apex\.com|\/drip\.js\b|\bDrip\.init\s*\(|\bwindow\.drip\b/i;
17987
- function pageReport(url, shopId, status3, html, workerSnippetPresent, bodyTruncated) {
18279
+ function pageReport(url, shopId, status4, html, workerSnippetPresent, bodyTruncated) {
17988
18280
  const challenge = detectChallenge(html);
17989
18281
  const shopIdMatches = html.includes(shopId);
17990
18282
  const snippetPresent = workerSnippetPresent || LEGACY_SNIPPET_MARKER.test(html) && shopIdMatches;
@@ -17992,29 +18284,29 @@ function pageReport(url, shopId, status3, html, workerSnippetPresent, bodyTrunca
17992
18284
  return {
17993
18285
  url,
17994
18286
  status: "WARN",
17995
- httpStatus: status3,
18287
+ httpStatus: status4,
17996
18288
  snippetPresent,
17997
18289
  shopIdMatches,
17998
18290
  botChallenge: true,
17999
18291
  bodyTruncated,
18000
18292
  message: "Bot challenge detected; verify in a real browser."
18001
18293
  };
18002
- if (status3 < 200 || status3 >= 300)
18294
+ if (status4 < 200 || status4 >= 300)
18003
18295
  return {
18004
18296
  url,
18005
18297
  status: "FAIL",
18006
- httpStatus: status3,
18298
+ httpStatus: status4,
18007
18299
  snippetPresent,
18008
18300
  shopIdMatches,
18009
18301
  botChallenge: false,
18010
18302
  bodyTruncated,
18011
- message: `Storefront page returned HTTP ${status3}; the install cannot be verified from this response.`
18303
+ message: `Storefront page returned HTTP ${status4}; the install cannot be verified from this response.`
18012
18304
  };
18013
18305
  if (!snippetPresent)
18014
18306
  return {
18015
18307
  url,
18016
18308
  status: "FAIL",
18017
- httpStatus: status3,
18309
+ httpStatus: status4,
18018
18310
  snippetPresent,
18019
18311
  shopIdMatches,
18020
18312
  botChallenge: false,
@@ -18025,7 +18317,7 @@ function pageReport(url, shopId, status3, html, workerSnippetPresent, bodyTrunca
18025
18317
  return {
18026
18318
  url,
18027
18319
  status: "FAIL",
18028
- httpStatus: status3,
18320
+ httpStatus: status4,
18029
18321
  snippetPresent,
18030
18322
  shopIdMatches,
18031
18323
  botChallenge: false,
@@ -18035,7 +18327,7 @@ function pageReport(url, shopId, status3, html, workerSnippetPresent, bodyTrunca
18035
18327
  return {
18036
18328
  url,
18037
18329
  status: "PASS",
18038
- httpStatus: status3,
18330
+ httpStatus: status4,
18039
18331
  snippetPresent,
18040
18332
  shopIdMatches,
18041
18333
  botChallenge: false,
@@ -18195,7 +18487,7 @@ async function checkServedConfig(configUrl, workerBaseUrl, workerBaseSource) {
18195
18487
  var installCommand = new Command("install").description("Check Apex storefront installation").command("check").description("Verify snippet, served config, and recent tracking for the current shop").option("--url <url>", "Check one explicit bound storefront URL").option("--shop-id <id>", "Apex shop ID to verify (falls back to DRIP_SHOP_ID)").option(
18196
18488
  "--worker-url <url>",
18197
18489
  "Apex Worker base URL that serves /config (defaults to the URL in the served snippet, then DRIP_WORKER_URL, then the production worker)"
18198
- ).option("--environment <key>", "Environment key to request from the worker", "production").action(async function action61(opts) {
18490
+ ).option("--environment <key>", "Environment key to request from the worker", "production").action(async function action65(opts) {
18199
18491
  const creds = requireCredentials();
18200
18492
  const shopId = opts.shopId?.trim() || process.env.DRIP_SHOP_ID?.trim() || null;
18201
18493
  if (!shopId)
@@ -18379,13 +18671,13 @@ function compareEdgePublication(currentValue, edgeValue, expectedWorstCaseSecond
18379
18671
  if (edge.publication_revision < current.publication_revision) return { ...base, verdict: "propagating", relationship: "edge_behind" };
18380
18672
  return { ...base, verdict: "live", relationship: edge.publication_revision > current.publication_revision ? "edge_ahead" : "equal" };
18381
18673
  }
18382
- function formatEdgePropagationStatus(status3) {
18674
+ function formatEdgePropagationStatus(status4) {
18383
18675
  const caveat = "This checks one CDN vantage point; other PoPs may lag by up to max-age.";
18384
- if (status3.verdict === "pending_publication") return `Pending publication \u2014 flag updated ${status3.flag_updated_at}, after the latest publication generated ${status3.current.generated_at ?? "never"}; the latest change is not live. After publication, allow ~${status3.expected_worst_case_seconds}s for propagation. ${caveat}`;
18385
- if (status3.verdict === "unknown") return `Unknown \u2014 edge payload predates publication metadata. ${caveat}`;
18386
- if (status3.verdict === "propagating") return `Propagating \u2014 edge revision ${status3.edge.publication_revision} (generated ${status3.edge.generated_at}) vs current revision ${status3.current.publication_revision} (generated ${status3.current.generated_at}); expected within ~${status3.expected_worst_case_seconds}s after generation. ${caveat}`;
18387
- const anomaly = status3.relationship === "edge_ahead" ? ` (edge is ahead of current revision ${status3.current.publication_revision}; anomalous)` : "";
18388
- return `Live at edge \u2014 revision ${status3.edge.publication_revision} (generated ${status3.edge.generated_at}) confirmed at edge at ${status3.confirmed_at}${anomaly}. ${caveat}`;
18676
+ if (status4.verdict === "pending_publication") return `Pending publication \u2014 flag updated ${status4.flag_updated_at}, after the latest publication generated ${status4.current.generated_at ?? "never"}; the latest change is not live. After publication, allow ~${status4.expected_worst_case_seconds}s for propagation. ${caveat}`;
18677
+ if (status4.verdict === "unknown") return `Unknown \u2014 edge payload predates publication metadata. ${caveat}`;
18678
+ if (status4.verdict === "propagating") return `Propagating \u2014 edge revision ${status4.edge.publication_revision} (generated ${status4.edge.generated_at}) vs current revision ${status4.current.publication_revision} (generated ${status4.current.generated_at}); expected within ~${status4.expected_worst_case_seconds}s after generation. ${caveat}`;
18679
+ const anomaly = status4.relationship === "edge_ahead" ? ` (edge is ahead of current revision ${status4.current.publication_revision}; anomalous)` : "";
18680
+ return `Live at edge \u2014 revision ${status4.edge.publication_revision} (generated ${status4.edge.generated_at}) confirmed at edge at ${status4.confirmed_at}${anomaly}. ${caveat}`;
18389
18681
  }
18390
18682
 
18391
18683
  // src/commands/flags.ts
@@ -18444,7 +18736,7 @@ var get5 = new Command("get").description("Get a flag").argument("<id>").action(
18444
18736
  fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
18445
18737
  output(this, r.data, () => detail(r.data));
18446
18738
  });
18447
- var status2 = new Command("status").description("Check whether a flag publication is live at the edge").argument("<id>").action(async function(id) {
18739
+ var status3 = new Command("status").description("Check whether a flag publication is live at the edge").argument("<id>").action(async function(id) {
18448
18740
  const r = await getFlagStatus(requireCredentials(), id);
18449
18741
  if (!r.ok || !r.data)
18450
18742
  fail(this, r.error ?? "Failed to get flag status", classifyStatus(r.status), r.errorDetails);
@@ -18507,15 +18799,15 @@ function write(name) {
18507
18799
  });
18508
18800
  return c;
18509
18801
  }
18510
- function lifecycle(action74) {
18511
- return new Command(action74).description(`${action74[0].toUpperCase()}${action74.slice(1)} a flag`).argument("<id>").action(async function(id) {
18512
- const r = await transitionManagedDelivery(requireCredentials(), kind, id, action74);
18802
+ function lifecycle(action78) {
18803
+ return new Command(action78).description(`${action78[0].toUpperCase()}${action78.slice(1)} a flag`).argument("<id>").action(async function(id) {
18804
+ const r = await transitionManagedDelivery(requireCredentials(), kind, id, action78);
18513
18805
  if (!r.ok || !r.data)
18514
- fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
18806
+ fail(this, r.error ?? `Failed to ${action78}`, classifyStatus(r.status), r.errorDetails);
18515
18807
  output(this, r.data, () => detail(r.data));
18516
18808
  });
18517
18809
  }
18518
- var flagsCommand = new Command("flags").description("Manage flags").addCommand(list6).addCommand(get5).addCommand(status2).addCommand(write("create")).addCommand(write("update")).addCommand(lifecycle("activate")).addCommand(lifecycle("pause")).addCommand(lifecycle("archive")).addHelpText(
18810
+ var flagsCommand = new Command("flags").description("Manage flags").addCommand(list6).addCommand(get5).addCommand(status3).addCommand(write("create")).addCommand(write("update")).addCommand(lifecycle("activate")).addCommand(lifecycle("pause")).addCommand(lifecycle("archive")).addHelpText(
18519
18811
  "after",
18520
18812
  `
18521
18813
  Examples:
@@ -18648,11 +18940,11 @@ var get6 = new Command("get").description("Get a backlog item identified as apex
18648
18940
  const labels = await fieldLabels(this, r.data);
18649
18941
  output(this, r.data, () => detail2(r.data, labels));
18650
18942
  });
18651
- function write2(action74) {
18652
- const c = new Command(action74).description(
18653
- `${action74 === "create" ? "Create" : "Update"} a ${singular}`
18943
+ function write2(action78) {
18944
+ const c = new Command(action78).description(
18945
+ `${action78 === "create" ? "Create" : "Update"} a ${singular}`
18654
18946
  );
18655
- if (action74 === "update") c.argument("<id>", `${singular} ID`);
18947
+ if (action78 === "update") c.argument("<id>", `${singular} ID`);
18656
18948
  c.argument("[json]", "Inline JSON, @file, or file path").option("--file <path>", "JSON body file").option(
18657
18949
  "--field <key=value>",
18658
18950
  "Set one of the shop's own fields; repeatable. Use key=null to clear it.",
@@ -18661,17 +18953,17 @@ function write2(action74) {
18661
18953
  );
18662
18954
  c.action(async function(...args) {
18663
18955
  const o = this.opts();
18664
- const id = action74 === "update" ? String(args[0]) : void 0;
18665
- const raw = o.file ? `@${o.file}` : String(args[action74 === "update" ? 1 : 0] ?? "") || void 0;
18956
+ const id = action78 === "update" ? String(args[0]) : void 0;
18957
+ const raw = o.file ? `@${o.file}` : String(args[action78 === "update" ? 1 : 0] ?? "") || void 0;
18666
18958
  const body5 = parse(this, raw);
18667
18959
  const customFields = await customFieldsFromPairs(this, o.field);
18668
18960
  if (customFields) {
18669
18961
  body5.custom_fields = { ...customFieldsRecord(body5.custom_fields), ...customFields };
18670
18962
  }
18671
18963
  if (!Object.keys(body5).length) fail(this, "Provide a JSON body.", EXIT_CODES.validation);
18672
- const r = action74 === "create" ? await createInsight(requireCredentials(), kind2, body5) : await updateInsight(requireCredentials(), kind2, id, body5);
18964
+ const r = action78 === "create" ? await createInsight(requireCredentials(), kind2, body5) : await updateInsight(requireCredentials(), kind2, id, body5);
18673
18965
  if (!r.ok || !r.data)
18674
- fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
18966
+ fail(this, r.error ?? `Failed to ${action78}`, classifyStatus(r.status), r.errorDetails);
18675
18967
  const labels = await fieldLabels(this, r.data);
18676
18968
  output(this, r.data, () => detail2(r.data, labels));
18677
18969
  });
@@ -18694,7 +18986,7 @@ var restore = new Command("restore").description("Restore an archived backlog it
18694
18986
  fail(this, r.error ?? "Failed to restore", classifyStatus(r.status), r.errorDetails);
18695
18987
  output(this, r.data, () => console.log(`Restored ${id}`));
18696
18988
  });
18697
- var remove2 = new Command("delete").description("Delete a backlog item").argument("<id>", "backlog item ID").option("-f, --force", "Skip confirmation").action(async function(id, opts) {
18989
+ var remove = new Command("delete").description("Delete a backlog item").argument("<id>", "backlog item ID").option("-f, --force", "Skip confirmation").action(async function(id, opts) {
18698
18990
  if (!opts.force) {
18699
18991
  if (!process.stdin.isTTY || !process.stdout.isTTY)
18700
18992
  confirmationRequired(
@@ -18810,7 +19102,7 @@ var fieldsArchive = new Command("archive").description("Archive an idea field; v
18810
19102
  output(this, r.data, () => fieldDetail(r.data));
18811
19103
  });
18812
19104
  var fields2 = new Command("fields").description("Manage the shop's own idea fields").addCommand(fieldsList).addCommand(fieldsCreate).addCommand(fieldsUpdate).addCommand(fieldsArchive);
18813
- var backlogCommand = new Command("backlog").description("Manage backlog").addCommand(list7).addCommand(get6).addCommand(write2("create")).addCommand(write2("update")).addCommand(archive).addCommand(restore).addCommand(remove2).addCommand(fields2);
19105
+ var backlogCommand = new Command("backlog").description("Manage backlog").addCommand(list7).addCommand(get6).addCommand(write2("create")).addCommand(write2("update")).addCommand(archive).addCommand(restore).addCommand(remove).addCommand(fields2);
18814
19106
 
18815
19107
  // src/commands/signals.ts
18816
19108
  var import_node_fs21 = __toESM(require("node:fs"));
@@ -18872,35 +19164,35 @@ var get7 = new Command("get").description("Get a signal").argument("<id>", "sign
18872
19164
  fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
18873
19165
  output(this, r.data, () => detail3(r.data));
18874
19166
  });
18875
- function write3(action74) {
18876
- const c = new Command(action74).description(
18877
- `${action74 === "create" ? "Create" : "Update"} a ${singular2}`
19167
+ function write3(action78) {
19168
+ const c = new Command(action78).description(
19169
+ `${action78 === "create" ? "Create" : "Update"} a ${singular2}`
18878
19170
  );
18879
- if (action74 === "update") c.argument("<id>", `${singular2} ID`);
19171
+ if (action78 === "update") c.argument("<id>", `${singular2} ID`);
18880
19172
  c.argument("[json]", "Inline JSON, @file, or file path").option(
18881
19173
  "--file <path>",
18882
19174
  "JSON body file"
18883
19175
  );
18884
19176
  c.action(async function(...args) {
18885
19177
  const o = this.opts();
18886
- const id = action74 === "update" ? String(args[0]) : void 0;
18887
- const raw = o.file ? `@${o.file}` : String(args[action74 === "update" ? 1 : 0] ?? "") || void 0;
19178
+ const id = action78 === "update" ? String(args[0]) : void 0;
19179
+ const raw = o.file ? `@${o.file}` : String(args[action78 === "update" ? 1 : 0] ?? "") || void 0;
18888
19180
  const body5 = parse2(this, raw);
18889
19181
  if (!Object.keys(body5).length) fail(this, "Provide a JSON body.", EXIT_CODES.validation);
18890
- const r = action74 === "create" ? await createInsight(requireCredentials(), kind3, body5) : await updateInsight(requireCredentials(), kind3, id, body5);
19182
+ const r = action78 === "create" ? await createInsight(requireCredentials(), kind3, body5) : await updateInsight(requireCredentials(), kind3, id, body5);
18891
19183
  if (!r.ok || !r.data)
18892
- fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
19184
+ fail(this, r.error ?? `Failed to ${action78}`, classifyStatus(r.status), r.errorDetails);
18893
19185
  output(this, r.data, () => detail3(r.data));
18894
19186
  });
18895
19187
  return c;
18896
19188
  }
18897
- var remove3 = new Command("delete").alias("archive").description("Delete or archive a signal").argument("<id>", "signal ID").action(async function(id) {
19189
+ var remove2 = new Command("delete").alias("archive").description("Delete or archive a signal").argument("<id>", "signal ID").action(async function(id) {
18898
19190
  const r = await deleteInsight(requireCredentials(), kind3, id);
18899
19191
  if (!r.ok || !r.data)
18900
19192
  fail(this, r.error ?? "Failed to delete", classifyStatus(r.status), r.errorDetails);
18901
19193
  output(this, r.data, () => console.log(`Deleted ${id}`));
18902
19194
  });
18903
- var signalsCommand = new Command("signals").description("Manage signals").addCommand(list8).addCommand(get7).addCommand(write3("create")).addCommand(write3("update")).addCommand(remove3);
19195
+ var signalsCommand = new Command("signals").description("Manage signals").addCommand(list8).addCommand(get7).addCommand(write3("create")).addCommand(write3("update")).addCommand(remove2);
18904
19196
 
18905
19197
  // src/commands/personalizations.ts
18906
19198
  init_source();
@@ -18981,11 +19273,11 @@ function write4(name) {
18981
19273
  });
18982
19274
  return c;
18983
19275
  }
18984
- function lifecycle2(action74) {
18985
- return new Command(action74).description(`${action74[0].toUpperCase()}${action74.slice(1)} a personalization`).argument("<id>").action(async function(id) {
18986
- const r = await transitionManagedDelivery(requireCredentials(), kind4, id, action74);
19276
+ function lifecycle2(action78) {
19277
+ return new Command(action78).description(`${action78[0].toUpperCase()}${action78.slice(1)} a personalization`).argument("<id>").action(async function(id) {
19278
+ const r = await transitionManagedDelivery(requireCredentials(), kind4, id, action78);
18987
19279
  if (!r.ok || !r.data)
18988
- fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
19280
+ fail(this, r.error ?? `Failed to ${action78}`, classifyStatus(r.status), r.errorDetails);
18989
19281
  output(this, r.data, () => detail4(r.data));
18990
19282
  });
18991
19283
  }
@@ -19067,21 +19359,21 @@ function targetingResourceCommand(kind5) {
19067
19359
  );
19068
19360
  })
19069
19361
  );
19070
- for (const action74 of ["create", "update"]) {
19071
- const command = new Command(action74).description(
19072
- `${action74 === "create" ? "Create" : "Update"} a ${singular3}`
19362
+ for (const action78 of ["create", "update"]) {
19363
+ const command = new Command(action78).description(
19364
+ `${action78 === "create" ? "Create" : "Update"} a ${singular3}`
19073
19365
  );
19074
- if (action74 === "update") command.argument("<id>");
19366
+ if (action78 === "update") command.argument("<id>");
19075
19367
  command.argument("[json]", "Inline JSON, @file, or file path").option("--file <path>", "JSON body file").action(async function(...args) {
19076
19368
  const opts = this.opts();
19077
- const id = action74 === "update" ? String(args[0]) : void 0;
19078
- const value = opts.file ? `@${opts.file}` : String(args[action74 === "update" ? 1 : 0] ?? "") || void 0;
19369
+ const id = action78 === "update" ? String(args[0]) : void 0;
19370
+ const value = opts.file ? `@${opts.file}` : String(args[action78 === "update" ? 1 : 0] ?? "") || void 0;
19079
19371
  const requestBody = body3(this, value);
19080
- if (action74 === "update" && typeof requestBody === "object" && requestBody !== null && !Array.isArray(requestBody) && Object.keys(requestBody).length === 0)
19372
+ if (action78 === "update" && typeof requestBody === "object" && requestBody !== null && !Array.isArray(requestBody) && Object.keys(requestBody).length === 0)
19081
19373
  fail(this, `Updating a ${singular3} requires at least one field.`, EXIT_CODES.validation);
19082
19374
  const response = await requestTargetingResource(
19083
19375
  requireCredentials(),
19084
- action74 === "create" ? "POST" : "PATCH",
19376
+ action78 === "create" ? "POST" : "PATCH",
19085
19377
  kind5,
19086
19378
  id,
19087
19379
  requestBody
@@ -19089,11 +19381,11 @@ function targetingResourceCommand(kind5) {
19089
19381
  if (!response.ok || !response.data)
19090
19382
  fail(
19091
19383
  this,
19092
- response.error ?? `Failed to ${action74}`,
19384
+ response.error ?? `Failed to ${action78}`,
19093
19385
  classifyStatus(response.status),
19094
19386
  response.errorDetails
19095
19387
  );
19096
- output(this, response.data, () => console.log(source_default.green(`${action74}d ${singular3}`)));
19388
+ output(this, response.data, () => console.log(source_default.green(`${action78}d ${singular3}`)));
19097
19389
  });
19098
19390
  root.addCommand(command);
19099
19391
  }
@@ -19162,7 +19454,7 @@ var exclusionGroupsCommand = targetingResourceCommand("exclusion-groups");
19162
19454
  // src/commands/brand.ts
19163
19455
  init_source();
19164
19456
  var NO_PROFILE_MESSAGE = "No brand profile yet \u2014 run onboarding or a site check";
19165
- var brandCommand = new Command("brand").description("Print the shop brand context for external agents").action(async function action62() {
19457
+ var brandCommand = new Command("brand").description("Print the shop brand context for external agents").action(async function action66() {
19166
19458
  const creds = requireCredentials();
19167
19459
  const res = await getBrandContext(creds);
19168
19460
  if (res.status === 404) {
@@ -19197,7 +19489,7 @@ var brandCommand = new Command("brand").description("Print the shop brand contex
19197
19489
 
19198
19490
  // src/commands/targeting.ts
19199
19491
  init_source();
19200
- var test2 = new Command("test").description("Evaluate experiment URL targeting for a page").argument("<url>", "URL to evaluate").option("--status <status>", "Comma-separated statuses to include, or all", "all").action(async function action63(url, opts) {
19492
+ var test2 = new Command("test").description("Evaluate experiment URL targeting for a page").argument("<url>", "URL to evaluate").option("--status <status>", "Comma-separated statuses to include, or all", "all").action(async function action67(url, opts) {
19201
19493
  const creds = requireCredentials();
19202
19494
  const res = await testTargeting(creds, url, { status: opts.status });
19203
19495
  if (!res.ok || !res.data) {
@@ -19282,7 +19574,7 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
19282
19574
  "--type <type>",
19283
19575
  "full_handover|design|development|qa|config|bug|help (internal types require a DRIP account)",
19284
19576
  "qa"
19285
- ).option("--loom <url>", "Loom URL with reproduction or QA context").option("--experiment <id>", "Experiment ID").option("--backlog-item <id>", "Backlog item ID (internal SOS only)").option("--sub-class <text>", "SOS sub-classification").option("--description <text-or-@file>", "Description text or @path").argument("[description]", "Description text").action(async function action64(descriptionArg, opts) {
19577
+ ).option("--loom <url>", "Loom URL with reproduction or QA context").option("--experiment <id>", "Experiment ID").option("--backlog-item <id>", "Backlog item ID (internal SOS only)").option("--sub-class <text>", "SOS sub-classification").option("--description <text-or-@file>", "Description text or @path").argument("[description]", "Description text").action(async function action68(descriptionArg, opts) {
19286
19578
  if (!SOS_TYPES.has(opts.type)) {
19287
19579
  fail(
19288
19580
  this,
@@ -19313,7 +19605,7 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
19313
19605
  }
19314
19606
  output(this, res.data, () => printCreatedTicket(res.data));
19315
19607
  });
19316
- var mine = new Command("mine").description("List SOS tickets created by this API key").action(async function action65() {
19608
+ var mine = new Command("mine").description("List SOS tickets created by this API key").action(async function action69() {
19317
19609
  const creds = requireCredentials();
19318
19610
  const res = await listMySos(creds);
19319
19611
  if (!res.ok || !res.data) {
@@ -19629,7 +19921,7 @@ function commandSchema(command) {
19629
19921
  };
19630
19922
  }
19631
19923
  function createSchemaCommand(program3) {
19632
- return new Command("schema").description("Print the current Apex CLI command and flag schema").option("--json", "Print JSON schema").action(function action74() {
19924
+ return new Command("schema").description("Print the current Apex CLI command and flag schema").option("--json", "Print JSON schema").action(function action78() {
19633
19925
  const schema = commandSchema(program3);
19634
19926
  output(this, schema, () => {
19635
19927
  process.stdout.write(`${JSON.stringify(schema, null, 2)}
@@ -19782,7 +20074,7 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
19782
20074
  vtpPaths,
19783
20075
  ecommerceFrom: [...ecommerce][0] ?? null,
19784
20076
  serverContainerUrl: [...serverUrls][0] ?? null,
19785
- piiPaths: vtpPaths.filter((path25) => /email|phone|address|name/i.test(path25))
20077
+ piiPaths: vtpPaths.filter((path26) => /email|phone|address|name/i.test(path26))
19786
20078
  }
19787
20079
  };
19788
20080
  }
@@ -19851,11 +20143,11 @@ function isContainerField(field) {
19851
20143
  const types = baseFieldTypes(field);
19852
20144
  return types.has("obj") || types.has("arr");
19853
20145
  }
19854
- function hasSyntheticPlaceholder(path25) {
19855
- return path25.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
20146
+ function hasSyntheticPlaceholder(path26) {
20147
+ return path26.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
19856
20148
  }
19857
- function ambiguousDottedPrefix(event, path25) {
19858
- const segments = path25.split(".");
20149
+ function ambiguousDottedPrefix(event, path26) {
20150
+ const segments = path26.split(".");
19859
20151
  for (let index = 1; index < segments.length; index += 1) {
19860
20152
  const prefix = segments.slice(0, index).join(".");
19861
20153
  const parent = event.fields.find((field) => field.path === prefix);
@@ -19864,42 +20156,42 @@ function ambiguousDottedPrefix(event, path25) {
19864
20156
  return void 0;
19865
20157
  }
19866
20158
  function dimensionEligibility(event, field) {
19867
- const path25 = field.path;
19868
- if (hasSyntheticPlaceholder(path25)) {
20159
+ const path26 = field.path;
20160
+ if (hasSyntheticPlaceholder(path26)) {
19869
20161
  return {
19870
20162
  eligible: false,
19871
- message: `Path ${JSON.stringify(path25)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
20163
+ message: `Path ${JSON.stringify(path26)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
19872
20164
  };
19873
20165
  }
19874
20166
  if (isContainerField(field)) {
19875
20167
  return {
19876
20168
  eligible: false,
19877
- message: `Path ${JSON.stringify(path25)} is container-typed. Choose an observed scalar path.`
20169
+ message: `Path ${JSON.stringify(path26)} is container-typed. Choose an observed scalar path.`
19878
20170
  };
19879
20171
  }
19880
- if (path25.includes("[]")) {
20172
+ if (path26.includes("[]")) {
19881
20173
  return {
19882
20174
  eligible: false,
19883
- message: `Path ${JSON.stringify(path25)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
20175
+ message: `Path ${JSON.stringify(path26)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
19884
20176
  };
19885
20177
  }
19886
20178
  if (!isStableField(field)) {
19887
20179
  return {
19888
20180
  eligible: false,
19889
- message: `Path ${JSON.stringify(path25)} does not have a stable observed type. Choose a path without a type collision.`
20181
+ message: `Path ${JSON.stringify(path26)} does not have a stable observed type. Choose a path without a type collision.`
19890
20182
  };
19891
20183
  }
19892
20184
  if (!event.event) {
19893
20185
  return {
19894
20186
  eligible: false,
19895
- message: `Path ${JSON.stringify(path25)} has no observed event name. Choose a path from a named event.`
20187
+ message: `Path ${JSON.stringify(path26)} has no observed event name. Choose a path from a named event.`
19896
20188
  };
19897
20189
  }
19898
- const ambiguousPrefix = ambiguousDottedPrefix(event, path25);
20190
+ const ambiguousPrefix = ambiguousDottedPrefix(event, path26);
19899
20191
  if (ambiguousPrefix) {
19900
20192
  return {
19901
20193
  eligible: false,
19902
- message: `Path ${JSON.stringify(path25)} could be a literal key containing dots because the discovery report has no observed container at ${JSON.stringify(ambiguousPrefix)}. Apex cannot safely create a capture for it.`
20194
+ message: `Path ${JSON.stringify(path26)} could be a literal key containing dots because the discovery report has no observed container at ${JSON.stringify(ambiguousPrefix)}. Apex cannot safely create a capture for it.`
19903
20195
  };
19904
20196
  }
19905
20197
  return { eligible: true };
@@ -19908,24 +20200,24 @@ function isNumericField(field) {
19908
20200
  const numericTypes = /* @__PURE__ */ new Set(["num", "numstr", "numstr_comma"]);
19909
20201
  return field.types.length > 0 && field.types.every((type) => numericTypes.has(type.split(":")[0]));
19910
20202
  }
19911
- function defaultDimensionLabel(path25) {
19912
- const words = path25.split(/[._-]+/).filter(Boolean).join(" ");
19913
- return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path25;
20203
+ function defaultDimensionLabel(path26) {
20204
+ const words = path26.split(/[._-]+/).filter(Boolean).join(" ");
20205
+ return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path26;
19914
20206
  }
19915
20207
  function quoteShellArgument(value) {
19916
20208
  return "'" + value.replaceAll("'", "'\\''") + "'";
19917
20209
  }
19918
- function createDimensionInvocation(path25, eventName) {
19919
- return `apex discover --create-dimension ${quoteShellArgument(path25)} --event ${quoteShellArgument(eventName)}`;
20210
+ function createDimensionInvocation(path26, eventName) {
20211
+ return `apex discover --create-dimension ${quoteShellArgument(path26)} --event ${quoteShellArgument(eventName)}`;
19920
20212
  }
19921
- function resolveDimensionObservation(command, report, path25, eventName) {
20213
+ function resolveDimensionObservation(command, report, path26, eventName) {
19922
20214
  const matches = report.data_layer.flatMap(
19923
- (event) => event.fields.filter((field) => field.path === path25).map((field) => ({ event, field }))
20215
+ (event) => event.fields.filter((field) => field.path === path26).map((field) => ({ event, field }))
19924
20216
  );
19925
20217
  if (!matches.length) {
19926
20218
  fail(
19927
20219
  command,
19928
- `Path ${JSON.stringify(path25)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
20220
+ `Path ${JSON.stringify(path26)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
19929
20221
  EXIT_CODES.validation
19930
20222
  );
19931
20223
  }
@@ -19935,7 +20227,7 @@ function resolveDimensionObservation(command, report, path25, eventName) {
19935
20227
  if (!eventMatches.length) {
19936
20228
  fail(
19937
20229
  command,
19938
- `Path ${JSON.stringify(path25)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
20230
+ `Path ${JSON.stringify(path26)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
19939
20231
  EXIT_CODES.validation
19940
20232
  );
19941
20233
  }
@@ -19943,7 +20235,7 @@ function resolveDimensionObservation(command, report, path25, eventName) {
19943
20235
  if (layers.length > 1) {
19944
20236
  fail(
19945
20237
  command,
19946
- `Path ${JSON.stringify(path25)} on event ${JSON.stringify(eventName)} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
20238
+ `Path ${JSON.stringify(path26)} on event ${JSON.stringify(eventName)} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
19947
20239
  EXIT_CODES.validation
19948
20240
  );
19949
20241
  }
@@ -19955,7 +20247,7 @@ function resolveDimensionObservation(command, report, path25, eventName) {
19955
20247
  if (namedEvents.length > 1) {
19956
20248
  fail(
19957
20249
  command,
19958
- `Path ${JSON.stringify(path25)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
20250
+ `Path ${JSON.stringify(path26)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
19959
20251
  EXIT_CODES.validation
19960
20252
  );
19961
20253
  }
@@ -19964,7 +20256,7 @@ function resolveDimensionObservation(command, report, path25, eventName) {
19964
20256
  if (layers.length > 1) {
19965
20257
  fail(
19966
20258
  command,
19967
- `Path ${JSON.stringify(path25)} on event ${JSON.stringify(namedEvents[0] ?? "")} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
20259
+ `Path ${JSON.stringify(path26)} on event ${JSON.stringify(namedEvents[0] ?? "")} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
19968
20260
  EXIT_CODES.validation
19969
20261
  );
19970
20262
  }
@@ -19999,9 +20291,9 @@ function renderDiscovery(report) {
19999
20291
  `
20000
20292
  ${event.event || "(unnamed event)"} [${event.dlName}] \u2014 ${event.count} observation(s)${event.matched ? " \u2014 matched" : ""}; t_ms p50=${event.t_ms_p50 ?? "n/a"}, p95=${event.t_ms_p95 ?? "n/a"}, pre-init=${event.pre_init_count}`
20001
20293
  );
20002
- for (const path25 of event.paths)
20294
+ for (const path26 of event.paths)
20003
20295
  lines.push(
20004
- ` URL ${path25.path}: ${path25.pageviews} pageviews, ${path25.count} pushes, ${path25.pspReturn_count} PSP returns`
20296
+ ` URL ${path26.path}: ${path26.pageviews} pageviews, ${path26.count} pushes, ${path26.pspReturn_count} PSP returns`
20005
20297
  );
20006
20298
  for (const field of event.fields) {
20007
20299
  lines.push(
@@ -20044,7 +20336,7 @@ ${event.event || "(unnamed event)"} [${event.dlName}] \u2014 ${event.count} obse
20044
20336
  return `${lines.join("\n")}
20045
20337
  `;
20046
20338
  }
20047
- var discoverCommand = new Command("discover").description("Read back the tracking schema observed for the current shop").option("--minutes <n>", "Observation window in minutes").option("--gtm <url-or-id>", "Mine a public page or GTM container locally").option("--gtm-url <url>", "Fetch an explicit GTM container URL, including first-party proxies").option("--create-dimension <path>", "Create a custom dimension from an observed dataLayer path").option("--event <name>", "Select the observed event when a path appears on multiple events").option("--key <signal-key>", "Override the dimension signal key").option("--label <text>", "Override the dimension label").action(async function action66(opts) {
20339
+ var discoverCommand = new Command("discover").description("Read back the tracking schema observed for the current shop").option("--minutes <n>", "Observation window in minutes").option("--gtm <url-or-id>", "Mine a public page or GTM container locally").option("--gtm-url <url>", "Fetch an explicit GTM container URL, including first-party proxies").option("--create-dimension <path>", "Create a custom dimension from an observed dataLayer path").option("--event <name>", "Select the observed event when a path appears on multiple events").option("--key <signal-key>", "Override the dimension signal key").option("--label <text>", "Override the dimension label").action(async function action70(opts) {
20048
20340
  if (opts.gtm && opts.gtmUrl)
20049
20341
  fail(this, "Use only one of --gtm or --gtm-url", EXIT_CODES.validation);
20050
20342
  if (opts.createDimension && (opts.gtm || opts.gtmUrl)) {
@@ -20064,7 +20356,7 @@ var discoverCommand = new Command("discover").description("Read back the trackin
20064
20356
  let report;
20065
20357
  let apiSucceeded = false;
20066
20358
  let discoveryCredentials;
20067
- let apiFailure;
20359
+ let apiFailure2;
20068
20360
  try {
20069
20361
  discoveryCredentials = requireCredentials();
20070
20362
  const result = await discoverTrackingSchema(discoveryCredentials, minutes);
@@ -20072,24 +20364,24 @@ var discoverCommand = new Command("discover").description("Read back the trackin
20072
20364
  report = result.data;
20073
20365
  apiSucceeded = true;
20074
20366
  } else
20075
- apiFailure = {
20367
+ apiFailure2 = {
20076
20368
  message: result.error ?? "Discovery API unavailable",
20077
20369
  status: result.status,
20078
20370
  details: result.errorDetails
20079
20371
  };
20080
20372
  } catch (error) {
20081
- apiFailure = {
20373
+ apiFailure2 = {
20082
20374
  message: error instanceof Error ? error.message : String(error),
20083
20375
  ...error instanceof CliError ? { details: error.details, cliError: error } : {}
20084
20376
  };
20085
20377
  }
20086
20378
  if (!report && (!gtmInput || opts.createDimension)) {
20087
- if (apiFailure?.cliError) throw apiFailure.cliError;
20379
+ if (apiFailure2?.cliError) throw apiFailure2.cliError;
20088
20380
  fail(
20089
20381
  this,
20090
- apiFailure?.message ?? "Failed to discover tracking schema",
20091
- classifyStatus(apiFailure?.status ?? 0),
20092
- apiFailure?.details
20382
+ apiFailure2?.message ?? "Failed to discover tracking schema",
20383
+ classifyStatus(apiFailure2?.status ?? 0),
20384
+ apiFailure2?.details
20093
20385
  );
20094
20386
  }
20095
20387
  report ??= {
@@ -20104,7 +20396,7 @@ var discoverCommand = new Command("discover").description("Read back the trackin
20104
20396
  trigger_diagnoses: [],
20105
20397
  storage: [],
20106
20398
  notes: [
20107
- `GTM static analysis completed without discovery API data: ${apiFailure?.message ?? "platform credentials were not provided"}`
20399
+ `GTM static analysis completed without discovery API data: ${apiFailure2?.message ?? "platform credentials were not provided"}`
20108
20400
  ]
20109
20401
  };
20110
20402
  if (gtmInput) {
@@ -20142,9 +20434,9 @@ var discoverCommand = new Command("discover").description("Read back the trackin
20142
20434
  if (!apiSucceeded || !discoveryCredentials) {
20143
20435
  fail(
20144
20436
  this,
20145
- apiFailure?.message ?? "Discovery API data is required to create a dimension",
20146
- classifyStatus(apiFailure?.status ?? 0),
20147
- apiFailure?.details
20437
+ apiFailure2?.message ?? "Discovery API data is required to create a dimension",
20438
+ classifyStatus(apiFailure2?.status ?? 0),
20439
+ apiFailure2?.details
20148
20440
  );
20149
20441
  }
20150
20442
  const observation = resolveDimensionObservation(
@@ -20247,7 +20539,7 @@ function installSkill(paths, targetRoot) {
20247
20539
  import_node_fs26.default.copyFileSync(paths.agents, import_node_path23.default.join(target, "AGENTS.md"));
20248
20540
  return target;
20249
20541
  }
20250
- var skillsCommand = new Command("skills").description("Print or install the Apex CLI agent skill files").option("--install", "Copy the Apex CLI skill into ./.claude/skills/apex-cli").option("--target <dir>", "Install target skills directory", ".claude/skills").option("--json", "Print JSON").action(function action67(opts) {
20542
+ var skillsCommand = new Command("skills").description("Print or install the Apex CLI agent skill files").option("--install", "Copy the Apex CLI skill into ./.claude/skills/apex-cli").option("--target <dir>", "Install target skills directory", ".claude/skills").option("--json", "Print JSON").action(function action71(opts) {
20251
20543
  let paths;
20252
20544
  try {
20253
20545
  paths = skillPaths();
@@ -20674,8 +20966,8 @@ async function fetchText(url) {
20674
20966
  });
20675
20967
  return { status: res.status, text: await readResponseText(res), finalUrl: res.url };
20676
20968
  }
20677
- function abs(base, path25) {
20678
- return new URL(path25, base).toString();
20969
+ function abs(base, path26) {
20970
+ return new URL(path26, base).toString();
20679
20971
  }
20680
20972
  function looksLikeShopifyCart(text2) {
20681
20973
  try {
@@ -20695,9 +20987,9 @@ async function collectSignals(url) {
20695
20987
  const chunks = [`__apex_primary_status_${primary.status}__`, primary.text];
20696
20988
  const protectedHit = primary.status === 403 || primary.status === 429 || matchDetectionMarkers(primary.text, "protected").length > 0;
20697
20989
  if (!protectedHit) {
20698
- for (const path25 of ["/cart.js"]) {
20990
+ for (const path26 of ["/cart.js"]) {
20699
20991
  try {
20700
- const secondary = await fetchText(abs(primary.finalUrl || url, path25));
20992
+ const secondary = await fetchText(abs(primary.finalUrl || url, path26));
20701
20993
  if (secondary.status >= 200 && secondary.status < 300 && looksLikeShopifyCart(secondary.text)) {
20702
20994
  chunks.push("__apex_secondary_shopify_cart_js__", secondary.text.slice(0, 2e3));
20703
20995
  }
@@ -20876,7 +21168,7 @@ function selectedEvents(opts) {
20876
21168
  }
20877
21169
  var list14 = new Command("list").alias("ls").description(
20878
21170
  "List webhooks. JSON includes availableEvents; secrets are never returned after create."
20879
- ).action(async function action68() {
21171
+ ).action(async function action72() {
20880
21172
  const data = await loadWebhookIndex(this);
20881
21173
  output(this, data, () => printWebhooks(data.data));
20882
21174
  });
@@ -20885,7 +21177,7 @@ var create7 = new Command("create").description(`Create a webhook. Prints the si
20885
21177
  "Webhook event to subscribe to; repeat for multiple events",
20886
21178
  (value, previous = []) => [...previous, value],
20887
21179
  []
20888
- ).action(async function action69(opts) {
21180
+ ).action(async function action73(opts) {
20889
21181
  const events = selectedEvents(opts);
20890
21182
  if (events.length === 0) fail(this, "At least one --event is required", EXIT_CODES.validation);
20891
21183
  const index = await loadWebhookIndex(this);
@@ -20914,7 +21206,7 @@ Signature verification: ${SIGNATURE_DOC}
20914
21206
  console.log(`Signature verification: ${SIGNATURE_DOC}`);
20915
21207
  });
20916
21208
  });
20917
- var remove4 = new Command("delete").alias("rm").description("Delete a webhook").argument("<id>", "Webhook id").action(async function action70(id) {
21209
+ var remove3 = new Command("delete").alias("rm").description("Delete a webhook").argument("<id>", "Webhook id").action(async function action74(id) {
20918
21210
  requireShopSecretKey(this);
20919
21211
  const result = handleResult(
20920
21212
  this,
@@ -20923,7 +21215,7 @@ var remove4 = new Command("delete").alias("rm").description("Delete a webhook").
20923
21215
  );
20924
21216
  output(this, result, () => console.log(source_default.green(`Deleted webhook ${id}`)));
20925
21217
  });
20926
- var deliveries = new Command("deliveries").description("List webhook deliveries including failure and disabled state").argument("<id>", "Webhook id").option("--limit <n>", "Maximum deliveries to show", "20").action(async function action71(id, opts) {
21218
+ var deliveries = new Command("deliveries").description("List webhook deliveries including failure and disabled state").argument("<id>", "Webhook id").option("--limit <n>", "Maximum deliveries to show", "20").action(async function action75(id, opts) {
20927
21219
  const limit = Number.parseInt(opts.limit, 10);
20928
21220
  if (!Number.isInteger(limit) || limit < 1)
20929
21221
  fail(this, "--limit must be a positive integer", EXIT_CODES.validation);
@@ -20940,7 +21232,7 @@ var update5 = new Command("update").description("Update a webhook URL, events, o
20940
21232
  "Replacement event; repeat for multiple events",
20941
21233
  (value, previous = []) => [...previous, value],
20942
21234
  []
20943
- ).option("--active", "Enable the webhook").option("--inactive", "Disable the webhook").action(async function action72(id, opts) {
21235
+ ).option("--active", "Enable the webhook").option("--inactive", "Disable the webhook").action(async function action76(id, opts) {
20944
21236
  if (opts.active && opts.inactive)
20945
21237
  fail(this, "Use only one of --active or --inactive", EXIT_CODES.validation);
20946
21238
  const events = selectedEvents(opts);
@@ -20973,7 +21265,7 @@ var update5 = new Command("update").description("Update a webhook URL, events, o
20973
21265
  );
20974
21266
  output(this, result, () => printWebhooks([result]));
20975
21267
  });
20976
- var test3 = new Command("test").description("Send a signed test event to a webhook").argument("<id>", "Webhook id").action(async function action73(id) {
21268
+ var test3 = new Command("test").description("Send a signed test event to a webhook").argument("<id>", "Webhook id").action(async function action77(id) {
20977
21269
  requireShopSecretKey(this);
20978
21270
  const result = handleResult(
20979
21271
  this,
@@ -20988,7 +21280,7 @@ var test3 = new Command("test").description("Send a signed test event to a webho
20988
21280
  )
20989
21281
  );
20990
21282
  });
20991
- var webhooksCommand = new Command("webhooks").description("Manage shop webhooks").addCommand(list14).addCommand(create7).addCommand(update5).addCommand(remove4).addCommand(deliveries).addCommand(test3);
21283
+ var webhooksCommand = new Command("webhooks").description("Manage shop webhooks").addCommand(list14).addCommand(create7).addCommand(update5).addCommand(remove3).addCommand(deliveries).addCommand(test3);
20992
21284
 
20993
21285
  // src/commands/simulate.ts
20994
21286
  init_source();
@@ -21117,8 +21409,584 @@ var simulateCommand = new Command("simulate").description("Simulate data-layer t
21117
21409
  });
21118
21410
 
21119
21411
  // src/commands/config-resources.ts
21412
+ var import_node_fs29 = __toESM(require("node:fs"));
21413
+ var import_node_path26 = __toESM(require("node:path"));
21414
+
21415
+ // src/commands/landing-pages.ts
21416
+ var import_node_crypto7 = __toESM(require("node:crypto"));
21120
21417
  var import_node_fs28 = __toESM(require("node:fs"));
21121
21418
  var import_node_path25 = __toESM(require("node:path"));
21419
+ function apiFailure(command, response, fallback2) {
21420
+ if (!response.ok || response.data === void 0) {
21421
+ fail(
21422
+ command,
21423
+ response.error ?? fallback2,
21424
+ classifyStatus(response.status),
21425
+ response.errorDetails
21426
+ );
21427
+ }
21428
+ }
21429
+ function sha256(body5) {
21430
+ return import_node_crypto7.default.createHash("sha256").update(body5).digest("hex");
21431
+ }
21432
+ function markerPath(folder) {
21433
+ return import_node_path25.default.join(folder, LANDING_PAGE_CHECKOUT_MARKER);
21434
+ }
21435
+ function readMarker(folder) {
21436
+ const file = markerPath(folder);
21437
+ if (!import_node_fs28.default.existsSync(file)) return null;
21438
+ try {
21439
+ return JSON.parse(import_node_fs28.default.readFileSync(file, "utf8"));
21440
+ } catch (error) {
21441
+ throw new CliError(
21442
+ `${LANDING_PAGE_CHECKOUT_MARKER} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
21443
+ EXIT_CODES.validation,
21444
+ {
21445
+ code: "validation_failed",
21446
+ next_steps: [
21447
+ {
21448
+ command: "apex landing-pages pull <id> --out <new folder>",
21449
+ why: "Pull a fresh checkout and copy your changes over."
21450
+ }
21451
+ ]
21452
+ }
21453
+ );
21454
+ }
21455
+ }
21456
+ function writeMarker(folder, marker) {
21457
+ import_node_fs28.default.mkdirSync(import_node_path25.default.dirname(markerPath(folder)), { recursive: true });
21458
+ import_node_fs28.default.writeFileSync(import_node_path25.default.join(folder, ".apex", ".gitignore"), "landing-page.json\n", "utf8");
21459
+ import_node_fs28.default.writeFileSync(markerPath(folder), `${JSON.stringify(marker, null, 2)}
21460
+ `, "utf8");
21461
+ }
21462
+ function requireCheckout(command, folderArg) {
21463
+ const folder = import_node_path25.default.resolve(process.cwd(), folderArg);
21464
+ const marker = import_node_fs28.default.existsSync(folder) && import_node_fs28.default.statSync(folder).isDirectory() ? readMarker(folder) : null;
21465
+ if (!marker) {
21466
+ fail(command, `${folderArg} is not a landing-page checkout.`, EXIT_CODES.validation, {
21467
+ code: "validation_failed",
21468
+ next_steps: [
21469
+ { command: "apex landing-pages pull <id> --out <folder>", why: "Create a checkout first." }
21470
+ ]
21471
+ });
21472
+ }
21473
+ return { folder, marker };
21474
+ }
21475
+ function resolvePageId(target) {
21476
+ const folder = import_node_path25.default.resolve(process.cwd(), target);
21477
+ if (import_node_fs28.default.existsSync(folder) && import_node_fs28.default.statSync(folder).isDirectory()) {
21478
+ const marker = readMarker(folder);
21479
+ if (marker) return marker.landingPageId;
21480
+ throw new CliError(
21481
+ `${target} is a folder without a landing-page checkout.`,
21482
+ EXIT_CODES.validation,
21483
+ {
21484
+ code: "validation_failed",
21485
+ next_steps: [
21486
+ {
21487
+ command: "apex landing-pages pull <id> --out <folder>",
21488
+ why: "Create a checkout first."
21489
+ }
21490
+ ]
21491
+ }
21492
+ );
21493
+ }
21494
+ return target;
21495
+ }
21496
+ function readCheckoutFiles(folder) {
21497
+ const files = [];
21498
+ const visit2 = (directory) => {
21499
+ for (const entry of import_node_fs28.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
21500
+ const absolute = import_node_path25.default.join(directory, entry.name);
21501
+ const relative = import_node_path25.default.relative(folder, absolute).split(import_node_path25.default.sep).join("/");
21502
+ if (!isLandingPageCheckoutPath(relative)) continue;
21503
+ if (entry.isDirectory()) visit2(absolute);
21504
+ else if (entry.isFile()) {
21505
+ const body5 = import_node_fs28.default.readFileSync(absolute);
21506
+ files.push({ path: relative, sha256: sha256(body5), bytes: body5.byteLength, body: body5 });
21507
+ }
21508
+ }
21509
+ };
21510
+ visit2(folder);
21511
+ return files;
21512
+ }
21513
+ function readManifest2(command, folder) {
21514
+ const file = import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
21515
+ if (!import_node_fs28.default.existsSync(file)) {
21516
+ fail(
21517
+ command,
21518
+ `${LANDING_PAGE_MANIFEST_FILE} is missing from the checkout.`,
21519
+ EXIT_CODES.validation,
21520
+ {
21521
+ code: "validation_failed"
21522
+ }
21523
+ );
21524
+ }
21525
+ try {
21526
+ return JSON.parse(import_node_fs28.default.readFileSync(file, "utf8"));
21527
+ } catch (error) {
21528
+ fail(
21529
+ command,
21530
+ `${LANDING_PAGE_MANIFEST_FILE} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
21531
+ EXIT_CODES.validation,
21532
+ {
21533
+ code: "validation_failed"
21534
+ }
21535
+ );
21536
+ }
21537
+ }
21538
+ function declared(files) {
21539
+ return files.map(({ path: filePath, sha256: digest, bytes }) => ({
21540
+ path: filePath,
21541
+ sha256: digest,
21542
+ bytes
21543
+ }));
21544
+ }
21545
+ function printPlan(plan2) {
21546
+ const { files, manifest } = plan2;
21547
+ if (!plan2.would_change) {
21548
+ console.log("No changes.");
21549
+ return;
21550
+ }
21551
+ for (const file of files.added) console.log(` + ${file}`);
21552
+ for (const file of files.changed) console.log(` ~ ${file}`);
21553
+ for (const file of files.removed) console.log(` - ${file}`);
21554
+ console.log(` ${files.unchanged} unchanged \xB7 ${plan2.file_count} files \xB7 ${plan2.bytes} bytes`);
21555
+ if (manifest.fields.length) console.log(`Manifest: ${manifest.fields.join(", ")} changed`);
21556
+ const goals = manifest.goals;
21557
+ if (goals.added.length + goals.changed.length + goals.removed.length) {
21558
+ console.log(
21559
+ `Goals: ${[
21560
+ ...goals.added.map((key) => `+${key}`),
21561
+ ...goals.changed.map((key) => `~${key}`),
21562
+ ...goals.removed.map((key) => `-${key} (kept in Apex)`)
21563
+ ].join(" ")}`
21564
+ );
21565
+ }
21566
+ console.log(
21567
+ `Deploy target: ${plan2.deploy_target ?? "none yet: set host in apex-landing-page.json to a domain this shop holds"}`
21568
+ );
21569
+ }
21570
+ async function confirm2(message) {
21571
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
21572
+ const readline3 = await import("node:readline");
21573
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
21574
+ const answer = await new Promise((resolve2) => rl.question(`${message} [y/N] `, resolve2));
21575
+ rl.close();
21576
+ return answer.trim().toLowerCase() === "y";
21577
+ }
21578
+ function manifestDigest(folder) {
21579
+ const file = import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
21580
+ return import_node_fs28.default.existsSync(file) ? sha256(import_node_fs28.default.readFileSync(file)) : null;
21581
+ }
21582
+ function localChanges(folder, marker) {
21583
+ const local = new Map(readCheckoutFiles(folder).map((file) => [file.path, file.sha256]));
21584
+ const changed = /* @__PURE__ */ new Set();
21585
+ for (const [file, digest] of local) if (marker.files[file] !== digest) changed.add(file);
21586
+ for (const file of Object.keys(marker.files)) {
21587
+ if (isLandingPageCheckoutPath(file) && !local.has(file)) changed.add(file);
21588
+ }
21589
+ if (marker.manifestSha256 && manifestDigest(folder) !== marker.manifestSha256) {
21590
+ changed.add(LANDING_PAGE_MANIFEST_FILE);
21591
+ }
21592
+ return [...changed].sort();
21593
+ }
21594
+ function removeStaleFiles(folder, previous, next, added = []) {
21595
+ if (!previous) return;
21596
+ for (const file of [...Object.keys(previous.files), ...added]) {
21597
+ if (next.has(file) || !isLandingPageCheckoutPath(file)) continue;
21598
+ const absolute = import_node_path25.default.resolve(folder, file);
21599
+ if (absolute.startsWith(folder + import_node_path25.default.sep) && import_node_fs28.default.existsSync(absolute)) import_node_fs28.default.rmSync(absolute);
21600
+ }
21601
+ }
21602
+ async function pullCheckout(command, creds, id, folder, force = false) {
21603
+ const existing = readMarker(folder);
21604
+ if (existing && existing.landingPageId !== id) {
21605
+ fail(
21606
+ command,
21607
+ `${folder} is a checkout of another landing page (${existing.landingPageId}).`,
21608
+ EXIT_CODES.validation
21609
+ );
21610
+ }
21611
+ const changed = existing && !force ? localChanges(folder, existing) : [];
21612
+ if (changed.length) {
21613
+ fail(
21614
+ command,
21615
+ `${folder} has changes that are not applied: ${changed.slice(0, 5).join(", ")}${changed.length > 5 ? ` and ${changed.length - 5} more` : ""}.`,
21616
+ EXIT_CODES.conflict,
21617
+ {
21618
+ code: "landing_page_local_changes",
21619
+ next_steps: [
21620
+ {
21621
+ command: `apex landing-pages pull ${id} --out <new folder>`,
21622
+ why: "Pull the current version next to your checkout, then copy your changes over."
21623
+ },
21624
+ {
21625
+ command: `apex landing-pages apply ${folder} --yes --force`,
21626
+ why: "Replace the current version with your checkout."
21627
+ },
21628
+ {
21629
+ command: `apex landing-pages pull ${id} --out ${folder} --force`,
21630
+ why: "Discard your changes."
21631
+ }
21632
+ ]
21633
+ }
21634
+ );
21635
+ }
21636
+ const api = landingPages(creds);
21637
+ const listing = await api.checkout(id);
21638
+ apiFailure(command, listing, "Failed to pull landing page");
21639
+ import_node_fs28.default.mkdirSync(folder, { recursive: true });
21640
+ const written = {};
21641
+ const downloaded = [];
21642
+ for (const batch of landingPageBatches(listing.data.files, (file) => file.bytes)) {
21643
+ const contents = await api.checkout(
21644
+ id,
21645
+ batch.map((file) => file.path)
21646
+ );
21647
+ apiFailure(command, contents, "Failed to pull landing page files");
21648
+ const returned2 = new Set(contents.data.files.map((file) => file.path));
21649
+ const missing = batch.filter((file) => !returned2.has(file.path)).map((file) => file.path);
21650
+ if (missing.length)
21651
+ fail(command, `The server did not return ${missing.slice(0, 5).join(", ")}. Pull again.`);
21652
+ for (const file of contents.data.files) {
21653
+ const target = import_node_path25.default.resolve(folder, file.path);
21654
+ if (!target.startsWith(folder + import_node_path25.default.sep))
21655
+ fail(command, `Refusing to write outside the checkout: ${file.path}`);
21656
+ const body5 = file.encoding === "base64" ? Buffer.from(file.content ?? "", "base64") : Buffer.from(file.content ?? "", "utf8");
21657
+ if (sha256(body5) !== file.sha256)
21658
+ fail(command, `The server returned ${file.path} incomplete. Pull again.`);
21659
+ downloaded.push({ path: file.path, target, body: body5, sha256: file.sha256 });
21660
+ }
21661
+ }
21662
+ if (!existing && !force) {
21663
+ const clobbered = downloaded.filter(
21664
+ ({ target, sha256: digest }) => import_node_fs28.default.existsSync(target) && sha256(import_node_fs28.default.readFileSync(target)) !== digest
21665
+ ).map((file) => file.path);
21666
+ if (clobbered.length) {
21667
+ fail(
21668
+ command,
21669
+ `${folder} already has files the pull would replace: ${clobbered.slice(0, 5).join(", ")}${clobbered.length > 5 ? ` and ${clobbered.length - 5} more` : ""}.`,
21670
+ EXIT_CODES.conflict,
21671
+ {
21672
+ code: "landing_page_folder_not_empty",
21673
+ next_steps: [
21674
+ {
21675
+ command: `apex landing-pages pull ${id} --out <new folder>`,
21676
+ why: "Pull into an empty folder."
21677
+ },
21678
+ {
21679
+ command: `apex landing-pages pull ${id} --out ${folder} --force`,
21680
+ why: "Replace those files."
21681
+ }
21682
+ ]
21683
+ }
21684
+ );
21685
+ }
21686
+ }
21687
+ for (const file of downloaded) {
21688
+ import_node_fs28.default.mkdirSync(import_node_path25.default.dirname(file.target), { recursive: true });
21689
+ import_node_fs28.default.writeFileSync(file.target, file.body);
21690
+ written[file.path] = file.sha256;
21691
+ }
21692
+ removeStaleFiles(
21693
+ folder,
21694
+ existing,
21695
+ new Set(Object.keys(written)),
21696
+ force ? readCheckoutFiles(folder).map((file) => file.path) : []
21697
+ );
21698
+ import_node_fs28.default.writeFileSync(
21699
+ import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE),
21700
+ `${JSON.stringify(listing.data.manifest ?? {}, null, 2)}
21701
+ `,
21702
+ "utf8"
21703
+ );
21704
+ const contextDir = import_node_path25.default.join(folder, LANDING_PAGE_CONTEXT_DIR);
21705
+ import_node_fs28.default.mkdirSync(contextDir, { recursive: true });
21706
+ const contextName = /^[a-z0-9_-]+\.json$/i;
21707
+ const contextEntries = Object.entries(listing.data.context ?? {}).filter(
21708
+ ([name]) => contextName.test(name)
21709
+ );
21710
+ const returned = new Set(contextEntries.map(([name]) => name));
21711
+ for (const name of import_node_fs28.default.readdirSync(contextDir)) {
21712
+ if (contextName.test(name) && !returned.has(name)) import_node_fs28.default.rmSync(import_node_path25.default.join(contextDir, name));
21713
+ }
21714
+ for (const [name, value] of contextEntries) {
21715
+ import_node_fs28.default.writeFileSync(import_node_path25.default.join(contextDir, name), `${JSON.stringify(value, null, 2)}
21716
+ `, "utf8");
21717
+ }
21718
+ writeMarker(folder, {
21719
+ schemaVersion: 1,
21720
+ baseUrl: creds.baseUrl,
21721
+ landingPageId: id,
21722
+ versionId: listing.data.version?.id ?? null,
21723
+ versionNumber: listing.data.version?.version_number ?? null,
21724
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
21725
+ files: written,
21726
+ manifestSha256: manifestDigest(folder) ?? void 0
21727
+ });
21728
+ return {
21729
+ folder,
21730
+ landing_page: listing.data.landing_page,
21731
+ version: listing.data.version,
21732
+ files: Object.keys(written).length,
21733
+ notes: listing.data.notes ?? []
21734
+ };
21735
+ }
21736
+ var create8 = new Command("create").description("Create a landing page to build outside Apex").requiredOption("--name <name>", "Page name").option("--slug <slug>", "URL slug (default: from the name)").option("--idea <key>", "Idea key or id the page is built for").option("--out <folder>", "Also pull the new page into this folder").action(async function(opts) {
21737
+ const creds = requireCredentials();
21738
+ const created = await landingPages(creds).create({
21739
+ name: opts.name,
21740
+ ...opts.slug ? { slug: opts.slug } : {},
21741
+ ...opts.idea ? { idea_key: opts.idea } : {}
21742
+ });
21743
+ apiFailure(this, created, "Failed to create landing page");
21744
+ const pulled = opts.out ? await pullCheckout(this, creds, created.data.id, import_node_path25.default.resolve(process.cwd(), opts.out)) : null;
21745
+ output(
21746
+ this,
21747
+ { landing_page: created.data, ...pulled ? { folder: pulled.folder } : {} },
21748
+ () => {
21749
+ console.log(created.data.id);
21750
+ if (pulled) console.log(`Checkout: ${pulled.folder}`);
21751
+ }
21752
+ );
21753
+ });
21754
+ var pull = new Command("pull").description("Pull a landing page into a checkout folder").argument("<id>", "Landing page id").option("--out <folder>", "Target folder (default: a folder named after the id)").option("--force", "Overwrite changes in the checkout that are not applied").action(async function(id, opts) {
21755
+ const creds = requireCredentials();
21756
+ const folder = import_node_path25.default.resolve(process.cwd(), opts.out ?? id);
21757
+ const result = await pullCheckout(this, creds, id, folder, opts.force === true);
21758
+ output(this, result, () => {
21759
+ console.log(
21760
+ `Pulled ${result.landing_page.name} (version ${result.version?.version_number ?? "none"}, ${result.files} files)`
21761
+ );
21762
+ console.log(result.folder);
21763
+ for (const note of result.notes) console.log(note);
21764
+ });
21765
+ });
21766
+ var plan = new Command("plan").description("Show what applying a checkout would change").argument("[folder]", "Checkout folder", ".").action(async function(folderArg) {
21767
+ const creds = requireCredentials();
21768
+ const { folder, marker } = requireCheckout(this, folderArg);
21769
+ const files = readCheckoutFiles(folder);
21770
+ const response = await landingPages(creds).plan(marker.landingPageId, {
21771
+ manifest: readManifest2(this, folder),
21772
+ files: declared(files)
21773
+ });
21774
+ apiFailure(this, response, "Failed to plan landing page");
21775
+ output(
21776
+ this,
21777
+ { folder, landingPageId: marker.landingPageId, plan: response.data },
21778
+ () => printPlan(response.data)
21779
+ );
21780
+ });
21781
+ var apply = new Command("apply").description("Apply a checkout as a new version").argument("[folder]", "Checkout folder", ".").option("--yes", "Apply without asking for confirmation").option("--force", "Apply even when the page changed since this checkout was pulled").action(async function(folderArg, opts) {
21782
+ const creds = requireCredentials();
21783
+ const { folder, marker } = requireCheckout(this, folderArg);
21784
+ const api = landingPages(creds);
21785
+ const files = readCheckoutFiles(folder);
21786
+ const manifest = readManifest2(this, folder);
21787
+ const planned = await api.plan(marker.landingPageId, { manifest, files: declared(files) });
21788
+ apiFailure(this, planned, "Failed to plan landing page");
21789
+ if (!planned.data.would_change) {
21790
+ output(
21791
+ this,
21792
+ { folder, applied: false, plan: planned.data, message: "No changes." },
21793
+ () => console.log("No changes.")
21794
+ );
21795
+ return;
21796
+ }
21797
+ if (!opts.yes) {
21798
+ if (!shouldUseJson(this)) printPlan(planned.data);
21799
+ const ok = await confirm2("Apply these changes as a new version?");
21800
+ if (ok === null) {
21801
+ confirmationRequired(
21802
+ this,
21803
+ "Applying a landing page needs confirmation.",
21804
+ `apex landing-pages apply ${folderArg} --yes`
21805
+ );
21806
+ }
21807
+ if (!ok) {
21808
+ output(this, { aborted: true }, () => console.log("Aborted."));
21809
+ return;
21810
+ }
21811
+ }
21812
+ const required = new Set(planned.data.files.upload_required);
21813
+ const toUpload = files.filter((file) => required.has(file.path));
21814
+ for (const batch of landingPageBatches(toUpload, (file) => file.bytes)) {
21815
+ const uploaded = await api.upload(
21816
+ marker.landingPageId,
21817
+ batch.map((file) => ({
21818
+ path: file.path,
21819
+ content: file.body.toString("base64"),
21820
+ encoding: "base64"
21821
+ }))
21822
+ );
21823
+ apiFailure(this, uploaded, "Failed to upload landing page files");
21824
+ }
21825
+ const applied = await api.apply(marker.landingPageId, {
21826
+ manifest,
21827
+ files: declared(files),
21828
+ ...opts.force ? {} : { base_version_id: marker.versionId }
21829
+ });
21830
+ if (!applied.ok && applied.status === 409 && applied.errorDetails?.code === "landing_page_version_conflict") {
21831
+ fail(
21832
+ this,
21833
+ applied.error ?? "The page changed since this checkout was pulled.",
21834
+ EXIT_CODES.conflict,
21835
+ {
21836
+ ...applied.errorDetails,
21837
+ next_steps: [
21838
+ {
21839
+ command: `apex landing-pages pull ${marker.landingPageId} --out ${folderArg}`,
21840
+ why: "Bring the checkout up to date."
21841
+ },
21842
+ {
21843
+ command: `apex landing-pages apply ${folderArg} --yes --force`,
21844
+ why: "Replace the current version anyway."
21845
+ }
21846
+ ]
21847
+ }
21848
+ );
21849
+ }
21850
+ apiFailure(this, applied, "Failed to apply landing page");
21851
+ writeMarker(folder, {
21852
+ ...marker,
21853
+ versionId: applied.data.version_id,
21854
+ versionNumber: applied.data.version_number,
21855
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
21856
+ files: Object.fromEntries(files.map((file) => [file.path, file.sha256])),
21857
+ manifestSha256: manifestDigest(folder) ?? void 0
21858
+ });
21859
+ output(this, { folder, applied: true, ...applied.data }, () => {
21860
+ console.log(`Version ${applied.data.version_number}`);
21861
+ if (applied.data.preview_url) console.log(`Preview: ${applied.data.preview_url}`);
21862
+ console.log(
21863
+ `Deploy target: ${applied.data.deploy_target ?? "none yet: set host in apex-landing-page.json to a domain this shop holds"}`
21864
+ );
21865
+ });
21866
+ });
21867
+ var preview = new Command("preview").description("Print a signed preview link (valid 24 hours)").argument("[folder-or-id]", "Checkout folder or landing page id", ".").option("--version <number>", "Version number (default: current)").action(async function(target, opts) {
21868
+ const versionNumber = opts.version === void 0 ? void 0 : Number(opts.version);
21869
+ if (versionNumber !== void 0 && (!Number.isInteger(versionNumber) || versionNumber < 1)) {
21870
+ fail(this, "--version must be a version number.", EXIT_CODES.validation);
21871
+ }
21872
+ const response = await landingPages(requireCredentials()).preview(
21873
+ resolvePageId(target),
21874
+ versionNumber
21875
+ );
21876
+ apiFailure(this, response, "Failed to get preview link");
21877
+ output(this, response.data, () => console.log(response.data.preview_url));
21878
+ });
21879
+ async function versionIdFor(command, creds, id, raw) {
21880
+ if (raw === void 0) return void 0;
21881
+ const number = Number(raw);
21882
+ if (!Number.isInteger(number) || number < 1)
21883
+ fail(command, "--version must be a version number.", EXIT_CODES.validation);
21884
+ const versions2 = await landingPages(creds).versions(id);
21885
+ apiFailure(command, versions2, "Failed to list versions");
21886
+ const match2 = versions2.data.find((version) => version.version_number === number);
21887
+ if (!match2) fail(command, `Version ${number} not found.`, EXIT_CODES.validation);
21888
+ return match2.id;
21889
+ }
21890
+ var SPLIT_KEEPS_SERVING_HELP = "\nWhile an experiment splits this page, visitors keep getting its two arms and its goals stay as they are (new manifest goals are added). The deployed version serves when the experiment ends; its goal changes apply at the next deploy after that.\n";
21891
+ var deploy = new Command("deploy").description("Deploy a version to a domain this shop holds").argument("[folder-or-id]", "Checkout folder or landing page id", ".").option(
21892
+ "--host <host>",
21893
+ "Domain claimed with apex domains claim (default: the manifest host; one of the two is required)"
21894
+ ).option("--path <path>", "Path on the host").option("--version <number>", "Version number (default: current)").action(async function(target, opts) {
21895
+ const creds = requireCredentials();
21896
+ const id = resolvePageId(target);
21897
+ const versionId = await versionIdFor(this, creds, id, opts.version);
21898
+ const response = await landingPages(creds).deploy(id, {
21899
+ ...versionId ? { version_id: versionId } : {},
21900
+ ...opts.host ? { host: opts.host } : {},
21901
+ ...opts.path ? { path: opts.path } : {}
21902
+ });
21903
+ apiFailure(this, response, "Failed to deploy landing page");
21904
+ output(this, response.data, () => {
21905
+ console.log(response.data.url);
21906
+ console.log(`Version ${response.data.version_number} is live.`);
21907
+ });
21908
+ }).addHelpText("after", SPLIT_KEEPS_SERVING_HELP);
21909
+ var versions = new Command("versions").description("List versions").argument("[folder-or-id]", "Checkout folder or landing page id", ".").action(async function(target) {
21910
+ const response = await landingPages(requireCredentials()).versions(resolvePageId(target));
21911
+ apiFailure(this, response, "Failed to list versions");
21912
+ output(
21913
+ this,
21914
+ response.data,
21915
+ () => printTable(
21916
+ ["VERSION", "SOURCE", "STATE", "FILES", "BYTES", "CREATED"],
21917
+ response.data.map((version) => [
21918
+ String(version.version_number),
21919
+ version.source,
21920
+ [version.published ? "live" : "", version.current ? "current" : ""].filter(Boolean).join(", ") || "-",
21921
+ version.file_count === null ? "-" : String(version.file_count),
21922
+ version.bytes === null ? "-" : String(version.bytes),
21923
+ version.created_at
21924
+ ]),
21925
+ [8, 10, 14, 6, 10, 26]
21926
+ )
21927
+ );
21928
+ });
21929
+ var rollback = new Command("rollback").description("Deploy an earlier version to the page's current address").argument("[folder-or-id]", "Checkout folder or landing page id", ".").requiredOption("--version <number>", "Version number to roll back to").action(async function(target, opts) {
21930
+ const number = Number(opts.version);
21931
+ if (!Number.isInteger(number) || number < 1)
21932
+ fail(this, "--version must be a version number.", EXIT_CODES.validation);
21933
+ const response = await landingPages(requireCredentials()).rollback(resolvePageId(target), {
21934
+ version_number: number
21935
+ });
21936
+ apiFailure(this, response, "Failed to roll back landing page");
21937
+ output(this, response.data, () => {
21938
+ console.log(response.data.url);
21939
+ console.log(`Version ${response.data.version_number} is live.`);
21940
+ });
21941
+ }).addHelpText("after", SPLIT_KEEPS_SERVING_HELP);
21942
+ var experiment = new Command("experiment").description("Create a draft experiment that splits the live page between two versions").argument("[folder-or-id]", "Checkout folder or landing page id", ".").option(
21943
+ "--control <number>",
21944
+ "Control version number (default: the manifest's experiment.control)"
21945
+ ).option(
21946
+ "--variant <number>",
21947
+ "Variant version number (default: the manifest's experiment.variant)"
21948
+ ).option("--name <name>", "Experiment name").option("--primary-goal <key>", "Manifest goal key to make primary (default: the first goal)").action(async function(target, opts) {
21949
+ const folder = import_node_path25.default.resolve(process.cwd(), target);
21950
+ const fromManifest = import_node_fs28.default.existsSync(import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE)) ? readManifest2(this, folder)?.experiment ?? {} : {};
21951
+ const control = Number(opts.control ?? fromManifest.control);
21952
+ const variant = Number(opts.variant ?? fromManifest.variant);
21953
+ if (!Number.isInteger(control) || !Number.isInteger(variant) || control < 1 || variant < 1) {
21954
+ fail(
21955
+ this,
21956
+ "Name the control and variant version numbers (--control, --variant, or experiment in apex-landing-page.json).",
21957
+ EXIT_CODES.validation
21958
+ );
21959
+ }
21960
+ const response = await landingPages(requireCredentials()).experiment(resolvePageId(target), {
21961
+ control,
21962
+ variant,
21963
+ ...opts.name ? { name: opts.name } : {},
21964
+ ...opts.primaryGoal ? { primary_goal: opts.primaryGoal } : {}
21965
+ });
21966
+ apiFailure(this, response, "Failed to create experiment");
21967
+ output(this, response.data, () => {
21968
+ console.log(`Draft experiment ${String(response.data.experiment.id)} created.`);
21969
+ console.log("Start it in the Apex dashboard when it is ready.");
21970
+ });
21971
+ });
21972
+ var open2 = new Command("open").description("Print the page's dashboard URL").argument("[folder-or-id]", "Checkout folder or landing page id", ".").action(function(target) {
21973
+ const url = `${requireCredentials().baseUrl.replace(/\/+$/, "")}/landing-pages/${encodeURIComponent(resolvePageId(target))}`;
21974
+ output(this, { url }, () => console.log(url));
21975
+ });
21976
+ var landingPageWorkspaceCommands = [
21977
+ create8,
21978
+ pull,
21979
+ plan,
21980
+ apply,
21981
+ preview,
21982
+ deploy,
21983
+ versions,
21984
+ rollback,
21985
+ experiment,
21986
+ open2
21987
+ ];
21988
+
21989
+ // src/commands/config-resources.ts
21122
21990
  function show(resource) {
21123
21991
  process.stdout.write(`${JSON.stringify(resource, null, 2)}
21124
21992
  `);
@@ -21184,12 +22052,12 @@ function readFamily(family, label) {
21184
22052
  }
21185
22053
  var assetReads = readFamily("assets", "assets");
21186
22054
  var upload = new Command("upload").description("Upload an asset").argument("<file>").action(async function(file) {
21187
- const absolute = import_node_path25.default.resolve(process.cwd(), file);
21188
- if (!import_node_fs28.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
21189
- const fileStat = import_node_fs28.default.statSync(absolute);
22055
+ const absolute = import_node_path26.default.resolve(process.cwd(), file);
22056
+ if (!import_node_fs29.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
22057
+ const fileStat = import_node_fs29.default.statSync(absolute);
21190
22058
  if (!fileStat.isFile()) fail(this, `File not found: ${file}`, 3);
21191
22059
  if (fileStat.size > MAX_ASSET_SIZE_BYTES) fail(this, `${file} is larger than 10 MB`, 3);
21192
- const mimeType = ASSET_MIME_TYPES[import_node_path25.default.extname(absolute).toLowerCase()];
22060
+ const mimeType = ASSET_MIME_TYPES[import_node_path26.default.extname(absolute).toLowerCase()];
21193
22061
  if (!mimeType)
21194
22062
  fail(
21195
22063
  this,
@@ -21198,8 +22066,8 @@ var upload = new Command("upload").description("Upload an asset").argument("<fil
21198
22066
  );
21199
22067
  const r = await uploadAssets(requireCredentials(), [
21200
22068
  {
21201
- name: import_node_path25.default.basename(absolute),
21202
- bytes: import_node_fs28.default.readFileSync(absolute),
22069
+ name: import_node_path26.default.basename(absolute),
22070
+ bytes: import_node_fs29.default.readFileSync(absolute),
21203
22071
  type: mimeType
21204
22072
  }
21205
22073
  ]);
@@ -21207,7 +22075,7 @@ var upload = new Command("upload").description("Upload an asset").argument("<fil
21207
22075
  fail(this, r.error ?? "Failed to upload asset", classifyStatus(r.status), r.errorDetails);
21208
22076
  output(this, r.data, () => r.data.assets.forEach(show));
21209
22077
  });
21210
- var remove5 = new Command("delete").alias("rm").description("Delete an asset").argument("<id>").option("-f, --force", "Skip confirmation").action(async function(id, opts) {
22078
+ var remove4 = new Command("delete").alias("rm").description("Delete an asset").argument("<id>").option("-f, --force", "Skip confirmation").action(async function(id, opts) {
21211
22079
  if (!opts.force) {
21212
22080
  const confirmed = await confirmAssetDelete(id);
21213
22081
  if (!confirmed) {
@@ -21223,9 +22091,12 @@ var remove5 = new Command("delete").alias("rm").description("Delete an asset").a
21223
22091
  fail(this, r.error ?? "Failed to delete asset", classifyStatus(r.status), r.errorDetails);
21224
22092
  output(this, r.data, () => console.log(`Deleted ${id}`));
21225
22093
  });
21226
- var assetsCommand = new Command("assets").description("Manage assets").addCommand(assetReads.list).addCommand(assetReads.get).addCommand(upload).addCommand(remove5);
22094
+ var assetsCommand = new Command("assets").description("Manage assets").addCommand(assetReads.list).addCommand(assetReads.get).addCommand(upload).addCommand(remove4);
21227
22095
  var landingReads = readFamily("landing-pages", "landing pages");
21228
- var landingPagesCommand = new Command("landing-pages").description("Read landing pages").addCommand(landingReads.list).addCommand(landingReads.get);
22096
+ var landingPagesCommand = new Command("landing-pages").description(
22097
+ "Build landing pages with your own tools, then deploy, track and test them with Apex"
22098
+ ).addCommand(landingReads.list).addCommand(landingReads.get);
22099
+ for (const command of landingPageWorkspaceCommands) landingPagesCommand.addCommand(command);
21229
22100
  var snippet = new Command("snippet").description("Generate an external purchase pixel snippet").option("--provider <name>").option("--handover-param <name>").option("--global-name <name>").action(async function(opts) {
21230
22101
  const body5 = Object.fromEntries(
21231
22102
  Object.entries(opts).filter(([, value]) => value !== void 0)