@techwavedev/agi-agent-kit 1.1.7 β 1.2.7
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/CHANGELOG.md +142 -1
- package/README.md +195 -15
- package/bin/init.js +154 -5
- package/package.json +6 -3
- package/templates/base/AGENTS.md +54 -23
- package/templates/base/README.md +327 -0
- package/templates/base/directives/memory_integration.md +95 -0
- package/templates/base/execution/memory_manager.py +309 -0
- package/templates/base/execution/session_boot.py +218 -0
- package/templates/base/execution/session_init.py +320 -0
- package/templates/base/requirements.txt +45 -6
- package/templates/base/skill-creator/SKILL_skillcreator.md +3 -3
- package/templates/skills/knowledge/design-md/README.md +0 -0
- package/templates/skills/knowledge/design-md/SKILL.md +0 -0
- package/templates/skills/knowledge/design-md/examples/DESIGN.md +0 -0
- package/templates/skills/knowledge/intelligent-routing/SKILL.md +237 -164
- package/templates/skills/knowledge/notebooklm-rag/SKILL.md +216 -0
- package/templates/skills/knowledge/notebooklm-rag/requirements.txt +9 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/ask_question.py +237 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/auth_manager.py +307 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/browser_utils.py +101 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/cleanup_manager.py +87 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/config.py +45 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/notebook_manager.py +334 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/run.py +92 -0
- package/templates/skills/knowledge/notebooklm-rag/scripts/setup_environment.py +68 -0
- package/templates/skills/knowledge/parallel-agents/SKILL.md +345 -73
- package/templates/skills/knowledge/plugin-discovery/SKILL.md +581 -0
- package/templates/skills/knowledge/plugin-discovery/scripts/platform_setup.py +1083 -0
- package/templates/skills/knowledge/react-components/README.md +0 -0
- package/templates/skills/knowledge/react-components/SKILL.md +0 -0
- package/templates/skills/knowledge/react-components/examples/gold-standard-card.tsx +0 -0
- package/templates/skills/knowledge/react-components/package-lock.json +0 -0
- package/templates/skills/knowledge/react-components/package.json +0 -0
- package/templates/skills/knowledge/react-components/resources/architecture-checklist.md +0 -0
- package/templates/skills/knowledge/react-components/resources/component-template.tsx +0 -0
- package/templates/skills/knowledge/react-components/resources/stitch-api-reference.md +0 -0
- package/templates/skills/knowledge/react-components/resources/style-guide.json +0 -0
- package/templates/skills/knowledge/react-components/scripts/validate.js +0 -0
- package/templates/skills/knowledge/self-update/SKILL.md +0 -0
- package/templates/skills/knowledge/self-update/scripts/update_kit.py +0 -0
- package/templates/skills/knowledge/stitch-loop/README.md +0 -0
- package/templates/skills/knowledge/stitch-loop/SKILL.md +3 -3
- package/templates/skills/knowledge/stitch-loop/examples/SITE.md +0 -0
- package/templates/skills/knowledge/stitch-loop/examples/next-prompt.md +0 -0
- package/templates/skills/knowledge/stitch-loop/resources/baton-schema.md +0 -0
- package/templates/skills/knowledge/stitch-loop/resources/site-template.md +0 -0
- package/templates/skills/stitch-loop/SKILL.md +3 -3
- package/templates/skills/core/qdrant-memory/scripts/__pycache__/embedding_utils.cpython-314.pyc +0 -0
- package/templates/skills/core/qdrant-memory/scripts/__pycache__/init_collection.cpython-314.pyc +0 -0
- package/templates/skills/knowledge/SKILLS_CATALOG.md +0 -796
- package/templates/skills/knowledge/jira/scripts/__pycache__/jira_client.cpython-314.pyc +0 -0
- package/templates/skills/knowledge/notebooklm-mcp/SKILL.md +0 -71
- package/templates/skills/knowledge/notebooklm-mcp/assets/example_asset.txt +0 -24
- package/templates/skills/knowledge/notebooklm-mcp/references/api_reference.md +0 -34
- package/templates/skills/knowledge/notebooklm-mcp/scripts/example.py +0 -19
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Authentication Manager for NotebookLM RAG Skill
|
|
4
|
+
Handles Google login and browser state persistence
|
|
5
|
+
Adapted from PleasePrompto/notebooklm-skill (MIT License)
|
|
6
|
+
|
|
7
|
+
Implements hybrid auth approach:
|
|
8
|
+
- Persistent browser profile (user_data_dir) for fingerprint consistency
|
|
9
|
+
- Manual cookie injection from state.json for session cookies (Playwright bug workaround)
|
|
10
|
+
See: https://github.com/microsoft/playwright/issues/36139
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import time
|
|
15
|
+
import argparse
|
|
16
|
+
import shutil
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional, Dict, Any
|
|
21
|
+
|
|
22
|
+
from patchright.sync_api import sync_playwright, BrowserContext
|
|
23
|
+
|
|
24
|
+
# Add parent directory to path
|
|
25
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
26
|
+
|
|
27
|
+
from config import BROWSER_STATE_DIR, STATE_FILE, AUTH_INFO_FILE, DATA_DIR
|
|
28
|
+
from browser_utils import BrowserFactory
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AuthManager:
|
|
32
|
+
"""
|
|
33
|
+
Manages authentication and browser state for NotebookLM
|
|
34
|
+
|
|
35
|
+
Features:
|
|
36
|
+
- Interactive Google login
|
|
37
|
+
- Browser state persistence
|
|
38
|
+
- Session restoration
|
|
39
|
+
- Account switching
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
"""Initialize the authentication manager"""
|
|
44
|
+
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
BROWSER_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
self.state_file = STATE_FILE
|
|
48
|
+
self.auth_info_file = AUTH_INFO_FILE
|
|
49
|
+
self.browser_state_dir = BROWSER_STATE_DIR
|
|
50
|
+
|
|
51
|
+
def is_authenticated(self) -> bool:
|
|
52
|
+
"""Check if valid authentication exists"""
|
|
53
|
+
if not self.state_file.exists():
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
age_days = (time.time() - self.state_file.stat().st_mtime) / 86400
|
|
57
|
+
if age_days > 7:
|
|
58
|
+
print(f"β οΈ Browser state is {age_days:.1f} days old, may need re-authentication")
|
|
59
|
+
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def get_auth_info(self) -> Dict[str, Any]:
|
|
63
|
+
"""Get authentication information"""
|
|
64
|
+
info = {
|
|
65
|
+
'authenticated': self.is_authenticated(),
|
|
66
|
+
'state_file': str(self.state_file),
|
|
67
|
+
'state_exists': self.state_file.exists()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if self.auth_info_file.exists():
|
|
71
|
+
try:
|
|
72
|
+
with open(self.auth_info_file, 'r') as f:
|
|
73
|
+
saved_info = json.load(f)
|
|
74
|
+
info.update(saved_info)
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
if info['state_exists']:
|
|
79
|
+
age_hours = (time.time() - self.state_file.stat().st_mtime) / 3600
|
|
80
|
+
info['state_age_hours'] = age_hours
|
|
81
|
+
|
|
82
|
+
return info
|
|
83
|
+
|
|
84
|
+
def setup_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Perform interactive authentication setup
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
headless: Run browser in headless mode (False for login)
|
|
90
|
+
timeout_minutes: Maximum time to wait for login
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
True if authentication successful
|
|
94
|
+
"""
|
|
95
|
+
print("π Starting authentication setup...")
|
|
96
|
+
print(f" Timeout: {timeout_minutes} minutes")
|
|
97
|
+
|
|
98
|
+
playwright = None
|
|
99
|
+
context = None
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
playwright = sync_playwright().start()
|
|
103
|
+
|
|
104
|
+
context = BrowserFactory.launch_persistent_context(
|
|
105
|
+
playwright,
|
|
106
|
+
headless=headless
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
page = context.new_page()
|
|
110
|
+
page.goto("https://notebooklm.google.com", wait_until="domcontentloaded")
|
|
111
|
+
|
|
112
|
+
if "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url:
|
|
113
|
+
print(" β
Already authenticated!")
|
|
114
|
+
self._save_browser_state(context)
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
print("\n β³ Please log in to your Google account...")
|
|
118
|
+
print(f" β±οΈ Waiting up to {timeout_minutes} minutes for login...")
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
timeout_ms = int(timeout_minutes * 60 * 1000)
|
|
122
|
+
page.wait_for_url(re.compile(r"^https://notebooklm\.google\.com/"), timeout=timeout_ms)
|
|
123
|
+
|
|
124
|
+
print(f" β
Login successful!")
|
|
125
|
+
|
|
126
|
+
self._save_browser_state(context)
|
|
127
|
+
self._save_auth_info()
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f" β Authentication timeout: {e}")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
except Exception as e:
|
|
135
|
+
print(f" β Error: {e}")
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
finally:
|
|
139
|
+
if context:
|
|
140
|
+
try:
|
|
141
|
+
context.close()
|
|
142
|
+
except Exception:
|
|
143
|
+
pass
|
|
144
|
+
if playwright:
|
|
145
|
+
try:
|
|
146
|
+
playwright.stop()
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
def _save_browser_state(self, context: BrowserContext):
|
|
151
|
+
"""Save browser state to disk"""
|
|
152
|
+
try:
|
|
153
|
+
context.storage_state(path=str(self.state_file))
|
|
154
|
+
print(f" πΎ Saved browser state to: {self.state_file}")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
print(f" β Failed to save browser state: {e}")
|
|
157
|
+
raise
|
|
158
|
+
|
|
159
|
+
def _save_auth_info(self):
|
|
160
|
+
"""Save authentication metadata"""
|
|
161
|
+
try:
|
|
162
|
+
info = {
|
|
163
|
+
'authenticated_at': time.time(),
|
|
164
|
+
'authenticated_at_iso': time.strftime('%Y-%m-%d %H:%M:%S')
|
|
165
|
+
}
|
|
166
|
+
with open(self.auth_info_file, 'w') as f:
|
|
167
|
+
json.dump(info, f, indent=2)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
def clear_auth(self) -> bool:
|
|
172
|
+
"""Clear all authentication data"""
|
|
173
|
+
print("ποΈ Clearing authentication data...")
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
if self.state_file.exists():
|
|
177
|
+
self.state_file.unlink()
|
|
178
|
+
print(" β
Removed browser state")
|
|
179
|
+
|
|
180
|
+
if self.auth_info_file.exists():
|
|
181
|
+
self.auth_info_file.unlink()
|
|
182
|
+
print(" β
Removed auth info")
|
|
183
|
+
|
|
184
|
+
if self.browser_state_dir.exists():
|
|
185
|
+
shutil.rmtree(self.browser_state_dir)
|
|
186
|
+
self.browser_state_dir.mkdir(parents=True, exist_ok=True)
|
|
187
|
+
print(" β
Cleared browser data")
|
|
188
|
+
|
|
189
|
+
return True
|
|
190
|
+
except Exception as e:
|
|
191
|
+
print(f" β Error clearing auth: {e}")
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
def re_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool:
|
|
195
|
+
"""Perform re-authentication (clear and setup)"""
|
|
196
|
+
print("π Starting re-authentication...")
|
|
197
|
+
self.clear_auth()
|
|
198
|
+
return self.setup_auth(headless, timeout_minutes)
|
|
199
|
+
|
|
200
|
+
def validate_auth(self) -> bool:
|
|
201
|
+
"""Validate that stored authentication works"""
|
|
202
|
+
if not self.is_authenticated():
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
print("π Validating authentication...")
|
|
206
|
+
|
|
207
|
+
playwright = None
|
|
208
|
+
context = None
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
playwright = sync_playwright().start()
|
|
212
|
+
|
|
213
|
+
context = BrowserFactory.launch_persistent_context(
|
|
214
|
+
playwright,
|
|
215
|
+
headless=True
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
page = context.new_page()
|
|
219
|
+
page.goto("https://notebooklm.google.com", wait_until="domcontentloaded", timeout=30000)
|
|
220
|
+
|
|
221
|
+
if "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url:
|
|
222
|
+
print(" β
Authentication is valid")
|
|
223
|
+
return True
|
|
224
|
+
else:
|
|
225
|
+
print(" β Authentication is invalid (redirected to login)")
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
print(f" β Validation failed: {e}")
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
finally:
|
|
233
|
+
if context:
|
|
234
|
+
try:
|
|
235
|
+
context.close()
|
|
236
|
+
except Exception:
|
|
237
|
+
pass
|
|
238
|
+
if playwright:
|
|
239
|
+
try:
|
|
240
|
+
playwright.stop()
|
|
241
|
+
except Exception:
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def main():
|
|
246
|
+
"""Command-line interface for authentication management"""
|
|
247
|
+
parser = argparse.ArgumentParser(description='Manage NotebookLM authentication')
|
|
248
|
+
|
|
249
|
+
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
|
250
|
+
|
|
251
|
+
setup_parser = subparsers.add_parser('setup', help='Setup authentication')
|
|
252
|
+
setup_parser.add_argument('--headless', action='store_true', help='Run in headless mode')
|
|
253
|
+
setup_parser.add_argument('--timeout', type=float, default=10, help='Login timeout in minutes (default: 10)')
|
|
254
|
+
|
|
255
|
+
subparsers.add_parser('status', help='Check authentication status')
|
|
256
|
+
subparsers.add_parser('validate', help='Validate authentication')
|
|
257
|
+
subparsers.add_parser('clear', help='Clear authentication')
|
|
258
|
+
|
|
259
|
+
reauth_parser = subparsers.add_parser('reauth', help='Re-authenticate (clear + setup)')
|
|
260
|
+
reauth_parser.add_argument('--timeout', type=float, default=10, help='Login timeout in minutes (default: 10)')
|
|
261
|
+
|
|
262
|
+
args = parser.parse_args()
|
|
263
|
+
|
|
264
|
+
auth = AuthManager()
|
|
265
|
+
|
|
266
|
+
if args.command == 'setup':
|
|
267
|
+
if auth.setup_auth(headless=args.headless, timeout_minutes=args.timeout):
|
|
268
|
+
print("\nβ
Authentication setup complete!")
|
|
269
|
+
print("You can now use ask_question.py to query NotebookLM")
|
|
270
|
+
else:
|
|
271
|
+
print("\nβ Authentication setup failed")
|
|
272
|
+
exit(1)
|
|
273
|
+
|
|
274
|
+
elif args.command == 'status':
|
|
275
|
+
info = auth.get_auth_info()
|
|
276
|
+
print("\nπ Authentication Status:")
|
|
277
|
+
print(f" Authenticated: {'Yes' if info['authenticated'] else 'No'}")
|
|
278
|
+
if info.get('state_age_hours'):
|
|
279
|
+
print(f" State age: {info['state_age_hours']:.1f} hours")
|
|
280
|
+
if info.get('authenticated_at_iso'):
|
|
281
|
+
print(f" Last auth: {info['authenticated_at_iso']}")
|
|
282
|
+
print(f" State file: {info['state_file']}")
|
|
283
|
+
|
|
284
|
+
elif args.command == 'validate':
|
|
285
|
+
if auth.validate_auth():
|
|
286
|
+
print("Authentication is valid and working")
|
|
287
|
+
else:
|
|
288
|
+
print("Authentication is invalid or expired")
|
|
289
|
+
print("Run: auth_manager.py setup")
|
|
290
|
+
|
|
291
|
+
elif args.command == 'clear':
|
|
292
|
+
if auth.clear_auth():
|
|
293
|
+
print("Authentication cleared")
|
|
294
|
+
|
|
295
|
+
elif args.command == 'reauth':
|
|
296
|
+
if auth.re_auth(timeout_minutes=args.timeout):
|
|
297
|
+
print("\nβ
Re-authentication complete!")
|
|
298
|
+
else:
|
|
299
|
+
print("\nβ Re-authentication failed")
|
|
300
|
+
exit(1)
|
|
301
|
+
|
|
302
|
+
else:
|
|
303
|
+
parser.print_help()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
if __name__ == "__main__":
|
|
307
|
+
main()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Browser Utilities for NotebookLM RAG Skill
|
|
3
|
+
Handles browser launching, stealth features, and common interactions
|
|
4
|
+
Adapted from PleasePrompto/notebooklm-skill (MIT License)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import time
|
|
9
|
+
import random
|
|
10
|
+
from typing import Optional, List
|
|
11
|
+
|
|
12
|
+
from patchright.sync_api import Playwright, BrowserContext, Page
|
|
13
|
+
from config import BROWSER_PROFILE_DIR, STATE_FILE, BROWSER_ARGS, USER_AGENT
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BrowserFactory:
|
|
17
|
+
"""Factory for creating configured browser contexts"""
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def launch_persistent_context(
|
|
21
|
+
playwright: Playwright,
|
|
22
|
+
headless: bool = True,
|
|
23
|
+
user_data_dir: str = str(BROWSER_PROFILE_DIR)
|
|
24
|
+
) -> BrowserContext:
|
|
25
|
+
"""
|
|
26
|
+
Launch a persistent browser context with anti-detection features
|
|
27
|
+
and cookie workaround.
|
|
28
|
+
"""
|
|
29
|
+
context = playwright.chromium.launch_persistent_context(
|
|
30
|
+
user_data_dir=user_data_dir,
|
|
31
|
+
channel="chrome",
|
|
32
|
+
headless=headless,
|
|
33
|
+
no_viewport=True,
|
|
34
|
+
ignore_default_args=["--enable-automation"],
|
|
35
|
+
user_agent=USER_AGENT,
|
|
36
|
+
args=BROWSER_ARGS
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Cookie Workaround for Playwright bug #36139
|
|
40
|
+
BrowserFactory._inject_cookies(context)
|
|
41
|
+
|
|
42
|
+
return context
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _inject_cookies(context: BrowserContext):
|
|
46
|
+
"""Inject cookies from state.json if available"""
|
|
47
|
+
if STATE_FILE.exists():
|
|
48
|
+
try:
|
|
49
|
+
with open(STATE_FILE, 'r') as f:
|
|
50
|
+
state = json.load(f)
|
|
51
|
+
if 'cookies' in state and len(state['cookies']) > 0:
|
|
52
|
+
context.add_cookies(state['cookies'])
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f" β οΈ Could not load state.json: {e}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class StealthUtils:
|
|
58
|
+
"""Human-like interaction utilities"""
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def random_delay(min_ms: int = 100, max_ms: int = 500):
|
|
62
|
+
"""Add random delay"""
|
|
63
|
+
time.sleep(random.uniform(min_ms / 1000, max_ms / 1000))
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def human_type(page: Page, selector: str, text: str, wpm_min: int = 320, wpm_max: int = 480):
|
|
67
|
+
"""Type with human-like speed"""
|
|
68
|
+
element = page.query_selector(selector)
|
|
69
|
+
if not element:
|
|
70
|
+
try:
|
|
71
|
+
element = page.wait_for_selector(selector, timeout=2000)
|
|
72
|
+
except:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
if not element:
|
|
76
|
+
print(f"β οΈ Element not found for typing: {selector}")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
element.click()
|
|
80
|
+
|
|
81
|
+
for char in text:
|
|
82
|
+
element.type(char, delay=random.uniform(25, 75))
|
|
83
|
+
if random.random() < 0.05:
|
|
84
|
+
time.sleep(random.uniform(0.15, 0.4))
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def realistic_click(page: Page, selector: str):
|
|
88
|
+
"""Click with realistic movement"""
|
|
89
|
+
element = page.query_selector(selector)
|
|
90
|
+
if not element:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
box = element.bounding_box()
|
|
94
|
+
if box:
|
|
95
|
+
x = box['x'] + box['width'] / 2
|
|
96
|
+
y = box['y'] + box['height'] / 2
|
|
97
|
+
page.mouse.move(x, y, steps=5)
|
|
98
|
+
|
|
99
|
+
StealthUtils.random_delay(100, 300)
|
|
100
|
+
element.click()
|
|
101
|
+
StealthUtils.random_delay(100, 300)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Data Cleanup Manager for NotebookLM RAG Skill
|
|
4
|
+
Handles cleaning browser data, auth state, and optionally library
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
13
|
+
from config import DATA_DIR, BROWSER_STATE_DIR, STATE_FILE, AUTH_INFO_FILE, LIBRARY_FILE
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def preview_cleanup(preserve_library: bool = False):
|
|
17
|
+
"""Show what would be cleaned up"""
|
|
18
|
+
print("π Cleanup Preview:")
|
|
19
|
+
items = []
|
|
20
|
+
|
|
21
|
+
if BROWSER_STATE_DIR.exists():
|
|
22
|
+
size = sum(f.stat().st_size for f in BROWSER_STATE_DIR.rglob('*') if f.is_file())
|
|
23
|
+
items.append(f" Browser state: {size / 1024:.1f} KB")
|
|
24
|
+
|
|
25
|
+
if STATE_FILE.exists():
|
|
26
|
+
items.append(f" State file: {STATE_FILE}")
|
|
27
|
+
|
|
28
|
+
if AUTH_INFO_FILE.exists():
|
|
29
|
+
items.append(f" Auth info: {AUTH_INFO_FILE}")
|
|
30
|
+
|
|
31
|
+
if not preserve_library and LIBRARY_FILE.exists():
|
|
32
|
+
items.append(f" Library: {LIBRARY_FILE}")
|
|
33
|
+
elif preserve_library and LIBRARY_FILE.exists():
|
|
34
|
+
items.append(f" Library: PRESERVED β")
|
|
35
|
+
|
|
36
|
+
if items:
|
|
37
|
+
for item in items:
|
|
38
|
+
print(item)
|
|
39
|
+
else:
|
|
40
|
+
print(" Nothing to clean up")
|
|
41
|
+
|
|
42
|
+
return len(items) > 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def execute_cleanup(preserve_library: bool = False):
|
|
46
|
+
"""Execute cleanup"""
|
|
47
|
+
print("ποΈ Cleaning up...")
|
|
48
|
+
|
|
49
|
+
if BROWSER_STATE_DIR.exists():
|
|
50
|
+
shutil.rmtree(BROWSER_STATE_DIR)
|
|
51
|
+
BROWSER_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
print(" β
Cleared browser state")
|
|
53
|
+
|
|
54
|
+
if STATE_FILE.exists():
|
|
55
|
+
STATE_FILE.unlink()
|
|
56
|
+
print(" β
Removed state file")
|
|
57
|
+
|
|
58
|
+
if AUTH_INFO_FILE.exists():
|
|
59
|
+
AUTH_INFO_FILE.unlink()
|
|
60
|
+
print(" β
Removed auth info")
|
|
61
|
+
|
|
62
|
+
if not preserve_library and LIBRARY_FILE.exists():
|
|
63
|
+
LIBRARY_FILE.unlink()
|
|
64
|
+
print(" β
Removed library")
|
|
65
|
+
elif preserve_library:
|
|
66
|
+
print(" π Library preserved")
|
|
67
|
+
|
|
68
|
+
print(" β
Cleanup complete!")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
parser = argparse.ArgumentParser(description='Clean up NotebookLM RAG skill data')
|
|
73
|
+
parser.add_argument('--confirm', action='store_true', help='Execute cleanup (default: preview only)')
|
|
74
|
+
parser.add_argument('--preserve-library', action='store_true', help='Keep notebook library')
|
|
75
|
+
|
|
76
|
+
args = parser.parse_args()
|
|
77
|
+
|
|
78
|
+
if args.confirm:
|
|
79
|
+
execute_cleanup(preserve_library=args.preserve_library)
|
|
80
|
+
else:
|
|
81
|
+
has_items = preview_cleanup(preserve_library=args.preserve_library)
|
|
82
|
+
if has_items:
|
|
83
|
+
print("\nRun with --confirm to execute cleanup")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration for NotebookLM RAG Skill
|
|
3
|
+
Centralizes constants, selectors, and paths
|
|
4
|
+
Adapted from PleasePrompto/notebooklm-skill (MIT License)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Paths
|
|
10
|
+
SKILL_DIR = Path(__file__).parent.parent
|
|
11
|
+
DATA_DIR = SKILL_DIR / "data"
|
|
12
|
+
BROWSER_STATE_DIR = DATA_DIR / "browser_state"
|
|
13
|
+
BROWSER_PROFILE_DIR = BROWSER_STATE_DIR / "browser_profile"
|
|
14
|
+
STATE_FILE = BROWSER_STATE_DIR / "state.json"
|
|
15
|
+
AUTH_INFO_FILE = DATA_DIR / "auth_info.json"
|
|
16
|
+
LIBRARY_FILE = DATA_DIR / "library.json"
|
|
17
|
+
|
|
18
|
+
# NotebookLM Selectors
|
|
19
|
+
QUERY_INPUT_SELECTORS = [
|
|
20
|
+
"textarea.query-box-input", # Primary
|
|
21
|
+
'textarea[aria-label="Feld fΓΌr Anfragen"]', # Fallback German
|
|
22
|
+
'textarea[aria-label="Input for queries"]', # Fallback English
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
RESPONSE_SELECTORS = [
|
|
26
|
+
".to-user-container .message-text-content", # Primary
|
|
27
|
+
"[data-message-author='bot']",
|
|
28
|
+
"[data-message-author='assistant']",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# Browser Configuration
|
|
32
|
+
BROWSER_ARGS = [
|
|
33
|
+
'--disable-blink-features=AutomationControlled',
|
|
34
|
+
'--disable-dev-shm-usage',
|
|
35
|
+
'--no-sandbox',
|
|
36
|
+
'--no-first-run',
|
|
37
|
+
'--no-default-browser-check'
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
41
|
+
|
|
42
|
+
# Timeouts
|
|
43
|
+
LOGIN_TIMEOUT_MINUTES = 10
|
|
44
|
+
QUERY_TIMEOUT_SECONDS = 120
|
|
45
|
+
PAGE_LOAD_TIMEOUT = 30000
|