@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.
Files changed (2) hide show
  1. package/dist/index.js +95 -13
  2. 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
- ).action(async (id, opts) => {
2401
- try {
2402
- if (opts.approve === opts.reject) {
2403
- throw new Error("specify exactly one of --approve or --reject");
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("Query page analytics (how published pages perform with real visitors)");
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]+)$/);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.45.0",
3
+ "version": "0.46.0",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {