@go-labs-sg/bb 2.18.0 → 2.20.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 +6 -0
- package/command-manifest.json +4 -4
- package/command-reference.md +4 -4
- package/dist/commands.js +48 -59
- package/dist/index.js +5 -5
- package/dist/registry/index.js +12 -0
- package/package.json +1 -1
- package/role-aware-agent-guide.md +2 -0
package/README.md
CHANGED
|
@@ -181,6 +181,10 @@ New sources are registered against an existing Typeform form ID and always start
|
|
|
181
181
|
with processing disabled in `COLLECT_ONLY`; creating a source does not call any
|
|
182
182
|
external provider.
|
|
183
183
|
|
|
184
|
+
Deal-lifecycle retries require Slack and Asana to succeed. Google Calendar is
|
|
185
|
+
attempted afterward; a Calendar failure is logged without failing the overall
|
|
186
|
+
lifecycle operation.
|
|
187
|
+
|
|
184
188
|
### Mutation safety
|
|
185
189
|
|
|
186
190
|
While an estimate email is `QUEUED`, `PROCESSING`, or awaiting provider
|
|
@@ -233,6 +237,8 @@ The table below is a behavior index keyed by the compatibility dispatcher's lega
|
|
|
233
237
|
|
|
234
238
|
Project and budget query payloads keep the Asana identities explicit: `asanaTaskId` is the Deals task GID, while `asanaProjectGid` and `asanaProjectUrl` identify the operational project created for Won delivery work.
|
|
235
239
|
|
|
240
|
+
Bill, claim, and quotation mutations accept only suppliers whose approval status is `APPROVED`. An active supplier that is `PENDING_APPROVAL`, `REJECTED`, or `SUPERSEDED` must be approved before it can be used.
|
|
241
|
+
|
|
236
242
|
| Area | Legacy handler labels (reference only; non-exhaustive) |
|
|
237
243
|
| --- | --- |
|
|
238
244
|
| **Budgets** | `list-budgets` (full payload by default; `--summary` or `--includeDetails false` for slim list), `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `rename-budget-version`, `restore-budget-version`, `update-budget-status` (`ESTIMATE_ACCEPTED` requires win proof, auto-marks `PITCH` or `LOST` projects `WON`, and auto-creates an Asana Event Ops section from Prompt 5 tasks when enabled; Prompt 5 skips quantity-zero items, combines matching non-GO-internal suppliers, keeps each GO internal item separate, removes generated work that repeats another line item or group, and schedules applicable subtasks relative to the project start date; `--projectStatusOnCommercialRejection PITCH\|LOST` is required when rejecting the only accepted/closed budget on a commercial project), `mark-budget-won` (`<budgetId>` + proof file path; `PITCH` or `LOST` projects become `WON` automatically), `create-budget` / `update-budget` (`--payload`; cloned unavailable lines require `unavailableItemReviewAcknowledged: true`), `delete-budget`, `create-budget-approval` (also sends approval request emails), `create-estimate`, `send-estimate-to-contact-person` (same contact-person email workflow as web), `add-budget-items`, `update-budget-item`, `replace-budget-item`, `approve-unavailable-item-exception` (Lead/Admin), `remove-budget-item`, `reorder-budget-items`, `update-budget-item-supplier`, `mark-budget-items-not-utilized`, `restore-budget-item`, `create-placeholder-bill` (admin recovery), `create-budget-category`, `update-budget-category`, `delete-budget-category`, `update-budget-commission`, `delete-budget-commission`, `update-budget-discount`, `delete-budget-discount` (`--payload` where noted), `upload-budget-attachment` (`<budgetId>` + local file path; uses `attachment.requestBudgetAttachmentUpload` + PUT + `attachment.confirmBudgetAttachment`) |
|
package/command-manifest.json
CHANGED
|
@@ -1764,7 +1764,7 @@
|
|
|
1764
1764
|
{
|
|
1765
1765
|
"path": ["bill", "create"],
|
|
1766
1766
|
"legacyAliases": ["create-bill", "create_bill"],
|
|
1767
|
-
"summary": "Create
|
|
1767
|
+
"summary": "Create a bill or claim with an approved supplier.",
|
|
1768
1768
|
"globalOptions": [
|
|
1769
1769
|
{
|
|
1770
1770
|
"name": "--help",
|
|
@@ -1810,7 +1810,7 @@
|
|
|
1810
1810
|
{
|
|
1811
1811
|
"path": ["bill", "selection", "validate"],
|
|
1812
1812
|
"legacyAliases": ["validate-bill-selection", "validate_bill_selection"],
|
|
1813
|
-
"summary": "Validate
|
|
1813
|
+
"summary": "Validate the approved supplier and selected bill line items.",
|
|
1814
1814
|
"globalOptions": [
|
|
1815
1815
|
{
|
|
1816
1816
|
"name": "--help",
|
|
@@ -2658,7 +2658,7 @@
|
|
|
2658
2658
|
{
|
|
2659
2659
|
"path": ["quotation", "create"],
|
|
2660
2660
|
"legacyAliases": ["create-quotation", "create_quotation"],
|
|
2661
|
-
"summary": "Create
|
|
2661
|
+
"summary": "Create a quotation with an approved supplier.",
|
|
2662
2662
|
"globalOptions": [
|
|
2663
2663
|
{
|
|
2664
2664
|
"name": "--help",
|
|
@@ -2704,7 +2704,7 @@
|
|
|
2704
2704
|
{
|
|
2705
2705
|
"path": ["quotation", "update"],
|
|
2706
2706
|
"legacyAliases": ["update-quotation", "update_quotation"],
|
|
2707
|
-
"summary": "Update
|
|
2707
|
+
"summary": "Update a quotation with an approved supplier.",
|
|
2708
2708
|
"globalOptions": [
|
|
2709
2709
|
{
|
|
2710
2710
|
"name": "--help",
|
package/command-reference.md
CHANGED
|
@@ -322,7 +322,7 @@ Effects: state-change, email.
|
|
|
322
322
|
|
|
323
323
|
## `bb bill create`
|
|
324
324
|
|
|
325
|
-
Create
|
|
325
|
+
Create a bill or claim with an approved supplier.
|
|
326
326
|
|
|
327
327
|
Legacy aliases: `create-bill`, `create_bill`.
|
|
328
328
|
|
|
@@ -330,7 +330,7 @@ Effects: state-change, email, external-write, financial-write.
|
|
|
330
330
|
|
|
331
331
|
## `bb bill selection validate`
|
|
332
332
|
|
|
333
|
-
Validate
|
|
333
|
+
Validate the approved supplier and selected bill line items.
|
|
334
334
|
|
|
335
335
|
Legacy aliases: `validate-bill-selection`, `validate_bill_selection`.
|
|
336
336
|
|
|
@@ -474,7 +474,7 @@ Effects: state-change, delete.
|
|
|
474
474
|
|
|
475
475
|
## `bb quotation create`
|
|
476
476
|
|
|
477
|
-
Create
|
|
477
|
+
Create a quotation with an approved supplier.
|
|
478
478
|
|
|
479
479
|
Legacy aliases: `create-quotation`, `create_quotation`.
|
|
480
480
|
|
|
@@ -482,7 +482,7 @@ Effects: state-change.
|
|
|
482
482
|
|
|
483
483
|
## `bb quotation update`
|
|
484
484
|
|
|
485
|
-
Update
|
|
485
|
+
Update a quotation with an approved supplier.
|
|
486
486
|
|
|
487
487
|
Legacy aliases: `update-quotation`, `update_quotation`.
|
|
488
488
|
|
package/dist/commands.js
CHANGED
|
@@ -35,16 +35,35 @@ const getAllNotifications = async () => {
|
|
|
35
35
|
} while (cursor);
|
|
36
36
|
return notifications;
|
|
37
37
|
};
|
|
38
|
-
const createApprovalEmailDeliveryReport = (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
const createApprovalEmailDeliveryReport = (delivery) => {
|
|
39
|
+
if (!delivery) {
|
|
40
|
+
return {
|
|
41
|
+
queued: false,
|
|
42
|
+
sent: false,
|
|
43
|
+
count: 0,
|
|
44
|
+
queuedCount: 0,
|
|
45
|
+
sentCount: 0,
|
|
46
|
+
skippedCount: 0,
|
|
47
|
+
error: "No approval email delivery was queued",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const { queuedCount, sentCount, skippedCount, message, operationIds } = delivery;
|
|
51
|
+
return {
|
|
52
|
+
queued: queuedCount > 0,
|
|
53
|
+
sent: false,
|
|
54
|
+
count: queuedCount,
|
|
55
|
+
queuedCount,
|
|
56
|
+
sentCount,
|
|
57
|
+
skippedCount,
|
|
58
|
+
operationIds,
|
|
59
|
+
message,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
45
62
|
const createApprovalEmailDeliveryFailure = (error) => ({
|
|
63
|
+
queued: false,
|
|
46
64
|
sent: false,
|
|
47
65
|
count: 0,
|
|
66
|
+
queuedCount: 0,
|
|
48
67
|
sentCount: 0,
|
|
49
68
|
skippedCount: 0,
|
|
50
69
|
error,
|
|
@@ -533,16 +552,11 @@ export async function approveBill(billId) {
|
|
|
533
552
|
}
|
|
534
553
|
let email;
|
|
535
554
|
try {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
});
|
|
539
|
-
email = { sent: true };
|
|
555
|
+
const delivery = result.emailDelivery;
|
|
556
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
540
557
|
}
|
|
541
558
|
catch (error) {
|
|
542
|
-
email =
|
|
543
|
-
sent: false,
|
|
544
|
-
error: error instanceof Error ? error.message : String(error),
|
|
545
|
-
};
|
|
559
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
546
560
|
}
|
|
547
561
|
out({
|
|
548
562
|
...result,
|
|
@@ -629,7 +643,7 @@ export async function createBillApproval(billId) {
|
|
|
629
643
|
}
|
|
630
644
|
else {
|
|
631
645
|
try {
|
|
632
|
-
const delivery =
|
|
646
|
+
const delivery = result.emailDelivery;
|
|
633
647
|
email = createApprovalEmailDeliveryReport(delivery);
|
|
634
648
|
}
|
|
635
649
|
catch (error) {
|
|
@@ -1078,7 +1092,7 @@ export async function createBudgetApproval(budgetId, preflight) {
|
|
|
1078
1092
|
}
|
|
1079
1093
|
else {
|
|
1080
1094
|
try {
|
|
1081
|
-
const delivery =
|
|
1095
|
+
const delivery = result.emailDelivery;
|
|
1082
1096
|
email = createApprovalEmailDeliveryReport(delivery);
|
|
1083
1097
|
}
|
|
1084
1098
|
catch (error) {
|
|
@@ -1488,7 +1502,7 @@ export async function createSupplierFromPayload(raw) {
|
|
|
1488
1502
|
}
|
|
1489
1503
|
else {
|
|
1490
1504
|
try {
|
|
1491
|
-
const delivery =
|
|
1505
|
+
const delivery = approval.emailDelivery;
|
|
1492
1506
|
emailResult = createApprovalEmailDeliveryReport(delivery);
|
|
1493
1507
|
}
|
|
1494
1508
|
catch (error) {
|
|
@@ -1534,7 +1548,7 @@ export async function updateSupplierFromPayload(raw) {
|
|
|
1534
1548
|
}
|
|
1535
1549
|
else {
|
|
1536
1550
|
try {
|
|
1537
|
-
const delivery =
|
|
1551
|
+
const delivery = approval.emailDelivery;
|
|
1538
1552
|
approvalResult = {
|
|
1539
1553
|
requested: true,
|
|
1540
1554
|
approvalCount: approvalIds.length,
|
|
@@ -1614,16 +1628,11 @@ export async function approveBudget(budgetId) {
|
|
|
1614
1628
|
});
|
|
1615
1629
|
let email;
|
|
1616
1630
|
try {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
});
|
|
1620
|
-
email = { sent: true };
|
|
1631
|
+
const delivery = result.emailDelivery;
|
|
1632
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
1621
1633
|
}
|
|
1622
1634
|
catch (error) {
|
|
1623
|
-
email =
|
|
1624
|
-
sent: false,
|
|
1625
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1626
|
-
};
|
|
1635
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
1627
1636
|
}
|
|
1628
1637
|
out({
|
|
1629
1638
|
...result,
|
|
@@ -1653,16 +1662,11 @@ export async function rejectBudget(budgetId, reason) {
|
|
|
1653
1662
|
});
|
|
1654
1663
|
let email;
|
|
1655
1664
|
try {
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
});
|
|
1659
|
-
email = { sent: true };
|
|
1665
|
+
const delivery = result.emailDelivery;
|
|
1666
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
1660
1667
|
}
|
|
1661
1668
|
catch (error) {
|
|
1662
|
-
email =
|
|
1663
|
-
sent: false,
|
|
1664
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1665
|
-
};
|
|
1669
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
1666
1670
|
}
|
|
1667
1671
|
out({
|
|
1668
1672
|
...result,
|
|
@@ -1751,16 +1755,11 @@ export async function approveSupplier(supplierId) {
|
|
|
1751
1755
|
});
|
|
1752
1756
|
let email;
|
|
1753
1757
|
try {
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
});
|
|
1757
|
-
email = { sent: true };
|
|
1758
|
+
const delivery = result.emailDelivery;
|
|
1759
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
1758
1760
|
}
|
|
1759
1761
|
catch (error) {
|
|
1760
|
-
email =
|
|
1761
|
-
sent: false,
|
|
1762
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1763
|
-
};
|
|
1762
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
1764
1763
|
}
|
|
1765
1764
|
out({
|
|
1766
1765
|
...result,
|
|
@@ -1789,16 +1788,11 @@ export async function rejectSupplier(supplierId, reason) {
|
|
|
1789
1788
|
});
|
|
1790
1789
|
let email;
|
|
1791
1790
|
try {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
});
|
|
1795
|
-
email = { sent: true };
|
|
1791
|
+
const delivery = result.emailDelivery;
|
|
1792
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
1796
1793
|
}
|
|
1797
1794
|
catch (error) {
|
|
1798
|
-
email =
|
|
1799
|
-
sent: false,
|
|
1800
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1801
|
-
};
|
|
1795
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
1802
1796
|
}
|
|
1803
1797
|
out({
|
|
1804
1798
|
...result,
|
|
@@ -1831,16 +1825,11 @@ export async function rejectBill(billId, reason) {
|
|
|
1831
1825
|
}
|
|
1832
1826
|
let email;
|
|
1833
1827
|
try {
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
});
|
|
1837
|
-
email = { sent: true };
|
|
1828
|
+
const delivery = result.emailDelivery;
|
|
1829
|
+
email = createApprovalEmailDeliveryReport(delivery);
|
|
1838
1830
|
}
|
|
1839
1831
|
catch (error) {
|
|
1840
|
-
email =
|
|
1841
|
-
sent: false,
|
|
1842
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1843
|
-
};
|
|
1832
|
+
email = createApprovalEmailDeliveryFailure(error instanceof Error ? error.message : String(error));
|
|
1844
1833
|
}
|
|
1845
1834
|
out({
|
|
1846
1835
|
...result,
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,7 @@ Budgets
|
|
|
176
176
|
create-budget --payload '<json>' (budget.createBudget; when sourceBudgetId contains unavailable items, set unavailableItemReviewAcknowledged=true after explicit review)
|
|
177
177
|
update-budget --payload '<json>' (budget.updateBudget; must include id)
|
|
178
178
|
delete-budget <budgetId>
|
|
179
|
-
create-budget-approval <budgetId> (also
|
|
179
|
+
create-budget-approval <budgetId> (also queues approval request emails)
|
|
180
180
|
create-estimate <budgetId> (creates QuickBooks estimate; supports preflight updates)
|
|
181
181
|
send-estimate-to-contact-person --payload '<json>' Send using the web contact-person workflow; payload includes recipient fields plus HTML content/signature.
|
|
182
182
|
add-budget-items --budgetId --items '[{"itemId":"…","quantity":1,"markup":30},…]'
|
|
@@ -203,9 +203,9 @@ Bills
|
|
|
203
203
|
list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
|
|
204
204
|
isClaimable differentiates the shared bill/claim records: false = bill, true = claim.
|
|
205
205
|
Non-legacy supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage linked to every selected line item, independent of the bill supplier, including already-paid bills.
|
|
206
|
-
create-bill-approval <billId> (also
|
|
207
|
-
create-bill --payload '<json>' (bill.create; payload.isClaimable false = bill, true = claim)
|
|
208
|
-
validate-bill-selection --payload '<json>'
|
|
206
|
+
create-bill-approval <billId> (also queues approval request emails)
|
|
207
|
+
create-bill --payload '<json>' (bill.create; payload.isClaimable false = bill, true = claim; supplier must be APPROVED)
|
|
208
|
+
validate-bill-selection --payload '<json>' Require an APPROVED supplier, then verify approved quotation links for each budgetItemId; alreadyPaid never bypasses quotation checks.
|
|
209
209
|
stage-bill-attachment <projectId> <filePath...> [--file <path>] [--files <csv>] Upload files before create-bill; returns attachment JSON for attachments/paymentProofAttachments
|
|
210
210
|
cleanup-staged-bill-attachments <projectId> --keys <csv> Delete unattached staged bill/claim uploads.
|
|
211
211
|
update-bill --payload '<json>' (bill.update; must include id)
|
|
@@ -251,7 +251,7 @@ Customer invoices
|
|
|
251
251
|
void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
|
|
252
252
|
approve-customer-invoice <batchId> Admin approval; rejects voided invoices, notifies the creator, and closes the estimate at cumulative 100% coverage.
|
|
253
253
|
reject-customer-invoice <batchId> --reason <text> Admin rejection; voids QBO invoices and notifies the creator.
|
|
254
|
-
send-customer-invoice-to-contact-person --payload '<json>'
|
|
254
|
+
send-customer-invoice-to-contact-person --payload '<json>' Queues same composer workflow as web; requires invoiceId, to, cc, replyTo, subject, HTML content, and HTML signature.
|
|
255
255
|
download-customer-invoice-pdf <invoiceId> [--output <path>]
|
|
256
256
|
sync-customer-invoice <invoiceId> Refresh local status and balance from QuickBooks; paid invoices become PAID, reversals restore delivery/approval, and QBO voids can reopen an estimate.
|
|
257
257
|
|
package/dist/registry/index.js
CHANGED
|
@@ -194,6 +194,18 @@ const titleCase = (value) => value
|
|
|
194
194
|
const summaryFor = (legacyTarget) => {
|
|
195
195
|
if (legacyTarget === "whoami")
|
|
196
196
|
return "Show the active API-key identity.";
|
|
197
|
+
if (legacyTarget === "create-bill") {
|
|
198
|
+
return "Create a bill or claim with an approved supplier.";
|
|
199
|
+
}
|
|
200
|
+
if (legacyTarget === "validate-bill-selection") {
|
|
201
|
+
return "Validate the approved supplier and selected bill line items.";
|
|
202
|
+
}
|
|
203
|
+
if (legacyTarget === "create-quotation") {
|
|
204
|
+
return "Create a quotation with an approved supplier.";
|
|
205
|
+
}
|
|
206
|
+
if (legacyTarget === "update-quotation") {
|
|
207
|
+
return "Update a quotation with an approved supplier.";
|
|
208
|
+
}
|
|
197
209
|
if (legacyTarget === "download-customer-invoice-pdf") {
|
|
198
210
|
return "Download a customer invoice PDF when permitted.";
|
|
199
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.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",
|
|
@@ -144,6 +144,8 @@ Do not retry with lower-level upload/request procedures, direct tRPC paths, a di
|
|
|
144
144
|
|
|
145
145
|
On `BAD_REQUEST` or `CONFLICT`, re-read the resource before deciding whether to retry. These responses commonly indicate an invalid state transition, stale data, missing evidence, or an integration operation already in progress.
|
|
146
146
|
|
|
147
|
+
Bill, claim, and quotation mutations accept only suppliers whose approval status is `APPROVED`. An active supplier that is `PENDING_APPROVAL`, `REJECTED`, or `SUPERSEDED` must not be submitted or retried until it is approved.
|
|
148
|
+
|
|
147
149
|
## Safe examples
|
|
148
150
|
|
|
149
151
|
Base user preparing a claim:
|