@shin1ohno/sage 0.8.8 → 0.9.1

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 (78) hide show
  1. package/README.md +31 -0
  2. package/dist/cli/http-server-with-config.d.ts +2 -0
  3. package/dist/cli/http-server-with-config.d.ts.map +1 -1
  4. package/dist/cli/http-server-with-config.js +70 -0
  5. package/dist/cli/http-server-with-config.js.map +1 -1
  6. package/dist/cli/main-entry.d.ts.map +1 -1
  7. package/dist/cli/main-entry.js +1 -0
  8. package/dist/cli/main-entry.js.map +1 -1
  9. package/dist/cli/parser.d.ts +2 -0
  10. package/dist/cli/parser.d.ts.map +1 -1
  11. package/dist/cli/parser.js +7 -0
  12. package/dist/cli/parser.js.map +1 -1
  13. package/dist/config/validation.d.ts +721 -0
  14. package/dist/config/validation.d.ts.map +1 -1
  15. package/dist/config/validation.js +203 -0
  16. package/dist/config/validation.js.map +1 -1
  17. package/dist/integrations/calendar-service.d.ts +14 -0
  18. package/dist/integrations/calendar-service.d.ts.map +1 -1
  19. package/dist/integrations/calendar-service.js.map +1 -1
  20. package/dist/integrations/calendar-source-manager.d.ts +86 -3
  21. package/dist/integrations/calendar-source-manager.d.ts.map +1 -1
  22. package/dist/integrations/calendar-source-manager.js +146 -15
  23. package/dist/integrations/calendar-source-manager.js.map +1 -1
  24. package/dist/integrations/google-calendar-service.d.ts +55 -3
  25. package/dist/integrations/google-calendar-service.d.ts.map +1 -1
  26. package/dist/integrations/google-calendar-service.js +213 -4
  27. package/dist/integrations/google-calendar-service.js.map +1 -1
  28. package/dist/oauth/encryption-service.d.ts +104 -0
  29. package/dist/oauth/encryption-service.d.ts.map +1 -0
  30. package/dist/oauth/encryption-service.js +271 -0
  31. package/dist/oauth/encryption-service.js.map +1 -0
  32. package/dist/oauth/file-mutex.d.ts +68 -0
  33. package/dist/oauth/file-mutex.d.ts.map +1 -0
  34. package/dist/oauth/file-mutex.js +140 -0
  35. package/dist/oauth/file-mutex.js.map +1 -0
  36. package/dist/oauth/google-oauth-handler.d.ts +8 -9
  37. package/dist/oauth/google-oauth-handler.d.ts.map +1 -1
  38. package/dist/oauth/google-oauth-handler.js +30 -65
  39. package/dist/oauth/google-oauth-handler.js.map +1 -1
  40. package/dist/oauth/index.d.ts +1 -0
  41. package/dist/oauth/index.d.ts.map +1 -1
  42. package/dist/oauth/index.js +2 -0
  43. package/dist/oauth/index.js.map +1 -1
  44. package/dist/oauth/oauth-server.d.ts +61 -1
  45. package/dist/oauth/oauth-server.d.ts.map +1 -1
  46. package/dist/oauth/oauth-server.js +181 -40
  47. package/dist/oauth/oauth-server.js.map +1 -1
  48. package/dist/oauth/persistent-client-store.d.ts +58 -0
  49. package/dist/oauth/persistent-client-store.d.ts.map +1 -0
  50. package/dist/oauth/persistent-client-store.js +187 -0
  51. package/dist/oauth/persistent-client-store.js.map +1 -0
  52. package/dist/oauth/persistent-refresh-token-store.d.ts +77 -0
  53. package/dist/oauth/persistent-refresh-token-store.d.ts.map +1 -0
  54. package/dist/oauth/persistent-refresh-token-store.js +225 -0
  55. package/dist/oauth/persistent-refresh-token-store.js.map +1 -0
  56. package/dist/oauth/persistent-session-store.d.ts +69 -0
  57. package/dist/oauth/persistent-session-store.d.ts.map +1 -0
  58. package/dist/oauth/persistent-session-store.js +154 -0
  59. package/dist/oauth/persistent-session-store.js.map +1 -0
  60. package/dist/oauth/session-store.d.ts +31 -0
  61. package/dist/oauth/session-store.d.ts.map +1 -0
  62. package/dist/oauth/session-store.js +47 -0
  63. package/dist/oauth/session-store.js.map +1 -0
  64. package/dist/services/working-cadence.d.ts +37 -1
  65. package/dist/services/working-cadence.d.ts.map +1 -1
  66. package/dist/services/working-cadence.js +151 -13
  67. package/dist/services/working-cadence.js.map +1 -1
  68. package/dist/tools/calendar/handlers.d.ts +82 -3
  69. package/dist/tools/calendar/handlers.d.ts.map +1 -1
  70. package/dist/tools/calendar/handlers.js +200 -16
  71. package/dist/tools/calendar/handlers.js.map +1 -1
  72. package/dist/types/google-calendar-types.d.ts +150 -3
  73. package/dist/types/google-calendar-types.d.ts.map +1 -1
  74. package/dist/types/google-calendar-types.js +79 -2
  75. package/dist/types/google-calendar-types.js.map +1 -1
  76. package/dist/types/task.d.ts +14 -0
  77. package/dist/types/task.d.ts.map +1 -1
  78. package/package.json +1 -1
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Encryption Service for OAuth Data
3
+ * Requirements: FR-4 (Encryption Key Management)
4
+ *
5
+ * Provides AES-256-GCM encryption/decryption for sensitive OAuth data.
6
+ * Supports key derivation via scrypt and secure key management.
7
+ */
8
+ import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';
9
+ import { promisify } from 'util';
10
+ import { readFile, writeFile, mkdir, chmod, unlink, rename } from 'fs/promises';
11
+ import { join } from 'path';
12
+ import { homedir } from 'os';
13
+ import { existsSync } from 'fs';
14
+ import { FileMutex } from './file-mutex.js';
15
+ const scryptAsync = promisify(scrypt);
16
+ /**
17
+ * Encryption Service Class
18
+ *
19
+ * Handles AES-256-GCM encryption/decryption for OAuth data persistence.
20
+ * Manages encryption key lifecycle and secure file operations.
21
+ */
22
+ export class EncryptionService {
23
+ encryptionKey;
24
+ keyStoragePath;
25
+ initialized = false;
26
+ fileMutex = new FileMutex();
27
+ constructor(config = {}) {
28
+ this.keyStoragePath = config.keyStoragePath || join(homedir(), '.sage', 'oauth_encryption_key');
29
+ this.encryptionKey = config.encryptionKey || '';
30
+ }
31
+ /**
32
+ * Initialize encryption service and load/generate key
33
+ *
34
+ * Key loading priority:
35
+ * 1. SAGE_ENCRYPTION_KEY environment variable (highest priority)
36
+ * 2. Persistent key file at ~/.sage/oauth_encryption_key
37
+ * 3. Generate new key and save (with warning)
38
+ */
39
+ async initialize() {
40
+ if (this.initialized) {
41
+ return; // Already initialized
42
+ }
43
+ // Priority 1: Use SAGE_ENCRYPTION_KEY environment variable
44
+ if (process.env.SAGE_ENCRYPTION_KEY) {
45
+ this.encryptionKey = process.env.SAGE_ENCRYPTION_KEY;
46
+ console.log('[OAuth] Using encryption key from SAGE_ENCRYPTION_KEY environment variable');
47
+ this.initialized = true;
48
+ return;
49
+ }
50
+ // Priority 2: Load existing key from storage
51
+ if (existsSync(this.keyStoragePath)) {
52
+ try {
53
+ this.encryptionKey = (await readFile(this.keyStoragePath, 'utf-8')).trim();
54
+ console.log('[OAuth] Loaded encryption key from storage');
55
+ this.initialized = true;
56
+ return;
57
+ }
58
+ catch (error) {
59
+ console.error('[OAuth] Failed to load encryption key:', error);
60
+ // Fall through to generation
61
+ }
62
+ }
63
+ // Priority 3: Generate new key and store it
64
+ console.warn('[OAuth] No encryption key found. Generating new key...');
65
+ console.warn('[OAuth] Warning: Set SAGE_ENCRYPTION_KEY environment variable for production use');
66
+ this.encryptionKey = randomBytes(32).toString('hex');
67
+ // Ensure directory exists with secure permissions
68
+ const dir = join(homedir(), '.sage');
69
+ await mkdir(dir, { recursive: true, mode: 0o700 });
70
+ // Write key with restricted permissions (read/write for owner only)
71
+ await writeFile(this.keyStoragePath, this.encryptionKey, { mode: 0o600 });
72
+ // Ensure permissions are set correctly (some systems ignore mode in writeFile)
73
+ try {
74
+ await chmod(this.keyStoragePath, 0o600);
75
+ }
76
+ catch (error) {
77
+ // chmod may fail on Windows, log but continue
78
+ console.warn('[OAuth] Could not set file permissions (may not be supported on this OS)');
79
+ }
80
+ console.log(`[OAuth] Generated encryption key stored at: ${this.keyStoragePath}`);
81
+ this.initialized = true;
82
+ }
83
+ /**
84
+ * Encrypt data using AES-256-GCM
85
+ *
86
+ * Format: salt:iv:authTag:encrypted
87
+ *
88
+ * @param data - Plain text data to encrypt
89
+ * @returns Encrypted data in format "salt:iv:authTag:encrypted"
90
+ */
91
+ async encrypt(data) {
92
+ if (!this.initialized) {
93
+ throw new Error('EncryptionService not initialized. Call initialize() first.');
94
+ }
95
+ try {
96
+ // Generate salt and derive key using scrypt
97
+ const salt = randomBytes(16);
98
+ const key = (await scryptAsync(this.encryptionKey, salt, 32));
99
+ // Generate initialization vector
100
+ const iv = randomBytes(16);
101
+ // Create cipher
102
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
103
+ // Encrypt data
104
+ let encrypted = cipher.update(data, 'utf8', 'hex');
105
+ encrypted += cipher.final('hex');
106
+ // Get authentication tag for integrity verification
107
+ const authTag = cipher.getAuthTag();
108
+ // Combine: salt:iv:authTag:encrypted
109
+ return `${salt.toString('hex')}:${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
110
+ }
111
+ catch (error) {
112
+ console.error('[OAuth] Encryption failed:', error);
113
+ throw new Error('Failed to encrypt data');
114
+ }
115
+ }
116
+ /**
117
+ * Decrypt data using AES-256-GCM
118
+ *
119
+ * @param encryptedData - Encrypted data in format "salt:iv:authTag:encrypted"
120
+ * @returns Decrypted plain text data
121
+ */
122
+ async decrypt(encryptedData) {
123
+ if (!this.initialized) {
124
+ throw new Error('EncryptionService not initialized. Call initialize() first.');
125
+ }
126
+ try {
127
+ // Split encrypted data
128
+ const parts = encryptedData.split(':');
129
+ if (parts.length !== 4) {
130
+ throw new Error('Invalid encrypted data format');
131
+ }
132
+ const [saltHex, ivHex, authTagHex, encrypted] = parts;
133
+ // Convert from hex
134
+ const salt = Buffer.from(saltHex, 'hex');
135
+ const iv = Buffer.from(ivHex, 'hex');
136
+ const authTag = Buffer.from(authTagHex, 'hex');
137
+ // Derive key from encryption key using scrypt
138
+ const key = (await scryptAsync(this.encryptionKey, salt, 32));
139
+ // Create decipher
140
+ const decipher = createDecipheriv('aes-256-gcm', key, iv);
141
+ decipher.setAuthTag(authTag);
142
+ // Decrypt data
143
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
144
+ decrypted += decipher.final('utf8');
145
+ return decrypted;
146
+ }
147
+ catch (error) {
148
+ console.error('[OAuth] Decryption failed:', error);
149
+ throw new Error('Failed to decrypt data');
150
+ }
151
+ }
152
+ /**
153
+ * Encrypt and save data to file
154
+ *
155
+ * Uses atomic write pattern (temp file + rename) to prevent corruption.
156
+ * Serialized with mutex to prevent concurrent write race conditions.
157
+ *
158
+ * @param data - Plain text data to encrypt and save
159
+ * @param filePath - Destination file path
160
+ */
161
+ async encryptToFile(data, filePath) {
162
+ await this.fileMutex.withLock(filePath, async () => {
163
+ const encrypted = await this.encrypt(data);
164
+ // Ensure directory exists with secure permissions
165
+ const { dirname } = require('path');
166
+ const dir = dirname(filePath);
167
+ await mkdir(dir, { recursive: true, mode: 0o700 });
168
+ // Atomic write using temp file + rename pattern
169
+ const tempPath = `${filePath}.tmp`;
170
+ try {
171
+ // Write to temp file with restricted permissions
172
+ await writeFile(tempPath, encrypted, { mode: 0o600 });
173
+ // Ensure permissions are set (some systems ignore mode in writeFile)
174
+ try {
175
+ await chmod(tempPath, 0o600);
176
+ }
177
+ catch (error) {
178
+ // chmod may fail on Windows, log but continue
179
+ console.warn('[OAuth] Could not set file permissions (may not be supported on this OS)');
180
+ }
181
+ // Rename is atomic on most filesystems - prevents corruption
182
+ await rename(tempPath, filePath);
183
+ }
184
+ catch (error) {
185
+ console.error(`[OAuth] Failed to write ${filePath}:`, error);
186
+ // Clean up temp file if it exists
187
+ try {
188
+ await unlink(tempPath);
189
+ }
190
+ catch {
191
+ // Ignore cleanup errors
192
+ }
193
+ throw error;
194
+ }
195
+ });
196
+ }
197
+ /**
198
+ * Load and decrypt data from file
199
+ *
200
+ * Serialized with mutex to prevent read-during-write issues.
201
+ *
202
+ * @param filePath - Source file path
203
+ * @returns Decrypted data or null if file doesn't exist
204
+ */
205
+ async decryptFromFile(filePath) {
206
+ return await this.fileMutex.withLock(filePath, async () => {
207
+ try {
208
+ if (!existsSync(filePath)) {
209
+ return null;
210
+ }
211
+ const encrypted = await readFile(filePath, 'utf-8');
212
+ return await this.decrypt(encrypted);
213
+ }
214
+ catch (error) {
215
+ console.error(`[OAuth] Failed to decrypt file ${filePath}:`, error);
216
+ return null;
217
+ }
218
+ });
219
+ }
220
+ /**
221
+ * Check if encryption service is initialized
222
+ */
223
+ isInitialized() {
224
+ return this.initialized;
225
+ }
226
+ /**
227
+ * Get the storage path for the encryption key
228
+ */
229
+ getKeyStoragePath() {
230
+ return this.keyStoragePath;
231
+ }
232
+ /**
233
+ * Get health status for monitoring
234
+ */
235
+ getHealthStatus() {
236
+ let keySource = 'generated';
237
+ if (process.env.SAGE_ENCRYPTION_KEY) {
238
+ keySource = 'environment';
239
+ }
240
+ else if (existsSync(this.keyStoragePath)) {
241
+ keySource = 'file';
242
+ }
243
+ return {
244
+ initialized: this.initialized,
245
+ keySource,
246
+ keyStoragePath: this.keyStoragePath,
247
+ mutex: this.fileMutex.getMetrics(),
248
+ };
249
+ }
250
+ /**
251
+ * Get mutex metrics for monitoring
252
+ */
253
+ getMutexMetrics() {
254
+ return this.fileMutex.getMetrics();
255
+ }
256
+ /**
257
+ * Wait for all pending file operations to complete
258
+ *
259
+ * Used for graceful shutdown to ensure all data is persisted.
260
+ */
261
+ async waitForPendingWrites() {
262
+ await this.fileMutex.waitForPending();
263
+ }
264
+ /**
265
+ * Check if there are pending file operations
266
+ */
267
+ hasPendingWrites() {
268
+ return this.fileMutex.hasPendingOperations();
269
+ }
270
+ }
271
+ //# sourceMappingURL=encryption-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption-service.js","sourceRoot":"","sources":["../../src/oauth/encryption-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * File Mutex for Serializing File Operations
3
+ * Requirements: FR-1 (File Write Serialization), FR-2 (Per-File Mutex)
4
+ *
5
+ * Provides per-file mutex to prevent race conditions during concurrent
6
+ * encrypted file operations. Uses Promise queue pattern for serialization.
7
+ */
8
+ /**
9
+ * Metrics for monitoring mutex performance
10
+ */
11
+ export interface FileMutexMetrics {
12
+ activeFiles: number;
13
+ totalWaitTimeMs: number;
14
+ longestWaitMs: number;
15
+ queueDepthWarnings: number;
16
+ }
17
+ /**
18
+ * File Mutex Class
19
+ *
20
+ * Provides per-file locking mechanism to serialize file operations.
21
+ * Each file path gets its own independent mutex, allowing parallel
22
+ * operations on different files while serializing operations on the same file.
23
+ */
24
+ export declare class FileMutex {
25
+ private locks;
26
+ private metrics;
27
+ private static readonly QUEUE_DEPTH_WARNING_THRESHOLD;
28
+ private static readonly WAIT_TIME_WARNING_THRESHOLD_MS;
29
+ /**
30
+ * Execute an operation with exclusive lock on the specified file
31
+ *
32
+ * @param filePath - Path to the file to lock
33
+ * @param operation - Async operation to execute while holding the lock
34
+ * @returns Result of the operation
35
+ */
36
+ withLock<T>(filePath: string, operation: () => Promise<T>): Promise<T>;
37
+ /**
38
+ * Normalize file path for consistent lock identification
39
+ */
40
+ private normalizePath;
41
+ /**
42
+ * Check and warn if queue depth exceeds threshold
43
+ */
44
+ private checkQueueWarnings;
45
+ /**
46
+ * Record wait time and log if exceeds thresholds
47
+ */
48
+ private recordWaitTime;
49
+ /**
50
+ * Get metrics for monitoring/debugging
51
+ */
52
+ getMetrics(): FileMutexMetrics;
53
+ /**
54
+ * Check if there are pending operations for any file
55
+ */
56
+ hasPendingOperations(): boolean;
57
+ /**
58
+ * Wait for all pending operations to complete
59
+ *
60
+ * Used for graceful shutdown to ensure all queued operations finish.
61
+ */
62
+ waitForPending(): Promise<void>;
63
+ /**
64
+ * Reset metrics (primarily for testing)
65
+ */
66
+ resetMetrics(): void;
67
+ }
68
+ //# sourceMappingURL=file-mutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-mutex.d.ts","sourceRoot":"","sources":["../../src/oauth/file-mutex.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAaD;;;;;;GAMG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,OAAO,CAKb;IAGF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CAAM;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAQ;IAE9D;;;;;;OAMG;IACG,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyC5E;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;OAEG;IACH,UAAU,IAAI,gBAAgB;IAI9B;;OAEG;IACH,oBAAoB,IAAI,OAAO;IAS/B;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC;;OAEG;IACH,YAAY,IAAI,IAAI;CAQrB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * File Mutex for Serializing File Operations
3
+ * Requirements: FR-1 (File Write Serialization), FR-2 (Per-File Mutex)
4
+ *
5
+ * Provides per-file mutex to prevent race conditions during concurrent
6
+ * encrypted file operations. Uses Promise queue pattern for serialization.
7
+ */
8
+ import { resolve } from 'path';
9
+ /**
10
+ * File Mutex Class
11
+ *
12
+ * Provides per-file locking mechanism to serialize file operations.
13
+ * Each file path gets its own independent mutex, allowing parallel
14
+ * operations on different files while serializing operations on the same file.
15
+ */
16
+ export class FileMutex {
17
+ locks = new Map();
18
+ metrics = {
19
+ activeFiles: 0,
20
+ totalWaitTimeMs: 0,
21
+ longestWaitMs: 0,
22
+ queueDepthWarnings: 0,
23
+ };
24
+ // Thresholds for warnings
25
+ static QUEUE_DEPTH_WARNING_THRESHOLD = 10;
26
+ static WAIT_TIME_WARNING_THRESHOLD_MS = 5000;
27
+ /**
28
+ * Execute an operation with exclusive lock on the specified file
29
+ *
30
+ * @param filePath - Path to the file to lock
31
+ * @param operation - Async operation to execute while holding the lock
32
+ * @returns Result of the operation
33
+ */
34
+ async withLock(filePath, operation) {
35
+ const normalizedPath = this.normalizePath(filePath);
36
+ // Get or create mutex state for this file
37
+ if (!this.locks.has(normalizedPath)) {
38
+ this.locks.set(normalizedPath, { queue: [], isLocked: false });
39
+ this.metrics.activeFiles++;
40
+ }
41
+ const state = this.locks.get(normalizedPath);
42
+ // Wait if locked
43
+ if (state.isLocked) {
44
+ const queuedAt = Date.now();
45
+ await new Promise((resolvePromise) => {
46
+ state.queue.push({ resolve: resolvePromise, queuedAt });
47
+ this.checkQueueWarnings(normalizedPath, state);
48
+ });
49
+ }
50
+ // Acquire lock
51
+ state.isLocked = true;
52
+ try {
53
+ return await operation();
54
+ }
55
+ finally {
56
+ // Release lock and notify next in queue
57
+ if (state.queue.length > 0) {
58
+ const next = state.queue.shift();
59
+ const waitTime = Date.now() - next.queuedAt;
60
+ this.recordWaitTime(waitTime, normalizedPath);
61
+ next.resolve();
62
+ }
63
+ else {
64
+ state.isLocked = false;
65
+ // Clean up empty mutex state
66
+ this.locks.delete(normalizedPath);
67
+ this.metrics.activeFiles = Math.max(0, this.metrics.activeFiles - 1);
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Normalize file path for consistent lock identification
73
+ */
74
+ normalizePath(filePath) {
75
+ return resolve(filePath);
76
+ }
77
+ /**
78
+ * Check and warn if queue depth exceeds threshold
79
+ */
80
+ checkQueueWarnings(filePath, state) {
81
+ if (state.queue.length >= FileMutex.QUEUE_DEPTH_WARNING_THRESHOLD) {
82
+ this.metrics.queueDepthWarnings++;
83
+ console.warn(`[OAuth] High mutex contention on ${filePath}: ${state.queue.length} queued operations`);
84
+ }
85
+ }
86
+ /**
87
+ * Record wait time and log if exceeds thresholds
88
+ */
89
+ recordWaitTime(waitTimeMs, filePath) {
90
+ this.metrics.totalWaitTimeMs += waitTimeMs;
91
+ if (waitTimeMs > this.metrics.longestWaitMs) {
92
+ this.metrics.longestWaitMs = waitTimeMs;
93
+ }
94
+ // Log warnings for long wait times
95
+ if (waitTimeMs >= FileMutex.WAIT_TIME_WARNING_THRESHOLD_MS) {
96
+ console.warn(`[OAuth] Long mutex wait on ${filePath}: ${waitTimeMs}ms`);
97
+ }
98
+ }
99
+ /**
100
+ * Get metrics for monitoring/debugging
101
+ */
102
+ getMetrics() {
103
+ return { ...this.metrics };
104
+ }
105
+ /**
106
+ * Check if there are pending operations for any file
107
+ */
108
+ hasPendingOperations() {
109
+ for (const state of this.locks.values()) {
110
+ if (state.isLocked || state.queue.length > 0) {
111
+ return true;
112
+ }
113
+ }
114
+ return false;
115
+ }
116
+ /**
117
+ * Wait for all pending operations to complete
118
+ *
119
+ * Used for graceful shutdown to ensure all queued operations finish.
120
+ */
121
+ async waitForPending() {
122
+ // Keep checking until no more pending operations
123
+ while (this.hasPendingOperations()) {
124
+ // Wait a short time before checking again
125
+ await new Promise((resolve) => setTimeout(resolve, 10));
126
+ }
127
+ }
128
+ /**
129
+ * Reset metrics (primarily for testing)
130
+ */
131
+ resetMetrics() {
132
+ this.metrics = {
133
+ activeFiles: this.locks.size,
134
+ totalWaitTimeMs: 0,
135
+ longestWaitMs: 0,
136
+ queueDepthWarnings: 0,
137
+ };
138
+ }
139
+ }
140
+ //# sourceMappingURL=file-mutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-mutex.js","sourceRoot":"","sources":["../../src/oauth/file-mutex.ts"],"names":[],"mappings":"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"}
@@ -36,21 +36,20 @@ export declare const GOOGLE_CALENDAR_SCOPES: string[];
36
36
  export declare class GoogleOAuthHandler {
37
37
  private codeVerifier;
38
38
  private config;
39
- private readonly encryptionKey;
39
+ private readonly encryptionService;
40
40
  private readonly tokensStoragePath;
41
+ private initialized;
41
42
  constructor(config: GoogleOAuthConfig, encryptionKey?: string, userId?: string);
42
43
  /**
43
- * Create OAuth2Client instance
44
- */
45
- private createOAuth2Client;
46
- /**
47
- * Encrypt data using AES-256-GCM
44
+ * Initialize encryption service
45
+ *
46
+ * Must be called before any token storage operations.
48
47
  */
49
- private encrypt;
48
+ private ensureInitialized;
50
49
  /**
51
- * Decrypt data using AES-256-GCM
50
+ * Create OAuth2Client instance
52
51
  */
53
- private decrypt;
52
+ private createOAuth2Client;
54
53
  /**
55
54
  * Generate authorization URL with PKCE code_challenge
56
55
  *
@@ -1 +1 @@
1
- {"version":3,"file":"google-oauth-handler.d.ts","sourceRoot":"","sources":["../../src/oauth/google-oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAUnD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAYD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,UAGlC,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAE/B,MAAM,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAU9E;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;YACW,OAAO;IAsBrB;;OAEG;YACW,OAAO;IA4BrB;;;;;;;;OAQG;IACG,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBhE;;;;;;;;;;;OAWG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC;IAqC7B;;;;;;OAMG;IACG,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA6B1E;;;;;OAKG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrD;;;;;;;OAOG;IACG,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3D;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA2BpD;;;;;;OAMG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BnC;;;;;;;;OAQG;IACG,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAShE;;;;;;;;;OASG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAgCzC;;;;;;;OAOG;IACH,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAgBzD"}
1
+ {"version":3,"file":"google-oauth-handler.d.ts","sourceRoot":"","sources":["../../src/oauth/google-oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMnD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAYD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,UAGlC,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAY9E;;;;OAIG;YACW,iBAAiB;IAO/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;;;;;;OAQG;IACG,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBhE;;;;;;;;;;;OAWG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC;IAqC7B;;;;;;OAMG;IACG,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA6B1E;;;;;OAKG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrD;;;;;;;OAOG;IACG,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB3D;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA6BpD;;;;;;OAMG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BnC;;;;;;;;OAQG;IACG,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAShE;;;;;;;;;OASG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAgCzC;;;;;;;OAOG;IACH,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAgBzD"}