@drip-apex/apex-cli 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/README.md +36 -0
- package/dist/cli.js +208 -163
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +3 -1
package/AGENTS.md
CHANGED
|
@@ -18,7 +18,10 @@ Use `apex` as the canonical command name. `drip` is a compatibility alias.
|
|
|
18
18
|
- Launch is dashboard-only. The API and CLI do not start traffic.
|
|
19
19
|
- Prefer `--json`; piped stdout defaults to JSON automatically.
|
|
20
20
|
- JSON output is a stable contract: top level is `{ ok, version, command, data | error }`.
|
|
21
|
-
- Diagnostics, prompts, progress,
|
|
21
|
+
- With `--json`, stdout is exactly one JSON document. Diagnostics, prompts, progress,
|
|
22
|
+
and update notices go to stderr.
|
|
23
|
+
- For single-request commands `data` is the API response body as returned (lists at
|
|
24
|
+
`.data.data[]`). API failures carry the API error body in `error.api`.
|
|
22
25
|
- Do not parse human tables. Use `apex schema --json` first if you need flags.
|
|
23
26
|
- Keep command names exact. Do not infer or guess names from the CLI's
|
|
24
27
|
"did you mean" hints — those are stderr diagnostics for humans; machine
|
package/README.md
CHANGED
|
@@ -100,6 +100,42 @@ Summary timestamps include `created_at` and `archived_at`; experiments have no `
|
|
|
100
100
|
`--include-archived` to include them; archived rows display their archive date
|
|
101
101
|
when the API provides `archived_at`.
|
|
102
102
|
|
|
103
|
+
## JSON output contract
|
|
104
|
+
|
|
105
|
+
With `--json`, or whenever stdout is piped, stdout carries exactly one JSON
|
|
106
|
+
document and nothing else. Spinners, progress, prompts, warnings, human text and
|
|
107
|
+
child-process output go to stderr, so `apex ... --json | jq` always parses.
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{ "ok": true, "version": "0.13.0", "command": "apex flags get", "data": {}, "context": {} }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- `data` is the API response body exactly as the API returned it for every command
|
|
114
|
+
backed by one API request. Lists keep the API's `{ "data": [...], "pagination": {...} }`
|
|
115
|
+
shape, so rows are at `.data.data[]`; single resources are at `.data` directly.
|
|
116
|
+
- `apex experiments get` adds one CLI-derived key, `dashboard_url`, beside the
|
|
117
|
+
experiment fields.
|
|
118
|
+
- Commands that combine several requests or local files (`experiments create`,
|
|
119
|
+
`pull`, `plan`, `apply`, `qa approve`, `triggers`, `project-code`, `harness`,
|
|
120
|
+
`workspace`, `doctor`, `whoami`, `push`, `analytics urls`, `flags status`,
|
|
121
|
+
`goals test`) print a documented CLI-composed `data` object.
|
|
122
|
+
- Failures print `{ "ok": false, "version", "command", "error", "context" }`.
|
|
123
|
+
`error.code` is the CLI exit code, `error.details` carries retry hints and
|
|
124
|
+
server details, and `error.api` is `{ "status", "body" }` with the API error
|
|
125
|
+
envelope verbatim whenever an API response caused the failure.
|
|
126
|
+
- `deprecated` appears on the envelope only when a deprecated flag was used.
|
|
127
|
+
|
|
128
|
+
**Breaking in 0.13.0.** Earlier releases re-nested or renamed API bodies. Update
|
|
129
|
+
scripts: `.data.experiments[]` becomes `.data.data[]`; `experiments get`
|
|
130
|
+
`.data.experiment.id` becomes `.data.id`; `.data.flag`, `.data.goal`,
|
|
131
|
+
`.data.funnel`, `.data.signal`, `.data.backlog_item`, `.data.personalization`,
|
|
132
|
+
`.data.exclusion_group`, `.data.asset`, `.data.landing_page`, `.data.webhook`,
|
|
133
|
+
`.data.targeting` and `.data.verification` wrappers are gone (read the fields at
|
|
134
|
+
`.data`); `experiments results` reads at `.data` instead of `.data.results`,
|
|
135
|
+
`funnels run` at `.data` instead of `.data.run`, and `brand` at `.data.data`
|
|
136
|
+
instead of `.data.brandContext`; lifecycle and delete commands return the API's
|
|
137
|
+
body instead of a CLI summary.
|
|
138
|
+
|
|
103
139
|
## Command index
|
|
104
140
|
|
|
105
141
|
Use `apex schema --json` to emit the complete machine-readable command surface,
|
package/dist/cli.js
CHANGED
|
@@ -7112,7 +7112,7 @@ var import_node_os3 = __toESM(require("node:os"));
|
|
|
7112
7112
|
// package.json
|
|
7113
7113
|
var package_default = {
|
|
7114
7114
|
name: "@drip-apex/apex-cli",
|
|
7115
|
-
version: "0.
|
|
7115
|
+
version: "0.13.0",
|
|
7116
7116
|
license: "MIT",
|
|
7117
7117
|
description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
|
|
7118
7118
|
private: false,
|
|
@@ -7172,16 +7172,42 @@ var EXIT_CODES = {
|
|
|
7172
7172
|
confirmationRequired: 4,
|
|
7173
7173
|
conflict: 5
|
|
7174
7174
|
};
|
|
7175
|
+
var MAX_RECORDED_API_FAILURES = 32;
|
|
7176
|
+
var recentApiFailures = [];
|
|
7177
|
+
function recordApiResponse(response) {
|
|
7178
|
+
if (response.ok) return;
|
|
7179
|
+
recentApiFailures.push({
|
|
7180
|
+
status: response.status,
|
|
7181
|
+
body: response.data ?? null,
|
|
7182
|
+
message: response.error,
|
|
7183
|
+
details: response.errorDetails
|
|
7184
|
+
});
|
|
7185
|
+
if (recentApiFailures.length > MAX_RECORDED_API_FAILURES) recentApiFailures.shift();
|
|
7186
|
+
}
|
|
7187
|
+
function matchApiFailure(message, details) {
|
|
7188
|
+
const byDetails = details !== void 0 ? recentApiFailures.find((entry) => entry.details === details) : void 0;
|
|
7189
|
+
if (byDetails) return { status: byDetails.status, body: byDetails.body };
|
|
7190
|
+
const byMessage = recentApiFailures.filter(
|
|
7191
|
+
(entry) => Boolean(entry.message) && message.includes(entry.message)
|
|
7192
|
+
);
|
|
7193
|
+
if (byMessage.length !== 1) return void 0;
|
|
7194
|
+
return { status: byMessage[0].status, body: byMessage[0].body };
|
|
7195
|
+
}
|
|
7175
7196
|
var CliError = class extends Error {
|
|
7176
7197
|
exitCode;
|
|
7177
7198
|
details;
|
|
7178
|
-
|
|
7199
|
+
api;
|
|
7200
|
+
constructor(message, exitCode = EXIT_CODES.general, details, api) {
|
|
7179
7201
|
super(message);
|
|
7180
7202
|
this.name = "CliError";
|
|
7181
7203
|
this.exitCode = exitCode;
|
|
7182
7204
|
this.details = details;
|
|
7205
|
+
this.api = api ?? matchApiFailure(message, details);
|
|
7183
7206
|
}
|
|
7184
7207
|
};
|
|
7208
|
+
function apiFailureOf(response) {
|
|
7209
|
+
return response.ok ? void 0 : { status: response.status, body: response.data ?? null };
|
|
7210
|
+
}
|
|
7185
7211
|
var initialEnvironmentApiKey = process.env.APEX_API_KEY || process.env.DRIP_API_KEY;
|
|
7186
7212
|
var envelopeContext = {
|
|
7187
7213
|
org_id: null,
|
|
@@ -7196,6 +7222,7 @@ var commandName = "apex";
|
|
|
7196
7222
|
var wroteEnvelope = false;
|
|
7197
7223
|
var stdoutLines = [];
|
|
7198
7224
|
var originalConsoleLog = null;
|
|
7225
|
+
var rawStdoutWrite = null;
|
|
7199
7226
|
var deprecationMarker = null;
|
|
7200
7227
|
var interceptedCommands = /* @__PURE__ */ new WeakMap();
|
|
7201
7228
|
function interceptCliError(err, command) {
|
|
@@ -7222,18 +7249,52 @@ function formatConsoleArgs(args) {
|
|
|
7222
7249
|
return JSON.stringify(arg);
|
|
7223
7250
|
}).join(" ");
|
|
7224
7251
|
}
|
|
7252
|
+
function installJsonStdoutGuard() {
|
|
7253
|
+
if (rawStdoutWrite) return;
|
|
7254
|
+
const stdout = process.stdout;
|
|
7255
|
+
const original = stdout.write.bind(stdout);
|
|
7256
|
+
rawStdoutWrite = (text) => original(text);
|
|
7257
|
+
stdout.write = ((chunk, ...args) => process.stderr.write(
|
|
7258
|
+
chunk,
|
|
7259
|
+
...args
|
|
7260
|
+
));
|
|
7261
|
+
}
|
|
7262
|
+
function isJsonMode() {
|
|
7263
|
+
return jsonMode;
|
|
7264
|
+
}
|
|
7225
7265
|
function configureRuntime(command) {
|
|
7226
7266
|
commandName = commandPath(command) || commandName;
|
|
7227
7267
|
jsonMode = shouldUseJson(command);
|
|
7228
|
-
if (!jsonMode
|
|
7268
|
+
if (!jsonMode) return;
|
|
7269
|
+
installJsonStdoutGuard();
|
|
7270
|
+
if (originalConsoleLog) return;
|
|
7229
7271
|
originalConsoleLog = console.log;
|
|
7230
7272
|
console.log = (...args) => {
|
|
7231
7273
|
stdoutLines.push(formatConsoleArgs(args));
|
|
7232
7274
|
};
|
|
7233
7275
|
}
|
|
7276
|
+
function writeJsonDocument(envelope) {
|
|
7277
|
+
const text = `${JSON.stringify(envelope, null, 2)}
|
|
7278
|
+
`;
|
|
7279
|
+
if (wroteEnvelope) {
|
|
7280
|
+
process.stderr.write(text);
|
|
7281
|
+
return;
|
|
7282
|
+
}
|
|
7283
|
+
wroteEnvelope = true;
|
|
7284
|
+
const pendingHumanLines = stdoutLines;
|
|
7285
|
+
stdoutLines = [];
|
|
7286
|
+
restoreConsoleLog();
|
|
7287
|
+
if (pendingHumanLines.length > 0) process.stderr.write(`${pendingHumanLines.join("\n")}
|
|
7288
|
+
`);
|
|
7289
|
+
if (rawStdoutWrite) rawStdoutWrite(text);
|
|
7290
|
+
else process.stdout.write(text);
|
|
7291
|
+
}
|
|
7234
7292
|
function flushFallbackEnvelope() {
|
|
7235
|
-
if (!jsonMode || wroteEnvelope
|
|
7236
|
-
|
|
7293
|
+
if (!jsonMode || wroteEnvelope) return;
|
|
7294
|
+
if (process.exitCode !== void 0 && process.exitCode !== 0) return;
|
|
7295
|
+
const output2 = stdoutLines.join("\n");
|
|
7296
|
+
stdoutLines = [];
|
|
7297
|
+
writeEnvelope(output2 ? { output: output2 } : null);
|
|
7237
7298
|
}
|
|
7238
7299
|
function restoreConsoleLog() {
|
|
7239
7300
|
if (!originalConsoleLog) return;
|
|
@@ -7252,10 +7313,7 @@ function writeEnvelope(data, command = commandName) {
|
|
|
7252
7313
|
context: envelopeContext,
|
|
7253
7314
|
...deprecationMarker ? { deprecated: deprecationMarker } : {}
|
|
7254
7315
|
};
|
|
7255
|
-
|
|
7256
|
-
restoreConsoleLog();
|
|
7257
|
-
process.stdout.write(`${JSON.stringify(envelope, null, 2)}
|
|
7258
|
-
`);
|
|
7316
|
+
writeJsonDocument(envelope);
|
|
7259
7317
|
}
|
|
7260
7318
|
function writeErrorEnvelope(error, command = commandName) {
|
|
7261
7319
|
const envelope = {
|
|
@@ -7265,14 +7323,12 @@ function writeErrorEnvelope(error, command = commandName) {
|
|
|
7265
7323
|
error: {
|
|
7266
7324
|
message: error.message,
|
|
7267
7325
|
code: error.exitCode,
|
|
7268
|
-
...error.details ? { details: error.details } : {}
|
|
7326
|
+
...error.details ? { details: error.details } : {},
|
|
7327
|
+
...error.api ? { api: error.api } : {}
|
|
7269
7328
|
},
|
|
7270
7329
|
context: envelopeContext
|
|
7271
7330
|
};
|
|
7272
|
-
|
|
7273
|
-
restoreConsoleLog();
|
|
7274
|
-
process.stdout.write(`${JSON.stringify(envelope, null, 2)}
|
|
7275
|
-
`);
|
|
7331
|
+
writeJsonDocument(envelope);
|
|
7276
7332
|
}
|
|
7277
7333
|
function output(command, data, human) {
|
|
7278
7334
|
if (shouldUseJson(command)) {
|
|
@@ -7291,8 +7347,8 @@ function commandPath(command) {
|
|
|
7291
7347
|
}
|
|
7292
7348
|
return names.join(" ");
|
|
7293
7349
|
}
|
|
7294
|
-
function fail(command, message, exitCode = EXIT_CODES.general, details) {
|
|
7295
|
-
throw new CliError(message, exitCode, details);
|
|
7350
|
+
function fail(command, message, exitCode = EXIT_CODES.general, details, api) {
|
|
7351
|
+
throw new CliError(message, exitCode, details, api);
|
|
7296
7352
|
}
|
|
7297
7353
|
function classifyStatus(status2) {
|
|
7298
7354
|
if (status2 === 401 || status2 === 403) return EXIT_CODES.auth;
|
|
@@ -7679,7 +7735,7 @@ function createHttpClient(creds, options) {
|
|
|
7679
7735
|
}
|
|
7680
7736
|
inspectVersion(response.headers);
|
|
7681
7737
|
const json = await response.json().catch(() => null);
|
|
7682
|
-
if (!response.ok) return { ok: false, status: response.status, data:
|
|
7738
|
+
if (!response.ok) return { ok: false, status: response.status, data: json, error: extractErrorMessage(json, response.status), errorDetails: extractErrorDetails(json), requestId: response.headers.get("x-request-id"), rateLimit: { limit: response.headers.get("x-ratelimit-limit"), remaining: response.headers.get("x-ratelimit-remaining"), reset: response.headers.get("x-ratelimit-reset") } };
|
|
7683
7739
|
return { ok: true, status: response.status, data: json };
|
|
7684
7740
|
}
|
|
7685
7741
|
return { request: (method, path24, body5, timeoutMs) => request2(method, path24, body5, timeoutMs), requestForm: (method, path24, body5) => request2(method, path24, body5, void 0, true) };
|
|
@@ -7792,10 +7848,14 @@ function sharedClient(creds) {
|
|
|
7792
7848
|
return client;
|
|
7793
7849
|
}
|
|
7794
7850
|
async function request(creds, method, path24, body5, requestTimeoutMs) {
|
|
7795
|
-
|
|
7851
|
+
const response = await sharedClient(creds).request(method, path24, body5, requestTimeoutMs);
|
|
7852
|
+
recordApiResponse(response);
|
|
7853
|
+
return response;
|
|
7796
7854
|
}
|
|
7797
7855
|
async function requestForm(creds, method, path24, body5) {
|
|
7798
|
-
|
|
7856
|
+
const response = await sharedClient(creds).requestForm(method, path24, body5);
|
|
7857
|
+
recordApiResponse(response);
|
|
7858
|
+
return response;
|
|
7799
7859
|
}
|
|
7800
7860
|
function listExperiments(creds, opts) {
|
|
7801
7861
|
const params = new URLSearchParams();
|
|
@@ -9841,7 +9901,8 @@ async function resolveExperiment(idOrSlug) {
|
|
|
9841
9901
|
throw new CliError(
|
|
9842
9902
|
res2.status === 404 && isTestKeyReference(idOrSlug) ? `Experiment "${idOrSlug}" not found in selected shop ${creds.resolvedContext?.shopId ?? "(API key shop)"}. Test keys resolve within the selected shop; use --shop <shop-id> to change it.` : res2.error ?? `Experiment "${idOrSlug}" not found`,
|
|
9843
9903
|
classifyStatus(res2.status),
|
|
9844
|
-
res2.errorDetails
|
|
9904
|
+
res2.errorDetails,
|
|
9905
|
+
apiFailureOf(res2)
|
|
9845
9906
|
);
|
|
9846
9907
|
}
|
|
9847
9908
|
const slug = idOrSlug.startsWith("slug:") ? idOrSlug.slice(5) : idOrSlug;
|
|
@@ -9850,7 +9911,8 @@ async function resolveExperiment(idOrSlug) {
|
|
|
9850
9911
|
throw new CliError(
|
|
9851
9912
|
res.error ?? `Experiment "${idOrSlug}" not found`,
|
|
9852
9913
|
classifyStatus(res.status),
|
|
9853
|
-
res.errorDetails
|
|
9914
|
+
res.errorDetails,
|
|
9915
|
+
apiFailureOf(res)
|
|
9854
9916
|
);
|
|
9855
9917
|
}
|
|
9856
9918
|
var list = new Command("list").alias("ls").description(
|
|
@@ -9877,7 +9939,7 @@ var list = new Command("list").alias("ls").description(
|
|
|
9877
9939
|
}
|
|
9878
9940
|
spinner?.stop();
|
|
9879
9941
|
const experiments = res.data.data;
|
|
9880
|
-
output(this,
|
|
9942
|
+
output(this, res.data, () => {
|
|
9881
9943
|
if (experiments.length === 0) {
|
|
9882
9944
|
console.log(source_default.dim("No experiments found."));
|
|
9883
9945
|
return;
|
|
@@ -9929,11 +9991,7 @@ var get = new Command("get").description(
|
|
|
9929
9991
|
exp.shop_id ?? creds.resolvedContext?.shopId,
|
|
9930
9992
|
exp.id
|
|
9931
9993
|
);
|
|
9932
|
-
output(
|
|
9933
|
-
this,
|
|
9934
|
-
{ experiment: exp, dashboard_url: dashboardUrl },
|
|
9935
|
-
() => printExperiment(exp, dashboardUrl)
|
|
9936
|
-
);
|
|
9994
|
+
output(this, { ...exp, dashboard_url: dashboardUrl }, () => printExperiment(exp, dashboardUrl));
|
|
9937
9995
|
});
|
|
9938
9996
|
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) {
|
|
9939
9997
|
const experiment = await resolveExperiment(experimentRef);
|
|
@@ -9990,7 +10048,7 @@ var results = new Command("results").description("Read experiment results and ti
|
|
|
9990
10048
|
spinner?.stop();
|
|
9991
10049
|
output(
|
|
9992
10050
|
this,
|
|
9993
|
-
|
|
10051
|
+
res.data,
|
|
9994
10052
|
() => printExperimentResults(res.data, opts.currency)
|
|
9995
10053
|
);
|
|
9996
10054
|
} catch (error) {
|
|
@@ -10576,7 +10634,7 @@ var pause = new Command("pause").description("Pause a running experiment").argum
|
|
|
10576
10634
|
res.errorDetails
|
|
10577
10635
|
);
|
|
10578
10636
|
}
|
|
10579
|
-
output(this,
|
|
10637
|
+
output(this, res.data, () => {
|
|
10580
10638
|
spinner.succeed(`Experiment "${exp.name}" is now ${statusBadge("paused")}`);
|
|
10581
10639
|
});
|
|
10582
10640
|
});
|
|
@@ -10598,7 +10656,7 @@ var complete = new Command("complete").description("Complete/stop an experiment"
|
|
|
10598
10656
|
res.errorDetails
|
|
10599
10657
|
);
|
|
10600
10658
|
}
|
|
10601
|
-
output(this,
|
|
10659
|
+
output(this, res.data, () => {
|
|
10602
10660
|
spinner.succeed(`Experiment "${exp.name}" is now ${statusBadge("completed")}`);
|
|
10603
10661
|
});
|
|
10604
10662
|
});
|
|
@@ -10626,7 +10684,7 @@ var del = new Command("delete").alias("rm").description("Delete an experiment").
|
|
|
10626
10684
|
});
|
|
10627
10685
|
rl.close();
|
|
10628
10686
|
if (answer.toLowerCase() !== "y") {
|
|
10629
|
-
console.log("Aborted.");
|
|
10687
|
+
output(this, { aborted: true }, () => console.log("Aborted."));
|
|
10630
10688
|
return;
|
|
10631
10689
|
}
|
|
10632
10690
|
}
|
|
@@ -10641,11 +10699,7 @@ var del = new Command("delete").alias("rm").description("Delete an experiment").
|
|
|
10641
10699
|
res.errorDetails
|
|
10642
10700
|
);
|
|
10643
10701
|
}
|
|
10644
|
-
output(
|
|
10645
|
-
this,
|
|
10646
|
-
{ experiment: { id: exp.id, name: exp.name }, deleted: true },
|
|
10647
|
-
() => spinner.succeed(`Experiment "${exp.name}" deleted.`)
|
|
10648
|
-
);
|
|
10702
|
+
output(this, res.data, () => spinner.succeed(`Experiment "${exp.name}" deleted.`));
|
|
10649
10703
|
});
|
|
10650
10704
|
var pullFilesystem = new Command("pull").description("Pull an experiment as an editable filesystem").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--out <path>", "Target folder").action(async function action13(idOrSlug, opts) {
|
|
10651
10705
|
const creds = requireCredentials();
|
|
@@ -10814,7 +10868,7 @@ var applyFilesystem = new Command("apply").option("--sync", "Refresh local autho
|
|
|
10814
10868
|
);
|
|
10815
10869
|
}
|
|
10816
10870
|
if (!ok) {
|
|
10817
|
-
console.log("Aborted.");
|
|
10871
|
+
output(this, { aborted: true }, () => console.log("Aborted."));
|
|
10818
10872
|
return;
|
|
10819
10873
|
}
|
|
10820
10874
|
}
|
|
@@ -10920,7 +10974,7 @@ var qaStatus = new Command("status").description(
|
|
|
10920
10974
|
resolved.folder,
|
|
10921
10975
|
resolved.experimentId
|
|
10922
10976
|
);
|
|
10923
|
-
output(this,
|
|
10977
|
+
output(this, res.data, () => printQaRun(res.data.run));
|
|
10924
10978
|
} catch (error) {
|
|
10925
10979
|
spinner.fail("Failed to fetch QA run");
|
|
10926
10980
|
throw error;
|
|
@@ -10962,7 +11016,7 @@ var qaCheck = new Command("check").description("Update one QA checklist item wit
|
|
|
10962
11016
|
resolved.folder,
|
|
10963
11017
|
resolved.experimentId
|
|
10964
11018
|
);
|
|
10965
|
-
const data = opts.summary ? qaSummary(run3, run3.checklist.find((item) => item.id === opts.item) ?? null) :
|
|
11019
|
+
const data = opts.summary ? qaSummary(run3, run3.checklist.find((item) => item.id === opts.item) ?? null) : res.data;
|
|
10966
11020
|
output(this, data, () => {
|
|
10967
11021
|
spinner.succeed("Updated QA checklist");
|
|
10968
11022
|
if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
@@ -11011,7 +11065,7 @@ var qaNotes = new Command("notes").description("Update QA run notes or acceptanc
|
|
|
11011
11065
|
const data = opts.summary ? qaSummary(run3, {
|
|
11012
11066
|
notes: run3.notes,
|
|
11013
11067
|
acceptanceCriteriaSummary: run3.acceptanceCriteriaSummary
|
|
11014
|
-
}) :
|
|
11068
|
+
}) : res.data;
|
|
11015
11069
|
output(this, data, () => {
|
|
11016
11070
|
spinner.succeed("Updated QA notes");
|
|
11017
11071
|
if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
@@ -11092,11 +11146,7 @@ var qaReopen = new Command("reopen").description("Reopen QA and clear scheduled
|
|
|
11092
11146
|
resolved.folder,
|
|
11093
11147
|
resolved.experimentId
|
|
11094
11148
|
);
|
|
11095
|
-
output(
|
|
11096
|
-
this,
|
|
11097
|
-
{ experimentId: resolved.experimentId, qaStatus: "open" },
|
|
11098
|
-
() => spinner.succeed(`QA reopened for ${resolved.label}`)
|
|
11099
|
-
);
|
|
11149
|
+
output(this, res.data, () => spinner.succeed(`QA reopened for ${resolved.label}`));
|
|
11100
11150
|
} catch (error) {
|
|
11101
11151
|
spinner.fail("Failed to reopen QA");
|
|
11102
11152
|
throw error;
|
|
@@ -11203,7 +11253,7 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
|
|
|
11203
11253
|
const data = opts.summary ? qaSummary(
|
|
11204
11254
|
run3,
|
|
11205
11255
|
run3.evidenceRecords.filter((record2) => !previousIds.has(record2.id))
|
|
11206
|
-
) :
|
|
11256
|
+
) : response.data;
|
|
11207
11257
|
output(this, data, () => {
|
|
11208
11258
|
if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
11209
11259
|
`);
|
|
@@ -12057,7 +12107,10 @@ function cleanupResetJs(experiment) {
|
|
|
12057
12107
|
function runAbHarness(args, cwd) {
|
|
12058
12108
|
const bin = process.env.AB_HARNESS_BIN || "ab-harness";
|
|
12059
12109
|
return new Promise((resolve2, reject) => {
|
|
12060
|
-
const child = (0, import_node_child_process6.spawn)(bin, args, {
|
|
12110
|
+
const child = (0, import_node_child_process6.spawn)(bin, args, {
|
|
12111
|
+
cwd,
|
|
12112
|
+
stdio: isJsonMode() ? ["inherit", 2, "inherit"] : "inherit"
|
|
12113
|
+
});
|
|
12061
12114
|
child.on("error", reject);
|
|
12062
12115
|
child.on("exit", (code) => {
|
|
12063
12116
|
if (code === 0) resolve2();
|
|
@@ -13066,29 +13119,36 @@ function printManifestPlan(manifest) {
|
|
|
13066
13119
|
var trackingCommand = new Command("tracking").description(
|
|
13067
13120
|
"Create Apex-first tracking ingestion manifests and QA handoffs"
|
|
13068
13121
|
);
|
|
13069
|
-
trackingCommand.command("inspect").description("Create a local tracking manifest from funnel URLs").argument("<urls...>", "Landing, storefront, checkout, or confirmation URLs").option("--shop-id <id>", "Apex shop ID for snippet generation").option("--endpoint <url>", "Apex Worker endpoint", "https://events.drip-apex.com").option("--out <path>", "Manifest output path", defaultManifestPath()).action((urls, opts)
|
|
13122
|
+
trackingCommand.command("inspect").description("Create a local tracking manifest from funnel URLs").argument("<urls...>", "Landing, storefront, checkout, or confirmation URLs").option("--shop-id <id>", "Apex shop ID for snippet generation").option("--endpoint <url>", "Apex Worker endpoint", "https://events.drip-apex.com").option("--out <path>", "Manifest output path", defaultManifestPath()).action(function(urls, opts) {
|
|
13070
13123
|
const manifest = buildManifest(urls, opts);
|
|
13071
13124
|
const outPath = manifestPath(opts.out);
|
|
13072
13125
|
writeManifest(outPath, manifest);
|
|
13073
|
-
|
|
13074
|
-
if (manifest.recommendedCookieDomain) {
|
|
13075
|
-
console.log(`Recommended cookie domain: ${source_default.cyan(manifest.recommendedCookieDomain)}`);
|
|
13076
|
-
}
|
|
13077
|
-
if (manifest.snippet.html) {
|
|
13078
|
-
console.log(`Head snippet: ${source_default.cyan(manifest.snippet.html)}`);
|
|
13079
|
-
} else {
|
|
13126
|
+
if (!manifest.snippet.html)
|
|
13080
13127
|
printWarning("No shop ID provided, so the manifest has no install snippet.");
|
|
13081
|
-
}
|
|
13128
|
+
output(this, { path: outPath, manifest }, () => {
|
|
13129
|
+
printSuccess(`Wrote ${outPath}`);
|
|
13130
|
+
if (manifest.recommendedCookieDomain) {
|
|
13131
|
+
console.log(`Recommended cookie domain: ${source_default.cyan(manifest.recommendedCookieDomain)}`);
|
|
13132
|
+
}
|
|
13133
|
+
if (manifest.snippet.html) {
|
|
13134
|
+
console.log(`Head snippet: ${source_default.cyan(manifest.snippet.html)}`);
|
|
13135
|
+
}
|
|
13136
|
+
});
|
|
13082
13137
|
});
|
|
13083
|
-
trackingCommand.command("plan").description("Print the implementation plan from a tracking manifest").argument("[manifest]", "Tracking manifest path", defaultManifestPath()).action((input)
|
|
13084
|
-
|
|
13138
|
+
trackingCommand.command("plan").description("Print the implementation plan from a tracking manifest").argument("[manifest]", "Tracking manifest path", defaultManifestPath()).action(function(input) {
|
|
13139
|
+
const filePath = manifestPath(input);
|
|
13140
|
+
const manifest = readManifest(filePath);
|
|
13141
|
+
output(this, { path: filePath, manifest }, () => printManifestPlan(manifest));
|
|
13085
13142
|
});
|
|
13086
|
-
trackingCommand.command("qa").description("Print the QA checklist for a tracking manifest").argument("[manifest]", "Tracking manifest path", defaultManifestPath()).action((input)
|
|
13087
|
-
const
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
console.log(
|
|
13143
|
+
trackingCommand.command("qa").description("Print the QA checklist for a tracking manifest").argument("[manifest]", "Tracking manifest path", defaultManifestPath()).action(function(input) {
|
|
13144
|
+
const filePath = manifestPath(input);
|
|
13145
|
+
const manifest = readManifest(filePath);
|
|
13146
|
+
output(this, { path: filePath, checks: manifest.qa.checks }, () => {
|
|
13147
|
+
console.log(source_default.bold("Apex tracking QA"));
|
|
13148
|
+
console.log();
|
|
13149
|
+
manifest.qa.checks.forEach((check, index) => {
|
|
13150
|
+
console.log(`${index + 1}. ${check}`);
|
|
13151
|
+
});
|
|
13092
13152
|
});
|
|
13093
13153
|
});
|
|
13094
13154
|
trackingCommand.command("verify").description("Verify live tracking events for the current shop").option("--minutes <n>", "Lookback window in minutes", "120").option("--limit <n>", "Recent events to include", "20").option("--experiment <id>", "Filter to one experiment ID").option("--event-type <type>", "Filter to one event type; repeat with comma-separated values").action(async function action33(opts) {
|
|
@@ -13107,7 +13167,7 @@ trackingCommand.command("verify").description("Verify live tracking events for t
|
|
|
13107
13167
|
res.errorDetails
|
|
13108
13168
|
);
|
|
13109
13169
|
}
|
|
13110
|
-
output(this,
|
|
13170
|
+
output(this, res.data, () => {
|
|
13111
13171
|
const totals = res.data.totals;
|
|
13112
13172
|
console.log(source_default.bold("Apex tracking verification"));
|
|
13113
13173
|
console.log();
|
|
@@ -13412,7 +13472,7 @@ var list2 = new Command("list").alias("ls").description("List goals").option("-n
|
|
|
13412
13472
|
if (!res.ok || !res.data) {
|
|
13413
13473
|
fail(this, res.error ?? "Failed to list goals", classifyStatus(res.status), res.errorDetails);
|
|
13414
13474
|
}
|
|
13415
|
-
output(this,
|
|
13475
|
+
output(this, res.data, () => {
|
|
13416
13476
|
if (res.data.data.length === 0) {
|
|
13417
13477
|
console.log(source_default.dim("No goals found."));
|
|
13418
13478
|
return;
|
|
@@ -13454,7 +13514,7 @@ var create2 = new Command("create").description("Create a goal").requiredOption(
|
|
|
13454
13514
|
res.errorDetails
|
|
13455
13515
|
);
|
|
13456
13516
|
}
|
|
13457
|
-
output(this,
|
|
13517
|
+
output(this, res.data, () => printGoal(res.data));
|
|
13458
13518
|
});
|
|
13459
13519
|
var update2 = new Command("update").description("Update a goal").argument("<id>", "Goal ID").option("--name <name>", "Goal name").option("--type <type>", "click|pageview|custom|revenue").option("--measurement <measurement>", "conversion|value").option(
|
|
13460
13520
|
"--value-property <property>",
|
|
@@ -13486,7 +13546,7 @@ var update2 = new Command("update").description("Update a goal").argument("<id>"
|
|
|
13486
13546
|
res.errorDetails
|
|
13487
13547
|
);
|
|
13488
13548
|
}
|
|
13489
|
-
output(this,
|
|
13549
|
+
output(this, res.data, () => printGoal(res.data));
|
|
13490
13550
|
});
|
|
13491
13551
|
var test = new Command("test").description("Dry run a goal URL pattern against traffic").option(
|
|
13492
13552
|
"--pattern <regex>",
|
|
@@ -13583,9 +13643,8 @@ var suggest = new Command("suggest").description("Suggest goals and funnels from
|
|
|
13583
13643
|
classifyStatus(res.status),
|
|
13584
13644
|
res.errorDetails
|
|
13585
13645
|
);
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
for (const goal of res.data.goals) {
|
|
13646
|
+
if (!shouldUseJson(this)) printSuggestions(res.data);
|
|
13647
|
+
for (const goal of opts.create ? res.data.goals : []) {
|
|
13589
13648
|
console.error(`Would create ${goal.type} goal: ${goal.name}`);
|
|
13590
13649
|
if (!opts.yes) {
|
|
13591
13650
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
@@ -13620,6 +13679,7 @@ var suggest = new Command("suggest").description("Suggest goals and funnels from
|
|
|
13620
13679
|
created.errorDetails
|
|
13621
13680
|
);
|
|
13622
13681
|
}
|
|
13682
|
+
output(this, res.data, () => void 0);
|
|
13623
13683
|
});
|
|
13624
13684
|
var goalsCommand = new Command("goals").description("Manage conversion and revenue goals").addCommand(list2).addCommand(create2).addCommand(update2).addCommand(test).addCommand(suggest).addHelpText(
|
|
13625
13685
|
"after",
|
|
@@ -13674,7 +13734,7 @@ var list3 = new Command("list").alias("ls").description("List funnels").action(a
|
|
|
13674
13734
|
);
|
|
13675
13735
|
output(
|
|
13676
13736
|
this,
|
|
13677
|
-
|
|
13737
|
+
res.data,
|
|
13678
13738
|
() => printTable(
|
|
13679
13739
|
["NAME", "ID", "STEPS"],
|
|
13680
13740
|
res.data.data.map((f) => [f.name.slice(0, 36), f.id, String(f.steps.length)]),
|
|
@@ -13694,7 +13754,7 @@ var create3 = new Command("create").description("Create a funnel").requiredOptio
|
|
|
13694
13754
|
classifyStatus(res.status),
|
|
13695
13755
|
res.errorDetails
|
|
13696
13756
|
);
|
|
13697
|
-
output(this,
|
|
13757
|
+
output(this, res.data, () => printFunnel(res.data));
|
|
13698
13758
|
});
|
|
13699
13759
|
var update3 = new Command("update").description("Replace a funnel's steps").argument("<id>", "Funnel ID").requiredOption("--steps-file <file>", "JSON array of { label, pattern }").action(async function action41(id, opts) {
|
|
13700
13760
|
const res = await updateFunnel(requireCredentials(), id, {
|
|
@@ -13707,7 +13767,7 @@ var update3 = new Command("update").description("Replace a funnel's steps").argu
|
|
|
13707
13767
|
classifyStatus(res.status),
|
|
13708
13768
|
res.errorDetails
|
|
13709
13769
|
);
|
|
13710
|
-
output(this,
|
|
13770
|
+
output(this, res.data, () => printFunnel(res.data));
|
|
13711
13771
|
});
|
|
13712
13772
|
var del2 = new Command("delete").description("Delete a funnel").argument("<id>", "Funnel ID").action(async function action42(id) {
|
|
13713
13773
|
const res = await deleteFunnel(requireCredentials(), id);
|
|
@@ -13718,7 +13778,7 @@ var del2 = new Command("delete").description("Delete a funnel").argument("<id>",
|
|
|
13718
13778
|
classifyStatus(res.status),
|
|
13719
13779
|
res.errorDetails
|
|
13720
13780
|
);
|
|
13721
|
-
output(this,
|
|
13781
|
+
output(this, res.data ?? null, () => console.log(`Deleted funnel ${id}`));
|
|
13722
13782
|
});
|
|
13723
13783
|
var run = new Command("run").description("Run funnel analysis").argument("<id>", "Funnel ID").option("--days <n>", "Analysis window", "30").action(async function action43(id, opts) {
|
|
13724
13784
|
if (!/^[1-9]\d*$/.test(opts.days))
|
|
@@ -13731,7 +13791,7 @@ var run = new Command("run").description("Run funnel analysis").argument("<id>",
|
|
|
13731
13791
|
fail(this, res.error ?? "Failed to run funnel", classifyStatus(res.status), res.errorDetails);
|
|
13732
13792
|
output(
|
|
13733
13793
|
this,
|
|
13734
|
-
|
|
13794
|
+
res.data,
|
|
13735
13795
|
() => printTable(
|
|
13736
13796
|
["STEP", "LABEL", "VISITORS", "DROP-OFF"],
|
|
13737
13797
|
res.data.steps.map((s) => [
|
|
@@ -13806,8 +13866,8 @@ async function load2(command) {
|
|
|
13806
13866
|
return result.data;
|
|
13807
13867
|
}
|
|
13808
13868
|
var list4 = new Command("list").alias("ls").description("List runtime environments and mapped domains").action(async function action45() {
|
|
13809
|
-
const
|
|
13810
|
-
output(this,
|
|
13869
|
+
const body5 = await load2(this);
|
|
13870
|
+
output(this, body5, () => printEnvironments(body5.environments.items));
|
|
13811
13871
|
});
|
|
13812
13872
|
var setDomain = new Command("set-domain").description("Map a domain to a runtime environment").argument("<domain>", "Domain to map").requiredOption("--environment <env>", "Environment key").action(async function action46(domain, opts) {
|
|
13813
13873
|
const normalizedDomain = normalizeEnvironmentDomain(domain);
|
|
@@ -13850,11 +13910,7 @@ var setDomain = new Command("set-domain").description("Map a domain to a runtime
|
|
|
13850
13910
|
);
|
|
13851
13911
|
}
|
|
13852
13912
|
warnOnPrecompileError(this, result.data.precompile);
|
|
13853
|
-
output(
|
|
13854
|
-
this,
|
|
13855
|
-
{ environments: result.data.environments, precompile: result.data.precompile },
|
|
13856
|
-
() => printEnvironments(result.data.environments.items)
|
|
13857
|
-
);
|
|
13913
|
+
output(this, result.data, () => printEnvironments(result.data.environments.items));
|
|
13858
13914
|
});
|
|
13859
13915
|
var removeDomain = new Command("remove-domain").description("Remove a domain mapping").argument("<domain>", "Domain to remove").action(async function action47(domain) {
|
|
13860
13916
|
const normalizedDomain = normalizeEnvironmentDomain(domain);
|
|
@@ -13885,11 +13941,7 @@ var removeDomain = new Command("remove-domain").description("Remove a domain map
|
|
|
13885
13941
|
);
|
|
13886
13942
|
}
|
|
13887
13943
|
warnOnPrecompileError(this, result.data.precompile);
|
|
13888
|
-
output(
|
|
13889
|
-
this,
|
|
13890
|
-
{ environments: result.data.environments, precompile: result.data.precompile },
|
|
13891
|
-
() => printEnvironments(result.data.environments.items)
|
|
13892
|
-
);
|
|
13944
|
+
output(this, result.data, () => printEnvironments(result.data.environments.items));
|
|
13893
13945
|
});
|
|
13894
13946
|
var environmentsCommand = new Command("environments").description("Manage runtime environments and domain mappings").addCommand(list4).addCommand(setDomain).addCommand(removeDomain);
|
|
13895
13947
|
|
|
@@ -15766,7 +15818,9 @@ async function runDoctor(options = {}) {
|
|
|
15766
15818
|
null,
|
|
15767
15819
|
res.error ?? `Failed to load ${label}`,
|
|
15768
15820
|
classifyStatus(res.status),
|
|
15769
|
-
res.errorDetails
|
|
15821
|
+
res.errorDetails,
|
|
15822
|
+
// Parallel reads: name this response's envelope explicitly.
|
|
15823
|
+
apiFailureOf(res)
|
|
15770
15824
|
);
|
|
15771
15825
|
}
|
|
15772
15826
|
for (const [label, res] of [
|
|
@@ -16127,7 +16181,7 @@ function analyticsSubcommand(name, description, defaultDays) {
|
|
|
16127
16181
|
classifyStatus(result.status),
|
|
16128
16182
|
result.errorDetails
|
|
16129
16183
|
);
|
|
16130
|
-
output(this,
|
|
16184
|
+
output(this, result.data, () => printAnalytics(result.data.data));
|
|
16131
16185
|
});
|
|
16132
16186
|
}
|
|
16133
16187
|
function urlsCommand() {
|
|
@@ -16964,7 +17018,7 @@ var list6 = new Command("list").alias("ls").description("List flags").option("-n
|
|
|
16964
17018
|
fail(this, r.error ?? "Failed to list", classifyStatus(r.status), r.errorDetails);
|
|
16965
17019
|
output(
|
|
16966
17020
|
this,
|
|
16967
|
-
|
|
17021
|
+
r.data,
|
|
16968
17022
|
() => printTable(
|
|
16969
17023
|
["KEY/NAME", "STATE", "UPDATED"],
|
|
16970
17024
|
r.data.data.map((x) => [x.key || x.name, x.status, x.updated_at ?? "-"]),
|
|
@@ -16976,7 +17030,7 @@ var get5 = new Command("get").description("Get a flag").argument("<id>").action(
|
|
|
16976
17030
|
const r = await getManagedDelivery(requireCredentials(), kind, id);
|
|
16977
17031
|
if (!r.ok || !r.data)
|
|
16978
17032
|
fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
|
|
16979
|
-
output(this,
|
|
17033
|
+
output(this, r.data, () => detail(r.data));
|
|
16980
17034
|
});
|
|
16981
17035
|
var status = new Command("status").description("Check whether a flag publication is live at the edge").argument("<id>").action(async function(id) {
|
|
16982
17036
|
const r = await getFlagStatus(requireCredentials(), id);
|
|
@@ -17037,7 +17091,7 @@ function write(name) {
|
|
|
17037
17091
|
const r = name === "create" ? await createManagedDelivery(requireCredentials(), kind, b) : await updateManagedDelivery(requireCredentials(), kind, id, b);
|
|
17038
17092
|
if (!r.ok || !r.data)
|
|
17039
17093
|
fail(this, r.error ?? `Failed to ${name}`, classifyStatus(r.status), r.errorDetails);
|
|
17040
|
-
output(this,
|
|
17094
|
+
output(this, r.data, () => detail(r.data));
|
|
17041
17095
|
});
|
|
17042
17096
|
return c;
|
|
17043
17097
|
}
|
|
@@ -17046,7 +17100,7 @@ function lifecycle(action74) {
|
|
|
17046
17100
|
const r = await transitionManagedDelivery(requireCredentials(), kind, id, action74);
|
|
17047
17101
|
if (!r.ok || !r.data)
|
|
17048
17102
|
fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
|
|
17049
|
-
output(this,
|
|
17103
|
+
output(this, r.data, () => detail(r.data));
|
|
17050
17104
|
});
|
|
17051
17105
|
}
|
|
17052
17106
|
var flagsCommand = new Command("flags").description("Manage flags").addCommand(list6).addCommand(get5).addCommand(status).addCommand(write("create")).addCommand(write("update")).addCommand(lifecycle("activate")).addCommand(lifecycle("pause")).addCommand(lifecycle("archive")).addHelpText(
|
|
@@ -17163,7 +17217,7 @@ var list7 = new Command("list").alias("ls").description("List backlog items iden
|
|
|
17163
17217
|
fail(this, r.error ?? "Failed to list", classifyStatus(r.status), r.errorDetails);
|
|
17164
17218
|
output(
|
|
17165
17219
|
this,
|
|
17166
|
-
|
|
17220
|
+
r.data,
|
|
17167
17221
|
() => printTable(
|
|
17168
17222
|
["ID", "NAME", "STATE"],
|
|
17169
17223
|
r.data.data.map((x) => [
|
|
@@ -17180,7 +17234,7 @@ var get6 = new Command("get").description("Get a backlog item identified as apex
|
|
|
17180
17234
|
if (!r.ok || !r.data)
|
|
17181
17235
|
fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
|
|
17182
17236
|
const labels = await fieldLabels(this, r.data);
|
|
17183
|
-
output(this,
|
|
17237
|
+
output(this, r.data, () => detail2(r.data, labels));
|
|
17184
17238
|
});
|
|
17185
17239
|
function write2(action74) {
|
|
17186
17240
|
const c = new Command(action74).description(
|
|
@@ -17207,7 +17261,7 @@ function write2(action74) {
|
|
|
17207
17261
|
if (!r.ok || !r.data)
|
|
17208
17262
|
fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
|
|
17209
17263
|
const labels = await fieldLabels(this, r.data);
|
|
17210
|
-
output(this,
|
|
17264
|
+
output(this, r.data, () => detail2(r.data, labels));
|
|
17211
17265
|
});
|
|
17212
17266
|
return c;
|
|
17213
17267
|
}
|
|
@@ -17220,13 +17274,13 @@ var archive = new Command("archive").description("Archive a backlog item with a
|
|
|
17220
17274
|
const r = await archiveBacklogInsight(requireCredentials(), id, opts.reason.trim());
|
|
17221
17275
|
if (!r.ok || !r.data)
|
|
17222
17276
|
fail(this, r.error ?? "Failed to archive", classifyStatus(r.status), r.errorDetails);
|
|
17223
|
-
output(this,
|
|
17277
|
+
output(this, r.data, () => console.log(`Archived ${id}`));
|
|
17224
17278
|
});
|
|
17225
17279
|
var restore = new Command("restore").description("Restore an archived backlog item").argument("<id>", "backlog item ID").action(async function(id) {
|
|
17226
17280
|
const r = await restoreBacklogInsight(requireCredentials(), id);
|
|
17227
17281
|
if (!r.ok || !r.data)
|
|
17228
17282
|
fail(this, r.error ?? "Failed to restore", classifyStatus(r.status), r.errorDetails);
|
|
17229
|
-
output(this,
|
|
17283
|
+
output(this, r.data, () => console.log(`Restored ${id}`));
|
|
17230
17284
|
});
|
|
17231
17285
|
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) {
|
|
17232
17286
|
if (!opts.force) {
|
|
@@ -17243,14 +17297,14 @@ var remove2 = new Command("delete").description("Delete a backlog item").argumen
|
|
|
17243
17297
|
);
|
|
17244
17298
|
rl.close();
|
|
17245
17299
|
if (answer.toLowerCase() !== "y") {
|
|
17246
|
-
console.log("Aborted.");
|
|
17300
|
+
output(this, { aborted: true }, () => console.log("Aborted."));
|
|
17247
17301
|
return;
|
|
17248
17302
|
}
|
|
17249
17303
|
}
|
|
17250
17304
|
const r = await deleteInsight(requireCredentials(), kind2, id);
|
|
17251
17305
|
if (!r.ok || !r.data)
|
|
17252
17306
|
fail(this, r.error ?? "Failed to delete", classifyStatus(r.status), r.errorDetails);
|
|
17253
|
-
output(this,
|
|
17307
|
+
output(this, r.data, () => console.log(`Deleted ${id}`));
|
|
17254
17308
|
});
|
|
17255
17309
|
function fieldDetail(field) {
|
|
17256
17310
|
console.log();
|
|
@@ -17277,7 +17331,7 @@ var fieldsList = new Command("list").alias("ls").description("List the shop's id
|
|
|
17277
17331
|
fail(this, r.error ?? "Failed to list fields", classifyStatus(r.status), r.errorDetails);
|
|
17278
17332
|
output(
|
|
17279
17333
|
this,
|
|
17280
|
-
|
|
17334
|
+
r.data,
|
|
17281
17335
|
() => printTable(
|
|
17282
17336
|
["KEY", "LABEL", "TYPE", "REQUIRED", "CLIENT"],
|
|
17283
17337
|
r.data.data.map((field) => [
|
|
@@ -17312,7 +17366,7 @@ var fieldsCreate = new Command("create").description("Add an idea field").requir
|
|
|
17312
17366
|
});
|
|
17313
17367
|
if (!r.ok || !r.data)
|
|
17314
17368
|
fail(this, r.error ?? "Failed to create field", classifyStatus(r.status), r.errorDetails);
|
|
17315
|
-
output(this,
|
|
17369
|
+
output(this, r.data, () => fieldDetail(r.data));
|
|
17316
17370
|
});
|
|
17317
17371
|
var fieldsUpdate = new Command("update").description("Update an idea field").argument("<id>", "field ID").option("--label <text>", "What the field is called").option(
|
|
17318
17372
|
"--option <value>",
|
|
@@ -17335,13 +17389,13 @@ var fieldsUpdate = new Command("update").description("Update an idea field").arg
|
|
|
17335
17389
|
const r = await updateBacklogField(requireCredentials(), id, body5);
|
|
17336
17390
|
if (!r.ok || !r.data)
|
|
17337
17391
|
fail(this, r.error ?? "Failed to update field", classifyStatus(r.status), r.errorDetails);
|
|
17338
|
-
output(this,
|
|
17392
|
+
output(this, r.data, () => fieldDetail(r.data));
|
|
17339
17393
|
});
|
|
17340
17394
|
var fieldsArchive = new Command("archive").description("Archive an idea field; values already on ideas are kept").argument("<id>", "field ID").action(async function(id) {
|
|
17341
17395
|
const r = await archiveBacklogField(requireCredentials(), id);
|
|
17342
17396
|
if (!r.ok || !r.data)
|
|
17343
17397
|
fail(this, r.error ?? "Failed to archive field", classifyStatus(r.status), r.errorDetails);
|
|
17344
|
-
output(this,
|
|
17398
|
+
output(this, r.data, () => fieldDetail(r.data));
|
|
17345
17399
|
});
|
|
17346
17400
|
var fields2 = new Command("fields").description("Manage the shop's own idea fields").addCommand(fieldsList).addCommand(fieldsCreate).addCommand(fieldsUpdate).addCommand(fieldsArchive);
|
|
17347
17401
|
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);
|
|
@@ -17388,7 +17442,7 @@ var list8 = new Command("list").alias("ls").description("List signals").option("
|
|
|
17388
17442
|
fail(this, r.error ?? "Failed to list", classifyStatus(r.status), r.errorDetails);
|
|
17389
17443
|
output(
|
|
17390
17444
|
this,
|
|
17391
|
-
|
|
17445
|
+
r.data,
|
|
17392
17446
|
() => printTable(
|
|
17393
17447
|
["ID", "NAME", "STATE"],
|
|
17394
17448
|
r.data.data.map((x) => [
|
|
@@ -17404,7 +17458,7 @@ var get7 = new Command("get").description("Get a signal").argument("<id>", "sign
|
|
|
17404
17458
|
const r = await getInsight(requireCredentials(), kind3, id);
|
|
17405
17459
|
if (!r.ok || !r.data)
|
|
17406
17460
|
fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
|
|
17407
|
-
output(this,
|
|
17461
|
+
output(this, r.data, () => detail3(r.data));
|
|
17408
17462
|
});
|
|
17409
17463
|
function write3(action74) {
|
|
17410
17464
|
const c = new Command(action74).description(
|
|
@@ -17424,7 +17478,7 @@ function write3(action74) {
|
|
|
17424
17478
|
const r = action74 === "create" ? await createInsight(requireCredentials(), kind3, body5) : await updateInsight(requireCredentials(), kind3, id, body5);
|
|
17425
17479
|
if (!r.ok || !r.data)
|
|
17426
17480
|
fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
|
|
17427
|
-
output(this,
|
|
17481
|
+
output(this, r.data, () => detail3(r.data));
|
|
17428
17482
|
});
|
|
17429
17483
|
return c;
|
|
17430
17484
|
}
|
|
@@ -17432,7 +17486,7 @@ var remove3 = new Command("delete").alias("archive").description("Delete or arch
|
|
|
17432
17486
|
const r = await deleteInsight(requireCredentials(), kind3, id);
|
|
17433
17487
|
if (!r.ok || !r.data)
|
|
17434
17488
|
fail(this, r.error ?? "Failed to delete", classifyStatus(r.status), r.errorDetails);
|
|
17435
|
-
output(this,
|
|
17489
|
+
output(this, r.data, () => console.log(`Deleted ${id}`));
|
|
17436
17490
|
});
|
|
17437
17491
|
var signalsCommand = new Command("signals").description("Manage signals").addCommand(list8).addCommand(get7).addCommand(write3("create")).addCommand(write3("update")).addCommand(remove3);
|
|
17438
17492
|
|
|
@@ -17481,7 +17535,7 @@ var list9 = new Command("list").alias("ls").description("List personalizations")
|
|
|
17481
17535
|
fail(this, r.error ?? "Failed to list", classifyStatus(r.status), r.errorDetails);
|
|
17482
17536
|
output(
|
|
17483
17537
|
this,
|
|
17484
|
-
|
|
17538
|
+
r.data,
|
|
17485
17539
|
() => printTable(
|
|
17486
17540
|
["KEY/NAME", "STATE", "UPDATED"],
|
|
17487
17541
|
r.data.data.map((x) => [x.key || x.name, x.status, x.updated_at ?? "-"]),
|
|
@@ -17493,7 +17547,7 @@ var get8 = new Command("get").description("Get a personalization").argument("<id
|
|
|
17493
17547
|
const r = await getManagedDelivery(requireCredentials(), kind4, id);
|
|
17494
17548
|
if (!r.ok || !r.data)
|
|
17495
17549
|
fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
|
|
17496
|
-
output(this,
|
|
17550
|
+
output(this, r.data, () => detail4(r.data));
|
|
17497
17551
|
});
|
|
17498
17552
|
function write4(name) {
|
|
17499
17553
|
const c = new Command(name).description(
|
|
@@ -17511,7 +17565,7 @@ function write4(name) {
|
|
|
17511
17565
|
const r = name === "create" ? await createManagedDelivery(requireCredentials(), kind4, b) : await updateManagedDelivery(requireCredentials(), kind4, id, b);
|
|
17512
17566
|
if (!r.ok || !r.data)
|
|
17513
17567
|
fail(this, r.error ?? `Failed to ${name}`, classifyStatus(r.status), r.errorDetails);
|
|
17514
|
-
output(this,
|
|
17568
|
+
output(this, r.data, () => detail4(r.data));
|
|
17515
17569
|
});
|
|
17516
17570
|
return c;
|
|
17517
17571
|
}
|
|
@@ -17520,7 +17574,7 @@ function lifecycle2(action74) {
|
|
|
17520
17574
|
const r = await transitionManagedDelivery(requireCredentials(), kind4, id, action74);
|
|
17521
17575
|
if (!r.ok || !r.data)
|
|
17522
17576
|
fail(this, r.error ?? `Failed to ${action74}`, classifyStatus(r.status), r.errorDetails);
|
|
17523
|
-
output(this,
|
|
17577
|
+
output(this, r.data, () => detail4(r.data));
|
|
17524
17578
|
});
|
|
17525
17579
|
}
|
|
17526
17580
|
var personalizationsCommand = new Command("personalizations").description("Manage personalizations").addCommand(list9).addCommand(get8).addCommand(write4("create")).addCommand(write4("update")).addCommand(lifecycle2("activate")).addCommand(lifecycle2("pause")).addCommand(lifecycle2("archive"));
|
|
@@ -17570,7 +17624,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17570
17624
|
const page = response.data;
|
|
17571
17625
|
output(
|
|
17572
17626
|
this,
|
|
17573
|
-
|
|
17627
|
+
page,
|
|
17574
17628
|
() => printTable(
|
|
17575
17629
|
["NAME", "ID", "UPDATED"],
|
|
17576
17630
|
page.data.map((item) => [
|
|
@@ -17596,7 +17650,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17596
17650
|
);
|
|
17597
17651
|
output(
|
|
17598
17652
|
this,
|
|
17599
|
-
|
|
17653
|
+
response.data,
|
|
17600
17654
|
() => console.log(source_default.bold(response.data.name ?? id))
|
|
17601
17655
|
);
|
|
17602
17656
|
})
|
|
@@ -17627,13 +17681,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17627
17681
|
classifyStatus(response.status),
|
|
17628
17682
|
response.errorDetails
|
|
17629
17683
|
);
|
|
17630
|
-
output(
|
|
17631
|
-
this,
|
|
17632
|
-
{
|
|
17633
|
-
[kind5 === "exclusion-groups" ? "exclusion_group" : singular3]: response.data
|
|
17634
|
-
},
|
|
17635
|
-
() => console.log(source_default.green(`${action74}d ${singular3}`))
|
|
17636
|
-
);
|
|
17684
|
+
output(this, response.data, () => console.log(source_default.green(`${action74}d ${singular3}`)));
|
|
17637
17685
|
});
|
|
17638
17686
|
root.addCommand(command);
|
|
17639
17687
|
}
|
|
@@ -17658,7 +17706,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17658
17706
|
});
|
|
17659
17707
|
rl.close();
|
|
17660
17708
|
if (answer.toLowerCase() !== "y") {
|
|
17661
|
-
console.log("Aborted.");
|
|
17709
|
+
output(this, { aborted: true }, () => console.log("Aborted."));
|
|
17662
17710
|
return;
|
|
17663
17711
|
}
|
|
17664
17712
|
}
|
|
@@ -17670,7 +17718,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17670
17718
|
classifyStatus(response.status),
|
|
17671
17719
|
response.errorDetails
|
|
17672
17720
|
);
|
|
17673
|
-
output(this,
|
|
17721
|
+
output(this, response.data ?? null, () => console.log(source_default.green(`Deleted ${singular3}`)));
|
|
17674
17722
|
})
|
|
17675
17723
|
);
|
|
17676
17724
|
if (kind5 === "exclusion-groups")
|
|
@@ -17690,11 +17738,7 @@ function targetingResourceCommand(kind5) {
|
|
|
17690
17738
|
classifyStatus(response.status),
|
|
17691
17739
|
response.errorDetails
|
|
17692
17740
|
);
|
|
17693
|
-
output(
|
|
17694
|
-
this,
|
|
17695
|
-
{ exclusion_group: response.data },
|
|
17696
|
-
() => console.log(source_default.green("Replaced members"))
|
|
17697
|
-
);
|
|
17741
|
+
output(this, response.data, () => console.log(source_default.green("Replaced members")));
|
|
17698
17742
|
})
|
|
17699
17743
|
);
|
|
17700
17744
|
return root;
|
|
@@ -17710,7 +17754,9 @@ var brandCommand = new Command("brand").description("Print the shop brand contex
|
|
|
17710
17754
|
const creds = requireCredentials();
|
|
17711
17755
|
const res = await getBrandContext(creds);
|
|
17712
17756
|
if (res.status === 404) {
|
|
17713
|
-
|
|
17757
|
+
if (shouldUseJson(this)) process.stderr.write(`${NO_PROFILE_MESSAGE}
|
|
17758
|
+
`);
|
|
17759
|
+
output(this, { data: null }, () => {
|
|
17714
17760
|
console.log(source_default.dim(NO_PROFILE_MESSAGE));
|
|
17715
17761
|
});
|
|
17716
17762
|
return;
|
|
@@ -17724,7 +17770,7 @@ var brandCommand = new Command("brand").description("Print the shop brand contex
|
|
|
17724
17770
|
);
|
|
17725
17771
|
}
|
|
17726
17772
|
const brandContext = res.data.data;
|
|
17727
|
-
output(this,
|
|
17773
|
+
output(this, res.data, () => {
|
|
17728
17774
|
if (!brandContext.hasProfile) {
|
|
17729
17775
|
console.log(source_default.dim(NO_PROFILE_MESSAGE));
|
|
17730
17776
|
return;
|
|
@@ -17750,7 +17796,7 @@ var test2 = new Command("test").description("Evaluate experiment URL targeting f
|
|
|
17750
17796
|
res.errorDetails
|
|
17751
17797
|
);
|
|
17752
17798
|
}
|
|
17753
|
-
output(this,
|
|
17799
|
+
output(this, res.data, () => {
|
|
17754
17800
|
console.log(source_default.bold("Apex targeting test"));
|
|
17755
17801
|
console.log();
|
|
17756
17802
|
console.log(`URL: ${source_default.cyan(res.data.url)}`);
|
|
@@ -17824,7 +17870,7 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
|
|
|
17824
17870
|
"--type <type>",
|
|
17825
17871
|
"full_handover|design|development|qa|config|bug|help (internal types require a DRIP account)",
|
|
17826
17872
|
"qa"
|
|
17827
|
-
).option("--loom <url>", "Loom URL with reproduction or QA context").option("--experiment <id>", "Experiment ID").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) {
|
|
17873
|
+
).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) {
|
|
17828
17874
|
if (!SOS_TYPES.has(opts.type)) {
|
|
17829
17875
|
fail(
|
|
17830
17876
|
this,
|
|
@@ -17832,12 +17878,16 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
|
|
|
17832
17878
|
3
|
|
17833
17879
|
);
|
|
17834
17880
|
}
|
|
17881
|
+
if (opts.experiment && opts.backlogItem) {
|
|
17882
|
+
fail(this, "Provide only one of --experiment or --backlog-item", 3);
|
|
17883
|
+
}
|
|
17835
17884
|
const creds = requireCredentials();
|
|
17836
17885
|
const description = readText(opts.description) ?? descriptionArg ?? `Apex CLI ${opts.type} SOS request${opts.loom ? ` with Loom evidence: ${opts.loom}` : "."}`;
|
|
17837
17886
|
const res = await createSos(creds, {
|
|
17838
17887
|
type: opts.type,
|
|
17839
17888
|
description,
|
|
17840
17889
|
experiment_id: opts.experiment ?? null,
|
|
17890
|
+
backlog_item_id: opts.backlogItem ?? null,
|
|
17841
17891
|
sub_class: opts.subClass ?? null,
|
|
17842
17892
|
loom_url: opts.loom ?? null
|
|
17843
17893
|
});
|
|
@@ -17862,7 +17912,7 @@ var mine = new Command("mine").description("List SOS tickets created by this API
|
|
|
17862
17912
|
res.errorDetails
|
|
17863
17913
|
);
|
|
17864
17914
|
}
|
|
17865
|
-
output(this,
|
|
17915
|
+
output(this, res.data, () => {
|
|
17866
17916
|
if (res.data.tickets.length === 0) {
|
|
17867
17917
|
console.log(source_default.dim("No SOS tickets found for this API key."));
|
|
17868
17918
|
return;
|
|
@@ -17963,7 +18013,7 @@ var list12 = new Command("list").alias("ls").description("List organization shop
|
|
|
17963
18013
|
fail(this, r.error ?? "Failed to list shops", classifyStatus(r.status), r.errorDetails);
|
|
17964
18014
|
output(
|
|
17965
18015
|
this,
|
|
17966
|
-
|
|
18016
|
+
r.data,
|
|
17967
18017
|
() => printTable(
|
|
17968
18018
|
["ID", "NAME", "DOMAIN", "TIER"],
|
|
17969
18019
|
r.data.data.map((s) => [s.id, s.name ?? "-", s.domain ?? "-", s.tier ?? "-"]),
|
|
@@ -18741,7 +18791,7 @@ var discoverCommand = new Command("discover").description("Read back the trackin
|
|
|
18741
18791
|
created.errorDetails
|
|
18742
18792
|
);
|
|
18743
18793
|
}
|
|
18744
|
-
output(this,
|
|
18794
|
+
output(this, created.data, () => {
|
|
18745
18795
|
process.stdout.write(`Created dimension ${created.data.id}.
|
|
18746
18796
|
`);
|
|
18747
18797
|
process.stdout.write("Next: open Audience > Attributes in the Apex dashboard.\n");
|
|
@@ -18893,7 +18943,10 @@ async function confirmArchive(command, shopId, force) {
|
|
|
18893
18943
|
return true;
|
|
18894
18944
|
}
|
|
18895
18945
|
var archive2 = new Command("archive").description("Archive a shop and disable Apex delivery").argument("<shopId>", "Shop ID").option("-f, --force", "Skip confirmation").action(async function(shopId, opts) {
|
|
18896
|
-
if (!await confirmArchive(this, shopId, opts.force))
|
|
18946
|
+
if (!await confirmArchive(this, shopId, opts.force)) {
|
|
18947
|
+
output(this, { aborted: true }, () => void 0);
|
|
18948
|
+
return;
|
|
18949
|
+
}
|
|
18897
18950
|
const res = await archiveAdminShop(requireUserCredentials(), shopId);
|
|
18898
18951
|
if (!res.ok || !res.data)
|
|
18899
18952
|
fail(
|
|
@@ -19137,7 +19190,9 @@ var create6 = new Command("create").requiredOption("--name <name>").requiredOpti
|
|
|
19137
19190
|
classifyApiError(res.status, res.errorDetails),
|
|
19138
19191
|
res.errorDetails
|
|
19139
19192
|
);
|
|
19140
|
-
|
|
19193
|
+
if (shouldUseJson(this))
|
|
19194
|
+
process.stderr.write("Warning: this secret will not be shown again.\n");
|
|
19195
|
+
output(this, res.data, () => {
|
|
19141
19196
|
console.error("Warning: this secret will not be shown again.");
|
|
19142
19197
|
console.log(res.data.data.key);
|
|
19143
19198
|
});
|
|
@@ -19434,21 +19489,18 @@ var create7 = new Command("create").description(`Create a webhook. Prints the si
|
|
|
19434
19489
|
await createWebhook(requireCredentials(), { url: opts.url, events }),
|
|
19435
19490
|
"Failed to create webhook"
|
|
19436
19491
|
);
|
|
19437
|
-
|
|
19438
|
-
|
|
19439
|
-
|
|
19440
|
-
|
|
19441
|
-
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
}
|
|
19445
|
-
(
|
|
19446
|
-
|
|
19447
|
-
|
|
19448
|
-
|
|
19449
|
-
console.log(`Signature verification: ${SIGNATURE_DOC}`);
|
|
19450
|
-
}
|
|
19451
|
-
);
|
|
19492
|
+
if (shouldUseJson(this))
|
|
19493
|
+
process.stderr.write(
|
|
19494
|
+
`Store this signing secret now. Apex will not show it again.
|
|
19495
|
+
Signature verification: ${SIGNATURE_DOC}
|
|
19496
|
+
`
|
|
19497
|
+
);
|
|
19498
|
+
output(this, result, () => {
|
|
19499
|
+
console.log(source_default.green(`Created webhook ${result.id}`));
|
|
19500
|
+
console.log(source_default.yellow("Store this signing secret now. Apex will not show it again."));
|
|
19501
|
+
console.log(result.secret);
|
|
19502
|
+
console.log(`Signature verification: ${SIGNATURE_DOC}`);
|
|
19503
|
+
});
|
|
19452
19504
|
});
|
|
19453
19505
|
var remove4 = new Command("delete").alias("rm").description("Delete a webhook").argument("<id>", "Webhook id").action(async function action70(id) {
|
|
19454
19506
|
requireShopSecretKey(this);
|
|
@@ -19507,7 +19559,7 @@ var update5 = new Command("update").description("Update a webhook URL, events, o
|
|
|
19507
19559
|
await updateWebhook(requireCredentials(), id, body5),
|
|
19508
19560
|
"Failed to update webhook"
|
|
19509
19561
|
);
|
|
19510
|
-
output(this,
|
|
19562
|
+
output(this, result, () => printWebhooks([result]));
|
|
19511
19563
|
});
|
|
19512
19564
|
var test3 = new Command("test").description("Send a signed test event to a webhook").argument("<id>", "Webhook id").action(async function action73(id) {
|
|
19513
19565
|
requireShopSecretKey(this);
|
|
@@ -19702,10 +19754,7 @@ function readFamily(family, label) {
|
|
|
19702
19754
|
fail(this, r.error ?? `Failed to list ${label}`, classifyStatus(r.status), r.errorDetails);
|
|
19703
19755
|
output(
|
|
19704
19756
|
this,
|
|
19705
|
-
|
|
19706
|
-
[family.replace("-", "_")]: r.data.data,
|
|
19707
|
-
pagination: r.data.pagination
|
|
19708
|
-
},
|
|
19757
|
+
r.data,
|
|
19709
19758
|
() => printTable(
|
|
19710
19759
|
["ID", "NAME", "STATUS"],
|
|
19711
19760
|
r.data.data.map((x) => [x.id, x.name ?? "-", x.status ?? "-"]),
|
|
@@ -19717,11 +19766,7 @@ function readFamily(family, label) {
|
|
|
19717
19766
|
const r = await getConfigResource(requireCredentials(), family, id);
|
|
19718
19767
|
if (!r.ok || !r.data)
|
|
19719
19768
|
fail(this, r.error ?? "Failed to get resource", classifyStatus(r.status), r.errorDetails);
|
|
19720
|
-
output(
|
|
19721
|
-
this,
|
|
19722
|
-
{ [family === "assets" ? "asset" : "landing_page"]: r.data },
|
|
19723
|
-
() => show(r.data)
|
|
19724
|
-
);
|
|
19769
|
+
output(this, r.data, () => show(r.data));
|
|
19725
19770
|
});
|
|
19726
19771
|
return { list: list15, get: get10 };
|
|
19727
19772
|
}
|
package/package.json
CHANGED
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -11,7 +11,9 @@ Start by asking the installed CLI for its live contract:
|
|
|
11
11
|
apex schema --json
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Prefer JSON output. When stdout is piped, Apex emits JSON by default.
|
|
14
|
+
Prefer JSON output. When stdout is piped, Apex emits JSON by default. Stdout then
|
|
15
|
+
carries exactly one JSON document; `data` is the API response body as returned
|
|
16
|
+
(lists at `.data.data[]`), and API failures carry the API error body in `error.api`.
|
|
15
17
|
|
|
16
18
|
## Zero-Experiment Shop Bootstrap
|
|
17
19
|
|