@erdoai/cli 0.25.0 → 0.26.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/index.js +46 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1298,9 +1298,45 @@ wsCmd.command("attach <slug>").description("Link a resource (page, dataset, hear
|
|
|
1298
1298
|
}
|
|
1299
1299
|
}
|
|
1300
1300
|
);
|
|
1301
|
+
var VARIANT_SPEC_KEYS = /* @__PURE__ */ new Set([
|
|
1302
|
+
"key",
|
|
1303
|
+
"label",
|
|
1304
|
+
"alloc",
|
|
1305
|
+
"allocation_percent",
|
|
1306
|
+
"control",
|
|
1307
|
+
"is_control",
|
|
1308
|
+
"page",
|
|
1309
|
+
"url",
|
|
1310
|
+
"treatment_url",
|
|
1311
|
+
"type",
|
|
1312
|
+
"treatment_resource_type",
|
|
1313
|
+
"resource",
|
|
1314
|
+
"treatment_resource_id",
|
|
1315
|
+
"class",
|
|
1316
|
+
"treatment_class"
|
|
1317
|
+
]);
|
|
1318
|
+
var COMMA_SAFE_VARIANT_KEYS = /* @__PURE__ */ new Set(["url", "treatment_url", "label"]);
|
|
1319
|
+
function splitVariantSpec(spec) {
|
|
1320
|
+
const parts = [];
|
|
1321
|
+
let lastKey = "";
|
|
1322
|
+
for (const raw of spec.split(",")) {
|
|
1323
|
+
const trimmed = raw.trim();
|
|
1324
|
+
if (!trimmed) continue;
|
|
1325
|
+
const eq = trimmed.indexOf("=");
|
|
1326
|
+
const key = eq > 0 ? trimmed.slice(0, eq).trim() : "";
|
|
1327
|
+
const startsToken = trimmed === "control" || trimmed === "is_control" || key !== "" && VARIANT_SPEC_KEYS.has(key);
|
|
1328
|
+
if (!startsToken && parts.length > 0 && COMMA_SAFE_VARIANT_KEYS.has(lastKey)) {
|
|
1329
|
+
parts[parts.length - 1] += "," + raw;
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
parts.push(raw);
|
|
1333
|
+
lastKey = startsToken && key !== "" ? key : "";
|
|
1334
|
+
}
|
|
1335
|
+
return parts;
|
|
1336
|
+
}
|
|
1301
1337
|
function parseVariant(spec) {
|
|
1302
1338
|
const v = {};
|
|
1303
|
-
for (const raw of spec
|
|
1339
|
+
for (const raw of splitVariantSpec(spec)) {
|
|
1304
1340
|
const part = raw.trim();
|
|
1305
1341
|
if (!part) continue;
|
|
1306
1342
|
const eq = part.indexOf("=");
|
|
@@ -1309,7 +1345,7 @@ function parseVariant(spec) {
|
|
|
1309
1345
|
v.is_control = true;
|
|
1310
1346
|
continue;
|
|
1311
1347
|
}
|
|
1312
|
-
throw new Error(`--variant: unknown flag "${part}" (expected key=, label=, page=, alloc=, class=, or 'control')`);
|
|
1348
|
+
throw new Error(`--variant: unknown flag "${part}" (expected key=, label=, page=, url=, alloc=, class=, or 'control')`);
|
|
1313
1349
|
}
|
|
1314
1350
|
const k = part.slice(0, eq).trim();
|
|
1315
1351
|
const val = part.slice(eq + 1).trim();
|
|
@@ -1333,6 +1369,10 @@ function parseVariant(spec) {
|
|
|
1333
1369
|
v.treatment_resource_type = "page";
|
|
1334
1370
|
v.treatment_resource_id = val;
|
|
1335
1371
|
break;
|
|
1372
|
+
case "url":
|
|
1373
|
+
case "treatment_url":
|
|
1374
|
+
v.treatment_url = val;
|
|
1375
|
+
break;
|
|
1336
1376
|
case "type":
|
|
1337
1377
|
case "treatment_resource_type":
|
|
1338
1378
|
v.treatment_resource_type = val;
|
|
@@ -1352,6 +1392,9 @@ function parseVariant(spec) {
|
|
|
1352
1392
|
if (!v.key) {
|
|
1353
1393
|
throw new Error('--variant requires key=, e.g. --variant "key=control,label=Current,control"');
|
|
1354
1394
|
}
|
|
1395
|
+
if (v.treatment_url && (v.treatment_resource_type || v.treatment_resource_id)) {
|
|
1396
|
+
throw new Error("--variant: url= is mutually exclusive with page=/type=/resource= (a variant is either an Erdo page or an external URL)");
|
|
1397
|
+
}
|
|
1355
1398
|
if (!v.label) v.label = v.key;
|
|
1356
1399
|
return v;
|
|
1357
1400
|
}
|
|
@@ -1412,7 +1455,7 @@ expCmd.command("policy <slug>").description(
|
|
|
1412
1455
|
});
|
|
1413
1456
|
expCmd.command("create").description("Create an experiment with its variants, evidence datasets and decision rule").requiredOption("--project <slug>", "project slug").requiredOption("--slug <slug>", "experiment slug (unique per org)").requiredOption("--title <title>", "title").option("--workstream <slug>", "host workstream").option("--scope <scope>", "local (default), platform_canary, platform_global").option("--hypothesis <text>", "hypothesis markdown").option("--primary-metric <metric>", "the metric the decision rule reads").option("--decision-rule <text>", "the rule that decides ship/stop from the primary metric").option("--guardrail <metric>", "a metric that must not regress (repeatable)", collect, []).option(
|
|
1414
1457
|
"--variant <spec>",
|
|
1415
|
-
|
|
1458
|
+
`a variant "key=b,label=3-field form,page=<artifact-id>,alloc=50,class=form_length_change,control" (repeatable). Fields: key=, label=, page= (Erdo artifact id) OR url=<https://\u2026> (an external page the persona panel visits, e.g. a client's live landing page \u2014 mutually exclusive with page=), alloc=, class=, and the bare flag control (or control=true) to mark the baseline. class= declares the structural change vs control for cross-experiment learning: headline_change | hero_media_change | cta_change | form_length_change | social_proof_add | layout_reorder | offer_change | full_page_rebuild`,
|
|
1416
1459
|
collect,
|
|
1417
1460
|
[]
|
|
1418
1461
|
).option(
|