@koda-sl/baker-cli 0.355.0 → 0.357.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/dist/cli.js CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  clipDurationsFor,
29
29
  clipParamRecipe,
30
30
  clipProfileFor,
31
+ coercePathUrlToUrl,
31
32
  collectAssetRefLikes,
32
33
  createEngineFromEnv,
33
34
  defaultRegistry,
@@ -68,7 +69,7 @@ import {
68
69
  validateCanvasDeep,
69
70
  withoutCastName,
70
71
  ytDlpBlockSignal
71
- } from "./chunk-4HRJQGVW.js";
72
+ } from "./chunk-H3SAAHZA.js";
72
73
  import {
73
74
  csvOrJson,
74
75
  daysAgoIso,
@@ -36809,7 +36810,7 @@ function rewriteTarget(value, workspaceRoot) {
36809
36810
  }
36810
36811
  return null;
36811
36812
  }
36812
- function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
36813
+ function normalizeCanvasPaths(canvas, canvasDir, workspaceRoot) {
36813
36814
  if (!canvas || typeof canvas !== "object") return { canvas, rewrites: [] };
36814
36815
  const c = canvas;
36815
36816
  if (!Array.isArray(c.nodes)) return { canvas, rewrites: [] };
@@ -36822,6 +36823,10 @@ function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
36822
36823
  const params = n.params;
36823
36824
  if (!params || typeof params !== "object") return node;
36824
36825
  const nodeId = typeof n.id === "string" ? n.id : "?";
36826
+ if (n.type === "ingest" && params.source === "path" && looksLikeHttpUrl(params.path)) {
36827
+ rewrites.push({ nodeId, param: "source", from: "path", to: "url" });
36828
+ return { ...node, params: coercePathUrlToUrl(params) };
36829
+ }
36825
36830
  const key = n.type === "ingest" && params.source === "path" ? "path" : n.type === "hyperframe_render" ? "composition" : null;
36826
36831
  if (!key) return node;
36827
36832
  const target2 = rewriteTarget(params[key], root);
@@ -36832,11 +36837,11 @@ function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
36832
36837
  });
36833
36838
  return rewrites.length === 0 ? { canvas, rewrites } : { canvas: { ...canvas, nodes }, rewrites };
36834
36839
  }
36835
- async function healAbsoluteCanvasPaths(filePath, canvas) {
36840
+ async function healCanvasPaths(filePath, canvas) {
36836
36841
  const canvasDir = path5.dirname(filePath);
36837
36842
  const workspaceRoot = findWorkspaceRoot(canvasDir);
36838
36843
  if (!workspaceRoot) return { canvas, rewrites: [], text: null };
36839
- const normalized = normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot);
36844
+ const normalized = normalizeCanvasPaths(canvas, canvasDir, workspaceRoot);
36840
36845
  if (normalized.rewrites.length === 0) return { ...normalized, text: null };
36841
36846
  const text2 = `${JSON.stringify(normalized.canvas, null, 2)}
36842
36847
  `;
@@ -41778,12 +41783,12 @@ async function executeCanvasRun(opts) {
41778
41783
  }
41779
41784
  const attemptedSlug = creativeSlugFromCanvasPath(filePath);
41780
41785
  if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
41781
- const healed = await healAbsoluteCanvasPaths(filePath, parsed);
41786
+ const healed = await healCanvasPaths(filePath, parsed);
41782
41787
  parsed = healed.canvas;
41783
41788
  const canvasText = healed.text ?? raw;
41784
41789
  if (healed.rewrites.length > 0) {
41785
41790
  process.stderr.write(
41786
- `[canvas] rewrote ${healed.rewrites.length} absolute asset path(s) to canvas-relative \u2014 they resolve only in this workspace:
41791
+ `[canvas] rewrote ${healed.rewrites.length} asset reference(s) in the canvas file so it says what runs (absolute paths \u2192 canvas-relative, a URL in an ingest path \u2192 source "url"):
41787
41792
  ${describeRewrites(healed.rewrites)}
41788
41793
  `
41789
41794
  );
@@ -45376,7 +45381,7 @@ var validateCommand = defineCommand118({
45376
45381
  `);
45377
45382
  process.exit(2);
45378
45383
  }
45379
- const healed = await healAbsoluteCanvasPaths(filePath, parsed);
45384
+ const healed = await healCanvasPaths(filePath, parsed);
45380
45385
  parsed = healed.canvas;
45381
45386
  parsed = resolveRelativeCanvasPaths(parsed, path28.dirname(filePath));
45382
45387
  let styleProjection = "not_applicable";
@@ -47295,6 +47300,80 @@ async function declarationsOf(root, files) {
47295
47300
  return sources.flatMap(eventDeclarationsIn);
47296
47301
  }
47297
47302
 
47303
+ // src/commands/experiment/readout.ts
47304
+ function isoMoment(ms) {
47305
+ return ms === null ? null : new Date(ms).toISOString();
47306
+ }
47307
+ function isoDay2(ms) {
47308
+ return ms === null ? null : new Date(ms).toISOString().slice(0, 10);
47309
+ }
47310
+ function compactArm(arm) {
47311
+ return { visitors: arm.visitors, conversions: arm.conversions, rate: Math.round(arm.rate * 1e4) / 1e4 };
47312
+ }
47313
+ function compactMetric(metric) {
47314
+ return {
47315
+ name: metric.name,
47316
+ reading: metric.reading,
47317
+ control: metric.control.conversions,
47318
+ variant: metric.variant.conversions
47319
+ };
47320
+ }
47321
+ function fullArm(arm) {
47322
+ return {
47323
+ ...arm,
47324
+ ...arm.snapshotCapturedAt === void 0 ? {} : { snapshotCapturedAt: isoMoment(arm.snapshotCapturedAt) }
47325
+ };
47326
+ }
47327
+ function statusReadout(row, { full, now }) {
47328
+ const decisionWhen = {
47329
+ earliestDecisionAt: isoDay2(row.earliestDecisionAt),
47330
+ ...row.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row.earliestDecisionAt, now) }
47331
+ };
47332
+ const moments = {
47333
+ startedAt: isoMoment(row.startedAt),
47334
+ pausedAt: isoMoment(row.pausedAt),
47335
+ finishedAt: isoMoment(row.finishedAt)
47336
+ };
47337
+ if (full) {
47338
+ return {
47339
+ ...row,
47340
+ ...moments,
47341
+ ...decisionWhen,
47342
+ control: fullArm(row.control),
47343
+ variant: fullArm(row.variant)
47344
+ };
47345
+ }
47346
+ return {
47347
+ experimentId: row.experimentId,
47348
+ landingSlug: row.landingSlug,
47349
+ variantSlug: row.variantSlug,
47350
+ status: row.status,
47351
+ verdict: row.verdict,
47352
+ summary: row.summary,
47353
+ ...row.winner === null ? {} : { winner: row.winner },
47354
+ ...row.invalidReason === null ? {} : { invalidReason: row.invalidReason },
47355
+ stillNeeded: row.stillNeeded,
47356
+ ...decisionWhen,
47357
+ willNotConclude: row.willNotConclude,
47358
+ control: compactArm(row.control),
47359
+ variant: compactArm(row.variant),
47360
+ secondary: row.secondary.map(compactMetric),
47361
+ hypothesisStatement: row.hypothesisStatement,
47362
+ minDetectableRelativeLift: row.minDetectableRelativeLift,
47363
+ goal: row.goal,
47364
+ ...row.variantGoal === null ? {} : { variantGoal: row.variantGoal },
47365
+ goalLabel: row.goalLabel,
47366
+ ...moments,
47367
+ ...row.status === "finished" ? {
47368
+ survivor: row.survivor,
47369
+ hypothesisOutcome: row.hypothesisOutcome,
47370
+ fold: row.fold,
47371
+ learning: row.learning,
47372
+ endReason: row.endReason
47373
+ } : {}
47374
+ };
47375
+ }
47376
+
47298
47377
  // src/commands/experiment/update.ts
