@audienti/cli 0.1.73 → 0.1.74

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/CHANGELOG.md CHANGED
@@ -4,10 +4,17 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.74] - 2026-09-27
8
+
7
9
  ### Added
8
10
 
11
+ - Add `accounts show`, `company-rules show`, and `linkedin-lookups <kind>` so account details, single company rules, and LinkedIn targeting values are reachable from the CLI.
9
12
  - Add Motion-owned discovery signal tracking to `motions show` and the `motions signals` command, including attribution holds, source counts, and retained Agent provenance.
10
13
 
14
+ ### Fixed
15
+
16
+ - LinkedIn lookups accept API tokens and prefixed ICP ids, so CLI lookups no longer fail with an authentication error.
17
+
11
18
  ## [0.1.73] - 2026-09-26
12
19
 
13
20
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -40,6 +40,14 @@ export class AudientiClient {
40
40
  return this.requestJson("/api/v1/accounts.json");
41
41
  }
42
42
 
43
+ account(accountId) {
44
+ return this.requestJson(accountPath(accountId, []));
45
+ }
46
+
47
+ linkedinLookup(kind, query = {}) {
48
+ return this.requestJson(apiPath(["linkedin_lookups", kind], query));
49
+ }
50
+
43
51
  mcp(message) {
44
52
  return this.requestJson("/mcp", {
45
53
  method: "POST",
@@ -187,6 +195,10 @@ export class AudientiClient {
187
195
  return this.requestJson(accountPath(accountId, ["company_rules"]));
188
196
  }
189
197
 
198
+ companyRule(accountId, ruleId) {
199
+ return this.requestJson(accountPath(accountId, ["company_rules", ruleId]));
200
+ }
201
+
190
202
  createCompanyRule(accountId, body) {
191
203
  return this.requestJson(accountPath(accountId, ["company_rules"]), {
192
204
  method: "POST",
@@ -819,11 +831,13 @@ export class AudientiClient {
819
831
  }
820
832
 
821
833
  function accountPath(accountId, segments, query = {}) {
834
+ return apiPath(["accounts", accountId, ...segments], query);
835
+ }
836
+
837
+ function apiPath(segments, query = {}) {
822
838
  const encodedSegments = [
823
839
  "api",
824
840
  "v1",
825
- "accounts",
826
- accountId,
827
841
  ...segments
828
842
  ].map((segment) => encodeURIComponent(segment));
829
843
  const searchParams = new URLSearchParams();
package/src/cli.js CHANGED
@@ -112,6 +112,18 @@ const COMPANY_RULES_CREATE_USAGE = "Usage: audienti company-rules create (--link
112
112
  const COMPANY_RULES_UPDATE_USAGE = "Usage: audienti company-rules update <rule_id> [--linkedin-url <url>] [--domain <domain>] [--disposition <monitor|nurture|not_fit|reject>] [--name <text>] [--user <account_user_id|email|me|none>] [--note <text>] [--json] [--account <acct_id>]";
113
113
  const COMPANY_RULES_REMOVE_USAGE = "Usage: audienti company-rules remove <rule_id> [--json] [--account <acct_id>]";
114
114
  const COMPANY_RULES_APPLY_USAGE = "Usage: audienti company-rules apply (<rule_id>|--all) [--json] [--account <acct_id>]";
115
+ const COMPANY_RULES_SHOW_USAGE = "Usage: audienti company-rules show <rule_id> [--json] [--account <acct_id>]";
116
+ const ACCOUNTS_SHOW_USAGE = "Usage: audienti accounts show [<acct_id>] [--json] [--account <acct_id>]";
117
+ const LINKEDIN_LOOKUPS_USAGE = "Usage: audienti linkedin-lookups <company-sizes|company-types|functions|industries|job-titles|locations|seniorities> [--query <text>] [--icp <icp_id>] [--json] [--account <acct_id>]";
118
+ const LINKEDIN_LOOKUP_KINDS = new Map([
119
+ ["company-sizes", { path: "company_sizes", query: "none", icp: true }],
120
+ ["company-types", { path: "company_types", query: "none", icp: true }],
121
+ ["functions", { path: "functions", query: "required", icp: true }],
122
+ ["industries", { path: "industries", query: "required", icp: false }],
123
+ ["job-titles", { path: "job_titles", query: "required", icp: true }],
124
+ ["locations", { path: "locations", query: "required", icp: true }],
125
+ ["seniorities", { path: "seniorities", query: "optional", icp: true }]
126
+ ]);
115
127
  const TASKS_LIST_USAGE = "Usage: audienti tasks list [--status <open|completed>] [--limit <n>] [--json] [--account <acct_id>]";
116
128
  const TASKS_ADD_USAGE = "Usage: audienti tasks add --title <text> --due <time> [--prospect <prsp_id>] [--list <list_id>] [--assigned-user <id|me>] [--notes <text>] [--json] [--account <acct_id>]";
117
129
  const TASKS_COMPLETE_USAGE = "Usage: audienti tasks complete <ptsk_id> [--json] [--account <acct_id>]";
@@ -265,6 +277,8 @@ async function dispatch(argv, context) {
265
277
  if (normalizedResource === "update" && action === "check") return updateCheck(rest, context);
266
278
  if (normalizedResource === "accounts" && action === "list") return accountsList(rest, context, { accountOverride });
267
279
  if (normalizedResource === "accounts" && action === "select") return accountsSelect(rest, context);
280
+ if (normalizedResource === "accounts" && action === "show") return accountsShow(rest, context, { accountOverride });
281
+ if (normalizedResource === "linkedin-lookups") return linkedinLookups(action, rest, context, { accountOverride });
268
282
  if (normalizedResource === "users" && action === "list") return usersList(rest, context, { accountOverride });
269
283
  if (normalizedResource === "users" && action === "select") return usersSelect(rest, context, { accountOverride });
270
284
  if (normalizedResource === "users" && action === "activity") return usersActivity(rest, context, { accountOverride });
@@ -291,6 +305,7 @@ async function dispatch(argv, context) {
291
305
  if (normalizedResource === "dnc" && action === "import") return dncImport(rest, context, { accountOverride });
292
306
  if (normalizedResource === "dnc" && ["remove", "delete"].includes(action)) return dncRemove(rest, context, { accountOverride });
293
307
  if (normalizedResource === "company-rules" && action === "list") return companyRulesList(rest, context, { accountOverride });
308
+ if (normalizedResource === "company-rules" && action === "show") return companyRulesShow(rest, context, { accountOverride });
294
309
  if (normalizedResource === "company-rules" && action === "create") return companyRulesCreate(rest, context, { accountOverride });
295
310
  if (normalizedResource === "company-rules" && action === "update") return companyRulesUpdate(rest, context, { accountOverride });
296
311
  if (normalizedResource === "company-rules" && ["remove", "delete"].includes(action)) return companyRulesRemove(rest, context, { accountOverride });
@@ -482,6 +497,7 @@ function normalizeResource(resource) {
482
497
  if (resource === "principals") return "users";
483
498
  if (resource === "writers") return "writer";
484
499
  if (resource === "company_rules" || resource === "company-rules") return "company-rules";
500
+ if (resource === "linkedin_lookups" || resource === "linkedin-lookups") return "linkedin-lookups";
485
501
  if (resource === "social_cookie" || resource === "social_cookies") return "social-cookies";
486
502
  return resource === "plays" ? "motions" : resource;
487
503
  }
@@ -802,6 +818,64 @@ async function accountsList(args, context, { accountOverride } = {}) {
802
818
  }
803
819
  }
804
820
 
821
+ async function accountsShow(args, context, { accountOverride } = {}) {
822
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
823
+ if (positionals.length > 1) throw new CommandError(ACCOUNTS_SHOW_USAGE);
824
+
825
+ const config = await requireAuthenticatedConfig(context);
826
+ const accountId = positionals[0] || accountOverride || config.accountId;
827
+ if (!accountId) {
828
+ throw new CommandError("No active account. Run `audienti accounts select <acct_id>`, pass `--account <acct_id>`, or give an account id.");
829
+ }
830
+
831
+ const account = await clientFromConfig(config, context).account(accountId);
832
+ if (values.json) return writeJson(context.stdout, account);
833
+
834
+ writeLine(context.stdout, `Account: ${display(account?.name)} (${display(account?.prefix_id || accountId)})`);
835
+ writeLine(context.stdout, `Personal: ${account?.personal ? "yes" : "no"}`);
836
+ writeLine(context.stdout, `Members: ${Array.isArray(account?.account_users) ? account.account_users.length : 0}`);
837
+ writeLine(context.stdout, `Created: ${display(account?.created_at, "-")}`);
838
+ }
839
+
840
+ async function linkedinLookups(kind, args, context, { accountOverride } = {}) {
841
+ const lookup = LINKEDIN_LOOKUP_KINDS.get(String(kind || "").replaceAll("_", "-"));
842
+ if (!lookup) throw new CommandError(LINKEDIN_LOOKUPS_USAGE);
843
+
844
+ const { values, positionals } = parseCommandArgs(args, {
845
+ ...jsonOptions(),
846
+ query: { type: "string" },
847
+ icp: { type: "string" }
848
+ });
849
+ const query = values.query?.trim();
850
+ const invalid = positionals.length > 0 ||
851
+ (lookup.query === "required" && !query) ||
852
+ (lookup.query === "none" && values.query !== undefined) ||
853
+ (!lookup.icp && values.icp !== undefined);
854
+ if (invalid) throw new CommandError(LINKEDIN_LOOKUPS_USAGE);
855
+
856
+ const config = await requireAuthenticatedConfig(context);
857
+ const params = { q: query };
858
+ if (values.icp) {
859
+ const accountId = accountOverride || config.accountId;
860
+ if (!accountId) {
861
+ throw new CommandError("No active account. Run `audienti accounts select <acct_id>` or pass `--account <acct_id>` to look up with --icp.");
862
+ }
863
+ params.icp_id = values.icp;
864
+ params.account_id = accountId;
865
+ }
866
+
867
+ const payload = await clientFromConfig(config, context).linkedinLookup(lookup.path, params);
868
+ if (values.json) return writeJson(context.stdout, payload);
869
+
870
+ const rows = Array.isArray(payload) ? payload : [];
871
+ if (rows.length === 0) return writeLine(context.stdout, "No results found.");
872
+
873
+ writeLine(context.stdout, "ID\tNAME\tSOURCE");
874
+ for (const row of rows) {
875
+ writeLine(context.stdout, [display(row.id, "-"), display(row.name), display(row.source, "-")].join("\t"));
876
+ }
877
+ }
878
+
805
879
  async function accountsSelect(args, context) {
806
880
  const { positionals } = parseCommandArgs(args, {});
807
881
  if (positionals.length !== 1) throw new CommandError("Usage: audienti accounts select <acct_id>");
@@ -1339,6 +1413,22 @@ async function companyRulesList(args, context, { accountOverride } = {}) {
1339
1413
  renderCompanyRules(payload, context);
1340
1414
  }
1341
1415
 
1416
+ async function companyRulesShow(args, context, { accountOverride } = {}) {
1417
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
1418
+ if (positionals.length !== 1) throw new CommandError(COMPANY_RULES_SHOW_USAGE);
1419
+
1420
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1421
+ const payload = await client.companyRule(accountId, positionals[0]);
1422
+ if (values.json) return writeJson(context.stdout, payload);
1423
+
1424
+ const rule = payload?.company_rule;
1425
+ writeLine(context.stdout, `Company rule ${display(rule?.name || rule?.domain || rule?.linkedin_company_identifier)} (${display(rule?.prefix_id || rule?.id)})`);
1426
+ writeLine(context.stdout, `Disposition: ${display(rule?.disposition)} | Scope: ${companyRuleScopeLabel(rule)} | Active: ${rule?.active ? "yes" : "no"}`);
1427
+ writeLine(context.stdout, `LinkedIn: ${display(rule?.linkedin_company_url || rule?.linkedin_company_identifier, "-")}`);
1428
+ writeLine(context.stdout, `Domain: ${display(rule?.domain, "-")}`);
1429
+ if (rule?.note) writeLine(context.stdout, `Note: ${rule.note}`);
1430
+ }
1431
+
1342
1432
  async function companyRulesCreate(args, context, { accountOverride } = {}) {
1343
1433
  const { values, positionals } = parseCommandArgs(args, companyRuleOptions());
1344
1434
  if (positionals.length > 0 || (!values["linkedin-url"] && !values.domain) || !values.disposition) {
@@ -8411,6 +8501,7 @@ const HELP_TOPICS = new Map([
8411
8501
  " Setup & identity",
8412
8502
  " audienti auth status",
8413
8503
  " audienti config list",
8504
+ " audienti accounts show [acct_id]",
8414
8505
  " audienti update check",
8415
8506
  " audienti setup play preflight",
8416
8507
  " audienti users list",
@@ -8498,9 +8589,11 @@ const HELP_TOPICS = new Map([
8498
8589
  " audienti icps add-tag <icp_id> <tag>",
8499
8590
  " audienti icps remove-tag <icp_id> <tag>",
8500
8591
  " audienti companies search --query <text>",
8592
+ " audienti linkedin-lookups <kind> [--query <text>]",
8501
8593
  " audienti dnc list",
8502
8594
  " audienti dnc add <email|citation_id|profile_url>",
8503
8595
  " audienti company-rules list",
8596
+ " audienti company-rules show <rule_id>",
8504
8597
  " audienti company-rules create (--linkedin-url <url> | --domain <domain>) --disposition <state>",
8505
8598
  "",
8506
8599
  " Writer",
@@ -8725,6 +8818,7 @@ const HELP_TOPICS = new Map([
8725
8818
  ["accounts", [
8726
8819
  "Usage:",
8727
8820
  " audienti accounts list [--json]",
8821
+ " audienti accounts show [<acct_id>] [--json]",
8728
8822
  " audienti accounts select <acct_id>",
8729
8823
  "",
8730
8824
  "Status: implemented",
@@ -8783,6 +8877,51 @@ const HELP_TOPICS = new Map([
8783
8877
  " audienti accounts list --json"
8784
8878
  ].join("\n")],
8785
8879
 
8880
+ ["accounts show", [
8881
+ "Usage:",
8882
+ ` ${ACCOUNTS_SHOW_USAGE.slice("Usage: ".length)}`,
8883
+ "",
8884
+ "Status: implemented",
8885
+ "",
8886
+ "API:",
8887
+ " GET /api/v1/accounts/:id.json",
8888
+ "",
8889
+ "Input shape:",
8890
+ " acct_id: string Account prefix id; defaults to --account or the selected account",
8891
+ "",
8892
+ "Output shape:",
8893
+ " id, prefix_id, name, personal, owner_id, account_users[], created_at, updated_at"
8894
+ ].join("\n")],
8895
+
8896
+ ["linkedin-lookups", [
8897
+ "Usage:",
8898
+ ` ${LINKEDIN_LOOKUPS_USAGE.slice("Usage: ".length)}`,
8899
+ "",
8900
+ "Status: implemented",
8901
+ "",
8902
+ "Purpose:",
8903
+ " Look up LinkedIn targeting values (the ids used in ICP filters).",
8904
+ "",
8905
+ "Kinds:",
8906
+ " job-titles, functions, locations --query required",
8907
+ " industries --query required; --icp not supported",
8908
+ " seniorities --query optional filter",
8909
+ " company-sizes, company-types no --query; returns the full list",
8910
+ "",
8911
+ "ICP option:",
8912
+ " --icp <icp_id> uses values saved on that ICP of the active account (or --account).",
8913
+ " For company-sizes, company-types and seniorities it also saves the list onto that ICP.",
8914
+ "",
8915
+ "API:",
8916
+ " GET /api/v1/linkedin_lookups/<kind>.json?q=<text>&icp_id=<icp_id>",
8917
+ "",
8918
+ "Output shape:",
8919
+ " [{ id, name, source? }]",
8920
+ "",
8921
+ "Example:",
8922
+ " audienti linkedin-lookups job-titles --query \"head of sales\" --json"
8923
+ ].join("\n")],
8924
+
8786
8925
  ["accounts select", [
8787
8926
  "Usage:",
8788
8927
  " audienti accounts select <acct_id>",
@@ -9413,6 +9552,7 @@ const HELP_TOPICS = new Map([
9413
9552
  ["company-rules", [
9414
9553
  "Usage:",
9415
9554
  " audienti company-rules list [--json]",
9555
+ ` ${COMPANY_RULES_SHOW_USAGE.slice("Usage: ".length)}`,
9416
9556
  ` ${COMPANY_RULES_CREATE_USAGE.slice("Usage: ".length)}`,
9417
9557
  ` ${COMPANY_RULES_UPDATE_USAGE.slice("Usage: ".length)}`,
9418
9558
  ` ${COMPANY_RULES_REMOVE_USAGE.slice("Usage: ".length)}`,
@@ -9434,6 +9574,16 @@ const HELP_TOPICS = new Map([
9434
9574
  " GET /api/v1/accounts/:account_id/company_rules.json"
9435
9575
  ].join("\n")],
9436
9576
 
9577
+ ["company-rules show", [
9578
+ "Usage:",
9579
+ ` ${COMPANY_RULES_SHOW_USAGE.slice("Usage: ".length)}`,
9580
+ "",
9581
+ "Status: implemented",
9582
+ "",
9583
+ "API:",
9584
+ " GET /api/v1/accounts/:account_id/company_rules/:id.json"
9585
+ ].join("\n")],
9586
+
9437
9587
  ["company-rules create", [
9438
9588
  "Usage:",
9439
9589
  ` ${COMPANY_RULES_CREATE_USAGE.slice("Usage: ".length)}`,