@audienti/cli 0.1.25 → 0.1.28
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 +23 -0
- package/package.json +1 -1
- package/src/api-client.js +28 -0
- package/src/cli.js +409 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to the Audienti CLI are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.28] - 2026-07-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Add `audienti prospects reenrich` for dry-running or queueing one forced LinkedIn person profile re-enrichment.
|
|
12
|
+
- Add `audienti prospects refresh-queue` for dry-running or applying account-scoped next-action and operator draft cache repair for one prospect.
|
|
13
|
+
|
|
14
|
+
## [0.1.27] - 2026-07-22
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Add `audienti operator failed-drafts` and `audienti operator failed-drafts requeue` for listing failed prospect operator drafts and queueing async rewrites.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Show prospect replan coach errors as not persisted instead of displaying failed `--apply` runs as dry runs.
|
|
23
|
+
|
|
24
|
+
## [0.1.26] - 2026-07-22
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Add `audienti prospects replan <prsp_id> [--apply]` for dry-running or persisting refreshed next-action coach plans.
|
|
29
|
+
|
|
7
30
|
## [0.1.25] - 2026-07-22
|
|
8
31
|
|
|
9
32
|
### Added
|
package/package.json
CHANGED
package/src/api-client.js
CHANGED
|
@@ -366,6 +366,27 @@ export class AudientiClient {
|
|
|
366
366
|
return this.requestJson(accountPath(accountId, ["prospects", prospectId]));
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
replanProspect(accountId, prospectId, body = {}) {
|
|
370
|
+
return this.requestJson(accountPath(accountId, ["prospects", prospectId, "replan"]), {
|
|
371
|
+
method: "POST",
|
|
372
|
+
body
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
reenrichProspect(accountId, prospectId, body = {}) {
|
|
377
|
+
return this.requestJson(accountPath(accountId, ["prospects", prospectId, "reenrich"]), {
|
|
378
|
+
method: "POST",
|
|
379
|
+
body
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
refreshProspectQueue(accountId, prospectId, body = {}) {
|
|
384
|
+
return this.requestJson(accountPath(accountId, ["prospects", prospectId, "refresh_queue"]), {
|
|
385
|
+
method: "POST",
|
|
386
|
+
body
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
369
390
|
assignProspects(accountId, body) {
|
|
370
391
|
return this.requestJson(accountPath(accountId, ["prospects", "assign"]), {
|
|
371
392
|
method: "POST",
|
|
@@ -500,6 +521,13 @@ export class AudientiClient {
|
|
|
500
521
|
return this.requestJson(accountPath(accountId, ["operator", "next"], query));
|
|
501
522
|
}
|
|
502
523
|
|
|
524
|
+
requeueOperatorFailedDrafts(accountId, body = {}) {
|
|
525
|
+
return this.requestJson(accountPath(accountId, ["operator", "failed_drafts", "requeue"]), {
|
|
526
|
+
method: "POST",
|
|
527
|
+
body
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
503
531
|
operatorOutcome(accountId, body) {
|
|
504
532
|
return this.requestJson(accountPath(accountId, ["operator", "outcome"]), {
|
|
505
533
|
method: "POST",
|
package/src/cli.js
CHANGED
|
@@ -38,6 +38,9 @@ const PROSPECTS_ADD_PROFILE_USAGE = "Usage: audienti prospects add-profile <prsp
|
|
|
38
38
|
const PROSPECTS_REPORT_BAD_PROFILE_USAGE = "Usage: audienti prospects report-bad-profile <prsp_id> <prof_id|citation_id> [--json] [--account <acct_id>]";
|
|
39
39
|
const PROSPECTS_ASSIGN_USAGE = "Usage: audienti prospects assign <prsp_id> [prsp_id...] --assigned-user <id|me|unassign> [--json] [--account <acct_id>]";
|
|
40
40
|
const PROSPECTS_SET_STATUS_USAGE = "Usage: audienti prospects set-status <prsp_id> --status <active|nurture|non_responsive|not_fit|bad_data_404|rejected> [--json] [--account <acct_id>]";
|
|
41
|
+
const PROSPECTS_REPLAN_USAGE = "Usage: audienti prospects replan <prsp_id> [--apply] [--json] [--account <acct_id>]";
|
|
42
|
+
const PROSPECTS_REENRICH_USAGE = "Usage: audienti prospects reenrich <prsp_id> [--profile <prof_id|citation_id>] [--apply] [--json] [--account <acct_id>]";
|
|
43
|
+
const PROSPECTS_REFRESH_QUEUE_USAGE = "Usage: audienti prospects refresh-queue <prsp_id> [--apply] [--json] [--account <acct_id>]";
|
|
41
44
|
const PROSPECTS_REJECT_USAGE = "Usage: audienti prospects reject <prsp_id> [--json] [--account <acct_id>]";
|
|
42
45
|
const PROSPECTS_NURTURE_USAGE = "Usage: audienti prospects nurture <prsp_id> [--reason <nurture|non_responsive|not_fit|bad_data_404>] [--json] [--account <acct_id>]";
|
|
43
46
|
const PROSPECTS_RESTORE_USAGE = "Usage: audienti prospects restore <prsp_id> [--json] [--account <acct_id>]";
|
|
@@ -45,6 +48,8 @@ const PROSPECTS_LOCK_USAGE = "Usage: audienti prospects lock <prsp_id> [--note <
|
|
|
45
48
|
const PROSPECTS_UNLOCK_USAGE = "Usage: audienti prospects unlock <prsp_id> [--json] [--account <acct_id>]";
|
|
46
49
|
const PROSPECTS_CHECK_USAGE = "Usage: audienti prospects check [--json|--csv] [filters] [--account <acct_id>]";
|
|
47
50
|
const PROSPECTS_IMPORT_BATCH_USAGE = "Usage: audienti prospects import-batch --file <csv|jsonl|json> [--list <list_id>] [--motion <motn_id>] [--assigned-user <id|me>] [--json] [--account <acct_id>]";
|
|
51
|
+
const OPERATOR_FAILED_DRAFTS_USAGE = "Usage: audienti operator failed-drafts [--json] [filters] [--account <acct_id>]";
|
|
52
|
+
const OPERATOR_FAILED_DRAFTS_REQUEUE_USAGE = "Usage: audienti operator failed-drafts requeue (--all | <row_id> [row_id...]) [--limit <n>] [--json] [filters] [--account <acct_id>]";
|
|
48
53
|
const DNC_ADD_USAGE = "Usage: audienti dnc add <email|citation_id|profile_url> [--json] [--account <acct_id>]";
|
|
49
54
|
const DNC_IMPORT_USAGE = "Usage: audienti dnc import --file <txt|csv> [--json] [--account <acct_id>]";
|
|
50
55
|
const DNC_REMOVE_USAGE = "Usage: audienti dnc remove <dnc_entry_id> [--json] [--account <acct_id>]";
|
|
@@ -243,6 +248,9 @@ async function dispatch(argv, context) {
|
|
|
243
248
|
if (normalizedResource === "prospects" && action === "show") return prospectsShow(rest, context, { accountOverride });
|
|
244
249
|
if (normalizedResource === "prospects" && action === "assign") return prospectsAssign(rest, context, { accountOverride });
|
|
245
250
|
if (normalizedResource === "prospects" && action === "set-status") return prospectsSetStatus(rest, context, { accountOverride });
|
|
251
|
+
if (normalizedResource === "prospects" && action === "replan") return prospectsReplan(rest, context, { accountOverride });
|
|
252
|
+
if (normalizedResource === "prospects" && action === "reenrich") return prospectsReenrich(rest, context, { accountOverride });
|
|
253
|
+
if (normalizedResource === "prospects" && action === "refresh-queue") return prospectsRefreshQueue(rest, context, { accountOverride });
|
|
246
254
|
if (normalizedResource === "prospects" && action === "reject") return prospectsDisposition("reject", rest, context, { accountOverride });
|
|
247
255
|
if (normalizedResource === "prospects" && action === "nurture") return prospectsDisposition("nurture", rest, context, { accountOverride });
|
|
248
256
|
if (normalizedResource === "prospects" && action === "restore") return prospectsDisposition("restore", rest, context, { accountOverride });
|
|
@@ -264,6 +272,7 @@ async function dispatch(argv, context) {
|
|
|
264
272
|
if (normalizedResource === "tools" && action === "list") return toolsList(rest, context);
|
|
265
273
|
if (normalizedResource === "tools" && action === "get") return toolsGet(rest, context, { accountOverride });
|
|
266
274
|
if (normalizedResource === "tools" && action === "linkedin-review") return toolsLinkedinReview(rest, context, { accountOverride });
|
|
275
|
+
if (normalizedResource === "operator" && action === "failed-drafts") return operatorFailedDrafts(rest, context, { accountOverride });
|
|
267
276
|
if (normalizedResource === "operator" && action === "queue") return operatorQueue(rest, context, { accountOverride });
|
|
268
277
|
if (normalizedResource === "operator" && action === "next") return operatorNext(rest, context, { accountOverride });
|
|
269
278
|
if (normalizedResource === "operator" && action === "outcome") return operatorOutcome(rest, context, { accountOverride });
|
|
@@ -1840,6 +1849,56 @@ async function prospectsShow(args, context, { accountOverride } = {}) {
|
|
|
1840
1849
|
renderProspect(prospect, context);
|
|
1841
1850
|
}
|
|
1842
1851
|
|
|
1852
|
+
async function prospectsReplan(args, context, { accountOverride } = {}) {
|
|
1853
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1854
|
+
...jsonOptions(),
|
|
1855
|
+
apply: { type: "boolean" }
|
|
1856
|
+
});
|
|
1857
|
+
if (positionals.length !== 1) throw new CommandError(PROSPECTS_REPLAN_USAGE);
|
|
1858
|
+
|
|
1859
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1860
|
+
const payload = await client.replanProspect(accountId, positionals[0], compactObject({
|
|
1861
|
+
apply: values.apply
|
|
1862
|
+
}));
|
|
1863
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1864
|
+
|
|
1865
|
+
renderProspectReplan(payload, context);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
async function prospectsReenrich(args, context, { accountOverride } = {}) {
|
|
1869
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1870
|
+
...jsonOptions(),
|
|
1871
|
+
apply: { type: "boolean" },
|
|
1872
|
+
profile: { type: "string" }
|
|
1873
|
+
});
|
|
1874
|
+
if (positionals.length !== 1) throw new CommandError(PROSPECTS_REENRICH_USAGE);
|
|
1875
|
+
|
|
1876
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1877
|
+
const payload = await client.reenrichProspect(accountId, positionals[0], compactObject({
|
|
1878
|
+
apply: values.apply,
|
|
1879
|
+
profile_id: values.profile
|
|
1880
|
+
}));
|
|
1881
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1882
|
+
|
|
1883
|
+
renderProspectReenrich(payload, context);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
async function prospectsRefreshQueue(args, context, { accountOverride } = {}) {
|
|
1887
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1888
|
+
...jsonOptions(),
|
|
1889
|
+
apply: { type: "boolean" }
|
|
1890
|
+
});
|
|
1891
|
+
if (positionals.length !== 1) throw new CommandError(PROSPECTS_REFRESH_QUEUE_USAGE);
|
|
1892
|
+
|
|
1893
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1894
|
+
const payload = await client.refreshProspectQueue(accountId, positionals[0], compactObject({
|
|
1895
|
+
apply: values.apply
|
|
1896
|
+
}));
|
|
1897
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1898
|
+
|
|
1899
|
+
renderProspectRefreshQueue(payload, context);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1843
1902
|
async function prospectsDisposition(action, args, context, { accountOverride } = {}) {
|
|
1844
1903
|
const usageText = {
|
|
1845
1904
|
reject: PROSPECTS_REJECT_USAGE,
|
|
@@ -2444,6 +2503,41 @@ async function operatorQueue(args, context, { accountOverride } = {}) {
|
|
|
2444
2503
|
renderOperatorQueue(payload, context);
|
|
2445
2504
|
}
|
|
2446
2505
|
|
|
2506
|
+
async function operatorFailedDrafts(args, context, { accountOverride } = {}) {
|
|
2507
|
+
if (args[0] === "requeue") {
|
|
2508
|
+
return operatorFailedDraftsRequeue(args.slice(1), context, { accountOverride });
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
const { values, positionals } = parseCommandArgs(args, operatorFailedDraftOptions());
|
|
2512
|
+
if (positionals.length > 0) throw new CommandError(OPERATOR_FAILED_DRAFTS_USAGE);
|
|
2513
|
+
|
|
2514
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2515
|
+
const payload = await client.operatorQueue(accountId, operatorFailedDraftQuery(values));
|
|
2516
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
2517
|
+
|
|
2518
|
+
renderOperatorFailedDrafts(payload, context);
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
async function operatorFailedDraftsRequeue(args, context, { accountOverride } = {}) {
|
|
2522
|
+
const { values, positionals } = parseCommandArgs(args, operatorFailedDraftOptions({
|
|
2523
|
+
all: { type: "boolean" },
|
|
2524
|
+
limit: { type: "string" }
|
|
2525
|
+
}));
|
|
2526
|
+
if (values.all && positionals.length > 0) throw new CommandError("Choose either --all or row ids, not both.");
|
|
2527
|
+
if (!values.all && positionals.length === 0) throw new CommandError(OPERATOR_FAILED_DRAFTS_REQUEUE_USAGE);
|
|
2528
|
+
|
|
2529
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2530
|
+
const payload = await client.requeueOperatorFailedDrafts(accountId, compactObject({
|
|
2531
|
+
...operatorFailedDraftQuery(values),
|
|
2532
|
+
all: values.all || undefined,
|
|
2533
|
+
limit: values.limit,
|
|
2534
|
+
row_ids: positionals.length > 0 ? positionals : undefined
|
|
2535
|
+
}));
|
|
2536
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
2537
|
+
|
|
2538
|
+
renderOperatorFailedDraftRequeue(payload, context);
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2447
2541
|
async function operatorNext(args, context, { accountOverride } = {}) {
|
|
2448
2542
|
const { values, positionals } = parseCommandArgs(args, operatorNextOptions());
|
|
2449
2543
|
if (positionals.length > 0) throw new CommandError("Usage: audienti operator next [--json|--plan|--done|--skip|--fail|--return] [filters] [--note <text>] [--account <acct_id>]");
|
|
@@ -2705,6 +2799,18 @@ function operatorNextOptions() {
|
|
|
2705
2799
|
});
|
|
2706
2800
|
}
|
|
2707
2801
|
|
|
2802
|
+
function operatorFailedDraftOptions(extra = {}) {
|
|
2803
|
+
return {
|
|
2804
|
+
...jsonOptions(),
|
|
2805
|
+
principal: { type: "string" },
|
|
2806
|
+
motion: { type: "string" },
|
|
2807
|
+
list: { type: "string" },
|
|
2808
|
+
stage: { type: "string" },
|
|
2809
|
+
query: { type: "string" },
|
|
2810
|
+
...extra
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2708
2814
|
function operatorQuery(values) {
|
|
2709
2815
|
return compactObject({
|
|
2710
2816
|
principal_account_user_id: values.principal,
|
|
@@ -2716,6 +2822,18 @@ function operatorQuery(values) {
|
|
|
2716
2822
|
});
|
|
2717
2823
|
}
|
|
2718
2824
|
|
|
2825
|
+
function operatorFailedDraftQuery(values) {
|
|
2826
|
+
return compactObject({
|
|
2827
|
+
principal_account_user_id: values.principal,
|
|
2828
|
+
motion_id: values.motion,
|
|
2829
|
+
list_id: values.list,
|
|
2830
|
+
stage: values.stage,
|
|
2831
|
+
query: values.query,
|
|
2832
|
+
opportunity_kind: "prospect",
|
|
2833
|
+
writing_status: "draft_failed"
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2719
2837
|
function operatorNextOutcomeStatus(values) {
|
|
2720
2838
|
const selected = [
|
|
2721
2839
|
values.done ? "done" : null,
|
|
@@ -3901,6 +4019,95 @@ function renderProspect(prospect, context) {
|
|
|
3901
4019
|
if (nextActionLabel(prospect?.queue)) writeLine(context.stdout, `Next action: ${nextActionLabel(prospect.queue)}`);
|
|
3902
4020
|
}
|
|
3903
4021
|
|
|
4022
|
+
function renderProspectReplan(payload, context) {
|
|
4023
|
+
const prospect = payload?.prospect || {};
|
|
4024
|
+
const current = payload?.current || {};
|
|
4025
|
+
const replanned = payload?.replanned || {};
|
|
4026
|
+
const status = replanStatusLabel(payload);
|
|
4027
|
+
|
|
4028
|
+
writeLine(context.stdout, `Replan ${status} for ${display(prospect.display_name || prospect.name)} (${display(prospect.prefix_id)}).`);
|
|
4029
|
+
writeLine(context.stdout, `Changed: ${payload?.changed ? "yes" : "no"}`);
|
|
4030
|
+
if (payload?.reason_code) writeLine(context.stdout, `Reason: ${payload.reason_code}`);
|
|
4031
|
+
writeLine(context.stdout, `Current: ${formatCoachAction(current.next_action)}`);
|
|
4032
|
+
writeLine(context.stdout, `Replanned: ${formatCoachAction(replanned.next_action)}`);
|
|
4033
|
+
if (replanned.rationale) writeLine(context.stdout, `Rationale: ${replanned.rationale}`);
|
|
4034
|
+
if (replanned.guidance) writeLine(context.stdout, `Guidance: ${replanned.guidance}`);
|
|
4035
|
+
if (payload?.status === "dry_run") writeLine(context.stdout, "Run again with --apply to persist this plan.");
|
|
4036
|
+
if (payload?.status === "coach_error") writeLine(context.stdout, "The plan was not persisted. Fix the coach error and rerun with --apply.");
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
function renderProspectReenrich(payload, context) {
|
|
4040
|
+
const prospect = payload?.prospect || {};
|
|
4041
|
+
const profile = payload?.profile || {};
|
|
4042
|
+
const completeness = payload?.completeness || {};
|
|
4043
|
+
const enrichment = payload?.enrichment || {};
|
|
4044
|
+
const status = payload?.status === "queued" ? "queued" : (payload?.status === "not_queued" ? "not queued" : "dry run");
|
|
4045
|
+
|
|
4046
|
+
writeLine(context.stdout, `Re-enrich ${status} for ${display(prospect.display_name || prospect.name)} (${display(prospect.prefix_id)}).`);
|
|
4047
|
+
writeLine(context.stdout, `Profile: ${display(profile.prefix_id)} ${display(profile.identifier)} ${display(profile.url)}`);
|
|
4048
|
+
writeLine(context.stdout, `Profile status: ${display(profile.status)}`);
|
|
4049
|
+
writeLine(context.stdout, `Thin profile signals: ${completeness.thin_profile_signals ? "yes" : "no"}`);
|
|
4050
|
+
writeLine(context.stdout, `Experience entries: ${sumObjectValues(completeness.experience_counts)}`);
|
|
4051
|
+
writeLine(context.stdout, `Substantive bio length: ${display(completeness.substantive_bio_length, 0)}`);
|
|
4052
|
+
if (profile.invalid_reason) writeLine(context.stdout, `Invalid reason: ${profile.invalid_reason}`);
|
|
4053
|
+
if (enrichment.retry_counter_reset) writeLine(context.stdout, "A stale enrichment retry counter will be cleared.");
|
|
4054
|
+
if (enrichment.next_enrichment_at) writeLine(context.stdout, `Next enrichment at: ${enrichment.next_enrichment_at}`);
|
|
4055
|
+
if (payload?.status === "dry_run") writeLine(context.stdout, "Run again with --apply to queue full enrichment.");
|
|
4056
|
+
if (payload?.status === "not_queued") writeLine(context.stdout, "The profile was not queued, usually because an active enrichment is already running.");
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
function renderProspectRefreshQueue(payload, context) {
|
|
4060
|
+
const prospect = payload?.prospect || {};
|
|
4061
|
+
const draftCache = payload?.draft_cache || {};
|
|
4062
|
+
const coachCache = payload?.coach_cache || {};
|
|
4063
|
+
const refresh = payload?.queue_refresh || {};
|
|
4064
|
+
const status = payload?.status === "applied" ? "applied" : "dry run";
|
|
4065
|
+
|
|
4066
|
+
writeLine(context.stdout, `Refresh queue ${status} for ${display(prospect.display_name || prospect.name)} (${display(prospect.prefix_id)}).`);
|
|
4067
|
+
writeLine(context.stdout, `Coach cache: ${coachCache.present ? "present" : "empty"}`);
|
|
4068
|
+
if (coachCache.evaluated_at) writeLine(context.stdout, `Coach evaluated at: ${coachCache.evaluated_at}`);
|
|
4069
|
+
writeLine(context.stdout, `Draft cache rows: ${display(draftCache.count, 0)}`);
|
|
4070
|
+
writeLine(context.stdout, `Draft statuses: ${formatObjectCounts(draftCache.by_status)}`);
|
|
4071
|
+
writeLine(context.stdout, `Action types: ${Array.isArray(draftCache.action_types) && draftCache.action_types.length > 0 ? draftCache.action_types.join(", ") : "none"}`);
|
|
4072
|
+
if (payload?.applied) {
|
|
4073
|
+
writeLine(context.stdout, `Deleted draft rows: ${display(draftCache.deleted_count, 0)}`);
|
|
4074
|
+
writeLine(context.stdout, `Draft prewarm queued: ${display(refresh?.draft_prewarm?.enqueued_rows, 0)}`);
|
|
4075
|
+
} else {
|
|
4076
|
+
writeLine(context.stdout, "Run again with --apply to clear coach/draft cache and rebuild the queue row.");
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
function sumObjectValues(value) {
|
|
4081
|
+
if (!value || typeof value !== "object") return 0;
|
|
4082
|
+
|
|
4083
|
+
return Object.values(value).reduce((sum, item) => sum + Number(item || 0), 0);
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
function formatObjectCounts(value) {
|
|
4087
|
+
if (!value || typeof value !== "object" || Object.keys(value).length === 0) return "none";
|
|
4088
|
+
|
|
4089
|
+
return Object.entries(value)
|
|
4090
|
+
.map(([key, count]) => `${key}:${count}`)
|
|
4091
|
+
.join(", ");
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
function replanStatusLabel(payload) {
|
|
4095
|
+
if (payload?.status === "dry_run") return "dry run";
|
|
4096
|
+
if (payload?.status === "coach_error") return "coach error";
|
|
4097
|
+
if (payload?.status === "not_applied") return "not applied";
|
|
4098
|
+
|
|
4099
|
+
return payload?.applied ? "applied" : "dry run";
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
function formatCoachAction(nextAction = {}) {
|
|
4103
|
+
const action = nextAction?.label || nextAction?.type || "none";
|
|
4104
|
+
const mode = nextAction?.request_mode || nextAction?.mode;
|
|
4105
|
+
const timing = nextAction?.timing?.mode;
|
|
4106
|
+
const details = compactText([mode, timing]).join(", ");
|
|
4107
|
+
|
|
4108
|
+
return details ? `${action} (${details})` : action;
|
|
4109
|
+
}
|
|
4110
|
+
|
|
3904
4111
|
function renderProspectDisposition(payload, context, { action }) {
|
|
3905
4112
|
const prospect = payload?.prospect || {};
|
|
3906
4113
|
const accountProspect = payload?.account_prospect || {};
|
|
@@ -4597,6 +4804,87 @@ function renderOperatorOutcome(payload, context) {
|
|
|
4597
4804
|
}
|
|
4598
4805
|
}
|
|
4599
4806
|
|
|
4807
|
+
function renderOperatorFailedDrafts(payload, context) {
|
|
4808
|
+
const rows = Array.isArray(payload?.decision_queue) ? payload.decision_queue : [];
|
|
4809
|
+
if (rows.length === 0) {
|
|
4810
|
+
writeLine(context.stdout, "No failed operator drafts found.");
|
|
4811
|
+
return;
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
writeLine(context.stdout, "Failed operator drafts");
|
|
4815
|
+
writeAlignedTable(context, ["ROW ID", "PROSPECT", "MOTION", "STATUS", "REASON", "DRAFT"], rows.map(operatorFailedDraftTableRow));
|
|
4816
|
+
writeLine(context.stdout, "");
|
|
4817
|
+
writeLine(context.stdout, `Shown: ${rows.length}`);
|
|
4818
|
+
writeLine(context.stdout, "Requeue: audienti operator failed-drafts requeue <row_id> [row_id...]");
|
|
4819
|
+
writeLine(context.stdout, "If a rewrite fails again, it remains in this list with the latest failure reason.");
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
function renderOperatorFailedDraftRequeue(payload, context) {
|
|
4823
|
+
const metrics = payload?.metrics || {};
|
|
4824
|
+
const queued = Number(metrics.queued_count || 0);
|
|
4825
|
+
const skipped = Number(metrics.skipped_count || 0);
|
|
4826
|
+
const failed = Number(metrics.failed_count || 0);
|
|
4827
|
+
writeLine(context.stdout, `Queued draft rewrites: ${queued}`);
|
|
4828
|
+
if (skipped > 0) writeLine(context.stdout, `Skipped: ${skipped}`);
|
|
4829
|
+
if (failed > 0) writeLine(context.stdout, `Failed: ${failed}`);
|
|
4830
|
+
|
|
4831
|
+
writeOperatorFailedDraftRequeueDetails(context, "Queued", payload?.queued);
|
|
4832
|
+
writeOperatorFailedDraftRequeueDetails(context, "Skipped", payload?.skipped);
|
|
4833
|
+
writeOperatorFailedDraftRequeueDetails(context, "Failed", payload?.failed);
|
|
4834
|
+
|
|
4835
|
+
const message = payload?.message ||
|
|
4836
|
+
"Rewrites run asynchronously. Re-run `audienti operator failed-drafts` with the same filters to see drafts that still fail after rewriting.";
|
|
4837
|
+
writeLine(context.stdout, "");
|
|
4838
|
+
writeLine(context.stdout, message);
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
function writeOperatorFailedDraftRequeueDetails(context, label, rows) {
|
|
4842
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
4843
|
+
if (list.length === 0) return;
|
|
4844
|
+
|
|
4845
|
+
writeLine(context.stdout, "");
|
|
4846
|
+
writeLine(context.stdout, label);
|
|
4847
|
+
writeAlignedTable(context, ["ROW ID", "PROSPECT", "REASON", "DRAFT ID", "JOB ID"], list.map((row) => [
|
|
4848
|
+
display(row.row_id),
|
|
4849
|
+
display(row.prospect_name || row.prospect_id),
|
|
4850
|
+
display(row.reason),
|
|
4851
|
+
display(row.draft_id || row.source_draft_id),
|
|
4852
|
+
display(row.job_id)
|
|
4853
|
+
]));
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4856
|
+
function operatorFailedDraftTableRow(row) {
|
|
4857
|
+
const draft = row?.operator_draft || {};
|
|
4858
|
+
return [
|
|
4859
|
+
display(row?.id),
|
|
4860
|
+
operatorSubjectLabel(row),
|
|
4861
|
+
operatorMotionLabel(row),
|
|
4862
|
+
display(draft.status || draft.state),
|
|
4863
|
+
operatorFailedDraftReason(draft),
|
|
4864
|
+
operatorFailedDraftSnippet(draft)
|
|
4865
|
+
];
|
|
4866
|
+
}
|
|
4867
|
+
|
|
4868
|
+
function operatorFailedDraftReason(draft) {
|
|
4869
|
+
const payload = draft?.payload || {};
|
|
4870
|
+
const codes = Array.isArray(payload.quality_codes) ? payload.quality_codes.filter(Boolean).join(",") : "";
|
|
4871
|
+
return display(
|
|
4872
|
+
codes ||
|
|
4873
|
+
payload.blank_reason ||
|
|
4874
|
+
payload.error ||
|
|
4875
|
+
payload.message ||
|
|
4876
|
+
payload.status ||
|
|
4877
|
+
draft?.status ||
|
|
4878
|
+
draft?.state,
|
|
4879
|
+
"-"
|
|
4880
|
+
);
|
|
4881
|
+
}
|
|
4882
|
+
|
|
4883
|
+
function operatorFailedDraftSnippet(draft) {
|
|
4884
|
+
const payload = draft?.payload || {};
|
|
4885
|
+
return truncateCliText(payload.subject || payload.body || payload.text || "", 64) || "-";
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4600
4888
|
function renderAnalyticsProspects(payload, context) {
|
|
4601
4889
|
writeLine(context.stdout, `Prospect analytics (${analyticsWindowLabel(payload)})`);
|
|
4602
4890
|
writeAnalyticsCohort(payload, context);
|
|
@@ -5379,6 +5667,9 @@ const HELP_TOPICS = new Map([
|
|
|
5379
5667
|
" audienti prospects show <prsp_id>",
|
|
5380
5668
|
" audienti prospects assign <prsp_id> --assigned-user <id|me|unassign>",
|
|
5381
5669
|
" audienti prospects set-status <prsp_id> --status <active|nurture|non_responsive|not_fit|bad_data_404|rejected>",
|
|
5670
|
+
" audienti prospects replan <prsp_id> [--apply]",
|
|
5671
|
+
" audienti prospects reenrich <prsp_id> [--apply]",
|
|
5672
|
+
" audienti prospects refresh-queue <prsp_id> [--apply]",
|
|
5382
5673
|
" audienti prospects lock <prsp_id> [--note <text>]",
|
|
5383
5674
|
" audienti prospects reject <prsp_id>",
|
|
5384
5675
|
" audienti prospects nurture <prsp_id> [--reason <reason>]",
|
|
@@ -5417,11 +5708,13 @@ const HELP_TOPICS = new Map([
|
|
|
5417
5708
|
" audienti prospects write <prsp_id> --type <surface_key>",
|
|
5418
5709
|
" audienti prospects sequence-export <prsp_id>",
|
|
5419
5710
|
"",
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
"
|
|
5711
|
+
" Operator queue",
|
|
5712
|
+
" audienti operator next --plan",
|
|
5713
|
+
" audienti operator next --done --note <text>",
|
|
5714
|
+
" audienti operator queue",
|
|
5715
|
+
" audienti operator failed-drafts",
|
|
5716
|
+
" audienti operator failed-drafts requeue <row_id>",
|
|
5717
|
+
"",
|
|
5425
5718
|
" Analytics",
|
|
5426
5719
|
" audienti analytics prospects --window 24h",
|
|
5427
5720
|
" audienti analytics dashboard --play-tag <tag>",
|
|
@@ -6881,6 +7174,9 @@ const HELP_TOPICS = new Map([
|
|
|
6881
7174
|
" audienti prospects show <prsp_id> [--json]",
|
|
6882
7175
|
" audienti prospects assign <prsp_id> [prsp_id...] --assigned-user <id|me|unassign> [--json]",
|
|
6883
7176
|
" audienti prospects set-status <prsp_id> --status <active|nurture|non_responsive|not_fit|bad_data_404|rejected> [--json]",
|
|
7177
|
+
" audienti prospects replan <prsp_id> [--apply] [--json]",
|
|
7178
|
+
" audienti prospects reenrich <prsp_id> [--profile <prof_id|citation_id>] [--apply] [--json]",
|
|
7179
|
+
" audienti prospects refresh-queue <prsp_id> [--apply] [--json]",
|
|
6884
7180
|
" audienti prospects reject <prsp_id> [--json]",
|
|
6885
7181
|
" audienti prospects nurture <prsp_id> [--reason <reason>] [--json]",
|
|
6886
7182
|
" audienti prospects restore <prsp_id> [--json]",
|
|
@@ -7062,6 +7358,73 @@ const HELP_TOPICS = new Map([
|
|
|
7062
7358
|
" GET /api/v1/accounts/:account_id/prospects/:id.json"
|
|
7063
7359
|
].join("\n")],
|
|
7064
7360
|
|
|
7361
|
+
["prospects replan", [
|
|
7362
|
+
"Usage:",
|
|
7363
|
+
` ${PROSPECTS_REPLAN_USAGE.slice("Usage: ".length)}`,
|
|
7364
|
+
"",
|
|
7365
|
+
"Status: implemented",
|
|
7366
|
+
"",
|
|
7367
|
+
"Purpose:",
|
|
7368
|
+
" Re-run the next-action coach for one account prospect from the CLI without adding a product UI button.",
|
|
7369
|
+
"",
|
|
7370
|
+
"Behavior:",
|
|
7371
|
+
" Defaults to a dry-run so operators can compare the cached plan with the current planner output.",
|
|
7372
|
+
" Pass --apply to persist the replanned AccountProspect coach payload.",
|
|
7373
|
+
"",
|
|
7374
|
+
"API:",
|
|
7375
|
+
" POST /api/v1/accounts/:account_id/prospects/:id/replan.json",
|
|
7376
|
+
"",
|
|
7377
|
+
"JSON body:",
|
|
7378
|
+
" {",
|
|
7379
|
+
" \"apply\": true",
|
|
7380
|
+
" }"
|
|
7381
|
+
].join("\n")],
|
|
7382
|
+
|
|
7383
|
+
["prospects reenrich", [
|
|
7384
|
+
"Usage:",
|
|
7385
|
+
` ${PROSPECTS_REENRICH_USAGE.slice("Usage: ".length)}`,
|
|
7386
|
+
"",
|
|
7387
|
+
"Status: implemented",
|
|
7388
|
+
"",
|
|
7389
|
+
"Purpose:",
|
|
7390
|
+
" Force a full LinkedIn person profile enrichment for one prospect from the CLI.",
|
|
7391
|
+
"",
|
|
7392
|
+
"Behavior:",
|
|
7393
|
+
" Defaults to a dry-run showing the selected profile, completeness signals, and whether a stale retry counter would be cleared.",
|
|
7394
|
+
" Pass --apply to queue ProfileEnrichJob with a maintenance origin and schedule expansion after enrichment.",
|
|
7395
|
+
"",
|
|
7396
|
+
"API:",
|
|
7397
|
+
" POST /api/v1/accounts/:account_id/prospects/:id/reenrich.json",
|
|
7398
|
+
"",
|
|
7399
|
+
"JSON body:",
|
|
7400
|
+
" {",
|
|
7401
|
+
" \"apply\": true,",
|
|
7402
|
+
" \"profile_id\": \"prof_abc123\"",
|
|
7403
|
+
" }"
|
|
7404
|
+
].join("\n")],
|
|
7405
|
+
|
|
7406
|
+
["prospects refresh-queue", [
|
|
7407
|
+
"Usage:",
|
|
7408
|
+
` ${PROSPECTS_REFRESH_QUEUE_USAGE.slice("Usage: ".length)}`,
|
|
7409
|
+
"",
|
|
7410
|
+
"Status: implemented",
|
|
7411
|
+
"",
|
|
7412
|
+
"Purpose:",
|
|
7413
|
+
" Clear stale next-action coach and operator draft cache for one account prospect, then rebuild the queue row.",
|
|
7414
|
+
"",
|
|
7415
|
+
"Behavior:",
|
|
7416
|
+
" Defaults to a dry-run showing coach-cache and draft-cache state.",
|
|
7417
|
+
" Pass --apply to delete this prospect's account-scoped ProspectDraft rows, clear AccountProspect coach cache, and force the queue refresher.",
|
|
7418
|
+
"",
|
|
7419
|
+
"API:",
|
|
7420
|
+
" POST /api/v1/accounts/:account_id/prospects/:id/refresh_queue.json",
|
|
7421
|
+
"",
|
|
7422
|
+
"JSON body:",
|
|
7423
|
+
" {",
|
|
7424
|
+
" \"apply\": true",
|
|
7425
|
+
" }"
|
|
7426
|
+
].join("\n")],
|
|
7427
|
+
|
|
7065
7428
|
["prospects reject", [
|
|
7066
7429
|
"Usage:",
|
|
7067
7430
|
` ${PROSPECTS_REJECT_USAGE.slice("Usage: ".length)}`,
|
|
@@ -7682,9 +8045,11 @@ const HELP_TOPICS = new Map([
|
|
|
7682
8045
|
"Usage:",
|
|
7683
8046
|
" audienti operator next [--json|--plan|--done|--skip|--fail|--return]",
|
|
7684
8047
|
" audienti operator queue [--json]",
|
|
8048
|
+
" audienti operator failed-drafts [--json]",
|
|
8049
|
+
" audienti operator failed-drafts requeue (--all | <row_id> [row_id...])",
|
|
7685
8050
|
" audienti operator outcome <row_id> --payload <file.json>",
|
|
7686
8051
|
"",
|
|
7687
|
-
"Status: read commands and prospect next-move writeback implemented",
|
|
8052
|
+
"Status: read commands, failed draft requeue, and prospect next-move writeback implemented",
|
|
7688
8053
|
"",
|
|
7689
8054
|
"Filters:",
|
|
7690
8055
|
" --principal <account_user_id>",
|
|
@@ -7742,6 +8107,37 @@ const HELP_TOPICS = new Map([
|
|
|
7742
8107
|
" GET /api/v1/accounts/:account_id/operator.json"
|
|
7743
8108
|
].join("\n")],
|
|
7744
8109
|
|
|
8110
|
+
["operator failed-drafts", [
|
|
8111
|
+
"Usage:",
|
|
8112
|
+
" audienti operator failed-drafts [--json] [filters] [--account <acct_id>]",
|
|
8113
|
+
" audienti operator failed-drafts requeue (--all | <row_id> [row_id...]) [--limit <n>] [--json] [filters] [--account <acct_id>]",
|
|
8114
|
+
"",
|
|
8115
|
+
"Status: implemented",
|
|
8116
|
+
"",
|
|
8117
|
+
"Purpose:",
|
|
8118
|
+
" Lists failed prospect operator drafts and queues selected drafts for rewriting.",
|
|
8119
|
+
"",
|
|
8120
|
+
"Filters:",
|
|
8121
|
+
" --principal <account_user_id>",
|
|
8122
|
+
" --motion <motn_id>",
|
|
8123
|
+
" --list <list_id>",
|
|
8124
|
+
" --stage <stage>",
|
|
8125
|
+
" --query <text>",
|
|
8126
|
+
"",
|
|
8127
|
+
"Output:",
|
|
8128
|
+
" Plain text: failed draft rows with status, reason, and draft snippet",
|
|
8129
|
+
" JSON list: standard operator queue payload forced to prospect draft_failed rows",
|
|
8130
|
+
" JSON requeue: { status, queued[], skipped[], failed[], metrics, message }",
|
|
8131
|
+
"",
|
|
8132
|
+
"Notes:",
|
|
8133
|
+
" Requeue is async. A queued response means the rewrite job was accepted, not that the draft passed.",
|
|
8134
|
+
" If a rewrite fails again, rerun the list command with the same filters to see the latest failure reason.",
|
|
8135
|
+
"",
|
|
8136
|
+
"API:",
|
|
8137
|
+
" GET /api/v1/accounts/:account_id/operator.json?opportunity_kind=prospect&writing_status=draft_failed",
|
|
8138
|
+
" POST /api/v1/accounts/:account_id/operator/failed_drafts/requeue.json"
|
|
8139
|
+
].join("\n")],
|
|
8140
|
+
|
|
7745
8141
|
["operator outcome", [
|
|
7746
8142
|
"Usage:",
|
|
7747
8143
|
" audienti operator outcome <row_id> --payload <file.json> [--json] [--account <acct_id>]",
|
|
@@ -8008,11 +8404,13 @@ const HELP_TOPICS = new Map([
|
|
|
8008
8404
|
" audienti lists add-prospects <list_id> <prsp_id> [prsp_id...]",
|
|
8009
8405
|
" audienti motions add-prospects <motn_id> <prsp_id> [prsp_id...]",
|
|
8010
8406
|
"",
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8407
|
+
"6. Work the operator queue",
|
|
8408
|
+
" audienti operator next",
|
|
8409
|
+
" audienti operator next --plan",
|
|
8410
|
+
" audienti operator queue --json",
|
|
8411
|
+
" audienti operator failed-drafts",
|
|
8412
|
+
" audienti operator failed-drafts requeue <row_id>",
|
|
8413
|
+
" audienti operator outcome <row_id> --payload <file.json>",
|
|
8016
8414
|
"",
|
|
8017
8415
|
"7. Inspect account analytics",
|
|
8018
8416
|
" audienti users activity me --window 7d",
|