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.
- package/dist/src/browser/session-manager.d.ts +28 -1
- package/dist/src/browser/session-manager.d.ts.map +1 -1
- package/dist/src/browser/session-manager.js +73 -0
- package/dist/src/browser/session-manager.js.map +1 -1
- package/dist/src/index.d.ts +10 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +47 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/server/mcp-server.d.ts +14 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +21 -6
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/server/session-store.d.ts +18 -3
- package/dist/src/server/session-store.d.ts.map +1 -1
- package/dist/src/server/session-store.js +27 -3
- package/dist/src/server/session-store.js.map +1 -1
- package/dist/src/session/session-worker-binding.d.ts +57 -0
- package/dist/src/session/session-worker-binding.d.ts.map +1 -0
- package/dist/src/session/session-worker-binding.js +109 -0
- package/dist/src/session/session-worker-binding.js.map +1 -0
- package/dist/src/shared/services/logging.service.d.ts +8 -0
- package/dist/src/shared/services/logging.service.d.ts.map +1 -1
- package/dist/src/shared/services/logging.service.js +10 -0
- package/dist/src/shared/services/logging.service.js.map +1 -1
- package/dist/src/snapshot/snapshot-compiler.d.ts +14 -0
- package/dist/src/snapshot/snapshot-compiler.d.ts.map +1 -1
- package/dist/src/snapshot/snapshot-compiler.js +119 -3
- package/dist/src/snapshot/snapshot-compiler.js.map +1 -1
- package/dist/src/state/actionables-filter.d.ts.map +1 -1
- package/dist/src/state/actionables-filter.js +8 -30
- package/dist/src/state/actionables-filter.js.map +1 -1
- package/dist/src/state/locator-generator.d.ts.map +1 -1
- package/dist/src/state/locator-generator.js +1 -10
- package/dist/src/state/locator-generator.js.map +1 -1
- package/dist/src/state/node-layer.d.ts +22 -0
- package/dist/src/state/node-layer.d.ts.map +1 -0
- package/dist/src/state/node-layer.js +42 -0
- package/dist/src/state/node-layer.js.map +1 -0
- package/dist/src/state/state-manager.d.ts.map +1 -1
- package/dist/src/state/state-manager.js +9 -8
- package/dist/src/state/state-manager.js.map +1 -1
- package/dist/src/tools/tool-schemas.d.ts +22 -22
- package/dist/src/tools/tool-schemas.d.ts.map +1 -1
- package/dist/src/tools/tool-schemas.js.map +1 -1
- package/dist/src/worker/chrome-worker-process.d.ts +123 -0
- package/dist/src/worker/chrome-worker-process.d.ts.map +1 -0
- package/dist/src/worker/chrome-worker-process.js +294 -0
- package/dist/src/worker/chrome-worker-process.js.map +1 -0
- package/dist/src/worker/errors/index.d.ts +5 -0
- package/dist/src/worker/errors/index.d.ts.map +1 -0
- package/dist/src/worker/errors/index.js +5 -0
- package/dist/src/worker/errors/index.js.map +1 -0
- package/dist/src/worker/errors/worker.error.d.ts +122 -0
- package/dist/src/worker/errors/worker.error.d.ts.map +1 -0
- package/dist/src/worker/errors/worker.error.js +199 -0
- package/dist/src/worker/errors/worker.error.js.map +1 -0
- package/dist/src/worker/health-monitor.d.ts +141 -0
- package/dist/src/worker/health-monitor.d.ts.map +1 -0
- package/dist/src/worker/health-monitor.js +260 -0
- package/dist/src/worker/health-monitor.js.map +1 -0
- package/dist/src/worker/index.d.ts +16 -0
- package/dist/src/worker/index.d.ts.map +1 -0
- package/dist/src/worker/index.js +19 -0
- package/dist/src/worker/index.js.map +1 -0
- package/dist/src/worker/lease-manager.d.ts +137 -0
- package/dist/src/worker/lease-manager.d.ts.map +1 -0
- package/dist/src/worker/lease-manager.js +334 -0
- package/dist/src/worker/lease-manager.js.map +1 -0
- package/dist/src/worker/multi-tenant-config.d.ts +46 -0
- package/dist/src/worker/multi-tenant-config.d.ts.map +1 -0
- package/dist/src/worker/multi-tenant-config.js +94 -0
- package/dist/src/worker/multi-tenant-config.js.map +1 -0
- package/dist/src/worker/port-allocator.d.ts +96 -0
- package/dist/src/worker/port-allocator.d.ts.map +1 -0
- package/dist/src/worker/port-allocator.js +153 -0
- package/dist/src/worker/port-allocator.js.map +1 -0
- package/dist/src/worker/types.d.ts +218 -0
- package/dist/src/worker/types.d.ts.map +1 -0
- package/dist/src/worker/types.js +38 -0
- package/dist/src/worker/types.js.map +1 -0
- package/dist/src/worker/worker-manager.d.ts +157 -0
- package/dist/src/worker/worker-manager.d.ts.map +1 -0
- package/dist/src/worker/worker-manager.js +500 -0
- package/dist/src/worker/worker-manager.js.map +1 -0
- 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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"}
|