@firela/billclaw-core 0.1.3

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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/billclaw.d.ts +76 -0
  4. package/dist/billclaw.d.ts.map +1 -0
  5. package/dist/billclaw.js +205 -0
  6. package/dist/billclaw.js.map +1 -0
  7. package/dist/credentials/index.d.ts +8 -0
  8. package/dist/credentials/index.d.ts.map +1 -0
  9. package/dist/credentials/index.js +8 -0
  10. package/dist/credentials/index.js.map +1 -0
  11. package/dist/credentials/keychain.d.ts +92 -0
  12. package/dist/credentials/keychain.d.ts.map +1 -0
  13. package/dist/credentials/keychain.js +172 -0
  14. package/dist/credentials/keychain.js.map +1 -0
  15. package/dist/credentials/store.d.ts +76 -0
  16. package/dist/credentials/store.d.ts.map +1 -0
  17. package/dist/credentials/store.js +144 -0
  18. package/dist/credentials/store.js.map +1 -0
  19. package/dist/errors/errors.d.ts +92 -0
  20. package/dist/errors/errors.d.ts.map +1 -0
  21. package/dist/errors/errors.js +315 -0
  22. package/dist/errors/errors.js.map +1 -0
  23. package/dist/errors/index.d.ts +7 -0
  24. package/dist/errors/index.d.ts.map +1 -0
  25. package/dist/errors/index.js +7 -0
  26. package/dist/errors/index.js.map +1 -0
  27. package/dist/exporters/beancount.d.ts +42 -0
  28. package/dist/exporters/beancount.d.ts.map +1 -0
  29. package/dist/exporters/beancount.js +141 -0
  30. package/dist/exporters/beancount.js.map +1 -0
  31. package/dist/exporters/index.d.ts +8 -0
  32. package/dist/exporters/index.d.ts.map +1 -0
  33. package/dist/exporters/index.js +8 -0
  34. package/dist/exporters/index.js.map +1 -0
  35. package/dist/exporters/ledger.d.ts +42 -0
  36. package/dist/exporters/ledger.d.ts.map +1 -0
  37. package/dist/exporters/ledger.js +139 -0
  38. package/dist/exporters/ledger.js.map +1 -0
  39. package/dist/index.d.ts +23 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +34 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/models/config.d.ts +552 -0
  44. package/dist/models/config.d.ts.map +1 -0
  45. package/dist/models/config.js +168 -0
  46. package/dist/models/config.js.map +1 -0
  47. package/dist/models/index.d.ts +7 -0
  48. package/dist/models/index.d.ts.map +1 -0
  49. package/dist/models/index.js +8 -0
  50. package/dist/models/index.js.map +1 -0
  51. package/dist/runtime/index.d.ts +7 -0
  52. package/dist/runtime/index.d.ts.map +1 -0
  53. package/dist/runtime/index.js +7 -0
  54. package/dist/runtime/index.js.map +1 -0
  55. package/dist/runtime/types.d.ts +110 -0
  56. package/dist/runtime/types.d.ts.map +1 -0
  57. package/dist/runtime/types.js +85 -0
  58. package/dist/runtime/types.js.map +1 -0
  59. package/dist/security/audit.d.ts +148 -0
  60. package/dist/security/audit.d.ts.map +1 -0
  61. package/dist/security/audit.js +286 -0
  62. package/dist/security/audit.js.map +1 -0
  63. package/dist/security/index.d.ts +7 -0
  64. package/dist/security/index.d.ts.map +1 -0
  65. package/dist/security/index.js +7 -0
  66. package/dist/security/index.js.map +1 -0
  67. package/dist/services/event-emitter.d.ts +171 -0
  68. package/dist/services/event-emitter.d.ts.map +1 -0
  69. package/dist/services/event-emitter.js +287 -0
  70. package/dist/services/event-emitter.js.map +1 -0
  71. package/dist/services/index.d.ts +8 -0
  72. package/dist/services/index.d.ts.map +1 -0
  73. package/dist/services/index.js +8 -0
  74. package/dist/services/index.js.map +1 -0
  75. package/dist/sources/gmail/bill-recognizer.d.ts +71 -0
  76. package/dist/sources/gmail/bill-recognizer.d.ts.map +1 -0
  77. package/dist/sources/gmail/bill-recognizer.js +341 -0
  78. package/dist/sources/gmail/bill-recognizer.js.map +1 -0
  79. package/dist/sources/gmail/email-parser.d.ts +68 -0
  80. package/dist/sources/gmail/email-parser.d.ts.map +1 -0
  81. package/dist/sources/gmail/email-parser.js +238 -0
  82. package/dist/sources/gmail/email-parser.js.map +1 -0
  83. package/dist/sources/gmail/gmail-fetch.d.ts +54 -0
  84. package/dist/sources/gmail/gmail-fetch.d.ts.map +1 -0
  85. package/dist/sources/gmail/gmail-fetch.js +300 -0
  86. package/dist/sources/gmail/gmail-fetch.js.map +1 -0
  87. package/dist/sources/gmail/index.d.ts +7 -0
  88. package/dist/sources/gmail/index.d.ts.map +1 -0
  89. package/dist/sources/gmail/index.js +7 -0
  90. package/dist/sources/gmail/index.js.map +1 -0
  91. package/dist/sources/index.d.ts +8 -0
  92. package/dist/sources/index.d.ts.map +1 -0
  93. package/dist/sources/index.js +8 -0
  94. package/dist/sources/index.js.map +1 -0
  95. package/dist/sources/plaid/index.d.ts +7 -0
  96. package/dist/sources/plaid/index.d.ts.map +1 -0
  97. package/dist/sources/plaid/index.js +7 -0
  98. package/dist/sources/plaid/index.js.map +1 -0
  99. package/dist/sources/plaid/plaid-sync.d.ts +42 -0
  100. package/dist/sources/plaid/plaid-sync.d.ts.map +1 -0
  101. package/dist/sources/plaid/plaid-sync.js +182 -0
  102. package/dist/sources/plaid/plaid-sync.js.map +1 -0
  103. package/dist/storage/cache.d.ts +134 -0
  104. package/dist/storage/cache.d.ts.map +1 -0
  105. package/dist/storage/cache.js +239 -0
  106. package/dist/storage/cache.js.map +1 -0
  107. package/dist/storage/index.d.ts +11 -0
  108. package/dist/storage/index.d.ts.map +1 -0
  109. package/dist/storage/index.js +11 -0
  110. package/dist/storage/index.js.map +1 -0
  111. package/dist/storage/indexes.d.ts +136 -0
  112. package/dist/storage/indexes.d.ts.map +1 -0
  113. package/dist/storage/indexes.js +294 -0
  114. package/dist/storage/indexes.js.map +1 -0
  115. package/dist/storage/locking.d.ts +103 -0
  116. package/dist/storage/locking.d.ts.map +1 -0
  117. package/dist/storage/locking.js +158 -0
  118. package/dist/storage/locking.js.map +1 -0
  119. package/dist/storage/streaming.d.ts +102 -0
  120. package/dist/storage/streaming.d.ts.map +1 -0
  121. package/dist/storage/streaming.js +245 -0
  122. package/dist/storage/streaming.js.map +1 -0
  123. package/dist/storage/transaction-storage.d.ts +101 -0
  124. package/dist/storage/transaction-storage.d.ts.map +1 -0
  125. package/dist/storage/transaction-storage.js +193 -0
  126. package/dist/storage/transaction-storage.js.map +1 -0
  127. package/dist/sync/index.d.ts +7 -0
  128. package/dist/sync/index.d.ts.map +1 -0
  129. package/dist/sync/index.js +7 -0
  130. package/dist/sync/index.js.map +1 -0
  131. package/dist/sync/sync-service.d.ts +42 -0
  132. package/dist/sync/sync-service.d.ts.map +1 -0
  133. package/dist/sync/sync-service.js +112 -0
  134. package/dist/sync/sync-service.js.map +1 -0
  135. package/dist/test-fixtures.d.ts +38 -0
  136. package/dist/test-fixtures.d.ts.map +1 -0
  137. package/dist/test-fixtures.js +137 -0
  138. package/dist/test-fixtures.js.map +1 -0
  139. package/package.json +68 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 fire-la
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @firela/billclaw-core
2
+
3
+ Framework-agnostic core business logic for BillClaw financial data import.
4
+
5
+ ## Overview
6
+
7
+ This package contains all core functionality with zero dependencies on any AI framework (OpenClaw, OpenHands, etc.). It provides:
8
+
9
+ - **Data Models**: TypeScript types and Zod schemas for financial data
10
+ - **Storage**: Transaction storage with JSON/CSV support, file locking, and streaming
11
+ - **Sync**: Plaid and Gmail integration for fetching transactions
12
+ - **Exporters**: Beancount and Ledger format exporters
13
+ - **Security**: Platform keychain integration and audit logging
14
+ - **Performance**: TTL-based memory cache and query indexes
15
+ - **Runtime Abstractions**: Logger, ConfigProvider, and EventEmitter interfaces
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pnpm add @firela/billclaw-core
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { Billclaw } from "@firela/billclaw-core";
27
+ import { createRuntimeContext } from "./runtime.js";
28
+
29
+ // Create a runtime context (framework-specific adapter)
30
+ const runtime = createRuntimeContext();
31
+
32
+ // Initialize BillClaw
33
+ const billclaw = new Billclaw(runtime);
34
+
35
+ // Sync transactions from Plaid
36
+ const results = await billclaw.syncPlaid();
37
+
38
+ // Export to Beancount
39
+ const transactions = await billclaw.getTransactions("all", 2024, 1);
40
+ const beancount = await exportToBeancount(transactions, {
41
+ accountId: "all",
42
+ year: 2024,
43
+ month: 1,
44
+ commodity: "USD",
45
+ });
46
+ ```
47
+
48
+ ## Architecture
49
+
50
+ ### Core Classes
51
+
52
+ - **`Billclaw`**: Main class for managing financial data operations
53
+ - **`TransactionStorage`**: Handles transaction persistence and queries
54
+ - **`MemoryCache`**: TTL-based in-memory caching
55
+ - **`AuditLogger`**: Security event logging
56
+
57
+ ### Data Sources
58
+
59
+ - **Plaid**: Bank account and credit card transactions via Plaid API
60
+ - **Gmail**: Bill parsing from email using pattern recognition
61
+ - **GoCardless**: European bank accounts via open banking (planned)
62
+
63
+ ### Exporters
64
+
65
+ - **Beancount**: Plain text accounting format export
66
+ - **Ledger**: Ledger CLI format export
67
+
68
+ ## API Reference
69
+
70
+ ### Billclaw
71
+
72
+ ```typescript
73
+ class Billclaw {
74
+ constructor(context: RuntimeContext)
75
+
76
+ // Account management
77
+ getAccounts(): Promise<AccountConfig[]>
78
+
79
+ // Sync operations
80
+ syncPlaid(accountIds?: string[]): Promise<PlaidSyncResult[]>
81
+ syncGmail(accountIds?: string[], days?: number): Promise<GmailFetchResult[]>
82
+ syncDueAccounts(): Promise<any[]>
83
+
84
+ // Transaction queries
85
+ getTransactions(accountId: string, year: number, month: number): Promise<Transaction[]>
86
+
87
+ // Export operations
88
+ exportToBeancount(transactions: Transaction[], options: BeancountExportOptions): Promise<string>
89
+ exportToLedger(transactions: Transaction[], options: LedgerExportOptions): Promise<string>
90
+ }
91
+ ```
92
+
93
+ ### Configuration
94
+
95
+ ```typescript
96
+ interface BillclawConfig {
97
+ accounts: AccountConfig[];
98
+ webhooks: WebhookConfig[];
99
+ storage: StorageConfig;
100
+ sync: SyncConfig;
101
+ plaid: PlaidConfig;
102
+ gocardless?: GoCardlessConfig;
103
+ gmail?: GmailConfig;
104
+ }
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,76 @@
1
+ /**
2
+ * BillClaw - Main class for financial data import
3
+ *
4
+ * This class provides the primary API for interacting with BillClaw.
5
+ * It is framework-agnostic and can be used by any adapter (CLI, OpenClaw, etc.).
6
+ */
7
+ import type { Logger } from "./errors/errors.js";
8
+ import type { RuntimeContext, ConfigProvider } from "./runtime/types.js";
9
+ import type { Transaction, SyncState } from "./storage/transaction-storage.js";
10
+ import type { PlaidSyncResult } from "./sources/plaid/plaid-sync.js";
11
+ import type { GmailFetchResult } from "./sources/gmail/gmail-fetch.js";
12
+ import type { SyncResult } from "./sync/sync-service.js";
13
+ /**
14
+ * BillClaw - Main class for financial data import
15
+ */
16
+ export declare class Billclaw {
17
+ private readonly context;
18
+ constructor(context: RuntimeContext);
19
+ /**
20
+ * Get the logger
21
+ */
22
+ get logger(): Logger;
23
+ /**
24
+ * Get the config provider
25
+ */
26
+ get config(): ConfigProvider;
27
+ /**
28
+ * Initialize the storage directory structure
29
+ */
30
+ initializeStorage(): Promise<void>;
31
+ /**
32
+ * Get all registered accounts
33
+ */
34
+ getAccounts(): Promise<any[]>;
35
+ /**
36
+ * Get transactions for an account and month
37
+ */
38
+ getTransactions(accountId: string, year: number, month: number): Promise<Transaction[]>;
39
+ /**
40
+ * Get sync states for an account
41
+ */
42
+ getSyncStates(accountId: string): Promise<SyncState[]>;
43
+ /**
44
+ * Sync provider interface - implemented by adapters
45
+ */
46
+ syncAccount(accountId: string): Promise<SyncResult>;
47
+ /**
48
+ * Sync all accounts that are due
49
+ */
50
+ syncDueAccounts(): Promise<SyncResult[]>;
51
+ /**
52
+ * Sync Plaid accounts
53
+ */
54
+ syncPlaid(accountIds?: string[]): Promise<PlaidSyncResult[]>;
55
+ /**
56
+ * Sync a single Plaid account
57
+ */
58
+ private syncPlaidAccount;
59
+ /**
60
+ * Sync Gmail accounts
61
+ */
62
+ syncGmail(accountIds?: string[], days?: number): Promise<GmailFetchResult[]>;
63
+ /**
64
+ * Sync a single Gmail account
65
+ */
66
+ private syncGmailAccount;
67
+ /**
68
+ * Export transactions to Beancount format
69
+ */
70
+ exportToBeancount(accountId: string, year: number, month: number, options?: Partial<any>): Promise<string>;
71
+ /**
72
+ * Export transactions to Ledger format
73
+ */
74
+ exportToLedger(accountId: string, year: number, month: number, options?: Partial<any>): Promise<string>;
75
+ }
76
+ //# sourceMappingURL=billclaw.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billclaw.d.ts","sourceRoot":"","sources":["../src/billclaw.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAC9E,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,+BAA+B,CAAA;AACtC,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAsBxD;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;gBAE5B,OAAO,EAAE,cAAc;IAInC;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,cAAc,CAE3B;IAID;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAKnC;;OAEG;IACG,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,CAAC;IAKzB;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAO5D;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzD;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAO9C;;OAEG;IACG,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAgClE;;OAEG;YACW,gBAAgB;IAe9B;;OAEG;IACG,SAAS,CACb,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,IAAI,GAAE,MAAW,GAChB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyC9B;;OAEG;YACW,gBAAgB;IAe9B;;OAEG;IACG,iBAAiB,CACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GACrB,OAAO,CAAC,MAAM,CAAC;IAWlB;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GACrB,OAAO,CAAC,MAAM,CAAC;CAInB"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * BillClaw - Main class for financial data import
3
+ *
4
+ * This class provides the primary API for interacting with BillClaw.
5
+ * It is framework-agnostic and can be used by any adapter (CLI, OpenClaw, etc.).
6
+ */
7
+ // Storage
8
+ import { readTransactions, readSyncStates, initializeStorage, } from "./storage/transaction-storage.js";
9
+ // Sync
10
+ import { syncDueAccounts } from "./sync/sync-service.js";
11
+ // Sources
12
+ import { syncPlaidAccounts } from "./sources/plaid/plaid-sync.js";
13
+ import { fetchGmailBills } from "./sources/gmail/gmail-fetch.js";
14
+ // Exporters
15
+ import { exportStorageToBeancount, exportStorageToLedger, } from "./exporters/index.js";
16
+ /**
17
+ * BillClaw - Main class for financial data import
18
+ */
19
+ export class Billclaw {
20
+ context;
21
+ constructor(context) {
22
+ this.context = context;
23
+ }
24
+ /**
25
+ * Get the logger
26
+ */
27
+ get logger() {
28
+ return this.context.logger;
29
+ }
30
+ /**
31
+ * Get the config provider
32
+ */
33
+ get config() {
34
+ return this.context.config;
35
+ }
36
+ // ==================== Storage ====================
37
+ /**
38
+ * Initialize the storage directory structure
39
+ */
40
+ async initializeStorage() {
41
+ const storageConfig = await this.context.config.getStorageConfig();
42
+ await initializeStorage(storageConfig);
43
+ this.logger.info?.("Storage initialized");
44
+ }
45
+ /**
46
+ * Get all registered accounts
47
+ */
48
+ async getAccounts() {
49
+ const config = await this.context.config.getConfig();
50
+ return config.accounts || [];
51
+ }
52
+ /**
53
+ * Get transactions for an account and month
54
+ */
55
+ async getTransactions(accountId, year, month) {
56
+ const storageConfig = await this.context.config.getStorageConfig();
57
+ return readTransactions(accountId, year, month, storageConfig);
58
+ }
59
+ /**
60
+ * Get sync states for an account
61
+ */
62
+ async getSyncStates(accountId) {
63
+ const storageConfig = await this.context.config.getStorageConfig();
64
+ return readSyncStates(accountId, storageConfig);
65
+ }
66
+ // ==================== Sync ====================
67
+ /**
68
+ * Sync provider interface - implemented by adapters
69
+ */
70
+ async syncAccount(accountId) {
71
+ const config = await this.context.config.getConfig();
72
+ const account = config.accounts.find((a) => a.id === accountId);
73
+ if (!account) {
74
+ return {
75
+ accountId,
76
+ success: false,
77
+ transactionsAdded: 0,
78
+ transactionsUpdated: 0,
79
+ errors: [`Account not found: ${accountId}`],
80
+ };
81
+ }
82
+ switch (account.type) {
83
+ case "plaid":
84
+ return await this.syncPlaidAccount(account);
85
+ case "gmail":
86
+ return await this.syncGmailAccount(account);
87
+ default:
88
+ return {
89
+ accountId,
90
+ success: false,
91
+ transactionsAdded: 0,
92
+ transactionsUpdated: 0,
93
+ errors: [`Unsupported account type: ${account.type}`],
94
+ };
95
+ }
96
+ }
97
+ /**
98
+ * Sync all accounts that are due
99
+ */
100
+ async syncDueAccounts() {
101
+ const config = await this.context.config.getConfig();
102
+ return syncDueAccounts(config.accounts, this, this.logger);
103
+ }
104
+ // ==================== Plaid ====================
105
+ /**
106
+ * Sync Plaid accounts
107
+ */
108
+ async syncPlaid(accountIds) {
109
+ const config = await this.context.config.getConfig();
110
+ const storageConfig = await this.context.config.getStorageConfig();
111
+ const plaidConfig = {
112
+ clientId: config.plaid.clientId || process.env.PLAID_CLIENT_ID || "",
113
+ secret: config.plaid.secret || process.env.PLAID_SECRET || "",
114
+ environment: config.plaid.environment || "sandbox",
115
+ };
116
+ const accounts = config.accounts
117
+ .filter((a) => a.type === "plaid" && a.enabled && a.plaidAccessToken)
118
+ .filter((a) => !accountIds || accountIds.includes(a.id))
119
+ .map((a) => ({
120
+ id: a.id,
121
+ plaidAccessToken: a.plaidAccessToken,
122
+ }));
123
+ if (accounts.length === 0) {
124
+ this.logger.warn?.("No enabled Plaid accounts found");
125
+ return [];
126
+ }
127
+ return syncPlaidAccounts(accounts, plaidConfig, storageConfig, this.logger, config.webhooks || []);
128
+ }
129
+ /**
130
+ * Sync a single Plaid account
131
+ */
132
+ async syncPlaidAccount(account) {
133
+ const results = await this.syncPlaid([account.id]);
134
+ const result = results[0];
135
+ return {
136
+ accountId: result.accountId,
137
+ success: result.success,
138
+ transactionsAdded: result.transactionsAdded,
139
+ transactionsUpdated: result.transactionsUpdated,
140
+ errors: result.errors,
141
+ };
142
+ }
143
+ // ==================== Gmail ====================
144
+ /**
145
+ * Sync Gmail accounts
146
+ */
147
+ async syncGmail(accountIds, days = 30) {
148
+ const config = await this.context.config.getConfig();
149
+ const storageConfig = await this.context.config.getStorageConfig();
150
+ const gmailConfig = config.gmail || {
151
+ senderWhitelist: [],
152
+ keywords: ["invoice", "statement", "bill due", "receipt", "payment due"],
153
+ confidenceThreshold: 0.5,
154
+ requireAmount: false,
155
+ requireDate: false,
156
+ };
157
+ const accounts = config.accounts
158
+ .filter((a) => a.type === "gmail" && a.enabled)
159
+ .filter((a) => !accountIds || accountIds.includes(a.id))
160
+ .map((a) => ({
161
+ id: a.id,
162
+ gmailEmailAddress: a.gmailEmailAddress || "",
163
+ }));
164
+ if (accounts.length === 0) {
165
+ this.logger.warn?.("No enabled Gmail accounts found");
166
+ return [];
167
+ }
168
+ const results = [];
169
+ for (const account of accounts) {
170
+ const result = await fetchGmailBills(account, days, gmailConfig, storageConfig, this.logger);
171
+ results.push(result);
172
+ }
173
+ return results;
174
+ }
175
+ /**
176
+ * Sync a single Gmail account
177
+ */
178
+ async syncGmailAccount(account) {
179
+ const results = await this.syncGmail([account.id]);
180
+ const result = results[0];
181
+ return {
182
+ accountId: result.accountId,
183
+ success: result.success,
184
+ transactionsAdded: result.transactionsAdded,
185
+ transactionsUpdated: result.transactionsUpdated,
186
+ errors: result.errors,
187
+ };
188
+ }
189
+ // ==================== Exporters ====================
190
+ /**
191
+ * Export transactions to Beancount format
192
+ */
193
+ async exportToBeancount(accountId, year, month, options) {
194
+ const storageConfig = await this.context.config.getStorageConfig();
195
+ return exportStorageToBeancount(accountId, year, month, storageConfig, options);
196
+ }
197
+ /**
198
+ * Export transactions to Ledger format
199
+ */
200
+ async exportToLedger(accountId, year, month, options) {
201
+ const storageConfig = await this.context.config.getStorageConfig();
202
+ return exportStorageToLedger(accountId, year, month, storageConfig, options);
203
+ }
204
+ }
205
+ //# sourceMappingURL=billclaw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billclaw.js","sourceRoot":"","sources":["../src/billclaw.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Credential storage for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./store.js";
7
+ export * from "./keychain.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Credential storage for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./store.js";
7
+ export * from "./keychain.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Platform keychain credential storage for BillClaw
3
+ *
4
+ * Uses keytar to securely store sensitive tokens in the platform keychain:
5
+ * - macOS: Keychain
6
+ * - Windows: Credential Manager
7
+ * - Linux: Secret Service API / KWallet
8
+ *
9
+ * This provides P0 security for:
10
+ * - Plaid access tokens
11
+ * - Gmail refresh tokens
12
+ * - GoCardless access tokens
13
+ */
14
+ import type { Logger } from "../errors/errors.js";
15
+ /**
16
+ * Initialize keytar module
17
+ * Call this before using keychain functions
18
+ */
19
+ export declare function initKeytar(): Promise<void>;
20
+ /**
21
+ * Store a credential in the platform keychain
22
+ *
23
+ * @param key - Credential identifier (e.g., "plaid_access_token:account123")
24
+ * @param value - Sensitive value to store (e.g., access token)
25
+ * @param logger - Optional logger for audit logging
26
+ */
27
+ export declare function setCredential(key: string, value: string, logger?: Logger): Promise<void>;
28
+ /**
29
+ * Retrieve a credential from the platform keychain
30
+ *
31
+ * @param key - Credential identifier
32
+ * @param logger - Optional logger for audit logging
33
+ * @returns The credential value, or null if not found
34
+ */
35
+ export declare function getCredential(key: string, logger?: Logger): Promise<string | null>;
36
+ /**
37
+ * Delete a credential from the platform keychain
38
+ *
39
+ * @param key - Credential identifier
40
+ * @param logger - Optional logger for audit logging
41
+ * @returns true if the credential was deleted, false if it didn't exist
42
+ */
43
+ export declare function deleteCredential(key: string, logger?: Logger): Promise<boolean>;
44
+ /**
45
+ * Check if a credential exists in the keychain
46
+ *
47
+ * @param key - Credential identifier
48
+ * @param logger - Optional logger for audit logging
49
+ * @returns true if the credential exists
50
+ */
51
+ export declare function hasCredential(key: string, logger?: Logger): Promise<boolean>;
52
+ /**
53
+ * List all credential keys for BillClaw
54
+ *
55
+ * Note: keytar doesn't provide a direct way to list all keys,
56
+ * so this returns empty array. Applications should track
57
+ * their own credential keys separately.
58
+ *
59
+ * @returns Empty array (keytar limitation)
60
+ */
61
+ export declare function listCredentialKeys(): Promise<string[]>;
62
+ /**
63
+ * Clear all BillClaw credentials from the keychain
64
+ *
65
+ * WARNING: This is a destructive operation. Use with caution.
66
+ *
67
+ * @param keys - List of credential keys to delete
68
+ * @param logger - Optional logger for audit logging
69
+ */
70
+ export declare function clearAllCredentials(keys: string[], logger?: Logger): Promise<void>;
71
+ /**
72
+ * Keychain utility functions for building credential keys
73
+ */
74
+ export declare const KeychainKeys: {
75
+ /**
76
+ * Build a Plaid access token key
77
+ */
78
+ plaidAccessToken(accountId: string): string;
79
+ /**
80
+ * Build a Gmail refresh token key
81
+ */
82
+ gmailRefreshToken(accountId: string): string;
83
+ /**
84
+ * Build a GoCardless access token key
85
+ */
86
+ gocardlessAccessToken(accountId: string): string;
87
+ /**
88
+ * Build a GoCardless requisition ID key
89
+ */
90
+ gocardlessRequisitionId(accountId: string): string;
91
+ };
92
+ //# sourceMappingURL=keychain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../src/credentials/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAKjD;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAUhD;AAOD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BxB;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAI5D;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;IACvB;;OAEG;gCACyB,MAAM,GAAG,MAAM;IAI3C;;OAEG;iCAC0B,MAAM,GAAG,MAAM;IAI5C;;OAEG;qCAC8B,MAAM,GAAG,MAAM;IAIhD;;OAEG;uCACgC,MAAM,GAAG,MAAM;CAGnD,CAAA"}