@audienti/cli 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.9] - 2026-07-15
8
+
9
+ ### Added
10
+
11
+ - Add `audienti prospects assign <prsp_id> [prsp_id...] --assigned-user <id|me|unassign>` for reassigning existing prospects from the CLI.
12
+ - Add `audienti users activity <account_user_id|me>` for inspecting one account user's outbound activity feed.
13
+ - Add `audienti prospects import-batch --file <csv|jsonl|json>` for starting multiple normal prospect imports with shared list, motion, and assignee defaults.
14
+ - Add `audienti prospects list --assigned-user unassigned` for finding prospects without an owner.
15
+
7
16
  ## [0.1.8] - 2026-07-14
8
17
 
9
18
  ### Added
package/README.md CHANGED
@@ -56,6 +56,9 @@ audienti writer test-run <prsp_id>
56
56
  audienti motions analytics <motn_id>
57
57
  audienti prospects show <prsp_id> --json
58
58
  audienti prospects list --profiles
59
+ audienti prospects list --assigned-user unassigned
60
+ audienti prospects assign <prsp_id> --assigned-user me
61
+ audienti users activity me --window 7d
59
62
  audienti analytics prospects --window 24h
60
63
  audienti analytics users --user me --window 30d
61
64
  audienti analytics visibility --window 24h --user me
@@ -126,6 +129,22 @@ audienti prospects add-profile <prsp_id> --url https://www.linkedin.com/in/examp
126
129
  audienti prospects report-bad-profile <prsp_id> <prof_id>
127
130
  ```
128
131
 
132
+ To reassign or clear ownership for existing prospects:
133
+
134
+ ```bash
135
+ audienti prospects assign <prsp_id> --assigned-user <account_user_id|me>
136
+ audienti prospects assign <prsp_id> --assigned-user unassign
137
+ ```
138
+
139
+ To import multiple LinkedIn people through the same per-prospect import path:
140
+
141
+ ```bash
142
+ audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me
143
+ ```
144
+
145
+ CSV files should include a `linkedin_url` or `url` header. Optional row columns
146
+ `list_id`, `motion_id`, and `assigned_user_id` override command defaults.
147
+
129
148
  ## Compatibility
130
149
 
131
150
  The CLI talks to the versioned Audienti `/api/v1` contract at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,6 +51,10 @@ audienti help agent-workflows
51
51
  ```bash
52
52
  audienti help agent-workflows
53
53
  audienti prospects list --query "name or company" --wide --json
54
+ audienti prospects list --assigned-user unassigned --json
55
+ audienti prospects assign <prsp_id> --assigned-user me --json
56
+ audienti users activity me --window 7d --json
57
+ audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
54
58
  audienti lists create --name "Target list" --json
55
59
  audienti operator next --json
56
60
  audienti operator next --plan
package/src/api-client.js CHANGED
@@ -43,6 +43,10 @@ export class AudientiClient {
43
43
  return this.requestJson(accountPath(accountId, ["users"]));
44
44
  }
45
45
 
46
+ userActivity(accountId, userId, query = {}) {
47
+ return this.requestJson(accountPath(accountId, ["operations", "users", userId, "activity"], query));
48
+ }
49
+
46
50
  offers(accountId) {
47
51
  return this.requestJson(accountPath(accountId, ["offers"]));
48
52
  }
@@ -167,6 +171,13 @@ export class AudientiClient {
167
171
  return this.requestJson(accountPath(accountId, ["prospects", prospectId]));
168
172
  }
169
173
 
174
+ assignProspects(accountId, body) {
175
+ return this.requestJson(accountPath(accountId, ["prospects", "assign"]), {
176
+ method: "POST",
177
+ body
178
+ });
179
+ }
180
+
170
181
  prospectTimeline(accountId, prospectId, query = {}) {
171
182
  return this.requestJson(accountPath(accountId, ["prospects", prospectId, "timeline"], query));
172
183
  }
package/src/cli.js CHANGED
@@ -29,6 +29,9 @@ const PROSPECTS_ADD_NOTE_USAGE = "Usage: audienti prospects add-note <prsp_id> (
29
29
  const PROSPECTS_ADD_STEER_USAGE = "Usage: audienti prospects add-steer <prsp_id> (--message <text> [--engagement-type <key>] | --payload <file.json>) [--json] [--account <acct_id>]";
