@pan-sec/notebooklm-mcp 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/config.d.ts +4 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +10 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/events/event-emitter.d.ts +45 -0
  6. package/dist/events/event-emitter.d.ts.map +1 -0
  7. package/dist/events/event-emitter.js +100 -0
  8. package/dist/events/event-emitter.js.map +1 -0
  9. package/dist/events/event-types.d.ts +124 -0
  10. package/dist/events/event-types.d.ts.map +1 -0
  11. package/dist/events/event-types.js +18 -0
  12. package/dist/events/event-types.js.map +1 -0
  13. package/dist/gemini/gemini-client.d.ts +45 -0
  14. package/dist/gemini/gemini-client.d.ts.map +1 -0
  15. package/dist/gemini/gemini-client.js +211 -0
  16. package/dist/gemini/gemini-client.js.map +1 -0
  17. package/dist/gemini/index.d.ts +8 -0
  18. package/dist/gemini/index.d.ts.map +1 -0
  19. package/dist/gemini/index.js +8 -0
  20. package/dist/gemini/index.js.map +1 -0
  21. package/dist/gemini/types.d.ts +136 -0
  22. package/dist/gemini/types.d.ts.map +1 -0
  23. package/dist/gemini/types.js +10 -0
  24. package/dist/gemini/types.js.map +1 -0
  25. package/dist/index.js +76 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/library/notebook-library.d.ts +25 -2
  28. package/dist/library/notebook-library.d.ts.map +1 -1
  29. package/dist/library/notebook-library.js +142 -2
  30. package/dist/library/notebook-library.js.map +1 -1
  31. package/dist/library/types.d.ts +15 -0
  32. package/dist/library/types.d.ts.map +1 -1
  33. package/dist/notebook-creation/audio-manager.d.ts +56 -0
  34. package/dist/notebook-creation/audio-manager.d.ts.map +1 -0
  35. package/dist/notebook-creation/audio-manager.js +335 -0
  36. package/dist/notebook-creation/audio-manager.js.map +1 -0
  37. package/dist/notebook-creation/discover-creation-flow.d.ts +8 -0
  38. package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -0
  39. package/dist/notebook-creation/discover-creation-flow.js +177 -0
  40. package/dist/notebook-creation/discover-creation-flow.js.map +1 -0
  41. package/dist/notebook-creation/discover-quota.d.ts +8 -0
  42. package/dist/notebook-creation/discover-quota.d.ts.map +1 -0
  43. package/dist/notebook-creation/discover-quota.js +195 -0
  44. package/dist/notebook-creation/discover-quota.js.map +1 -0
  45. package/dist/notebook-creation/discover-source-dialog.d.ts +8 -0
  46. package/dist/notebook-creation/discover-source-dialog.d.ts.map +1 -0
  47. package/dist/notebook-creation/discover-source-dialog.js +134 -0
  48. package/dist/notebook-creation/discover-source-dialog.js.map +1 -0
  49. package/dist/notebook-creation/discover-sources.d.ts +8 -0
  50. package/dist/notebook-creation/discover-sources.d.ts.map +1 -0
  51. package/dist/notebook-creation/discover-sources.js +273 -0
  52. package/dist/notebook-creation/discover-sources.js.map +1 -0
  53. package/dist/notebook-creation/discover-text-input.d.ts +7 -0
  54. package/dist/notebook-creation/discover-text-input.d.ts.map +1 -0
  55. package/dist/notebook-creation/discover-text-input.js +135 -0
  56. package/dist/notebook-creation/discover-text-input.js.map +1 -0
  57. package/dist/notebook-creation/index.d.ts +12 -0
  58. package/dist/notebook-creation/index.d.ts.map +1 -0
  59. package/dist/notebook-creation/index.js +12 -0
  60. package/dist/notebook-creation/index.js.map +1 -0
  61. package/dist/notebook-creation/notebook-creator.d.ts +95 -0
  62. package/dist/notebook-creation/notebook-creator.d.ts.map +1 -0
  63. package/dist/notebook-creation/notebook-creator.js +689 -0
  64. package/dist/notebook-creation/notebook-creator.js.map +1 -0
  65. package/dist/notebook-creation/notebook-sync.d.ts +93 -0
  66. package/dist/notebook-creation/notebook-sync.d.ts.map +1 -0
  67. package/dist/notebook-creation/notebook-sync.js +370 -0
  68. package/dist/notebook-creation/notebook-sync.js.map +1 -0
  69. package/dist/notebook-creation/run-discovery.d.ts +11 -0
  70. package/dist/notebook-creation/run-discovery.d.ts.map +1 -0
  71. package/dist/notebook-creation/run-discovery.js +151 -0
  72. package/dist/notebook-creation/run-discovery.js.map +1 -0
  73. package/dist/notebook-creation/selector-discovery.d.ts +65 -0
  74. package/dist/notebook-creation/selector-discovery.d.ts.map +1 -0
  75. package/dist/notebook-creation/selector-discovery.js +421 -0
  76. package/dist/notebook-creation/selector-discovery.js.map +1 -0
  77. package/dist/notebook-creation/selectors.d.ts +150 -0
  78. package/dist/notebook-creation/selectors.d.ts.map +1 -0
  79. package/dist/notebook-creation/selectors.js +225 -0
  80. package/dist/notebook-creation/selectors.js.map +1 -0
  81. package/dist/notebook-creation/source-manager.d.ts +73 -0
  82. package/dist/notebook-creation/source-manager.d.ts.map +1 -0
  83. package/dist/notebook-creation/source-manager.js +486 -0
  84. package/dist/notebook-creation/source-manager.js.map +1 -0
  85. package/dist/notebook-creation/test-create.d.ts +8 -0
  86. package/dist/notebook-creation/test-create.d.ts.map +1 -0
  87. package/dist/notebook-creation/test-create.js +72 -0
  88. package/dist/notebook-creation/test-create.js.map +1 -0
  89. package/dist/notebook-creation/types.d.ts +173 -0
  90. package/dist/notebook-creation/types.d.ts.map +1 -0
  91. package/dist/notebook-creation/types.js +5 -0
  92. package/dist/notebook-creation/types.js.map +1 -0
  93. package/dist/quota/index.d.ts +8 -0
  94. package/dist/quota/index.d.ts.map +1 -0
  95. package/dist/quota/index.js +8 -0
  96. package/dist/quota/index.js.map +1 -0
  97. package/dist/quota/quota-manager.d.ts +125 -0
  98. package/dist/quota/quota-manager.d.ts.map +1 -0
  99. package/dist/quota/quota-manager.js +330 -0
  100. package/dist/quota/quota-manager.js.map +1 -0
  101. package/dist/session/session-manager.d.ts +5 -0
  102. package/dist/session/session-manager.d.ts.map +1 -1
  103. package/dist/session/session-manager.js +6 -0
  104. package/dist/session/session-manager.js.map +1 -1
  105. package/dist/tools/definitions/gemini.d.ts +12 -0
  106. package/dist/tools/definitions/gemini.d.ts.map +1 -0
  107. package/dist/tools/definitions/gemini.js +135 -0
  108. package/dist/tools/definitions/gemini.js.map +1 -0
  109. package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
  110. package/dist/tools/definitions/notebook-management.js +525 -0
  111. package/dist/tools/definitions/notebook-management.js.map +1 -1
  112. package/dist/tools/definitions/system.d.ts.map +1 -1
  113. package/dist/tools/definitions/system.js +158 -0
  114. package/dist/tools/definitions/system.js.map +1 -1
  115. package/dist/tools/definitions.d.ts.map +1 -1
  116. package/dist/tools/definitions.js +2 -0
  117. package/dist/tools/definitions.js.map +1 -1
  118. package/dist/tools/handlers.d.ts +257 -0
  119. package/dist/tools/handlers.d.ts.map +1 -1
  120. package/dist/tools/handlers.js +1097 -0
  121. package/dist/tools/handlers.js.map +1 -1
  122. package/dist/webhooks/index.d.ts +8 -0
  123. package/dist/webhooks/index.d.ts.map +1 -0
  124. package/dist/webhooks/index.js +8 -0
  125. package/dist/webhooks/index.js.map +1 -0
  126. package/dist/webhooks/types.d.ts +57 -0
  127. package/dist/webhooks/types.d.ts.map +1 -0
  128. package/dist/webhooks/types.js +5 -0
  129. package/dist/webhooks/types.js.map +1 -0
  130. package/dist/webhooks/webhook-dispatcher.d.ts +120 -0
  131. package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -0
  132. package/dist/webhooks/webhook-dispatcher.js +519 -0
  133. package/dist/webhooks/webhook-dispatcher.js.map +1 -0
  134. package/package.json +2 -1
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Run NotebookLM UI Selector Discovery
4
+ *
5
+ * This script launches a browser, navigates NotebookLM's interface,
6
+ * and discovers CSS selectors for notebook creation elements.
7
+ *
8
+ * Usage: npx ts-node src/notebook-creation/run-discovery.ts
9
+ */
10
+ import { AuthManager } from "../auth/auth-manager.js";
11
+ import { SharedContextManager } from "../session/shared-context-manager.js";
12
+ import { discoverSelectors } from "./selector-discovery.js";
13
+ import { log } from "../utils/logger.js";
14
+ import { CONFIG } from "../config.js";
15
+ import fs from "fs";
16
+ import path from "path";
17
+ async function main() {
18
+ log.info("šŸš€ Starting NotebookLM UI Selector Discovery");
19
+ log.info(" This will open a browser and analyze the NotebookLM interface.");
20
+ log.info("");
21
+ // Initialize managers
22
+ const authManager = new AuthManager();
23
+ const contextManager = new SharedContextManager(authManager);
24
+ try {
25
+ // Get browser context (visible for debugging)
26
+ log.info("🌐 Getting browser context (visible mode)...");
27
+ const context = await contextManager.getOrCreateContext(false); // false = show browser
28
+ // Check authentication
29
+ log.info("šŸ” Checking authentication...");
30
+ const isAuthenticated = await authManager.validateCookiesExpiry(context);
31
+ if (!isAuthenticated) {
32
+ log.warning("āš ļø Not authenticated or session expired!");
33
+ log.warning(" Please login first using setup_auth tool.");
34
+ log.info(" Run the MCP server and use the setup_auth tool.");
35
+ process.exit(1);
36
+ }
37
+ log.success("āœ… Authenticated!");
38
+ // Run discovery
39
+ log.info("\nšŸ” Running selector discovery...\n");
40
+ const result = await discoverSelectors(context);
41
+ // Save results to file
42
+ const outputPath = path.join(CONFIG.dataDir, "discovered-selectors.json");
43
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
44
+ fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
45
+ log.success(`\nāœ… Discovery complete! Results saved to: ${outputPath}`);
46
+ // Generate selectors.ts content
47
+ if (Object.keys(result.selectors).length > 0) {
48
+ const selectorsCode = generateSelectorsCode(result);
49
+ const selectorsPath = path.join(path.dirname(new URL(import.meta.url).pathname), "selectors.ts");
50
+ fs.writeFileSync(selectorsPath, selectorsCode);
51
+ log.success(`šŸ“ Generated selectors.ts at: ${selectorsPath}`);
52
+ }
53
+ // Print summary
54
+ printSummary(result);
55
+ }
56
+ catch (error) {
57
+ log.error(`āŒ Discovery failed: ${error instanceof Error ? error.message : String(error)}`);
58
+ process.exit(1);
59
+ }
60
+ finally {
61
+ // Keep browser open briefly for inspection
62
+ log.info("\nā³ Keeping browser open for 5 seconds for inspection...");
63
+ await new Promise((resolve) => setTimeout(resolve, 5000));
64
+ await contextManager.closeContext();
65
+ log.info("šŸ‘‹ Done!");
66
+ }
67
+ }
68
+ /**
69
+ * Generate selectors.ts code from discovery results
70
+ */
71
+ function generateSelectorsCode(result) {
72
+ const lines = [
73
+ '/**',
74
+ ' * NotebookLM UI Selectors',
75
+ ' *',
76
+ ` * Auto-generated on ${result.discoveredAt}`,
77
+ ' * by selector-discovery.ts',
78
+ ' *',
79
+ ' * These selectors are used for notebook creation automation.',
80
+ ' */',
81
+ '',
82
+ 'export const NOTEBOOKLM_SELECTORS = {',
83
+ ];
84
+ for (const [key, info] of Object.entries(result.selectors)) {
85
+ if (info && typeof info === 'object' && 'primary' in info) {
86
+ const selectorInfo = info;
87
+ lines.push(` /** ${selectorInfo.description} */`);
88
+ lines.push(` ${key}: {`);
89
+ lines.push(` primary: ${JSON.stringify(selectorInfo.primary)},`);
90
+ lines.push(` fallbacks: ${JSON.stringify(selectorInfo.fallbacks)},`);
91
+ lines.push(` confirmed: ${selectorInfo.confirmed},`);
92
+ lines.push(` },`);
93
+ lines.push('');
94
+ }
95
+ }
96
+ lines.push('} as const;');
97
+ lines.push('');
98
+ lines.push('export type SelectorKey = keyof typeof NOTEBOOKLM_SELECTORS;');
99
+ lines.push('');
100
+ lines.push('/**');
101
+ lines.push(' * Get selector with fallbacks');
102
+ lines.push(' */');
103
+ lines.push('export function getSelector(key: SelectorKey): string[] {');
104
+ lines.push(' const info = NOTEBOOKLM_SELECTORS[key];');
105
+ lines.push(' return [info.primary, ...info.fallbacks].filter(Boolean);');
106
+ lines.push('}');
107
+ lines.push('');
108
+ return lines.join('\n');
109
+ }
110
+ /**
111
+ * Print discovery summary
112
+ */
113
+ function printSummary(result) {
114
+ log.info("\n" + "=".repeat(60));
115
+ log.info("šŸ“Š DISCOVERY SUMMARY");
116
+ log.info("=".repeat(60));
117
+ const found = [];
118
+ const notFound = [];
119
+ for (const [key, info] of Object.entries(result.selectors)) {
120
+ if (info && typeof info === 'object' && 'primary' in info) {
121
+ const selectorInfo = info;
122
+ if (selectorInfo.primary) {
123
+ found.push(key);
124
+ }
125
+ else {
126
+ notFound.push(key);
127
+ }
128
+ }
129
+ }
130
+ log.info(`\nāœ… Found (${found.length}):`);
131
+ for (const key of found) {
132
+ log.info(` - ${key}`);
133
+ }
134
+ if (notFound.length > 0) {
135
+ log.warning(`\nāš ļø Not Found (${notFound.length}):`);
136
+ for (const key of notFound) {
137
+ log.warning(` - ${key}`);
138
+ }
139
+ }
140
+ log.info("\n" + "=".repeat(60));
141
+ if (notFound.length > 0) {
142
+ log.info("\nšŸ’” Manual inspection may be needed for missing selectors.");
143
+ log.info(" The browser was kept open briefly for visual inspection.");
144
+ }
145
+ }
146
+ // Run main
147
+ main().catch((error) => {
148
+ log.error(`Fatal error: ${error}`);
149
+ process.exit(1);
150
+ });
151
+ //# sourceMappingURL=run-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-discovery.js","sourceRoot":"","sources":["../../src/notebook-creation/run-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Automated UI selector discovery for NotebookLM
3
+ *
4
+ * This tool navigates NotebookLM's interface and discovers CSS selectors
5
+ * for elements needed to create notebooks programmatically.
6
+ */
7
+ import type { BrowserContext } from "patchright";
8
+ import type { ElementInfo, DiscoveryResult } from "./types.js";
9
+ /**
10
+ * Discovers UI selectors for NotebookLM's notebook creation workflow
11
+ */
12
+ export declare class SelectorDiscovery {
13
+ private context;
14
+ private page;
15
+ constructor(context: BrowserContext);
16
+ /**
17
+ * Run the full discovery process
18
+ */
19
+ discover(): Promise<DiscoveryResult>;
20
+ /**
21
+ * Navigate to NotebookLM homepage
22
+ */
23
+ private navigateToHome;
24
+ /**
25
+ * Dump all interactive elements on the current page
26
+ */
27
+ dumpInteractiveElements(): Promise<ElementInfo[]>;
28
+ /**
29
+ * Find a selector matching keywords
30
+ */
31
+ private findSelector;
32
+ /**
33
+ * Find input element selector
34
+ */
35
+ private findInputSelector;
36
+ /**
37
+ * Find textarea selector
38
+ */
39
+ private findTextareaSelector;
40
+ /**
41
+ * Find file input element
42
+ */
43
+ private findFileInput;
44
+ /**
45
+ * Build a CSS selector for an element
46
+ */
47
+ private buildSelector;
48
+ /**
49
+ * Try to click the new notebook button
50
+ */
51
+ private tryClickNewNotebook;
52
+ /**
53
+ * Try to click an element by selector
54
+ */
55
+ private tryClick;
56
+ /**
57
+ * Log discovery results summary
58
+ */
59
+ private logDiscoveryResults;
60
+ }
61
+ /**
62
+ * Run selector discovery and return results
63
+ */
64
+ export declare function discoverSelectors(context: BrowserContext): Promise<DiscoveryResult>;
65
+ //# sourceMappingURL=selector-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-discovery.d.ts","sourceRoot":"","sources":["../../src/notebook-creation/selector-discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAQ,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAgB,MAAM,YAAY,CAAC;AAoB7E;;GAEG;AACH,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,OAAO;IAF3B,OAAO,CAAC,IAAI,CAAqB;gBAEb,OAAO,EAAE,cAAc;IAE3C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;IAiJ1C;;OAEG;YACW,cAAc;IAc5B;;OAEG;IACG,uBAAuB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAsDvD;;OAEG;IACH,OAAO,CAAC,YAAY;IAyDpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;OAEG;YACW,aAAa;IAuC3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoCrB;;OAEG;YACW,mBAAmB;IA2CjC;;OAEG;YACW,QAAQ;IAgBtB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAyB5B;AAWD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAGzF"}
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Automated UI selector discovery for NotebookLM
3
+ *
4
+ * This tool navigates NotebookLM's interface and discovers CSS selectors
5
+ * for elements needed to create notebooks programmatically.
6
+ */
7
+ import { log } from "../utils/logger.js";
8
+ import { randomDelay, realisticClick } from "../utils/stealth-utils.js";
9
+ import { CONFIG } from "../config.js";
10
+ const NOTEBOOKLM_URL = "https://notebooklm.google.com/";
11
+ /**
12
+ * Keywords to identify UI elements by their text/aria-label
13
+ */
14
+ const ELEMENT_KEYWORDS = {
15
+ newNotebook: ["new", "create", "notebook", "+", "add notebook"],
16
+ addSource: ["add source", "add", "source", "+", "upload"],
17
+ urlSource: ["url", "website", "web", "link", "webpage"],
18
+ textSource: ["text", "paste", "copy", "clipboard"],
19
+ fileSource: ["file", "upload", "pdf", "document", "drive"],
20
+ submit: ["add", "submit", "done", "create", "save", "ok", "confirm"],
21
+ cancel: ["cancel", "close", "back", "x"],
22
+ };
23
+ /**
24
+ * Discovers UI selectors for NotebookLM's notebook creation workflow
25
+ */
26
+ export class SelectorDiscovery {
27
+ context;
28
+ page = null;
29
+ constructor(context) {
30
+ this.context = context;
31
+ }
32
+ /**
33
+ * Run the full discovery process
34
+ */
35
+ async discover() {
36
+ const result = {
37
+ selectors: {},
38
+ homepageElements: [],
39
+ creationElements: [],
40
+ sourceElements: [],
41
+ discoveredAt: new Date().toISOString(),
42
+ errors: [],
43
+ };
44
+ try {
45
+ // Create a new page for discovery
46
+ this.page = await this.context.newPage();
47
+ log.info("šŸ” Starting NotebookLM UI selector discovery...");
48
+ // Phase 1: Discover homepage elements
49
+ log.info("šŸ“„ Phase 1: Analyzing homepage...");
50
+ await this.navigateToHome();
51
+ result.homepageElements = await this.dumpInteractiveElements();
52
+ result.selectors.newNotebookButton = this.findSelector(result.homepageElements, ELEMENT_KEYWORDS.newNotebook, "New notebook button");
53
+ // Phase 2: Navigate to notebook creation and discover elements
54
+ log.info("šŸ“„ Phase 2: Analyzing notebook creation UI...");
55
+ const clickedNew = await this.tryClickNewNotebook(result.selectors.newNotebookButton);
56
+ if (clickedNew) {
57
+ await randomDelay(1500, 2500);
58
+ result.creationElements = await this.dumpInteractiveElements();
59
+ // Find notebook name input
60
+ result.selectors.notebookNameInput = this.findInputSelector(result.creationElements, ["name", "title", "notebook"], "Notebook name input");
61
+ // Find add source button
62
+ result.selectors.addSourceButton = this.findSelector(result.creationElements, ELEMENT_KEYWORDS.addSource, "Add source button");
63
+ }
64
+ // Phase 3: Try to access source addition UI
65
+ log.info("šŸ“„ Phase 3: Analyzing source addition UI...");
66
+ if (result.selectors.addSourceButton?.primary) {
67
+ const clickedAddSource = await this.tryClick(result.selectors.addSourceButton.primary);
68
+ if (clickedAddSource) {
69
+ await randomDelay(1000, 2000);
70
+ result.sourceElements = await this.dumpInteractiveElements();
71
+ // Find source type options
72
+ result.selectors.urlSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.urlSource, "URL source option");
73
+ result.selectors.textSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.textSource, "Text source option");
74
+ result.selectors.fileSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.fileSource, "File source option");
75
+ // Find file input element
76
+ result.selectors.fileInput = await this.findFileInput();
77
+ // Find URL input
78
+ result.selectors.urlInput = this.findInputSelector(result.sourceElements, ["url", "link", "website", "http"], "URL input field");
79
+ // Find text input
80
+ result.selectors.textInput = this.findTextareaSelector(result.sourceElements, ["text", "paste", "content"], "Text input area");
81
+ // Find submit button
82
+ result.selectors.submitButton = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.submit, "Submit/Add button");
83
+ }
84
+ }
85
+ // Phase 4: Look for status indicators
86
+ log.info("šŸ“„ Phase 4: Identifying status indicators...");
87
+ const allElements = [
88
+ ...result.homepageElements,
89
+ ...result.creationElements,
90
+ ...result.sourceElements,
91
+ ];
92
+ result.selectors.processingIndicator = this.findSelector(allElements, ["loading", "processing", "uploading", "progress", "spinner"], "Processing indicator");
93
+ result.selectors.successIndicator = this.findSelector(allElements, ["success", "done", "complete", "added", "āœ“", "check"], "Success indicator");
94
+ result.selectors.errorMessage = this.findSelector(allElements, ["error", "failed", "invalid", "warning"], "Error message");
95
+ log.success("āœ… Discovery complete!");
96
+ this.logDiscoveryResults(result);
97
+ }
98
+ catch (error) {
99
+ const errorMsg = error instanceof Error ? error.message : String(error);
100
+ log.error(`āŒ Discovery error: ${errorMsg}`);
101
+ result.errors.push(errorMsg);
102
+ }
103
+ finally {
104
+ if (this.page) {
105
+ await this.page.close().catch(() => { });
106
+ }
107
+ }
108
+ return result;
109
+ }
110
+ /**
111
+ * Navigate to NotebookLM homepage
112
+ */
113
+ async navigateToHome() {
114
+ if (!this.page)
115
+ throw new Error("Page not initialized");
116
+ await this.page.goto(NOTEBOOKLM_URL, {
117
+ waitUntil: "domcontentloaded",
118
+ timeout: CONFIG.browserTimeout,
119
+ });
120
+ await randomDelay(2000, 3000);
121
+ // Wait for page to be interactive
122
+ await this.page.waitForLoadState("networkidle").catch(() => { });
123
+ }
124
+ /**
125
+ * Dump all interactive elements on the current page
126
+ */
127
+ async dumpInteractiveElements() {
128
+ if (!this.page)
129
+ return [];
130
+ // Note: page.evaluate runs in browser context - uses any types
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ const results = await this.page.evaluate(() => {
133
+ // @ts-expect-error - DOM types available in browser context
134
+ const elements = document.querySelectorAll('button, input, textarea, [role="button"], [role="menuitem"], ' +
135
+ '[role="option"], a[href], [tabindex="0"], [onclick], ' +
136
+ '[data-action], .clickable, [aria-haspopup]');
137
+ return Array.from(elements).map((el) => {
138
+ const rect = el.getBoundingClientRect();
139
+ // @ts-expect-error - DOM types available in browser context
140
+ const computedStyle = window.getComputedStyle(el);
141
+ const isVisible = rect.width > 0 &&
142
+ rect.height > 0 &&
143
+ computedStyle.visibility !== "hidden" &&
144
+ computedStyle.display !== "none" &&
145
+ computedStyle.opacity !== "0";
146
+ const attrs = Array.from(el.attributes || []);
147
+ const dataAttrs = {};
148
+ for (const attr of attrs) {
149
+ if (attr.name && attr.name.startsWith("data-")) {
150
+ dataAttrs[attr.name] = attr.value;
151
+ }
152
+ }
153
+ return {
154
+ tag: el.tagName?.toLowerCase() || "",
155
+ id: el.id || "",
156
+ classes: typeof el.className === 'string' ? el.className : "",
157
+ ariaLabel: el.getAttribute?.("aria-label") || null,
158
+ text: el.textContent?.trim().slice(0, 100) || null,
159
+ dataAttrs,
160
+ role: el.getAttribute?.("role") || null,
161
+ isVisible,
162
+ boundingBox: isVisible ? {
163
+ x: rect.x,
164
+ y: rect.y,
165
+ width: rect.width,
166
+ height: rect.height,
167
+ } : undefined,
168
+ };
169
+ });
170
+ });
171
+ return results;
172
+ }
173
+ /**
174
+ * Find a selector matching keywords
175
+ */
176
+ findSelector(elements, keywords, description) {
177
+ const matches = [];
178
+ for (const el of elements) {
179
+ if (!el.isVisible)
180
+ continue;
181
+ let score = 0;
182
+ const textLower = (el.text || "").toLowerCase();
183
+ const ariaLower = (el.ariaLabel || "").toLowerCase();
184
+ const classLower = (el.classes || "").toLowerCase();
185
+ for (const keyword of keywords) {
186
+ const kw = keyword.toLowerCase();
187
+ if (textLower.includes(kw))
188
+ score += 3;
189
+ if (ariaLower.includes(kw))
190
+ score += 4;
191
+ if (classLower.includes(kw))
192
+ score += 2;
193
+ if (el.id.toLowerCase().includes(kw))
194
+ score += 3;
195
+ // Check data attributes
196
+ for (const [, value] of Object.entries(el.dataAttrs)) {
197
+ if (value.toLowerCase().includes(kw))
198
+ score += 2;
199
+ }
200
+ }
201
+ if (score > 0) {
202
+ matches.push({
203
+ element: el,
204
+ score,
205
+ selector: this.buildSelector(el),
206
+ });
207
+ }
208
+ }
209
+ // Sort by score descending
210
+ matches.sort((a, b) => b.score - a.score);
211
+ if (matches.length === 0) {
212
+ return {
213
+ primary: "",
214
+ fallbacks: [],
215
+ description,
216
+ confirmed: false,
217
+ };
218
+ }
219
+ return {
220
+ primary: matches[0].selector,
221
+ fallbacks: matches.slice(1, 4).map((m) => m.selector),
222
+ description,
223
+ confirmed: false,
224
+ };
225
+ }
226
+ /**
227
+ * Find input element selector
228
+ */
229
+ findInputSelector(elements, keywords, description) {
230
+ const inputs = elements.filter((el) => el.tag === "input" || el.tag === "textarea");
231
+ if (inputs.length === 0) {
232
+ // Fall back to general search
233
+ return this.findSelector(elements, keywords, description);
234
+ }
235
+ return this.findSelector(inputs, keywords, description);
236
+ }
237
+ /**
238
+ * Find textarea selector
239
+ */
240
+ findTextareaSelector(elements, keywords, description) {
241
+ const textareas = elements.filter((el) => el.tag === "textarea");
242
+ if (textareas.length === 0) {
243
+ return this.findSelector(elements, keywords, description);
244
+ }
245
+ return this.findSelector(textareas, keywords, description);
246
+ }
247
+ /**
248
+ * Find file input element
249
+ */
250
+ async findFileInput() {
251
+ if (!this.page) {
252
+ return {
253
+ primary: "",
254
+ fallbacks: [],
255
+ description: "File input element",
256
+ confirmed: false,
257
+ };
258
+ }
259
+ // Look for file input directly
260
+ const fileInputs = await this.page.$$('input[type="file"]');
261
+ if (fileInputs.length > 0) {
262
+ // Try to get a more specific selector
263
+ const selector = await this.page.evaluate((el) => {
264
+ // @ts-expect-error - DOM types available in browser context
265
+ const input = el;
266
+ if (input.id)
267
+ return `#${input.id}`;
268
+ if (input.name)
269
+ return `input[type="file"][name="${input.name}"]`;
270
+ return 'input[type="file"]';
271
+ }, fileInputs[0]);
272
+ return {
273
+ primary: selector,
274
+ fallbacks: ['input[type="file"]'],
275
+ description: "File input element",
276
+ confirmed: true,
277
+ };
278
+ }
279
+ return {
280
+ primary: 'input[type="file"]',
281
+ fallbacks: [],
282
+ description: "File input element",
283
+ confirmed: false,
284
+ };
285
+ }
286
+ /**
287
+ * Build a CSS selector for an element
288
+ */
289
+ buildSelector(el) {
290
+ // Priority: ID > unique class > aria-label > data attribute > tag + position
291
+ if (el.id) {
292
+ return `#${CSS.escape(el.id)}`;
293
+ }
294
+ if (el.ariaLabel) {
295
+ return `[aria-label="${CSS.escape(el.ariaLabel)}"]`;
296
+ }
297
+ // Try to use a unique class
298
+ const classes = (el.classes || "").split(/\s+/).filter(Boolean);
299
+ for (const cls of classes) {
300
+ // Skip generic classes
301
+ if (cls.length > 3 && !["button", "btn", "input", "text"].includes(cls.toLowerCase())) {
302
+ return `.${CSS.escape(cls)}`;
303
+ }
304
+ }
305
+ // Use data attribute if available
306
+ for (const [attr, value] of Object.entries(el.dataAttrs)) {
307
+ if (value) {
308
+ return `[${attr}="${CSS.escape(value)}"]`;
309
+ }
310
+ }
311
+ // Fall back to role
312
+ if (el.role) {
313
+ return `[role="${el.role}"]`;
314
+ }
315
+ // Last resort: tag
316
+ return el.tag;
317
+ }
318
+ /**
319
+ * Try to click the new notebook button
320
+ */
321
+ async tryClickNewNotebook(selectorInfo) {
322
+ if (!this.page || !selectorInfo?.primary)
323
+ return false;
324
+ const selectors = [selectorInfo.primary, ...selectorInfo.fallbacks];
325
+ for (const selector of selectors) {
326
+ try {
327
+ const element = await this.page.$(selector);
328
+ if (element && await element.isVisible()) {
329
+ await realisticClick(this.page, selector, true);
330
+ return true;
331
+ }
332
+ }
333
+ catch {
334
+ continue;
335
+ }
336
+ }
337
+ // Try common patterns for "new" buttons
338
+ const commonPatterns = [
339
+ 'button:has-text("New")',
340
+ 'button:has-text("Create")',
341
+ '[aria-label*="new" i]',
342
+ '[aria-label*="create" i]',
343
+ 'button[data-action="create"]',
344
+ '.create-button',
345
+ '.new-button',
346
+ ];
347
+ for (const pattern of commonPatterns) {
348
+ try {
349
+ const element = await this.page.$(pattern);
350
+ if (element && await element.isVisible()) {
351
+ await element.click();
352
+ return true;
353
+ }
354
+ }
355
+ catch {
356
+ continue;
357
+ }
358
+ }
359
+ return false;
360
+ }
361
+ /**
362
+ * Try to click an element by selector
363
+ */
364
+ async tryClick(selector) {
365
+ if (!this.page)
366
+ return false;
367
+ try {
368
+ const element = await this.page.$(selector);
369
+ if (element && await element.isVisible()) {
370
+ await element.click();
371
+ return true;
372
+ }
373
+ }
374
+ catch {
375
+ return false;
376
+ }
377
+ return false;
378
+ }
379
+ /**
380
+ * Log discovery results summary
381
+ */
382
+ logDiscoveryResults(result) {
383
+ log.info("\nšŸ“Š Discovery Results Summary:");
384
+ log.info(` Homepage elements: ${result.homepageElements.length}`);
385
+ log.info(` Creation UI elements: ${result.creationElements.length}`);
386
+ log.info(` Source UI elements: ${result.sourceElements.length}`);
387
+ log.info("\nšŸŽÆ Discovered Selectors:");
388
+ for (const [key, info] of Object.entries(result.selectors)) {
389
+ if (info && typeof info === 'object' && 'primary' in info) {
390
+ const selectorInfo = info;
391
+ const status = selectorInfo.primary ? "āœ“" : "āœ—";
392
+ log.info(` ${status} ${key}: ${selectorInfo.primary || "(not found)"}`);
393
+ if (selectorInfo.fallbacks.length > 0) {
394
+ log.info(` Fallbacks: ${selectorInfo.fallbacks.join(", ")}`);
395
+ }
396
+ }
397
+ }
398
+ if (result.errors.length > 0) {
399
+ log.warning("\nāš ļø Errors encountered:");
400
+ for (const err of result.errors) {
401
+ log.warning(` - ${err}`);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ /**
407
+ * CSS.escape polyfill for Node.js
408
+ */
409
+ const CSS = {
410
+ escape: (value) => {
411
+ return value.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, "\\$1");
412
+ },
413
+ };
414
+ /**
415
+ * Run selector discovery and return results
416
+ */
417
+ export async function discoverSelectors(context) {
418
+ const discovery = new SelectorDiscovery(context);
419
+ return await discovery.discover();
420
+ }
421
+ //# sourceMappingURL=selector-discovery.js.map