@pan-sec/notebooklm-mcp 1.4.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/LICENSE +21 -0
- package/README.md +289 -0
- package/SECURITY.md +539 -0
- package/dist/auth/auth-manager.d.ts +137 -0
- package/dist/auth/auth-manager.d.ts.map +1 -0
- package/dist/auth/auth-manager.js +984 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/auth/mcp-auth.d.ts +102 -0
- package/dist/auth/mcp-auth.d.ts.map +1 -0
- package/dist/auth/mcp-auth.js +286 -0
- package/dist/auth/mcp-auth.js.map +1 -0
- package/dist/config.d.ts +89 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +216 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +371 -0
- package/dist/index.js.map +1 -0
- package/dist/library/notebook-library.d.ts +70 -0
- package/dist/library/notebook-library.d.ts.map +1 -0
- package/dist/library/notebook-library.js +279 -0
- package/dist/library/notebook-library.js.map +1 -0
- package/dist/library/types.d.ts +67 -0
- package/dist/library/types.d.ts.map +1 -0
- package/dist/library/types.js +8 -0
- package/dist/library/types.js.map +1 -0
- package/dist/resources/resource-handlers.d.ts +22 -0
- package/dist/resources/resource-handlers.d.ts.map +1 -0
- package/dist/resources/resource-handlers.js +216 -0
- package/dist/resources/resource-handlers.js.map +1 -0
- package/dist/session/browser-session.d.ts +108 -0
- package/dist/session/browser-session.d.ts.map +1 -0
- package/dist/session/browser-session.js +621 -0
- package/dist/session/browser-session.js.map +1 -0
- package/dist/session/session-manager.d.ts +77 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +314 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/session-timeout.d.ts +122 -0
- package/dist/session/session-timeout.d.ts.map +1 -0
- package/dist/session/session-timeout.js +281 -0
- package/dist/session/session-timeout.js.map +1 -0
- package/dist/session/shared-context-manager.d.ts +107 -0
- package/dist/session/shared-context-manager.d.ts.map +1 -0
- package/dist/session/shared-context-manager.js +447 -0
- package/dist/session/shared-context-manager.js.map +1 -0
- package/dist/tools/definitions/ask-question.d.ts +8 -0
- package/dist/tools/definitions/ask-question.d.ts.map +1 -0
- package/dist/tools/definitions/ask-question.js +211 -0
- package/dist/tools/definitions/ask-question.js.map +1 -0
- package/dist/tools/definitions/notebook-management.d.ts +3 -0
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -0
- package/dist/tools/definitions/notebook-management.js +243 -0
- package/dist/tools/definitions/notebook-management.js.map +1 -0
- package/dist/tools/definitions/session-management.d.ts +3 -0
- package/dist/tools/definitions/session-management.d.ts.map +1 -0
- package/dist/tools/definitions/session-management.js +41 -0
- package/dist/tools/definitions/session-management.js.map +1 -0
- package/dist/tools/definitions/system.d.ts +3 -0
- package/dist/tools/definitions/system.d.ts.map +1 -0
- package/dist/tools/definitions/system.js +143 -0
- package/dist/tools/definitions/system.js.map +1 -0
- package/dist/tools/definitions.d.ts +12 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +26 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/handlers.d.ts +213 -0
- package/dist/tools/handlers.d.ts.map +1 -0
- package/dist/tools/handlers.js +813 -0
- package/dist/tools/handlers.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/audit-logger.d.ts +140 -0
- package/dist/utils/audit-logger.d.ts.map +1 -0
- package/dist/utils/audit-logger.js +361 -0
- package/dist/utils/audit-logger.js.map +1 -0
- package/dist/utils/cert-pinning.d.ts +97 -0
- package/dist/utils/cert-pinning.d.ts.map +1 -0
- package/dist/utils/cert-pinning.js +328 -0
- package/dist/utils/cert-pinning.js.map +1 -0
- package/dist/utils/cleanup-manager.d.ts +133 -0
- package/dist/utils/cleanup-manager.d.ts.map +1 -0
- package/dist/utils/cleanup-manager.js +673 -0
- package/dist/utils/cleanup-manager.js.map +1 -0
- package/dist/utils/cli-handler.d.ts +16 -0
- package/dist/utils/cli-handler.d.ts.map +1 -0
- package/dist/utils/cli-handler.js +102 -0
- package/dist/utils/cli-handler.js.map +1 -0
- package/dist/utils/crypto.d.ts +175 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/crypto.js +612 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/logger.d.ts +61 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +92 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/page-utils.d.ts +54 -0
- package/dist/utils/page-utils.d.ts.map +1 -0
- package/dist/utils/page-utils.js +405 -0
- package/dist/utils/page-utils.js.map +1 -0
- package/dist/utils/response-validator.d.ts +98 -0
- package/dist/utils/response-validator.d.ts.map +1 -0
- package/dist/utils/response-validator.js +352 -0
- package/dist/utils/response-validator.js.map +1 -0
- package/dist/utils/secrets-scanner.d.ts +126 -0
- package/dist/utils/secrets-scanner.d.ts.map +1 -0
- package/dist/utils/secrets-scanner.js +443 -0
- package/dist/utils/secrets-scanner.js.map +1 -0
- package/dist/utils/secure-memory.d.ts +130 -0
- package/dist/utils/secure-memory.d.ts.map +1 -0
- package/dist/utils/secure-memory.js +279 -0
- package/dist/utils/secure-memory.js.map +1 -0
- package/dist/utils/security.d.ts +83 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +272 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/settings-manager.d.ts +37 -0
- package/dist/utils/settings-manager.d.ts.map +1 -0
- package/dist/utils/settings-manager.js +125 -0
- package/dist/utils/settings-manager.js.map +1 -0
- package/dist/utils/stealth-utils.d.ts +135 -0
- package/dist/utils/stealth-utils.d.ts.map +1 -0
- package/dist/utils/stealth-utils.js +398 -0
- package/dist/utils/stealth-utils.js.map +1 -0
- package/dist/utils/tool-validation.d.ts +93 -0
- package/dist/utils/tool-validation.d.ts.map +1 -0
- package/dist/utils/tool-validation.js +277 -0
- package/dist/utils/tool-validation.js.map +1 -0
- package/docs/SECURITY_IMPLEMENTATION_PLAN.md +437 -0
- package/docs/configuration.md +94 -0
- package/docs/tools.md +34 -0
- package/docs/troubleshooting.md +59 -0
- package/docs/usage-guide.md +245 -0
- package/package.json +82 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Timeout Manager for NotebookLM MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable session timeout enforcement:
|
|
5
|
+
* - Hard timeout: Maximum session lifetime (default: 8 hours)
|
|
6
|
+
* - Inactivity timeout: Auto-logout after idle period (default: 30 minutes)
|
|
7
|
+
* - Warning callbacks before timeout
|
|
8
|
+
* - Memory scrubbing on timeout
|
|
9
|
+
*
|
|
10
|
+
* Added by Pantheon Security for hardened fork.
|
|
11
|
+
*/
|
|
12
|
+
import { audit } from "../utils/audit-logger.js";
|
|
13
|
+
import { log } from "../utils/logger.js";
|
|
14
|
+
/**
|
|
15
|
+
* Get timeout configuration from environment
|
|
16
|
+
*/
|
|
17
|
+
function getDefaultConfig() {
|
|
18
|
+
const maxLifetimeSec = parseInt(process.env.NLMCP_SESSION_MAX_LIFETIME || "28800", 10); // 8 hours
|
|
19
|
+
const inactivitySec = parseInt(process.env.NLMCP_SESSION_INACTIVITY_TIMEOUT || "1800", 10); // 30 minutes
|
|
20
|
+
const warningBeforeSec = parseInt(process.env.NLMCP_SESSION_WARNING_BEFORE || "300", 10); // 5 minutes
|
|
21
|
+
return {
|
|
22
|
+
maxLifetimeMs: maxLifetimeSec * 1000,
|
|
23
|
+
inactivityTimeoutMs: inactivitySec * 1000,
|
|
24
|
+
warningBeforeMs: warningBeforeSec * 1000,
|
|
25
|
+
enableHardTimeout: process.env.NLMCP_SESSION_HARD_TIMEOUT !== "false",
|
|
26
|
+
enableInactivityTimeout: process.env.NLMCP_SESSION_INACTIVITY !== "false",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Session Timeout Manager
|
|
31
|
+
*
|
|
32
|
+
* Manages session timeouts with configurable lifetime and inactivity limits.
|
|
33
|
+
*/
|
|
34
|
+
export class SessionTimeoutManager {
|
|
35
|
+
config;
|
|
36
|
+
sessions = new Map();
|
|
37
|
+
checkInterval = null;
|
|
38
|
+
onTimeout = null;
|
|
39
|
+
onWarning = null;
|
|
40
|
+
constructor(config) {
|
|
41
|
+
this.config = { ...getDefaultConfig(), ...config };
|
|
42
|
+
// Start periodic check
|
|
43
|
+
this.startPeriodicCheck();
|
|
44
|
+
log.info(`🕐 Session timeout manager initialized`);
|
|
45
|
+
log.info(` Max lifetime: ${this.formatDuration(this.config.maxLifetimeMs)}`);
|
|
46
|
+
log.info(` Inactivity timeout: ${this.formatDuration(this.config.inactivityTimeoutMs)}`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format duration for logging
|
|
50
|
+
*/
|
|
51
|
+
formatDuration(ms) {
|
|
52
|
+
const seconds = Math.floor(ms / 1000);
|
|
53
|
+
const minutes = Math.floor(seconds / 60);
|
|
54
|
+
const hours = Math.floor(minutes / 60);
|
|
55
|
+
if (hours > 0) {
|
|
56
|
+
return `${hours}h ${minutes % 60}m`;
|
|
57
|
+
}
|
|
58
|
+
else if (minutes > 0) {
|
|
59
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return `${seconds}s`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Register a new session
|
|
67
|
+
*/
|
|
68
|
+
startSession(sessionId) {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
this.sessions.set(sessionId, {
|
|
71
|
+
sessionId,
|
|
72
|
+
createdAt: now,
|
|
73
|
+
lastActivity: now,
|
|
74
|
+
warningIssued: false,
|
|
75
|
+
inactivityWarningIssued: false,
|
|
76
|
+
});
|
|
77
|
+
log.info(`🕐 Session ${sessionId} registered with timeout manager`);
|
|
78
|
+
audit.session("session_timeout_started", sessionId, {
|
|
79
|
+
max_lifetime_ms: this.config.maxLifetimeMs,
|
|
80
|
+
inactivity_timeout_ms: this.config.inactivityTimeoutMs,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Update session activity (reset inactivity timer)
|
|
85
|
+
*/
|
|
86
|
+
touchSession(sessionId) {
|
|
87
|
+
const state = this.sessions.get(sessionId);
|
|
88
|
+
if (state) {
|
|
89
|
+
state.lastActivity = Date.now();
|
|
90
|
+
state.inactivityWarningIssued = false; // Reset warning after activity
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Remove a session from tracking
|
|
95
|
+
*/
|
|
96
|
+
removeSession(sessionId) {
|
|
97
|
+
this.sessions.delete(sessionId);
|
|
98
|
+
log.info(`🕐 Session ${sessionId} removed from timeout manager`);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if a session has expired
|
|
102
|
+
*/
|
|
103
|
+
isExpired(sessionId) {
|
|
104
|
+
const state = this.sessions.get(sessionId);
|
|
105
|
+
if (!state) {
|
|
106
|
+
return { expired: false };
|
|
107
|
+
}
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
// Check hard timeout (max lifetime)
|
|
110
|
+
if (this.config.enableHardTimeout) {
|
|
111
|
+
const age = now - state.createdAt;
|
|
112
|
+
if (age >= this.config.maxLifetimeMs) {
|
|
113
|
+
return { expired: true, reason: "lifetime" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check inactivity timeout
|
|
117
|
+
if (this.config.enableInactivityTimeout) {
|
|
118
|
+
const inactive = now - state.lastActivity;
|
|
119
|
+
if (inactive >= this.config.inactivityTimeoutMs) {
|
|
120
|
+
return { expired: true, reason: "inactivity" };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { expired: false };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get time remaining for a session
|
|
127
|
+
*/
|
|
128
|
+
getTimeRemaining(sessionId) {
|
|
129
|
+
const state = this.sessions.get(sessionId);
|
|
130
|
+
if (!state) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const lifetimeRemaining = this.config.enableHardTimeout
|
|
135
|
+
? Math.max(0, this.config.maxLifetimeMs - (now - state.createdAt))
|
|
136
|
+
: Infinity;
|
|
137
|
+
const inactivityRemaining = this.config.enableInactivityTimeout
|
|
138
|
+
? Math.max(0, this.config.inactivityTimeoutMs - (now - state.lastActivity))
|
|
139
|
+
: Infinity;
|
|
140
|
+
return {
|
|
141
|
+
lifetime: lifetimeRemaining,
|
|
142
|
+
inactivity: inactivityRemaining,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get session info for all tracked sessions
|
|
147
|
+
*/
|
|
148
|
+
getAllSessionsInfo() {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
const results = [];
|
|
151
|
+
for (const state of this.sessions.values()) {
|
|
152
|
+
const remaining = this.getTimeRemaining(state.sessionId);
|
|
153
|
+
results.push({
|
|
154
|
+
sessionId: state.sessionId,
|
|
155
|
+
createdAt: state.createdAt,
|
|
156
|
+
lastActivity: state.lastActivity,
|
|
157
|
+
ageMs: now - state.createdAt,
|
|
158
|
+
inactiveMs: now - state.lastActivity,
|
|
159
|
+
lifetimeRemainingMs: remaining?.lifetime ?? 0,
|
|
160
|
+
inactivityRemainingMs: remaining?.inactivity ?? 0,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return results;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Set callback for when a session times out
|
|
167
|
+
*/
|
|
168
|
+
setTimeoutCallback(callback) {
|
|
169
|
+
this.onTimeout = callback;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Set callback for timeout warnings
|
|
173
|
+
*/
|
|
174
|
+
setWarningCallback(callback) {
|
|
175
|
+
this.onWarning = callback;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Start periodic timeout check
|
|
179
|
+
*/
|
|
180
|
+
startPeriodicCheck() {
|
|
181
|
+
// Check every 30 seconds
|
|
182
|
+
this.checkInterval = setInterval(() => {
|
|
183
|
+
this.checkAllSessions();
|
|
184
|
+
}, 30000);
|
|
185
|
+
// Don't prevent process exit
|
|
186
|
+
this.checkInterval.unref();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check all sessions for expiry and warnings
|
|
190
|
+
*/
|
|
191
|
+
async checkAllSessions() {
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
for (const state of this.sessions.values()) {
|
|
194
|
+
// Check for expiry
|
|
195
|
+
const expiry = this.isExpired(state.sessionId);
|
|
196
|
+
if (expiry.expired && expiry.reason) {
|
|
197
|
+
log.warning(`🕐 Session ${state.sessionId} expired due to ${expiry.reason}`);
|
|
198
|
+
await audit.session("session_timeout_expired", state.sessionId, {
|
|
199
|
+
reason: expiry.reason,
|
|
200
|
+
age_ms: now - state.createdAt,
|
|
201
|
+
inactive_ms: now - state.lastActivity,
|
|
202
|
+
});
|
|
203
|
+
if (this.onTimeout) {
|
|
204
|
+
await this.onTimeout(state.sessionId, expiry.reason);
|
|
205
|
+
}
|
|
206
|
+
this.sessions.delete(state.sessionId);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
// Check for warnings
|
|
210
|
+
const remaining = this.getTimeRemaining(state.sessionId);
|
|
211
|
+
if (!remaining)
|
|
212
|
+
continue;
|
|
213
|
+
// Lifetime warning
|
|
214
|
+
if (this.config.enableHardTimeout &&
|
|
215
|
+
!state.warningIssued &&
|
|
216
|
+
remaining.lifetime <= this.config.warningBeforeMs) {
|
|
217
|
+
state.warningIssued = true;
|
|
218
|
+
log.warning(`🕐 Session ${state.sessionId} will expire in ${this.formatDuration(remaining.lifetime)} (max lifetime)`);
|
|
219
|
+
if (this.onWarning) {
|
|
220
|
+
this.onWarning(state.sessionId, "lifetime", remaining.lifetime);
|
|
221
|
+
}
|
|
222
|
+
await audit.session("session_timeout_warning", state.sessionId, {
|
|
223
|
+
reason: "lifetime",
|
|
224
|
+
remaining_ms: remaining.lifetime,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
// Inactivity warning
|
|
228
|
+
if (this.config.enableInactivityTimeout &&
|
|
229
|
+
!state.inactivityWarningIssued &&
|
|
230
|
+
remaining.inactivity <= this.config.warningBeforeMs) {
|
|
231
|
+
state.inactivityWarningIssued = true;
|
|
232
|
+
log.warning(`🕐 Session ${state.sessionId} will expire in ${this.formatDuration(remaining.inactivity)} (inactivity)`);
|
|
233
|
+
if (this.onWarning) {
|
|
234
|
+
this.onWarning(state.sessionId, "inactivity", remaining.inactivity);
|
|
235
|
+
}
|
|
236
|
+
await audit.session("session_timeout_warning", state.sessionId, {
|
|
237
|
+
reason: "inactivity",
|
|
238
|
+
remaining_ms: remaining.inactivity,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Stop the timeout manager
|
|
245
|
+
*/
|
|
246
|
+
stop() {
|
|
247
|
+
if (this.checkInterval) {
|
|
248
|
+
clearInterval(this.checkInterval);
|
|
249
|
+
this.checkInterval = null;
|
|
250
|
+
}
|
|
251
|
+
this.sessions.clear();
|
|
252
|
+
log.info(`🕐 Session timeout manager stopped`);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get configuration
|
|
256
|
+
*/
|
|
257
|
+
getConfig() {
|
|
258
|
+
return { ...this.config };
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Update configuration
|
|
262
|
+
*/
|
|
263
|
+
updateConfig(config) {
|
|
264
|
+
this.config = { ...this.config, ...config };
|
|
265
|
+
log.info(`🕐 Session timeout config updated`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Global timeout manager instance
|
|
270
|
+
*/
|
|
271
|
+
let globalTimeoutManager = null;
|
|
272
|
+
/**
|
|
273
|
+
* Get or create the global timeout manager
|
|
274
|
+
*/
|
|
275
|
+
export function getSessionTimeoutManager() {
|
|
276
|
+
if (!globalTimeoutManager) {
|
|
277
|
+
globalTimeoutManager = new SessionTimeoutManager();
|
|
278
|
+
}
|
|
279
|
+
return globalTimeoutManager;
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=session-timeout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-timeout.js","sourceRoot":"","sources":["../../src/session/session-timeout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Context Manager with Persistent Chrome Profile
|
|
3
|
+
*
|
|
4
|
+
* Manages ONE global persistent BrowserContext for ALL sessions.
|
|
5
|
+
* This is critical for avoiding bot detection:
|
|
6
|
+
*
|
|
7
|
+
* - Google tracks browser fingerprints (Canvas, WebGL, Audio, Fonts, etc.)
|
|
8
|
+
* - Multiple contexts = Multiple fingerprints = Suspicious!
|
|
9
|
+
* - ONE persistent context = ONE consistent fingerprint = Normal user
|
|
10
|
+
* - Persistent user_data_dir = SAME fingerprint across all app restarts!
|
|
11
|
+
*
|
|
12
|
+
* Based on the Python implementation from shared_context_manager.py
|
|
13
|
+
*/
|
|
14
|
+
import type { BrowserContext } from "patchright";
|
|
15
|
+
import { AuthManager } from "../auth/auth-manager.js";
|
|
16
|
+
/**
|
|
17
|
+
* Shared Context Manager
|
|
18
|
+
*
|
|
19
|
+
* Benefits:
|
|
20
|
+
* 1. ONE consistent browser fingerprint for all sessions
|
|
21
|
+
* 2. Fingerprint persists across app restarts (user_data_dir)
|
|
22
|
+
* 3. Mimics real user behavior (one browser, multiple tabs)
|
|
23
|
+
* 4. Google sees: "Same browser since day 1"
|
|
24
|
+
*/
|
|
25
|
+
export declare class SharedContextManager {
|
|
26
|
+
private authManager;
|
|
27
|
+
private globalContext;
|
|
28
|
+
private contextCreatedAt;
|
|
29
|
+
private currentProfileDir;
|
|
30
|
+
private isIsolatedProfile;
|
|
31
|
+
private currentHeadlessMode;
|
|
32
|
+
constructor(authManager: AuthManager);
|
|
33
|
+
/**
|
|
34
|
+
* Get the global shared persistent context, or create new if needed
|
|
35
|
+
*
|
|
36
|
+
* Context is recreated only when:
|
|
37
|
+
* - First time (no context exists in this app instance)
|
|
38
|
+
* - Context was closed/invalid
|
|
39
|
+
*
|
|
40
|
+
* Note: Auth expiry does NOT recreate context - we reuse the SAME
|
|
41
|
+
* fingerprint and just re-login!
|
|
42
|
+
*
|
|
43
|
+
* @param overrideHeadless Optional override for headless mode (true = show browser)
|
|
44
|
+
*/
|
|
45
|
+
getOrCreateContext(overrideHeadless?: boolean): Promise<BrowserContext>;
|
|
46
|
+
/**
|
|
47
|
+
* Check if global context needs to be recreated
|
|
48
|
+
*/
|
|
49
|
+
private needsRecreation;
|
|
50
|
+
/**
|
|
51
|
+
* Create/Load the global PERSISTENT context with Chrome user_data_dir
|
|
52
|
+
*
|
|
53
|
+
* This is THE KEY to fingerprint persistence!
|
|
54
|
+
*
|
|
55
|
+
* First time:
|
|
56
|
+
* - Chrome creates new profile in user_data_dir
|
|
57
|
+
* - Generates fingerprint (Canvas, WebGL, Audio, etc.)
|
|
58
|
+
* - Saves everything to disk
|
|
59
|
+
*
|
|
60
|
+
* Subsequent starts:
|
|
61
|
+
* - Chrome loads profile from user_data_dir
|
|
62
|
+
* - SAME fingerprint as before! ✅
|
|
63
|
+
* - Google sees: "Same browser since day 1"
|
|
64
|
+
*
|
|
65
|
+
* @param overrideHeadless Optional override for headless mode (true = show browser)
|
|
66
|
+
*/
|
|
67
|
+
private recreateContext;
|
|
68
|
+
/**
|
|
69
|
+
* Manually close the global context (e.g., on shutdown)
|
|
70
|
+
*
|
|
71
|
+
* Note: This closes the context for ALL sessions!
|
|
72
|
+
* Chrome will save everything to user_data_dir automatically.
|
|
73
|
+
*/
|
|
74
|
+
closeContext(): Promise<void>;
|
|
75
|
+
private prepareIsolatedProfileDir;
|
|
76
|
+
private pruneIsolatedProfiles;
|
|
77
|
+
private safeRemoveIsolatedProfile;
|
|
78
|
+
/**
|
|
79
|
+
* Get information about the global persistent context
|
|
80
|
+
*/
|
|
81
|
+
getContextInfo(): {
|
|
82
|
+
exists: boolean;
|
|
83
|
+
age_seconds?: number;
|
|
84
|
+
age_hours?: number;
|
|
85
|
+
fingerprint_id?: string;
|
|
86
|
+
user_data_dir: string;
|
|
87
|
+
persistent: boolean;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Get the current headless mode of the browser context
|
|
91
|
+
*
|
|
92
|
+
* @returns boolean | null - true if headless, false if visible, null if no context exists
|
|
93
|
+
*/
|
|
94
|
+
getCurrentHeadlessMode(): boolean | null;
|
|
95
|
+
/**
|
|
96
|
+
* Check if the browser context needs to be recreated due to headless mode change
|
|
97
|
+
*
|
|
98
|
+
* @param overrideHeadless - Optional override for headless mode (true = show browser)
|
|
99
|
+
* @returns boolean - true if context needs to be recreated with new mode
|
|
100
|
+
*/
|
|
101
|
+
needsHeadlessModeChange(overrideHeadless?: boolean): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Get context ID for logging
|
|
104
|
+
*/
|
|
105
|
+
private getContextId;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=shared-context-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-context-manager.d.ts","sourceRoot":"","sources":["../../src/session/shared-context-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAItD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,mBAAmB,CAAwB;gBAEvC,WAAW,EAAE,WAAW;IAapC;;;;;;;;;;;OAWG;IACG,kBAAkB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAkB7E;;OAEG;YACW,eAAe;IAqB7B;;;;;;;;;;;;;;;;OAgBG;YACW,eAAe;IA+H7B;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAkCrB,yBAAyB;YA2BzB,qBAAqB;YAwDrB,yBAAyB;IAgBvC;;OAEG;IACH,cAAc,IAAI;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,OAAO,CAAC;KACrB;IAwBD;;;;OAIG;IACH,sBAAsB,IAAI,OAAO,GAAG,IAAI;IAIxC;;;;;OAKG;IACH,uBAAuB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,OAAO;IAuB5D;;OAEG;IACH,OAAO,CAAC,YAAY;CAOrB"}
|