agent-web-interface 4.1.0 → 4.2.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 (85) hide show
  1. package/dist/src/browser/session-manager.d.ts +28 -1
  2. package/dist/src/browser/session-manager.d.ts.map +1 -1
  3. package/dist/src/browser/session-manager.js +73 -0
  4. package/dist/src/browser/session-manager.js.map +1 -1
  5. package/dist/src/index.d.ts +10 -1
  6. package/dist/src/index.d.ts.map +1 -1
  7. package/dist/src/index.js +47 -0
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/server/mcp-server.d.ts +14 -1
  10. package/dist/src/server/mcp-server.d.ts.map +1 -1
  11. package/dist/src/server/mcp-server.js +21 -6
  12. package/dist/src/server/mcp-server.js.map +1 -1
  13. package/dist/src/server/session-store.d.ts +18 -3
  14. package/dist/src/server/session-store.d.ts.map +1 -1
  15. package/dist/src/server/session-store.js +27 -3
  16. package/dist/src/server/session-store.js.map +1 -1
  17. package/dist/src/session/session-worker-binding.d.ts +57 -0
  18. package/dist/src/session/session-worker-binding.d.ts.map +1 -0
  19. package/dist/src/session/session-worker-binding.js +109 -0
  20. package/dist/src/session/session-worker-binding.js.map +1 -0
  21. package/dist/src/shared/services/logging.service.d.ts +8 -0
  22. package/dist/src/shared/services/logging.service.d.ts.map +1 -1
  23. package/dist/src/shared/services/logging.service.js +10 -0
  24. package/dist/src/shared/services/logging.service.js.map +1 -1
  25. package/dist/src/snapshot/snapshot-compiler.d.ts +14 -0
  26. package/dist/src/snapshot/snapshot-compiler.d.ts.map +1 -1
  27. package/dist/src/snapshot/snapshot-compiler.js +119 -3
  28. package/dist/src/snapshot/snapshot-compiler.js.map +1 -1
  29. package/dist/src/state/actionables-filter.d.ts.map +1 -1
  30. package/dist/src/state/actionables-filter.js +8 -30
  31. package/dist/src/state/actionables-filter.js.map +1 -1
  32. package/dist/src/state/locator-generator.d.ts.map +1 -1
  33. package/dist/src/state/locator-generator.js +1 -10
  34. package/dist/src/state/locator-generator.js.map +1 -1
  35. package/dist/src/state/node-layer.d.ts +22 -0
  36. package/dist/src/state/node-layer.d.ts.map +1 -0
  37. package/dist/src/state/node-layer.js +42 -0
  38. package/dist/src/state/node-layer.js.map +1 -0
  39. package/dist/src/state/state-manager.d.ts.map +1 -1
  40. package/dist/src/state/state-manager.js +9 -8
  41. package/dist/src/state/state-manager.js.map +1 -1
  42. package/dist/src/tools/tool-schemas.d.ts +22 -22
  43. package/dist/src/tools/tool-schemas.d.ts.map +1 -1
  44. package/dist/src/tools/tool-schemas.js.map +1 -1
  45. package/dist/src/worker/chrome-worker-process.d.ts +123 -0
  46. package/dist/src/worker/chrome-worker-process.d.ts.map +1 -0
  47. package/dist/src/worker/chrome-worker-process.js +294 -0
  48. package/dist/src/worker/chrome-worker-process.js.map +1 -0
  49. package/dist/src/worker/errors/index.d.ts +5 -0
  50. package/dist/src/worker/errors/index.d.ts.map +1 -0
  51. package/dist/src/worker/errors/index.js +5 -0
  52. package/dist/src/worker/errors/index.js.map +1 -0
  53. package/dist/src/worker/errors/worker.error.d.ts +122 -0
  54. package/dist/src/worker/errors/worker.error.d.ts.map +1 -0
  55. package/dist/src/worker/errors/worker.error.js +199 -0
  56. package/dist/src/worker/errors/worker.error.js.map +1 -0
  57. package/dist/src/worker/health-monitor.d.ts +141 -0
  58. package/dist/src/worker/health-monitor.d.ts.map +1 -0
  59. package/dist/src/worker/health-monitor.js +260 -0
  60. package/dist/src/worker/health-monitor.js.map +1 -0
  61. package/dist/src/worker/index.d.ts +16 -0
  62. package/dist/src/worker/index.d.ts.map +1 -0
  63. package/dist/src/worker/index.js +19 -0
  64. package/dist/src/worker/index.js.map +1 -0
  65. package/dist/src/worker/lease-manager.d.ts +137 -0
  66. package/dist/src/worker/lease-manager.d.ts.map +1 -0
  67. package/dist/src/worker/lease-manager.js +334 -0
  68. package/dist/src/worker/lease-manager.js.map +1 -0
  69. package/dist/src/worker/multi-tenant-config.d.ts +46 -0
  70. package/dist/src/worker/multi-tenant-config.d.ts.map +1 -0
  71. package/dist/src/worker/multi-tenant-config.js +94 -0
  72. package/dist/src/worker/multi-tenant-config.js.map +1 -0
  73. package/dist/src/worker/port-allocator.d.ts +96 -0
  74. package/dist/src/worker/port-allocator.d.ts.map +1 -0
  75. package/dist/src/worker/port-allocator.js +153 -0
  76. package/dist/src/worker/port-allocator.js.map +1 -0
  77. package/dist/src/worker/types.d.ts +218 -0
  78. package/dist/src/worker/types.d.ts.map +1 -0
  79. package/dist/src/worker/types.js +38 -0
  80. package/dist/src/worker/types.js.map +1 -0
  81. package/dist/src/worker/worker-manager.d.ts +157 -0
  82. package/dist/src/worker/worker-manager.d.ts.map +1 -0
  83. package/dist/src/worker/worker-manager.js +500 -0
  84. package/dist/src/worker/worker-manager.js.map +1 -0
  85. package/package.json +1 -1
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Lease Manager
3
+ *
4
+ * Manages exclusive leases for worker access.
5
+ * Each tenant can hold at most one lease at a time.
6
+ */
7
+ import type { LeaseDescriptor, LeaseAcquisitionResult } from './types.js';
8
+ /**
9
+ * Configuration for LeaseManager
10
+ */
11
+ export interface LeaseManagerConfig {
12
+ /** Default lease TTL in milliseconds */
13
+ defaultTtlMs: number;
14
+ /** Interval for checking expired leases (ms) */
15
+ cleanupIntervalMs?: number;
16
+ }
17
+ /**
18
+ * Manages exclusive leases for tenant workers.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const leaseManager = new LeaseManager({ defaultTtlMs: 300000 });
23
+ *
24
+ * // Acquire a lease
25
+ * const result = leaseManager.acquire('tenant-a', 'controller-1', 'w-123');
26
+ * if (result.success) {
27
+ * console.log(`Lease acquired: ${result.lease.leaseId}`);
28
+ * }
29
+ *
30
+ * // Refresh the lease before it expires
31
+ * leaseManager.refresh('tenant-a');
32
+ *
33
+ * // Release when done
34
+ * leaseManager.release('tenant-a');
35
+ * ```
36
+ */
37
+ export declare class LeaseManager {
38
+ private readonly defaultTtlMs;
39
+ private readonly leases;
40
+ private cleanupIntervalId;
41
+ /** Callbacks for lease expiration */
42
+ private onLeaseExpiredCallbacks;
43
+ /** Callbacks for lease revocation */
44
+ private onLeaseRevokedCallbacks;
45
+ constructor(config: LeaseManagerConfig);
46
+ /**
47
+ * Get number of active leases
48
+ */
49
+ get leaseCount(): number;
50
+ /**
51
+ * Acquire a lease for a tenant
52
+ *
53
+ * @param tenantId - Tenant identifier
54
+ * @param controllerId - Controller/session identifier
55
+ * @param workerId - Worker to lease
56
+ * @param ttlMs - Optional custom TTL (uses default if not specified)
57
+ * @returns Lease acquisition result
58
+ */
59
+ acquire(tenantId: string, controllerId: string, workerId: string, ttlMs?: number): LeaseAcquisitionResult;
60
+ /**
61
+ * Release a lease
62
+ *
63
+ * @param tenantId - Tenant identifier
64
+ * @param controllerId - Optional controller ID to validate ownership
65
+ * @returns true if lease was released
66
+ */
67
+ release(tenantId: string, controllerId?: string): boolean;
68
+ /**
69
+ * Refresh a lease to extend its TTL
70
+ *
71
+ * @param tenantId - Tenant identifier
72
+ * @param ttlMs - Optional new TTL (uses default if not specified)
73
+ * @returns true if lease was refreshed
74
+ * @throws WorkerError if lease not found or expired
75
+ */
76
+ refresh(tenantId: string, ttlMs?: number): boolean;
77
+ /**
78
+ * Revoke a lease (typically on worker crash)
79
+ *
80
+ * @param tenantId - Tenant identifier
81
+ * @param reason - Reason for revocation
82
+ */
83
+ revoke(tenantId: string, reason: string): void;
84
+ /**
85
+ * Check if a tenant has an active lease
86
+ */
87
+ hasLease(tenantId: string): boolean;
88
+ /**
89
+ * Get the lease for a tenant (if any)
90
+ */
91
+ getLease(tenantId: string): LeaseDescriptor | undefined;
92
+ /**
93
+ * Get the lease holder (controller) for a tenant
94
+ */
95
+ getLeaseHolder(tenantId: string): string | undefined;
96
+ /**
97
+ * Check if a specific controller holds the lease for a tenant
98
+ */
99
+ isLeaseHeldBy(tenantId: string, controllerId: string): boolean;
100
+ /**
101
+ * Get the worker ID for a tenant's lease
102
+ */
103
+ getWorkerIdForTenant(tenantId: string): string | undefined;
104
+ /**
105
+ * Register a callback for lease expiration
106
+ */
107
+ onLeaseExpired(callback: (lease: LeaseDescriptor) => void): void;
108
+ /**
109
+ * Register a callback for lease revocation
110
+ */
111
+ onLeaseRevoked(callback: (lease: LeaseDescriptor, reason: string) => void): void;
112
+ /**
113
+ * Clean up expired leases
114
+ */
115
+ cleanupExpired(): number;
116
+ /**
117
+ * Get all active leases
118
+ */
119
+ getAllLeases(): LeaseDescriptor[];
120
+ /**
121
+ * Clear all leases
122
+ */
123
+ clear(): void;
124
+ /**
125
+ * Stop the cleanup interval
126
+ */
127
+ stop(): void;
128
+ /**
129
+ * Internal: refresh lease TTL
130
+ */
131
+ private refreshLease;
132
+ /**
133
+ * Internal: start cleanup interval
134
+ */
135
+ private startCleanupInterval;
136
+ }
137
+ //# sourceMappingURL=lease-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease-manager.d.ts","sourceRoot":"","sources":["../../../src/worker/lease-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAM1E;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,iBAAiB,CAA+C;IAExE,qCAAqC;IACrC,OAAO,CAAC,uBAAuB,CAA4C;IAC3E,qCAAqC;IACrC,OAAO,CAAC,uBAAuB,CAA4D;gBAE/E,MAAM,EAAE,kBAAkB;IAStC;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;;;;;OAQG;IACH,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,sBAAsB;IA+DzB;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO;IA4BzD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO;IAoBlD;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IA4B9C;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAanC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIvD;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKpD;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAW9D;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ1D;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAIhE;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIhF;;OAEG;IACH,cAAc,IAAI,MAAM;IAoCxB;;OAEG;IACH,YAAY,IAAI,eAAe,EAAE;IAIjC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAQ7B"}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Lease Manager
3
+ *
4
+ * Manages exclusive leases for worker access.
5
+ * Each tenant can hold at most one lease at a time.
6
+ */
7
+ import { randomUUID } from 'crypto';
8
+ import { WorkerError } from './errors/index.js';
9
+ import { createLogger } from '../shared/services/logging.service.js';
10
+ const logger = createLogger('LeaseManager');
11
+ /**
12
+ * Manages exclusive leases for tenant workers.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const leaseManager = new LeaseManager({ defaultTtlMs: 300000 });
17
+ *
18
+ * // Acquire a lease
19
+ * const result = leaseManager.acquire('tenant-a', 'controller-1', 'w-123');
20
+ * if (result.success) {
21
+ * console.log(`Lease acquired: ${result.lease.leaseId}`);
22
+ * }
23
+ *
24
+ * // Refresh the lease before it expires
25
+ * leaseManager.refresh('tenant-a');
26
+ *
27
+ * // Release when done
28
+ * leaseManager.release('tenant-a');
29
+ * ```
30
+ */
31
+ export class LeaseManager {
32
+ defaultTtlMs;
33
+ leases = new Map();
34
+ cleanupIntervalId = null;
35
+ /** Callbacks for lease expiration */
36
+ onLeaseExpiredCallbacks = [];
37
+ /** Callbacks for lease revocation */
38
+ onLeaseRevokedCallbacks = [];
39
+ constructor(config) {
40
+ this.defaultTtlMs = config.defaultTtlMs;
41
+ // Start cleanup interval if specified
42
+ if (config.cleanupIntervalMs && config.cleanupIntervalMs > 0) {
43
+ this.startCleanupInterval(config.cleanupIntervalMs);
44
+ }
45
+ }
46
+ /**
47
+ * Get number of active leases
48
+ */
49
+ get leaseCount() {
50
+ return this.leases.size;
51
+ }
52
+ /**
53
+ * Acquire a lease for a tenant
54
+ *
55
+ * @param tenantId - Tenant identifier
56
+ * @param controllerId - Controller/session identifier
57
+ * @param workerId - Worker to lease
58
+ * @param ttlMs - Optional custom TTL (uses default if not specified)
59
+ * @returns Lease acquisition result
60
+ */
61
+ acquire(tenantId, controllerId, workerId, ttlMs) {
62
+ const existingLease = this.leases.get(tenantId);
63
+ // Check if lease already exists
64
+ if (existingLease) {
65
+ // If same controller, just refresh
66
+ if (existingLease.controllerId === controllerId) {
67
+ this.refreshLease(existingLease, ttlMs);
68
+ return {
69
+ success: true,
70
+ lease: existingLease,
71
+ workerId: existingLease.workerId,
72
+ };
73
+ }
74
+ // Different controller - check if lease is still active
75
+ if (existingLease.status === 'active' && existingLease.expiresAt > Date.now()) {
76
+ logger.debug('Lease acquisition blocked - held by different controller', {
77
+ tenantId,
78
+ controllerId,
79
+ heldBy: existingLease.controllerId,
80
+ });
81
+ return {
82
+ success: false,
83
+ error: 'Lease is held by another controller',
84
+ errorCode: 'LEASE_ALREADY_HELD',
85
+ };
86
+ }
87
+ // Lease expired - clean it up and allow new acquisition
88
+ this.leases.delete(tenantId);
89
+ }
90
+ // Create new lease
91
+ const now = Date.now();
92
+ const lease = {
93
+ leaseId: randomUUID(),
94
+ tenantId,
95
+ workerId,
96
+ controllerId,
97
+ acquiredAt: now,
98
+ expiresAt: now + (ttlMs ?? this.defaultTtlMs),
99
+ status: 'active',
100
+ };
101
+ this.leases.set(tenantId, lease);
102
+ logger.info('Lease acquired', {
103
+ leaseId: lease.leaseId,
104
+ tenantId,
105
+ controllerId,
106
+ workerId,
107
+ ttlMs: ttlMs ?? this.defaultTtlMs,
108
+ });
109
+ return {
110
+ success: true,
111
+ lease,
112
+ workerId,
113
+ };
114
+ }
115
+ /**
116
+ * Release a lease
117
+ *
118
+ * @param tenantId - Tenant identifier
119
+ * @param controllerId - Optional controller ID to validate ownership
120
+ * @returns true if lease was released
121
+ */
122
+ release(tenantId, controllerId) {
123
+ const lease = this.leases.get(tenantId);
124
+ if (!lease) {
125
+ return false;
126
+ }
127
+ // Validate controller ownership if specified
128
+ if (controllerId && lease.controllerId !== controllerId) {
129
+ logger.debug('Lease release rejected - wrong controller', {
130
+ tenantId,
131
+ controllerId,
132
+ actualController: lease.controllerId,
133
+ });
134
+ return false;
135
+ }
136
+ this.leases.delete(tenantId);
137
+ logger.info('Lease released', {
138
+ leaseId: lease.leaseId,
139
+ tenantId,
140
+ controllerId: lease.controllerId,
141
+ });
142
+ return true;
143
+ }
144
+ /**
145
+ * Refresh a lease to extend its TTL
146
+ *
147
+ * @param tenantId - Tenant identifier
148
+ * @param ttlMs - Optional new TTL (uses default if not specified)
149
+ * @returns true if lease was refreshed
150
+ * @throws WorkerError if lease not found or expired
151
+ */
152
+ refresh(tenantId, ttlMs) {
153
+ const lease = this.leases.get(tenantId);
154
+ if (!lease) {
155
+ throw WorkerError.leaseNotFound(tenantId);
156
+ }
157
+ if (lease.status !== 'active') {
158
+ throw WorkerError.leaseExpired(tenantId, lease.expiresAt);
159
+ }
160
+ if (lease.expiresAt <= Date.now()) {
161
+ lease.status = 'expired';
162
+ throw WorkerError.leaseExpired(tenantId, lease.expiresAt);
163
+ }
164
+ this.refreshLease(lease, ttlMs);
165
+ return true;
166
+ }
167
+ /**
168
+ * Revoke a lease (typically on worker crash)
169
+ *
170
+ * @param tenantId - Tenant identifier
171
+ * @param reason - Reason for revocation
172
+ */
173
+ revoke(tenantId, reason) {
174
+ const lease = this.leases.get(tenantId);
175
+ if (!lease) {
176
+ return;
177
+ }
178
+ lease.status = 'revoked';
179
+ this.leases.delete(tenantId);
180
+ logger.info('Lease revoked', {
181
+ leaseId: lease.leaseId,
182
+ tenantId,
183
+ reason,
184
+ });
185
+ // Notify callbacks
186
+ for (const callback of this.onLeaseRevokedCallbacks) {
187
+ try {
188
+ callback(lease, reason);
189
+ }
190
+ catch (err) {
191
+ logger.error('Error in lease revoked callback', err instanceof Error ? err : undefined, {
192
+ errorMessage: String(err),
193
+ });
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Check if a tenant has an active lease
199
+ */
200
+ hasLease(tenantId) {
201
+ const lease = this.leases.get(tenantId);
202
+ if (!lease)
203
+ return false;
204
+ // Check if expired
205
+ if (lease.expiresAt <= Date.now()) {
206
+ lease.status = 'expired';
207
+ return false;
208
+ }
209
+ return lease.status === 'active';
210
+ }
211
+ /**
212
+ * Get the lease for a tenant (if any)
213
+ */
214
+ getLease(tenantId) {
215
+ return this.leases.get(tenantId);
216
+ }
217
+ /**
218
+ * Get the lease holder (controller) for a tenant
219
+ */
220
+ getLeaseHolder(tenantId) {
221
+ const lease = this.leases.get(tenantId);
222
+ return lease?.controllerId;
223
+ }
224
+ /**
225
+ * Check if a specific controller holds the lease for a tenant
226
+ */
227
+ isLeaseHeldBy(tenantId, controllerId) {
228
+ const lease = this.leases.get(tenantId);
229
+ if (!lease)
230
+ return false;
231
+ if (lease.expiresAt <= Date.now()) {
232
+ return false;
233
+ }
234
+ return lease.status === 'active' && lease.controllerId === controllerId;
235
+ }
236
+ /**
237
+ * Get the worker ID for a tenant's lease
238
+ */
239
+ getWorkerIdForTenant(tenantId) {
240
+ const lease = this.leases.get(tenantId);
241
+ if (lease?.status !== 'active' || lease.expiresAt <= Date.now()) {
242
+ return undefined;
243
+ }
244
+ return lease.workerId;
245
+ }
246
+ /**
247
+ * Register a callback for lease expiration
248
+ */
249
+ onLeaseExpired(callback) {
250
+ this.onLeaseExpiredCallbacks.push(callback);
251
+ }
252
+ /**
253
+ * Register a callback for lease revocation
254
+ */
255
+ onLeaseRevoked(callback) {
256
+ this.onLeaseRevokedCallbacks.push(callback);
257
+ }
258
+ /**
259
+ * Clean up expired leases
260
+ */
261
+ cleanupExpired() {
262
+ const now = Date.now();
263
+ let cleaned = 0;
264
+ for (const [tenantId, lease] of this.leases.entries()) {
265
+ if (lease.status === 'active' && lease.expiresAt <= now) {
266
+ lease.status = 'expired';
267
+ this.leases.delete(tenantId);
268
+ cleaned++;
269
+ logger.info('Lease expired', {
270
+ leaseId: lease.leaseId,
271
+ tenantId,
272
+ controllerId: lease.controllerId,
273
+ });
274
+ // Notify callbacks
275
+ for (const callback of this.onLeaseExpiredCallbacks) {
276
+ try {
277
+ callback(lease);
278
+ }
279
+ catch (err) {
280
+ logger.error('Error in lease expired callback', err instanceof Error ? err : undefined, {
281
+ errorMessage: String(err),
282
+ });
283
+ }
284
+ }
285
+ }
286
+ }
287
+ return cleaned;
288
+ }
289
+ /**
290
+ * Get all active leases
291
+ */
292
+ getAllLeases() {
293
+ return Array.from(this.leases.values());
294
+ }
295
+ /**
296
+ * Clear all leases
297
+ */
298
+ clear() {
299
+ this.leases.clear();
300
+ }
301
+ /**
302
+ * Stop the cleanup interval
303
+ */
304
+ stop() {
305
+ if (this.cleanupIntervalId) {
306
+ clearInterval(this.cleanupIntervalId);
307
+ this.cleanupIntervalId = null;
308
+ }
309
+ }
310
+ /**
311
+ * Internal: refresh lease TTL
312
+ */
313
+ refreshLease(lease, ttlMs) {
314
+ const now = Date.now();
315
+ lease.expiresAt = now + (ttlMs ?? this.defaultTtlMs);
316
+ logger.debug('Lease refreshed', {
317
+ leaseId: lease.leaseId,
318
+ tenantId: lease.tenantId,
319
+ newExpiresAt: lease.expiresAt,
320
+ });
321
+ }
322
+ /**
323
+ * Internal: start cleanup interval
324
+ */
325
+ startCleanupInterval(intervalMs) {
326
+ this.cleanupIntervalId = setInterval(() => {
327
+ const cleaned = this.cleanupExpired();
328
+ if (cleaned > 0) {
329
+ logger.debug('Cleaned up expired leases', { count: cleaned });
330
+ }
331
+ }, intervalMs);
332
+ }
333
+ }
334
+ //# sourceMappingURL=lease-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease-manager.js","sourceRoot":"","sources":["../../../src/worker/lease-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Multi-Tenant Configuration
3
+ *
4
+ * Parses and validates environment variables for multi-tenant mode.
5
+ */
6
+ import { type WorkerManagerConfig } from './types.js';
7
+ /**
8
+ * Multi-tenant mode configuration
9
+ */
10
+ export interface MultiTenantConfig {
11
+ /** Whether multi-tenant mode is enabled */
12
+ enabled: boolean;
13
+ /** Tenant identifier (required when enabled) */
14
+ tenantId: string;
15
+ /** Controller/session identifier (auto-generated if not provided) */
16
+ controllerId: string;
17
+ /** WorkerManager configuration */
18
+ workerConfig: WorkerManagerConfig;
19
+ }
20
+ /**
21
+ * Parse and validate multi-tenant configuration from environment variables.
22
+ *
23
+ * Environment variables:
24
+ * - MULTI_TENANT_MODE: Enable multi-tenant mode (true/false)
25
+ * - TENANT_ID: Unique tenant identifier (required when enabled)
26
+ * - CONTROLLER_ID: Session/controller ID (auto-generated if not provided)
27
+ * - WORKER_PROFILE_DIR: Base directory for Chrome profiles (default: /tmp/athena-workers)
28
+ * - WORKER_IDLE_TIMEOUT_MS: Idle worker timeout (default: 300000 = 5 min)
29
+ * - WORKER_HARD_TTL_MS: Max worker runtime (default: 7200000 = 2 hrs)
30
+ * - WORKER_LEASE_TTL_MS: Lease TTL (default: 300000 = 5 min)
31
+ * - WORKER_HEALTH_CHECK_INTERVAL_MS: Health check interval (default: 30000 = 30 sec)
32
+ * - WORKER_PORT_MIN: Minimum CDP port (default: 9300)
33
+ * - WORKER_PORT_MAX: Maximum CDP port (default: 9399)
34
+ * - WORKER_MAX_COUNT: Maximum concurrent workers (default: 100)
35
+ * - CHROME_PATH: Path to Chrome executable (auto-detected if not set)
36
+ *
37
+ * @returns Multi-tenant configuration
38
+ * @throws Error if MULTI_TENANT_MODE is true but TENANT_ID is not provided
39
+ */
40
+ export declare function getMultiTenantConfig(): MultiTenantConfig;
41
+ /**
42
+ * Validate that multi-tenant config is properly set up.
43
+ * Logs warnings for common misconfigurations.
44
+ */
45
+ export declare function validateMultiTenantConfig(config: MultiTenantConfig): void;
46
+ //# sourceMappingURL=multi-tenant-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-tenant-config.d.ts","sourceRoot":"","sources":["../../../src/worker/multi-tenant-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CA8CxD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAiBzE"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Multi-Tenant Configuration
3
+ *
4
+ * Parses and validates environment variables for multi-tenant mode.
5
+ */
6
+ import { randomUUID } from 'crypto';
7
+ import { DEFAULT_WORKER_CONFIG } from './types.js';
8
+ /**
9
+ * Parse integer from environment variable with default
10
+ */
11
+ function parseIntEnv(name, defaultValue) {
12
+ const value = process.env[name];
13
+ if (!value)
14
+ return defaultValue;
15
+ const parsed = parseInt(value, 10);
16
+ return isNaN(parsed) ? defaultValue : parsed;
17
+ }
18
+ /**
19
+ * Parse and validate multi-tenant configuration from environment variables.
20
+ *
21
+ * Environment variables:
22
+ * - MULTI_TENANT_MODE: Enable multi-tenant mode (true/false)
23
+ * - TENANT_ID: Unique tenant identifier (required when enabled)
24
+ * - CONTROLLER_ID: Session/controller ID (auto-generated if not provided)
25
+ * - WORKER_PROFILE_DIR: Base directory for Chrome profiles (default: /tmp/athena-workers)
26
+ * - WORKER_IDLE_TIMEOUT_MS: Idle worker timeout (default: 300000 = 5 min)
27
+ * - WORKER_HARD_TTL_MS: Max worker runtime (default: 7200000 = 2 hrs)
28
+ * - WORKER_LEASE_TTL_MS: Lease TTL (default: 300000 = 5 min)
29
+ * - WORKER_HEALTH_CHECK_INTERVAL_MS: Health check interval (default: 30000 = 30 sec)
30
+ * - WORKER_PORT_MIN: Minimum CDP port (default: 9300)
31
+ * - WORKER_PORT_MAX: Maximum CDP port (default: 9399)
32
+ * - WORKER_MAX_COUNT: Maximum concurrent workers (default: 100)
33
+ * - CHROME_PATH: Path to Chrome executable (auto-detected if not set)
34
+ *
35
+ * @returns Multi-tenant configuration
36
+ * @throws Error if MULTI_TENANT_MODE is true but TENANT_ID is not provided
37
+ */
38
+ export function getMultiTenantConfig() {
39
+ const enabled = process.env.MULTI_TENANT_MODE === 'true';
40
+ if (!enabled) {
41
+ return {
42
+ enabled: false,
43
+ tenantId: '',
44
+ controllerId: '',
45
+ workerConfig: {
46
+ profileBaseDir: '/tmp/athena-workers',
47
+ ...DEFAULT_WORKER_CONFIG,
48
+ },
49
+ };
50
+ }
51
+ const tenantId = process.env.TENANT_ID;
52
+ if (!tenantId) {
53
+ throw new Error('TENANT_ID environment variable is required when MULTI_TENANT_MODE is enabled');
54
+ }
55
+ const controllerId = process.env.CONTROLLER_ID ?? `ctrl-${randomUUID().substring(0, 8)}`;
56
+ const profileBaseDir = process.env.WORKER_PROFILE_DIR ?? '/tmp/athena-workers';
57
+ const workerConfig = {
58
+ profileBaseDir,
59
+ idleTimeoutMs: parseIntEnv('WORKER_IDLE_TIMEOUT_MS', DEFAULT_WORKER_CONFIG.idleTimeoutMs),
60
+ hardTtlMs: parseIntEnv('WORKER_HARD_TTL_MS', DEFAULT_WORKER_CONFIG.hardTtlMs),
61
+ leaseTtlMs: parseIntEnv('WORKER_LEASE_TTL_MS', DEFAULT_WORKER_CONFIG.leaseTtlMs),
62
+ healthCheckIntervalMs: parseIntEnv('WORKER_HEALTH_CHECK_INTERVAL_MS', DEFAULT_WORKER_CONFIG.healthCheckIntervalMs),
63
+ portRange: {
64
+ min: parseIntEnv('WORKER_PORT_MIN', DEFAULT_WORKER_CONFIG.portRange.min),
65
+ max: parseIntEnv('WORKER_PORT_MAX', DEFAULT_WORKER_CONFIG.portRange.max),
66
+ },
67
+ maxWorkers: parseIntEnv('WORKER_MAX_COUNT', DEFAULT_WORKER_CONFIG.maxWorkers),
68
+ chromePath: process.env.CHROME_PATH,
69
+ };
70
+ return {
71
+ enabled,
72
+ tenantId,
73
+ controllerId,
74
+ workerConfig,
75
+ };
76
+ }
77
+ /**
78
+ * Validate that multi-tenant config is properly set up.
79
+ * Logs warnings for common misconfigurations.
80
+ */
81
+ export function validateMultiTenantConfig(config) {
82
+ if (!config.enabled)
83
+ return;
84
+ // Check port range validity
85
+ if (config.workerConfig.portRange.min > config.workerConfig.portRange.max) {
86
+ throw new Error(`Invalid port range: WORKER_PORT_MIN (${config.workerConfig.portRange.min}) > WORKER_PORT_MAX (${config.workerConfig.portRange.max})`);
87
+ }
88
+ // Check maxWorkers fits in port range
89
+ const portCapacity = config.workerConfig.portRange.max - config.workerConfig.portRange.min + 1;
90
+ if (config.workerConfig.maxWorkers > portCapacity) {
91
+ console.warn(`[WARN] WORKER_MAX_COUNT (${config.workerConfig.maxWorkers}) exceeds port range capacity (${portCapacity}). Effective limit will be ${portCapacity}.`);
92
+ }
93
+ }
94
+ //# sourceMappingURL=multi-tenant-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-tenant-config.js","sourceRoot":"","sources":["../../../src/worker/multi-tenant-config.ts"],"names":[],"mappings":"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"}