30
30
  const PROSPECTS_ADD_PROFILE_USAGE = "Usage: audienti prospects add-profile <prsp_id> --url <profile_url|email|phone> [--json] [--account <acct_id>]";
31
31
  const PROSPECTS_REPORT_BAD_PROFILE_USAGE = "Usage: audienti prospects report-bad-profile <prsp_id> <prof_id|citation_id> [--json] [--account <acct_id>]";
32
+ const PROSPECTS_ASSIGN_USAGE = "Usage: audienti prospects assign <prsp_id> [prsp_id...] --assigned-user <id|me|unassign> [--json] [--account <acct_id>]";
33
+ 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>]";
34
+ const USERS_ACTIVITY_USAGE = "Usage: audienti users activity <account_user_id|me> [--mode <actor|account_usage>] [--window <24h|7d|30d>] [--platform <linkedin|email|gmail>] [--query <text>] [--limit <n>] [--page <n>] [--json] [--account <acct_id>]";
32
35
  const WRITER_TEST_RUN_USAGE = "Usage: audienti writer test-run <prsp_id> [--json] [--mode <report|plan|step>] [--branch <both|no-accept|accepted>] [--step <step_key|row_number>] [--no-cache] [--clear-cache] [--account <acct_id>]";
33
36
  const MOTIONS_ANALYTICS_USAGE = "Usage: audienti motions analytics <motn_id> [--window 30d] [--json] [--account <acct_id>]";
34
37
  const MOTIONS_CLONE_USAGE = "Usage: audienti motions clone <motn_id> --name <text> [--json] [--account <acct_id>]";
