@audienti/cli 0.1.137 → 0.1.139

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,19 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.139] - 2026-10-25
8
+
9
+ ### Changed
10
+
11
+ - No CLI behavior change. The failed-draft requeue now reads its failed-status list from the draft model (#3091).
12
+
13
+ ## [0.1.138] - 2026-10-24
14
+
15
+ ### Added
16
+
17
+ - Review and explicitly accept Email Find credit quotes. Review queued rows and resume, pause, cancel or approve a new payer through shared API controls. Read exact credit receipts; ordinary tool history hides supplier monetary amounts (#2643). Real charging remains separately gated.
18
+
19
+
7
20
  ## [0.1.137] - 2026-10-24
8
21
 
9
22
  ### Changed
package/README.md CHANGED
@@ -328,6 +328,23 @@ 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:
332
+
333
+ ```bash
334
+ audienti tools email-find --file people.csv --quote
335
+ audienti tools email-find --file people.csv --accept-quote <quote_reference>
336
+ audienti tools runs review <run_id>
337
+ audienti tools runs resume <run_id> --item <item_id> --revision <revision> --idempotency-key <unique_key>
338
+ ```
339
+
340
+ Use the same input when accepting the quote. Queued rows retain the accepted price.
341
+ Review reports available credits and the rows that can continue. Select fewer rows
342
+ or top up; a top-up does not restart manually paused work. Use `pause` or `cancel`
343
+ in place of `resume` with the same reviewed selection. `approve-payer` requires the
344
+ new root administrator and does not resume work. `tools runs usage <usage_reference>`
345
+ opens the exact receipt data. Tool history displays credits; supplier costs stay internal.
346
+ Real prices and production charging require separate approval.
347
+
331
348
  Prefer `tools email-find` for an email lookup. `tools get email` still runs the
332
349
  prospect import pipeline and creates a prospect.
333
350
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.137",
3
+ "version": "0.1.139",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -1318,6 +1318,18 @@ export class AudientiClient {
1318
1318
  }
1319
1319
 
1320
1320
  // Standalone account tool runs (#2554).
1321
+ quoteToolRun(accountId, body) {
1322
+ return this.requestJson(accountPath(accountId, ["tools", "run_quotes"]), { method: "POST", body });
1323
+ }
1324
+
1325
+ toolRunCreditAction(accountId, runId, body) {
1326
+ return this.requestJson(accountPath(accountId, ["tools", "runs", runId, "credit_actions"]), { method: "POST", body });
1327
+ }
1328
+
1329
+ toolCreditUsage(accountId, reference) {
1330
+ return this.requestJson(accountPath(accountId, ["tools", "credit_usages", reference]));
1331
+ }
1332
+
1321
1333
  createToolRun(accountId, body) {
1322
1334
  return this.requestJson(accountPath(accountId, ["tools", "runs"]), { method: "POST", body });
1323
1335
  }
package/src/cli.js CHANGED
@@ -231,11 +231,11 @@ 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 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>) ${TOOL_RUN_FLAGS_USAGE}`;
234
+ 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
235
  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
236
  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}`;
237
237
  const TOOLS_WRITE_USAGE = `Usage: audienti tools write --purpose <text> --audience <text> --fact <text> [--fact <text> ...] --channel <channel> [--tone <tone>] [--subject|--no-subject] [--prospect <prsp_id>] ${TOOL_RUN_FLAGS_USAGE}`;
238
- const TOOLS_RUNS_USAGE = "Usage: audienti tools runs (list [--tool <tool>] [--status <status>] [--limit <n>] [--cursor <id>] | show <trun_id> | results <trun_id> [--limit <n>] [--cursor <n>] | export <trun_id> [--format <csv|json>] [--output <path>]) [--json] [--account <acct_id>]";
238
+ const TOOLS_RUNS_USAGE = "Usage: audienti tools runs (list [--tool <tool>] [--status <status>] [--limit <n>] [--cursor <id>] | show <trun_id> | results <trun_id> [--limit <n>] [--cursor <n>] | export <trun_id> [--format <csv|json>] [--output <path>] | review <trun_id> | (resume|pause|cancel|approve-payer) <trun_id> --item <item_id> --revision <revision> --idempotency-key <key> [--cancel-others] | usage <reference>) [--json] [--account <acct_id>]";
239
239
  const NETWORK_USAGE = "Usage: audienti network (list|export) --cookie <scok_id> [--platform <linkedin|twitter>] [--kind <connection|follow>] [--direction <incoming|outgoing>] [--query <text>] [--limit <n>] [--cursor <n>] [--format <csv|json>] [--output <path>] [--json] [--account <acct_id>]";
240
240
  const TOOLS_LINKEDIN_REVIEW_USAGE = "Usage: audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>] [--json] [--account <acct_id>]";
241
241
  const TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE = "Usage: audienti tools linkedin-review reports [--limit <n>] [--json] [--account <acct_id>]";
@@ -16353,6 +16353,8 @@ async function toolsEmailFind(args, context, { accountOverride } = {}) {
16353
16353
  const { values, positionals } = parseCommandArgs(args, {
16354
16354
  ...jsonOptions(),
16355
16355
  ...TOOL_RUN_WAIT_OPTIONS,
16356
+ quote: { type: "boolean" },
16357
+ "accept-quote": { type: "string" },
16356
16358
  "first-name": { type: "string" },
16357
16359
  "last-name": { type: "string" },
16358
16360
  "company-domain": { type: "string" },
@@ -16450,7 +16452,16 @@ async function toolsWrite(args, context, { accountOverride } = {}) {
16450
16452
 
16451
16453
  async function submitToolRun(tool, input, values, context, { accountOverride } = {}) {
16452
16454
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
16453
- const run = await client.createToolRun(accountId, compactObject({ tool, input, request_key: values["request-key"] }));
16455
+ if (values.quote) {
16456
+ if (values["accept-quote"]) throw new CommandError("Choose --quote or --accept-quote, not both.");
16457
+ const quote = await client.quoteToolRun(accountId, { tool, input });
16458
+ 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.`);
16460
+ writeLine(context.stdout, `Accept with the same input and --accept-quote ${quote.quote_reference}`);
16461
+ return 0;
16462
+ }
16463
+ const run = await client.createToolRun(accountId, compactObject({ tool, input, request_key: values["request-key"],
16464
+ quote_reference: values["accept-quote"], accept_quote: values["accept-quote"] ? true : undefined }));
16454
16465
  if (!values.wait) {
16455
16466
  if (values.json) return writeJson(context.stdout, run);
16456
16467
 
@@ -16507,6 +16518,7 @@ async function fetchAllToolRunItems(client, accountId, runId) {
16507
16518
 
16508
16519
  async function toolsRuns(args, context, { accountOverride } = {}) {
16509
16520
  const [subcommand, ...rest] = args;
16521
+ if (["review", "resume", "pause", "cancel", "approve-payer", "usage"].includes(subcommand)) return toolsRunsCredits(subcommand, rest, context, { accountOverride });
16510
16522
  if (subcommand === "list") return toolsRunsList(rest, context, { accountOverride });
16511
16523
  if (subcommand === "show") return toolsRunsShow(rest, context, { accountOverride });
16512
16524
  if (subcommand === "results") return toolsRunsResults(rest, context, { accountOverride });
@@ -16515,6 +16527,24 @@ async function toolsRuns(args, context, { accountOverride } = {}) {
16515
16527
  throw new CommandError(TOOLS_RUNS_USAGE);
16516
16528
  }
16517
16529
 
16530
+ async function toolsRunsCredits(action, args, context, { accountOverride } = {}) {
16531
+ const { values, positionals } = parseCommandArgs(args, {
16532
+ ...jsonOptions(), item: { type: "string", multiple: true }, revision: { type: "string" },
16533
+ "idempotency-key": { type: "string" }, "cancel-others": { type: "boolean" }
16534
+ });
16535
+ if (positionals.length !== 1) throw new CommandError("Supply one run id (or usage reference for usage).");
16536
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
16537
+ if (action === "usage") return writeJson(context.stdout, await client.toolCreditUsage(accountId, positionals[0]));
16538
+ if (action !== "review" && (!values.item?.length || !values.revision || !values["idempotency-key"])) {
16539
+ throw new CommandError("Review first, then supply --item for each selected row, --revision and --idempotency-key.");
16540
+ }
16541
+ const result = await client.toolRunCreditAction(accountId, positionals[0], compactObject({
16542
+ action_name: action.replaceAll("-", "_"), item_ids: values.item, revision: values.revision,
16543
+ idempotency_key: values["idempotency-key"], cancel_others: values["cancel-others"]
16544
+ }));
16545
+ return writeJson(context.stdout, result);
16546
+ }
16547
+
16518
16548
  async function toolsRunsList(args, context, { accountOverride } = {}) {
16519
16549
  const { values, positionals } = parseCommandArgs(args, {
16520
16550
  ...jsonOptions(),
@@ -16666,8 +16696,8 @@ function renderToolRun(run, context) {
16666
16696
  if (Number(run.summary.cache_hit_count) > 0) usage.push(`${run.summary.cache_hit_count} cached`);
16667
16697
  writeLine(context.stdout, `Usage: ${usage.join("; ")}`);
16668
16698
  }
16669
- const stillRunningWithoutExpenses = ["queued", "running"].includes(run?.status) && run?.current_expenses?.state === "none";
16670
- if (run?.current_expenses && !stillRunningWithoutExpenses) writeLine(context.stdout, `Recorded expenses: ${toolRunExpenseSummary(run.current_expenses)}`);
16699
+ if (run?.credit_usage) writeLine(context.stdout, run.credit_usage.credit_history_available
16700
+ ? `Charged: ${run.credit_usage.charged_credits} credits` : "Credit history unavailable");
16671
16701
  const outcomes = run?.summary?.outcomes || {};
16672
16702
  if (Object.keys(outcomes).length > 0) {
16673
16703
  writeLine(context.stdout, `Outcomes: ${Object.entries(outcomes).map(([outcome, count]) => `${outcome} ${count}`).join(", ")}`);
@@ -16676,31 +16706,6 @@ function renderToolRun(run, context) {
16676
16706
  if (run?.details_expired) writeLine(context.stdout, "Details expired after 90 days; only this summary remains.");
16677
16707
  }
16678
16708
 
16679
- function toolRunExpenseSummary(expenses) {
16680
- if (expenses.state === "unavailable") return "data unavailable";
16681
- if (expenses.state === "none") return "no recorded expenses";
16682
- if (expenses.state === "expired") {
16683
- const known = expenses.completion_snapshot?.known_billed_expense_cents;
16684
- return `current receipt detail expired; completion-time snapshot ${expenseAmountLabel(known)}; ${display(expenses.uncertain_attempt_count, 0)} call(s) with unknown or uncertain expense at completion`;
16685
- }
16686
-
16687
- const known = expenses.known_expense_cents;
16688
- const parts = [known === null || known === undefined ? "no priced receipts" : `${expenseAmountLabel(known)} recorded`];
16689
- if (expenses.unknown_expense_receipt_count > 0) parts.push(`${expenses.unknown_expense_receipt_count} linked receipt(s) with unknown amount`);
16690
- if (expenses.uncertain_attempt_count > 0) parts.push(`${expenses.uncertain_attempt_count} call(s) with unknown or uncertain expense`);
16691
- if (expenses.completion_unverified_attempt_count > 0) parts.push(`${expenses.completion_unverified_attempt_count} call(s) with cost unverified at completion`);
16692
- if (expenses.in_progress_attempt_count > 0) parts.push(`${expenses.in_progress_attempt_count} call(s) in progress`);
16693
- if (expenses.state === "partial") parts.push("known sum may be partial");
16694
- return parts.join("; ");
16695
- }
16696
-
16697
- function expenseAmountLabel(cents) {
16698
- if (cents === null || cents === undefined) return "no known amount";
16699
- const amount = Number(cents);
16700
- if (amount > 0 && amount < 0.01) return "less than 0.01¢";
16701
- return `${amount.toFixed(2)}¢`;
16702
- }
16703
-
16704
16709
  function renderToolRunItems(tool, items, context) {
16705
16710
  if (items.length === 0) return writeLine(context.stdout, "No results yet.");
16706
16711