@erdoai/cli 0.29.0 → 0.31.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 +105 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -203,6 +203,16 @@ var ErdoClient = class {
|
|
|
203
203
|
listOrganizations() {
|
|
204
204
|
return this.request("GET", "/v1/organizations");
|
|
205
205
|
}
|
|
206
|
+
// --- API tokens (user credentials; default_org is only the header-absent fallback) ---
|
|
207
|
+
createToken(input) {
|
|
208
|
+
return this.request("POST", "/v1/tokens", input);
|
|
209
|
+
}
|
|
210
|
+
listTokens() {
|
|
211
|
+
return this.request("GET", "/v1/tokens");
|
|
212
|
+
}
|
|
213
|
+
revokeToken(id) {
|
|
214
|
+
return this.request("DELETE", `/v1/tokens/${encodeURIComponent(id)}`);
|
|
215
|
+
}
|
|
206
216
|
getAutonomyMode() {
|
|
207
217
|
return this.request("GET", "/v1/autonomy-mode");
|
|
208
218
|
}
|
|
@@ -696,6 +706,14 @@ var ErdoClient = class {
|
|
|
696
706
|
deployPage(input) {
|
|
697
707
|
return this.request("POST", "/v1/pages", input);
|
|
698
708
|
}
|
|
709
|
+
// Start a review of external page URLs with the real landing critic; returns a
|
|
710
|
+
// review id immediately (poll getPageReview for the typed findings).
|
|
711
|
+
reviewPages(urls) {
|
|
712
|
+
return this.request("POST", "/v1/page-reviews", { urls });
|
|
713
|
+
}
|
|
714
|
+
getPageReview(id) {
|
|
715
|
+
return this.request("GET", `/v1/page-reviews/${encodeURIComponent(id)}`);
|
|
716
|
+
}
|
|
699
717
|
updatePage(pageID, input) {
|
|
700
718
|
return this.request("PUT", `/v1/pages/${encodeURIComponent(pageID)}`, input);
|
|
701
719
|
}
|
|
@@ -1158,6 +1176,55 @@ org.command("autonomy [mode]").description("Show or set the org's engine-autonom
|
|
|
1158
1176
|
fail(e);
|
|
1159
1177
|
}
|
|
1160
1178
|
});
|
|
1179
|
+
var tokenCmd = program.command("token").description("Manage your API tokens (account-level credentials)");
|
|
1180
|
+
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)).option("--org <idOrSlug>", "the token's default org (defaults to your active org; must be one you belong to)").action(async (opts) => {
|
|
1181
|
+
try {
|
|
1182
|
+
const res = await new ErdoClient().createToken({
|
|
1183
|
+
name: opts.name,
|
|
1184
|
+
expires_in_days: opts.expiresDays ?? 30,
|
|
1185
|
+
organization_id: opts.org
|
|
1186
|
+
});
|
|
1187
|
+
process.stderr.write(
|
|
1188
|
+
"Store this token now \u2014 it is shown only once and cannot be retrieved again.\n"
|
|
1189
|
+
);
|
|
1190
|
+
console.log(res.token);
|
|
1191
|
+
process.stderr.write(
|
|
1192
|
+
`
|
|
1193
|
+
id: ${res.id}
|
|
1194
|
+
hint: ${res.hint}
|
|
1195
|
+
expires: ${res.expires_at}
|
|
1196
|
+
default org: ${res.default_org ? `${res.default_org.name} (${res.default_org.id})` : "(none)"}
|
|
1197
|
+
This token acts as you in any org you belong to; pass --org on a command to target another.
|
|
1198
|
+
`
|
|
1199
|
+
);
|
|
1200
|
+
} catch (e) {
|
|
1201
|
+
fail(e);
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
tokenCmd.command("list").description("List your API tokens (never shows the secret)").action(async () => {
|
|
1205
|
+
try {
|
|
1206
|
+
const { tokens } = await new ErdoClient().listTokens();
|
|
1207
|
+
if (tokens.length === 0) {
|
|
1208
|
+
console.log("No API tokens. Create one with: erdo token create --name <name>");
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
for (const t of tokens) {
|
|
1212
|
+
const org2 = t.default_org ? t.default_org.name || t.default_org.id : "-";
|
|
1213
|
+
const used = t.last_used_at ?? "never";
|
|
1214
|
+
console.log(`${t.id} ${t.name || "-"} ${t.hint} default-org:${org2} expires:${t.expires_at} used:${used}`);
|
|
1215
|
+
}
|
|
1216
|
+
} catch (e) {
|
|
1217
|
+
fail(e);
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
tokenCmd.command("revoke <id>").description("Revoke an API token by id").action(async (id) => {
|
|
1221
|
+
try {
|
|
1222
|
+
await new ErdoClient().revokeToken(id);
|
|
1223
|
+
console.log(`revoked: ${id}`);
|
|
1224
|
+
} catch (e) {
|
|
1225
|
+
fail(e);
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1161
1228
|
var evalCmd = program.command("eval").description("Run and manage evals");
|
|
1162
1229
|
evalCmd.command("suites").description("List eval suites in the active org (+ global)").action(async () => {
|
|
1163
1230
|
try {
|
|
@@ -2241,6 +2308,41 @@ pagesCmd.command("get <id>").description("Show an artifact").action(async (id) =
|
|
|
2241
2308
|
fail(e);
|
|
2242
2309
|
}
|
|
2243
2310
|
});
|
|
2311
|
+
pagesCmd.command("review <url...>").description(
|
|
2312
|
+
"Review external landing page URLs with Erdo's real conversion critic \u2014 typed defects (severity, issue, why it costs conversions, evidence, fix) plus strengths, judged from fold + full-page captures on desktop and mobile. Returns a review id; use --wait to poll and print the findings."
|
|
2313
|
+
).option("--wait", "poll until the review is ready, then print the findings").action(async (urls, opts) => {
|
|
2314
|
+
try {
|
|
2315
|
+
const client = new ErdoClient();
|
|
2316
|
+
const started = await client.reviewPages(urls);
|
|
2317
|
+
if (!opts.wait) {
|
|
2318
|
+
print(started);
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const deadline = Date.now() + 15 * 60 * 1e3;
|
|
2322
|
+
process.stderr.write("reviewing");
|
|
2323
|
+
while (Date.now() < deadline) {
|
|
2324
|
+
const res = await client.getPageReview(started.id);
|
|
2325
|
+
if (res.status === "ready" || res.status === "failed") {
|
|
2326
|
+
process.stderr.write("\n");
|
|
2327
|
+
print(res);
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
process.stderr.write(".");
|
|
2331
|
+
await sleep2(5e3);
|
|
2332
|
+
}
|
|
2333
|
+
process.stderr.write("\n");
|
|
2334
|
+
print(await client.getPageReview(started.id));
|
|
2335
|
+
} catch (e) {
|
|
2336
|
+
fail(e);
|
|
2337
|
+
}
|
|
2338
|
+
});
|
|
2339
|
+
pagesCmd.command("review-result <id>").description("Read a page review by id \u2014 its status and, once ready, the typed per-page findings").action(async (id) => {
|
|
2340
|
+
try {
|
|
2341
|
+
print(await new ErdoClient().getPageReview(id));
|
|
2342
|
+
} catch (e) {
|
|
2343
|
+
fail(e);
|
|
2344
|
+
}
|
|
2345
|
+
});
|
|
2244
2346
|
var datasetsCmd = program.command("datasets").description("Datasets");
|
|
2245
2347
|
datasetsCmd.command("list").description("List datasets").action(async () => {
|
|
2246
2348
|
try {
|
|
@@ -2344,11 +2446,11 @@ analytics.command("query <hogql>").description("Run a read-only HogQL query agai
|
|
|
2344
2446
|
fail(e);
|
|
2345
2447
|
}
|
|
2346
2448
|
});
|
|
2347
|
-
var OPERATORS = ["equals", "not_equals", "greater_than", "less_than", "contains", "between"];
|
|
2449
|
+
var OPERATORS = ["equals", "not_equals", "greater_than", "less_than", "contains", "not_contains", "between"];
|
|
2348
2450
|
function parseCondition(s) {
|
|
2349
2451
|
const m = s.trim().match(/^(\S+)\s+(\S+)\s+([\s\S]+)$/);
|
|
2350
2452
|
if (!m) {
|
|
2351
|
-
fail(new Error(`invalid --where ${JSON.stringify(s)}; expected '<column> <operator> <value>', e.g. 'email
|
|
2453
|
+
fail(new Error(`invalid --where ${JSON.stringify(s)}; expected '<column> <operator> <value>', e.g. 'email not_contains @example.com'`));
|
|
2352
2454
|
}
|
|
2353
2455
|
const [, column, operator, value] = m;
|
|
2354
2456
|
if (!OPERATORS.includes(operator)) {
|
|
@@ -2443,7 +2545,7 @@ pipelinesCmd.command("get <slug>").description("Show one event pipeline \u2014 s
|
|
|
2443
2545
|
});
|
|
2444
2546
|
filterCmd.command("add <slug>").description("Add a filter to a dataset (default-on unless --no-default)").requiredOption(
|
|
2445
2547
|
"--where <condition>",
|
|
2446
|
-
"a '<column> <operator> <value>' condition (repeatable, AND-combined),
|
|
2548
|
+
"a '<column> <operator> <value>' condition (repeatable, AND-combined). A filter KEEPS matching rows, so exclude by matching what you want to keep: --where 'email not_contains @example.com' (label 'Exclude test submissions') hides the test rows; --where 'email contains @acme.com' would keep ONLY @acme.com rows. Operators: " + OPERATORS.join(", ") + " (for 'between' use 'col between 10,100')",
|
|
2447
2549
|
collect,
|
|
2448
2550
|
[]
|
|
2449
2551
|
).option("--name <name>", "optional human-readable label, e.g. 'Exclude test submissions'").option(
|