@drip-apex/apex-cli 0.8.0 → 0.9.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/README.md +15 -0
- package/dist/cli.js +35 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -313,3 +313,18 @@ Create/get return `dashboard_url`; apply returns `dashboardUrl`. These links
|
|
|
313
313
|
switch the signed-in dashboard user's active shop before opening the experiment.
|
|
314
314
|
|
|
315
315
|
Filesystem apply caches uploaded asset URLs by byte SHA-256 in `.apex/experiment-filesystem.json`, so unchanged images reuse their URL across applies without rewriting authored files.
|
|
316
|
+
|
|
317
|
+
`--instructions` cannot be combined with `--out` (or its legacy alias `--output`); file exports always contain the full JSON contract.
|
|
318
|
+
|
|
319
|
+
Roadmap handoff JSON identifies itself as `apex.roadmap_handoff.v1`; backlog get/list
|
|
320
|
+
items identify themselves as `apex.backlog_item.v1`, both with `schema_version: 1`.
|
|
321
|
+
The handoff embeds the same closed item projection that backlog responses serve
|
|
322
|
+
alongside their existing raw fields. Published schemas and fixtures live in
|
|
323
|
+
[`docs/contracts/experiment-os`](../../docs/contracts/experiment-os/README.md).
|
|
324
|
+
Breaking changes require a new v2 contract; the backlog `version` is a separate
|
|
325
|
+
optimistic concurrency token.
|
|
326
|
+
|
|
327
|
+
| Key | Source |
|
|
328
|
+
| --- | --- |
|
|
329
|
+
| `approval` | Roadmap decision status (`proposed` without a decision), item stage, and decision's nullable `decided_at`. Top-level `status` remains the item lifecycle status. |
|
|
330
|
+
| `goals` | `apex_goal_id` is the linked experiment goal or null. |
|
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.9.0",
|
|
7116
7116
|
license: "MIT",
|
|
7117
7117
|
description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
|
|
7118
7118
|
private: false,
|
|
@@ -8969,6 +8969,16 @@ function buildQaPreviewUrl2(storeUrl, experimentId, variationId) {
|
|
|
8969
8969
|
return url;
|
|
8970
8970
|
}
|
|
8971
8971
|
|
|
8972
|
+
// src/lib/device-label.ts
|
|
8973
|
+
function deviceLabel(targeting) {
|
|
8974
|
+
if (!targeting || typeof targeting !== "object") return void 0;
|
|
8975
|
+
const device = targeting.deviceType;
|
|
8976
|
+
if (device === "desktop") return "Desktop only";
|
|
8977
|
+
if (device === "mobile") return "Mobile only";
|
|
8978
|
+
if (device === "tablet") return "Tablet only";
|
|
8979
|
+
return void 0;
|
|
8980
|
+
}
|
|
8981
|
+
|
|
8972
8982
|
// src/lib/test-key.ts
|
|
8973
8983
|
var TEST_KEY_RE = /^([A-Z0-9]{2,8})-(\d+)$/;
|
|
8974
8984
|
function isTestKeyReference(value) {
|
|
@@ -9279,8 +9289,13 @@ function printExperiment(exp, dashboardUrl = null) {
|
|
|
9279
9289
|
console.log(` Status: ${statusBadge(exp.status)}`);
|
|
9280
9290
|
console.log(` Classification: ${exp.counting_type}`);
|
|
9281
9291
|
console.log(` Traffic: ${(exp.traffic_allocation * 100).toFixed(0)}%`);
|
|
9292
|
+
console.log(` QA approved: ${exp.qa_approved_at ?? "none"}`);
|
|
9293
|
+
console.log(` Scheduled: ${exp.scheduled_start_at ?? "none"}`);
|
|
9294
|
+
console.log(` Started: ${exp.started_at ?? "none"}`);
|
|
9282
9295
|
if (exp.targeting !== void 0) {
|
|
9283
9296
|
console.log(` Targeting: ${effectivePageTargetingLabel(exp.targeting)}`);
|
|
9297
|
+
const device = deviceLabel(exp.targeting);
|
|
9298
|
+
if (device) console.log(` Device: ${device}`);
|
|
9284
9299
|
}
|
|
9285
9300
|
console.log(` Created: ${formatDate(exp.created_at)}`);
|
|
9286
9301
|
if (exp.linked_idea) {
|
|
@@ -9560,6 +9575,9 @@ function printFilesystemPlan(plan) {
|
|
|
9560
9575
|
console.log(source_default.dim(" No changes."));
|
|
9561
9576
|
} else {
|
|
9562
9577
|
plan.changes.forEach((change) => {
|
|
9578
|
+
if (change.entity === "experiment.targeting" && Array.isArray(change.detail?.changedKeys)) {
|
|
9579
|
+
console.log(` Changed keys: ${change.detail.changedKeys.join(", ")}`);
|
|
9580
|
+
}
|
|
9563
9581
|
const pathLabel = change.path ? source_default.dim(` ${change.path}`) : "";
|
|
9564
9582
|
const qa3 = change.invalidatesQa ? source_default.yellow(" invalidates QA") : "";
|
|
9565
9583
|
console.log(
|
|
@@ -9826,7 +9844,9 @@ var list = new Command("list").alias("ls").description("List experiments (archiv
|
|
|
9826
9844
|
}
|
|
9827
9845
|
});
|
|
9828
9846
|
});
|
|
9829
|
-
var get = new Command("get").description(
|
|
9847
|
+
var get = new Command("get").description(
|
|
9848
|
+
"Get experiment details by ID or slug. JSON readback always carries started_at, scheduled_start_at and qa_approved_at as ISO-8601 UTC timestamps or null (all null on a fresh draft)."
|
|
9849
|
+
).argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).action(async function action4(idOrSlug) {
|
|
9830
9850
|
const spinner = await createSpinner("Fetching experiment...");
|
|
9831
9851
|
const exp = await resolveExperiment(idOrSlug);
|
|
9832
9852
|
spinner?.stop();
|
|
@@ -10140,7 +10160,9 @@ ${rows.map((row, index) => ` ${index + 1}. ${row.name} (${row.id})`).join("\n")
|
|
|
10140
10160
|
`
|
|
10141
10161
|
);
|
|
10142
10162
|
}
|
|
10143
|
-
var create = new Command("create").description(
|
|
10163
|
+
var create = new Command("create").description(
|
|
10164
|
+
"Create a complete experiment with page targeting, audiences, goals, and an optional QA preview URL. JSON readback always carries started_at, scheduled_start_at and qa_approved_at as ISO-8601 UTC timestamps or null (all null on a fresh draft)."
|
|
10165
|
+
).addOption(
|
|
10144
10166
|
new Option("--counting-type <type>", "Experiment classification").choices(countingTypes).default("standard")
|
|
10145
10167
|
).option("--name <name>", "Experiment name").option("--slug <slug>", "URL-safe slug for GitOps").option("--description <desc>", "Description").option("--type <type>", "Experiment type: mutation or redirect", "mutation").option(
|
|
10146
10168
|
"--source-url <pattern>",
|
|
@@ -10963,6 +10985,7 @@ var qaApprove = new Command("approve").description("Approve QA and move the expe
|
|
|
10963
10985
|
{
|
|
10964
10986
|
experimentId: resolved.experimentId,
|
|
10965
10987
|
qaStatus: "done",
|
|
10988
|
+
experiment: res.data,
|
|
10966
10989
|
experiment_status_before: before.status,
|
|
10967
10990
|
experiment_status_after: res.data.status,
|
|
10968
10991
|
transition
|
|
@@ -12767,7 +12790,11 @@ More results: --cursor ${res.data.pagination.next_cursor}`));
|
|
|
12767
12790
|
}
|
|
12768
12791
|
);
|
|
12769
12792
|
});
|
|
12770
|
-
var handoff = new Command("handoff").description(
|
|
12793
|
+
var handoff = new Command("handoff").description(
|
|
12794
|
+
"Get apex.roadmap_handoff.v1 JSON: contract, schema_version, item, experiment, instructions, goals, mockups, acceptance_criteria, harness, scoring, source, metadata"
|
|
12795
|
+
).argument("<id>", "Roadmap/backlog item ID").option("--out <path>", "Write handoff JSON to a file").addOption(new Option("-o, --output <path>", "Legacy alias for --out").hideHelp()).addOption(
|
|
12796
|
+
new Option("--instructions", "Print only the build instructions").conflicts(["out", "output"])
|
|
12797
|
+
).action(async function action32(id, opts) {
|
|
12771
12798
|
if (opts.output !== void 0) {
|
|
12772
12799
|
process.stderr.write(
|
|
12773
12800
|
`Warning: --output is deprecated for apex roadmap handoff; use --out instead. --output will be removed in v${LEGACY_OUTPUT_REMOVAL_VERSION}.
|
|
@@ -12788,7 +12815,8 @@ var handoff = new Command("handoff").description("Get the full build brief for a
|
|
|
12788
12815
|
classifyStatus(res.status),
|
|
12789
12816
|
res.errorDetails
|
|
12790
12817
|
);
|
|
12791
|
-
const payload = res.data
|
|
12818
|
+
const payload = res.data;
|
|
12819
|
+
if (!payload) fail(this, "Missing roadmap handoff response");
|
|
12792
12820
|
const rendered = opts.instructions ? String(payload.instructions ?? "") : `${JSON.stringify(payload, null, 2)}
|
|
12793
12821
|
`;
|
|
12794
12822
|
const outputData = opts.instructions ? { roadmapItemId: id, instructions: String(payload.instructions ?? "") } : payload;
|
|
@@ -16988,7 +17016,7 @@ async function fieldLabels(command, item) {
|
|
|
16988
17016
|
(fields3.ok && fields3.data ? fields3.data.data : []).map((field) => [field.key, field.label])
|
|
16989
17017
|
);
|
|
16990
17018
|
}
|
|
16991
|
-
var list7 = new Command("list").alias("ls").description("List backlog").option("-n, --limit <n>", "Max results", "50").option("--cursor <cursor>", "Continue from cursor").option("--include-archived", "Include archived backlog items").action(async function(o) {
|
|
17019
|
+
var list7 = new Command("list").alias("ls").description("List backlog items identified as apex.backlog_item.v1").option("-n, --limit <n>", "Max results", "50").option("--cursor <cursor>", "Continue from cursor").option("--include-archived", "Include archived backlog items").action(async function(o) {
|
|
16992
17020
|
const r = await listInsights(
|
|
16993
17021
|
requireCredentials(),
|
|
16994
17022
|
kind2,
|
|
@@ -17012,7 +17040,7 @@ var list7 = new Command("list").alias("ls").description("List backlog").option("
|
|
|
17012
17040
|
)
|
|
17013
17041
|
);
|
|
17014
17042
|
});
|
|
17015
|
-
var get6 = new Command("get").description("Get a backlog item").argument("<id>", "backlog item ID").action(async function(id) {
|
|
17043
|
+
var get6 = new Command("get").description("Get a backlog item identified as apex.backlog_item.v1").argument("<id>", "backlog item ID").action(async function(id) {
|
|
17016
17044
|
const r = await getInsight(requireCredentials(), kind2, id);
|
|
17017
17045
|
if (!r.ok || !r.data)
|
|
17018
17046
|
fail(this, r.error ?? "Failed to get", classifyStatus(r.status), r.errorDetails);
|