@audienti/cli 0.1.138 → 0.1.140

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.140] - 2026-10-25
8
+
9
+ ### Added
10
+
11
+ - Connect Signal search to shared quote acceptance and one-search credit units.
12
+ - Add credit history, exact receipts, root price/payer approvals and staff result-refund commands through shared APIs.
13
+ - Preserve exact decimal credit strings and keep supplier accounting out of customer financial output.
14
+
15
+ ## [0.1.139] - 2026-10-25
16
+
17
+ ### Changed
18
+
19
+ - No CLI behavior change. The failed-draft requeue now reads its failed-status list from the draft model (#3091).
20
+
7
21
  ## [0.1.138] - 2026-10-24
8
22
 
9
23
  ### Added
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.138",
3
+ "version": "0.1.140",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -1351,6 +1351,34 @@ export class AudientiClient {
1351
1351
  return this.requestJson(accountPath(accountId, ["tools", "runs", runId, "export"], query));
1352
1352
  }
1353
1353
 
1354
+ creditRefundReview(accountId, usageId) {
1355
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId, "refund"]));
1356
+ }
1357
+
1358
+ approveCreditRefund(accountId, usageId, body) {
1359
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId, "refund"]), { method: "POST", body });
1360
+ }
1361
+
1362
+ creditUsages(accountId, query = {}) {
1363
+ return this.requestJson(accountPath(accountId, ["credit_usages"], query));
1364
+ }
1365
+
1366
+ creditUsage(accountId, usageId) {
1367
+ return this.requestJson(accountPath(accountId, ["credit_usages", usageId]));
1368
+ }
1369
+
1370
+ creditApprovals(accountId) {
1371
+ return this.requestJson(accountPath(accountId, ["credit_approvals"]));
1372
+ }
1373
+
1374
+ acceptCreditPrice(accountId, priceId) {
1375
+ return this.requestJson(accountPath(accountId, ["credit_approvals", "prices", priceId]), { method: "POST", body: {} });
1376
+ }
1377
+
1378
+ approveCreditPayer(accountId, operationId, body) {
1379
+ return this.requestJson(accountPath(accountId, ["credit_approvals", "payers", operationId]), { method: "POST", body });
1380
+ }
1381
+
1354
1382
  // Saved network of one connected account (owner-only).
1355
1383
  socialCookieNetwork(accountId, socialCookieId, query = {}) {
1356
1384
  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 });