@@ -117,6 +120,7 @@ async function dispatch(argv, context) {
117
120
  if (normalizedResource === "accounts" && action === "list") return accountsList(rest, context, { accountOverride });
118
121
  if (normalizedResource === "accounts" && action === "select") return accountsSelect(rest, context);
119
122
  if (normalizedResource === "users" && action === "list") return usersList(rest, context, { accountOverride });
123
+ if (normalizedResource === "users" && action === "activity") return usersActivity(rest, context, { accountOverride });
120
124
  if (normalizedResource === "offers" && action === "list") return offersList(rest, context, { accountOverride });
121
125
  if (normalizedResource === "offers" && action === "create") return offersCreate(rest, context, { accountOverride });
122
126
  if (normalizedResource === "icps" && action === "list") return icpsList(rest, context, { accountOverride });
@@ -141,6 +145,7 @@ async function dispatch(argv, context) {
141
145
  if (normalizedResource === "motions" && action === "move-prospects") return motionsMoveProspects(rest, context, { accountOverride });
142
146
  if (normalizedResource === "prospects" && action === "list") return prospectsList(rest, context, { accountOverride });
143
147
  if (normalizedResource === "prospects" && action === "show") return prospectsShow(rest, context, { accountOverride });
148
+ if (normalizedResource === "prospects" && action === "assign") return prospectsAssign(rest, context, { accountOverride });
144
149
  if (normalizedResource === "prospects" && action === "timeline") return prospectsTimeline(rest, context, { accountOverride });
145
150
  if (normalizedResource === "prospects" && action === "message-types") return prospectsMessageTypes(rest, context, { accountOverride });
146
151
  if (normalizedResource === "prospects" && action === "write") return prospectsWrite(rest, context, { accountOverride });
@@ -151,6 +156,7 @@ async function dispatch(argv, context) {
151
156
  if (normalizedResource === "prospects" && action === "sequence-preview") return prospectsSequencePreview(rest, context, { accountOverride });
152
157
  if (normalizedResource === "prospects" && action === "sequence-export") return prospectsSequenceExport(rest, context, { accountOverride });
153
158
  if (normalizedResource === "prospects" && action === "import") return prospectsImport(rest, context, { accountOverride });
159
+ if (normalizedResource === "prospects" && action === "import-batch") return prospectsImportBatch(rest, context, { accountOverride });
154
160
  if (normalizedResource === "prospects" && action === "import-status") return prospectsImportStatus(rest, context, { accountOverride });
155
161
  if (normalizedResource === "writer" && action === "test-run") return writerTestRun(rest, context, { accountOverride });
156
162
  if (normalizedResource === "tools" && action === "get") return toolsGet(rest, context, { accountOverride });
@@ -399,6 +405,32 @@ async function usersList(args, context, { accountOverride } = {}) {
399
405
  renderUsers(users, context);
400
406
  }
401
407
 
408
+ async function usersActivity(args, context, { accountOverride } = {}) {
409
+ const { values, positionals } = parseCommandArgs(args, {
410
+ ...jsonOptions(),
411
+ mode: { type: "string" },
412
+ window: { type: "string" },
413
+ platform: { type: "string" },
414
+ query: { type: "string" },
415
+ limit: { type: "string" },
416
+ page: { type: "string" }
417
+ });
418
+ if (positionals.length !== 1) throw new CommandError(USERS_ACTIVITY_USAGE);
419
+
420
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
421
+ const payload = await client.userActivity(accountId, positionals[0], compactObject({
422
+ mode: values.mode,
423
+ window: values.window,
424
+ platform: values.platform,
425
+ query: values.query,
426
+ limit: values.limit,
427
+ page: values.page
428
+ }));
429
+ if (values.json) return writeJson(context.stdout, payload);
430
+
431
+ renderUserActivity(payload, context);
432
+ }
433
+
402
434
  async function offersList(args, context, { accountOverride } = {}) {
403
435
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
404
436
  if (positionals.length > 0) throw new CommandError("Usage: audienti offers list [--json] [--account <acct_id>]");
@@ -882,6 +914,36 @@ async function prospectsList(args, context, { accountOverride } = {}) {
882
914
  renderProspects(payload, context, { wide: values.wide || values.all, profiles: values.profiles });
883
915
  }
884
916
 
917
+ async function prospectsAssign(args, context, { accountOverride } = {}) {
918
+ const { values, positionals } = parseCommandArgs(args, {
919
+ ...jsonOptions(),
920
+ "assigned-user": { type: "string" }
921
+ });
922
+ if (positionals.length < 1 || !values["assigned-user"]) {
923
+ throw new CommandError(PROSPECTS_ASSIGN_USAGE);
924
+ }
925
+
926
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
927
+ const { payload, rejected } = await performBulkMutation(() =>
928
+ client.assignProspects(accountId, {
929
+ prospect_ids: positionals,
930
+ assigned_user_id: values["assigned-user"]
931
+ }));
932
+ if (values.json) {
933
+ writeJson(context.stdout, payload);
934
+ return rejected ? 1 : 0;
935
+ }
936
+
937
+ const successLabel = values["assigned-user"] === "unassign" ?
938
+ `Unassigned ${successCount(payload)} prospects.` :
939
+ `Assigned ${successCount(payload)} prospects to ${display(values["assigned-user"])}.`;
940
+ renderBulkMutationResult(payload, context, {
941
+ successLabel,
942
+ zeroSuccessLabel: "No prospects were assigned."
943
+ });
944
+ return rejected ? 1 : 0;
945
+ }
946
+
885
947
  async function prospectsShow(args, context, { accountOverride } = {}) {
886
948
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
887
949
  if (positionals.length !== 1) throw new CommandError("Usage: audienti prospects show <prsp_id> [--json] [--account <acct_id>]");
@@ -1295,6 +1357,66 @@ async function prospectsImport(args, context, { accountOverride } = {}) {
1295
1357
  renderProspectImportStarted(payload, context);
1296
1358
  }
1297
1359
 
1360
+ async function prospectsImportBatch(args, context, { accountOverride } = {}) {
1361
+ const { values, positionals } = parseCommandArgs(args, {
1362
+ ...jsonOptions(),
1363
+ file: { type: "string" },
1364
+ list: { type: "string" },
1365
+ motion: { type: "string" },
1366
+ "assigned-user": { type: "string" }
1367
+ });
1368
+ if (positionals.length > 0 || !values.file) {
1369
+ throw new CommandError(PROSPECTS_IMPORT_BATCH_USAGE);
1370
+ }
1371
+
1372
+ const rows = await readProspectImportBatchFile(values.file);
1373
+ if (rows.length === 0) throw new CommandError("Import batch file did not contain any prospects.");
1374
+
1375
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1376
+ const result = {
1377
+ summary: {
1378
+ total: rows.length,
1379
+ started: 0,
1380
+ failed: 0
1381
+ },
1382
+ imports: [],
1383
+ failed: []
1384
+ };
1385
+
1386
+ for (const row of rows) {
1387
+ const body = compactObject({
1388
+ linkedin_url: row.linkedin_url,
1389
+ list_id: row.list_id || values.list,
1390
+ motion_id: row.motion_id || values.motion,
1391
+ assigned_user_id: row.assigned_user_id || values["assigned-user"]
1392
+ });
1393
+
1394
+ try {
1395
+ const payload = await client.prospectImport(accountId, body);
1396
+ result.imports.push(payload);
1397
+ result.summary.started += 1;
1398
+ } catch (error) {
1399
+ if (!(error instanceof ApiError)) throw error;
1400
+
1401
+ result.failed.push({
1402
+ row: row.row,
1403
+ linkedin_url: row.linkedin_url,
1404
+ status: error.status,
1405
+ error: error.body?.error || error.message
1406
+ });
1407
+ result.summary.failed += 1;
1408
+ }
1409
+ }
1410
+
1411
+ if (values.json) {
1412
+ writeJson(context.stdout, result);
1413
+ return result.summary.failed > 0 ? 1 : 0;
1414
+ }
1415
+
1416
+ renderProspectImportBatchResult(result, context);
1417
+ return result.summary.failed > 0 ? 1 : 0;
1418
+ }
1419
+
1298
1420
  async function prospectsImportStatus(args, context, { accountOverride } = {}) {
1299
1421
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
1300
1422
  if (positionals.length !== 1) {
@@ -1984,6 +2106,113 @@ async function readJsonPayload(filePath) {
1984
2106
  }
1985
2107
  }
1986
2108
 
2109
+ async function readProspectImportBatchFile(filePath) {
2110
+ let contents;
2111
+ try {
2112
+ contents = await readFile(filePath, "utf8");
2113
+ } catch (error) {
2114
+ throw new CommandError(`Could not read import batch file ${filePath}: ${error.message}`);
2115
+ }
2116
+
2117
+ return parseProspectImportBatch(contents, filePath);
2118
+ }
2119
+
2120
+ function parseProspectImportBatch(contents, filePath = "batch file") {
2121
+ const trimmed = String(contents || "").trim();
2122
+ if (!trimmed) return [];
2123
+
2124
+ if (trimmed.startsWith("[") || (trimmed.startsWith("{") && !trimmed.includes("\n"))) {
2125
+ try {
2126
+ const parsed = JSON.parse(trimmed);
2127
+ return normalizeImportBatchRows(Array.isArray(parsed) ? parsed : [parsed], filePath);
2128
+ } catch (error) {
2129
+ throw new CommandError(`Invalid JSON import batch in ${filePath}: ${error.message}`);
2130
+ }
2131
+ }
2132
+
2133
+ const lines = trimmed.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
2134
+ if (lines.length === 0) return [];
2135
+
2136
+ if (looksLikeCsvHeader(lines[0])) {
2137
+ return parseProspectImportCsv(lines, filePath);
2138
+ }
2139
+
2140
+ const rows = lines.map((line, index) => {
2141
+ if (line.startsWith("{")) {
2142
+ try {
2143
+ return { ...JSON.parse(line), row: index + 1 };
2144
+ } catch (error) {
2145
+ throw new CommandError(`Invalid JSONL row ${index + 1} in ${filePath}: ${error.message}`);
2146
+ }
2147
+ }
2148
+
2149
+ return { linkedin_url: line, row: index + 1 };
2150
+ });
2151
+
2152
+ return normalizeImportBatchRows(rows, filePath);
2153
+ }
2154
+
2155
+ function looksLikeCsvHeader(line) {
2156
+ const headers = parseCsvLine(line).map((header) => header.trim().toLowerCase());
2157
+ return headers.includes("linkedin_url") || headers.includes("url");
2158
+ }
2159
+
2160
+ function parseProspectImportCsv(lines, filePath) {
2161
+ const headers = parseCsvLine(lines[0]).map((header) => header.trim());
2162
+ const rows = lines.slice(1).map((line, index) => {
2163
+ const values = parseCsvLine(line);
2164
+ return headers.reduce((row, header, headerIndex) => {
2165
+ row[header] = values[headerIndex] || "";
2166
+ return row;
2167
+ }, { row: index + 1 });
2168
+ });
2169
+
2170
+ return normalizeImportBatchRows(rows, filePath);
2171
+ }
2172
+
2173
+ function parseCsvLine(line) {
2174
+ const values = [];
2175
+ let current = "";
2176
+ let inQuotes = false;
2177
+
2178
+ for (let index = 0; index < line.length; index += 1) {
2179
+ const character = line[index];
2180
+ const next = line[index + 1];
2181
+
2182
+ if (character === "\"" && inQuotes && next === "\"") {
2183
+ current += "\"";
2184
+ index += 1;
2185
+ } else if (character === "\"") {
2186
+ inQuotes = !inQuotes;
2187
+ } else if (character === "," && !inQuotes) {
2188
+ values.push(current);
2189
+ current = "";
2190
+ } else {
2191
+ current += character;
2192
+ }
2193
+ }
2194
+
2195
+ values.push(current);
2196
+ return values.map((value) => value.trim());
2197
+ }
2198
+
2199
+ function normalizeImportBatchRows(rows, filePath) {
2200
+ return rows.map((row, index) => {
2201
+ const rowNumber = row?.row || index + 1;
2202
+ const normalized = typeof row === "string" ? { linkedin_url: row } : row;
2203
+ const linkedinUrl = normalized?.linkedin_url || normalized?.url;
2204
+ if (!linkedinUrl) throw new CommandError(`Missing linkedin_url on row ${rowNumber} in ${filePath}.`);
2205
+
2206
+ return compactObject({
2207
+ row: rowNumber,
2208
+ linkedin_url: linkedinUrl,
2209
+ list_id: normalized.list_id,
2210
+ motion_id: normalized.motion_id,
2211
+ assigned_user_id: normalized.assigned_user_id || normalized.assigned_user
2212
+ });
2213
+ });
2214
+ }
2215
+
1987
2216
  function writeLine(stream, text = "") {
1988
2217
  stream.write(`${text}\n`);
1989
2218
  }
@@ -2029,6 +2258,41 @@ function renderUsers(users, context) {
2029
2258
  }
2030
2259
  }
2031
2260
 
2261
+ function renderUserActivity(payload, context) {
2262
+ const accountUser = payload?.account_user || {};
2263
+ const summary = payload?.summary || {};
2264
+ const events = Array.isArray(payload?.events) ? payload.events : [];
2265
+ const pagination = payload?.pagination || {};
2266
+
2267
+ writeLine(context.stdout, `User: ${display(accountUser.name || accountUser.email)} (${display(accountUser.id)})`);
2268
+ writeLine(context.stdout, `Window actions: ${display(summary.window_count, 0)}`);
2269
+ if (pagination.page || pagination.pages) {
2270
+ writeLine(context.stdout, `Page: ${display(pagination.page, 1)} of ${display(pagination.pages, 1)}`);
2271
+ }
2272
+ renderCountRows(context, "By platform", summary.by_platform);
2273
+ renderCountRows(context, "By action", summary.by_key);
2274
+
2275
+ if (events.length === 0) return writeLine(context.stdout, "No activity events found.");
2276
+
2277
+ writeLine(context.stdout, "TIME\tACTION\tPLATFORM\tPROSPECT\tCOMPANY\tDETAILS");
2278
+ for (const event of events) {
2279
+ writeLine(context.stdout, [
2280
+ display(event.occurred_at),
2281
+ display(event.action_label || event.key),
2282
+ display(event.platform),
2283
+ display(event.prospect?.name || event.prospect?.display_name || event.prospect?.prefix_id),
2284
+ display(event.prospect?.company),
2285
+ display(event.details)
2286
+ ].join("\t"));
2287
+ }
2288
+ }
2289
+
2290
+ function renderCountRows(context, label, rows) {
2291
+ if (!Array.isArray(rows) || rows.length === 0) return;
2292
+
2293
+ writeLine(context.stdout, `${label}: ${rows.map((row) => `${display(row.label || row.key)} ${display(row.count, 0)}`).join(" | ")}`);
2294
+ }
2295
+
2032
2296
  function renderOffers(offers, context) {
2033
2297
  if (!Array.isArray(offers) || offers.length === 0) return writeLine(context.stdout, "No offers found.");
2034
2298
 
@@ -2564,6 +2828,30 @@ function renderProspectImportStarted(payload, context) {
2564
2828
  if (payload?.prefix_id) writeLine(context.stdout, `Run \`audienti prospects import-status ${payload.prefix_id}\` to check completion.`);
2565
2829
  }
2566
2830
 
2831
+ function renderProspectImportBatchResult(result, context) {
2832
+ const imports = Array.isArray(result?.imports) ? result.imports : [];
2833
+ const failed = Array.isArray(result?.failed) ? result.failed : [];
2834
+
2835
+ writeLine(context.stdout, `Started ${display(result?.summary?.started, 0)} prospect imports.`);
2836
+ writeLine(context.stdout, `Failures: ${display(result?.summary?.failed, 0)}`);
2837
+
2838
+ if (imports.length > 0) {
2839
+ writeLine(context.stdout, "IMPORT ID\tPROSPECT\tPROSPECT ID\tSTATUS");
2840
+ for (const payload of imports) {
2841
+ writeLine(context.stdout, [
2842
+ display(payload?.prefix_id),
2843
+ display(payload?.prospect?.display_name || payload?.prospect?.name),
2844
+ display(payload?.prospect?.prefix_id),
2845
+ display(payload?.status)
2846
+ ].join("\t"));
2847
+ }
2848
+ }
2849
+
2850
+ for (const row of failed) {
2851
+ writeLine(context.stdout, `- row ${display(row.row)} ${display(row.linkedin_url)}: ${display(row.error, "failed")}`);
2852
+ }
2853
+ }
2854
+
2567
2855
  function renderProspectImportStatus(payload, context) {
2568
2856
  writeLine(context.stdout, `Import: ${display(payload?.prefix_id)}`);
2569
2857
  writeLine(context.stdout, `Status: ${display(payload?.status)}`);
@@ -3313,6 +3601,7 @@ const HELP_TOPICS = new Map([
3313
3601
  " audienti auth status",
3314
3602
  " audienti config list",
3315
3603
  " audienti users list",
3604
+ " audienti users activity <account_user_id|me>",
3316
3605
  "",
3317
3606
  " Motions / plays",
3318
3607
  " audienti motions list",
@@ -3327,8 +3616,10 @@ const HELP_TOPICS = new Map([
3327
3616
  " Prospects",
3328
3617
  " audienti prospects list [filters]",
3329
3618
  " audienti prospects show <prsp_id>",
3619
+ " audienti prospects assign <prsp_id> --assigned-user <id|me|unassign>",
3330
3620
  " audienti prospects timeline <prsp_id>",
3331
3621
  " audienti prospects import <linkedin_url> [--motion <motn_id>]",
3622
+ " audienti prospects import-batch --file <csv|jsonl|json>",
3332
3623
  " audienti prospects add-note <prsp_id> --message <text>",
3333
3624
  " audienti prospects add-profile <prsp_id> --url <profile_url|email|phone>",
3334
3625
  "",
@@ -3505,6 +3796,7 @@ const HELP_TOPICS = new Map([
3505
3796
  ["users", [
3506
3797
  "Usage:",
3507
3798
  " audienti users list [--json]",
3799
+ " audienti users activity <account_user_id|me> [--json]",
3508
3800
  "",
3509
3801
  "Status: implemented",
3510
3802
  "",
@@ -3533,6 +3825,25 @@ const HELP_TOPICS = new Map([
3533
3825
  " current: boolean"
3534
3826
  ].join("\n")],
3535
3827
 
3828
+ ["users activity", [
3829
+ "Usage:",
3830
+ ` ${USERS_ACTIVITY_USAGE.slice("Usage: ".length)}`,
3831
+ "",
3832
+ "Status: implemented",
3833
+ "",
3834
+ "Purpose:",
3835
+ " Inspect one workspace user's outbound activity feed and action summary.",
3836
+ "",
3837
+ "Input shape:",
3838
+ " account_user_id: integer account user id, or me for the authenticated token user",
3839
+ " mode: actor | account_usage",
3840
+ " window: 24h | 7d | 30d",
3841
+ " platform: linkedin | email | gmail",
3842
+ "",
3843
+ "API:",
3844
+ " GET /api/v1/accounts/:account_id/operations/users/:user_id/activity.json"
3845
+ ].join("\n")],
3846
+
3536
3847
  ["offers", [
3537
3848
  "Usage:",
3538
3849
  " audienti offers list [--json]",
@@ -4131,6 +4442,7 @@ const HELP_TOPICS = new Map([
4131
4442
  "Usage:",
4132
4443
  " audienti prospects list [--json] [filters]",
4133
4444
  " audienti prospects show <prsp_id> [--json]",
4445
+ " audienti prospects assign <prsp_id> [prsp_id...] --assigned-user <id|me|unassign> [--json]",
4134
4446
  " audienti prospects timeline <prsp_id> [--json]",
4135
4447
  " audienti prospects message-types <prsp_id> [--json]",
4136
4448
  " audienti prospects write <prsp_id> --type <surface_key> [--json]",
@@ -4141,9 +4453,10 @@ const HELP_TOPICS = new Map([
4141
4453
  " audienti prospects sequence-preview <prsp_id> [--json]",
4142
4454
  " audienti prospects sequence-export <prsp_id> [--csv]",
4143
4455
  " audienti prospects import <linkedin_url> [--list <list_id>] [--motion <motn_id>] [--json]",
4456
+ " audienti prospects import-batch --file <csv|jsonl|json> [--list <list_id>] [--motion <motn_id>] [--json]",
4144
4457
  " audienti prospects import-status <primp_id> [--json]",
4145
4458
  "",
4146
- "Status: read commands, per-prospect draft preview, sequence preview, and import implemented; disposition planned",
4459
+ "Status: read commands, assignment, per-prospect draft preview, sequence preview, and import implemented; disposition planned",
4147
4460
  "",
4148
4461
  "Filters:",
4149
4462
  " --query <text>",
@@ -4153,7 +4466,7 @@ const HELP_TOPICS = new Map([
4153
4466
  " --play <motn_id>",
4154
4467
  " --list <list_id>",
4155
4468
  " --stage <stage>",
4156
- " --assigned-user <account_user_id|me>",
4469
+ " --assigned-user <account_user_id|me|unassigned>",
4157
4470
  " --limit <n>",
4158
4471
  " --page <n>",
4159
4472
  " --offset <n>",
@@ -4181,7 +4494,7 @@ const HELP_TOPICS = new Map([
4181
4494
  " --play <motn_id> Filter to a play using the same motion relationship",
4182
4495
  " --list <list_id> Filter to a prospect list",
4183
4496
  " --stage <stage> Filter to a pipeline stage",
4184
- " --assigned-user <id|me> Filter by assigned account user",
4497
+ " --assigned-user <id|me|unassigned> Filter by assigned account user",
4185
4498
  " --limit <n> Max rows for one page; with --all it caps total rows up to 1000",
4186
4499
  " --page <n> 1-based page number",
4187
4500
  " --offset <n> Row offset for manual pagination",
@@ -4208,9 +4521,33 @@ const HELP_TOPICS = new Map([
4208
4521
  "",
4209
4522
  "Examples:",
4210
4523
  " audienti prospects list --stage identified --page 2 --limit 50",
4524
+ " audienti prospects list --assigned-user unassigned",
4211
4525
  " audienti prospects list --all --csv"
4212
4526
  ].join("\n")],
4213
4527
 
4528
+ ["prospects assign", [
4529
+ "Usage:",
4530
+ ` ${PROSPECTS_ASSIGN_USAGE.slice("Usage: ".length)}`,
4531
+ "",
4532
+ "Status: implemented",
4533
+ "",
4534
+ "Input shape:",
4535
+ " prsp_id: one or more prsp_ prefix ids",
4536
+ " assigned_user_id: account user id, me, or unassign",
4537
+ "",
4538
+ "Behavior:",
4539
+ " Updates AccountProspect.assigned_to_account_user_id for existing account prospects without changing motion or list membership.",
4540
+ "",
4541
+ "API:",
4542
+ " POST /api/v1/accounts/:account_id/prospects/assign.json",
4543
+ "",
4544
+ "JSON body:",
4545
+ " {",
4546
+ " \"prospect_ids\": [\"prsp_abc123\", \"prsp_def456\"],",
4547
+ " \"assigned_user_id\": \"me\"",
4548
+ " }"
4549
+ ].join("\n")],
4550
+
4214
4551
  ["prospects show", [
4215
4552
  "Usage:",
4216
4553
  " audienti prospects show <prsp_id> [--json] [--account <acct_id>]",
@@ -4536,6 +4873,28 @@ const HELP_TOPICS = new Map([
4536
4873
  " }"
4537
4874
  ].join("\n")],
4538
4875
 
4876
+ ["prospects import-batch", [
4877
+ "Usage:",
4878
+ ` ${PROSPECTS_IMPORT_BATCH_USAGE.slice("Usage: ".length)}`,
4879
+ "",
4880
+ "Status: implemented",
4881
+ "",
4882
+ "Input shape:",
4883
+ " file: CSV with linkedin_url/url header, JSON array, JSONL objects, or newline-delimited LinkedIn URLs",
4884
+ " list_id: list_ prefix id | optional default for every row",
4885
+ " motn_id: motn_ prefix id | optional default for every row",
4886
+ " assigned_user_id: account user id or me | optional default for every row",
4887
+ "",
4888
+ "CSV columns:",
4889
+ " linkedin_url or url, list_id, motion_id, assigned_user_id",
4890
+ "",
4891
+ "Behavior:",
4892
+ " Starts one normal prospect import per row. Row-level list_id, motion_id, and assigned_user_id override command defaults.",
4893
+ "",
4894
+ "API:",
4895
+ " POST /api/v1/accounts/:account_id/prospect_imports.json"
4896
+ ].join("\n")],
4897
+
4539
4898
  ["prospects import-status", [
4540
4899
  "Usage:",
4541
4900
  " audienti prospects import-status <primp_id> [--json] [--account <acct_id>]",
@@ -4879,12 +5238,15 @@ const HELP_TOPICS = new Map([
4879
5238
  "3. Add a new prospect from LinkedIn and poll enrichment",
4880
5239
  " audienti lists create --name \"Target list\"",
4881
5240
  " audienti prospects import https://www.linkedin.com/in/example --list <list_id> --assigned-user me",
5241
+ " audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me",
4882
5242
  " audienti prospects import-status <primp_id>",
4883
5243
  " audienti prospects show <prsp_id>",
4884
5244
  " audienti tools get email --url https://www.linkedin.com/in/example",
4885
5245
  "",
4886
5246
  "4. Find an existing prospect and inspect next step",
4887
5247
  " audienti prospects list --query \"name or company\" --wide",
5248
+ " audienti prospects list --assigned-user unassigned",
5249
+ " audienti prospects assign <prsp_id> --assigned-user me",
4888
5250
  " audienti companies search --query \"Honeywell\"",
4889
5251
  " audienti prospects list --company-profile <prof_id>",
4890
5252
  " audienti prospects show <prsp_id>",
@@ -4908,6 +5270,7 @@ const HELP_TOPICS = new Map([
4908
5270
  " audienti operator outcome <row_id> --payload <file.json>",
4909
5271
  "",
4910
5272
  "7. Inspect account analytics",
5273
+ " audienti users activity me --window 7d",
4911
5274
  " audienti analytics prospects --window 24h",
4912
5275
  " audienti analytics users --user me --window 30d",
4913
5276
  " audienti analytics visibility --window 24h --user me",