@erdoai/cli 0.45.0 → 0.46.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 +95 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -287,6 +287,13 @@ var ErdoClient = class {
|
|
|
287
287
|
queryPageAnalytics(query) {
|
|
288
288
|
return this.request("POST", "/v1/page-analytics/query", { query });
|
|
289
289
|
}
|
|
290
|
+
// Read which analytics destinations the org's published pages send visitor data
|
|
291
|
+
// to — the check that stops "this audience is big enough to retarget" being
|
|
292
|
+
// concluded from traffic no pixel was ever tagging. Read-only: destinations are
|
|
293
|
+
// Erdo-provisioned, never caller-set.
|
|
294
|
+
getPageTracking() {
|
|
295
|
+
return this.request("GET", "/v1/page-tracking");
|
|
296
|
+
}
|
|
290
297
|
listEvalSuites() {
|
|
291
298
|
return this.request("GET", "/v1/evals/suites");
|
|
292
299
|
}
|
|
@@ -2397,20 +2404,57 @@ approvalsCmd.command("decide <id>").description("Approve or reject a pending app
|
|
|
2397
2404
|
"--scope <scope>",
|
|
2398
2405
|
"once (default) | always_this_job | always_this_workstream | always_org | always_user",
|
|
2399
2406
|
"once"
|
|
2400
|
-
).
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2407
|
+
).option(
|
|
2408
|
+
"--option <n>",
|
|
2409
|
+
"for standing (non-once) approvals: use the request's Nth scope option (1-based) as the policy's parameter constraints \u2014 list them with `erdo approvals list --json`",
|
|
2410
|
+
(v) => parseInt(v, 10)
|
|
2411
|
+
).option(
|
|
2412
|
+
"--constraints <json>",
|
|
2413
|
+
`explicit parameter constraints for the standing policy, e.g. '{"spreadsheet_id":{"values":["..."]}}'`
|
|
2414
|
+
).action(
|
|
2415
|
+
async (id, opts) => {
|
|
2416
|
+
try {
|
|
2417
|
+
if (opts.approve === opts.reject) {
|
|
2418
|
+
throw new Error("specify exactly one of --approve or --reject");
|
|
2419
|
+
}
|
|
2420
|
+
if (opts.option !== void 0 && opts.constraints !== void 0) {
|
|
2421
|
+
throw new Error("specify at most one of --option or --constraints");
|
|
2422
|
+
}
|
|
2423
|
+
const client = new ErdoClient();
|
|
2424
|
+
let constraints;
|
|
2425
|
+
if (opts.constraints !== void 0) {
|
|
2426
|
+
try {
|
|
2427
|
+
constraints = JSON.parse(opts.constraints);
|
|
2428
|
+
} catch {
|
|
2429
|
+
throw new Error(
|
|
2430
|
+
`--constraints must be JSON, e.g. '{"spreadsheet_id":{"values":["abc"]}}'`
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
} else if (opts.option !== void 0) {
|
|
2434
|
+
const res2 = await client.listApprovals({ status: "pending" });
|
|
2435
|
+
const req = res2.requests.find((r) => r.id === id);
|
|
2436
|
+
if (!req) {
|
|
2437
|
+
throw new Error(`pending approval ${id} not found`);
|
|
2438
|
+
}
|
|
2439
|
+
const options = req.scope_options ?? [];
|
|
2440
|
+
if (opts.option < 1 || opts.option > options.length) {
|
|
2441
|
+
const listing = options.length ? options.map((o, i) => ` ${i + 1}. ${o.label}`).join("\n") : " (none \u2014 pass --constraints instead)";
|
|
2442
|
+
throw new Error(`--option must be 1..${options.length}; available options:
|
|
2443
|
+
${listing}`);
|
|
2444
|
+
}
|
|
2445
|
+
constraints = options[opts.option - 1].constraints;
|
|
2446
|
+
}
|
|
2447
|
+
const res = await client.decideApproval(id, {
|
|
2448
|
+
decision: opts.approve ? "approved" : "rejected",
|
|
2449
|
+
scope: opts.scope,
|
|
2450
|
+
parameter_constraints: constraints
|
|
2451
|
+
});
|
|
2452
|
+
console.log(res.message);
|
|
2453
|
+
} catch (e) {
|
|
2454
|
+
fail(e);
|
|
2404
2455
|
}
|
|
2405
|
-
const res = await new ErdoClient().decideApproval(id, {
|
|
2406
|
-
decision: opts.approve ? "approved" : "rejected",
|
|
2407
|
-
scope: opts.scope
|
|
2408
|
-
});
|
|
2409
|
-
console.log(res.message);
|
|
2410
|
-
} catch (e) {
|
|
2411
|
-
fail(e);
|
|
2412
2456
|
}
|
|
2413
|
-
|
|
2457
|
+
);
|
|
2414
2458
|
var attnCmd = program.command("attention").description("The attention feed \u2014 digests, choices, escalations awaiting a human");
|
|
2415
2459
|
attnCmd.command("list").description("List attention items").option("--status <status...>", "open | answered | dismissed | expired").option("--open", "shorthand for --status open").option("--engine-actions", "only engine-generated items").option("-n, --limit <n>", "max items", (v) => parseInt(v, 10)).option("--offset <n>", "pagination offset", (v) => parseInt(v, 10)).action(
|
|
2416
2460
|
async (opts) => {
|
|
@@ -3019,7 +3063,7 @@ function printAnalyticsTable(columns, rows) {
|
|
|
3019
3063
|
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
3020
3064
|
for (const r of rows) console.log(line(columns.map((_, i) => cell(r[i]))));
|
|
3021
3065
|
}
|
|
3022
|
-
var analytics = program.command("analytics").description("
|
|
3066
|
+
var analytics = program.command("analytics").description("Page analytics \u2014 what is tracking your published pages, and how they perform with real visitors");
|
|
3023
3067
|
analytics.command("query <hogql>").description("Run a read-only HogQL query against this org's page-analytics events").option("--json", "print the raw JSON result instead of a table").action(async (hogql, opts) => {
|
|
3024
3068
|
try {
|
|
3025
3069
|
const res = await new ErdoClient().queryPageAnalytics(hogql);
|
|
@@ -3038,6 +3082,44 @@ analytics.command("query <hogql>").description("Run a read-only HogQL query agai
|
|
|
3038
3082
|
fail(e);
|
|
3039
3083
|
}
|
|
3040
3084
|
});
|
|
3085
|
+
var TRACKING_KINDS = [
|
|
3086
|
+
{ kind: "session_analytics", what: "heatmaps, session replay, and the events behind `erdo analytics query`" },
|
|
3087
|
+
{ kind: "google_analytics", what: "GA4 measurement" },
|
|
3088
|
+
{ kind: "meta_pixel", what: "cookies visitors so Meta retargeting audiences can be built" }
|
|
3089
|
+
];
|
|
3090
|
+
analytics.command("tracking").description("Show which analytics destinations this org's published pages send visitor data to").option("--json", "print the raw JSON result instead of a table").action(async (opts) => {
|
|
3091
|
+
try {
|
|
3092
|
+
const res = await new ErdoClient().getPageTracking();
|
|
3093
|
+
if (opts.json) {
|
|
3094
|
+
print(res);
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
const destinations = res.destinations ?? [];
|
|
3098
|
+
const byKind = new Map(destinations.map((d) => [d.kind, d]));
|
|
3099
|
+
const rows = TRACKING_KINDS.map(({ kind, what }) => {
|
|
3100
|
+
const d = byKind.get(kind);
|
|
3101
|
+
const status = d === void 0 ? "not configured" : d.enabled ? "on" : "configured, off";
|
|
3102
|
+
return [kind, status, d?.public_id ?? "", d?.provider ?? "", what];
|
|
3103
|
+
});
|
|
3104
|
+
for (const d of destinations) {
|
|
3105
|
+
if (!TRACKING_KINDS.some((k) => k.kind === d.kind)) {
|
|
3106
|
+
rows.push([d.kind, d.enabled ? "on" : "configured, off", d.public_id ?? "", d.provider ?? "", ""]);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
printAnalyticsTable(["kind", "status", "public id", "provider", "what it does"], rows);
|
|
3110
|
+
console.log(`
|
|
3111
|
+
session replay input masking: ${res.mask_inputs ? "on" : "OFF \u2014 form values are recorded"}`);
|
|
3112
|
+
console.log(`consent: ${res.consent}${res.consent === "required" ? " (recording waits on a consent banner, so thin volume may be the gate, not the traffic)" : ""}`);
|
|
3113
|
+
if (!byKind.get("meta_pixel")?.enabled) {
|
|
3114
|
+
console.log(`
|
|
3115
|
+
No Meta pixel is firing on these pages, so no retargeting audience is accumulating \u2014 page traffic alone does not mean those visitors can be served ads.`);
|
|
3116
|
+
}
|
|
3117
|
+
console.log(`
|
|
3118
|
+
Erdo's own page-events beacon is provisioned per page at render time and is not listed above \u2014 it records independently of every vendor here.`);
|
|
3119
|
+
} catch (e) {
|
|
3120
|
+
fail(e);
|
|
3121
|
+
}
|
|
3122
|
+
});
|
|
3041
3123
|
var OPERATORS = ["equals", "not_equals", "greater_than", "less_than", "contains", "not_contains", "between"];
|
|
3042
3124
|
function parseCondition(s) {
|
|
3043
3125
|
const m = s.trim().match(/^(\S+)\s+(\S+)\s+([\s\S]+)$/);
|