@go-labs-sg/bb 2.42.0 → 2.43.0

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/README.md CHANGED
@@ -22,6 +22,27 @@ The shared API rechecks existing records at submission. Canonical matches reuse
22
22
 
23
23
  `bb company use` and `bb company create` are state-changing, potentially external-writing commands. In non-interactive mode their existing permission gates require `--allow-state-change --allow-external-write`. Deploy the backend before publishing this CLI change. See [company discovery details](../api/docs/company-discovery.md) for limits and retry behaviour.
24
24
 
25
+ ## Record a payment made outside Budget Builder
26
+
27
+ Admins can record an already-made payment on an APPROVED bill or claim without transferring money or emailing the supplier/claimant:
28
+
29
+ ```bash
30
+ bb bill payment accounts
31
+ bb bill payment record-external <billId> \
32
+ --paymentReference 2343190532 \
33
+ --paymentDate 2026-09-01 \
34
+ --paymentAccount <quickbooks-account-id> \
35
+ --paymentProof receipt.pdf
36
+ ```
37
+
38
+ Choose the bank or card account actually used (including `Events Requisitions`); no account or date is defaulted. Omit `--paymentProof` if a payment-proof PDF is already attached. In non-interactive use, add `--allow-state-change --allow-external-write --allow-financial-write`; no `--allow-email` is needed. The UI offers the same **Record external payment** action next to ordinary **Mark paid**.
39
+
40
+ BB marks the bill PAID immediately and queues durable QuickBooks reconciliation. A settled zero-balance QBO bill reuses its existing BillPayment IDs. Otherwise reconciliation creates a payment with the specified date/account and a stable provider request ID. Partially settled or inconsistent QBO bills require manual reconciliation; BB never guesses. Inspect failed/pending work in Integration Queue and **do not pay the bill again**. Exact retries with the same reference/date/account return the recorded settlement; changed details conflict. Ordinary Mark paid keeps its existing payment-advice workflow.
41
+
42
+ `bb update-bill --payload '{"id":"<billId>","comment":"Already paid; do not pay again"}'` now accepts a partial payload. Omitted fields are preserved; comment-only updates do not change financial data or sync QBO, and are allowed on approved/paid bills for the creator or an admin. Financial edits retain their draft/rejected restrictions.
43
+
44
+ Deployment prerequisite: apply the Budget Builder Prisma schema additions (`Bill.externalPayment` JSON and `EXTERNAL_PAYMENT` operation enum) before deploying the API/web/workers; deploy the backend before publishing the CLI. This feature does not modify existing paid bills or repair historical QBO entries automatically.
45
+
25
46
  ## Payload help and uncertain writes
26
47
 
27
48
  Run `bb <command> --help` (for example, `bb create-item --help`) to see required and optional `--payload` fields and an example JSON object. Item creation and updates accept a finite numeric `cost` and convert it to the API's decimal-string input. Validation errors identify the affected field; unsupported QuickBooks name characters are listed explicitly.
