@go-labs-sg/bb 2.43.0 → 2.45.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 +27 -1
- package/command-manifest.json +21 -5
- package/command-reference.md +5 -5
- package/dist/index.js +100 -18
- package/package.json +1 -1
- package/role-aware-agent-guide.md +3 -2
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ See the [supplier review and rollout policy](../api/docs/supplier-edit-review.md
|
|
|
63
63
|
|
|
64
64
|
## Runtime and installation
|
|
65
65
|
|
|
66
|
-
The CLI requires [Bun](https://bun.com/docs/installation) 1.4.0 or newer and a Budget Builder account with any active user role.
|
|
66
|
+
The same CLI package supports macOS and Windows. It requires [Bun](https://bun.com/docs/installation) 1.4.0 or newer and a Budget Builder account with any active user role. Windows users can run the commands below in native PowerShell; Bash and `export PATH=...` are not required.
|
|
67
67
|
|
|
68
68
|
Check whether a compatible Bun version is already available:
|
|
69
69
|
|
|
@@ -106,6 +106,25 @@ Or run without installing:
|
|
|
106
106
|
bunx @go-labs-sg/bb <command>
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
### Historical budget reads (Admin only)
|
|
110
|
+
|
|
111
|
+
Budget reads are active-only by default. The API enforces the historical-read permission; the CLI does not grant access based on the flag alone.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Active budgets only (default)
|
|
115
|
+
bb list-budgets
|
|
116
|
+
bb get-budget <budget-id>
|
|
117
|
+
|
|
118
|
+
# Include inactive budgets; each form requires an ADMIN identity
|
|
119
|
+
bb list-budgets --active false
|
|
120
|
+
bb list-budgets --active all
|
|
121
|
+
bb get-budget <budget-id> --includeInactive
|
|
122
|
+
bb get-budget-items <budget-id> --includeInactive
|
|
123
|
+
bb get-budget-details <budget-id> --includeInactive
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--active false` lists inactive budgets only. `--active all` lists active and inactive budgets. `--includeInactive` allows the three budget/detail/item commands to read inactive history. If the authenticated identity is not an Admin, the API rejects the historical request before reading budget data. For `get-budget`, the CLI requests the budget and its line items concurrently; each API read independently enforces the same active-only default and Admin-only historical opt-in.
|
|
127
|
+
|
|
109
128
|
## Authentication
|
|
110
129
|
|
|
111
130
|
Interactive users authorize the CLI through Budget Builder's normal Google-backed
|
|
@@ -123,6 +142,11 @@ credentials through `Bun.secrets`. It never stores Google credentials. Use
|
|
|
123
142
|
`bb auth logout` to revoke the interactive session. For SSH or containers, pass
|
|
124
143
|
`--no-browser` and open the printed URL on another device.
|
|
125
144
|
|
|
145
|
+
Credentials use macOS Keychain or Windows Credential Manager, not plaintext files.
|
|
146
|
+
The per-user lock directory contains only random ownership markers used to serialize
|
|
147
|
+
login, refresh, and logout. macOS/Linux enforce owner-only POSIX permissions there;
|
|
148
|
+
Windows uses its native filesystem access controls rather than POSIX mode checks.
|
|
149
|
+
|
|
126
150
|
Budget Builder API keys remain supported for MCP, CI, and non-interactive
|
|
127
151
|
automation. When `BB_API_KEY` is present it takes precedence over the interactive
|
|
128
152
|
session for that process. Create and manage automation keys under **API Keys** or
|
|
@@ -484,6 +508,8 @@ Project and budget query payloads keep the Asana identities explicit: `asanaTask
|
|
|
484
508
|
|
|
485
509
|
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.
|
|
486
510
|
|
|
511
|
+
Admin/Lead users can run `bb supplier approve <supplierId>` to directly re-approve a `REJECTED` supplier even when no pending approval request exists. This appends a new approval record and activity entry with the current responder/time, retains earlier approval and rejection history unchanged, and synchronizes QuickBooks; it does not send a requester reply email. Non-interactive direct re-approval requires `--allow-state-change --allow-external-write`, without `--allow-email`. An archived supplier remains archived and also needs Admin reactivation (`bb supplier reactivate --ids <supplierId>`) before new use. Reactivating alone does not change approval status. For a pending supplier, the command still resolves the current user's assigned request and queues the existing reply email.
|
|
512
|
+
|
|
487
513
|
| Area | Legacy handler labels (reference only; non-exhaustive) |
|
|
488
514
|
| --- | --- |
|
|
489
515
|
| **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
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
{
|
|
200
200
|
"path": ["budget", "list"],
|
|
201
201
|
"legacyAliases": ["list-budgets", "list_budgets"],
|
|
202
|
-
"summary": "List budgets.",
|
|
202
|
+
"summary": "List budgets. Omit --active for active budgets only; use --active false or --active all for Admin-only historical reads.",
|
|
203
203
|
"globalOptions": [
|
|
204
204
|
{
|
|
205
205
|
"name": "--help",
|
|
@@ -240,6 +240,10 @@
|
|
|
240
240
|
{
|
|
241
241
|
"name": "--allow-financial-write",
|
|
242
242
|
"description": "Allow financial-record changes in non-interactive use."
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"name": "--active <true|false|all>",
|
|
246
|
+
"description": "Omit for active budgets only; false or all requires Admin access."
|
|
243
247
|
}
|
|
244
248
|
],
|
|
245
249
|
"argumentMode": "legacy-passthrough",
|
|
@@ -249,7 +253,7 @@
|
|
|
249
253
|
{
|
|
250
254
|
"path": ["budget", "get"],
|
|
251
255
|
"legacyAliases": ["get-budget", "get_budget"],
|
|
252
|
-
"summary": "Get budget.",
|
|
256
|
+
"summary": "Get a budget by ID. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.",
|
|
253
257
|
"globalOptions": [
|
|
254
258
|
{
|
|
255
259
|
"name": "--help",
|
|
@@ -290,6 +294,10 @@
|
|
|
290
294
|
{
|
|
291
295
|
"name": "--allow-financial-write",
|
|
292
296
|
"description": "Allow financial-record changes in non-interactive use."
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"name": "--includeInactive",
|
|
300
|
+
"description": "Include inactive historical budgets; Admin only."
|
|
293
301
|
}
|
|
294
302
|
],
|
|
295
303
|
"argumentMode": "legacy-passthrough",
|
|
@@ -399,7 +407,7 @@
|
|
|
399
407
|
{
|
|
400
408
|
"path": ["budget", "item", "list"],
|
|
401
409
|
"legacyAliases": ["get-budget-items", "get_budget_items"],
|
|
402
|
-
"summary": "Get
|
|
410
|
+
"summary": "Get line items for a budget. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.",
|
|
403
411
|
"globalOptions": [
|
|
404
412
|
{
|
|
405
413
|
"name": "--help",
|
|
@@ -440,6 +448,10 @@
|
|
|
440
448
|
{
|
|
441
449
|
"name": "--allow-financial-write",
|
|
442
450
|
"description": "Allow financial-record changes in non-interactive use."
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"name": "--includeInactive",
|
|
454
|
+
"description": "Include inactive historical budgets; Admin only."
|
|
443
455
|
}
|
|
444
456
|
],
|
|
445
457
|
"argumentMode": "legacy-passthrough",
|
|
@@ -449,7 +461,7 @@
|
|
|
449
461
|
{
|
|
450
462
|
"path": ["budget", "detail", "get"],
|
|
451
463
|
"legacyAliases": ["get-budget-details", "get_budget_details"],
|
|
452
|
-
"summary": "Get budget details.",
|
|
464
|
+
"summary": "Get budget details. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.",
|
|
453
465
|
"globalOptions": [
|
|
454
466
|
{
|
|
455
467
|
"name": "--help",
|
|
@@ -490,6 +502,10 @@
|
|
|
490
502
|
{
|
|
491
503
|
"name": "--allow-financial-write",
|
|
492
504
|
"description": "Allow financial-record changes in non-interactive use."
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"name": "--includeInactive",
|
|
508
|
+
"description": "Include inactive historical budgets; Admin only."
|
|
493
509
|
}
|
|
494
510
|
],
|
|
495
511
|
"argumentMode": "legacy-passthrough",
|
|
@@ -4779,7 +4795,7 @@
|
|
|
4779
4795
|
{
|
|
4780
4796
|
"path": ["supplier", "approve"],
|
|
4781
4797
|
"legacyAliases": ["approve-supplier", "approve_supplier"],
|
|
4782
|
-
"summary": "Approve
|
|
4798
|
+
"summary": "Approve a pending supplier request and send a requester reply email, or directly re-approve a rejected supplier as Admin/Lead without a pending request or email. QuickBooks is synchronized; archived suppliers remain archived. Argument: <supplierId>.",
|
|
4783
4799
|
"globalOptions": [
|
|
4784
4800
|
{
|
|
4785
4801
|
"name": "--help",
|
package/command-reference.md
CHANGED
|
@@ -51,7 +51,7 @@ Effects: none.
|
|
|
51
51
|
|
|
52
52
|
## `bb budget list`
|
|
53
53
|
|
|
54
|
-
List budgets.
|
|
54
|
+
List budgets. Omit --active for active budgets only; use --active false or --active all for Admin-only historical reads.
|
|
55
55
|
|
|
56
56
|
Legacy aliases: `list-budgets`, `list_budgets`.
|
|
57
57
|
|
|
@@ -59,7 +59,7 @@ Effects: none.
|
|
|
59
59
|
|
|
60
60
|
## `bb budget get`
|
|
61
61
|
|
|
62
|
-
Get budget.
|
|
62
|
+
Get a budget by ID. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.
|
|
63
63
|
|
|
64
64
|
Legacy aliases: `get-budget`, `get_budget`.
|
|
65
65
|
|
|
@@ -83,7 +83,7 @@ Effects: state-change, external-write, financial-write.
|
|
|
83
83
|
|
|
84
84
|
## `bb budget item list`
|
|
85
85
|
|
|
86
|
-
Get
|
|
86
|
+
Get line items for a budget. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.
|
|
87
87
|
|
|
88
88
|
Legacy aliases: `get-budget-items`, `get_budget_items`.
|
|
89
89
|
|
|
@@ -91,7 +91,7 @@ Effects: none.
|
|
|
91
91
|
|
|
92
92
|
## `bb budget detail get`
|
|
93
93
|
|
|
94
|
-
Get budget details.
|
|
94
|
+
Get budget details. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.
|
|
95
95
|
|
|
96
96
|
Legacy aliases: `get-budget-details`, `get_budget_details`.
|
|
97
97
|
|
|
@@ -833,7 +833,7 @@ Effects: state-change, email.
|
|
|
833
833
|
|
|
834
834
|
## `bb supplier approve`
|
|
835
835
|
|
|
836
|
-
Approve
|
|
836
|
+
Approve a pending supplier request and send a requester reply email, or directly re-approve a rejected supplier as Admin/Lead without a pending request or email. QuickBooks is synchronized; archived suppliers remain archived. Argument: <supplierId>.
|
|
837
837
|
|
|
838
838
|
Legacy aliases: `approve-supplier`, `approve_supplier`.
|
|
839
839
|
|
package/dist/index.js
CHANGED
|
@@ -6071,7 +6071,7 @@ var ensurePrivateCredentialLockDirectory = async () => {
|
|
|
6071
6071
|
return directoryStat;
|
|
6072
6072
|
};
|
|
6073
6073
|
const directoryStat = await inspectDirectory();
|
|
6074
|
-
if ((directoryStat.mode & 63) !== 0) {
|
|
6074
|
+
if (process.platform !== "win32" && (directoryStat.mode & 63) !== 0) {
|
|
6075
6075
|
await chmod(directory, 448);
|
|
6076
6076
|
const securedDirectoryStat = await inspectDirectory();
|
|
6077
6077
|
if ((securedDirectoryStat.mode & 63) !== 0) {
|
|
@@ -8504,7 +8504,8 @@ var assertSensitiveWorkflowConfirmed = async (input) => {
|
|
|
8504
8504
|
await confirmCurrentCommand({
|
|
8505
8505
|
action: input.action,
|
|
8506
8506
|
target: input.entity,
|
|
8507
|
-
details: input.details
|
|
8507
|
+
details: input.details,
|
|
8508
|
+
effects: input.effects
|
|
8508
8509
|
});
|
|
8509
8510
|
return;
|
|
8510
8511
|
}
|
|
@@ -8604,6 +8605,7 @@ async function listBudgets(opts) {
|
|
|
8604
8605
|
createdBy: opts.createdBy,
|
|
8605
8606
|
dateFrom: opts.dateFrom,
|
|
8606
8607
|
dateTo: opts.dateTo,
|
|
8608
|
+
active: opts.active,
|
|
8607
8609
|
sortBy: opts.sortBy,
|
|
8608
8610
|
sortDir: opts.sortDir,
|
|
8609
8611
|
page: opts.page ?? 1,
|
|
@@ -8613,10 +8615,16 @@ async function listBudgets(opts) {
|
|
|
8613
8615
|
const result = await api2.budget.getAllBudgets.query(input);
|
|
8614
8616
|
out(result);
|
|
8615
8617
|
}
|
|
8616
|
-
async function getBudget(id) {
|
|
8618
|
+
async function getBudget(id, opts) {
|
|
8617
8619
|
const [budget, items] = await Promise.all([
|
|
8618
|
-
api2.budget.getBudget.query({
|
|
8619
|
-
|
|
8620
|
+
api2.budget.getBudget.query({
|
|
8621
|
+
id,
|
|
8622
|
+
includeInactive: opts?.includeInactive ?? false
|
|
8623
|
+
}),
|
|
8624
|
+
api2.budget.getBudgetItems.query({
|
|
8625
|
+
budgetId: id,
|
|
8626
|
+
includeInactive: opts?.includeInactive ?? false
|
|
8627
|
+
})
|
|
8620
8628
|
]);
|
|
8621
8629
|
out({ budget, items });
|
|
8622
8630
|
}
|
|
@@ -9223,8 +9231,11 @@ async function getBillDetails(id) {
|
|
|
9223
9231
|
const result = await api2.bill.getById.query({ id });
|
|
9224
9232
|
out(result);
|
|
9225
9233
|
}
|
|
9226
|
-
async function getBudgetItemsOnly(budgetId) {
|
|
9227
|
-
const items = await api2.budget.getBudgetItems.query({
|
|
9234
|
+
async function getBudgetItemsOnly(budgetId, opts) {
|
|
9235
|
+
const items = await api2.budget.getBudgetItems.query({
|
|
9236
|
+
budgetId,
|
|
9237
|
+
includeInactive: opts?.includeInactive ?? false
|
|
9238
|
+
});
|
|
9228
9239
|
out(items);
|
|
9229
9240
|
}
|
|
9230
9241
|
async function addBudgetItems(opts) {
|
|
@@ -9325,8 +9336,11 @@ var sendEstimateToContactPersonFromPayload = async (raw) => {
|
|
|
9325
9336
|
const result = await api2.email.sendEstimateToContactPerson.mutate(input);
|
|
9326
9337
|
out(result);
|
|
9327
9338
|
};
|
|
9328
|
-
async function getBudgetDetails(budgetId) {
|
|
9329
|
-
const detail = await api2.budget.getBudgetDetail.query({
|
|
9339
|
+
async function getBudgetDetails(budgetId, opts) {
|
|
9340
|
+
const detail = await api2.budget.getBudgetDetail.query({
|
|
9341
|
+
id: budgetId,
|
|
9342
|
+
includeInactive: opts?.includeInactive ?? false
|
|
9343
|
+
});
|
|
9330
9344
|
out(detail);
|
|
9331
9345
|
}
|
|
9332
9346
|
async function createBudgetFromPayload(raw) {
|
|
@@ -9940,6 +9954,17 @@ async function getSupplierDetails(supplierId) {
|
|
|
9940
9954
|
});
|
|
9941
9955
|
}
|
|
9942
9956
|
async function approveSupplier(supplierId) {
|
|
9957
|
+
const supplier = await api2.supplier.getSupplierById.query({ id: supplierId });
|
|
9958
|
+
if (supplier.status === ExtendedApprovalStatus.REJECTED) {
|
|
9959
|
+
await assertSensitiveWorkflowConfirmed({
|
|
9960
|
+
action: "Re-approve supplier",
|
|
9961
|
+
entity: `supplier ${supplierId}`,
|
|
9962
|
+
effects: ["state-change", "external-write"],
|
|
9963
|
+
details: supplier.active ? "and sync its details with QuickBooks; append a new approval and retain earlier history; no requester reply email is sent" : "and sync its details with QuickBooks; append a new approval and retain earlier history; it remains archived and no requester reply email is sent"
|
|
9964
|
+
});
|
|
9965
|
+
out(await api2.supplier.reapproveSupplier.mutate({ supplierId }));
|
|
9966
|
+
return;
|
|
9967
|
+
}
|
|
9943
9968
|
await assertSensitiveWorkflowConfirmed({
|
|
9944
9969
|
action: "Approve supplier",
|
|
9945
9970
|
entity: `supplier ${supplierId}`,
|
|
@@ -10881,6 +10906,8 @@ var effectsFor = (legacyTarget) => {
|
|
|
10881
10906
|
};
|
|
10882
10907
|
var titleCase = (value) => value.split("-").map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`).join(" ");
|
|
10883
10908
|
var summaryFor = (legacyTarget) => {
|
|
10909
|
+
if (legacyTarget === "approve-supplier")
|
|
10910
|
+
return "Approve a pending supplier request and send a requester reply email, or directly re-approve a rejected supplier as Admin/Lead without a pending request or email. QuickBooks is synchronized; archived suppliers remain archived. Argument: <supplierId>.";
|
|
10884
10911
|
if (legacyTarget === "search-existing-companies")
|
|
10885
10912
|
return "Search BB and QuickBooks companies without writes. --query <name> [--page <number>].";
|
|
10886
10913
|
if (legacyTarget === "use-existing-company")
|
|
@@ -10903,6 +10930,18 @@ var summaryFor = (legacyTarget) => {
|
|
|
10903
10930
|
}
|
|
10904
10931
|
if (legacyTarget === "whoami")
|
|
10905
10932
|
return "Show the active API-key identity.";
|
|
10933
|
+
if (legacyTarget === "list-budgets") {
|
|
10934
|
+
return "List budgets. Omit --active for active budgets only; use --active false or --active all for Admin-only historical reads.";
|
|
10935
|
+
}
|
|
10936
|
+
if (legacyTarget === "get-budget") {
|
|
10937
|
+
return "Get a budget by ID. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.";
|
|
10938
|
+
}
|
|
10939
|
+
if (legacyTarget === "get-budget-items") {
|
|
10940
|
+
return "Get line items for a budget. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.";
|
|
10941
|
+
}
|
|
10942
|
+
if (legacyTarget === "get-budget-details") {
|
|
10943
|
+
return "Get budget details. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.";
|
|
10944
|
+
}
|
|
10906
10945
|
if (legacyTarget === "create-bill") {
|
|
10907
10946
|
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.";
|
|
10908
10947
|
}
|
|
@@ -10977,12 +11016,32 @@ var registry2 = [
|
|
|
10977
11016
|
authCommand("status", "Show the active CLI authentication method and session status."),
|
|
10978
11017
|
authCommand("logout", "Revoke the interactive CLI session and remove its local credential."),
|
|
10979
11018
|
legacyCommand("whoami", ["auth", "whoami"]),
|
|
10980
|
-
legacyCommand("list-budgets", ["budget", "list"]
|
|
10981
|
-
|
|
11019
|
+
legacyCommand("list-budgets", ["budget", "list"], [], [
|
|
11020
|
+
{
|
|
11021
|
+
name: "--active <true|false|all>",
|
|
11022
|
+
description: "Omit for active budgets only; false or all requires Admin access."
|
|
11023
|
+
}
|
|
11024
|
+
]),
|
|
11025
|
+
legacyCommand("get-budget", ["budget", "get"], [], [
|
|
11026
|
+
{
|
|
11027
|
+
name: "--includeInactive",
|
|
11028
|
+
description: "Include inactive historical budgets; Admin only."
|
|
11029
|
+
}
|
|
11030
|
+
]),
|
|
10982
11031
|
legacyCommand("upload-budget-attachment", ["budget", "attachment", "upload"]),
|
|
10983
11032
|
legacyCommand("mark-budget-won", ["budget", "won", "mark"]),
|
|
10984
|
-
legacyCommand("get-budget-items", ["budget", "item", "list"]
|
|
10985
|
-
|
|
11033
|
+
legacyCommand("get-budget-items", ["budget", "item", "list"], [], [
|
|
11034
|
+
{
|
|
11035
|
+
name: "--includeInactive",
|
|
11036
|
+
description: "Include inactive historical budgets; Admin only."
|
|
11037
|
+
}
|
|
11038
|
+
]),
|
|
11039
|
+
legacyCommand("get-budget-details", ["budget", "detail", "get"], [], [
|
|
11040
|
+
{
|
|
11041
|
+
name: "--includeInactive",
|
|
11042
|
+
description: "Include inactive historical budgets; Admin only."
|
|
11043
|
+
}
|
|
11044
|
+
]),
|
|
10986
11045
|
legacyCommand("get-budget-categories", ["budget", "category", "list"]),
|
|
10987
11046
|
legacyCommand("get-budget-versions", ["budget", "version", "list"]),
|
|
10988
11047
|
legacyCommand("rename-budget-version", ["budget", "version", "rename"]),
|
|
@@ -11526,6 +11585,22 @@ function parseOptionalBoolFlag(flags, key) {
|
|
|
11526
11585
|
return false;
|
|
11527
11586
|
throw new Error(`--${key} must be true or false.`);
|
|
11528
11587
|
}
|
|
11588
|
+
function parseBudgetActiveFlag(flags) {
|
|
11589
|
+
const raw = flags.active;
|
|
11590
|
+
if (raw === undefined)
|
|
11591
|
+
return;
|
|
11592
|
+
if (raw === true) {
|
|
11593
|
+
throw new Error("--active requires true, false, or all.");
|
|
11594
|
+
}
|
|
11595
|
+
const value = String(raw).toLowerCase();
|
|
11596
|
+
if (value === "all")
|
|
11597
|
+
return "all";
|
|
11598
|
+
if (value === "true" || value === "1")
|
|
11599
|
+
return true;
|
|
11600
|
+
if (value === "false" || value === "0")
|
|
11601
|
+
return false;
|
|
11602
|
+
throw new Error("--active must be true, false, or all.");
|
|
11603
|
+
}
|
|
11529
11604
|
var CUSTOMER_INVOICE_SORT_FIELDS = [
|
|
11530
11605
|
"createdAt",
|
|
11531
11606
|
"status",
|
|
@@ -11762,7 +11837,7 @@ Approvals
|
|
|
11762
11837
|
reject-bill <billId> --reason <text> (also sends requester reply email)
|
|
11763
11838
|
approve-budget <budgetId> (also sends requester reply email)
|
|
11764
11839
|
reject-budget <budgetId> --reason <text> (also sends requester reply email)
|
|
11765
|
-
approve-supplier <supplierId> (
|
|
11840
|
+
approve-supplier <supplierId> (approve a pending request, or Admin/Lead re-approve a rejected supplier)
|
|
11766
11841
|
reject-supplier <supplierId> --reason <text> (also sends requester reply email)
|
|
11767
11842
|
|
|
11768
11843
|
Companies & projects
|
|
@@ -12197,7 +12272,7 @@ Waiting for approval\u2026
|
|
|
12197
12272
|
}
|
|
12198
12273
|
configureAccessToken(accessToken);
|
|
12199
12274
|
}
|
|
12200
|
-
if (!resolvedCommand.isLegacyAlias && cmd !== "create-bill" && cmd !== "import-qbo-customer-invoice" && resolvedCommand.command.effects.length > 0) {
|
|
12275
|
+
if (!resolvedCommand.isLegacyAlias && cmd !== "create-bill" && cmd !== "import-qbo-customer-invoice" && cmd !== "approve-supplier" && resolvedCommand.command.effects.length > 0) {
|
|
12201
12276
|
await confirmCurrentCommand({
|
|
12202
12277
|
action: resolvedCommand.command.path.join(" "),
|
|
12203
12278
|
target: commandConfirmationTarget(resolvedCommand),
|
|
@@ -12219,6 +12294,7 @@ Waiting for approval\u2026
|
|
|
12219
12294
|
createdBy: getFlag(flags, "createdBy"),
|
|
12220
12295
|
dateFrom: getFlag(flags, "dateFrom"),
|
|
12221
12296
|
dateTo: getFlag(flags, "dateTo"),
|
|
12297
|
+
active: parseBudgetActiveFlag(flags),
|
|
12222
12298
|
sortBy: getFlag(flags, "sortBy"),
|
|
12223
12299
|
sortDir: getFlag(flags, "sortDir"),
|
|
12224
12300
|
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
@@ -12231,7 +12307,9 @@ Waiting for approval\u2026
|
|
|
12231
12307
|
const id = positional[0];
|
|
12232
12308
|
if (!id)
|
|
12233
12309
|
throw new Error("get-budget requires <id>");
|
|
12234
|
-
await getBudget(id
|
|
12310
|
+
await getBudget(id, {
|
|
12311
|
+
includeInactive: flags.includeInactive === true ? true : parseOptionalBoolFlag(flags, "includeInactive") ?? false
|
|
12312
|
+
});
|
|
12235
12313
|
break;
|
|
12236
12314
|
}
|
|
12237
12315
|
case "upload-budget-attachment": {
|
|
@@ -12258,14 +12336,18 @@ Waiting for approval\u2026
|
|
|
12258
12336
|
const id = positional[0];
|
|
12259
12337
|
if (!id)
|
|
12260
12338
|
throw new Error("get-budget-items requires <budgetId>");
|
|
12261
|
-
await getBudgetItemsOnly(id
|
|
12339
|
+
await getBudgetItemsOnly(id, {
|
|
12340
|
+
includeInactive: flags.includeInactive === true ? true : parseOptionalBoolFlag(flags, "includeInactive") ?? false
|
|
12341
|
+
});
|
|
12262
12342
|
break;
|
|
12263
12343
|
}
|
|
12264
12344
|
case "get-budget-details": {
|
|
12265
12345
|
const id = positional[0];
|
|
12266
12346
|
if (!id)
|
|
12267
12347
|
throw new Error("get-budget-details requires <budgetId>");
|
|
12268
|
-
await getBudgetDetails(id
|
|
12348
|
+
await getBudgetDetails(id, {
|
|
12349
|
+
includeInactive: flags.includeInactive === true ? true : parseOptionalBoolFlag(flags, "includeInactive") ?? false
|
|
12350
|
+
});
|
|
12269
12351
|
break;
|
|
12270
12352
|
}
|
|
12271
12353
|
case "get-budget-categories": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.45.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",
|
|
@@ -25,7 +25,7 @@ The Budget Builder API, not the CLI, makes the final decision. A request may nee
|
|
|
25
25
|
2. A sufficient current database role: `USER`, `INSIDE_SALES`, `LEAD`, `ACCOUNTING_TEAM`, or `ADMIN`. Pending approval assignment does not preserve decision authority after a role change.
|
|
26
26
|
3. Access to the specific resource, commonly through creator ownership or project assignment as Business Development, Inside Sales, or Project Manager.
|
|
27
27
|
4. A compatible resource state, such as a bill being `DRAFT` or `REJECTED` before editing.
|
|
28
|
-
5. A pending approval record assigned to the current user for
|
|
28
|
+
5. A pending approval record assigned to the current user for request decisions. Direct Admin/Lead re-approval of a rejected supplier does not require a pending request.
|
|
29
29
|
6. Required evidence and consistent related records, such as payment proof, approved quotation coverage, or matching project/budget IDs.
|
|
30
30
|
|
|
31
31
|
Therefore, two users with the same role can receive different results for the same command and different resource IDs.
|
|
@@ -95,7 +95,8 @@ This table highlights the role-sensitive command groups an agent is most likely
|
|
|
95
95
|
| `bb project hub status`, `bb project hub setup`, and `bb project hub sync` | `ADMIN`, project creator, or project assignment as Business Development, Inside Sales, or Project Manager. Setup and sync also enforce Project Hub lifecycle/readiness checks. |
|
|
96
96
|
| `bb supplier update`, `bb item update`, `bb item category update` | Any active authenticated role. Normal-user supplier edits are submitted for approval by the shared API; item/category edits apply immediately. Human review and provider synchronization can temporarily block further supplier edits/new usage. |
|
|
97
97
|
| Edit/delete a bill or quotation | Usually creator or `ADMIN`, with a supported status. |
|
|
98
|
-
| `bb budget approve`, `bb budget reject`,
|
|
98
|
+
| `bb budget approve`, `bb budget reject`, and `bb supplier reject` | Current user must be the pending approver and currently have role `LEAD` or `ADMIN`. |
|
|
99
|
+
| `bb supplier approve` | `LEAD` or `ADMIN`. Pending suppliers require an assigned pending request; rejected suppliers can be directly re-approved without one. Re-approval preserves archive state and does not send a requester email. |
|
|
99
100
|
| `bb bill approve` and `bb bill reject` | Current user must be the pending approver and currently have role `ACCOUNTING_TEAM` for the finance/check stage or `ADMIN` for the final approval stage. |
|
|
100
101
|
| `bb quotation approve` and `bb quotation reject` | Current user must be the pending approver and currently have role `ADMIN`; a normal submitter cannot decide the quotation. |
|
|
101
102
|
| `bb bill status update ... CHECKED` | `ACCOUNTING_TEAM` or `ADMIN`. |
|