@audienti/cli 0.1.70 → 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 +31 -0
- package/README.md +7 -0
- package/package.json +1 -1
- package/skills/audienti/SKILL.md +1 -0
- package/src/api-client.js +27 -2
- package/src/cli.js +328 -7
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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
|
+
|
|
9
|
+
### Added
|
|
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.
|
|
12
|
+
- Add Motion-owned discovery signal tracking to `motions show` and the `motions signals` command, including attribution holds, source counts, and retained Agent provenance.
|
|
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
|
+
|
|
18
|
+
## [0.1.73] - 2026-09-26
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Add the account-scoped `motions signals` command for inspecting Motion-owned Topic and profile signals.
|
|
23
|
+
|
|
24
|
+
## [0.1.72] - 2026-09-26
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Add `social-cookies sync-messages` to request account-scoped email syncs with folder and explicit-retry controls, while printing the server's durable sync-slot status.
|
|
29
|
+
|
|
30
|
+
## [0.1.71] - 2026-09-24
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Motion updates now honor the server's durable first-discovery Approach lock: edits remain available before the first accepted discovery launch and are rejected after the lock is set.
|
|
35
|
+
|
|
36
|
+
- Prospect replan API responses now use the canonical Planner contract for admitted memberships.
|
|
37
|
+
|
|
7
38
|
## [0.1.70] - 2026-09-18
|
|
8
39
|
|
|
9
40
|
### Added
|
package/README.md
CHANGED
|
@@ -161,6 +161,7 @@ audienti content programs
|
|
|
161
161
|
audienti content plan <cprg_id>
|
|
162
162
|
audienti content approve <cpwi_id>
|
|
163
163
|
audienti content comments
|
|
164
|
+
audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] --json
|
|
164
165
|
audienti prospects show <prsp_id> --json
|
|
165
166
|
audienti prospects list --profiles
|
|
166
167
|
audienti prospects list --assigned-user unassigned
|
|
@@ -418,6 +419,12 @@ configuration, current outstanding inventory, any inventory blocker, and current
|
|
|
418
419
|
invitation capacity calculated by the server. A `null` weekly quota means
|
|
419
420
|
unlimited.
|
|
420
421
|
|
|
422
|
+
For an email social cookie, `social-cookies sync-messages` requests the shared
|
|
423
|
+
account-scoped cookie/folder sync slot and prints the server's durable status.
|
|
424
|
+
Pass `--folder` to select a provider folder and `--retry` only when an explicit
|
|
425
|
+
retry is intended. The command does not call a provider directly; `--json`
|
|
426
|
+
returns the accepted request and `sync_states` payload unchanged.
|
|
427
|
+
|
|
421
428
|
Use `users automation` to inspect or change one account user's LinkedIn safety
|
|
422
429
|
policy without changing the saved account selection:
|
|
423
430
|
|
package/package.json
CHANGED
package/skills/audienti/SKILL.md
CHANGED
|
@@ -122,6 +122,7 @@ audienti users activity me --window 7d --json
|
|
|
122
122
|
audienti users automation show <account_user_id|me> --platform linkedin --json --account <acct_id>
|
|
123
123
|
audienti users automation update <account_user_id|me> --payload automation-controls.json --json --account <acct_id>
|
|
124
124
|
audienti users automation update <account_user_id|me> --payload automation-controls.json --apply --json --account <acct_id>
|
|
125
|
+
audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] --json
|
|
125
126
|
audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
|
|
126
127
|
audienti lists create --name "Target list" --json
|
|
127
128
|
audienti lists routing-rules <list_id> list --json
|
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",
|
|
@@ -66,6 +74,13 @@ export class AudientiClient {
|
|
|
66
74
|
return this.requestJson(accountPath(accountId, ["social_cookies"], query));
|
|
67
75
|
}
|
|
68
76
|
|
|
77
|
+
syncSocialCookieMessages(accountId, socialCookieId, body = {}) {
|
|
78
|
+
return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "sync_messages"]), {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
userActivity(accountId, userId, query = {}) {
|
|
70
85
|
return this.requestJson(accountPath(accountId, ["operations", "users", userId, "activity"], query));
|
|
71
86
|
}
|
|
@@ -180,6 +195,10 @@ export class AudientiClient {
|
|
|
180
195
|
return this.requestJson(accountPath(accountId, ["company_rules"]));
|
|
181
196
|
}
|
|
182
197
|
|
|
198
|
+
companyRule(accountId, ruleId) {
|
|
199
|
+
return this.requestJson(accountPath(accountId, ["company_rules", ruleId]));
|
|
200
|
+
}
|
|
201
|
+
|
|
183
202
|
createCompanyRule(accountId, body) {
|
|
184
203
|
return this.requestJson(accountPath(accountId, ["company_rules"]), {
|
|
185
204
|
method: "POST",
|
|
@@ -339,6 +358,10 @@ export class AudientiClient {
|
|
|
339
358
|
return this.requestJson(accountPath(accountId, ["motions", motionId]));
|
|
340
359
|
}
|
|
341
360
|
|
|
361
|
+
motionSignals(accountId, motionId) {
|
|
362
|
+
return this.requestJson(accountPath(accountId, ["motions", motionId, "signals"]));
|
|
363
|
+
}
|
|
364
|
+
|
|
342
365
|
createMotion(accountId, body) {
|
|
343
366
|
return this.requestJson(accountPath(accountId, ["motions"]), {
|
|
344
367
|
method: "POST",
|
|
@@ -808,11 +831,13 @@ export class AudientiClient {
|
|
|
808
831
|
}
|
|
809
832
|
|
|
810
833
|
function accountPath(accountId, segments, query = {}) {
|
|
834
|
+
return apiPath(["accounts", accountId, ...segments], query);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function apiPath(segments, query = {}) {
|
|
811
838
|
const encodedSegments = [
|
|
812
839
|
"api",
|
|
813
840
|
"v1",
|
|
814
|
-
"accounts",
|
|
815
|
-
accountId,
|
|
816
841
|
...segments
|
|
817
842
|
].map((segment) => encodeURIComponent(segment));
|
|
818
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>]";
|
|
@@ -176,6 +188,7 @@ const TOOLS_LINKEDIN_REVIEW_USAGE = "Usage: audienti tools linkedin-review --url
|
|
|
176
188
|
const TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE = "Usage: audienti tools linkedin-review reports [--limit <n>] [--json] [--account <acct_id>]";
|
|
177
189
|
const TOOLS_LINKEDIN_REVIEW_SHOW_USAGE = "Usage: audienti tools linkedin-review show <rprt_id> [--json] [--account <acct_id>]";
|
|
178
190
|
const TOOLS_LINKEDIN_REVIEW_STATUS_USAGE = "Usage: audienti tools linkedin-review status <rprt_id> [--json] [--account <acct_id>]";
|
|
191
|
+
const SOCIAL_COOKIES_SYNC_MESSAGES_USAGE = "Usage: audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] [--json] [--account <acct_id>]";
|
|
179
192
|
const COHORT_STAGE_ORDER = [
|
|
180
193
|
"identified",
|
|
181
194
|
"pre_connect",
|
|
@@ -264,11 +277,14 @@ async function dispatch(argv, context) {
|
|
|
264
277
|
if (normalizedResource === "update" && action === "check") return updateCheck(rest, context);
|
|
265
278
|
if (normalizedResource === "accounts" && action === "list") return accountsList(rest, context, { accountOverride });
|
|
266
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 });
|
|
267
282
|
if (normalizedResource === "users" && action === "list") return usersList(rest, context, { accountOverride });
|
|
268
283
|
if (normalizedResource === "users" && action === "select") return usersSelect(rest, context, { accountOverride });
|
|
269
284
|
if (normalizedResource === "users" && action === "activity") return usersActivity(rest, context, { accountOverride });
|
|
270
285
|
if (normalizedResource === "users" && action === "automation") return usersAutomation(rest, context, { accountOverride });
|
|
271
286
|
if (normalizedResource === "setup" && action === "play" && rest[0] === "preflight") return setupPlayPreflight(rest.slice(1), context, { accountOverride });
|
|
287
|
+
if (normalizedResource === "social-cookies" && action === "sync-messages") return socialCookiesSyncMessages(rest, context, { accountOverride });
|
|
272
288
|
if (normalizedResource === "offers" && action === "list") return offersList(rest, context, { accountOverride });
|
|
273
289
|
if (normalizedResource === "offers" && action === "show") return offersShow(rest, context, { accountOverride });
|
|
274
290
|
if (normalizedResource === "offers" && action === "create") return offersCreate(rest, context, { accountOverride });
|
|
@@ -289,6 +305,7 @@ async function dispatch(argv, context) {
|
|
|
289
305
|
if (normalizedResource === "dnc" && action === "import") return dncImport(rest, context, { accountOverride });
|
|
290
306
|
if (normalizedResource === "dnc" && ["remove", "delete"].includes(action)) return dncRemove(rest, context, { accountOverride });
|
|
291
307
|
if (normalizedResource === "company-rules" && action === "list") return companyRulesList(rest, context, { accountOverride });
|
|
308
|
+
if (normalizedResource === "company-rules" && action === "show") return companyRulesShow(rest, context, { accountOverride });
|
|
292
309
|
if (normalizedResource === "company-rules" && action === "create") return companyRulesCreate(rest, context, { accountOverride });
|
|
293
310
|
if (normalizedResource === "company-rules" && action === "update") return companyRulesUpdate(rest, context, { accountOverride });
|
|
294
311
|
if (normalizedResource === "company-rules" && ["remove", "delete"].includes(action)) return companyRulesRemove(rest, context, { accountOverride });
|
|
@@ -311,6 +328,7 @@ async function dispatch(argv, context) {
|
|
|
311
328
|
if (normalizedResource === "lists" && ["routing-rules", "rules"].includes(action)) return listsRoutingRules(rest, context, { accountOverride });
|
|
312
329
|
if (normalizedResource === "motions" && action === "list") return motionsList(rest, context, { accountOverride });
|
|
313
330
|
if (normalizedResource === "motions" && action === "show") return motionsShow(rest, context, { accountOverride });
|
|
331
|
+
if (normalizedResource === "motions" && action === "signals") return motionsSignals(rest, context, { accountOverride });
|
|
314
332
|
if (normalizedResource === "motions" && action === "status") return motionsStatus(rest, context, { accountOverride });
|
|
315
333
|
if (normalizedResource === "motions" && ["abm-companies", "company-filters"].includes(action)) return motionsAbmCompanies(rest, context, { accountOverride });
|
|
316
334
|
if (normalizedResource === "motions" && action === "analytics") return motionsAnalytics(rest, context, { accountOverride });
|
|
@@ -479,6 +497,8 @@ function normalizeResource(resource) {
|
|
|
479
497
|
if (resource === "principals") return "users";
|
|
480
498
|
if (resource === "writers") return "writer";
|
|
481
499
|
if (resource === "company_rules" || resource === "company-rules") return "company-rules";
|
|
500
|
+
if (resource === "linkedin_lookups" || resource === "linkedin-lookups") return "linkedin-lookups";
|
|
501
|
+
if (resource === "social_cookie" || resource === "social_cookies") return "social-cookies";
|
|
482
502
|
return resource === "plays" ? "motions" : resource;
|
|
483
503
|
}
|
|
484
504
|
|
|
@@ -798,6 +818,64 @@ async function accountsList(args, context, { accountOverride } = {}) {
|
|
|
798
818
|
}
|
|
799
819
|
}
|
|
800
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
|
+
|
|
801
879
|
async function accountsSelect(args, context) {
|
|
802
880
|
const { positionals } = parseCommandArgs(args, {});
|
|
803
881
|
if (positionals.length !== 1) throw new CommandError("Usage: audienti accounts select <acct_id>");
|
|
@@ -1033,6 +1111,26 @@ async function setupPlayPreflight(args, context, { accountOverride } = {}) {
|
|
|
1033
1111
|
renderSetupPlayPreflight(payload, context);
|
|
1034
1112
|
}
|
|
1035
1113
|
|
|
1114
|
+
async function socialCookiesSyncMessages(args, context, { accountOverride } = {}) {
|
|
1115
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1116
|
+
...jsonOptions(),
|
|
1117
|
+
folder: { type: "string" },
|
|
1118
|
+
retry: { type: "boolean" }
|
|
1119
|
+
});
|
|
1120
|
+
if (positionals.length !== 1 || (values.folder !== undefined && !String(values.folder).trim())) {
|
|
1121
|
+
throw new CommandError(SOCIAL_COOKIES_SYNC_MESSAGES_USAGE);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1125
|
+
const payload = await client.syncSocialCookieMessages(accountId, positionals[0], compactObject({
|
|
1126
|
+
folder: values.folder,
|
|
1127
|
+
retry: values.retry ? true : undefined
|
|
1128
|
+
}));
|
|
1129
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1130
|
+
|
|
1131
|
+
renderSocialCookieSync(payload, context);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1036
1134
|
async function offersList(args, context, { accountOverride } = {}) {
|
|
1037
1135
|
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1038
1136
|
if (positionals.length > 0) throw new CommandError("Usage: audienti offers list [--json] [--account <acct_id>]");
|
|
@@ -1315,6 +1413,22 @@ async function companyRulesList(args, context, { accountOverride } = {}) {
|
|
|
1315
1413
|
renderCompanyRules(payload, context);
|
|
1316
1414
|
}
|
|
1317
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
|
+
|
|
1318
1432
|
async function companyRulesCreate(args, context, { accountOverride } = {}) {
|
|
1319
1433
|
const { values, positionals } = parseCommandArgs(args, companyRuleOptions());
|
|
1320
1434
|
if (positionals.length > 0 || (!values["linkedin-url"] && !values.domain) || !values.disposition) {
|
|
@@ -1774,6 +1888,17 @@ async function motionsShow(args, context, { accountOverride } = {}) {
|
|
|
1774
1888
|
renderMotion(motion, context);
|
|
1775
1889
|
}
|
|
1776
1890
|
|
|
1891
|
+
async function motionsSignals(args, context, { accountOverride } = {}) {
|
|
1892
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1893
|
+
if (positionals.length !== 1) throw new CommandError("Usage: audienti motions signals <motn_id> [--json] [--account <acct_id>]");
|
|
1894
|
+
|
|
1895
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1896
|
+
const payload = await client.motionSignals(accountId, positionals[0]);
|
|
1897
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1898
|
+
|
|
1899
|
+
renderMotionDiscoverySignals(payload, context);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1777
1902
|
async function motionsStatus(args, context, { accountOverride } = {}) {
|
|
1778
1903
|
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1779
1904
|
if (positionals.length !== 1) throw new CommandError("Usage: audienti motions status <motn_id> [--json] [--account <acct_id>]");
|
|
@@ -5441,6 +5566,30 @@ function renderSetupPlayPreflight(payload, context) {
|
|
|
5441
5566
|
}
|
|
5442
5567
|
}
|
|
5443
5568
|
|
|
5569
|
+
function renderSocialCookieSync(payload, context) {
|
|
5570
|
+
const requestStatus = payload?.requested ? "accepted" : "not requested";
|
|
5571
|
+
const coalesced = payload?.coalesced ? " (coalesced)" : "";
|
|
5572
|
+
writeLine(context.stdout, `Email sync: ${requestStatus}${coalesced} for ${display(payload?.social_cookie_id)}`);
|
|
5573
|
+
if (payload?.reason) writeLine(context.stdout, `Reason: ${payload.reason}`);
|
|
5574
|
+
|
|
5575
|
+
const states = Array.isArray(payload?.sync_states) ? payload.sync_states : [];
|
|
5576
|
+
if (states.length === 0) return writeLine(context.stdout, "No durable sync state returned.");
|
|
5577
|
+
|
|
5578
|
+
writeLine(context.stdout, "FOLDER\tSTATUS\tGENERATION\tATTEMPTS\tDUE\tLAST SUCCESS\tLAST ERROR\tSYNCED THROUGH");
|
|
5579
|
+
for (const state of states) {
|
|
5580
|
+
writeLine(context.stdout, [
|
|
5581
|
+
display(state?.canonical_folder),
|
|
5582
|
+
display(state?.status),
|
|
5583
|
+
display(state?.generation, "0"),
|
|
5584
|
+
display(state?.attempts, "0"),
|
|
5585
|
+
display(state?.due_at),
|
|
5586
|
+
display(state?.last_success_at),
|
|
5587
|
+
display(state?.last_error_code),
|
|
5588
|
+
display(state?.last_synced_through_at)
|
|
5589
|
+
].join("\t"));
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5444
5593
|
function renderSetupLinkedInCapabilities(capabilities, context) {
|
|
5445
5594
|
const storedBoolean = (value) => value === true ? "yes" : value === false ? "no" : "unknown";
|
|
5446
5595
|
writeLine(
|
|
@@ -5833,6 +5982,7 @@ function renderMotion(motion, context) {
|
|
|
5833
5982
|
if (Array.isArray(motion?.inbound_channels)) writeLine(context.stdout, `Inbound channels: ${display(motion.inbound_channels.join(", "), "-")}`);
|
|
5834
5983
|
renderMotionLopaProfiles(motion?.lopa_profiles, context);
|
|
5835
5984
|
renderMotionSignalRows(motion?.signal_rows, context);
|
|
5985
|
+
renderMotionDiscoverySignals(motion?.discovery_signals, context);
|
|
5836
5986
|
renderMotionAbmCompanies(motion, context, { showEmpty: false });
|
|
5837
5987
|
if (Array.isArray(motion?.play_tags)) writeLine(context.stdout, `Tags: ${display(motion.play_tags.join(", "), "-")}`);
|
|
5838
5988
|
if (motion?.principal_account_user?.id) {
|
|
@@ -5867,6 +6017,32 @@ function renderMotionSignalRows(rows, context) {
|
|
|
5867
6017
|
}
|
|
5868
6018
|
}
|
|
5869
6019
|
|
|
6020
|
+
function renderMotionDiscoverySignals(payload, context) {
|
|
6021
|
+
if (!payload || typeof payload !== "object") return;
|
|
6022
|
+
|
|
6023
|
+
const rows = Array.isArray(payload?.signals) ? payload.signals : [];
|
|
6024
|
+
const counts = payload?.counts || {};
|
|
6025
|
+
const motionId = payload?.prefix_id ? ` (${display(payload.prefix_id)})` : "";
|
|
6026
|
+
|
|
6027
|
+
writeLine(context.stdout, `Motion-owned discovery signals${motionId}: ${display(counts.total, "0")} total, ${display(counts.actionable, "0")} actionable, ${display(counts.pending, "0")} pending, ${display(counts.held, "0")} held`);
|
|
6028
|
+
if (rows.length === 0) return;
|
|
6029
|
+
|
|
6030
|
+
for (const row of rows) {
|
|
6031
|
+
const provenance = Array.isArray(row.agent_provenance)
|
|
6032
|
+
? row.agent_provenance.map((entry) => [entry.agent_name, entry.agent_id].filter(Boolean).join(" · ")).filter(Boolean).join(", ")
|
|
6033
|
+
: "";
|
|
6034
|
+
const status = row.actionable ? "actionable" : display(row.status, "pending");
|
|
6035
|
+
const sourceCounts = row.source_counts || {};
|
|
6036
|
+
const detail = [
|
|
6037
|
+
row.canonical_url || row.topic?.slug || row.source_key,
|
|
6038
|
+
`sources=${display(sourceCounts.total, "0")}/${display(sourceCounts.accepted, "0")}/${display(sourceCounts.rejected, "0")}`,
|
|
6039
|
+
provenance ? `agents=${provenance}` : null,
|
|
6040
|
+
!row.actionable && row.motion_attribution_reason ? `reason=${singleLine(row.motion_attribution_reason)}` : null
|
|
6041
|
+
].filter(Boolean).join("; ");
|
|
6042
|
+
writeLine(context.stdout, `- [${status}] ${display(row.type, "signal")}: ${display(row.label, "unnamed")} (${detail})`);
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
|
|
5870
6046
|
function renderMotionAbmCompanies(payload, context, { showEmpty = true } = {}) {
|
|
5871
6047
|
const rows = Array.isArray(payload?.abm_companies) ? payload.abm_companies : [];
|
|
5872
6048
|
if (rows.length === 0) {
|
|
@@ -6054,16 +6230,32 @@ function renderProspectReplan(payload, context) {
|
|
|
6054
6230
|
const current = payload?.current || {};
|
|
6055
6231
|
const replanned = payload?.replanned || {};
|
|
6056
6232
|
const status = replanStatusLabel(payload);
|
|
6233
|
+
const reason = payload?.reason_code || payload?.refresh?.reason;
|
|
6057
6234
|
|
|
6058
6235
|
writeLine(context.stdout, `Replan ${status} for ${display(prospect.display_name || prospect.name)} (${display(prospect.prefix_id)}).`);
|
|
6059
6236
|
writeLine(context.stdout, `Changed: ${payload?.changed ? "yes" : "no"}`);
|
|
6060
|
-
if (
|
|
6237
|
+
if (reason) writeLine(context.stdout, `Reason: ${reason}`);
|
|
6061
6238
|
writeLine(context.stdout, `Current: ${formatCoachAction(current.next_action)}`);
|
|
6062
6239
|
writeLine(context.stdout, `Replanned: ${formatCoachAction(replanned.next_action)}`);
|
|
6063
6240
|
if (replanned.rationale) writeLine(context.stdout, `Rationale: ${replanned.rationale}`);
|
|
6064
6241
|
if (replanned.guidance) writeLine(context.stdout, `Guidance: ${replanned.guidance}`);
|
|
6065
|
-
if (payload?.status === "dry_run")
|
|
6066
|
-
|
|
6242
|
+
if (payload?.status === "dry_run") {
|
|
6243
|
+
writeLine(context.stdout, "Run again with --apply to persist this plan.");
|
|
6244
|
+
} else if (payload?.status === "coach_error") {
|
|
6245
|
+
writeLine(context.stdout, "The plan was not persisted. Fix the coach error and rerun with --apply.");
|
|
6246
|
+
} else if (payload?.status === "pending") {
|
|
6247
|
+
writeLine(context.stdout, "The replan request was recorded but is not applied yet. Retry after the refresh reason clears.");
|
|
6248
|
+
} else if (payload?.status === "busy") {
|
|
6249
|
+
writeLine(context.stdout, "Planner execution is busy. No replan was applied; retry later.");
|
|
6250
|
+
} else if (payload?.status === "failed") {
|
|
6251
|
+
writeLine(context.stdout, "The request was recorded, but the Planner could not apply it. Review the reason before retrying.");
|
|
6252
|
+
} else if (payload?.status === "evaluation_error") {
|
|
6253
|
+
writeLine(context.stdout, "Planner evaluation failed. Fix the evaluation error and rerun with --apply.");
|
|
6254
|
+
} else if (payload?.status === "unsupported_plan_version") {
|
|
6255
|
+
writeLine(context.stdout, "This membership uses an unsupported Planner version. Migrate it before retrying.");
|
|
6256
|
+
} else if (payload?.status === "not_applied") {
|
|
6257
|
+
writeLine(context.stdout, "The plan was not persisted. Review the reason before retrying.");
|
|
6258
|
+
}
|
|
6067
6259
|
}
|
|
6068
6260
|
|
|
6069
6261
|
function renderProspectReenrich(payload, context) {
|
|
@@ -6121,12 +6313,24 @@ function formatObjectCounts(value) {
|
|
|
6121
6313
|
.join(", ");
|
|
6122
6314
|
}
|
|
6123
6315
|
|
|
6316
|
+
const REPLAN_STATUS_LABELS = Object.freeze({
|
|
6317
|
+
dry_run: "dry run",
|
|
6318
|
+
coach_error: "coach error",
|
|
6319
|
+
not_applied: "not applied",
|
|
6320
|
+
pending: "pending",
|
|
6321
|
+
busy: "busy",
|
|
6322
|
+
failed: "failed",
|
|
6323
|
+
evaluation_error: "evaluation error",
|
|
6324
|
+
unsupported_plan_version: "unsupported plan version"
|
|
6325
|
+
});
|
|
6326
|
+
|
|
6124
6327
|
function replanStatusLabel(payload) {
|
|
6125
|
-
|
|
6126
|
-
if (
|
|
6127
|
-
|
|
6328
|
+
const rawStatus = payload?.status;
|
|
6329
|
+
if (Object.prototype.hasOwnProperty.call(REPLAN_STATUS_LABELS, rawStatus)) {
|
|
6330
|
+
return REPLAN_STATUS_LABELS[rawStatus];
|
|
6331
|
+
}
|
|
6128
6332
|
|
|
6129
|
-
return payload?.applied ? "applied" : "
|
|
6333
|
+
return payload?.applied ? "applied" : "not applied";
|
|
6130
6334
|
}
|
|
6131
6335
|
|
|
6132
6336
|
function formatCoachAction(nextAction = {}) {
|
|
@@ -8297,6 +8501,7 @@ const HELP_TOPICS = new Map([
|
|
|
8297
8501
|
" Setup & identity",
|
|
8298
8502
|
" audienti auth status",
|
|
8299
8503
|
" audienti config list",
|
|
8504
|
+
" audienti accounts show [acct_id]",
|
|
8300
8505
|
" audienti update check",
|
|
8301
8506
|
" audienti setup play preflight",
|
|
8302
8507
|
" audienti users list",
|
|
@@ -8305,6 +8510,9 @@ const HELP_TOPICS = new Map([
|
|
|
8305
8510
|
" audienti users automation show <account_user_id|me>",
|
|
8306
8511
|
" audienti users automation update <account_user_id|me> --payload <file.json> [--apply]",
|
|
8307
8512
|
"",
|
|
8513
|
+
" Email sync",
|
|
8514
|
+
" audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry]",
|
|
8515
|
+
"",
|
|
8308
8516
|
" Motions / plays",
|
|
8309
8517
|
" audienti motions list",
|
|
8310
8518
|
" audienti motions show <motn_id>",
|
|
@@ -8381,9 +8589,11 @@ const HELP_TOPICS = new Map([
|
|
|
8381
8589
|
" audienti icps add-tag <icp_id> <tag>",
|
|
8382
8590
|
" audienti icps remove-tag <icp_id> <tag>",
|
|
8383
8591
|
" audienti companies search --query <text>",
|
|
8592
|
+
" audienti linkedin-lookups <kind> [--query <text>]",
|
|
8384
8593
|
" audienti dnc list",
|
|
8385
8594
|
" audienti dnc add <email|citation_id|profile_url>",
|
|
8386
8595
|
" audienti company-rules list",
|
|
8596
|
+
" audienti company-rules show <rule_id>",
|
|
8387
8597
|
" audienti company-rules create (--linkedin-url <url> | --domain <domain>) --disposition <state>",
|
|
8388
8598
|
"",
|
|
8389
8599
|
" Writer",
|
|
@@ -8608,6 +8818,7 @@ const HELP_TOPICS = new Map([
|
|
|
8608
8818
|
["accounts", [
|
|
8609
8819
|
"Usage:",
|
|
8610
8820
|
" audienti accounts list [--json]",
|
|
8821
|
+
" audienti accounts show [<acct_id>] [--json]",
|
|
8611
8822
|
" audienti accounts select <acct_id>",
|
|
8612
8823
|
"",
|
|
8613
8824
|
"Status: implemented",
|
|
@@ -8616,6 +8827,41 @@ const HELP_TOPICS = new Map([
|
|
|
8616
8827
|
" acct_id: string Account prefix id, for example acct_abc123"
|
|
8617
8828
|
].join("\n")],
|
|
8618
8829
|
|
|
8830
|
+
["social-cookies", [
|
|
8831
|
+
"Usage:",
|
|
8832
|
+
" audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] [--json] [--account <acct_id>]",
|
|
8833
|
+
"",
|
|
8834
|
+
"Status: implemented",
|
|
8835
|
+
"",
|
|
8836
|
+
"Commands:",
|
|
8837
|
+
" audienti social-cookies sync-messages Request the shared durable email sync slot",
|
|
8838
|
+
"",
|
|
8839
|
+
"Run `audienti social-cookies sync-messages help` for the request and output contract."
|
|
8840
|
+
].join("\n")],
|
|
8841
|
+
|
|
8842
|
+
["social-cookies sync-messages", [
|
|
8843
|
+
"Usage:",
|
|
8844
|
+
" audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] [--json] [--account <acct_id>]",
|
|
8845
|
+
"",
|
|
8846
|
+
"Status: implemented",
|
|
8847
|
+
"",
|
|
8848
|
+
"Purpose:",
|
|
8849
|
+
" Request one account-scoped email sync through the server's durable cookie/folder slot.",
|
|
8850
|
+
"",
|
|
8851
|
+
"Input shape:",
|
|
8852
|
+
" scok_id: string Social cookie prefix id or numeric id",
|
|
8853
|
+
" folder: string Provider folder; omitted means the server's INBOX default",
|
|
8854
|
+
" retry: flag Explicitly reopen an exhausted or credential-held slot",
|
|
8855
|
+
"",
|
|
8856
|
+
"Output shape:",
|
|
8857
|
+
" requested, coalesced, reason, sync_states Server response and durable slot status",
|
|
8858
|
+
" sync_states Includes status, generation, attempts, due, last error, and watermark",
|
|
8859
|
+
"",
|
|
8860
|
+
"API:",
|
|
8861
|
+
" POST /api/v1/accounts/:account_id/social_cookies/:id/sync_messages.json",
|
|
8862
|
+
" The CLI only requests work; provider traversal remains owned by the server worker."
|
|
8863
|
+
].join("\n")],
|
|
8864
|
+
|
|
8619
8865
|
["accounts list", [
|
|
8620
8866
|
"Usage:",
|
|
8621
8867
|
" audienti accounts list [--json] [--account <acct_id>]",
|
|
@@ -8631,6 +8877,51 @@ const HELP_TOPICS = new Map([
|
|
|
8631
8877
|
" audienti accounts list --json"
|
|
8632
8878
|
].join("\n")],
|
|
8633
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
|
+
|
|
8634
8925
|
["accounts select", [
|
|
8635
8926
|
"Usage:",
|
|
8636
8927
|
" audienti accounts select <acct_id>",
|
|
@@ -9261,6 +9552,7 @@ const HELP_TOPICS = new Map([
|
|
|
9261
9552
|
["company-rules", [
|
|
9262
9553
|
"Usage:",
|
|
9263
9554
|
" audienti company-rules list [--json]",
|
|
9555
|
+
` ${COMPANY_RULES_SHOW_USAGE.slice("Usage: ".length)}`,
|
|
9264
9556
|
` ${COMPANY_RULES_CREATE_USAGE.slice("Usage: ".length)}`,
|
|
9265
9557
|
` ${COMPANY_RULES_UPDATE_USAGE.slice("Usage: ".length)}`,
|
|
9266
9558
|
` ${COMPANY_RULES_REMOVE_USAGE.slice("Usage: ".length)}`,
|
|
@@ -9282,6 +9574,16 @@ const HELP_TOPICS = new Map([
|
|
|
9282
9574
|
" GET /api/v1/accounts/:account_id/company_rules.json"
|
|
9283
9575
|
].join("\n")],
|
|
9284
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
|
+
|
|
9285
9587
|
["company-rules create", [
|
|
9286
9588
|
"Usage:",
|
|
9287
9589
|
` ${COMPANY_RULES_CREATE_USAGE.slice("Usage: ".length)}`,
|
|
@@ -9774,6 +10076,7 @@ const HELP_TOPICS = new Map([
|
|
|
9774
10076
|
"Usage:",
|
|
9775
10077
|
" audienti motions list [--tag <tag>] [--json]",
|
|
9776
10078
|
" audienti motions show <motn_id> [--json]",
|
|
10079
|
+
" audienti motions signals <motn_id> [--json]",
|
|
9777
10080
|
" audienti motions status <motn_id> [--json]",
|
|
9778
10081
|
" audienti motions run-discovery <motn_id> [--target-count <n>] [--json]",
|
|
9779
10082
|
" audienti motions quick-start --url <company_url> [--confirm] [--wait] [--json]",
|
|
@@ -9869,12 +10172,30 @@ const HELP_TOPICS = new Map([
|
|
|
9869
10172
|
" inbound_channels: enabled inbound collectors for inbound motions",
|
|
9870
10173
|
" lopa_profiles[]: tracked LinkedIn profile rows for LOPA motions",
|
|
9871
10174
|
" signal_rows[]: outbound signal configuration rows",
|
|
10175
|
+
" discovery_signals: Motion-owned Topic/profile signals with attribution state and Agent provenance",
|
|
9872
10176
|
" abm_companies[]: motion-scoped positive company filter rows",
|
|
9873
10177
|
"",
|
|
9874
10178
|
"API:",
|
|
9875
10179
|
" GET /api/v1/accounts/:account_id/motions/:id.json"
|
|
9876
10180
|
].join("\n")],
|
|
9877
10181
|
|
|
10182
|
+
["motions signals", [
|
|
10183
|
+
"Usage:",
|
|
10184
|
+
" audienti motions signals <motn_id> [--json] [--account <acct_id>]",
|
|
10185
|
+
"",
|
|
10186
|
+
"Status: implemented",
|
|
10187
|
+
"",
|
|
10188
|
+
"Purpose:",
|
|
10189
|
+
" Read the Topic and tracked-profile signals owned by one Motion.",
|
|
10190
|
+
" Actionable rows have exact Motion attribution; pending and held rows remain inspectable with their reason and evidence.",
|
|
10191
|
+
"",
|
|
10192
|
+
"Output:",
|
|
10193
|
+
" Shows attribution state, canonical source URL or Topic, accepted/rejected source counts, and retained Agent provenance.",
|
|
10194
|
+
"",
|
|
10195
|
+
"API:",
|
|
10196
|
+
" GET /api/v1/accounts/:account_id/motions/:id/signals.json"
|
|
10197
|
+
].join("\n")],
|
|
10198
|
+
|
|
9878
10199
|
["motions abm-companies", [
|
|
9879
10200
|
"Usage:",
|
|
9880
10201
|
` ${MOTIONS_ABM_COMPANIES_LIST_USAGE.slice("Usage: ".length)}`,
|