claude-chrome-parallel 2.1.1 → 2.2.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.
- package/README.md +25 -0
- package/dist/cdp/connection-pool.d.ts +88 -0
- package/dist/cdp/connection-pool.d.ts.map +1 -0
- package/dist/cdp/connection-pool.js +280 -0
- package/dist/cdp/connection-pool.js.map +1 -0
- package/dist/cli/index.js +168 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/session-manager.d.ts +55 -1
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +139 -4
- package/dist/session-manager.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/network.d.ts +6 -0
- package/dist/tools/network.d.ts.map +1 -0
- package/dist/tools/network.js +215 -0
- package/dist/tools/network.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,6 +149,19 @@ All sessions work without conflicts!
|
|
|
149
149
|
| `javascript_tool` | Execute JavaScript |
|
|
150
150
|
| `tabs_context_mcp` | Get available tabs |
|
|
151
151
|
| `tabs_create_mcp` | Create new tab |
|
|
152
|
+
| `network` | Simulate network conditions (3G, 4G, offline, custom) |
|
|
153
|
+
|
|
154
|
+
### Network Simulation
|
|
155
|
+
|
|
156
|
+
Test how your app behaves under different network conditions:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
You: Simulate 3G network and navigate to myapp.com
|
|
160
|
+
|
|
161
|
+
Claude: [Applies 3G throttling: 1.5Mbps down, 750Kbps up, 100ms latency]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Available presets: `offline`, `slow-2g`, `2g`, `3g`, `4g`, `fast-wifi`, `custom`, `clear`
|
|
152
165
|
|
|
153
166
|
---
|
|
154
167
|
|
|
@@ -196,6 +209,15 @@ claude-chrome-parallel serve --port 9223
|
|
|
196
209
|
|
|
197
210
|
# Check installation health
|
|
198
211
|
claude-chrome-parallel doctor
|
|
212
|
+
|
|
213
|
+
# View session status and statistics
|
|
214
|
+
claude-chrome-parallel status
|
|
215
|
+
|
|
216
|
+
# View status as JSON (for automation)
|
|
217
|
+
claude-chrome-parallel status --json
|
|
218
|
+
|
|
219
|
+
# Clean up stale sessions and old backups
|
|
220
|
+
claude-chrome-parallel cleanup --max-age 24 --keep-backups 10
|
|
199
221
|
```
|
|
200
222
|
|
|
201
223
|
---
|
|
@@ -259,6 +281,9 @@ claude-chrome-parallel recover --list-backups
|
|
|
259
281
|
| Connection type | Shared extension state | Independent CDP |
|
|
260
282
|
| Max concurrent sessions | 1 | 20+ tested |
|
|
261
283
|
| Auto Chrome launch | ❌ | ✅ |
|
|
284
|
+
| Network simulation | ❌ | ✅ 3G/4G/offline presets |
|
|
285
|
+
| Session auto-cleanup | ❌ | ✅ TTL-based |
|
|
286
|
+
| Connection pooling | ❌ | ✅ Pre-warmed pages |
|
|
262
287
|
|
|
263
288
|
---
|
|
264
289
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Connection Pool - Pre-allocate and manage page instances for faster session creation
|
|
3
|
+
*/
|
|
4
|
+
import { Page } from 'puppeteer-core';
|
|
5
|
+
import { CDPClient } from './client';
|
|
6
|
+
export interface PoolConfig {
|
|
7
|
+
/** Minimum number of pre-allocated pages to keep ready (default: 2) */
|
|
8
|
+
minPoolSize?: number;
|
|
9
|
+
/** Maximum number of pre-allocated pages (default: 10) */
|
|
10
|
+
maxPoolSize?: number;
|
|
11
|
+
/** Page idle timeout in ms before returning to pool (default: 5 minutes) */
|
|
12
|
+
pageIdleTimeout?: number;
|
|
13
|
+
/** Whether to pre-warm pages on startup (default: true) */
|
|
14
|
+
preWarm?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface PoolStats {
|
|
17
|
+
/** Number of pages currently in the pool (ready to use) */
|
|
18
|
+
availablePages: number;
|
|
19
|
+
/** Number of pages currently in use */
|
|
20
|
+
inUsePages: number;
|
|
21
|
+
/** Total pages created since pool initialization */
|
|
22
|
+
totalPagesCreated: number;
|
|
23
|
+
/** Number of pages reused from pool */
|
|
24
|
+
pagesReused: number;
|
|
25
|
+
/** Number of pages created on-demand (pool was empty) */
|
|
26
|
+
pagesCreatedOnDemand: number;
|
|
27
|
+
/** Average time to acquire a page (ms) */
|
|
28
|
+
avgAcquireTimeMs: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class CDPConnectionPool {
|
|
31
|
+
private cdpClient;
|
|
32
|
+
private config;
|
|
33
|
+
private availablePages;
|
|
34
|
+
private inUsePages;
|
|
35
|
+
private maintenanceTimer;
|
|
36
|
+
private isInitialized;
|
|
37
|
+
private totalPagesCreated;
|
|
38
|
+
private pagesReused;
|
|
39
|
+
private pagesCreatedOnDemand;
|
|
40
|
+
private acquireTimes;
|
|
41
|
+
constructor(cdpClient?: CDPClient, config?: PoolConfig);
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the pool with pre-warmed pages
|
|
44
|
+
*/
|
|
45
|
+
initialize(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Acquire a page from the pool
|
|
48
|
+
*/
|
|
49
|
+
acquirePage(): Promise<Page>;
|
|
50
|
+
/**
|
|
51
|
+
* Release a page back to the pool
|
|
52
|
+
*/
|
|
53
|
+
releasePage(page: Page): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Create a new page
|
|
56
|
+
*/
|
|
57
|
+
private createNewPage;
|
|
58
|
+
/**
|
|
59
|
+
* Ensure minimum number of pages in pool
|
|
60
|
+
*/
|
|
61
|
+
private ensureMinimumPages;
|
|
62
|
+
/**
|
|
63
|
+
* Replenish pool asynchronously
|
|
64
|
+
*/
|
|
65
|
+
private replenishPoolAsync;
|
|
66
|
+
/**
|
|
67
|
+
* Perform maintenance on the pool
|
|
68
|
+
*/
|
|
69
|
+
private performMaintenance;
|
|
70
|
+
/**
|
|
71
|
+
* Get pool statistics
|
|
72
|
+
*/
|
|
73
|
+
getStats(): PoolStats;
|
|
74
|
+
/**
|
|
75
|
+
* Get current configuration
|
|
76
|
+
*/
|
|
77
|
+
getConfig(): Required<PoolConfig>;
|
|
78
|
+
/**
|
|
79
|
+
* Update configuration
|
|
80
|
+
*/
|
|
81
|
+
updateConfig(config: Partial<PoolConfig>): void;
|
|
82
|
+
/**
|
|
83
|
+
* Shutdown the pool
|
|
84
|
+
*/
|
|
85
|
+
shutdown(): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
export declare function getCDPConnectionPool(config?: PoolConfig): CDPConnectionPool;
|
|
88
|
+
//# sourceMappingURL=connection-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-pool.d.ts","sourceRoot":"","sources":["../../src/cdp/connection-pool.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAgB,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAeD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,cAAc,CAAoB;IAC1C,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,YAAY,CAAgB;gBAExB,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,UAAU;IAKtD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ClC;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAmD5C;;OAEG;YACW,aAAa;IAM3B;;OAEG;YACW,kBAAkB;IAsBhC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;YACW,kBAAkB;IAiChC;;OAEG;IACH,QAAQ,IAAI,SAAS;IAgBrB;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC;IAIjC;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAI/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA6BhC;AAKD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,iBAAiB,CAK3E"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CDP Connection Pool - Pre-allocate and manage page instances for faster session creation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CDPConnectionPool = void 0;
|
|
7
|
+
exports.getCDPConnectionPool = getCDPConnectionPool;
|
|
8
|
+
const client_1 = require("./client");
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
minPoolSize: 2,
|
|
11
|
+
maxPoolSize: 10,
|
|
12
|
+
pageIdleTimeout: 5 * 60 * 1000, // 5 minutes
|
|
13
|
+
preWarm: true,
|
|
14
|
+
};
|
|
15
|
+
class CDPConnectionPool {
|
|
16
|
+
cdpClient;
|
|
17
|
+
config;
|
|
18
|
+
availablePages = [];
|
|
19
|
+
inUsePages = new Map();
|
|
20
|
+
maintenanceTimer = null;
|
|
21
|
+
isInitialized = false;
|
|
22
|
+
// Stats
|
|
23
|
+
totalPagesCreated = 0;
|
|
24
|
+
pagesReused = 0;
|
|
25
|
+
pagesCreatedOnDemand = 0;
|
|
26
|
+
acquireTimes = [];
|
|
27
|
+
constructor(cdpClient, config) {
|
|
28
|
+
this.cdpClient = cdpClient || (0, client_1.getCDPClient)();
|
|
29
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize the pool with pre-warmed pages
|
|
33
|
+
*/
|
|
34
|
+
async initialize() {
|
|
35
|
+
if (this.isInitialized)
|
|
36
|
+
return;
|
|
37
|
+
await this.cdpClient.connect();
|
|
38
|
+
if (this.config.preWarm) {
|
|
39
|
+
console.error(`[Pool] Pre-warming ${this.config.minPoolSize} pages...`);
|
|
40
|
+
await this.ensureMinimumPages();
|
|
41
|
+
}
|
|
42
|
+
// Start maintenance timer
|
|
43
|
+
this.maintenanceTimer = setInterval(() => {
|
|
44
|
+
this.performMaintenance();
|
|
45
|
+
}, 30000); // Every 30 seconds
|
|
46
|
+
this.maintenanceTimer.unref();
|
|
47
|
+
this.isInitialized = true;
|
|
48
|
+
console.error('[Pool] Connection pool initialized');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Acquire a page from the pool
|
|
52
|
+
*/
|
|
53
|
+
async acquirePage() {
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
// Ensure initialized
|
|
56
|
+
if (!this.isInitialized) {
|
|
57
|
+
await this.initialize();
|
|
58
|
+
}
|
|
59
|
+
let page;
|
|
60
|
+
let pooledPage;
|
|
61
|
+
// Try to get from pool
|
|
62
|
+
if (this.availablePages.length > 0) {
|
|
63
|
+
pooledPage = this.availablePages.pop();
|
|
64
|
+
page = pooledPage.page;
|
|
65
|
+
pooledPage.lastUsedAt = Date.now();
|
|
66
|
+
this.pagesReused++;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Create new page on demand
|
|
70
|
+
page = await this.createNewPage();
|
|
71
|
+
pooledPage = {
|
|
72
|
+
page,
|
|
73
|
+
createdAt: Date.now(),
|
|
74
|
+
lastUsedAt: Date.now(),
|
|
75
|
+
};
|
|
76
|
+
this.pagesCreatedOnDemand++;
|
|
77
|
+
}
|
|
78
|
+
this.inUsePages.set(page, pooledPage);
|
|
79
|
+
// Track acquire time
|
|
80
|
+
const acquireTime = Date.now() - startTime;
|
|
81
|
+
this.acquireTimes.push(acquireTime);
|
|
82
|
+
if (this.acquireTimes.length > 100) {
|
|
83
|
+
this.acquireTimes.shift();
|
|
84
|
+
}
|
|
85
|
+
// Replenish pool in background if needed
|
|
86
|
+
this.replenishPoolAsync();
|
|
87
|
+
return page;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Release a page back to the pool
|
|
91
|
+
*/
|
|
92
|
+
async releasePage(page) {
|
|
93
|
+
const pooledPage = this.inUsePages.get(page);
|
|
94
|
+
if (!pooledPage) {
|
|
95
|
+
// Page not managed by this pool, just close it
|
|
96
|
+
try {
|
|
97
|
+
await page.close();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Ignore close errors
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.inUsePages.delete(page);
|
|
105
|
+
// Check if pool is at max capacity
|
|
106
|
+
if (this.availablePages.length >= this.config.maxPoolSize) {
|
|
107
|
+
// Close the page instead of returning to pool
|
|
108
|
+
try {
|
|
109
|
+
await page.close();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Ignore close errors
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Reset the page state before returning to pool
|
|
117
|
+
try {
|
|
118
|
+
// Navigate to blank page to clear state
|
|
119
|
+
await page.goto('about:blank', { waitUntil: 'domcontentloaded', timeout: 5000 });
|
|
120
|
+
// Clear cookies and storage
|
|
121
|
+
const client = await page.createCDPSession();
|
|
122
|
+
await client.send('Network.clearBrowserCookies');
|
|
123
|
+
await client.send('Storage.clearDataForOrigin', {
|
|
124
|
+
origin: '*',
|
|
125
|
+
storageTypes: 'all',
|
|
126
|
+
}).catch(() => { }); // Ignore if not supported
|
|
127
|
+
await client.detach();
|
|
128
|
+
pooledPage.lastUsedAt = Date.now();
|
|
129
|
+
this.availablePages.push(pooledPage);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Failed to reset, close the page
|
|
133
|
+
try {
|
|
134
|
+
await page.close();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Ignore close errors
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create a new page
|
|
143
|
+
*/
|
|
144
|
+
async createNewPage() {
|
|
145
|
+
const page = await this.cdpClient.createPage();
|
|
146
|
+
this.totalPagesCreated++;
|
|
147
|
+
return page;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Ensure minimum number of pages in pool
|
|
151
|
+
*/
|
|
152
|
+
async ensureMinimumPages() {
|
|
153
|
+
const pagesToCreate = this.config.minPoolSize - this.availablePages.length;
|
|
154
|
+
if (pagesToCreate <= 0)
|
|
155
|
+
return;
|
|
156
|
+
const promises = [];
|
|
157
|
+
for (let i = 0; i < pagesToCreate; i++) {
|
|
158
|
+
promises.push(this.createNewPage().then((page) => {
|
|
159
|
+
this.availablePages.push({
|
|
160
|
+
page,
|
|
161
|
+
createdAt: Date.now(),
|
|
162
|
+
lastUsedAt: Date.now(),
|
|
163
|
+
});
|
|
164
|
+
}).catch((err) => {
|
|
165
|
+
console.error('[Pool] Failed to pre-warm page:', err);
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
await Promise.all(promises);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Replenish pool asynchronously
|
|
172
|
+
*/
|
|
173
|
+
replenishPoolAsync() {
|
|
174
|
+
if (this.availablePages.length < this.config.minPoolSize) {
|
|
175
|
+
this.ensureMinimumPages().catch((err) => {
|
|
176
|
+
console.error('[Pool] Failed to replenish pool:', err);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Perform maintenance on the pool
|
|
182
|
+
*/
|
|
183
|
+
async performMaintenance() {
|
|
184
|
+
const now = Date.now();
|
|
185
|
+
const pagesToRemove = [];
|
|
186
|
+
// Find pages that have been idle too long
|
|
187
|
+
for (const pooledPage of this.availablePages) {
|
|
188
|
+
const idleTime = now - pooledPage.lastUsedAt;
|
|
189
|
+
if (idleTime > this.config.pageIdleTimeout &&
|
|
190
|
+
this.availablePages.length > this.config.minPoolSize) {
|
|
191
|
+
pagesToRemove.push(pooledPage);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Remove idle pages
|
|
195
|
+
for (const pooledPage of pagesToRemove) {
|
|
196
|
+
const index = this.availablePages.indexOf(pooledPage);
|
|
197
|
+
if (index !== -1) {
|
|
198
|
+
this.availablePages.splice(index, 1);
|
|
199
|
+
try {
|
|
200
|
+
await pooledPage.page.close();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Ignore close errors
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (pagesToRemove.length > 0) {
|
|
208
|
+
console.error(`[Pool] Maintenance: closed ${pagesToRemove.length} idle page(s)`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get pool statistics
|
|
213
|
+
*/
|
|
214
|
+
getStats() {
|
|
215
|
+
const avgAcquireTime = this.acquireTimes.length > 0
|
|
216
|
+
? this.acquireTimes.reduce((a, b) => a + b, 0) / this.acquireTimes.length
|
|
217
|
+
: 0;
|
|
218
|
+
return {
|
|
219
|
+
availablePages: this.availablePages.length,
|
|
220
|
+
inUsePages: this.inUsePages.size,
|
|
221
|
+
totalPagesCreated: this.totalPagesCreated,
|
|
222
|
+
pagesReused: this.pagesReused,
|
|
223
|
+
pagesCreatedOnDemand: this.pagesCreatedOnDemand,
|
|
224
|
+
avgAcquireTimeMs: Math.round(avgAcquireTime * 100) / 100,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get current configuration
|
|
229
|
+
*/
|
|
230
|
+
getConfig() {
|
|
231
|
+
return { ...this.config };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Update configuration
|
|
235
|
+
*/
|
|
236
|
+
updateConfig(config) {
|
|
237
|
+
this.config = { ...this.config, ...config };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Shutdown the pool
|
|
241
|
+
*/
|
|
242
|
+
async shutdown() {
|
|
243
|
+
if (this.maintenanceTimer) {
|
|
244
|
+
clearInterval(this.maintenanceTimer);
|
|
245
|
+
this.maintenanceTimer = null;
|
|
246
|
+
}
|
|
247
|
+
// Close all available pages
|
|
248
|
+
for (const pooledPage of this.availablePages) {
|
|
249
|
+
try {
|
|
250
|
+
await pooledPage.page.close();
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// Ignore close errors
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
this.availablePages = [];
|
|
257
|
+
// Close all in-use pages
|
|
258
|
+
for (const [page] of this.inUsePages) {
|
|
259
|
+
try {
|
|
260
|
+
await page.close();
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// Ignore close errors
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this.inUsePages.clear();
|
|
267
|
+
this.isInitialized = false;
|
|
268
|
+
console.error('[Pool] Connection pool shutdown');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
exports.CDPConnectionPool = CDPConnectionPool;
|
|
272
|
+
// Singleton instance
|
|
273
|
+
let poolInstance = null;
|
|
274
|
+
function getCDPConnectionPool(config) {
|
|
275
|
+
if (!poolInstance) {
|
|
276
|
+
poolInstance = new CDPConnectionPool(undefined, config);
|
|
277
|
+
}
|
|
278
|
+
return poolInstance;
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=connection-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-pool.js","sourceRoot":"","sources":["../../src/cdp/connection-pool.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -54,8 +54,8 @@ const fs = __importStar(require("fs"));
|
|
|
54
54
|
const os = __importStar(require("os"));
|
|
55
55
|
const child_process_1 = require("child_process");
|
|
56
56
|
const program = new commander_1.Command();
|
|
57
|
-
// Package info
|
|
58
|
-
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
57
|
+
// Package info - from dist/cli/ go up two levels to root
|
|
58
|
+
const packageJsonPath = path.join(__dirname, '..', '..', 'package.json');
|
|
59
59
|
let version = '0.1.0';
|
|
60
60
|
try {
|
|
61
61
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
@@ -354,6 +354,134 @@ program
|
|
|
354
354
|
console.log('⚠️ Could not recover - created new empty config');
|
|
355
355
|
console.log('Your corrupted file has been backed up');
|
|
356
356
|
});
|
|
357
|
+
program
|
|
358
|
+
.command('status')
|
|
359
|
+
.description('Show session manager status and statistics')
|
|
360
|
+
.option('--json', 'Output as JSON')
|
|
361
|
+
.action(async (options) => {
|
|
362
|
+
const sessionsDir = getSessionsDir();
|
|
363
|
+
const backupDir = path.join(os.homedir(), '.claude-chrome-parallel', 'backups');
|
|
364
|
+
const configPath = path.join(os.homedir(), '.claude.json');
|
|
365
|
+
// Gather statistics
|
|
366
|
+
let activeSessions = 0;
|
|
367
|
+
let totalSessionsSize = 0;
|
|
368
|
+
const sessionDetails = [];
|
|
369
|
+
if (fs.existsSync(sessionsDir)) {
|
|
370
|
+
const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
371
|
+
const now = Date.now();
|
|
372
|
+
for (const entry of entries) {
|
|
373
|
+
if (!entry.isDirectory())
|
|
374
|
+
continue;
|
|
375
|
+
const sessionDir = path.join(sessionsDir, entry.name);
|
|
376
|
+
const metadataPath = path.join(sessionDir, '.session-metadata.json');
|
|
377
|
+
activeSessions++;
|
|
378
|
+
const size = getDirSize(sessionDir);
|
|
379
|
+
totalSessionsSize += size;
|
|
380
|
+
let age = 'unknown';
|
|
381
|
+
if (fs.existsSync(metadataPath)) {
|
|
382
|
+
try {
|
|
383
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
384
|
+
const createdAt = new Date(metadata.createdAt).getTime();
|
|
385
|
+
age = formatDuration(now - createdAt);
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// ignore
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
sessionDetails.push({
|
|
392
|
+
id: entry.name,
|
|
393
|
+
age,
|
|
394
|
+
size: formatBytes(size),
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Count backups
|
|
399
|
+
let backupCount = 0;
|
|
400
|
+
let backupSize = 0;
|
|
401
|
+
if (fs.existsSync(backupDir)) {
|
|
402
|
+
const backups = fs.readdirSync(backupDir).filter(f => f.startsWith('.claude.json.'));
|
|
403
|
+
backupCount = backups.length;
|
|
404
|
+
for (const backup of backups) {
|
|
405
|
+
const stats = fs.statSync(path.join(backupDir, backup));
|
|
406
|
+
backupSize += stats.size;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Check config health
|
|
410
|
+
let configHealthy = true;
|
|
411
|
+
let configError = '';
|
|
412
|
+
if (fs.existsSync(configPath)) {
|
|
413
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
414
|
+
if (!isValidJson(content)) {
|
|
415
|
+
configHealthy = false;
|
|
416
|
+
configError = 'Invalid JSON (corrupted)';
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Memory usage
|
|
420
|
+
const memUsage = process.memoryUsage();
|
|
421
|
+
const status = {
|
|
422
|
+
sessions: {
|
|
423
|
+
active: activeSessions,
|
|
424
|
+
totalSize: formatBytes(totalSessionsSize),
|
|
425
|
+
details: sessionDetails,
|
|
426
|
+
},
|
|
427
|
+
backups: {
|
|
428
|
+
count: backupCount,
|
|
429
|
+
totalSize: formatBytes(backupSize),
|
|
430
|
+
},
|
|
431
|
+
config: {
|
|
432
|
+
healthy: configHealthy,
|
|
433
|
+
error: configError || undefined,
|
|
434
|
+
},
|
|
435
|
+
memory: {
|
|
436
|
+
heapUsed: formatBytes(memUsage.heapUsed),
|
|
437
|
+
heapTotal: formatBytes(memUsage.heapTotal),
|
|
438
|
+
rss: formatBytes(memUsage.rss),
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
if (options.json) {
|
|
442
|
+
console.log(JSON.stringify(status, null, 2));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
// Pretty print
|
|
446
|
+
console.log('Claude Chrome Parallel Status');
|
|
447
|
+
console.log('═'.repeat(40));
|
|
448
|
+
console.log();
|
|
449
|
+
// Sessions
|
|
450
|
+
console.log('Sessions');
|
|
451
|
+
console.log('─'.repeat(20));
|
|
452
|
+
console.log(` Active: ${activeSessions}`);
|
|
453
|
+
console.log(` Total Size: ${formatBytes(totalSessionsSize)}`);
|
|
454
|
+
if (sessionDetails.length > 0) {
|
|
455
|
+
console.log(' Details:');
|
|
456
|
+
for (const s of sessionDetails) {
|
|
457
|
+
console.log(` - ${s.id} (${s.age}, ${s.size})`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
console.log();
|
|
461
|
+
// Backups
|
|
462
|
+
console.log('Backups');
|
|
463
|
+
console.log('─'.repeat(20));
|
|
464
|
+
console.log(` Count: ${backupCount}`);
|
|
465
|
+
console.log(` Total Size: ${formatBytes(backupSize)}`);
|
|
466
|
+
console.log();
|
|
467
|
+
// Config
|
|
468
|
+
console.log('Config Health');
|
|
469
|
+
console.log('─'.repeat(20));
|
|
470
|
+
if (configHealthy) {
|
|
471
|
+
console.log(' ✅ .claude.json is healthy');
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
console.log(` ❌ .claude.json: ${configError}`);
|
|
475
|
+
console.log(' Run: claude-chrome-parallel recover');
|
|
476
|
+
}
|
|
477
|
+
console.log();
|
|
478
|
+
// Memory
|
|
479
|
+
console.log('Memory');
|
|
480
|
+
console.log('─'.repeat(20));
|
|
481
|
+
console.log(` Heap Used: ${formatBytes(memUsage.heapUsed)}`);
|
|
482
|
+
console.log(` Heap Total: ${formatBytes(memUsage.heapTotal)}`);
|
|
483
|
+
console.log(` RSS: ${formatBytes(memUsage.rss)}`);
|
|
484
|
+
});
|
|
357
485
|
program
|
|
358
486
|
.command('cleanup')
|
|
359
487
|
.description('Clean up stale sessions and old backups')
|
|
@@ -506,6 +634,44 @@ function formatBytes(bytes) {
|
|
|
506
634
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
507
635
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
508
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Format duration in milliseconds as human readable string
|
|
639
|
+
*/
|
|
640
|
+
function formatDuration(ms) {
|
|
641
|
+
const seconds = Math.floor(ms / 1000);
|
|
642
|
+
const minutes = Math.floor(seconds / 60);
|
|
643
|
+
const hours = Math.floor(minutes / 60);
|
|
644
|
+
const days = Math.floor(hours / 24);
|
|
645
|
+
if (days > 0)
|
|
646
|
+
return `${days}d ${hours % 24}h`;
|
|
647
|
+
if (hours > 0)
|
|
648
|
+
return `${hours}h ${minutes % 60}m`;
|
|
649
|
+
if (minutes > 0)
|
|
650
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
651
|
+
return `${seconds}s`;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get total size of a directory recursively
|
|
655
|
+
*/
|
|
656
|
+
function getDirSize(dirPath) {
|
|
657
|
+
let totalSize = 0;
|
|
658
|
+
try {
|
|
659
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
660
|
+
for (const entry of entries) {
|
|
661
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
662
|
+
if (entry.isDirectory()) {
|
|
663
|
+
totalSize += getDirSize(fullPath);
|
|
664
|
+
}
|
|
665
|
+
else if (entry.isFile()) {
|
|
666
|
+
totalSize += fs.statSync(fullPath).size;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
// Permission denied or other errors
|
|
672
|
+
}
|
|
673
|
+
return totalSize;
|
|
674
|
+
}
|
|
509
675
|
/**
|
|
510
676
|
* Attempt to recover valid JSON from corrupted content
|
|
511
677
|
*/
|