@@ -16403,6 +16405,8 @@ async function toolsSignalsFind(args, context, { accountOverride } = {}) {
16403
16405
  const { values, positionals } = parseCommandArgs(args, {
16404
16406
  ...jsonOptions(),
16405
16407
  ...TOOL_RUN_WAIT_OPTIONS,
16408
+ quote: { type: "boolean" },
16409
+ "accept-quote": { type: "string" },
16406
16410
  icp: { type: "string" },
16407
16411
  question: { type: "string" },
16408
16412
  count: { type: "string" },
@@ -16456,7 +16460,7 @@ async function submitToolRun(tool, input, values, context, { accountOverride } =
16456
16460
  if (values["accept-quote"]) throw new CommandError("Choose --quote or --accept-quote, not both.");
16457
16461
  const quote = await client.quoteToolRun(accountId, { tool, input });
16458
16462
  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.`);
16463
+ 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
16464
  writeLine(context.stdout, `Accept with the same input and --accept-quote ${quote.quote_reference}`);
16461
16465
  return 0;
16462
16466
  }
@@ -16545,6 +16549,140 @@ async function toolsRunsCredits(action, args, context, { accountOverride } = {})
16545
16549
  return writeJson(context.stdout, result);
16546
16550
  }
16547
16551
 
16552
+ async function creditsCommand(action, args, context, { accountOverride } = {}) {
16553
+ if (["refund-review", "refund"].includes(action)) return creditRefundCommand(action, args, context, { accountOverride });
16554
+ if (action === "history") return creditsHistory(args, context, { accountOverride });
16555
+ if (action === "show") return creditUsageShow(args, context, { accountOverride });
16556
+ if (action === "approvals") return creditApprovals(args, context, { accountOverride });
16557
+ if (action === "accept-price") return acceptCreditPrice(args, context, { accountOverride });
16558
+ if (action === "approve-payer") return approveCreditPayer(args, context, { accountOverride });
16559
+ throw new CommandError(CREDITS_USAGE);
16560
+ }
16561
+
16562
+ async function creditRefundCommand(action, args, context, { accountOverride } = {}) {
16563
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(),
16564
+ "cumulative-principal": { type: "string" }, "evidence-token": { type: "string" }, reason: { type: "string" }, evidence: { type: "string" }
16565
+ });
16566
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16567
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16568
+ if (action === "refund-review") return writeJson(context.stdout, await client.creditRefundReview(accountId, positionals[0]));
16569
+ if (!["cumulative-principal", "evidence-token", "reason", "evidence"].every((key) => values[key])) {
16570
+ throw new CommandError("Staff only: credits refund <usage_id> --cumulative-principal <exact_credits> --evidence-token <uuid> --reason <confirmed_defect> --evidence <exact_result_evidence>");
16571
+ }
16572
+ return writeJson(context.stdout, await client.approveCreditRefund(accountId, positionals[0], {
16573
+ cumulative_principal: values["cumulative-principal"], evidence_token: values["evidence-token"], reason: values.reason, evidence: values.evidence
16574
+ }));
16575
+ }
16576
+
16577
+ async function creditsHistory(args, context, { accountOverride } = {}) {
16578
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), limit: { type: "string" }, cursor: { type: "string" } });
16579
+ if (positionals.length > 0) throw new CommandError(CREDITS_USAGE);
16580
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16581
+ const payload = safeCreditHistory(await client.creditUsages(accountId, compactObject({
16582
+ limit: normalizeOptionalPositiveInteger(values.limit, "--limit"), cursor: values.cursor
16583
+ })));
16584
+ if (values.json) return writeJson(context.stdout, payload);
16585
+ writeLine(context.stdout, `Credit balance: ${display(payload.balance, "unknown")}`);
16586
+ const usages = payload.usages || [];
16587
+ if (usages.length === 0) return writeLine(context.stdout, "No customer credit charges recorded.");
16588
+ for (const usage of usages) renderCreditReceipt(usage, context);
16589
+ if (payload.next_cursor) writeLine(context.stdout, `More credit history: audienti credits history --cursor ${payload.next_cursor}`);
16590
+ }
16591
+
16592
+ async function creditUsageShow(args, context, { accountOverride } = {}) {
16593
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16594
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16595
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16596
+ const usage = safeCreditReceipt(await client.creditUsage(accountId, positionals[0]));
16597
+ if (values.json) return writeJson(context.stdout, usage);
16598
+ renderCreditReceipt(usage, context);
16599
+ }
16600
+
16601
+ async function creditApprovals(args, context, { accountOverride } = {}) {
16602
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16603
+ if (positionals.length > 0) throw new CommandError(CREDITS_USAGE);
16604
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16605
+ const approvals = safeCreditApprovals(await client.creditApprovals(accountId));
16606
+ if (values.json) return writeJson(context.stdout, approvals);
16607
+ renderCreditApprovals(approvals, context);
16608
+ }
16609
+
16610
+ async function acceptCreditPrice(args, context, { accountOverride } = {}) {
16611
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
16612
+ if (positionals.length !== 1) throw new CommandError(CREDITS_USAGE);
16613
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16614
+ const approvals = safeCreditApprovals(await client.acceptCreditPrice(accountId, positionals[0]));
16615
+ if (values.json) return writeJson(context.stdout, approvals);
16616
+ writeLine(context.stdout, `Price approval recorded for ${positionals[0]}.`);
16617
+ renderCreditApprovals(approvals, context);
16618
+ }
16619
+
16620
+ async function approveCreditPayer(args, context, { accountOverride } = {}) {
16621
+ const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), revision: { type: "string" }, "request-key": { type: "string" } });
16622
+ if (positionals.length !== 1 || !values.revision || !values["request-key"]) throw new CommandError(CREDITS_USAGE);
16623
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16624
+ const approvals = safeCreditApprovals(await client.approveCreditPayer(accountId, positionals[0], {
16625
+ revision: values.revision, idempotency_key: values["request-key"]
16626
+ }));
16627
+ if (values.json) return writeJson(context.stdout, approvals);
16628
+ writeLine(context.stdout, `Payer approval recorded for operation ${positionals[0]}. Waiting work remains paused.`);
16629
+ renderCreditApprovals(approvals, context);
16630
+ }
16631
+
16632
+ function renderCreditApprovals(approvals, context) {
16633
+ writeLine(context.stdout, `Payer: ${display(approvals.payer_account_id, "unknown")}`);
16634
+ writeLine(context.stdout, "Credit prices:");
16635
+ for (const price of approvals.prices || []) {
16636
+ writeLine(context.stdout, ` ${price.id} ${price.version}: ${JSON.stringify(price.prices)} credits (${price.accepted ? "approved" : "approval required"})${price.test_price ? " [test price]" : ""}`);
16637
+ }
16638
+ writeLine(context.stdout, "Payer approvals waiting:");
16639
+ for (const approval of approvals.payer_approvals || []) {
16640
+ 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}`);
16641
+ }
16642
+ }
16643
+
16644
+ function renderCreditReceipt(usage, context) {
16645
+ writeLine(context.stdout, `${display(usage.label, "Credit usage")} (${display(usage.id, "unknown")})`);
16646
+ if (usage.origin_account_id) writeLine(context.stdout, `Workspace: ${usage.origin_account_id}`);
16647
+ if (typeof usage.credits === "string") writeLine(context.stdout, `Credits charged: ${usage.credits}`);
16648
+ if (typeof usage.returned_credits === "string") writeLine(context.stdout, `Credits returned: ${usage.returned_credits}`);
16649
+ if (usage.created_at) writeLine(context.stdout, `Date: ${usage.created_at}`);
16650
+ if (usage.result_url && usage.result_available) writeLine(context.stdout, `Saved result: ${usage.result_url}`);
16651
+ else if (usage.result_message) writeLine(context.stdout, usage.result_message);
16652
+ }
16653
+
16654
+ function safeCreditHistory(payload) {
16655
+ const result = {};
16656
+ if (typeof payload?.balance === "string") result.balance = payload.balance;
16657
+ if (Array.isArray(payload?.usages)) result.usages = payload.usages.map(safeCreditReceipt);
16658
+ if (typeof payload?.next_cursor === "string") result.next_cursor = payload.next_cursor;
16659
+ return result;
16660
+ }
16661
+
16662
+ function safeCreditReceipt(usage) {
16663
+ return pickFields(usage, ["id", "key", "label", "credits", "returned_credits", "origin_account_id", "created_at", "result_available", "result_url", "result_message"], {
16664
+ decimalFields: ["credits", "returned_credits"]
16665
+ });
16666
+ }
16667
+
16668
+ function safeCreditApprovals(payload) {
16669
+ const result = {};
16670
+ if (typeof payload?.payer_account_id === "string") result.payer_account_id = payload.payer_account_id;
16671
+ 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")) }));
16672
+ if (Array.isArray(payload?.payer_approvals)) result.payer_approvals = payload.payer_approvals.map((approval) => pickFields(approval,
16673
+ ["operation_id", "origin_account_id", "key", "action_label", "price_version", "revision", "maximum_credits"], { decimalFields: ["maximum_credits"] }));
16674
+ return result;
16675
+ }
16676
+
16677
+ function pickFields(value, fields, { decimalFields = [] } = {}) {
16678
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
16679
+ return Object.fromEntries(fields.filter((field) => {
16680
+ if (!Object.hasOwn(value, field)) return false;
16681
+ if (decimalFields.includes(field)) return typeof value[field] === "string";
16682
+ return typeof value[field] === "string" || typeof value[field] === "boolean" || Number.isSafeInteger(value[field]);
16683
+ }).map((field) => [field, value[field]]));
16684
+ }
16685
+
16548
16686
  async function toolsRunsList(args, context, { accountOverride } = {}) {
16549
16687
  const { values, positionals } = parseCommandArgs(args, {
16550
16688
  ...jsonOptions(),