@audienti/cli 0.1.35 → 0.1.37
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 +16 -0
- package/README.md +36 -1
- package/package.json +1 -1
- package/skills/audienti/SKILL.md +20 -1
- package/src/api-client.js +38 -0
- package/src/cli.js +259 -13
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to the Audienti CLI are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.37] - 2026-08-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Add motion start/end scheduling and maximum-company discovery-cap configuration to motion API readback and CLI create/update/show workflows.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Show conversion totals, stage SLA, and oldest stage age in the default `audienti analytics stages` output so it matches the dashboard read model without requiring `--json`.
|
|
16
|
+
|
|
17
|
+
## [0.1.36] - 2026-08-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Add `audienti lists routing-rules` commands to inspect, create, update, remove, reorder, and apply the same account-scoped list routing rules available in the UI.
|
|
22
|
+
|
|
7
23
|
## [0.1.35] - 2026-08-18
|
|
8
24
|
|
|
9
25
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Audienti CLI is the agent-first command-line client for the Audienti production
|
|
|
4
4
|
API and the local bridge for Audienti's app-hosted MCP endpoint. It lets local
|
|
5
5
|
coding agents and operators inspect accounts,
|
|
6
6
|
create and manage plays, import prospects, build lists, manage task reminders,
|
|
7
|
-
and work supported operator flows.
|
|
7
|
+
configure list routing rules, and work supported operator flows.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -143,6 +143,7 @@ audienti motions quick-start --url https://example.com --wait --confirm
|
|
|
143
143
|
audienti motions abm-companies <motn_id> add --file abm-domains.txt
|
|
144
144
|
audienti motions abm-companies <motn_id> list
|
|
145
145
|
audienti motions update <motn_id> --status paused
|
|
146
|
+
audienti motions update <motn_id> --start-date 2026-09-01 --end-date 2026-09-30 --maximum-company-count 25
|
|
146
147
|
audienti motions update <motn_id> --own-post-engagement true
|
|
147
148
|
audienti motions update <motn_id> --payload motion-signals.json
|
|
148
149
|
audienti motions activate <motn_id>
|
|
@@ -163,6 +164,8 @@ audienti tasks add --title "Review Cristina" --due 2026-07-24T11:00 --prospect <
|
|
|
163
164
|
audienti tasks complete <ptsk_id>
|
|
164
165
|
audienti dnc list
|
|
165
166
|
audienti company-rules list
|
|
167
|
+
audienti lists routing-rules <list_id> list
|
|
168
|
+
audienti lists routing-rules <list_id> apply
|
|
166
169
|
audienti users activity --window 7d
|
|
167
170
|
audienti analytics prospects --window 24h
|
|
168
171
|
audienti analytics dashboard --play-tag wine_campaign
|
|
@@ -201,6 +204,38 @@ audienti tasks add --title "Review target account" --due 2026-07-24T11:00 --list
|
|
|
201
204
|
audienti tasks complete <ptsk_id>
|
|
202
205
|
```
|
|
203
206
|
|
|
207
|
+
To manage the same ordered rules shown in a list's Routing Rules UI, use a JSON
|
|
208
|
+
payload for create and update operations:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
audienti lists routing-rules <list_id> list
|
|
212
|
+
audienti lists routing-rules <list_id> create --payload routing-rule.json
|
|
213
|
+
audienti lists routing-rules <list_id> update <rule_id> --payload routing-rule.json
|
|
214
|
+
audienti lists routing-rules <list_id> move <rule_id> up
|
|
215
|
+
audienti lists routing-rules <list_id> remove <rule_id>
|
|
216
|
+
audienti lists routing-rules <list_id> apply
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"name": "VP prospects to Alice",
|
|
222
|
+
"enabled": true,
|
|
223
|
+
"action_kind": "assign_user",
|
|
224
|
+
"target_account_user_id": "alice@example.com",
|
|
225
|
+
"conditions": {
|
|
226
|
+
"seniorities": [
|
|
227
|
+
{"id": "5", "name": "Vice President", "negative": false}
|
|
228
|
+
],
|
|
229
|
+
"seniority_match_mode": "at_least"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Use `action_kind: "route_to_list"` with `target_list_id` to route to another
|
|
235
|
+
working list. `apply` queues the existing list-routing background job, just like
|
|
236
|
+
the UI; it reports that work was enqueued, not that every prospect has finished
|
|
237
|
+
routing. Run `audienti lists routing-rules help` for the full condition contract.
|
|
238
|
+
|
|
204
239
|
To inspect activity for prospects that entered the account during a specific
|
|
205
240
|
cohort while keeping a separate activity window:
|
|
206
241
|
|
package/package.json
CHANGED
package/skills/audienti/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: audienti
|
|
3
|
-
description: Use when the user wants to operate Audienti through the production CLI or app-hosted MCP endpoint, including account selection, plays, prospect imports, lists, message previews, analytics, or supported operator outcomes.
|
|
3
|
+
description: Use when the user wants to operate Audienti through the production CLI or app-hosted MCP endpoint, including account selection, plays, prospect imports, lists and routing rules, message previews, analytics, or supported operator outcomes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Audienti CLI and MCP
|
|
@@ -102,15 +102,21 @@ audienti prospects unlock <prsp_id> --json
|
|
|
102
102
|
audienti users activity me --window 7d --json
|
|
103
103
|
audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
|
|
104
104
|
audienti lists create --name "Target list" --json
|
|
105
|
+
audienti lists routing-rules <list_id> list --json
|
|
106
|
+
audienti lists routing-rules <list_id> create --payload routing-rule.json --json
|
|
107
|
+
audienti lists routing-rules <list_id> move <rule_id> up --json
|
|
108
|
+
audienti lists routing-rules <list_id> apply --json
|
|
105
109
|
audienti motions quick-start --url https://example.com --wait --confirm --json
|
|
106
110
|
audienti motions abm-companies <motn_id> add --file abm-domains.txt --json
|
|
107
111
|
audienti motions abm-companies <motn_id> list --json
|
|
108
112
|
audienti motions update <motn_id> --status paused --json
|
|
113
|
+
audienti motions update <motn_id> --start-date 2026-09-01 --end-date 2026-09-30 --maximum-company-count 25 --json
|
|
109
114
|
audienti motions activate <motn_id> --json
|
|
110
115
|
audienti motions delete <motn_id> --confirm yes --json
|
|
111
116
|
audienti operator next --json
|
|
112
117
|
audienti operator next --plan
|
|
113
118
|
audienti analytics prospects --window 24h --json
|
|
119
|
+
audienti analytics stages --window 30d
|
|
114
120
|
audienti analytics visibility --window 24h --user me --json
|
|
115
121
|
audienti analytics content --window week --json
|
|
116
122
|
audienti tools list --json
|
|
@@ -125,6 +131,19 @@ audienti writer test-run <prsp_id> --mode step --branch no-accept --step 3 --rep
|
|
|
125
131
|
audienti writer test-run show <prsp_id> <rprt_id>
|
|
126
132
|
```
|
|
127
133
|
|
|
134
|
+
List routing-rule create and update commands accept the same normalized
|
|
135
|
+
condition and action data as the list UI. Inspect the ordered rules first, use
|
|
136
|
+
`audienti lists routing-rules help` for the payload contract, and treat `apply`
|
|
137
|
+
as an asynchronous relaunch: it queues the existing routing job and does not
|
|
138
|
+
mean every list member has finished routing.
|
|
139
|
+
|
|
140
|
+
Motion start and end dates are optional lifecycle controls. A due start can
|
|
141
|
+
activate a configured preparing or paused motion; an end date is the final
|
|
142
|
+
active day and pauses the motion afterward. `--maximum-company-count` caps
|
|
143
|
+
durable company admissions only: research and people discovery for admitted
|
|
144
|
+
companies, prospect intake, and automation continue. Use `none` to clear any
|
|
145
|
+
of these settings.
|
|
146
|
+
|
|
128
147
|
`audienti writer test-run <prsp_id>` starts a report-backed writing session and
|
|
129
148
|
builds the campaign timeline without drafting every message. The printed
|
|
130
149
|
`Report: rprt_...` id is the session handle. Pass `--report <rprt_id>` when
|
package/src/api-client.js
CHANGED
|
@@ -268,6 +268,44 @@ export class AudientiClient {
|
|
|
268
268
|
});
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
listRoutingRules(accountId, listId) {
|
|
272
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules"]));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
createListRoutingRule(accountId, listId, body) {
|
|
276
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules"]), {
|
|
277
|
+
method: "POST",
|
|
278
|
+
body
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
updateListRoutingRule(accountId, listId, ruleId, body) {
|
|
283
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules", ruleId]), {
|
|
284
|
+
method: "PATCH",
|
|
285
|
+
body
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
removeListRoutingRule(accountId, listId, ruleId) {
|
|
290
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules", ruleId]), {
|
|
291
|
+
method: "DELETE"
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
moveListRoutingRule(accountId, listId, ruleId, body) {
|
|
296
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules", ruleId, "move"]), {
|
|
297
|
+
method: "PATCH",
|
|
298
|
+
body
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
applyListRoutingRules(accountId, listId) {
|
|
303
|
+
return this.requestJson(accountPath(accountId, ["lists", listId, "routing_rules", "apply"]), {
|
|
304
|
+
method: "POST",
|
|
305
|
+
body: {}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
271
309
|
motions(accountId) {
|
|
272
310
|
return this.requestJson(accountPath(accountId, ["motions"]));
|
|
273
311
|
}
|
package/src/cli.js
CHANGED
|
@@ -72,7 +72,7 @@ const OFFERS_DELETE_USAGE = "Usage: audienti offers delete <offr_id> --confirm <
|
|
|
72
72
|
const WRITER_TEST_RUN_USAGE = "Usage: audienti writer test-run <prsp_id> [--json] [--mode <plan|report|step>] [--branch <both|no-accept|accepted>] [--step <step_key|row_number>] [--report <rprt_id>] [--no-wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--account <acct_id>]";
|
|
73
73
|
const WRITER_TEST_RUN_SHOW_USAGE = "Usage: audienti writer test-run show <prsp_id> <rprt_id> [--json] [--account <acct_id>]";
|
|
74
74
|
const MOTIONS_ANALYTICS_USAGE = "Usage: audienti motions analytics <motn_id> [--window 30d] [--json] [--account <acct_id>]";
|
|
75
|
-
const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] | --payload <file.json>) [--json] [--account <acct_id>]";
|
|
75
|
+
const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--start-date <YYYY-MM-DD|none>] [--end-date <YYYY-MM-DD|none>] [--maximum-company-count <n|none>] | --payload <file.json>) [--json] [--account <acct_id>]";
|
|
76
76
|
const CONTENT_PROGRAMS_USAGE = "Usage: audienti content programs [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
|
|
77
77
|
const CONTENT_PLAN_USAGE = "Usage: audienti content plan <cprg_id> [--week <n>] [--due] [--json] [--account <acct_id>]";
|
|
78
78
|
const CONTENT_SHOW_USAGE = "Usage: audienti content show <cpwi_id> [--json] [--account <acct_id>]";
|
|
@@ -99,6 +99,12 @@ const ICPS_ADD_TAG_USAGE = "Usage: audienti icps add-tag <icp_id> <tag> [--json]
|
|
|
99
99
|
const ICPS_REMOVE_TAG_USAGE = "Usage: audienti icps remove-tag <icp_id> <tag> [--json] [--account <acct_id>]";
|
|
100
100
|
const LISTS_ADD_TAG_USAGE = "Usage: audienti lists add-tag <list_id> <tag> [--json] [--account <acct_id>]";
|
|
101
101
|
const LISTS_REMOVE_TAG_USAGE = "Usage: audienti lists remove-tag <list_id> <tag> [--json] [--account <acct_id>]";
|
|
102
|
+
const LIST_ROUTING_RULES_LIST_USAGE = "Usage: audienti lists routing-rules <list_id> list [--json] [--account <acct_id>]";
|
|
103
|
+
const LIST_ROUTING_RULES_CREATE_USAGE = "Usage: audienti lists routing-rules <list_id> create --payload <file.json> [--json] [--account <acct_id>]";
|
|
104
|
+
const LIST_ROUTING_RULES_UPDATE_USAGE = "Usage: audienti lists routing-rules <list_id> update <rule_id> --payload <file.json> [--json] [--account <acct_id>]";
|
|
105
|
+
const LIST_ROUTING_RULES_REMOVE_USAGE = "Usage: audienti lists routing-rules <list_id> remove <rule_id> [--json] [--account <acct_id>]";
|
|
106
|
+
const LIST_ROUTING_RULES_MOVE_USAGE = "Usage: audienti lists routing-rules <list_id> move <rule_id> <up|down> [--json] [--account <acct_id>]";
|
|
107
|
+
const LIST_ROUTING_RULES_APPLY_USAGE = "Usage: audienti lists routing-rules <list_id> apply [--json] [--account <acct_id>]";
|
|
102
108
|
const ANALYTICS_PROSPECTS_USAGE = "Usage: audienti analytics prospects [--window 24h] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--motion <motn_id>] [--list <list_id>] [--provenance <source>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
|
|
103
109
|
const ANALYTICS_PROSPECTS_COHORT_ANALYSIS_USAGE = "Usage: audienti analytics prospects cohort-analysis [--weeks <n>] [--window 24h] [--motion <motn_id>] [--list <list_id>] [--provenance <source>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
|
|
104
110
|
const ANALYTICS_USERS_USAGE = "Usage: audienti analytics users [--user <account_user_id|email|name|me>] [--window 30d | --start YYYY-MM-DD --end YYYY-MM-DD] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--motion <motn_id>] [--list <list_id>] [--provenance <source>] [--platform <linkedin|email|gmail>] [--json] [--account <acct_id>]";
|
|
@@ -237,6 +243,7 @@ async function dispatch(argv, context) {
|
|
|
237
243
|
if (normalizedResource === "lists" && action === "prospects") return listProspects(rest, context, { accountOverride });
|
|
238
244
|
if (normalizedResource === "lists" && action === "add-prospects") return listsAddProspects(rest, context, { accountOverride });
|
|
239
245
|
if (normalizedResource === "lists" && action === "remove-prospects") return listsRemoveProspects(rest, context, { accountOverride });
|
|
246
|
+
if (normalizedResource === "lists" && ["routing-rules", "rules"].includes(action)) return listsRoutingRules(rest, context, { accountOverride });
|
|
240
247
|
if (normalizedResource === "motions" && action === "list") return motionsList(rest, context, { accountOverride });
|
|
241
248
|
if (normalizedResource === "motions" && action === "show") return motionsShow(rest, context, { accountOverride });
|
|
242
249
|
if (normalizedResource === "motions" && action === "status") return motionsStatus(rest, context, { accountOverride });
|
|
@@ -1491,6 +1498,104 @@ async function listsRemoveProspects(args, context, { accountOverride } = {}) {
|
|
|
1491
1498
|
return rejected ? 1 : 0;
|
|
1492
1499
|
}
|
|
1493
1500
|
|
|
1501
|
+
async function listsRoutingRules(args, context, { accountOverride } = {}) {
|
|
1502
|
+
const [listId, subaction, ...rest] = args;
|
|
1503
|
+
if (!listId || !subaction) {
|
|
1504
|
+
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json] [--account <acct_id>]");
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
if (subaction === "list") return listRoutingRulesList(listId, rest, context, { accountOverride });
|
|
1508
|
+
if (subaction === "create") return listRoutingRulesCreate(listId, rest, context, { accountOverride });
|
|
1509
|
+
if (subaction === "update") return listRoutingRulesUpdate(listId, rest, context, { accountOverride });
|
|
1510
|
+
if (["remove", "delete"].includes(subaction)) return listRoutingRulesRemove(listId, rest, context, { accountOverride });
|
|
1511
|
+
if (subaction === "move") return listRoutingRulesMove(listId, rest, context, { accountOverride });
|
|
1512
|
+
if (subaction === "apply") return listRoutingRulesApply(listId, rest, context, { accountOverride });
|
|
1513
|
+
|
|
1514
|
+
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json] [--account <acct_id>]");
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
async function listRoutingRulesList(listId, args, context, { accountOverride } = {}) {
|
|
1518
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1519
|
+
if (positionals.length > 0) throw new CommandError(LIST_ROUTING_RULES_LIST_USAGE);
|
|
1520
|
+
|
|
1521
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1522
|
+
const payload = await client.listRoutingRules(accountId, listId);
|
|
1523
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1524
|
+
|
|
1525
|
+
renderListRoutingRules(payload, context);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
async function listRoutingRulesCreate(listId, args, context, { accountOverride } = {}) {
|
|
1529
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1530
|
+
...jsonOptions(),
|
|
1531
|
+
payload: {type: "string"}
|
|
1532
|
+
});
|
|
1533
|
+
if (positionals.length > 0 || !values.payload) throw new CommandError(LIST_ROUTING_RULES_CREATE_USAGE);
|
|
1534
|
+
|
|
1535
|
+
const ruleInput = await readJsonPayload(values.payload);
|
|
1536
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1537
|
+
const payload = await client.createListRoutingRule(accountId, listId, {routing_rule: ruleInput});
|
|
1538
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1539
|
+
|
|
1540
|
+
const rule = payload?.routing_rule;
|
|
1541
|
+
writeLine(context.stdout, `Created routing rule ${display(rule?.name)} (${display(rule?.id)}) on list ${display(payload?.list_id || listId)}.`);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
async function listRoutingRulesUpdate(listId, args, context, { accountOverride } = {}) {
|
|
1545
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1546
|
+
...jsonOptions(),
|
|
1547
|
+
payload: {type: "string"}
|
|
1548
|
+
});
|
|
1549
|
+
if (positionals.length !== 1 || !values.payload) throw new CommandError(LIST_ROUTING_RULES_UPDATE_USAGE);
|
|
1550
|
+
|
|
1551
|
+
const ruleInput = await readJsonPayload(values.payload);
|
|
1552
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1553
|
+
const payload = await client.updateListRoutingRule(accountId, listId, positionals[0], {routing_rule: ruleInput});
|
|
1554
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1555
|
+
|
|
1556
|
+
const rule = payload?.routing_rule;
|
|
1557
|
+
writeLine(context.stdout, `Updated routing rule ${display(rule?.name)} (${display(rule?.id)}) on list ${display(payload?.list_id || listId)}.`);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
async function listRoutingRulesRemove(listId, args, context, { accountOverride } = {}) {
|
|
1561
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1562
|
+
if (positionals.length !== 1) throw new CommandError(LIST_ROUTING_RULES_REMOVE_USAGE);
|
|
1563
|
+
|
|
1564
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1565
|
+
const payload = await client.removeListRoutingRule(accountId, listId, positionals[0]);
|
|
1566
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1567
|
+
|
|
1568
|
+
const rule = payload?.routing_rule;
|
|
1569
|
+
writeLine(context.stdout, `Removed routing rule ${display(rule?.name)} (${display(rule?.id || positionals[0])}) from list ${display(listId)}.`);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
async function listRoutingRulesMove(listId, args, context, { accountOverride } = {}) {
|
|
1573
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1574
|
+
const [ruleId, direction] = positionals;
|
|
1575
|
+
if (positionals.length !== 2 || !["up", "down"].includes(direction)) {
|
|
1576
|
+
throw new CommandError(LIST_ROUTING_RULES_MOVE_USAGE);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1580
|
+
const payload = await client.moveListRoutingRule(accountId, listId, ruleId, {direction});
|
|
1581
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1582
|
+
|
|
1583
|
+
const verb = payload?.moved ? "Moved" : "Could not move";
|
|
1584
|
+
writeLine(context.stdout, `${verb} routing rule ${display(ruleId)} ${direction} on list ${display(payload?.list_id || listId)}.`);
|
|
1585
|
+
renderListRoutingRules(payload, context);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
async function listRoutingRulesApply(listId, args, context, { accountOverride } = {}) {
|
|
1589
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1590
|
+
if (positionals.length > 0) throw new CommandError(LIST_ROUTING_RULES_APPLY_USAGE);
|
|
1591
|
+
|
|
1592
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1593
|
+
const payload = await client.applyListRoutingRules(accountId, listId);
|
|
1594
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1595
|
+
|
|
1596
|
+
writeLine(context.stdout, `Routing rules will be applied in the background to list ${display(payload?.list_id || listId)}.`);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1494
1599
|
async function motionsList(args, context, { accountOverride } = {}) {
|
|
1495
1600
|
const { values, positionals } = parseCommandArgs(args, {
|
|
1496
1601
|
...jsonOptions(),
|
|
@@ -1776,7 +1881,10 @@ async function motionsUpdate(args, context, { accountOverride } = {}) {
|
|
|
1776
1881
|
payload: { type: "string" },
|
|
1777
1882
|
status: { type: "string" },
|
|
1778
1883
|
tags: { type: "string" },
|
|
1779
|
-
"own-post-engagement": { type: "string" }
|
|
1884
|
+
"own-post-engagement": { type: "string" },
|
|
1885
|
+
"start-date": { type: "string" },
|
|
1886
|
+
"end-date": { type: "string" },
|
|
1887
|
+
"maximum-company-count": { type: "string" }
|
|
1780
1888
|
});
|
|
1781
1889
|
const hasUpdateField = values.payload || motionSimpleFieldsPresent(values);
|
|
1782
1890
|
if (positionals.length !== 1 || !hasUpdateField) {
|
|
@@ -1794,7 +1902,7 @@ async function motionsUpdate(args, context, { accountOverride } = {}) {
|
|
|
1794
1902
|
async function motionUpdatePayload(values) {
|
|
1795
1903
|
if (values.payload) {
|
|
1796
1904
|
if (motionSimpleFieldsPresent(values)) {
|
|
1797
|
-
throw new CommandError("Choose one motion input mode: either --payload <file.json> or the simple
|
|
1905
|
+
throw new CommandError("Choose one motion input mode: either --payload <file.json> or the simple motion update flags.");
|
|
1798
1906
|
}
|
|
1799
1907
|
|
|
1800
1908
|
const payload = await readJsonPayload(values.payload);
|
|
@@ -1802,15 +1910,54 @@ async function motionUpdatePayload(values) {
|
|
|
1802
1910
|
return payload;
|
|
1803
1911
|
}
|
|
1804
1912
|
|
|
1805
|
-
|
|
1913
|
+
const payload = compactObject({
|
|
1806
1914
|
status: normalizeMotionStatus(values.status),
|
|
1807
1915
|
play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
|
|
1808
1916
|
own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
|
|
1809
1917
|
});
|
|
1918
|
+
const nullableSettings = {
|
|
1919
|
+
starts_on: normalizeMotionDate(values["start-date"], "--start-date"),
|
|
1920
|
+
ends_on: normalizeMotionDate(values["end-date"], "--end-date"),
|
|
1921
|
+
maximum_company_count: normalizeMotionMaximumCompanyCount(values["maximum-company-count"])
|
|
1922
|
+
};
|
|
1923
|
+
for (const [key, value] of Object.entries(nullableSettings)) {
|
|
1924
|
+
if (value !== undefined) payload[key] = value;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
return payload;
|
|
1810
1928
|
}
|
|
1811
1929
|
|
|
1812
1930
|
function motionSimpleFieldsPresent(values) {
|
|
1813
|
-
return Boolean(values.status) ||
|
|
1931
|
+
return Boolean(values.status) ||
|
|
1932
|
+
values.tags !== undefined ||
|
|
1933
|
+
values["own-post-engagement"] !== undefined ||
|
|
1934
|
+
values["start-date"] !== undefined ||
|
|
1935
|
+
values["end-date"] !== undefined ||
|
|
1936
|
+
values["maximum-company-count"] !== undefined;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
function normalizeMotionDate(value, flagName) {
|
|
1940
|
+
if (value === undefined) return undefined;
|
|
1941
|
+
|
|
1942
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
1943
|
+
if (normalized === "none") return null;
|
|
1944
|
+
const match = normalized.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
1945
|
+
if (!match) throw new CommandError(`${flagName} must be YYYY-MM-DD or none.`);
|
|
1946
|
+
|
|
1947
|
+
const [, year, month, day] = match.map(Number);
|
|
1948
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
1949
|
+
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) {
|
|
1950
|
+
throw new CommandError(`${flagName} must be YYYY-MM-DD or none.`);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
return normalized;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
function normalizeMotionMaximumCompanyCount(value) {
|
|
1957
|
+
if (value === undefined) return undefined;
|
|
1958
|
+
if (String(value || "").trim().toLowerCase() === "none") return null;
|
|
1959
|
+
|
|
1960
|
+
return normalizeOptionalPositiveInteger(value, "--maximum-company-count");
|
|
1814
1961
|
}
|
|
1815
1962
|
|
|
1816
1963
|
function validateMotionSignalRows(payload) {
|
|
@@ -4132,6 +4279,30 @@ function renderList(list, context) {
|
|
|
4132
4279
|
if (list?.description) writeLine(context.stdout, `Description: ${list.description}`);
|
|
4133
4280
|
}
|
|
4134
4281
|
|
|
4282
|
+
function renderListRoutingRules(payload, context) {
|
|
4283
|
+
const rules = Array.isArray(payload?.routing_rules) ? payload.routing_rules : [];
|
|
4284
|
+
if (rules.length === 0) return writeLine(context.stdout, "No routing rules found.");
|
|
4285
|
+
|
|
4286
|
+
writeLine(context.stdout, "RULE ID\tPOSITION\tENABLED\tACTION\tTARGET\tNAME");
|
|
4287
|
+
for (const rule of rules) {
|
|
4288
|
+
writeLine(context.stdout, [
|
|
4289
|
+
display(rule.id),
|
|
4290
|
+
display(rule.position),
|
|
4291
|
+
rule.enabled ? "yes" : "no",
|
|
4292
|
+
display(rule.action_kind),
|
|
4293
|
+
routingRuleTargetLabel(rule),
|
|
4294
|
+
display(rule.name)
|
|
4295
|
+
].join("\t"));
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
function routingRuleTargetLabel(rule) {
|
|
4300
|
+
return rule?.target_account_user?.name ||
|
|
4301
|
+
rule?.target_account_user?.email ||
|
|
4302
|
+
rule?.target_list?.name ||
|
|
4303
|
+
"-";
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4135
4306
|
function renderTags(tags, context) {
|
|
4136
4307
|
if (!Array.isArray(tags) || tags.length === 0) return writeLine(context.stdout, "No tags found.");
|
|
4137
4308
|
|
|
@@ -4459,6 +4630,9 @@ function renderMotion(motion, context) {
|
|
|
4459
4630
|
writeLine(context.stdout, `Motion: ${display(motion?.name)} (${display(motion?.prefix_id)})`);
|
|
4460
4631
|
writeLine(context.stdout, `Status: ${display(motion?.status)}`);
|
|
4461
4632
|
writeLine(context.stdout, `Kind: ${display(motion?.kind)}`);
|
|
4633
|
+
writeLine(context.stdout, `Start date: ${display(motion?.starts_on, "not set")}`);
|
|
4634
|
+
writeLine(context.stdout, `End date: ${display(motion?.ends_on, "not set")}`);
|
|
4635
|
+
writeLine(context.stdout, `Maximum companies: ${display(motion?.maximum_company_count, "not set")}`);
|
|
4462
4636
|
renderExecutableConfiguration(motion?.executable_configuration, context);
|
|
4463
4637
|
if (motion?.offer?.name) writeLine(context.stdout, `Offer: ${motion.offer.name} (${display(motion.offer.prefix_id)})`);
|
|
4464
4638
|
if (motion?.icp?.name) writeLine(context.stdout, `ICP: ${motion.icp.name} (${display(motion.icp.prefix_id)})`);
|
|
@@ -5651,6 +5825,7 @@ function writeStageConversionTable(conversionGrid, context) {
|
|
|
5651
5825
|
const rows = Array.isArray(conversionGrid?.rows) ? conversionGrid.rows : [];
|
|
5652
5826
|
const definitions = Array.isArray(conversionGrid?.stage_definitions) ? conversionGrid.stage_definitions : [];
|
|
5653
5827
|
const columns = definitions.filter((definition) => definition?.key);
|
|
5828
|
+
const totalValues = conversionGrid?.total_values || {};
|
|
5654
5829
|
|
|
5655
5830
|
writeLine(context.stdout, "");
|
|
5656
5831
|
writeLine(context.stdout, display(conversionGrid?.window_description, "Conversion cohorts"));
|
|
@@ -5661,6 +5836,12 @@ function writeStageConversionTable(conversionGrid, context) {
|
|
|
5661
5836
|
display(row?.label),
|
|
5662
5837
|
...columns.map((column) => stageMetricValueLabel(row?.values?.[column.key]))
|
|
5663
5838
|
]);
|
|
5839
|
+
if (Object.keys(totalValues).length > 0) {
|
|
5840
|
+
tableRows.push([
|
|
5841
|
+
"TOTAL",
|
|
5842
|
+
...columns.map((column) => stageMetricValueLabel(totalValues[column.key]))
|
|
5843
|
+
]);
|
|
5844
|
+
}
|
|
5664
5845
|
|
|
5665
5846
|
writeAlignedTable(context, headers, tableRows);
|
|
5666
5847
|
}
|
|
@@ -5678,20 +5859,22 @@ function writeStageAgingTable(stageAging, context) {
|
|
|
5678
5859
|
);
|
|
5679
5860
|
writeAlignedTable(
|
|
5680
5861
|
context,
|
|
5681
|
-
["STAGE", "CURRENT", "DUE SOON", "OVERDUE", "OVERDUE %", "MED AGE", "OLDEST IDLE"],
|
|
5862
|
+
["STAGE", "SLA", "CURRENT", "DUE SOON", "OVERDUE", "OVERDUE %", "MED AGE", "OLDEST AGE", "OLDEST IDLE"],
|
|
5682
5863
|
rows.map(stageAgingRow),
|
|
5683
|
-
{ numericColumns: [false, true, true, true, true, true, true] }
|
|
5864
|
+
{ numericColumns: [false, true, true, true, true, true, true, true, true] }
|
|
5684
5865
|
);
|
|
5685
5866
|
}
|
|
5686
5867
|
|
|
5687
5868
|
function stageAgingRow(row) {
|
|
5688
5869
|
return [
|
|
5689
5870
|
display(row?.label || row?.key),
|
|
5871
|
+
dayCountLabel(row?.due_after_days),
|
|
5690
5872
|
display(row?.current_count, 0),
|
|
5691
5873
|
display(row?.due_soon_count, 0),
|
|
5692
5874
|
display(row?.overdue_count, 0),
|
|
5693
5875
|
percentageLabel(row?.overdue_rate),
|
|
5694
5876
|
dayCountLabel(row?.median_stage_age_days),
|
|
5877
|
+
dayCountLabel(row?.oldest_stage_age_days),
|
|
5695
5878
|
dayCountLabel(row?.oldest_idle_days)
|
|
5696
5879
|
];
|
|
5697
5880
|
}
|
|
@@ -6447,7 +6630,7 @@ const HELP_TOPICS = new Map([
|
|
|
6447
6630
|
" audienti motions abm-companies <motn_id> list",
|
|
6448
6631
|
" audienti motions abm-companies <motn_id> add <domain_or_linkedin_url>...",
|
|
6449
6632
|
" audienti motions create --payload <file.json>",
|
|
6450
|
-
" audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>]",
|
|
6633
|
+
" audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--start-date <date|none>] [--end-date <date|none>] [--maximum-company-count <n|none>]",
|
|
6451
6634
|
" audienti motions update <motn_id> --payload <file.json>",
|
|
6452
6635
|
" audienti motions add-tag <motn_id> <tag>",
|
|
6453
6636
|
" audienti motions remove-tag <motn_id> <tag>",
|
|
@@ -6492,6 +6675,9 @@ const HELP_TOPICS = new Map([
|
|
|
6492
6675
|
" audienti lists prospects <list_id>",
|
|
6493
6676
|
" audienti lists add-tag <list_id> <tag>",
|
|
6494
6677
|
" audienti lists remove-tag <list_id> <tag>",
|
|
6678
|
+
" audienti lists routing-rules <list_id> list",
|
|
6679
|
+
" audienti lists routing-rules <list_id> create --payload <file.json>",
|
|
6680
|
+
" audienti lists routing-rules <list_id> apply",
|
|
6495
6681
|
" audienti tags list",
|
|
6496
6682
|
" audienti tags show <tag>",
|
|
6497
6683
|
" audienti tasks list [--status open]",
|
|
@@ -7472,13 +7658,63 @@ const HELP_TOPICS = new Map([
|
|
|
7472
7658
|
" audienti lists prospects <list_id> [--json]",
|
|
7473
7659
|
" audienti lists add-prospects <list_id> <prsp_id> [prsp_id...] [--json]",
|
|
7474
7660
|
" audienti lists remove-prospects <list_id> <prsp_id> [prsp_id...] [--json]",
|
|
7661
|
+
" audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json]",
|
|
7475
7662
|
"",
|
|
7476
|
-
"Status: read, create, update, delete, and
|
|
7663
|
+
"Status: read, create, update, delete, membership, and routing-rule commands implemented",
|
|
7477
7664
|
"",
|
|
7478
7665
|
"ID shape:",
|
|
7479
7666
|
" list_id: list_ prefix id"
|
|
7480
7667
|
].join("\n")],
|
|
7481
7668
|
|
|
7669
|
+
["lists routing-rules", [
|
|
7670
|
+
"Usage:",
|
|
7671
|
+
" audienti lists routing-rules <list_id> list [--json] [--account <acct_id>]",
|
|
7672
|
+
" audienti lists routing-rules <list_id> create --payload <file.json> [--json] [--account <acct_id>]",
|
|
7673
|
+
" audienti lists routing-rules <list_id> update <rule_id> --payload <file.json> [--json] [--account <acct_id>]",
|
|
7674
|
+
" audienti lists routing-rules <list_id> remove <rule_id> [--json] [--account <acct_id>]",
|
|
7675
|
+
" audienti lists routing-rules <list_id> move <rule_id> <up|down> [--json] [--account <acct_id>]",
|
|
7676
|
+
" audienti lists routing-rules <list_id> apply [--json] [--account <acct_id>]",
|
|
7677
|
+
"",
|
|
7678
|
+
"Status: implemented",
|
|
7679
|
+
"",
|
|
7680
|
+
"Purpose:",
|
|
7681
|
+
" Configure, inspect, reorder, and relaunch the same routing rules available in the list UI.",
|
|
7682
|
+
" Apply only queues the existing background job; it does not wait for routing to finish.",
|
|
7683
|
+
"",
|
|
7684
|
+
"Payload shape:",
|
|
7685
|
+
" name: string",
|
|
7686
|
+
" enabled: boolean | optional",
|
|
7687
|
+
" position: non-negative integer | optional",
|
|
7688
|
+
" action_kind: assign_user | route_to_list",
|
|
7689
|
+
" target_account_user_id: account-user id, email, or me | required for assign_user",
|
|
7690
|
+
" target_list_id: list_ prefix id or numeric id | required for route_to_list",
|
|
7691
|
+
" conditions: object | optional",
|
|
7692
|
+
"",
|
|
7693
|
+
"Condition keys:",
|
|
7694
|
+
" locations, company_locations, seniorities, job_titles, bio_texts,",
|
|
7695
|
+
" company_sizes, company_types, company_keywords, seniority_match_mode, industry_groups",
|
|
7696
|
+
"",
|
|
7697
|
+
"Example payload file:",
|
|
7698
|
+
" {",
|
|
7699
|
+
" \"name\": \"VP prospects to Alice\",",
|
|
7700
|
+
" \"enabled\": true,",
|
|
7701
|
+
" \"action_kind\": \"assign_user\",",
|
|
7702
|
+
" \"target_account_user_id\": \"alice@example.com\",",
|
|
7703
|
+
" \"conditions\": {",
|
|
7704
|
+
" \"seniorities\": [{\"id\": \"5\", \"name\": \"Vice President\", \"negative\": false}],",
|
|
7705
|
+
" \"seniority_match_mode\": \"at_least\"",
|
|
7706
|
+
" }",
|
|
7707
|
+
" }",
|
|
7708
|
+
"",
|
|
7709
|
+
"API:",
|
|
7710
|
+
" GET /api/v1/accounts/:account_id/lists/:list_id/routing_rules.json",
|
|
7711
|
+
" POST /api/v1/accounts/:account_id/lists/:list_id/routing_rules.json",
|
|
7712
|
+
" PATCH /api/v1/accounts/:account_id/lists/:list_id/routing_rules/:id.json",
|
|
7713
|
+
" DELETE /api/v1/accounts/:account_id/lists/:list_id/routing_rules/:id.json",
|
|
7714
|
+
" PATCH /api/v1/accounts/:account_id/lists/:list_id/routing_rules/:id/move.json",
|
|
7715
|
+
" POST /api/v1/accounts/:account_id/lists/:list_id/routing_rules/apply.json"
|
|
7716
|
+
].join("\n")],
|
|
7717
|
+
|
|
7482
7718
|
["lists list", [
|
|
7483
7719
|
"Usage:",
|
|
7484
7720
|
" audienti lists list [--tag <tag>] [--json] [--account <acct_id>]",
|
|
@@ -7717,7 +7953,7 @@ const HELP_TOPICS = new Map([
|
|
|
7717
7953
|
" audienti motions abm-companies <motn_id> add (<domain_or_linkedin_url>... | --file <txt|json>) [--json]",
|
|
7718
7954
|
" audienti motions abm-companies <motn_id> remove <row_id> [--json]",
|
|
7719
7955
|
" audienti motions create --payload <file.json> [--json]",
|
|
7720
|
-
"
|
|
7956
|
+
` ${MOTIONS_UPDATE_USAGE.slice("Usage: ".length).replace(" [--account <acct_id>]", "")}`,
|
|
7721
7957
|
" audienti motions add-tag <motn_id> <tag> [--json]",
|
|
7722
7958
|
" audienti motions remove-tag <motn_id> <tag> [--json]",
|
|
7723
7959
|
" audienti motions activate <motn_id> [--json]",
|
|
@@ -7995,6 +8231,9 @@ const HELP_TOPICS = new Map([
|
|
|
7995
8231
|
" icp_id: icpp_ prefix id | optional",
|
|
7996
8232
|
" list_id: list_ prefix id | optional",
|
|
7997
8233
|
" play_tags: [string] | optional",
|
|
8234
|
+
" starts_on: YYYY-MM-DD | null | optional scheduled activation date",
|
|
8235
|
+
" ends_on: YYYY-MM-DD | null | optional final active date",
|
|
8236
|
+
" maximum_company_count: positive integer | null | optional new-company discovery cap",
|
|
7998
8237
|
" signal_rows: [{ scope: company | person | both, question: string, company_signal_category: string | optional, role_terms: string | optional, posting_language: hiring-only string | optional, topics: string | optional }] | optional for outbound motions",
|
|
7999
8238
|
" signal_questions: newline-delimited company::, person::, or both:: legacy question text | optional for outbound motions",
|
|
8000
8239
|
" inbound_channels: [linkedin | reddit] | optional; defaults to [linkedin] for inbound motions",
|
|
@@ -8009,6 +8248,9 @@ const HELP_TOPICS = new Map([
|
|
|
8009
8248
|
" \"offer_id\": \"offr_abc123\",",
|
|
8010
8249
|
" \"principal_account_user_id\": 42,",
|
|
8011
8250
|
" \"list_id\": \"list_abc123\",",
|
|
8251
|
+
" \"starts_on\": \"2026-09-01\",",
|
|
8252
|
+
" \"ends_on\": \"2026-09-30\",",
|
|
8253
|
+
" \"maximum_company_count\": 25,",
|
|
8012
8254
|
" \"play_tags\": [\"sarit\", \"pj\"]",
|
|
8013
8255
|
" }",
|
|
8014
8256
|
"",
|
|
@@ -8032,8 +8274,8 @@ const HELP_TOPICS = new Map([
|
|
|
8032
8274
|
" name: new motion name",
|
|
8033
8275
|
"",
|
|
8034
8276
|
"Behavior:",
|
|
8035
|
-
" Copies the motion kind, offer, ICP, principal, premise, approach, targeting profile, suppression policy, secondary roles, and active signal rows.",
|
|
8036
|
-
" The clone starts as draft with a new empty backing list.",
|
|
8277
|
+
" Copies the motion kind, offer, ICP, principal, premise, approach, maximum company count, targeting profile, suppression policy, secondary roles, and active signal rows.",
|
|
8278
|
+
" The clone starts as draft with a new empty backing list and no inherited schedule dates.",
|
|
8037
8279
|
"",
|
|
8038
8280
|
"API:",
|
|
8039
8281
|
" POST /api/v1/accounts/:account_id/motions/:id/clone.json",
|
|
@@ -8059,10 +8301,14 @@ const HELP_TOPICS = new Map([
|
|
|
8059
8301
|
" motn_id: motn_ prefix id",
|
|
8060
8302
|
" status: draft | preparing | active | paused | archived | optional",
|
|
8061
8303
|
" tags: comma-separated tag list | optional",
|
|
8304
|
+
" start-date: YYYY-MM-DD | none | optional",
|
|
8305
|
+
" end-date: YYYY-MM-DD | none | optional",
|
|
8306
|
+
" maximum-company-count: positive integer | none | optional",
|
|
8062
8307
|
" payload: file.json | optional full or partial motion object using the account API shape",
|
|
8063
8308
|
"",
|
|
8064
8309
|
"Behavior:",
|
|
8065
|
-
" Choose either --payload or simple flags. Updates only the provided fields. Sending --tags replaces the motion's full tag set.",
|
|
8310
|
+
" Choose either --payload or simple flags. Updates only the provided fields. Use none to clear a schedule date or company cap. Sending --tags replaces the motion's full tag set.",
|
|
8311
|
+
" The company cap stops only new company discovery; existing company research, people discovery, prospect intake, and automation continue.",
|
|
8066
8312
|
" Payload mode supports outbound signal_rows or legacy signal_questions; supplied rows replace the motion's active ready signals.",
|
|
8067
8313
|
" posting_language is only supported on company-scope hiring signal rows; use topics for person discussion evidence.",
|
|
8068
8314
|
"",
|