@audienti/cli 0.1.139 → 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 +8 -0
- package/README.md +17 -1
- package/package.json +1 -1
- package/src/api-client.js +28 -0
- package/src/cli.js +139 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ 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
|
+
|
|
7
15
|
## [0.1.139] - 2026-10-25
|
|
8
16
|
|
|
9
17
|
### 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
|
|
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
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(),
|