47299
47378
  function buildUpdateRequest(args5, rawArgs) {
47300
47379
  const expect = args5.expect === void 0 ? void 0 : String(args5.expect);
@@ -47536,18 +47615,26 @@ registerSchema({
47536
47615
  args: {
47537
47616
  id: { type: "string", description: "One test, by its id", required: false },
47538
47617
  landing: LANDING_FILTER_ARG,
47539
- full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
47618
+ full: {
47619
+ type: "boolean",
47620
+ description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
47621
+ required: false
47622
+ }
47540
47623
  }
47541
47624
  });
47542
47625
  var statusCommand6 = defineCommand124({
47543
47626
  meta: {
47544
47627
  name: "status",
47545
- description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `summary` is one sentence you can show the client, and `secondary` lists every other conversion the company counts on the same two arms \u2014 context, never a second decision. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
47628
+ description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `earliestDecisionAt` is the day this page's traffic would get it there (YYYY-MM-DD) and `earliestDecisionIn` how far off that is \u2014 say those, never work a date out yourself. `summary` is one sentence you can show the client, and `secondary` lists every other conversion the company counts on the same two arms \u2014 context, never a second decision. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
47546
47629
  },
47547
47630
  args: {
47548
47631
  id: { type: "string", description: "One test, by its id", required: false },
47549
47632
  landing: LANDING_FILTER_ARG,
47550
- full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
47633
+ full: {
47634
+ type: "boolean",
47635
+ description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
47636
+ required: false
47637
+ }
47551
47638
  },
47552
47639
  run: async ({ args: args5 }) => {
47553
47640
  try {
@@ -47556,10 +47643,13 @@ var statusCommand6 = defineCommand124({
47556
47643
  ...args5.landing ? { landingSlug: String(args5.landing) } : {},
47557
47644
  ...args5.full === true ? { full: true } : {}
47558
47645
  });
47646
+ const now = Date.now();
47559
47647
  writeJsonEnvelope({
47560
47648
  ok: true,
47561
- data: response.data,
47562
- hints: buildStatusHints(response.data.experiments)
47649
+ data: {
47650
+ experiments: response.data.experiments.map((row) => statusReadout(row, { full: args5.full === true, now }))
47651
+ },
47652
+ hints: buildStatusHints(response.data.experiments, now)
47563
47653
  });
47564
47654
  } catch (error) {
47565
47655
  reportApiError(error);