@pan-sec/notebooklm-mcp 1.10.5 → 1.10.8
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/auth/auth-manager.d.ts +3 -0
- package/dist/auth/auth-manager.d.ts.map +1 -1
- package/dist/auth/auth-manager.js +87 -70
- package/dist/auth/auth-manager.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/index.d.ts +7 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +7 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/query-logger.d.ts +144 -0
- package/dist/logging/query-logger.d.ts.map +1 -0
- package/dist/logging/query-logger.js +282 -0
- package/dist/logging/query-logger.js.map +1 -0
- package/dist/quota/quota-manager.d.ts +66 -2
- package/dist/quota/quota-manager.d.ts.map +1 -1
- package/dist/quota/quota-manager.js +220 -2
- package/dist/quota/quota-manager.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts +4 -0
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +43 -11
- package/dist/session/shared-context-manager.js.map +1 -1
- package/dist/tools/definitions/query-history.d.ts +9 -0
- package/dist/tools/definitions/query-history.d.ts.map +1 -0
- package/dist/tools/definitions/query-history.js +44 -0
- package/dist/tools/definitions/query-history.js.map +1 -0
- package/dist/tools/definitions/system.d.ts.map +1 -1
- package/dist/tools/definitions/system.js +12 -4
- package/dist/tools/definitions/system.js.map +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +2 -0
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts +47 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +152 -10
- package/dist/tools/handlers.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/file-lock.d.ts +79 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +222 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-logger.d.ts","sourceRoot":"","sources":["../../src/logging/query-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Logger for NotebookLM MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Provides persistent logging of all Q&A interactions with NotebookLM:
|
|
5
|
+
* - Full question and answer content
|
|
6
|
+
* - Session and notebook context
|
|
7
|
+
* - Quota information at time of query
|
|
8
|
+
* - Duration and metadata
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - JSONL format with daily rotation
|
|
12
|
+
* - 90-day retention (configurable)
|
|
13
|
+
* - Search and retrieval by session, notebook, date
|
|
14
|
+
* - Full content storage for research review
|
|
15
|
+
*/
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import crypto from "crypto";
|
|
19
|
+
import { CONFIG } from "../config.js";
|
|
20
|
+
import { mkdirSecure, appendFileSecure, PERMISSION_MODES, } from "../utils/file-permissions.js";
|
|
21
|
+
import { log } from "../utils/logger.js";
|
|
22
|
+
/**
|
|
23
|
+
* Get query logger configuration from environment
|
|
24
|
+
*/
|
|
25
|
+
function getQueryLoggerConfig() {
|
|
26
|
+
return {
|
|
27
|
+
enabled: process.env.NLMCP_QUERY_LOG_ENABLED !== "false",
|
|
28
|
+
logDir: process.env.NLMCP_QUERY_LOG_DIR || path.join(CONFIG.dataDir, "query_logs"),
|
|
29
|
+
retentionDays: parseInt(process.env.NLMCP_QUERY_LOG_RETENTION_DAYS || "90", 10),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate unique query ID
|
|
34
|
+
*/
|
|
35
|
+
function generateQueryId() {
|
|
36
|
+
return crypto.randomBytes(8).toString("hex");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Query Logger Class
|
|
40
|
+
*
|
|
41
|
+
* Logs all Q&A interactions to JSONL files for later review.
|
|
42
|
+
*/
|
|
43
|
+
export class QueryLogger {
|
|
44
|
+
config;
|
|
45
|
+
currentLogFile = "";
|
|
46
|
+
writeQueue = [];
|
|
47
|
+
isWriting = false;
|
|
48
|
+
stats = {
|
|
49
|
+
totalQueries: 0,
|
|
50
|
+
queriesThisSession: 0,
|
|
51
|
+
};
|
|
52
|
+
constructor(config) {
|
|
53
|
+
this.config = { ...getQueryLoggerConfig(), ...config };
|
|
54
|
+
if (this.config.enabled) {
|
|
55
|
+
this.ensureLogDirectory();
|
|
56
|
+
this.initializeLogFile();
|
|
57
|
+
this.cleanOldLogs();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Ensure query log directory exists
|
|
62
|
+
*/
|
|
63
|
+
ensureLogDirectory() {
|
|
64
|
+
mkdirSecure(this.config.logDir, PERMISSION_MODES.OWNER_FULL);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Initialize log file for today
|
|
68
|
+
*/
|
|
69
|
+
initializeLogFile() {
|
|
70
|
+
const today = new Date().toISOString().split("T")[0];
|
|
71
|
+
this.currentLogFile = path.join(this.config.logDir, `query-log-${today}.jsonl`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Clean up old log files based on retention policy
|
|
75
|
+
*/
|
|
76
|
+
cleanOldLogs() {
|
|
77
|
+
try {
|
|
78
|
+
const files = fs.readdirSync(this.config.logDir);
|
|
79
|
+
const cutoffDate = new Date();
|
|
80
|
+
cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);
|
|
81
|
+
let deletedCount = 0;
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
if (!file.startsWith("query-log-") || !file.endsWith(".jsonl"))
|
|
84
|
+
continue;
|
|
85
|
+
// Extract date from filename (query-log-YYYY-MM-DD.jsonl)
|
|
86
|
+
const dateStr = file.slice(10, 20);
|
|
87
|
+
const fileDate = new Date(dateStr);
|
|
88
|
+
if (fileDate < cutoffDate) {
|
|
89
|
+
fs.unlinkSync(path.join(this.config.logDir, file));
|
|
90
|
+
deletedCount++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (deletedCount > 0) {
|
|
94
|
+
log.info(`🗑️ Cleaned ${deletedCount} old query log files`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Ignore cleanup errors
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Write entry to log file
|
|
103
|
+
*/
|
|
104
|
+
async writeEntry(entry) {
|
|
105
|
+
this.writeQueue.push(entry);
|
|
106
|
+
if (this.isWriting)
|
|
107
|
+
return;
|
|
108
|
+
this.isWriting = true;
|
|
109
|
+
try {
|
|
110
|
+
while (this.writeQueue.length > 0) {
|
|
111
|
+
const batch = this.writeQueue.splice(0, 100);
|
|
112
|
+
const lines = batch.map(e => JSON.stringify(e)).join("\n") + "\n";
|
|
113
|
+
// Check if we need to rotate to new day's file
|
|
114
|
+
const today = new Date().toISOString().split("T")[0];
|
|
115
|
+
const expectedFile = path.join(this.config.logDir, `query-log-${today}.jsonl`);
|
|
116
|
+
if (this.currentLogFile !== expectedFile) {
|
|
117
|
+
this.currentLogFile = expectedFile;
|
|
118
|
+
}
|
|
119
|
+
appendFileSecure(this.currentLogFile, lines, PERMISSION_MODES.OWNER_READ_WRITE);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
this.isWriting = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Log a query (Q&A pair)
|
|
128
|
+
*/
|
|
129
|
+
async logQuery(entry) {
|
|
130
|
+
if (!this.config.enabled)
|
|
131
|
+
return "";
|
|
132
|
+
const queryId = generateQueryId();
|
|
133
|
+
const fullEntry = {
|
|
134
|
+
timestamp: new Date().toISOString(),
|
|
135
|
+
queryId,
|
|
136
|
+
...entry,
|
|
137
|
+
};
|
|
138
|
+
this.stats.totalQueries++;
|
|
139
|
+
this.stats.queriesThisSession++;
|
|
140
|
+
await this.writeEntry(fullEntry);
|
|
141
|
+
log.debug(`📝 Logged query ${queryId} (${entry.question.slice(0, 50)}...)`);
|
|
142
|
+
return queryId;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get all queries for a specific session
|
|
146
|
+
*/
|
|
147
|
+
async getQueriesForSession(sessionId) {
|
|
148
|
+
return this.filterQueries(entry => entry.sessionId === sessionId);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get all queries for a specific notebook URL
|
|
152
|
+
*/
|
|
153
|
+
async getQueriesForNotebook(notebookUrl) {
|
|
154
|
+
return this.filterQueries(entry => entry.notebookUrl === notebookUrl);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all queries for a specific notebook ID
|
|
158
|
+
*/
|
|
159
|
+
async getQueriesForNotebookId(notebookId) {
|
|
160
|
+
return this.filterQueries(entry => entry.notebookId === notebookId);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get all queries for a specific date (YYYY-MM-DD)
|
|
164
|
+
*/
|
|
165
|
+
async getQueriesForDate(date) {
|
|
166
|
+
const logFile = path.join(this.config.logDir, `query-log-${date}.jsonl`);
|
|
167
|
+
return this.readLogFile(logFile);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get recent queries
|
|
171
|
+
*/
|
|
172
|
+
async getRecentQueries(limit = 50) {
|
|
173
|
+
const allQueries = await this.getAllQueries();
|
|
174
|
+
// Sort by timestamp descending (most recent first)
|
|
175
|
+
allQueries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
176
|
+
return allQueries.slice(0, limit);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Search queries by pattern in question or answer
|
|
180
|
+
*/
|
|
181
|
+
async searchQueries(pattern, options) {
|
|
182
|
+
const limit = options?.limit ?? 100;
|
|
183
|
+
const caseSensitive = options?.caseSensitive ?? false;
|
|
184
|
+
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
|
|
185
|
+
const matches = await this.filterQueries(entry => {
|
|
186
|
+
const question = caseSensitive ? entry.question : entry.question.toLowerCase();
|
|
187
|
+
const answer = caseSensitive ? entry.answer : entry.answer.toLowerCase();
|
|
188
|
+
return question.includes(searchPattern) || answer.includes(searchPattern);
|
|
189
|
+
});
|
|
190
|
+
// Sort by timestamp descending
|
|
191
|
+
matches.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
192
|
+
return matches.slice(0, limit);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get all available log files
|
|
196
|
+
*/
|
|
197
|
+
getLogFiles() {
|
|
198
|
+
try {
|
|
199
|
+
const files = fs.readdirSync(this.config.logDir);
|
|
200
|
+
return files
|
|
201
|
+
.filter(f => f.startsWith("query-log-") && f.endsWith(".jsonl"))
|
|
202
|
+
.sort()
|
|
203
|
+
.reverse(); // Most recent first
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get statistics
|
|
211
|
+
*/
|
|
212
|
+
getStats() {
|
|
213
|
+
return { ...this.stats };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Force flush any pending writes
|
|
217
|
+
*/
|
|
218
|
+
async flush() {
|
|
219
|
+
while (this.isWriting || this.writeQueue.length > 0) {
|
|
220
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ============================================================================
|
|
224
|
+
// Private Helper Methods
|
|
225
|
+
// ============================================================================
|
|
226
|
+
/**
|
|
227
|
+
* Read and parse a log file
|
|
228
|
+
*/
|
|
229
|
+
readLogFile(filePath) {
|
|
230
|
+
if (!fs.existsSync(filePath))
|
|
231
|
+
return [];
|
|
232
|
+
try {
|
|
233
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
234
|
+
const lines = content.trim().split("\n").filter(l => l.length > 0);
|
|
235
|
+
return lines.map(line => JSON.parse(line));
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
log.warning(`⚠️ Failed to read query log ${filePath}: ${error}`);
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get all queries from all log files
|
|
244
|
+
*/
|
|
245
|
+
async getAllQueries() {
|
|
246
|
+
const logFiles = this.getLogFiles();
|
|
247
|
+
const allQueries = [];
|
|
248
|
+
for (const file of logFiles) {
|
|
249
|
+
const filePath = path.join(this.config.logDir, file);
|
|
250
|
+
const entries = this.readLogFile(filePath);
|
|
251
|
+
allQueries.push(...entries);
|
|
252
|
+
}
|
|
253
|
+
return allQueries;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Filter queries across all log files
|
|
257
|
+
*/
|
|
258
|
+
async filterQueries(predicate) {
|
|
259
|
+
const allQueries = await this.getAllQueries();
|
|
260
|
+
return allQueries.filter(predicate);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Global query logger instance
|
|
265
|
+
*/
|
|
266
|
+
let globalQueryLogger = null;
|
|
267
|
+
/**
|
|
268
|
+
* Get or create the global query logger
|
|
269
|
+
*/
|
|
270
|
+
export function getQueryLogger() {
|
|
271
|
+
if (!globalQueryLogger) {
|
|
272
|
+
globalQueryLogger = new QueryLogger();
|
|
273
|
+
}
|
|
274
|
+
return globalQueryLogger;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Convenience function for quick query logging
|
|
278
|
+
*/
|
|
279
|
+
export async function logQuery(entry) {
|
|
280
|
+
return getQueryLogger().logQuery(entry);
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=query-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-logger.js","sourceRoot":"","sources":["../../src/logging/query-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,6 +48,24 @@ export declare class QuotaManager {
|
|
|
48
48
|
* Extract source limit from source dialog (e.g., "0/300")
|
|
49
49
|
*/
|
|
50
50
|
extractSourceLimitFromDialog(page: Page): Promise<number | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Extract query usage from NotebookLM UI
|
|
53
|
+
*
|
|
54
|
+
* Looks for patterns like:
|
|
55
|
+
* - "X/50 queries" or "X of 50 queries"
|
|
56
|
+
* - "X queries remaining"
|
|
57
|
+
* - Usage indicators in settings/account area
|
|
58
|
+
*
|
|
59
|
+
* Returns { used, limit } or null if not found
|
|
60
|
+
*/
|
|
61
|
+
extractQueryUsageFromUI(page: Page): Promise<{
|
|
62
|
+
used: number;
|
|
63
|
+
limit: number;
|
|
64
|
+
} | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Check for rate limit error message on page
|
|
67
|
+
*/
|
|
68
|
+
checkForRateLimitError(page: Page): Promise<boolean>;
|
|
51
69
|
/**
|
|
52
70
|
* Count notebooks from homepage
|
|
53
71
|
*/
|
|
@@ -55,7 +73,14 @@ export declare class QuotaManager {
|
|
|
55
73
|
/**
|
|
56
74
|
* Update quota from UI scraping
|
|
57
75
|
*/
|
|
58
|
-
updateFromUI(page: Page): Promise<
|
|
76
|
+
updateFromUI(page: Page): Promise<{
|
|
77
|
+
tier: LicenseTier;
|
|
78
|
+
queryUsageFromGoogle: {
|
|
79
|
+
used: number;
|
|
80
|
+
limit: number;
|
|
81
|
+
} | null;
|
|
82
|
+
rateLimitDetected: boolean;
|
|
83
|
+
}>;
|
|
59
84
|
/**
|
|
60
85
|
* Manually set tier (for user override)
|
|
61
86
|
*/
|
|
@@ -77,9 +102,23 @@ export declare class QuotaManager {
|
|
|
77
102
|
*/
|
|
78
103
|
incrementNotebookCount(): void;
|
|
79
104
|
/**
|
|
80
|
-
* Increment query count
|
|
105
|
+
* Increment query count (synchronous, for backwards compatibility)
|
|
106
|
+
* Note: For concurrent safety, use incrementQueryCountAtomic() instead
|
|
81
107
|
*/
|
|
82
108
|
incrementQueryCount(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Increment query count atomically with file locking
|
|
111
|
+
*
|
|
112
|
+
* This method is safe for concurrent access from multiple processes/sessions.
|
|
113
|
+
* It reloads settings from disk before incrementing to ensure accuracy.
|
|
114
|
+
*/
|
|
115
|
+
incrementQueryCountAtomic(): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Refresh settings from disk with file locking
|
|
118
|
+
*
|
|
119
|
+
* Use this to ensure you have the latest quota state from disk.
|
|
120
|
+
*/
|
|
121
|
+
refreshSettings(): Promise<QuotaSettings>;
|
|
83
122
|
/**
|
|
84
123
|
* Check if can create notebook
|
|
85
124
|
*/
|
|
@@ -120,6 +159,31 @@ export declare class QuotaManager {
|
|
|
120
159
|
percent: number;
|
|
121
160
|
};
|
|
122
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* Get detailed quota status with remaining counts, warnings, and stop signals
|
|
164
|
+
* Used to provide visibility to users about when to stop querying for the day
|
|
165
|
+
*/
|
|
166
|
+
getDetailedStatus(): {
|
|
167
|
+
tier: LicenseTier;
|
|
168
|
+
queries: {
|
|
169
|
+
used: number;
|
|
170
|
+
limit: number;
|
|
171
|
+
remaining: number;
|
|
172
|
+
percentUsed: number;
|
|
173
|
+
shouldStop: boolean;
|
|
174
|
+
resetTime: string;
|
|
175
|
+
};
|
|
176
|
+
notebooks: {
|
|
177
|
+
used: number;
|
|
178
|
+
limit: number;
|
|
179
|
+
remaining: number;
|
|
180
|
+
percentUsed: number;
|
|
181
|
+
};
|
|
182
|
+
sources: {
|
|
183
|
+
limit: number;
|
|
184
|
+
};
|
|
185
|
+
warnings: string[];
|
|
186
|
+
};
|
|
123
187
|
}
|
|
124
188
|
export declare function getQuotaManager(): QuotaManager;
|
|
125
189
|
//# sourceMappingURL=quota-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quota-manager.d.ts","sourceRoot":"","sources":["../../src/quota/quota-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"quota-manager.d.ts","sourceRoot":"","sources":["../../src/quota/quota-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOvC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB;AAgCD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,YAAY,CAAS;;IAO7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAiBpB;;;OAGG;IACG,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IA0C1D;;OAEG;IACG,4BAA4B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAetE;;;;;;;;;OASG;IACG,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuE1F;;OAEG;IACG,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB1D;;OAEG;IACG,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBzD;;OAEG;IACG,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QACtC,IAAI,EAAE,WAAW,CAAC;QAClB,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QAC7D,iBAAiB,EAAE,OAAO,CAAC;KAC5B,CAAC;IAoDF;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAQhC;;OAEG;IACH,WAAW,IAAI,aAAa;IAI5B;;OAEG;IACH,SAAS,IAAI,WAAW;IAIxB;;OAEG;IACH,QAAQ,IAAI,UAAU;IAItB;;OAEG;IACH,sBAAsB,IAAI,IAAI;IAM9B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAc3B;;;;;OAKG;IACG,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkChD;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC;IAO/C;;OAEG;IACH,iBAAiB,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAmB1D;;OAEG;IACH,YAAY,CAAC,kBAAkB,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAa/E;;OAEG;IACH,YAAY,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IA2BrD;;OAEG;IACH,SAAS,IAAI;QACX,IAAI,EAAE,WAAW,CAAC;QAClB,SAAS,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5D,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAC3D;IAqBD;;;OAGG;IACH,iBAAiB,IAAI;QACnB,IAAI,EAAE,WAAW,CAAC;QAClB,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;YACd,SAAS,EAAE,MAAM,CAAC;YAClB,WAAW,EAAE,MAAM,CAAC;YACpB,UAAU,EAAE,OAAO,CAAC;YACpB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,SAAS,EAAE;YACT,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;YACd,SAAS,EAAE,MAAM,CAAC;YAClB,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC;QACF,OAAO,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB;CA4DF;AAKD,wBAAgB,eAAe,IAAI,YAAY,CAK9C"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { log } from "../utils/logger.js";
|
|
7
7
|
import { CONFIG } from "../config.js";
|
|
8
|
+
import { withLock } from "../utils/file-lock.js";
|
|
8
9
|
import fs from "fs";
|
|
9
10
|
import path from "path";
|
|
10
11
|
// Known limits by tier (based on NotebookLM documentation Dec 2025)
|
|
@@ -147,6 +148,100 @@ export class QuotaManager {
|
|
|
147
148
|
});
|
|
148
149
|
return limitInfo;
|
|
149
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Extract query usage from NotebookLM UI
|
|
153
|
+
*
|
|
154
|
+
* Looks for patterns like:
|
|
155
|
+
* - "X/50 queries" or "X of 50 queries"
|
|
156
|
+
* - "X queries remaining"
|
|
157
|
+
* - Usage indicators in settings/account area
|
|
158
|
+
*
|
|
159
|
+
* Returns { used, limit } or null if not found
|
|
160
|
+
*/
|
|
161
|
+
async extractQueryUsageFromUI(page) {
|
|
162
|
+
log.info("🔍 Looking for query usage in UI...");
|
|
163
|
+
const usageInfo = await page.evaluate(() => {
|
|
164
|
+
// @ts-expect-error - DOM types
|
|
165
|
+
const allText = document.body.innerText;
|
|
166
|
+
// Pattern 1: "X/Y queries" or "X / Y queries"
|
|
167
|
+
const slashPattern = allText.match(/(\d+)\s*\/\s*(\d+)\s*quer(?:y|ies)/i);
|
|
168
|
+
if (slashPattern) {
|
|
169
|
+
return {
|
|
170
|
+
used: parseInt(slashPattern[1], 10),
|
|
171
|
+
limit: parseInt(slashPattern[2], 10),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// Pattern 2: "X of Y queries"
|
|
175
|
+
const ofPattern = allText.match(/(\d+)\s+of\s+(\d+)\s*quer(?:y|ies)/i);
|
|
176
|
+
if (ofPattern) {
|
|
177
|
+
return {
|
|
178
|
+
used: parseInt(ofPattern[1], 10),
|
|
179
|
+
limit: parseInt(ofPattern[2], 10),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Pattern 3: "X queries remaining" with known limits
|
|
183
|
+
const remainingPattern = allText.match(/(\d+)\s*quer(?:y|ies)\s*remaining/i);
|
|
184
|
+
if (remainingPattern) {
|
|
185
|
+
const remaining = parseInt(remainingPattern[1], 10);
|
|
186
|
+
// Infer limit from known tiers
|
|
187
|
+
let limit = 50; // default free
|
|
188
|
+
if (remaining > 50)
|
|
189
|
+
limit = 500; // pro
|
|
190
|
+
if (remaining > 500)
|
|
191
|
+
limit = 5000; // ultra
|
|
192
|
+
return {
|
|
193
|
+
used: limit - remaining,
|
|
194
|
+
limit,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Pattern 4: "You have used X queries today"
|
|
198
|
+
const usedPattern = allText.match(/(?:used|made)\s*(\d+)\s*quer(?:y|ies)/i);
|
|
199
|
+
if (usedPattern) {
|
|
200
|
+
const used = parseInt(usedPattern[1], 10);
|
|
201
|
+
// Infer tier from usage
|
|
202
|
+
let limit = 50;
|
|
203
|
+
if (used > 50)
|
|
204
|
+
limit = 500;
|
|
205
|
+
if (used > 500)
|
|
206
|
+
limit = 5000;
|
|
207
|
+
return { used, limit };
|
|
208
|
+
}
|
|
209
|
+
// Pattern 5: Look for rate limit message
|
|
210
|
+
const rateLimitPattern = allText.match(/(?:limit|quota)\s*(?:reached|exceeded)/i);
|
|
211
|
+
if (rateLimitPattern) {
|
|
212
|
+
// At limit - try to find the number
|
|
213
|
+
const limitNum = allText.match(/(\d+)\s*(?:daily|per day)/i);
|
|
214
|
+
const limit = limitNum ? parseInt(limitNum[1], 10) : 50;
|
|
215
|
+
return { used: limit, limit };
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
});
|
|
219
|
+
if (usageInfo) {
|
|
220
|
+
log.info(` Found query usage: ${usageInfo.used}/${usageInfo.limit}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
log.info(" No query usage found in UI");
|
|
224
|
+
}
|
|
225
|
+
return usageInfo;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check for rate limit error message on page
|
|
229
|
+
*/
|
|
230
|
+
async checkForRateLimitError(page) {
|
|
231
|
+
const isRateLimited = await page.evaluate(() => {
|
|
232
|
+
// @ts-expect-error - DOM types
|
|
233
|
+
const allText = document.body.innerText.toLowerCase();
|
|
234
|
+
return (allText.includes("rate limit") ||
|
|
235
|
+
allText.includes("quota exceeded") ||
|
|
236
|
+
allText.includes("too many requests") ||
|
|
237
|
+
allText.includes("daily limit reached") ||
|
|
238
|
+
allText.includes("try again tomorrow"));
|
|
239
|
+
});
|
|
240
|
+
if (isRateLimited) {
|
|
241
|
+
log.warning("⚠️ Rate limit detected in UI!");
|
|
242
|
+
}
|
|
243
|
+
return isRateLimited;
|
|
244
|
+
}
|
|
150
245
|
/**
|
|
151
246
|
* Count notebooks from homepage
|
|
152
247
|
*/
|
|
@@ -187,9 +282,29 @@ export class QuotaManager {
|
|
|
187
282
|
if (sourceLimit) {
|
|
188
283
|
this.settings.limits.sourcesPerNotebook = sourceLimit;
|
|
189
284
|
}
|
|
285
|
+
// Try to extract query usage from UI
|
|
286
|
+
const queryUsage = await this.extractQueryUsageFromUI(page);
|
|
287
|
+
if (queryUsage) {
|
|
288
|
+
// Update local tracking with Google's numbers
|
|
289
|
+
this.settings.usage.queriesUsedToday = queryUsage.used;
|
|
290
|
+
this.settings.limits.queriesPerDay = queryUsage.limit;
|
|
291
|
+
this.settings.usage.lastQueryDate = new Date().toISOString().split("T")[0];
|
|
292
|
+
log.info(` Synced query usage from Google: ${queryUsage.used}/${queryUsage.limit}`);
|
|
293
|
+
}
|
|
294
|
+
// Check for rate limit
|
|
295
|
+
const rateLimitDetected = await this.checkForRateLimitError(page);
|
|
296
|
+
if (rateLimitDetected) {
|
|
297
|
+
// Mark as at limit
|
|
298
|
+
this.settings.usage.queriesUsedToday = this.settings.limits.queriesPerDay;
|
|
299
|
+
}
|
|
190
300
|
this.settings.usage.lastUpdated = new Date().toISOString();
|
|
191
301
|
this.saveSettings();
|
|
192
|
-
log.success(`✅ Quota updated: tier=${this.settings.tier}, notebooks=${this.settings.usage.notebooks}`);
|
|
302
|
+
log.success(`✅ Quota updated: tier=${this.settings.tier}, notebooks=${this.settings.usage.notebooks}, queries=${this.settings.usage.queriesUsedToday}/${this.settings.limits.queriesPerDay}`);
|
|
303
|
+
return {
|
|
304
|
+
tier: this.settings.tier,
|
|
305
|
+
queryUsageFromGoogle: queryUsage,
|
|
306
|
+
rateLimitDetected,
|
|
307
|
+
};
|
|
193
308
|
}
|
|
194
309
|
/**
|
|
195
310
|
* Manually set tier (for user override)
|
|
@@ -228,7 +343,8 @@ export class QuotaManager {
|
|
|
228
343
|
this.saveSettings();
|
|
229
344
|
}
|
|
230
345
|
/**
|
|
231
|
-
* Increment query count
|
|
346
|
+
* Increment query count (synchronous, for backwards compatibility)
|
|
347
|
+
* Note: For concurrent safety, use incrementQueryCountAtomic() instead
|
|
232
348
|
*/
|
|
233
349
|
incrementQueryCount() {
|
|
234
350
|
const today = new Date().toISOString().split("T")[0];
|
|
@@ -241,6 +357,49 @@ export class QuotaManager {
|
|
|
241
357
|
this.settings.usage.lastUpdated = new Date().toISOString();
|
|
242
358
|
this.saveSettings();
|
|
243
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Increment query count atomically with file locking
|
|
362
|
+
*
|
|
363
|
+
* This method is safe for concurrent access from multiple processes/sessions.
|
|
364
|
+
* It reloads settings from disk before incrementing to ensure accuracy.
|
|
365
|
+
*/
|
|
366
|
+
async incrementQueryCountAtomic() {
|
|
367
|
+
await withLock(this.settingsPath, async () => {
|
|
368
|
+
// Reload latest settings from disk (another process may have updated)
|
|
369
|
+
this.settings = this.loadSettings();
|
|
370
|
+
const today = new Date().toISOString().split("T")[0];
|
|
371
|
+
// Reset if new day
|
|
372
|
+
if (this.settings.usage.lastQueryDate !== today) {
|
|
373
|
+
this.settings.usage.queriesUsedToday = 0;
|
|
374
|
+
this.settings.usage.lastQueryDate = today;
|
|
375
|
+
}
|
|
376
|
+
this.settings.usage.queriesUsedToday++;
|
|
377
|
+
this.settings.usage.lastUpdated = new Date().toISOString();
|
|
378
|
+
// Save with lock held
|
|
379
|
+
try {
|
|
380
|
+
const dir = path.dirname(this.settingsPath);
|
|
381
|
+
if (!fs.existsSync(dir)) {
|
|
382
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
383
|
+
}
|
|
384
|
+
fs.writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2), { mode: 0o600 });
|
|
385
|
+
log.debug(`💾 Quota incremented atomically (${this.settings.usage.queriesUsedToday} queries today)`);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
log.error(`❌ Could not save quota settings: ${error}`);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Refresh settings from disk with file locking
|
|
394
|
+
*
|
|
395
|
+
* Use this to ensure you have the latest quota state from disk.
|
|
396
|
+
*/
|
|
397
|
+
async refreshSettings() {
|
|
398
|
+
return await withLock(this.settingsPath, async () => {
|
|
399
|
+
this.settings = this.loadSettings();
|
|
400
|
+
return { ...this.settings };
|
|
401
|
+
});
|
|
402
|
+
}
|
|
244
403
|
/**
|
|
245
404
|
* Check if can create notebook
|
|
246
405
|
*/
|
|
@@ -318,6 +477,65 @@ export class QuotaManager {
|
|
|
318
477
|
},
|
|
319
478
|
};
|
|
320
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Get detailed quota status with remaining counts, warnings, and stop signals
|
|
482
|
+
* Used to provide visibility to users about when to stop querying for the day
|
|
483
|
+
*/
|
|
484
|
+
getDetailedStatus() {
|
|
485
|
+
const today = new Date().toISOString().split("T")[0];
|
|
486
|
+
// Reset if new day
|
|
487
|
+
if (this.settings.usage.lastQueryDate !== today) {
|
|
488
|
+
this.settings.usage.queriesUsedToday = 0;
|
|
489
|
+
this.settings.usage.lastQueryDate = today;
|
|
490
|
+
}
|
|
491
|
+
const { tier, limits, usage } = this.settings;
|
|
492
|
+
const queriesRemaining = limits.queriesPerDay - usage.queriesUsedToday;
|
|
493
|
+
const queriesPercentUsed = Math.round((usage.queriesUsedToday / limits.queriesPerDay) * 100);
|
|
494
|
+
const notebooksRemaining = limits.notebooks - usage.notebooks;
|
|
495
|
+
const notebooksPercentUsed = Math.round((usage.notebooks / limits.notebooks) * 100);
|
|
496
|
+
// Calculate next reset time (midnight local time)
|
|
497
|
+
const tomorrow = new Date();
|
|
498
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
499
|
+
tomorrow.setHours(0, 0, 0, 0);
|
|
500
|
+
// Build warnings list
|
|
501
|
+
const warnings = [];
|
|
502
|
+
if (queriesRemaining <= 0) {
|
|
503
|
+
warnings.push(`CRITICAL: Daily query limit reached (${usage.queriesUsedToday}/${limits.queriesPerDay}). Wait until tomorrow or upgrade your plan.`);
|
|
504
|
+
}
|
|
505
|
+
else if (queriesRemaining <= 5) {
|
|
506
|
+
warnings.push(`CRITICAL: Only ${queriesRemaining} queries remaining today! Consider stopping soon.`);
|
|
507
|
+
}
|
|
508
|
+
else if (queriesRemaining <= 10) {
|
|
509
|
+
warnings.push(`WARNING: Only ${queriesRemaining} queries remaining today.`);
|
|
510
|
+
}
|
|
511
|
+
else if (queriesPercentUsed >= 80) {
|
|
512
|
+
warnings.push(`INFO: ${queriesPercentUsed}% of daily queries used (${queriesRemaining} remaining).`);
|
|
513
|
+
}
|
|
514
|
+
if (notebooksRemaining <= 5) {
|
|
515
|
+
warnings.push(`WARNING: Only ${notebooksRemaining} notebook slots remaining.`);
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
tier,
|
|
519
|
+
queries: {
|
|
520
|
+
used: usage.queriesUsedToday,
|
|
521
|
+
limit: limits.queriesPerDay,
|
|
522
|
+
remaining: queriesRemaining,
|
|
523
|
+
percentUsed: queriesPercentUsed,
|
|
524
|
+
shouldStop: queriesRemaining <= 5,
|
|
525
|
+
resetTime: tomorrow.toISOString(),
|
|
526
|
+
},
|
|
527
|
+
notebooks: {
|
|
528
|
+
used: usage.notebooks,
|
|
529
|
+
limit: limits.notebooks,
|
|
530
|
+
remaining: notebooksRemaining,
|
|
531
|
+
percentUsed: notebooksPercentUsed,
|
|
532
|
+
},
|
|
533
|
+
sources: {
|
|
534
|
+
limit: limits.sourcesPerNotebook,
|
|
535
|
+
},
|
|
536
|
+
warnings,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
321
539
|
}
|
|
322
540
|
// Singleton instance
|
|
323
541
|
let quotaManagerInstance = null;
|