@erdoai/cli 0.46.0 → 0.48.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 +140 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -246,6 +246,9 @@ var ErdoClient = class {
246
246
  listOrganizations() {
247
247
  return this.request("GET", "/v1/organizations");
248
248
  }
249
+ createOrganization(input) {
250
+ return this.request("POST", "/v1/organizations", input);
251
+ }
249
252
  // --- API tokens (user credentials; default_org is only the header-absent fallback) ---
250
253
  createToken(input) {
251
254
  return this.request("POST", "/v1/tokens", input);
@@ -281,6 +284,28 @@ var ErdoClient = class {
281
284
  createManagerKey() {
282
285
  return this.request("POST", "/v1/manager-key");
283
286
  }
287
+ // --- Custom page domains: the branded hostnames the org's pages serve from ---
288
+ listCustomDomains() {
289
+ return this.request("GET", "/v1/custom-domains");
290
+ }
291
+ createCustomDomain(domain) {
292
+ return this.request("POST", "/v1/custom-domains", { domain });
293
+ }
294
+ deleteCustomDomain(domain) {
295
+ return this.request(
296
+ "DELETE",
297
+ `/v1/custom-domains/${encodeURIComponent(domain)}`
298
+ );
299
+ }
300
+ // Moves a domain to another org you also administer, keeping its CDN hostname
301
+ // and certificate — the cutover is atomic, with no re-validation downtime.
302
+ transferCustomDomain(domain, targetOrg) {
303
+ return this.request(
304
+ "POST",
305
+ `/v1/custom-domains/${encodeURIComponent(domain)}/transfer`,
306
+ { target_org: targetOrg }
307
+ );
308
+ }
284
309
  // Run a read-only HogQL query against the org's page-analytics events. Rows are
285
310
  // positional per columns; enabled:false means page analytics is off for the org
286
311
  // (not zero traffic). A rejected query surfaces PostHog's message as the error.
@@ -662,6 +687,8 @@ var ErdoClient = class {
662
687
  listDatasets(params) {
663
688
  const q = new URLSearchParams();
664
689
  if (params?.class) q.set("class", params.class);
690
+ if (params?.limit !== void 0) q.set("limit", String(params.limit));
691
+ if (params?.offset !== void 0) q.set("offset", String(params.offset));
665
692
  const qs = q.toString();
666
693
  return this.request("GET", `/v1/datasets${qs ? `?${qs}` : ""}`);
667
694
  }
@@ -866,6 +893,8 @@ var ErdoClient = class {
866
893
  if (opts.status) params.set("status", opts.status);
867
894
  if (opts.limit) params.set("limit", String(opts.limit));
868
895
  if (opts.workstream_slug) params.set("workstream_slug", opts.workstream_slug);
896
+ if (opts.subject_resource_type) params.set("subject_resource_type", opts.subject_resource_type);
897
+ if (opts.subject_resource_id) params.set("subject_resource_id", opts.subject_resource_id);
869
898
  const qs = params.toString();
870
899
  return this.request(
871
900
  "GET",
@@ -1409,6 +1438,27 @@ org.command("use [idOrSlug]").description("Set the active org for the current ac
1409
1438
  fail(e);
1410
1439
  }
1411
1440
  });
1441
+ org.command("create <name>").description("Create a new organization").option("--slug <slug>", "desired slug (generated from the name if omitted, trying numeric suffixes on collision)").option("--use", "switch the active org to the new one after creation").action(async (name, opts) => {
1442
+ try {
1443
+ const o = await new ErdoClient().createOrganization({
1444
+ name,
1445
+ slug: opts.slug,
1446
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
1447
+ });
1448
+ console.log(`Created org: ${o.name}`);
1449
+ console.log(`slug: ${o.slug}
1450
+ id: ${o.id}`);
1451
+ if (opts.use) {
1452
+ updateActiveAccount({ organizationId: o.id, organizationName: o.name });
1453
+ console.log(`Active org: ${o.name}`);
1454
+ } else {
1455
+ process.stderr.write(`Switch to it with: erdo org use ${o.slug}
1456
+ `);
1457
+ }
1458
+ } catch (e) {
1459
+ fail(e);
1460
+ }
1461
+ });
1412
1462
  org.command("autonomy [mode]").description("Show or set the org's engine-autonomy mode (autopilot | propose | strict)").action(async (mode) => {
1413
1463
  try {
1414
1464
  const api = new ErdoClient();
@@ -2379,27 +2429,34 @@ runsCmd.command("get <id>").description("Show an agent run (status, output, trac
2379
2429
  }
2380
2430
  });
2381
2431
  var approvalsCmd = program.command("approvals").description("List and decide approval requests (actions agents paused on)");
2382
- approvalsCmd.command("list").description("List approval requests, optionally filtered by status").option("-s, --status <status>", "pending | approved | rejected | expired (default: all)").option("-l, --limit <n>", "max requests", (v) => parseInt(v, 10)).option("--workstream <slug>", "only approvals for this workstream").option("--json", "output raw JSON").action(async (opts) => {
2383
- try {
2384
- const res = await new ErdoClient().listApprovals({
2385
- status: opts.status,
2386
- limit: opts.limit,
2387
- workstream_slug: opts.workstream
2388
- });
2389
- if (opts.json) {
2390
- print(res);
2391
- return;
2392
- }
2393
- for (const r of res.requests) {
2394
- const occ = r.occurrence_count && r.occurrence_count > 1 ? ` \xD7${r.occurrence_count}` : "";
2395
- console.log(
2396
- `${r.id} ${r.status} ${r.action_key} ${r.action_display} ${r.created_at}${occ}`
2397
- );
2432
+ approvalsCmd.command("list").description("List approval requests, optionally filtered by status").option("-s, --status <status>", "pending | approved | rejected | expired (default: all)").option("-l, --limit <n>", "max requests", (v) => parseInt(v, 10)).option("--workstream <slug>", "only approvals for this workstream").option(
2433
+ "--subject-type <type>",
2434
+ "only approvals whose subject resource is of this type (e.g. paid_media_campaign)"
2435
+ ).option("--subject-id <id>", "only approvals whose subject resource has this id").option("--json", "output raw JSON").action(
2436
+ async (opts) => {
2437
+ try {
2438
+ const res = await new ErdoClient().listApprovals({
2439
+ status: opts.status,
2440
+ limit: opts.limit,
2441
+ workstream_slug: opts.workstream,
2442
+ subject_resource_type: opts.subjectType,
2443
+ subject_resource_id: opts.subjectId
2444
+ });
2445
+ if (opts.json) {
2446
+ print(res);
2447
+ return;
2448
+ }
2449
+ for (const r of res.requests) {
2450
+ const occ = r.occurrence_count && r.occurrence_count > 1 ? ` \xD7${r.occurrence_count}` : "";
2451
+ console.log(
2452
+ `${r.id} ${r.status} ${r.action_key} ${r.action_display} ${r.created_at}${occ}`
2453
+ );
2454
+ }
2455
+ } catch (e) {
2456
+ fail(e);
2398
2457
  }
2399
- } catch (e) {
2400
- fail(e);
2401
2458
  }
2402
- });
2459
+ );
2403
2460
  approvalsCmd.command("decide <id>").description("Approve or reject a pending approval request").option("--approve", "approve the request").option("--reject", "reject the request").option(
2404
2461
  "--scope <scope>",
2405
2462
  "once (default) | always_this_job | always_this_workstream | always_org | always_user",
@@ -2954,15 +3011,76 @@ pagesCmd.command("review-result <id>").description("Read a page review by id \u2
2954
3011
  fail(e);
2955
3012
  }
2956
3013
  });
3014
+ var domainsCmd = pagesCmd.command("domains").description("Custom domains the org's published pages serve from");
3015
+ domainsCmd.command("list").description("List the org's custom domains with live lifecycle status").action(async () => {
3016
+ try {
3017
+ const { domains } = await new ErdoClient().listCustomDomains();
3018
+ if (domains.length === 0) {
3019
+ console.log("No custom domains. Register one with: erdo pages domains add <domain>");
3020
+ return;
3021
+ }
3022
+ for (const d of domains) {
3023
+ const checked = d.last_checked_at ? `checked ${d.last_checked_at}` : "never checked";
3024
+ const reason = d.error_reason ? ` ${d.error_reason}` : "";
3025
+ console.log(`${d.domain} ${d.status} ${checked}${reason}`);
3026
+ }
3027
+ } catch (e) {
3028
+ fail(e);
3029
+ }
3030
+ });
3031
+ domainsCmd.command("add <domain>").description("Register a custom domain (a direct subdomain, e.g. pages.acme.com) and print the DNS records to create").action(async (domain) => {
3032
+ try {
3033
+ const d = await new ErdoClient().createCustomDomain(domain);
3034
+ console.log(`${d.domain}: ${d.status}`);
3035
+ if (d.dns_records.length > 0) {
3036
+ process.stderr.write("Create these records at the domain's DNS provider:\n");
3037
+ for (const r of d.dns_records) {
3038
+ console.log(`${r.type} ${r.name} ${r.value}`);
3039
+ }
3040
+ }
3041
+ } catch (e) {
3042
+ fail(e);
3043
+ }
3044
+ });
3045
+ domainsCmd.command("remove <domain>").description("Remove a custom domain registration (stops it serving pages)").action(async (domain) => {
3046
+ try {
3047
+ const res = await new ErdoClient().deleteCustomDomain(domain);
3048
+ console.log(`removed: ${res.domain}`);
3049
+ } catch (e) {
3050
+ fail(e);
3051
+ }
3052
+ });
3053
+ domainsCmd.command("transfer <domain> <targetOrg>").description("Move a domain to another org you administer, keeping its certificate (no serving downtime)").action(async (domain, targetOrg) => {
3054
+ try {
3055
+ const d = await new ErdoClient().transferCustomDomain(domain, targetOrg);
3056
+ console.log(`${d.domain} now belongs to ${targetOrg} (status: ${d.status})`);
3057
+ } catch (e) {
3058
+ fail(e);
3059
+ }
3060
+ });
2957
3061
  var datasetsCmd = program.command("datasets").description("Datasets");
2958
3062
  datasetsCmd.command("list").description("List datasets").option(
2959
3063
  "--class <class>",
2960
3064
  "filter by structural class: 'table' or 'scratch', or 'all' to include scratch analysis by-products (default hides scratch)"
2961
- ).action(async (opts) => {
3065
+ ).option(
3066
+ "-l, --limit <n>",
3067
+ "max datasets to return (default 20, max 100)",
3068
+ (v) => parseInt(v, 10)
3069
+ ).option("--offset <n>", "skip the first n datasets (newest first)", (v) => parseInt(v, 10)).action(async (opts) => {
2962
3070
  try {
2963
- const { datasets } = await new ErdoClient().listDatasets({ class: opts.class });
3071
+ const { datasets, total } = await new ErdoClient().listDatasets({
3072
+ class: opts.class,
3073
+ limit: opts.limit,
3074
+ offset: opts.offset
3075
+ });
2964
3076
  for (const d of datasets)
2965
3077
  console.log(`${d.slug} ${d.type} ${d.status} ${d.class ?? "-"} ${d.name}`);
3078
+ const shown = (opts.offset ?? 0) + datasets.length;
3079
+ if (total > shown) {
3080
+ console.error(`(showing ${datasets.length} of ${total} \u2014 use --limit/--offset for more)`);
3081
+ } else {
3082
+ console.error(`(${total} dataset${total === 1 ? "" : "s"})`);
3083
+ }
2966
3084
  } catch (e) {
2967
3085
  fail(e);
2968
3086
  }
@@ -3157,7 +3275,7 @@ integrationsCmd.command("apps [query]").description("Search connectable apps (na
3157
3275
  fail(e);
3158
3276
  }
3159
3277
  });
3160
- integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass credentials with -c, or get a browser connect URL for OAuth apps").option("-c, --credential <key=value...>", "credential field (repeatable)", (v, acc) => acc.concat(v), []).option("-n, --name <name>", "display name for the connection").action(async (app, opts) => {
3278
+ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass its API key or other credentials with -c, or omit them to get a browser connect URL for OAuth apps").option("-c, --credential <key=value...>", "credential field, e.g. -c api_key=\u2026 (repeatable); rejected by OAuth apps, which have no credential to pass", (v, acc) => acc.concat(v), []).option("-n, --name <name>", "display name for the connection").action(async (app, opts) => {
3161
3279
  try {
3162
3280
  let credentials;
3163
3281
  if (opts.credential.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {