@drip-apex/apex-cli 0.9.0 → 0.11.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 +15 -1
  2. package/dist/cli.js +49 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -86,6 +86,16 @@ apex schema --json
86
86
 
87
87
  Use `apex` as the canonical command name. `drip` is a compatibility alias.
88
88
 
89
+ `apex experiments list` returns code-free summaries by default, including
90
+ `targeting_urls` and `variation_count`, without targeting objects, variations, or goals.
91
+ Use `--full --json` for full details, including code. `--full` keeps the default table unchanged.
92
+ Use `--fields id,test_key,name,status --json` to select top-level keys (JSON always keeps `id`),
93
+ or `--fields status,name` to print exactly those table columns in order.
94
+ `--fields` and `--full` can combine; invalid fields return the allowed keys.
95
+ Existing CR-011 filters still narrow the set; these flags only select row content.
96
+ The direct API retains its full default view; the CLI requests `view=summary`.
97
+ Summary timestamps include `created_at` and `archived_at`; experiments have no `updated_at` field.
98
+
89
99
  `apex experiments list` hides archived experiments by default. Pass
90
100
  `--include-archived` to include them; archived rows display their archive date
91
101
  when the API provides `archived_at`.
@@ -129,7 +139,7 @@ including every subcommand, argument, option, and alias.
129
139
  - `apex experiments` — create, inspect, operate, and synchronize experiments.
130
140
  Use `apex experiments screenshots <id-or-slug>` to force-assign every capture, including control, to its variation server-side for deterministic screenshots. Resume a job that outlives the CLI timeout with `--job <id>`; polling continues without creating another job, and the stored job's devices and variations win, so `--devices`/`--variations` are ignored (the CLI warns). The command attaches terminal jobs to the QA run by default; use `--no-attach-qa` to opt out. Choose devices with `--devices`, filter treatments with `--variations`, and use `--json` for the signed result payload. Use `apex experiments qa evidence` with `--job`, repeatable `--file`, or `--records` to write QA evidence directly.
131
141
  - `apex push` — synchronize `.drip.config.ts` with Apex.
132
- - `apex preview` — open a variation QA preview.
142
+ - `apex preview` — open a variation QA preview; `--proxy` returns a `qa_run` receipt. Its `automated_evidence_count` counts the normalized `evidenceRecords` across all evidence kinds, matching the list shown by `apex experiments qa status`.
133
143
  - `apex roadmap` — consume approved roadmap ideas and handoffs.
134
144
  - `apex harness` — develop AB Test Harness projects with live QA sessions.
135
145
  - `apex workspace` — check out, preview, and publish draft workspace files.
@@ -328,3 +338,7 @@ optimistic concurrency token.
328
338
  | --- | --- |
329
339
  | `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
340
  | `goals` | `apex_goal_id` is the linked experiment goal or null. |
341
+
342
+ QA status creates a run only when none exists. Reading an existing run never writes
343
+ to it: `updatedAt` is its last stored mutation time, while `computedAt` is the ISO
344
+ timestamp when the current checklist and summary were computed.
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.9.0",
7115
+ version: "0.11.0",
7116
7116
  license: "MIT",
7117
7117
  description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
7118
7118
  private: false,
@@ -7762,6 +7762,8 @@ function listExperiments(creds, opts) {
7762
7762
  if (opts?.limit) params.set("limit", String(opts.limit));
7763
7763
  if (opts?.cursor) params.set("cursor", opts.cursor);
7764
7764
  if (opts?.includeArchived) params.set("archived", "1");
7765
+ if (opts?.view) params.set("view", opts.view);
7766
+ if (opts?.fields !== void 0) params.set("fields", opts.fields);
7765
7767
  const qs = params.toString();
7766
7768
  return request(creds, "GET", `/api/v1/experiments${qs ? `?${qs}` : ""}`);
7767
7769
  }
@@ -9639,6 +9641,7 @@ function printQaPreviewUrls(experimentId, result) {
9639
9641
  function printQaRun(run3) {
9640
9642
  console.log();
9641
9643
  console.log(source_default.bold(`QA run ${run3.id}`));
9644
+ console.log(` computedAt: ${run3.computedAt}`);
9642
9645
  console.log(` Status: ${statusBadge(run3.summary.status)}`);
9643
9646
  console.log(` Required: ${run3.summary.completedRequired}/${run3.summary.required}`);
9644
9647
  if (run3.approvedAt) console.log(` Approved: ${run3.approvedAt}`);
@@ -9804,16 +9807,23 @@ async function resolveExperiment(idOrSlug) {
9804
9807
  res.errorDetails
9805
9808
  );
9806
9809
  }
9807
- var list = new Command("list").alias("ls").description("List experiments (archived experiments are hidden by default)").option(
9810
+ var list = new Command("list").alias("ls").description(
9811
+ "List experiments in summary view by default; use --full for details or --fields for selected columns (archived experiments are hidden by default)"
9812
+ ).option(
9808
9813
  "-s, --status <status>",
9809
9814
  "Filter by status (draft|awaiting_client_approval|changes_requested|client_approved|qa|ready_to_launch|running|paused|completed)"
9810
- ).option("-n, --limit <n>", "Max results", "20").option("--include-archived", "Include archived experiments").action(async function action3(opts) {
9815
+ ).option("-n, --limit <n>", "Max results", "20").option("--include-archived", "Include archived experiments").option("--full", "Return full experiment details instead of the default summary").option(
9816
+ "--fields <list>",
9817
+ "Comma-separated fields to return and table columns in order (JSON always keeps id)"
9818
+ ).action(async function action3(opts) {
9811
9819
  const creds = requireCredentials();
9812
9820
  const spinner = await createSpinner("Fetching experiments...");
9813
9821
  const res = await listExperiments(creds, {
9814
9822
  status: opts.status,
9815
9823
  limit: parseInt(opts.limit, 10),
9816
- includeArchived: opts.includeArchived
9824
+ includeArchived: opts.includeArchived,
9825
+ view: opts.full ? "full" : "summary",
9826
+ fields: opts.fields
9817
9827
  });
9818
9828
  if (!res.ok) {
9819
9829
  spinner?.fail("Failed to fetch experiments");
@@ -9826,18 +9836,32 @@ var list = new Command("list").alias("ls").description("List experiments (archiv
9826
9836
  console.log(source_default.dim("No experiments found."));
9827
9837
  return;
9828
9838
  }
9829
- printTable(
9830
- ["NAME", "SLUG", "STATUS", "ARCHIVED", "TRAFFIC", "CREATED"],
9831
- experiments.map((e) => [
9832
- e.name.slice(0, 40),
9833
- e.slug ?? source_default.dim("-"),
9834
- statusBadge(e.status),
9835
- e.archived_at ? formatDate(e.archived_at) : source_default.dim("-"),
9836
- `${(e.traffic_allocation * 100).toFixed(0)}%`,
9837
- formatDate(e.created_at)
9838
- ]),
9839
- [40, 25, 12, 15, 8, 15]
9840
- );
9839
+ if (opts.fields !== void 0) {
9840
+ const fields3 = [
9841
+ ...new Set(opts.fields.split(",").map((field) => field.trim()))
9842
+ ];
9843
+ printTable(
9844
+ fields3.map((field) => field.toUpperCase()),
9845
+ experiments.map(
9846
+ (experiment) => fields3.map((field) => {
9847
+ const value = experiment[field];
9848
+ return value == null ? "-" : typeof value === "object" ? JSON.stringify(value) : String(value);
9849
+ })
9850
+ )
9851
+ );
9852
+ } else
9853
+ printTable(
9854
+ ["NAME", "SLUG", "STATUS", "ARCHIVED", "TRAFFIC", "CREATED"],
9855
+ experiments.map((e) => [
9856
+ e.name.slice(0, 40),
9857
+ e.slug ?? source_default.dim("-"),
9858
+ statusBadge(e.status),
9859
+ e.archived_at ? formatDate(e.archived_at) : source_default.dim("-"),
9860
+ `${(e.traffic_allocation * 100).toFixed(0)}%`,
9861
+ formatDate(e.created_at)
9862
+ ]),
9863
+ [40, 25, 12, 15, 8, 15]
9864
+ );
9841
9865
  if (res.data.pagination.has_more) {
9842
9866
  console.log(source_default.dim(`
9843
9867
  (more results available)`));
@@ -10826,7 +10850,9 @@ var applyFilesystem = new Command("apply").option("--sync", "Refresh local autho
10826
10850
  fail(this, error instanceof Error ? error.message : String(error));
10827
10851
  }
10828
10852
  });
10829
- var qaStatus = new Command("status").description("Show or create the latest experiment QA run").argument(
10853
+ var qaStatus = new Command("status").description(
10854
+ "Show the latest experiment QA run (creates one only when none exists; reads never change updatedAt)"
10855
+ ).argument(
10830
10856
  "[folder-or-id]",
10831
10857
  "Experiment filesystem folder, ID, slug, or test key (for example BIO-085)",
10832
10858
  "."
@@ -11791,7 +11817,9 @@ var pushCommand = new Command("push").description("Push experiment definitions f
11791
11817
 
11792
11818
  // src/commands/preview.ts
11793
11819
  init_source();
11794
- var previewCommand = new Command("preview").description("Open QA preview URL for an experiment variation").argument("<id-or-slug>", "Experiment ID or slug").argument("[variation-id]", "Variation ID (defaults to first non-control)").option("--store-url <url>", "Override experiment store_url (target website URL)").option("--proxy", "Create an artifact-injecting proxy preview").option("--ttl <hours>", "Proxy preview lifetime in hours (1-168)").option("--no-open", "Print URL instead of opening browser").action(
11820
+ var previewCommand = new Command("preview").description(
11821
+ "Open QA preview URL for an experiment variation; proxy previews return a QA run receipt"
11822
+ ).argument("<id-or-slug>", "Experiment ID or slug").argument("[variation-id]", "Variation ID (defaults to first non-control)").option("--store-url <url>", "Override experiment store_url (target website URL)").option("--proxy", "Create an artifact-injecting proxy preview").option("--ttl <hours>", "Proxy preview lifetime in hours (1-168)").option("--no-open", "Print URL instead of opening browser").action(
11795
11823
  async (idOrSlug, variationId, opts) => {
11796
11824
  const creds = requireCredentials();
11797
11825
  if (opts?.proxy) {
@@ -11819,6 +11847,9 @@ var previewCommand = new Command("preview").description("Open QA preview URL for
11819
11847
  const data = created.data;
11820
11848
  output(previewCommand, data, () => {
11821
11849
  console.log(source_default.cyan(data.preview_url));
11850
+ console.log(
11851
+ data.qa_run.created ? `QA run ${data.qa_run.id} created` : `QA run ${data.qa_run.id} refreshed (${data.qa_run.automated_evidence_count} automated evidence records)`
11852
+ );
11822
11853
  const expiresAt = new Date(data.expires_at);
11823
11854
  const remainingMs = expiresAt.getTime() - Date.now();
11824
11855
  const value = remainingMs < 864e5 ? Math.max(1, Math.round(remainingMs / 36e5)) : Math.round(remainingMs / 864e5);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drip-apex/apex-cli",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "description": "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
6
6
  "private": false,