@audienti/cli 0.1.139 → 0.1.141

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,20 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.141] - 2026-10-26
8
+
9
+ ### Added
10
+
11
+ - `audienti events defer-comment-retry <event_id>` is "Not now" for a post comment you said was not sent (`POST /api/v1/accounts/:account_id/events/:id/defer_comment_retry`). It hides the "Not sent: try again?" offer for 24 hours; the offer comes back once, and a second "Not now" ends it. Nothing is sent.
12
+
13
+ ## [0.1.140] - 2026-10-25
14
+
15
+ ### Added
16
+
17
+ - Connect Signal search to shared quote acceptance and one-search credit units.
18
+ - Add credit history, exact receipts, root price/payer approvals and staff result-refund commands through shared APIs.
19
+ - Preserve exact decimal credit strings and keep supplier accounting out of customer financial output.
20
+
7
21
  ## [0.1.139] - 2026-10-25
8
22
 
9
23
  ### Changed
package/README.md CHANGED
@@ -328,10 +328,11 @@ inside fields are not. With `--wait`, the exit code is 1 on an API error, a
328
328
  timeout or an aborted (failed) run; per-item outcomes such as `no_match` do
329
329
  not change it.
330
330
 
331
- For an active paid Email Find operation, review a server quote first:
331
+ For active paid Email Find or Signal search work, review a server quote first:
332
332
 
