@erdoai/cli 0.76.0 → 0.78.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 +60 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFileSync as readFileSync4 } from "fs";
|
|
5
5
|
import { basename } from "path";
|
|
6
6
|
import { select } from "@inquirer/prompts";
|
|
7
|
-
import { Command } from "commander";
|
|
7
|
+
import { Command as Command2 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -940,8 +940,14 @@ var ErdoClient = class {
|
|
|
940
940
|
}
|
|
941
941
|
// The org's dataset vocabulary: every purpose in use, its description, and
|
|
942
942
|
// the dataset that established it (the canonical write target for that role).
|
|
943
|
-
|
|
944
|
-
|
|
943
|
+
// scope="managed" reads the same across every client organization a manager
|
|
944
|
+
// account operates; each entry then names its organization and its
|
|
945
|
+
// established_by slug is org-qualified, so it can be queried as-is.
|
|
946
|
+
listDatasetPurposes(params) {
|
|
947
|
+
const q = new URLSearchParams();
|
|
948
|
+
if (params?.scope) q.set("scope", params.scope);
|
|
949
|
+
const qs = q.toString();
|
|
950
|
+
return this.request("GET", `/v1/datasets-purposes${qs ? `?${qs}` : ""}`);
|
|
945
951
|
}
|
|
946
952
|
// Correct which dataset carries a purpose. Pass `purpose` to set one,
|
|
947
953
|
// `clear_purpose` to remove it; moving a purpose is clearing it from the old
|
|
@@ -1582,6 +1588,32 @@ function readMaybeFile(value) {
|
|
|
1582
1588
|
return value.startsWith("@") ? readFileSync2(value.slice(1), "utf8") : value;
|
|
1583
1589
|
}
|
|
1584
1590
|
|
|
1591
|
+
// src/scope.ts
|
|
1592
|
+
import { Option } from "commander";
|
|
1593
|
+
var ORG_ENV = "ERDO_ORG";
|
|
1594
|
+
var PROJECT_ENV = "ERDO_PROJECT";
|
|
1595
|
+
function bindScopeFlags(program2) {
|
|
1596
|
+
program2.option("--org <idOrSlug>", "org to target for this command (overrides the active org)").option(
|
|
1597
|
+
"--project <slugOrId>",
|
|
1598
|
+
"project context for this command \u2014 slug or UUID (must belong to the active org)"
|
|
1599
|
+
);
|
|
1600
|
+
program2.on("option:org", (value) => {
|
|
1601
|
+
process.env[ORG_ENV] = value;
|
|
1602
|
+
});
|
|
1603
|
+
program2.on("option:project", (value) => {
|
|
1604
|
+
process.env[PROJECT_ENV] = value;
|
|
1605
|
+
});
|
|
1606
|
+
return program2;
|
|
1607
|
+
}
|
|
1608
|
+
function projectOption(description, opts = {}) {
|
|
1609
|
+
const option = new Option("--project <slug>", description).env(PROJECT_ENV);
|
|
1610
|
+
if (opts.mandatory) option.makeOptionMandatory();
|
|
1611
|
+
return option;
|
|
1612
|
+
}
|
|
1613
|
+
function orgOption(description) {
|
|
1614
|
+
return new Option("--org <idOrSlug>", description).env(ORG_ENV);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1585
1617
|
// src/update.ts
|
|
1586
1618
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
1587
1619
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -1796,14 +1828,9 @@ function compactEvalRunAttribution(run) {
|
|
|
1796
1828
|
].filter((part) => part !== void 0);
|
|
1797
1829
|
return [run.triggered_by, ...revisions].join(" ");
|
|
1798
1830
|
}
|
|
1799
|
-
var program = new
|
|
1800
|
-
program.name("erdo").description("Erdo CLI").version(pkg.version)
|
|
1801
|
-
program
|
|
1802
|
-
const opts = thisCommand.opts();
|
|
1803
|
-
const org2 = opts.org;
|
|
1804
|
-
if (org2) process.env.ERDO_ORG = org2;
|
|
1805
|
-
if (opts.project) process.env.ERDO_PROJECT = opts.project;
|
|
1806
|
-
});
|
|
1831
|
+
var program = new Command2();
|
|
1832
|
+
program.name("erdo").description("Erdo CLI").version(pkg.version);
|
|
1833
|
+
bindScopeFlags(program);
|
|
1807
1834
|
async function doLogin(opts) {
|
|
1808
1835
|
let token;
|
|
1809
1836
|
let me;
|
|
@@ -2170,7 +2197,9 @@ This key operates every org you manage; target one with X-Organization-ID (or \`
|
|
|
2170
2197
|
}
|
|
2171
2198
|
});
|
|
2172
2199
|
var tokenCmd = program.command("token").description("Manage your API tokens (account-level credentials)");
|
|
2173
|
-
tokenCmd.command("create").description("Mint a new API token; the secret is printed ONCE").requiredOption("--name <name>", "a label for the token").option("--expires-days <n>", "days until expiry (default: 30)", (v) => parseInt(v, 10)).
|
|
2200
|
+
tokenCmd.command("create").description("Mint a new API token; the secret is printed ONCE").requiredOption("--name <name>", "a label for the token").option("--expires-days <n>", "days until expiry (default: 30)", (v) => parseInt(v, 10)).addOption(
|
|
2201
|
+
orgOption("the token's default org (defaults to your active org; must be one you belong to)")
|
|
2202
|
+
).action(async (opts) => {
|
|
2174
2203
|
try {
|
|
2175
2204
|
const res = await new ErdoClient().createToken({
|
|
2176
2205
|
name: opts.name,
|
|
@@ -2272,7 +2301,7 @@ wsCmd.command("get <slug>").description("Show a workstream \u2014 status, phases
|
|
|
2272
2301
|
fail(e);
|
|
2273
2302
|
}
|
|
2274
2303
|
});
|
|
2275
|
-
wsCmd.command("create").description("Create a workstream in a project").
|
|
2304
|
+
wsCmd.command("create").description("Create a workstream in a project").addOption(projectOption("project slug", { mandatory: true })).requiredOption("--slug <slug>", "workstream slug (unique per org)").requiredOption("--title <title>", "human-readable title").option("--description <text>", "free-form description").option(
|
|
2276
2305
|
"--token-budget <millicents>",
|
|
2277
2306
|
"spend ceiling in millicents \u2014 100,000 per dollar, so 10000000 = $100 and 25000000 = $250; omit for unlimited",
|
|
2278
2307
|
(v) => parseInt(v, 10)
|
|
@@ -2566,7 +2595,7 @@ expCmd.command("policy <slug>").description(
|
|
|
2566
2595
|
fail(e);
|
|
2567
2596
|
}
|
|
2568
2597
|
});
|
|
2569
|
-
expCmd.command("create").description("Create an experiment with its variants, evidence datasets and decision rule").
|
|
2598
|
+
expCmd.command("create").description("Create an experiment with its variants, evidence datasets and decision rule").addOption(projectOption("project slug", { mandatory: true })).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(
|
|
2570
2599
|
"--variant <spec>",
|
|
2571
2600
|
`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`,
|
|
2572
2601
|
collect,
|
|
@@ -2704,7 +2733,7 @@ expCmd.command("set-status <slug> <status>").description("Set an experiment's st
|
|
|
2704
2733
|
});
|
|
2705
2734
|
expCmd.command("calibration").description(
|
|
2706
2735
|
"Read each judge's track record \u2014 paired predictions and pairwise agreement vs measured reality (the trust dial)"
|
|
2707
|
-
).
|
|
2736
|
+
).addOption(projectOption("limit to one project (default: every project you can view)")).option("--judge <slug>", "show only judges whose slug contains this text").option("-n, --limit <n>", "max ledger rows scanned (default 5000)", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
2708
2737
|
try {
|
|
2709
2738
|
const res = await new ErdoClient().judgeCalibration({
|
|
2710
2739
|
project_slug: opts.project,
|
|
@@ -4954,17 +4983,28 @@ datasetsCmd.command("list").description("List datasets").option(
|
|
|
4954
4983
|
});
|
|
4955
4984
|
datasetsCmd.command("purposes").description(
|
|
4956
4985
|
"List the org's dataset purposes \u2014 the vocabulary of dataset roles in use (e.g. leads, page events). The established_by slug is the canonical dataset for that role: write there rather than creating a sibling (one purpose = one dataset per org)."
|
|
4957
|
-
).
|
|
4986
|
+
).option(
|
|
4987
|
+
"--scope <own|managed>",
|
|
4988
|
+
"whose vocabulary to read: own (default) or managed, which reads every client organization a manager account operates and prints the organization alongside each purpose"
|
|
4989
|
+
).action(async (opts) => {
|
|
4958
4990
|
try {
|
|
4959
|
-
const
|
|
4991
|
+
const scope = opts.scope?.toLowerCase();
|
|
4992
|
+
if (scope && scope !== "own" && scope !== "managed") {
|
|
4993
|
+
fail(new Error("--scope must be own or managed"));
|
|
4994
|
+
return;
|
|
4995
|
+
}
|
|
4996
|
+
const { purposes, message } = await new ErdoClient().listDatasetPurposes({ scope });
|
|
4960
4997
|
if (purposes.length === 0) {
|
|
4961
|
-
console.error("(no dataset purposes established yet)");
|
|
4998
|
+
console.error(message ? `(${message})` : "(no dataset purposes established yet)");
|
|
4962
4999
|
return;
|
|
4963
5000
|
}
|
|
4964
|
-
for (const p of purposes)
|
|
5001
|
+
for (const p of purposes) {
|
|
5002
|
+
const target = p.established_by_dataset_slug ?? p.established_by_dataset_id;
|
|
5003
|
+
const count = `${p.dataset_count} dataset${p.dataset_count === 1 ? "" : "s"}`;
|
|
4965
5004
|
console.log(
|
|
4966
|
-
`${p.
|
|
5005
|
+
scope === "managed" ? `${p.organization_slug ?? "-"} ${p.purpose} ${target} ${count} ${p.description}` : `${p.purpose} ${target} ${count} ${p.description}`
|
|
4967
5006
|
);
|
|
5007
|
+
}
|
|
4968
5008
|
} catch (e) {
|
|
4969
5009
|
fail(e);
|
|
4970
5010
|
}
|