actual-mcp-server 0.5.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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +663 -0
  3. package/bin/actual-mcp-server.js +3 -0
  4. package/dist/generated/actual-client/types.js +5 -0
  5. package/dist/package.json +88 -0
  6. package/dist/src/actualConnection.js +157 -0
  7. package/dist/src/actualToolsManager.js +211 -0
  8. package/dist/src/auth/budget-acl.js +143 -0
  9. package/dist/src/auth/setup.js +58 -0
  10. package/dist/src/config.js +41 -0
  11. package/dist/src/index.js +313 -0
  12. package/dist/src/lib/ActualConnectionPool.js +343 -0
  13. package/dist/src/lib/ActualMCPConnection.js +125 -0
  14. package/dist/src/lib/actual-adapter.js +1228 -0
  15. package/dist/src/lib/actual-schema.js +222 -0
  16. package/dist/src/lib/budget-registry.js +64 -0
  17. package/dist/src/lib/constants.js +121 -0
  18. package/dist/src/lib/errors.js +19 -0
  19. package/dist/src/lib/loggerFactory.js +72 -0
  20. package/dist/src/lib/node-polyfills.js +20 -0
  21. package/dist/src/lib/query-validator.js +221 -0
  22. package/dist/src/lib/retry.js +26 -0
  23. package/dist/src/lib/schemas/common.js +203 -0
  24. package/dist/src/lib/toolFactory.js +109 -0
  25. package/dist/src/logger.js +127 -0
  26. package/dist/src/observability.js +58 -0
  27. package/dist/src/prompts/showLargeTransactions.js +6 -0
  28. package/dist/src/resources/accountsSummary.js +13 -0
  29. package/dist/src/server/httpServer.js +540 -0
  30. package/dist/src/server/httpServer_testing.js +401 -0
  31. package/dist/src/server/stdioServer.js +52 -0
  32. package/dist/src/server/streamable-http.js +148 -0
  33. package/dist/src/tests/actualToolsTests.js +70 -0
  34. package/dist/src/tests/observability.smoke.test.js +18 -0
  35. package/dist/src/tests/testMcpClient.js +170 -0
  36. package/dist/src/tests_adapter_runner.js +86 -0
  37. package/dist/src/tools/accounts_close.js +16 -0
  38. package/dist/src/tools/accounts_create.js +27 -0
  39. package/dist/src/tools/accounts_delete.js +16 -0
  40. package/dist/src/tools/accounts_get_balance.js +40 -0
  41. package/dist/src/tools/accounts_list.js +16 -0
  42. package/dist/src/tools/accounts_reopen.js +16 -0
  43. package/dist/src/tools/accounts_update.js +52 -0
  44. package/dist/src/tools/bank_sync.js +22 -0
  45. package/dist/src/tools/budget_updates_batch.js +77 -0
  46. package/dist/src/tools/budgets_getMonth.js +14 -0
  47. package/dist/src/tools/budgets_getMonths.js +14 -0
  48. package/dist/src/tools/budgets_get_all.js +13 -0
  49. package/dist/src/tools/budgets_holdForNextMonth.js +19 -0
  50. package/dist/src/tools/budgets_list_available.js +20 -0
  51. package/dist/src/tools/budgets_resetHold.js +16 -0
  52. package/dist/src/tools/budgets_setAmount.js +26 -0
  53. package/dist/src/tools/budgets_setCarryover.js +18 -0
  54. package/dist/src/tools/budgets_switch.js +27 -0
  55. package/dist/src/tools/budgets_transfer.js +64 -0
  56. package/dist/src/tools/categories_create.js +65 -0
  57. package/dist/src/tools/categories_delete.js +16 -0
  58. package/dist/src/tools/categories_get.js +14 -0
  59. package/dist/src/tools/categories_update.js +22 -0
  60. package/dist/src/tools/category_groups_create.js +18 -0
  61. package/dist/src/tools/category_groups_delete.js +26 -0
  62. package/dist/src/tools/category_groups_get.js +13 -0
  63. package/dist/src/tools/category_groups_update.js +21 -0
  64. package/dist/src/tools/get_id_by_name.js +36 -0
  65. package/dist/src/tools/index.js +63 -0
  66. package/dist/src/tools/payee_rules_get.js +27 -0
  67. package/dist/src/tools/payees_create.js +25 -0
  68. package/dist/src/tools/payees_delete.js +16 -0
  69. package/dist/src/tools/payees_get.js +14 -0
  70. package/dist/src/tools/payees_merge.js +17 -0
  71. package/dist/src/tools/payees_update.js +59 -0
  72. package/dist/src/tools/query_run.js +78 -0
  73. package/dist/src/tools/rules_create.js +129 -0
  74. package/dist/src/tools/rules_create_or_update.js +191 -0
  75. package/dist/src/tools/rules_delete.js +26 -0
  76. package/dist/src/tools/rules_get.js +13 -0
  77. package/dist/src/tools/rules_update.js +120 -0
  78. package/dist/src/tools/schedules_create.js +54 -0
  79. package/dist/src/tools/schedules_delete.js +41 -0
  80. package/dist/src/tools/schedules_get.js +13 -0
  81. package/dist/src/tools/schedules_update.js +40 -0
  82. package/dist/src/tools/server_get_version.js +22 -0
  83. package/dist/src/tools/server_info.js +86 -0
  84. package/dist/src/tools/session_close.js +100 -0
  85. package/dist/src/tools/session_list.js +24 -0
  86. package/dist/src/tools/transactions_create.js +50 -0
  87. package/dist/src/tools/transactions_delete.js +20 -0
  88. package/dist/src/tools/transactions_filter.js +73 -0
  89. package/dist/src/tools/transactions_get.js +23 -0
  90. package/dist/src/tools/transactions_import.js +21 -0
  91. package/dist/src/tools/transactions_search_by_amount.js +126 -0
  92. package/dist/src/tools/transactions_search_by_category.js +137 -0
  93. package/dist/src/tools/transactions_search_by_month.js +142 -0
  94. package/dist/src/tools/transactions_search_by_payee.js +142 -0
  95. package/dist/src/tools/transactions_summary_by_category.js +80 -0
  96. package/dist/src/tools/transactions_summary_by_payee.js +72 -0
  97. package/dist/src/tools/transactions_uncategorized.js +66 -0
  98. package/dist/src/tools/transactions_update.js +34 -0
  99. package/dist/src/tools/transactions_update_batch.js +60 -0
  100. package/dist/src/utils.js +63 -0
  101. package/package.json +88 -0
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Actual Budget Database Schema
3
+ *
4
+ * This module provides the schema definition for Actual Budget's database tables
5
+ * and fields. Used for pre-validation of SQL queries before execution to prevent
6
+ * server crashes from invalid field references.
7
+ *
8
+ * Based on: @actual-app/api schema definition
9
+ * Source: packages/loot-core/src/server/aql/schema/index.ts
10
+ */
11
+ /**
12
+ * Core Actual Budget database schema
13
+ * Defines all available tables and their fields
14
+ */
15
+ export const ACTUAL_SCHEMA = {
16
+ transactions: {
17
+ id: { type: 'id' },
18
+ is_parent: { type: 'boolean' },
19
+ is_child: { type: 'boolean' },
20
+ parent_id: { type: 'id' },
21
+ account: { type: 'id', ref: 'accounts', required: true },
22
+ category: { type: 'id', ref: 'categories' },
23
+ amount: { type: 'integer', default: 0, required: true },
24
+ payee: { type: 'id', ref: 'payees' },
25
+ notes: { type: 'string' },
26
+ date: { type: 'date', required: true },
27
+ imported_id: { type: 'string' },
28
+ imported_payee: { type: 'string' },
29
+ error: { type: 'json' },
30
+ cleared: { type: 'boolean' },
31
+ pending: { type: 'boolean' },
32
+ transfer_id: { type: 'id' },
33
+ sort_order: { type: 'float' },
34
+ starting_balance_flag: { type: 'boolean' },
35
+ reconciled: { type: 'boolean' },
36
+ schedule: { type: 'id', ref: 'schedules' },
37
+ tombstone: { type: 'boolean' },
38
+ },
39
+ accounts: {
40
+ id: { type: 'id' },
41
+ name: { type: 'string', required: true },
42
+ type: { type: 'string' },
43
+ offbudget: { type: 'boolean' },
44
+ closed: { type: 'boolean' },
45
+ sort_order: { type: 'float' },
46
+ tombstone: { type: 'boolean' },
47
+ account_id: { type: 'string' },
48
+ official_name: { type: 'string' },
49
+ balance_current: { type: 'integer' },
50
+ balance_available: { type: 'integer' },
51
+ balance_limit: { type: 'integer' },
52
+ mask: { type: 'string' },
53
+ bank: { type: 'id', ref: 'banks' },
54
+ account_sync_source: { type: 'string' },
55
+ },
56
+ categories: {
57
+ id: { type: 'id' },
58
+ name: { type: 'string', required: true },
59
+ is_income: { type: 'boolean', required: true },
60
+ group: { type: 'id', ref: 'category_groups', required: true },
61
+ sort_order: { type: 'float' },
62
+ tombstone: { type: 'boolean' },
63
+ hidden: { type: 'boolean' },
64
+ goal_def: { type: 'json' },
65
+ },
66
+ category_groups: {
67
+ id: { type: 'id' },
68
+ name: { type: 'string', required: true },
69
+ is_income: { type: 'boolean', required: true },
70
+ sort_order: { type: 'float' },
71
+ tombstone: { type: 'boolean' },
72
+ hidden: { type: 'boolean' },
73
+ },
74
+ payees: {
75
+ id: { type: 'id' },
76
+ name: { type: 'string', required: true },
77
+ category: { type: 'id', ref: 'categories' },
78
+ tombstone: { type: 'boolean' },
79
+ transfer_acct: { type: 'id', ref: 'accounts' },
80
+ },
81
+ schedules: {
82
+ id: { type: 'id' },
83
+ name: { type: 'string' },
84
+ rule: { type: 'id', ref: 'rules', required: true },
85
+ next_date: { type: 'date' },
86
+ completed: { type: 'boolean' },
87
+ posts_transaction: { type: 'boolean' },
88
+ tombstone: { type: 'boolean' },
89
+ _payee: { type: 'id', ref: 'payees' },
90
+ _account: { type: 'id', ref: 'accounts' },
91
+ _amount: { type: 'json/fallback' },
92
+ _amountOp: { type: 'string' },
93
+ _date: { type: 'json/fallback' },
94
+ _conditions: { type: 'json' },
95
+ _actions: { type: 'json' },
96
+ },
97
+ rules: {
98
+ id: { type: 'id' },
99
+ stage: { type: 'string' },
100
+ conditions_op: { type: 'string' },
101
+ conditions: { type: 'json' },
102
+ actions: { type: 'json' },
103
+ tombstone: { type: 'boolean' },
104
+ },
105
+ notes: {
106
+ id: { type: 'id' },
107
+ note: { type: 'string' },
108
+ },
109
+ banks: {
110
+ id: { type: 'id' },
111
+ bank_id: { type: 'string' },
112
+ name: { type: 'string' },
113
+ tombstone: { type: 'boolean' },
114
+ },
115
+ preferences: {
116
+ id: { type: 'id' },
117
+ value: { type: 'string' },
118
+ },
119
+ transaction_filters: {
120
+ id: { type: 'id' },
121
+ name: { type: 'string' },
122
+ conditions_op: { type: 'string' },
123
+ conditions: { type: 'json' },
124
+ tombstone: { type: 'boolean' },
125
+ },
126
+ custom_reports: {
127
+ id: { type: 'id' },
128
+ name: { type: 'string' },
129
+ start_date: { type: 'string', default: '2023-06' },
130
+ end_date: { type: 'string', default: '2023-09' },
131
+ date_static: { type: 'integer', default: 0 },
132
+ date_range: { type: 'string' },
133
+ mode: { type: 'string', default: 'total' },
134
+ group_by: { type: 'string', default: 'Category' },
135
+ sort_by: { type: 'string', default: 'desc' },
136
+ balance_type: { type: 'string', default: 'Expense' },
137
+ show_empty: { type: 'integer', default: 0 },
138
+ show_offbudget: { type: 'integer', default: 0 },
139
+ show_hidden: { type: 'integer', default: 0 },
140
+ show_uncategorized: { type: 'integer', default: 0 },
141
+ trim_intervals: { type: 'integer', default: 0 },
142
+ include_current: { type: 'integer', default: 0 },
143
+ graph_type: { type: 'string', default: 'BarGraph' },
144
+ conditions: { type: 'json' },
145
+ conditions_op: { type: 'string' },
146
+ metadata: { type: 'json' },
147
+ interval: { type: 'string', default: 'Monthly' },
148
+ color_scheme: { type: 'json' },
149
+ tombstone: { type: 'boolean' },
150
+ },
151
+ dashboard: {
152
+ id: { type: 'id' },
153
+ type: { type: 'string', required: true },
154
+ width: { type: 'integer', required: true },
155
+ height: { type: 'integer', required: true },
156
+ x: { type: 'integer', required: true },
157
+ y: { type: 'integer', required: true },
158
+ meta: { type: 'json' },
159
+ tombstone: { type: 'boolean' },
160
+ },
161
+ };
162
+ /**
163
+ * Common join paths for transactions
164
+ * Format: "field.joinField" maps to referenced table
165
+ */
166
+ export const JOIN_PATHS = {
167
+ 'payee.name': { table: 'payees', field: 'name' },
168
+ 'payee.category': { table: 'payees', field: 'category' },
169
+ 'category.name': { table: 'categories', field: 'name' },
170
+ 'category.group': { table: 'categories', field: 'group' },
171
+ 'category.is_income': { table: 'categories', field: 'is_income' },
172
+ 'category.hidden': { table: 'categories', field: 'hidden' },
173
+ 'account.name': { table: 'accounts', field: 'name' },
174
+ 'account.type': { table: 'accounts', field: 'type' },
175
+ 'account.offbudget': { table: 'accounts', field: 'offbudget' },
176
+ 'account.closed': { table: 'accounts', field: 'closed' },
177
+ };
178
+ /**
179
+ * Get all valid field names for a table
180
+ */
181
+ export function getTableFields(tableName) {
182
+ const table = ACTUAL_SCHEMA[tableName];
183
+ if (!table)
184
+ return null;
185
+ return Object.keys(table);
186
+ }
187
+ /**
188
+ * Get all valid table names
189
+ */
190
+ export function getTableNames() {
191
+ return Object.keys(ACTUAL_SCHEMA);
192
+ }
193
+ /**
194
+ * Check if a table exists
195
+ */
196
+ export function isValidTable(tableName) {
197
+ return tableName in ACTUAL_SCHEMA;
198
+ }
199
+ /**
200
+ * Check if a field exists in a table
201
+ */
202
+ export function isValidField(tableName, fieldName) {
203
+ const table = ACTUAL_SCHEMA[tableName];
204
+ if (!table)
205
+ return false;
206
+ return fieldName in table;
207
+ }
208
+ /**
209
+ * Check if a join path is valid
210
+ */
211
+ export function isValidJoinPath(path) {
212
+ return path in JOIN_PATHS;
213
+ }
214
+ /**
215
+ * Get field type information
216
+ */
217
+ export function getFieldType(tableName, fieldName) {
218
+ const table = ACTUAL_SCHEMA[tableName];
219
+ if (!table || !(fieldName in table))
220
+ return null;
221
+ return table[fieldName].type;
222
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Budget Registry — pre-configured multi-budget, multi-server support.
3
+ *
4
+ * Budgets are declared via environment variables:
5
+ *
6
+ * # Default budget (always present, from existing ACTUAL_* vars)
7
+ * BUDGET_DEFAULT_NAME=My Budget # optional, defaults to "Default"
8
+ * ACTUAL_SERVER_URL=http://actual:5006
9
+ * ACTUAL_PASSWORD=secret
10
+ * ACTUAL_BUDGET_SYNC_ID=uuid-here
11
+ * ACTUAL_BUDGET_PASSWORD= # optional, for E2E-encrypted budgets
12
+ *
13
+ * # Additional named budgets — each group can point to a different server
14
+ * BUDGET_1_NAME=Shared Family Account
15
+ * BUDGET_1_SERVER_URL=http://actual:5006 # optional, falls back to ACTUAL_SERVER_URL
16
+ * BUDGET_1_PASSWORD=secret # optional, falls back to ACTUAL_PASSWORD
17
+ * BUDGET_1_SYNC_ID=uuid-here # required
18
+ * BUDGET_1_ENCRYPTION_PASSWORD= # optional
19
+ *
20
+ * BUDGET_2_NAME=Office
21
+ * BUDGET_2_SERVER_URL=http://actual-office:5006
22
+ * BUDGET_2_PASSWORD=officepassword
23
+ * BUDGET_2_SYNC_ID=uuid-here
24
+ *
25
+ * The AI uses `actual_budgets_list_available` to see all configured budgets,
26
+ * then `actual_budgets_switch` with the budget name to switch between them.
27
+ */
28
+ /**
29
+ * Parse the budget registry from environment variables.
30
+ * Always includes the default budget from the provided defaults (ACTUAL_* vars).
31
+ * Additional budgets are read from sequential BUDGET_n_* groups.
32
+ */
33
+ export function parseBudgetRegistry(env, defaults) {
34
+ const registry = new Map();
35
+ const defaultName = env.BUDGET_DEFAULT_NAME ?? 'Default';
36
+ registry.set(defaultName.toLowerCase(), {
37
+ name: defaultName,
38
+ serverUrl: defaults.serverUrl,
39
+ password: defaults.password,
40
+ syncId: defaults.syncId,
41
+ encryptionPassword: defaults.encryptionPassword,
42
+ });
43
+ let i = 1;
44
+ while (env[`BUDGET_${i}_NAME`]) {
45
+ const prefix = `BUDGET_${i}_`;
46
+ const name = env[`${prefix}NAME`];
47
+ const serverUrl = env[`${prefix}SERVER_URL`] ?? defaults.serverUrl;
48
+ const password = env[`${prefix}PASSWORD`] ?? defaults.password;
49
+ const syncId = env[`${prefix}SYNC_ID`];
50
+ if (!syncId) {
51
+ console.error(`[CONFIG] BUDGET_${i}_SYNC_ID is required when BUDGET_${i}_NAME="${name}" is set`);
52
+ process.exit(1);
53
+ }
54
+ registry.set(name.toLowerCase(), {
55
+ name,
56
+ serverUrl,
57
+ password,
58
+ syncId,
59
+ encryptionPassword: env[`${prefix}ENCRYPTION_PASSWORD`],
60
+ });
61
+ i++;
62
+ }
63
+ return registry;
64
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Application-wide constants
3
+ *
4
+ * Centralized constant values for retry logic, timeouts, limits, and other
5
+ * configuration that should remain consistent across the application.
6
+ */
7
+ // ============================================================================
8
+ // RETRY & RESILIENCE
9
+ // ============================================================================
10
+ /**
11
+ * Default number of retry attempts for transient failures
12
+ */
13
+ export const DEFAULT_RETRY_ATTEMPTS = 3;
14
+ /**
15
+ * Initial backoff delay in milliseconds for exponential backoff retry
16
+ */
17
+ export const DEFAULT_RETRY_BACKOFF_MS = 200;
18
+ /**
19
+ * Maximum delay between retries (prevents unbounded exponential growth)
20
+ */
21
+ export const MAX_RETRY_DELAY_MS = 10000;
22
+ // ============================================================================
23
+ // CONCURRENCY & RATE LIMITING
24
+ // ============================================================================
25
+ /**
26
+ * Default concurrency limit for Actual Budget API operations
27
+ * Prevents overwhelming the API with too many simultaneous requests
28
+ */
29
+ export const DEFAULT_CONCURRENCY_LIMIT = 5;
30
+ // ============================================================================
31
+ // TIMEOUTS
32
+ // ============================================================================
33
+ /**
34
+ * Default timeout for API operations in milliseconds
35
+ */
36
+ export const DEFAULT_OPERATION_TIMEOUT_MS = 30000;
37
+ /**
38
+ * Timeout for server shutdown grace period
39
+ */
40
+ export const SHUTDOWN_GRACE_PERIOD_MS = 5000;
41
+ /**
42
+ * How long (ms) to wait after calling rawRunBankSync for the SDK's background
43
+ * promise to surface a BankSyncError as an unhandledRejection.
44
+ *
45
+ * Bank provider errors (GoCardless RATE_LIMIT_EXCEEDED, auth failures, etc.)
46
+ * arrive as HTTP responses. Fast banks respond within 1-3 seconds; slower
47
+ * institutions can take considerably longer. 30 seconds gives a comfortable
48
+ * margin while keeping the tool's wall-clock time acceptable for MCP clients.
49
+ */
50
+ export const BANK_SYNC_SETTLE_MS = 30_000;
51
+ /**
52
+ * How long (ms) to wait for additional queued writes before closing the
53
+ * shared budget session. Increasing this value batches more writes per
54
+ * session at the cost of slightly higher latency.
55
+ */
56
+ export const WRITE_SESSION_DELAY_MS = 100;
57
+ // ============================================================================
58
+ // MCP SERVER
59
+ // ============================================================================
60
+ /**
61
+ * Default HTTP server bind host
62
+ */
63
+ export const DEFAULT_BIND_HOST = 'localhost';
64
+ /**
65
+ * Default HTTP port for MCP server
66
+ */
67
+ export const DEFAULT_HTTP_PORT = 3000;
68
+ /**
69
+ * Default HTTP path for MCP server endpoint
70
+ */
71
+ export const DEFAULT_HTTP_PATH = '/';
72
+ /**
73
+ * Server information
74
+ * Note: version is read from package.json at startup and passed as a parameter.
75
+ */
76
+ export const SERVER_INFO = {
77
+ name: 'actual-budget-mcp',
78
+ description: 'MCP server for Actual Budget - 60 tools for finance management',
79
+ };
80
+ // ============================================================================
81
+ // VALIDATION & LIMITS
82
+ // ============================================================================
83
+ /**
84
+ * Maximum length for name fields (accounts, categories, payees)
85
+ */
86
+ export const MAX_NAME_LENGTH = 255;
87
+ /**
88
+ * Maximum length for notes/description fields
89
+ */
90
+ export const MAX_NOTES_LENGTH = 1000;
91
+ /**
92
+ * Date format pattern (YYYY-MM-DD)
93
+ */
94
+ export const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
95
+ /**
96
+ * Month format pattern (YYYY-MM)
97
+ */
98
+ export const MONTH_PATTERN = /^\d{4}-\d{2}$/;
99
+ /**
100
+ * UUID pattern for ID validation
101
+ */
102
+ export const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
103
+ // ============================================================================
104
+ // LOGGING
105
+ // ============================================================================
106
+ /**
107
+ * Log level for production environments
108
+ */
109
+ export const PRODUCTION_LOG_LEVEL = 'info';
110
+ /**
111
+ * Log level for development environments
112
+ */
113
+ export const DEVELOPMENT_LOG_LEVEL = 'debug';
114
+ /**
115
+ * Maximum number of log files to retain
116
+ */
117
+ export const MAX_LOG_FILES = 14;
118
+ /**
119
+ * Maximum size of a single log file before rotation
120
+ */
121
+ export const MAX_LOG_FILE_SIZE = '20m';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Build a "not found" error message with a next-step hint.
3
+ * @param entityType Human-readable entity name, e.g. "Account", "Category"
4
+ * @param id The ID that was not found
5
+ * @param listTool MCP tool name the caller should use to get valid IDs
6
+ */
7
+ export function notFoundMsg(entityType, id, listTool) {
8
+ return `${entityType} "${id}" not found. Use ${listTool} to list available ${entityType.toLowerCase()}s.`;
9
+ }
10
+ /**
11
+ * Build a "constraint error" message for SQLite-level failures.
12
+ * @param entityType Human-readable entity name
13
+ * @param id The ID that failed
14
+ * @param listTool MCP tool name for listing
15
+ */
16
+ export function constraintErrorMsg(entityType, id, listTool) {
17
+ return `Failed to delete ${entityType.toLowerCase()} "${id}". ` +
18
+ `It may be referenced by other records. Use ${listTool} to verify it exists.`;
19
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Module Logger Factory
3
+ *
4
+ * Creates module-specific loggers with consistent formatting.
5
+ * Each logger automatically prefixes messages with [MODULE_NAME] for
6
+ * easier tracing and debugging in production.
7
+ */
8
+ import logger from '../logger.js';
9
+ /**
10
+ * Create a module-specific logger
11
+ *
12
+ * @param moduleName - Name of the module (e.g., 'HTTP', 'ADAPTER', 'TOOLS')
13
+ * @returns Module logger with automatic prefixing
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { createModuleLogger } from '../lib/loggerFactory.js';
18
+ *
19
+ * const log = createModuleLogger('HTTP');
20
+ * log.info('Server started', { port: 3000 });
21
+ * // Output: [HTTP] Server started { port: 3000 }
22
+ *
23
+ * log.error('Connection failed', new Error('Timeout'), { retries: 3 });
24
+ * // Output: [HTTP] Connection failed { error: 'Timeout', stack: '...', retries: 3 }
25
+ * ```
26
+ */
27
+ export function createModuleLogger(moduleName) {
28
+ const prefix = `[${moduleName}]`;
29
+ return {
30
+ info: (message, meta) => {
31
+ logger.info(`${prefix} ${message}`, meta);
32
+ },
33
+ debug: (message, meta) => {
34
+ logger.debug(`${prefix} ${message}`, meta);
35
+ },
36
+ warn: (message, meta) => {
37
+ logger.warn(`${prefix} ${message}`, meta);
38
+ },
39
+ error: (message, error, meta) => {
40
+ if (error) {
41
+ logger.error(`${prefix} ${message}`, {
42
+ error: error.message,
43
+ stack: error.stack,
44
+ ...meta,
45
+ });
46
+ }
47
+ else {
48
+ logger.error(`${prefix} ${message}`, meta);
49
+ }
50
+ },
51
+ };
52
+ }
53
+ /**
54
+ * Pre-configured module loggers for common components
55
+ * Can be imported directly for convenience
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { ModuleLoggers } from '../lib/loggerFactory.js';
60
+ *
61
+ * ModuleLoggers.HTTP.info('Request received');
62
+ * ModuleLoggers.ADAPTER.debug('Processing transaction');
63
+ * ```
64
+ */
65
+ export const ModuleLoggers = {
66
+ HTTP: createModuleLogger('HTTP'),
67
+ ADAPTER: createModuleLogger('ADAPTER'),
68
+ TOOLS: createModuleLogger('TOOLS'),
69
+ SESSION: createModuleLogger('SESSION'),
70
+ CONNECTION: createModuleLogger('CONNECTION'),
71
+ RETRY: createModuleLogger('RETRY'),
72
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Node.js polyfills for browser globals required by @actual-app/api.
3
+ *
4
+ * @actual-app/api v26.3.0 introduced `navigator.platform` usage at the module
5
+ * top level (inside the Electron/browser bundle) which crashes on Node.js with
6
+ * `ReferenceError: navigator is not defined`.
7
+ *
8
+ * This file must be imported BEFORE any `@actual-app/api` import so that the
9
+ * global is defined when the bundle is first evaluated.
10
+ */
11
+ if (typeof globalThis.navigator === 'undefined') {
12
+ Object.defineProperty(globalThis, 'navigator', {
13
+ value: {
14
+ platform: process.platform === 'win32' ? 'Win32' : 'Linux',
15
+ },
16
+ writable: true,
17
+ configurable: true,
18
+ });
19
+ }
20
+ export {};