@@ -2261,7 +2261,7 @@
2261
2261
  {
2262
2262
  "path": ["bill", "update"],
2263
2263
  "legacyAliases": ["update-bill", "update_bill"],
2264
- "summary": "Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.",
2264
+ "summary": "Update a bill or claim with a partial payload including id; omitted fields are preserved. Comment-only updates are allowed on approved/paid bills without altering financial data or QuickBooks. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.",
2265
2265
  "globalOptions": [
2266
2266
  {
2267
2267
  "name": "--help",
@@ -2461,6 +2461,112 @@
2461
2461
  "effects": ["state-change", "email", "financial-write"],
2462
2462
  "legacyTarget": "update-bill-status"
2463
2463
  },
2464
+ {
2465
+ "path": ["bill", "payment", "record-external"],
2466
+ "legacyAliases": [
2467
+ "record-external-bill-payment",
2468
+ "record_external_bill_payment"
2469
+ ],
2470
+ "summary": "Record an already-made payment on an APPROVED bill or claim, with no email and no money transfer. Requires <billId> --paymentReference --paymentDate YYYY-MM-DD --paymentAccount <QBO account ID> [--paymentProof receipt.pdf]. Reuses settled QBO payment links or queues one dated payment from the selected account. Exact retries are safe; do not pay again while reconciliation is pending.",
2471
+ "globalOptions": [
2472
+ {
2473
+ "name": "--help",
2474
+ "description": "Show help for this command."
2475
+ },
2476
+ {
2477
+ "name": "--quiet",
2478
+ "description": "Suppress non-error diagnostics."
2479
+ },
2480
+ {
2481
+ "name": "--debug",
2482
+ "description": "Emit sanitized diagnostic traces."
2483
+ },
2484
+ {
2485
+ "name": "--api-url",
2486
+ "description": "Override the Budget Builder API base URL."
2487
+ },
2488
+ {
2489
+ "name": "--no-browser",
2490
+ "description": "Print the authorization URL without opening a browser."
2491
+ },
2492
+ {
2493
+ "name": "--allow-state-change",
2494
+ "description": "Allow a Budget Builder state change in non-interactive use."
2495
+ },
2496
+ {
2497
+ "name": "--allow-email",
2498
+ "description": "Allow sending email in non-interactive use."
2499
+ },
2500
+ {
2501
+ "name": "--allow-external-write",
2502
+ "description": "Allow writes to external systems in non-interactive use."
2503
+ },
2504
+ {
2505
+ "name": "--allow-delete",
2506
+ "description": "Allow deleting data in non-interactive use."
2507
+ },
2508
+ {
2509
+ "name": "--allow-financial-write",
2510
+ "description": "Allow financial-record changes in non-interactive use."
2511
+ }
2512
+ ],
2513
+ "argumentMode": "legacy-passthrough",
2514
+ "effects": ["state-change", "external-write", "financial-write"],
2515
+ "legacyTarget": "record-external-bill-payment"
2516
+ },
2517
+ {
2518
+ "path": ["bill", "payment", "accounts"],
2519
+ "legacyAliases": [
2520
+ "list-external-payment-accounts",
2521
+ "list_external_payment_accounts"
2522
+ ],
2523
+ "summary": "List active QuickBooks bank and credit-card account IDs for recording external payments (admin only).",
2524
+ "globalOptions": [
2525
+ {
2526
+ "name": "--help",
2527
+ "description": "Show help for this command."
2528
+ },
2529
+ {
2530
+ "name": "--quiet",
2531
+ "description": "Suppress non-error diagnostics."
2532
+ },
2533
+ {
2534
+ "name": "--debug",
2535
+ "description": "Emit sanitized diagnostic traces."
2536
+ },
2537
+ {
2538
+ "name": "--api-url",
2539
+ "description": "Override the Budget Builder API base URL."
2540
+ },
2541
+ {
2542
+ "name": "--no-browser",
2543
+ "description": "Print the authorization URL without opening a browser."
2544
+ },
2545
+ {
2546
+ "name": "--allow-state-change",
2547
+ "description": "Allow a Budget Builder state change in non-interactive use."
2548
+ },
2549
+ {
2550
+ "name": "--allow-email",
2551
+ "description": "Allow sending email in non-interactive use."
2552
+ },
2553
+ {
2554
+ "name": "--allow-external-write",
2555
+ "description": "Allow writes to external systems in non-interactive use."
2556
+ },
2557
+ {
2558
+ "name": "--allow-delete",
2559
+ "description": "Allow deleting data in non-interactive use."
2560
+ },
2561
+ {
2562
+ "name": "--allow-financial-write",
2563
+ "description": "Allow financial-record changes in non-interactive use."
2564
+ }
2565
+ ],
2566
+ "argumentMode": "legacy-passthrough",
2567
+ "effects": [],
2568
+ "legacyTarget": "list-external-payment-accounts"
2569
+ },
2464
2570
  {
2465
2571
  "path": ["bill", "payment", "patch"],
2466
2572
  "legacyAliases": ["patch-bill-payment", "patch_bill_payment"],
@@ -411,7 +411,7 @@ Payload fields (--payload JSON)
411
411
 
412
412
  ## `bb bill update`
413
413
 
414
- Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.
414
+ Update a bill or claim with a partial payload including id; omitted fields are preserved. Comment-only updates are allowed on approved/paid bills without altering financial data or QuickBooks. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.
415
415
 
416
416
  Legacy aliases: `update-bill`, `update_bill`.
417
417
 
@@ -451,6 +451,22 @@ Legacy aliases: `update-bill-status`, `update_bill_status`.
451
451
 
452
452
  Effects: state-change, email, financial-write.
453
453
 
454
+ ## `bb bill payment record-external`
455
+
456
+ Record an already-made payment on an APPROVED bill or claim, with no email and no money transfer. Requires <billId> --paymentReference --paymentDate YYYY-MM-DD --paymentAccount <QBO account ID> [--paymentProof receipt.pdf]. Reuses settled QBO payment links or queues one dated payment from the selected account. Exact retries are safe; do not pay again while reconciliation is pending.
457
+
458
+ Legacy aliases: `record-external-bill-payment`, `record_external_bill_payment`.
459
+
460
+ Effects: state-change, external-write, financial-write.
461
+
462
+ ## `bb bill payment accounts`
463
+
464
+ List active QuickBooks bank and credit-card account IDs for recording external payments (admin only).
465
+
466
+ Legacy aliases: `list-external-payment-accounts`, `list_external_payment_accounts`.
467
+
468
+ Effects: none.
469
+
454
470
  ## `bb bill payment patch`
455
471
 
456
472
  Patch Bill Payment.
package/dist/index.js CHANGED
@@ -7665,14 +7665,110 @@ function parseCreateBillPayload(raw, draftOnlyFlag, quotationExempt = false, req
7665
7665
  }
7666
7666
  function parseUpdateBillPayload(raw) {
7667
7667
  const o = requireObject(raw, "update-bill payload");
7668
- const { draftOnly, ...input } = parseCreateBillPayload(raw, undefined, false, false);
7669
- if (draftOnly !== undefined) {
7668
+ if (o.draftOnly !== undefined) {
7670
7669
  throw validationError("draftOnly is only supported when creating a bill.");
7671
7670
  }
7672
- return {
7673
- ...input,
7671
+ const input = {
7674
7672
  id: requiredString(o.id, "update-bill payload.id")
7675
7673
  };
7674
+ const knownFields = new Set([
7675
+ "id",
7676
+ "budgetId",
7677
+ "projectId",
7678
+ "supplierId",
7679
+ "quotationId",
7680
+ "quotationIds",
7681
+ "quotationAllocations",
7682
+ "overBudgetOverrideReason",
7683
+ "amount",
7684
+ "budgetItemIds",
7685
+ "lineAmounts",
7686
+ "comment",
7687
+ "isClaimable",
7688
+ "attachments",
7689
+ "alreadyPaid",
7690
+ "paymentReference",
7691
+ "paymentProofAttachments",
7692
+ "extractedSupplierName",
7693
+ "extractedAmount",
7694
+ "invoiceNumber",
7695
+ "invoiceDate",
7696
+ "verificationStatus",
7697
+ "verificationResults"
7698
+ ]);
7699
+ for (const key of Object.keys(o))
7700
+ if (!knownFields.has(key))
7701
+ throw validationError(`Unknown update-bill payload field: ${key}`);
7702
+ for (const key of [
7703
+ "budgetId",
7704
+ "projectId",
7705
+ "supplierId",
7706
+ "comment",
7707
+ "paymentReference",
7708
+ "extractedSupplierName",
7709
+ "invoiceNumber",
7710
+ "overBudgetOverrideReason"
7711
+ ]) {
7712
+ if (o[key] === undefined)
7713
+ continue;
7714
+ if (typeof o[key] !== "string")
7715
+ throw validationError(`update-bill payload.${key} must be a string.`);
7716
+ input[key] = o[key];
7717
+ }
7718
+ for (const key of ["amount", "extractedAmount"]) {
7719
+ if (o[key] === undefined)
7720
+ continue;
7721
+ const amount = Number(o[key]);
7722
+ if (!Number.isFinite(amount) || amount < 0)
7723
+ throw validationError(`update-bill payload.${key} must be a number >= 0.`);
7724
+ input[key] = amount;
7725
+ }
7726
+ for (const key of ["isClaimable", "alreadyPaid"]) {
7727
+ if (o[key] === undefined)
7728
+ continue;
7729
+ if (typeof o[key] !== "boolean")
7730
+ throw validationError(`update-bill payload.${key} must be boolean.`);
7731
+ input[key] = o[key];
7732
+ }
7733
+ for (const key of ["attachments", "paymentProofAttachments"]) {
7734
+ const attachments = o[key];
7735
+ if (attachments === undefined)
7736
+ continue;
7737
+ if (!Array.isArray(attachments))
7738
+ throw validationError(`update-bill payload.${key} must be an array.`);
7739
+ input[key] = attachments.map((value, index) => billAttachment(value, index, key));
7740
+ }
7741
+ if (o.budgetItemIds !== undefined) {
7742
+ if (!Array.isArray(o.budgetItemIds) || !o.budgetItemIds.length)
7743
+ throw validationError("update-bill payload.budgetItemIds must be a non-empty array.");
7744
+ input.budgetItemIds = o.budgetItemIds.map((id, index) => requiredString(id, `update-bill payload.budgetItemIds[${index}]`));
7745
+ }
7746
+ Object.assign(input, parseQuotationReferences(o, "update-bill"));
7747
+ if (o.lineAmounts !== undefined)
7748
+ input.lineAmounts = parseBillLineAmounts(o, "update-bill");
7749
+ if (o.invoiceDate !== undefined)
7750
+ input.invoiceDate = toDate(o.invoiceDate, "invoiceDate");
7751
+ if (o.verificationStatus !== undefined) {
7752
+ if (!Object.values(BillVerificationStatus).includes(o.verificationStatus))
7753
+ throw validationError("Invalid bill verification status.");
7754
+ input.verificationStatus = o.verificationStatus;
7755
+ }
7756
+ if (o.verificationResults !== undefined) {
7757
+ const value = requireObject(o.verificationResults, "verificationResults");
7758
+ const status = value.status;
7759
+ if (status !== BillVerificationStatus.PENDING && status !== BillVerificationStatus.PASS && status !== BillVerificationStatus.FAIL && status !== BillVerificationStatus.MANUAL_REVIEW)
7760
+ throw validationError("Invalid verificationResults.status.");
7761
+ input.verificationResults = {
7762
+ status,
7763
+ extractedAmount: value.extractedAmount == null ? null : Number(value.extractedAmount),
7764
+ difference: value.difference == null ? null : Number(value.difference),
7765
+ confidence: value.confidence == null ? null : Number(value.confidence),
7766
+ failureReason: value.failureReason == null ? null : String(value.failureReason)
7767
+ };
7768
+ }
7769
+ if (Object.keys(input).length === 1)
7770
+ throw validationError("Supply at least one field to update.");
7771
+ return input;
7676
7772
  }
7677
7773
  function parseValidateBillSelectionPayload(raw, quotationFlags = {}) {
7678
7774
  const input = requireObject(raw, "validate-bill-selection payload");
@@ -8836,6 +8932,42 @@ async function updateBillStatus(opts) {
8836
8932
  } : undefined);
8837
8933
  }
8838
8934
  }
8935
+ var listExternalBillPaymentAccounts = async () => {
8936
+ out(await api2.bill.getExternalPaymentAccounts.query());
8937
+ };
8938
+ var recordExternalBillPayment = async (opts) => {
8939
+ if (!opts.paymentReference.trim() || !opts.paymentAccount.trim() || !/^\d{4}-\d{2}-\d{2}$/.test(opts.paymentDate) || new Date(`${opts.paymentDate}T00:00:00Z`).toISOString().slice(0, 10) !== opts.paymentDate)
8940
+ throw new Error("Supply --paymentReference, --paymentDate YYYY-MM-DD, and --paymentAccount <QBO account ID>.");
8941
+ await assertSensitiveWorkflowConfirmed({
8942
+ action: "Record external payment (no email)",
8943
+ entity: `bill ${opts.id}`,
8944
+ details: `already paid on ${opts.paymentDate}; reference ${opts.paymentReference}; QBO account ${opts.paymentAccount}. No money is transferred.`
8945
+ });
8946
+ const bill = await api2.bill.getById.query({ id: opts.id });
8947
+ const proof = !bill.externalPayment && opts.paymentProofPath ? await stageBillPaymentProof(bill.projectId, opts.paymentProofPath) : undefined;
8948
+ try {
8949
+ out(await api2.bill.recordExternalPayment.mutate({
8950
+ id: opts.id,
8951
+ paymentReference: opts.paymentReference,
8952
+ paymentDate: opts.paymentDate,
8953
+ paymentAccount: opts.paymentAccount,
8954
+ paymentProofAttachments: proof ? [proof] : undefined
8955
+ }));
8956
+ } catch (error) {
8957
+ try {
8958
+ const latest = await api2.bill.getById.query({ id: opts.id });
8959
+ if (latest.externalPayment?.paymentAccount === opts.paymentAccount && latest.paymentReference === opts.paymentReference.trim() && latest.paymentDate?.toISOString().slice(0, 10) === opts.paymentDate) {
8960
+ out({
8961
+ ...latest,
8962
+ supplierNotified: false,
8963
+ quickbooksReconciliationPending: !latest.externalPayment.quickbooksBillPaymentIds.length
8964
+ });
8965
+ return;
8966
+ }
8967
+ } catch {}
8968
+ throw new Error("External payment result could not be confirmed. Inspect the bill, then retry the same reference/date/account; do not pay again. Any uploaded proof is retained.", { cause: error });
8969
+ }
8970
+ };
8839
8971
  async function patchBillPayment(opts) {
8840
8972
  const input = { id: opts.id };
8841
8973
  if (opts.paymentTrackingUrl !== undefined)
@@ -10602,6 +10734,7 @@ var mutationPrefixes = [
10602
10734
  "patch-",
10603
10735
  "reactivate-",
10604
10736
  "reconcile-",
10737
+ "record-",
10605
10738
  "reject-",
10606
10739
  "remove-",
10607
10740
  "replace-",
@@ -10642,6 +10775,7 @@ var emailTargets = new Set([
10642
10775
  "update-supplier"
10643
10776
  ]);
10644
10777
  var externalWriteTargets = new Set([
10778
+ "record-external-bill-payment",
10645
10779
  "approve-bill",
10646
10780
  "approve-customer-invoice",
10647
10781
  "approve-supplier",
@@ -10687,6 +10821,7 @@ var externalWriteTargets = new Set([
10687
10821
  "void-customer-invoice"
10688
10822
  ]);
10689
10823
  var financialWriteTargets = new Set([
10824
+ "record-external-bill-payment",
10690
10825
  "import-qbo-customer-invoice",
10691
10826
  "link-bill-quotations",
10692
10827
  "approve-bill",
@@ -10771,8 +10906,12 @@ var summaryFor = (legacyTarget) => {
10771
10906
  if (legacyTarget === "create-bill") {
10772
10907
  return "Create a bill or claim with an approved supplier. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.";
10773
10908
  }
10909
+ if (legacyTarget === "record-external-bill-payment")
10910
+ return "Record an already-made payment on an APPROVED bill or claim, with no email and no money transfer. Requires <billId> --paymentReference --paymentDate YYYY-MM-DD --paymentAccount <QBO account ID> [--paymentProof receipt.pdf]. Reuses settled QBO payment links or queues one dated payment from the selected account. Exact retries are safe; do not pay again while reconciliation is pending.";
10911
+ if (legacyTarget === "list-external-payment-accounts")
10912
+ return "List active QuickBooks bank and credit-card account IDs for recording external payments (admin only).";
10774
10913
  if (legacyTarget === "update-bill") {
10775
- return "Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.";
10914
+ return "Update a bill or claim with a partial payload including id; omitted fields are preserved. Comment-only updates are allowed on approved/paid bills without altering financial data or QuickBooks. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.";
10776
10915
  }
10777
10916
  if (legacyTarget === "validate-bill-selection") {
10778
10917
  return "Validate the approved supplier and selected bill line items.";
@@ -10932,6 +11071,16 @@ var registry2 = [
10932
11071
  ]),
10933
11072
  legacyCommand("delete-bill", ["bill", "delete"]),
10934
11073
  legacyCommand("update-bill-status", ["bill", "status", "update"]),
11074
+ legacyCommand("record-external-bill-payment", [
11075
+ "bill",
11076
+ "payment",
11077
+ "record-external"
11078
+ ]),
11079
+ legacyCommand("list-external-payment-accounts", [
11080
+ "bill",
11081
+ "payment",
11082
+ "accounts"
11083
+ ]),
10935
11084
  legacyCommand("patch-bill-payment", ["bill", "payment", "patch"]),
10936
11085
  legacyCommand("patch-bill-invoice-number", [
10937
11086
  "bill",
@@ -12508,6 +12657,27 @@ Waiting for approval\u2026
12508
12657
  });
12509
12658
  break;
12510
12659
  }
12660
+ case "list-external-payment-accounts": {
12661
+ await listExternalBillPaymentAccounts();
12662
+ break;
12663
+ }
12664
+ case "record-external-bill-payment": {
12665
+ const id = positional[0];
12666
+ const paymentReference = getFlag(flags, "paymentReference");
12667
+ const paymentDate = getFlag(flags, "paymentDate");
12668
+ const paymentAccount = getFlag(flags, "paymentAccount");
12669
+ const paymentProofPath = getFlag(flags, "paymentProof");
12670
+ if (!id || !paymentReference || !paymentDate || !paymentAccount)
12671
+ throw new Error("record-external-bill-payment requires <billId> --paymentReference --paymentDate YYYY-MM-DD --paymentAccount <QBO account ID> [--paymentProof receipt.pdf]");
12672
+ await recordExternalBillPayment({
12673
+ id,
12674
+ paymentReference,
12675
+ paymentDate,
12676
+ paymentAccount,
12677
+ paymentProofPath
12678
+ });
12679
+ break;
12680
+ }
12511
12681
  case "patch-bill-payment": {
12512
12682
  const id = positional[0];
12513
12683
  if (!id)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/bb",
3
- "version": "2.42.0",
3
+ "version": "2.43.0",
4
4
  "description": "Budget Builder CLI for AI agents — manage budgets, bills, claims, quotations, and customer invoices with explicit workflow previews for sensitive changes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",