333
333
  ```bash
334
334
  audienti tools email-find --file people.csv --quote
335
+ audienti tools signals-find --icp "Logistics companies" --question "Warehouse expansion" --count 5 --quote
335
336
  audienti tools email-find --file people.csv --accept-quote <quote_reference>
336
337
  audienti tools runs review <run_id>
337
338
  audienti tools runs resume <run_id> --item <item_id> --revision <revision> --idempotency-key <unique_key>
@@ -899,3 +900,18 @@ Batch import accepts up to 50 rows per API request. Split larger files before im
899
900
  Bulk destinations: `audienti prospects set-destination <prsp_id> --type list --destination <list_id>` adds by default. Use `--name "New list"` to create a list, `--type experiment --name "Existing relationships"` to create a transition without setup, or `--mode move` for explicit reassignment. Add preserves existing memberships; experiment Add skips people assigned elsewhere.
900
901
 
901
902
  Create a child workspace with `audienti accounts create --name <name> --parent <name-or-id> [--json]`. The parent can be a visible name, unique name fragment, numeric ID, or prefixed ID. You must administer a root team account. All parent admins receive access to the child; ordinary members do not. Ambiguous names show matching IDs and stop. A duplicate child name under the same parent fails clearly. The response includes the new account ID. Creation keeps your selected account unchanged: use `audienti accounts select <new_acct_id>` or `--account <new_acct_id>` before creating a motion there.
903
+
904
+ ### Credit history and root approvals
905
+
906
+ `audienti credits history` lists credit charges. `credits show <usage_id>` opens an
907
+ exact receipt. A root billing role does not grant access to private saved results.
908
+ `credits approvals` shows action prices and changed-payer requests. After the
909
+ root billing admin explicitly agrees, use `credits accept-price <price_id>` or
910
+ `credits approve-payer <operation_id> --revision <revision> --request-key <unique_key>`.
911
+ Approval never resumes waiting work. Increased prices also require the notice period.
912
+
913
+ Omalab staff can use `credits refund-review <usage_id>` to inspect the exact charge,
914
+ result and previous reviews. After confirming a defect, `credits refund <usage_id>
915
+ --cumulative-principal <exact_credits> --evidence-token <uuid> --reason <reason>
916
+ --evidence <exact_result_evidence>` records the cumulative credit return. Customer
917
+ billing admins cannot use these staff actions. This never refunds cash.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.139",
3
+ "version": "0.1.141",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -1154,6 +1154,12 @@ export class AudientiClient {
1154
1154
  });
1155
1155
  }
1156
1156
 
1157
+ deferCommentRetry(accountId, eventId) {
1158
+ return this.requestJson(accountPath(accountId, ["events", eventId, "defer_comment_retry"]), {
1159
+ method: "POST"
1160
+ });
1161
+ }
1162
+
1157
1163
  resolveUncertainCommentStatus(accountId, eventId, resolutionId) {
1158
1164
  return this.requestJson(accountPath(accountId, ["events", eventId, "resolve_uncertain_comment", "status"], { resolution_id: resolutionId }));
1159
1165
  }
@@ -1351,6 +1357,34 @@ export class AudientiClient {
1351
1357
  return this.requestJson(accountPath(accountId, ["tools", "runs", runId, "export"], query));
1352
1358
  }
1353
1359
 
1360
+ creditRefundReview(accountId, usageId) {
1361
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId, "refund"]));
1362
+ }
1363
+
1364
+ approveCreditRefund(accountId, usageId, body) {
1365
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId, "refund"]), { method: "POST", body });
1366
+ }
1367
+
1368
+ creditUsages(accountId, query = {}) {
1369
+ return this.requestJson(accountPath(accountId, ["credit_usages"], query));
1370
+ }
1371
+
1372
+ creditUsage(accountId, usageId) {
1373
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId]));
1374
+ }
1375
+
1376
+ creditApprovals(accountId) {
1377
+ return this.requestJson(accountPath(accountId, ["credit_approvals"]));
1378
+ }
1379
+
1380
+ acceptCreditPrice(accountId, priceId) {
1381
+ return this.requestJson(accountPath(accountId, ["credit_approvals", "prices", priceId]), { method: "POST", body: {} });
1382
+ }
1383
+
1384
+ approveCreditPayer(accountId, operationId, body) {
1385
+ return this.requestJson(accountPath(accountId, ["credit_approvals", "payers", operationId]), { method: "POST", body });
1386
+ }
1387
+
1354
1388
  // Saved network of one connected account (owner-only).
1355
1389
  socialCookieNetwork(accountId, socialCookieId, query = {}) {
1356
1390
  return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "network"], query));
package/src/cli.js CHANGED
@@ -231,6 +231,7 @@ const ANALYTICS_COHORT_LIST_USAGE = "Usage: audienti analytics cohorts create-li
231
231
  const TOOLS_LIST_USAGE = "Usage: audienti tools list [--json]";
232
232
  const TOOLS_HUMANIZE_USAGE = "Usage: audienti tools humanize --file <path> [--tone <professional|academic|blog|casual|creative|scientific|technical>] [--language <language>] [--async [--wait]] [--json] [--account <acct_id>]";
233
233
  const TOOL_RUN_FLAGS_USAGE = "[--request-key <key>] [--wait [--timeout-seconds <n>] [--poll-interval-seconds <n>]] [--json] [--account <acct_id>]";
234
+ const CREDITS_USAGE = "Usage: audienti credits (history [--limit <n>] [--cursor <id>] | show <usage_id> | approvals | accept-price <price_id> | approve-payer <operation_id> --revision <revision> --request-key <key>) [--json] [--account <acct_id>]";
234
235
  const TOOLS_EMAIL_FIND_USAGE = `Usage: audienti tools email-find (--first-name <name> --last-name <name> --company-domain <domain> | --linkedin-url <url> | --file <csv|jsonl|json>) [--quote | --accept-quote <reference>] ${TOOL_RUN_FLAGS_USAGE}`;
235
236
  const TOOLS_LINKEDIN_ENRICH_USAGE = `Usage: audienti tools linkedin-enrich (--url <linkedin_url> [--kind <person|company>] | --file <csv|jsonl|json>) ${TOOL_RUN_FLAGS_USAGE}`;
236
237
  const TOOLS_SIGNALS_FIND_USAGE = `Usage: audienti tools signals-find --icp <text> --question <text> [--count <1-10>] [--date-window-days <n>] ${TOOL_RUN_FLAGS_USAGE}`;
@@ -429,6 +430,7 @@ async function dispatch(argv, context) {
429
430
  if (normalizedResource === "webhooks") return webhooksCommand(action, rest, context, { accountOverride });
430
431
  if (normalizedResource === "reply-alerts") return replyAlertsCommand(action, rest, context);
431
432
  if (normalizedResource === "brand-profile") return brandProfileCommand(action, rest, context, { accountOverride });
433
+ if (normalizedResource === "credits") return creditsCommand(action, rest, context, { accountOverride });
432
434
  if (normalizedResource === "payment") return paymentCommand(action, rest, context, { accountOverride });
433
435
  if (normalizedResource === "find") return findByName([action, ...rest].filter((value) => value !== undefined), context, { accountOverride });
434
436
  if (normalizedResource === "tags" && action === "list") return tagsList(rest, context, { accountOverride });
@@ -15899,6 +15901,16 @@ const PARITY_ACTION_COMMANDS = new Map([
15899
15901
  return `Event ${id} is recorded as posted (${display(payload?.status)}).`;
15900
15902
  }
15901
15903
  }],
15904
+ ["events defer-comment-retry", {
15905
+ usage: "audienti events defer-comment-retry <event_id>",
15906
+ purpose: "Say \"Not now\" to the \"Not sent: try again?\" offer for a post comment you settled as not sent. Hides the offer for 24 hours; it comes back once, and a second \"Not now\" ends it. Nothing is sent.",
15907
+ api: "POST /api/v1/accounts/:account_id/events/:id/defer_comment_retry.json",
15908
+ run: (client, accountId, [id]) => client.deferCommentRetry(accountId, id),
15909
+ done: ([id], payload) => {
15910
+ if (payload?.status === "retired" || payload?.status === "already_retired") return `Event ${id}: the offer to send again has ended. Nothing was sent.`;
15911
+ return `Event ${id}: the offer to send again is hidden until ${display(payload?.hidden_until)}. Nothing was sent.`;
15912
+ }
15913
+ }],
15902
15914
  ["events resolve-uncertain-comment-status", {
15903
15915
  usage: "audienti events resolve-uncertain-comment-status <event_id> --resolution <resolution_id>",
15904
15916
  purpose: "Show whether your Computer has finished checking a short comment link: checking, resolved or failed. Read-only.",
@@ -16403,6 +16415,8 @@ async function toolsSignalsFind(args, context, { accountOverride } = {}) {
16403
16415
  const { values, positionals } = parseCommandArgs(args, {
16404
16416
  ...jsonOptions(),
16405
16417
  ...TOOL_RUN_WAIT_OPTIONS,
16418
+ quote: { type: "boolean" },
16419
+ "accept-quote": { type: "string" },
16406
16420
  icp: { type: "string" },
16407
16421
  question: { type: "string" },
16408
16422
  count: { type: "string" },
@@ -16456,7 +16470,7 @@ async function submitToolRun(tool, input, values, context, { accountOverride } =
16456
16470
  if (values["accept-quote"]) throw new CommandError("Choose --quote or --accept-quote, not both.");
16457
16471
  const quote = await client.quoteToolRun(accountId, { tool, input });
16458
16472
  if (values.json) return writeJson(context.stdout, quote);
16459
- writeLine(context.stdout, `Up to ${quote.maximum_credits} credits for ${quote.max_units} rows. Available: ${quote.available_credits} credits.`);
16473
+ writeLine(context.stdout, `Up to ${quote.maximum_credits} credits for ${tool === "signals_find" ? "one search" : `${quote.max_units} rows`}. Available: ${quote.available_credits} credits.`);
16460
16474
  writeLine(context.stdout, `Accept with the same input and --accept-quote ${quote.quote_reference}`);
16461
16475
  return 0;
16462
16476
  }
@@ -16545,6 +16559,140 @@ async function toolsRunsCredits(action, args, context, { accountOverride } = {})
16545
16559
  return writeJson(context.stdout, result);
16546
16560
  }
16547
16561
 
16562
+ async function creditsCommand(action, args, context, { accountOverride } = {}) {
16563
+ if (["refund-review", "refund"].includes(action)) return creditRefundCommand(action, args, context, { accountOverride });
16564
+ if (action === "history") return creditsHistory(args, context, { accountOverride });
16565
+ if (action === "show") return creditUsageShow(args, context, { accountOverride });
16566
+ if (action === "approvals") return creditApprovals(args, context, { accountOverride });
16567
+ if (action === "accept-price") return acceptCreditPrice(args, context, { accountOverride });
16568
+ if (action === "approve-payer") return approveCreditPayer(args, context, { accountOverride });
16569
+ throw new CommandError(CREDITS_USAGE);
16570
+ }
16571
+
16572
+ async function creditRefundCommand(action, args, context, { accountOverride } = {}) {
16573
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(),
16574
+ "cumulative-principal": { type: "string" }, "evidence-token": { type: "string" }, reason: { type: "string" }, evidence: { type: "string" }
16575
+ });
16576
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16577
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16578
+ if (action === "refund-review") return writeJson(context.stdout, await client.creditRefundReview(accountId, positionals[0]));
16579
+ if (!["cumulative-principal", "evidence-token", "reason", "evidence"].every((key) => values[key])) {
16580
+ throw new CommandError("Staff only: credits refund <usage_id> --cumulative-principal <exact_credits> --evidence-token <uuid> --reason <confirmed_defect> --evidence <exact_result_evidence>");
16581
+ }
16582
+ return writeJson(context.stdout, await client.approveCreditRefund(accountId, positionals[0], {
16583
+ cumulative_principal: values["cumulative-principal"], evidence_token: values["evidence-token"], reason: values.reason, evidence: values.evidence
16584
+ }));
16585
+ }
16586
+
16587
+ async function creditsHistory(args, context, { accountOverride } = {}) {
16588
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), limit: { type: "string" }, cursor: { type: "string" } });
16589
+ if (positionals.length > 0) throw new CommandError(CREDITS_USAGE);
16590
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16591
+ const payload = safeCreditHistory(await client.creditUsages(accountId, compactObject({
16592
+ limit: normalizeOptionalPositiveInteger(values.limit, "--limit"), cursor: values.cursor
16593
+ })));
16594
+ if (values.json) return writeJson(context.stdout, payload);
16595
+ writeLine(context.stdout, `Credit balance: ${display(payload.balance, "unknown")}`);
16596
+ const usages = payload.usages || [];
16597
+ if (usages.length === 0) return writeLine(context.stdout, "No customer credit charges recorded.");
16598
+ for (const usage of usages) renderCreditReceipt(usage, context);
16599
+ if (payload.next_cursor) writeLine(context.stdout, `More credit history: audienti credits history --cursor ${payload.next_cursor}`);
16600
+ }
16601
+
16602
+ async function creditUsageShow(args, context, { accountOverride } = {}) {
16603
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16604
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16605
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16606
+ const usage = safeCreditReceipt(await client.creditUsage(accountId, positionals[0]));
16607
+ if (values.json) return writeJson(context.stdout, usage);
16608
+ renderCreditReceipt(usage, context);
16609
+ }
16610
+
16611
+ async function creditApprovals(args, context, { accountOverride } = {}) {
16612
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16613
+ if (positionals.length > 0) throw new CommandError(CREDITS_USAGE);
16614
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16615
+ const approvals = safeCreditApprovals(await client.creditApprovals(accountId));
16616
+ if (values.json) return writeJson(context.stdout, approvals);
16617
+ renderCreditApprovals(approvals, context);
16618
+ }
16619
+
16620
+ async function acceptCreditPrice(args, context, { accountOverride } = {}) {
16621
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16622
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16623
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16624
+ const approvals = safeCreditApprovals(await client.acceptCreditPrice(accountId, positionals[0]));
16625
+ if (values.json) return writeJson(context.stdout, approvals);
16626
+ writeLine(context.stdout, `Price approval recorded for ${positionals[0]}.`);
16627
+ renderCreditApprovals(approvals, context);
16628
+ }
16629
+
16630
+ async function approveCreditPayer(args, context, { accountOverride } = {}) {
16631
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), revision: { type: "string" }, "request-key": { type: "string" } });
16632
+ if (positionals.length !== 1 || !values.revision || !values["request-key"]) throw new CommandError(CREDITS_USAGE);
16633
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16634
+ const approvals = safeCreditApprovals(await client.approveCreditPayer(accountId, positionals[0], {
16635
+ revision: values.revision, idempotency_key: values["request-key"]
16636
+ }));
16637
+ if (values.json) return writeJson(context.stdout, approvals);
16638
+ writeLine(context.stdout, `Payer approval recorded for operation ${positionals[0]}. Waiting work remains paused.`);
16639
+ renderCreditApprovals(approvals, context);
16640
+ }
16641
+
16642
+ function renderCreditApprovals(approvals, context) {
16643
+ writeLine(context.stdout, `Payer: ${display(approvals.payer_account_id, "unknown")}`);
16644
+ writeLine(context.stdout, "Credit prices:");
16645
+ for (const price of approvals.prices || []) {
16646
+ writeLine(context.stdout, ` ${price.id} ${price.version}: ${JSON.stringify(price.prices)} credits (${price.accepted ? "approved" : "approval required"})${price.test_price ? " [test price]" : ""}`);
16647
+ }
16648
+ writeLine(context.stdout, "Payer approvals waiting:");
16649
+ for (const approval of approvals.payer_approvals || []) {
16650
+ writeLine(context.stdout, ` ${approval.operation_id} workspace ${approval.origin_account_id}: ${approval.action_label}, price ${approval.price_version}, maximum ${approval.maximum_credits} credits, revision ${approval.revision}`);
16651
+ }
16652
+ }
16653
+
16654
+ function renderCreditReceipt(usage, context) {
16655
+ writeLine(context.stdout, `${display(usage.label, "Credit usage")} (${display(usage.id, "unknown")})`);
16656
+ if (usage.origin_account_id) writeLine(context.stdout, `Workspace: ${usage.origin_account_id}`);
16657
+ if (typeof usage.credits === "string") writeLine(context.stdout, `Credits charged: ${usage.credits}`);
16658
+ if (typeof usage.returned_credits === "string") writeLine(context.stdout, `Credits returned: ${usage.returned_credits}`);
16659
+ if (usage.created_at) writeLine(context.stdout, `Date: ${usage.created_at}`);
16660
+ if (usage.result_url && usage.result_available) writeLine(context.stdout, `Saved result: ${usage.result_url}`);
16661
+ else if (usage.result_message) writeLine(context.stdout, usage.result_message);
16662
+ }
16663
+
16664
+ function safeCreditHistory(payload) {
16665
+ const result = {};
16666
+ if (typeof payload?.balance === "string") result.balance = payload.balance;
16667
+ if (Array.isArray(payload?.usages)) result.usages = payload.usages.map(safeCreditReceipt);
16668
+ if (typeof payload?.next_cursor === "string") result.next_cursor = payload.next_cursor;
16669
+ return result;
16670
+ }
16671
+
16672
+ function safeCreditReceipt(usage) {
16673
+ return pickFields(usage, ["id", "key", "label", "credits", "returned_credits", "origin_account_id", "created_at", "result_available", "result_url", "result_message"], {
16674
+ decimalFields: ["credits", "returned_credits"]
16675
+ });
16676
+ }
16677
+
16678
+ function safeCreditApprovals(payload) {
16679
+ const result = {};
16680
+ if (typeof payload?.payer_account_id === "string") result.payer_account_id = payload.payer_account_id;
16681
+ if (Array.isArray(payload?.prices)) result.prices = payload.prices.map((price) => ({ ...pickFields(price, ["id", "version", "effective_at", "email_sent_at", "accepted", "test_price"]), prices: Object.fromEntries(Object.entries(price.prices || {}).filter(([key, value]) => ["tool.email_find", "tool.signals_find"].includes(key) && typeof value === "string")) }));
16682
+ if (Array.isArray(payload?.payer_approvals)) result.payer_approvals = payload.payer_approvals.map((approval) => pickFields(approval,
16683
+ ["operation_id", "origin_account_id", "key", "action_label", "price_version", "revision", "maximum_credits"], { decimalFields: ["maximum_credits"] }));
16684
+ return result;
16685
+ }
16686
+
16687
+ function pickFields(value, fields, { decimalFields = [] } = {}) {
16688
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
16689
+ return Object.fromEntries(fields.filter((field) => {
16690
+ if (!Object.hasOwn(value, field)) return false;
16691
+ if (decimalFields.includes(field)) return typeof value[field] === "string";
16692
+ return typeof value[field] === "string" || typeof value[field] === "boolean" || Number.isSafeInteger(value[field]);
16693
+ }).map((field) => [field, value[field]]));
16694
+ }
16695
+
16548
16696
  async function toolsRunsList(args, context, { accountOverride } = {}) {
16549
16697
  const { values, positionals } = parseCommandArgs(args, {
16550
16698
  ...jsonOptions(),