@go-labs-sg/bb 1.16.0 → 1.17.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 +15 -1
- package/dist/api-client.js +11 -1
- package/dist/commands.js +61 -3
- package/dist/index.js +109 -8
- package/dist/parse-cli-enums.js +12 -1
- package/dist/prisma-enums.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,20 @@ export BB_API_KEY=<your-key>
|
|
|
37
37
|
|
|
38
38
|
Create or revoke keys in the web app: **Goracle → API Keys** (MCP API keys).
|
|
39
39
|
|
|
40
|
+
Admins can provision API-only service identities and manage their keys from the
|
|
41
|
+
CLI. These identities are not human web accounts: `create-user` does not create
|
|
42
|
+
or link a Google OAuth account, so the resulting user cannot sign in with Google.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bb create-user --email agent@example.com --name "Budget Agent" --role USER
|
|
46
|
+
bb create-api-key --userId <user-id> --name "Budget Agent CLI"
|
|
47
|
+
bb list-api-keys --userId <user-id>
|
|
48
|
+
bb revoke-api-key <api-key-id> --userId <user-id>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The raw key is returned only by `create-api-key`; copy it immediately. Listing
|
|
52
|
+
keys returns metadata only.
|
|
53
|
+
|
|
40
54
|
The CLI talks to the production API: `https://budget-builder.getout.events`.
|
|
41
55
|
|
|
42
56
|
## Usage
|
|
@@ -75,7 +89,7 @@ Global options and flags use `--key=value` or `--key value` (see `bb help`).
|
|
|
75
89
|
| **Companies & projects** | `list-companies`, `get-company`, `create-company`, `update-company` (`--payload`), `delete-company`, `list-projects`, `get-project`, `create-project` (required: `--name`, `--companyId`, `--contactPersonId`, `--insideSalesId`, `--businessDevelopmentId`, `--venue`, `--startDate` as ISO datetime for project/window start; when `--asanaTaskId` is omitted, the CLI searches open Asana lead tasks in the Deals project, prompts for one of the top five matches, and resolves Slack channel fields from the selected deal card; optional `--asanaSearch`, `--pax`, `--endDate`, `--description`; always requests QBO project import like the web app), `update-project` (`--payload` with `dateRange.from` / `dateRange.to` for the project window; optional `requestQboAccountantNotification` in JSON), `delete-project`, `update-project-status` (`<id>` `<status>`: `PITCH` \| `WON` \| `COMPLETED` \| `LOST`; for `PITCH` → `WON` also pass `--projectManagerId` or `--projectManagerName`; when marking `WON` without an accepted/closed budget or proof, pass `--wonOverrideReason`) |
|
|
76
90
|
| **Contacts** | `list-contacts`, `create-contact-person` (`--payload`), `update-contact-person` (`--payload`) |
|
|
77
91
|
| **Suppliers & items** | `list-suppliers` (defaults to active suppliers, `--perPage 10`, sorted by `createdAt` desc; supports `--name`, `--sortBy` for scalar supplier fields, `--sortDir`, `--createdBy`, `--gstRegistered`, `--status`, `--supplierTags`, `--active false` for archived suppliers), `create-supplier` (`--payload`; when supplier status is `PENDING_APPROVAL`, also runs `supplier.createSupplierApproval` and `email.sendSupplierApprovalRequestEmail`), `update-supplier` (`--payload`; when supplier status is `PENDING_APPROVAL`, also runs `supplier.createSupplierApproval` and `email.sendSupplierApprovalRequestEmail`), `delete-suppliers` (`--ids` CSV; admin; archives/deactivates related items), `reactivate-suppliers` (`--ids` CSV; admin; reactivates related items), `create-certification` / `create-payment-method` / `create-supplier-role` / `create-supplier-tag` (`--name`), `get-supplier-details` (includes `supplierApprovalSummary` for pending approvers, superseded approvers, and the actual responder/respondedAt metadata), `get-supplier-analytics`, `list-items`, `create-item` (`--payload`), `update-item` (`--payload`), `delete-item`, `get-item`, `list-item-categories`, `create-item-category`, `update-item-category`, `delete-item-categories` (`--ids` CSV; admin) |
|
|
78
|
-
| **Dashboard & users** | `list-users`, `get-user-performance`, `get-dashboard`, `get-monthly-metrics`, `get-system-overview`, `get-estimate-performance`, `get-financial-overview` |
|
|
92
|
+
| **Dashboard & users** | `list-users`, `create-user` (admin; provisions an API-only service identity with no Google sign-in; `--email`, optional `--name`, optional `--role` defaulting to `USER`), `create-api-key` (admin; `--userId`, `--name`; raw key shown once), `list-api-keys` (admin; optional `--userId`), `revoke-api-key` (admin; key ID plus `--userId` for another user's key), `get-user-performance`, `get-dashboard`, `get-monthly-metrics`, `get-system-overview`, `get-estimate-performance`, `get-financial-overview` |
|
|
79
93
|
| **Errors** | `get-recent-errors`, `get-error-metrics` |
|
|
80
94
|
| **Historical / benchmarks** | `get-approved-budgets`, `get-budget-category-benchmarks`, `get-item-pricing-history`, `get-supplier-pricing-history` |
|
|
81
95
|
|
package/dist/api-client.js
CHANGED
|
@@ -8,10 +8,20 @@ function getAuthHeader() {
|
|
|
8
8
|
return undefined;
|
|
9
9
|
return `Bearer ${key.trim()}`;
|
|
10
10
|
}
|
|
11
|
+
const containsRawApiKey = (value, seen = new WeakSet()) => {
|
|
12
|
+
if (typeof value !== "object" || value === null)
|
|
13
|
+
return false;
|
|
14
|
+
if (seen.has(value))
|
|
15
|
+
return false;
|
|
16
|
+
seen.add(value);
|
|
17
|
+
return Object.entries(value).some(([key, nestedValue]) => (key.toLowerCase() === "apikey" && typeof nestedValue === "string") ||
|
|
18
|
+
containsRawApiKey(nestedValue, seen));
|
|
19
|
+
};
|
|
11
20
|
export const api = createTRPCProxyClient({
|
|
12
21
|
links: [
|
|
13
22
|
loggerLink({
|
|
14
|
-
enabled: () => shouldLogCliActions()
|
|
23
|
+
enabled: (opts) => shouldLogCliActions() &&
|
|
24
|
+
!(opts.direction === "down" && containsRawApiKey(opts.result)),
|
|
15
25
|
colorMode: "ansi",
|
|
16
26
|
withContext: false,
|
|
17
27
|
// tRPC defaults use console.log for requests; stderr keeps stdout JSON-safe for pipes.
|
package/dist/commands.js
CHANGED
|
@@ -589,7 +589,11 @@ export async function getBillAttachments(id) {
|
|
|
589
589
|
const result = await api.bill.getAttachments.query({ id });
|
|
590
590
|
out(result);
|
|
591
591
|
}
|
|
592
|
-
export async function uploadBillAttachmentFromPath(billId, filePath) {
|
|
592
|
+
export async function uploadBillAttachmentFromPath(billId, filePath, type = "BILL") {
|
|
593
|
+
const confirmed = await uploadSingleBillAttachmentFromPath(billId, filePath, type);
|
|
594
|
+
out(confirmed);
|
|
595
|
+
}
|
|
596
|
+
const uploadSingleBillAttachmentFromPath = async (billId, filePath, type = "BILL") => {
|
|
593
597
|
const buf = await readFile(filePath);
|
|
594
598
|
const fileName = basename(filePath);
|
|
595
599
|
const size = buf.byteLength;
|
|
@@ -606,13 +610,51 @@ export async function uploadBillAttachmentFromPath(billId, filePath) {
|
|
|
606
610
|
if (!res.ok) {
|
|
607
611
|
throw new Error(`S3 upload failed: HTTP ${res.status} ${(await res.text()).slice(0, 500)}`);
|
|
608
612
|
}
|
|
609
|
-
|
|
613
|
+
return await api.attachment.confirmBillAttachment.mutate({
|
|
610
614
|
billId,
|
|
611
615
|
key,
|
|
612
616
|
name: fileName,
|
|
613
617
|
size,
|
|
618
|
+
type,
|
|
619
|
+
});
|
|
620
|
+
};
|
|
621
|
+
export async function uploadBillAttachmentsFromPaths(billId, filePaths, type = "BILL") {
|
|
622
|
+
if (filePaths.length === 0) {
|
|
623
|
+
throw new Error("At least one bill attachment file path is required");
|
|
624
|
+
}
|
|
625
|
+
const results = [];
|
|
626
|
+
for (const filePath of filePaths) {
|
|
627
|
+
results.push(await uploadSingleBillAttachmentFromPath(billId, filePath, type));
|
|
628
|
+
}
|
|
629
|
+
out({
|
|
630
|
+
success: true,
|
|
631
|
+
billId,
|
|
632
|
+
type,
|
|
633
|
+
count: results.length,
|
|
634
|
+
attachments: results.map((result) => result.attachment),
|
|
635
|
+
results,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
export async function uploadBillDocumentsFromPaths({ billId, invoicePaths = [], paymentProofPaths = [], }) {
|
|
639
|
+
if (invoicePaths.length === 0 && paymentProofPaths.length === 0) {
|
|
640
|
+
throw new Error("At least one invoice or payment proof file path is required");
|
|
641
|
+
}
|
|
642
|
+
const invoiceResults = [];
|
|
643
|
+
for (const filePath of invoicePaths) {
|
|
644
|
+
invoiceResults.push(await uploadSingleBillAttachmentFromPath(billId, filePath, "BILL"));
|
|
645
|
+
}
|
|
646
|
+
const paymentProofResults = [];
|
|
647
|
+
for (const filePath of paymentProofPaths) {
|
|
648
|
+
paymentProofResults.push(await uploadSingleBillAttachmentFromPath(billId, filePath, "BILL_PAYMENT_PROOF"));
|
|
649
|
+
}
|
|
650
|
+
out({
|
|
651
|
+
success: true,
|
|
652
|
+
billId,
|
|
653
|
+
count: invoiceResults.length + paymentProofResults.length,
|
|
654
|
+
invoiceAttachments: invoiceResults.map((result) => result.attachment),
|
|
655
|
+
paymentProofAttachments: paymentProofResults.map((result) => result.attachment),
|
|
656
|
+
results: [...invoiceResults, ...paymentProofResults],
|
|
614
657
|
});
|
|
615
|
-
out(confirmed);
|
|
616
658
|
}
|
|
617
659
|
export async function uploadQuotationAttachmentFromPath(projectId, filePath) {
|
|
618
660
|
const buf = await readFile(filePath);
|
|
@@ -1495,6 +1537,22 @@ export async function getSupplierAnalytics(opts) {
|
|
|
1495
1537
|
out(result);
|
|
1496
1538
|
}
|
|
1497
1539
|
// --- Dashboard / analytics ---
|
|
1540
|
+
export async function createUser(input) {
|
|
1541
|
+
const user = await api.user.create.mutate(input);
|
|
1542
|
+
out(user);
|
|
1543
|
+
}
|
|
1544
|
+
export async function createApiKeyForUser(input) {
|
|
1545
|
+
const apiKey = await api.mcpApiKey.create.mutate(input);
|
|
1546
|
+
out(apiKey);
|
|
1547
|
+
}
|
|
1548
|
+
export async function listApiKeysForUser(input) {
|
|
1549
|
+
const apiKeys = await api.mcpApiKey.list.query(input);
|
|
1550
|
+
out(apiKeys);
|
|
1551
|
+
}
|
|
1552
|
+
export async function revokeApiKeyForUser(input) {
|
|
1553
|
+
const result = await api.mcpApiKey.revoke.mutate(input);
|
|
1554
|
+
out(result);
|
|
1555
|
+
}
|
|
1498
1556
|
export async function listUsers() {
|
|
1499
1557
|
const users = await api.user.getAllUsers.query();
|
|
1500
1558
|
out(users);
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import "./load-env.js";
|
|
3
3
|
import { requireApiKey } from "./api-client.js";
|
|
4
4
|
import { consumeCliQuietFlags, logCliAction, sanitizeFlagsForTrace, setCliQuiet, shouldLogCliActions, } from "./cli-trace.js";
|
|
5
|
-
import { addBudgetItems, approveBill, approveBudget, approveQuotation, approveSupplier, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createEstimate, createItemCategory, createItemFromPayload, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteCompanyById, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteSuppliersByIds, downloadQuotationPdf, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getQuotationDetails, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, rejectBill, rejectBudget, rejectQuotation, rejectSupplier, removeBudgetItem, reorderBudgetItemsCli, submitQuotation, updateBillFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, } from "./commands.js";
|
|
5
|
+
import { addBudgetItems, approveBill, approveBudget, approveQuotation, approveSupplier, createApiKeyForUser, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createEstimate, createItemCategory, createItemFromPayload, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, createUser, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteCompanyById, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteSuppliersByIds, downloadQuotationPdf, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getQuotationDetails, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, listApiKeysForUser, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, rejectBill, rejectBudget, rejectQuotation, rejectSupplier, removeBudgetItem, reorderBudgetItemsCli, revokeApiKeyForUser, submitQuotation, updateBillFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBillAttachmentsFromPaths, uploadBillDocumentsFromPaths, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, } from "./commands.js";
|
|
6
6
|
import { getFlag, parseArgs } from "./parse-args.js";
|
|
7
|
-
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedIds, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, projectStatusesForHelp, } from "./parse-cli-enums.js";
|
|
7
|
+
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedIds, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, parseUserRole, projectStatusesForHelp, userRolesForHelp, } from "./parse-cli-enums.js";
|
|
8
8
|
import { parseJsonFlag, parseOptionalNumber as parseOptNum, } from "./parse-json-flag.js";
|
|
9
9
|
function parsePositiveIntFlag(value, flagName) {
|
|
10
10
|
if (value === undefined)
|
|
@@ -171,7 +171,9 @@ Bills
|
|
|
171
171
|
Clear a field: --clearPaymentTrackingUrl true | --clearPaymentReference true | --clearQuickbooksBillId true | --clearPaymentDate true
|
|
172
172
|
patch-bill-invoice-number <billId> <invoiceNumber>
|
|
173
173
|
get-bill-attachments <billId>
|
|
174
|
-
upload-bill-attachment <billId> <filePath>
|
|
174
|
+
upload-bill-attachment <billId> <filePath...> [--file <path>] [--files <csv>] Attach invoice/supporting files as BILL attachments to any bill status, including PAID bills
|
|
175
|
+
attach-bill-payment-receipt <billId> <filePath...> [--file <path>] [--files <csv>] Attach Wise/payment-proof PDFs as BILL_PAYMENT_PROOF attachments
|
|
176
|
+
attach-bill-documents <billId> [--invoice <path>] [--invoices <csv>] [--paymentProof|--payment-proof|--proof <path>] [--paymentProofs|--payment-proofs|--proofs <csv>] Attach invoice and/or payment proof files with separate BB attachment types
|
|
175
177
|
get-bill-details <billId>
|
|
176
178
|
|
|
177
179
|
Quotations
|
|
@@ -241,6 +243,10 @@ Suppliers & items
|
|
|
241
243
|
|
|
242
244
|
Dashboard & users
|
|
243
245
|
list-users
|
|
246
|
+
create-user --email <email> [--name <name>] [--role ${userRolesForHelp.join("|")}] (admin; API-only service identity with no Google sign-in)
|
|
247
|
+
create-api-key --userId <userId> --name <label> (admin; raw key is shown once)
|
|
248
|
+
list-api-keys [--userId <userId>] (admin; defaults to the caller)
|
|
249
|
+
revoke-api-key <apiKeyId> [--userId <userId>] (admin; --userId is required for another user's key)
|
|
244
250
|
get-user-performance [--userId]
|
|
245
251
|
get-dashboard [--userId] [--role BD|CREATOR|INSIDE_SALES|ALL] [--deals ALL|SUCCESSFUL|LOST] [--timeFrame] [--startDate] [--endDate]
|
|
246
252
|
get-monthly-metrics [same optional flags as get-dashboard] (dashboard.getMonthlyMetrics)
|
|
@@ -684,12 +690,71 @@ async function main() {
|
|
|
684
690
|
await getBillAttachments(id);
|
|
685
691
|
break;
|
|
686
692
|
}
|
|
687
|
-
case "upload-bill-attachment":
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
693
|
+
case "upload-bill-attachment":
|
|
694
|
+
case "attach-bill-attachment":
|
|
695
|
+
case "attach-bill-payment-receipt": {
|
|
696
|
+
const [billId, ...positionalFilePaths] = positional;
|
|
697
|
+
const fileFlag = getFlag(flags, "file");
|
|
698
|
+
const filesFlag = getFlag(flags, "files");
|
|
699
|
+
const filePaths = [
|
|
700
|
+
...positionalFilePaths,
|
|
701
|
+
...(fileFlag !== undefined ? [String(fileFlag)] : []),
|
|
702
|
+
...(filesFlag !== undefined
|
|
703
|
+
? String(filesFlag)
|
|
704
|
+
.split(",")
|
|
705
|
+
.map((value) => value.trim())
|
|
706
|
+
.filter(Boolean)
|
|
707
|
+
: []),
|
|
708
|
+
];
|
|
709
|
+
if (!billId || filePaths.length === 0) {
|
|
710
|
+
throw new Error(`${cmd} requires <billId> <filePath...> or --file/--files`);
|
|
711
|
+
}
|
|
712
|
+
const attachmentType = cmd === "attach-bill-payment-receipt" ? "BILL_PAYMENT_PROOF" : "BILL";
|
|
713
|
+
const [firstFilePath] = filePaths;
|
|
714
|
+
if (filePaths.length === 1 && firstFilePath !== undefined) {
|
|
715
|
+
await uploadBillAttachmentFromPath(billId, firstFilePath, attachmentType);
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
await uploadBillAttachmentsFromPaths(billId, filePaths, attachmentType);
|
|
691
719
|
}
|
|
692
|
-
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
case "attach-bill-documents": {
|
|
723
|
+
const billId = positional[0];
|
|
724
|
+
if (!billId)
|
|
725
|
+
throw new Error("attach-bill-documents requires <billId>");
|
|
726
|
+
const getFlagAlias = (...flagNames) => {
|
|
727
|
+
for (const flagName of flagNames) {
|
|
728
|
+
const value = getFlag(flags, flagName);
|
|
729
|
+
if (value !== undefined)
|
|
730
|
+
return value;
|
|
731
|
+
}
|
|
732
|
+
return undefined;
|
|
733
|
+
};
|
|
734
|
+
const parseFilesFlag = (...flagNames) => {
|
|
735
|
+
const raw = getFlagAlias(...flagNames);
|
|
736
|
+
if (raw === undefined)
|
|
737
|
+
return [];
|
|
738
|
+
return String(raw)
|
|
739
|
+
.split(",")
|
|
740
|
+
.map((value) => value.trim())
|
|
741
|
+
.filter(Boolean);
|
|
742
|
+
};
|
|
743
|
+
const invoicePath = getFlagAlias("invoice", "invoice-file");
|
|
744
|
+
const paymentProofPath = getFlagAlias("paymentProof", "payment-proof", "proof", "receipt");
|
|
745
|
+
const invoicePaths = [
|
|
746
|
+
...(invoicePath !== undefined ? [String(invoicePath)] : []),
|
|
747
|
+
...parseFilesFlag("invoices", "invoice-files"),
|
|
748
|
+
];
|
|
749
|
+
const paymentProofPaths = [
|
|
750
|
+
...(paymentProofPath !== undefined ? [String(paymentProofPath)] : []),
|
|
751
|
+
...parseFilesFlag("paymentProofs", "payment-proofs", "proofs", "receipts"),
|
|
752
|
+
];
|
|
753
|
+
await uploadBillDocumentsFromPaths({
|
|
754
|
+
billId,
|
|
755
|
+
invoicePaths,
|
|
756
|
+
paymentProofPaths,
|
|
757
|
+
});
|
|
693
758
|
break;
|
|
694
759
|
}
|
|
695
760
|
case "get-bill-details": {
|
|
@@ -1171,6 +1236,42 @@ async function main() {
|
|
|
1171
1236
|
await listUsers();
|
|
1172
1237
|
break;
|
|
1173
1238
|
}
|
|
1239
|
+
case "create-user": {
|
|
1240
|
+
const email = getFlag(flags, "email");
|
|
1241
|
+
if (!email) {
|
|
1242
|
+
throw new Error(`create-user requires --email <email> [--name <name>] [--role ${userRolesForHelp.join("|")}]`);
|
|
1243
|
+
}
|
|
1244
|
+
await createUser({
|
|
1245
|
+
email,
|
|
1246
|
+
name: getFlag(flags, "name"),
|
|
1247
|
+
role: parseUserRole(getFlag(flags, "role")),
|
|
1248
|
+
});
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
case "create-api-key": {
|
|
1252
|
+
const userId = getFlag(flags, "userId");
|
|
1253
|
+
const name = getFlag(flags, "name");
|
|
1254
|
+
if (!userId || !name) {
|
|
1255
|
+
throw new Error("create-api-key requires --userId <userId> --name <label>");
|
|
1256
|
+
}
|
|
1257
|
+
await createApiKeyForUser({ userId, name });
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
case "list-api-keys": {
|
|
1261
|
+
await listApiKeysForUser({ userId: getFlag(flags, "userId") });
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
case "revoke-api-key": {
|
|
1265
|
+
const id = positional[0];
|
|
1266
|
+
if (!id) {
|
|
1267
|
+
throw new Error("revoke-api-key requires <apiKeyId> [--userId <userId>]");
|
|
1268
|
+
}
|
|
1269
|
+
await revokeApiKeyForUser({
|
|
1270
|
+
id,
|
|
1271
|
+
userId: getFlag(flags, "userId"),
|
|
1272
|
+
});
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1174
1275
|
case "get-user-performance": {
|
|
1175
1276
|
await getUserPerformance(getFlag(flags, "userId"));
|
|
1176
1277
|
break;
|
package/dist/parse-cli-enums.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { BudgetRole, Deals, ExtendedApprovalStatus, ExtendedBillStatus, ExtendedErrorSeverity, ExtendedErrorStatus, ExtendedProjectStatus, TimeFrame, } from "./filter-enums.js";
|
|
2
|
-
import { BillStatus, BudgetStatus, ProjectStatus } from "./prisma-enums.js";
|
|
2
|
+
import { BillStatus, BudgetStatus, ProjectStatus, UserRole, } from "./prisma-enums.js";
|
|
3
3
|
const BUDGET_STATUS_VALUES = new Set(Object.values(BudgetStatus));
|
|
4
4
|
const BILL_STATUS_VALUES = new Set(Object.values(BillStatus));
|
|
5
5
|
const PROJECT_STATUS_VALUES = new Set(Object.values(ProjectStatus));
|
|
6
|
+
const USER_ROLE_VALUES = new Set(Object.values(UserRole));
|
|
6
7
|
const EXTENDED_PROJECT_STATUS_VALUES = new Set(Object.values(ExtendedProjectStatus));
|
|
7
8
|
const DASHBOARD_ROLE_VALUES = new Set([
|
|
8
9
|
"BD",
|
|
@@ -52,6 +53,15 @@ export function parseProjectStatusForUpdate(raw) {
|
|
|
52
53
|
}
|
|
53
54
|
return raw;
|
|
54
55
|
}
|
|
56
|
+
export function parseUserRole(raw) {
|
|
57
|
+
if (raw === undefined || raw === "")
|
|
58
|
+
return UserRole.USER;
|
|
59
|
+
const normalizedRole = raw.trim().toUpperCase();
|
|
60
|
+
if (!USER_ROLE_VALUES.has(normalizedRole)) {
|
|
61
|
+
throw new Error(`Invalid user role "${raw}". Use one of: ${[...USER_ROLE_VALUES].join(", ")}.`);
|
|
62
|
+
}
|
|
63
|
+
return normalizedRole;
|
|
64
|
+
}
|
|
55
65
|
export function parseOptionalExtendedProjectStatus(raw) {
|
|
56
66
|
if (raw === undefined || raw === "")
|
|
57
67
|
return undefined;
|
|
@@ -233,3 +243,4 @@ export const budgetStatusesForHelp = Object.values(BudgetStatus);
|
|
|
233
243
|
export const billStatusesForHelp = Object.values(BillStatus);
|
|
234
244
|
export const billStatusesForUpdateHelp = Object.values(BillStatus).filter((s) => s !== BillStatus.PENDING_APPROVAL);
|
|
235
245
|
export const projectStatusesForHelp = Object.values(ProjectStatus);
|
|
246
|
+
export const userRolesForHelp = Object.values(UserRole);
|
package/dist/prisma-enums.js
CHANGED
|
@@ -36,6 +36,13 @@ export const ApprovalType = {
|
|
|
36
36
|
CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
|
|
37
37
|
QUOTATION: "QUOTATION",
|
|
38
38
|
};
|
|
39
|
+
export const UserRole = {
|
|
40
|
+
USER: "USER",
|
|
41
|
+
LEAD: "LEAD",
|
|
42
|
+
ADMIN: "ADMIN",
|
|
43
|
+
INSIDE_SALES: "INSIDE_SALES",
|
|
44
|
+
ACCOUNTING_TEAM: "ACCOUNTING_TEAM",
|
|
45
|
+
};
|
|
39
46
|
export const ErrorSeverity = {
|
|
40
47
|
LOW: "LOW",
|
|
41
48
|
MEDIUM: "MEDIUM",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "Budget Builder CLI for AI agents — manage budgets, bills, and claims; bill records use isClaimable=false for bills and isClaimable=true for claims.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|