AuthManager improvements for multi-account support:
1. Fresh profile support:
- Add use_fresh_profile parameter to setup() and _setup_google_with_patchright()
- Pass through to patchright for new account authentication
- _accounts_add() uses fresh profile to force account selection
2. Symlink management:
- Add quiet parameter to _ensure_storage_state_symlink()
- Call symlink update in __init__ after migration (silent)
- Update symlink on account switch for notebooklm-py compatibility
3. Better integration:
- Symlink now always points to active account's auth file
- Ensures notebooklm-py uses correct credentials after switch
Co-Authored-By: Claude <noreply@anthropic.com>
Major improvements for adding new Google accounts:
1. Temporary profile for new accounts:
- Create isolated temp directory with tempfile.mkdtemp()
- Avoids Chrome profile switching when signing into different account
- Auto-cleanup after authentication completes
2. CDP-based cross-window detection:
- Use Chrome DevTools Protocol to detect NotebookLM pages
- Works even when Chrome opens new windows during login
- Target.getTargets finds pages across all Chrome windows
3. Multi-tab monitoring:
- Check all pages in context, not just the first one
- Handle Google's multi-tab login flow correctly
4. Session capture from other windows:
- Navigate controlled page to NotebookLM after CDP detection
- Extract cookies (session shared across windows)
Co-Authored-By: Claude <noreply@anthropic.com>
Update ensure_google_auth() to check new multi-account structure:
- Check google/index.json first for multi-account mode
- Find active account's auth file from index
- Fall back to legacy google.json for backward compatibility
- Fix argument order in auth setup subprocess call
This prevents unnecessary auth prompts when multi-account is configured.
Co-Authored-By: Claude <noreply@anthropic.com>
When migrating from legacy google.json to multi-account structure,
abort migration if email cannot be extracted from credentials instead
of creating an "unknown@google.com" placeholder account.
- Keep legacy google.json active until proper re-authentication
- Guide user to run 'accounts add' to migrate with proper email
- Prevents orphan accounts with invalid email addresses
Co-Authored-By: Claude <noreply@anthropic.com>
Display active Google account email and index at the top
of the status output.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Detect notebook's associated account from library
- Use correct account credentials for cross-account queries
- Log when using a different account than active
Co-Authored-By: Claude <noreply@anthropic.com>
- Add account_index and account_email fields on add
- list_notebooks_for_account() filters by account
- list_all_notebooks_grouped() groups by account
- CLI: --all-accounts flag for list command
Co-Authored-By: Claude <noreply@anthropic.com>
NotebookLMWrapper can now be initialized with a specific
account_index to use that account's credentials instead
of the active account.
Co-Authored-By: Claude <noreply@anthropic.com>
During migration from single-account to multi-account structure,
existing notebooks in library.json are updated to include
account_index and account_email fields.
Co-Authored-By: Claude <noreply@anthropic.com>
- Delegate Google auth file resolution to AccountManager
- Handle add/update account flow in setup
- Maintain backward compatibility with existing API
Co-Authored-By: Claude <noreply@anthropic.com>
- add_account() with credential storage
- remove_account() with cleanup
- update_account_credentials() for reauth
- get_account_credentials() for loading
Co-Authored-By: Claude <noreply@anthropic.com>
- switch_account() by index or email
- get_account_by_index() and get_account_by_email()
- account_exists() for duplicate detection
Co-Authored-By: Claude <noreply@anthropic.com>
- AccountInfo dataclass for account metadata
- AccountManager class with index loading/saving
- Email sanitization for safe filenames
- Migration detection from legacy google.json
Co-Authored-By: Claude <noreply@anthropic.com>
Add GOOGLE_AUTH_DIR, GOOGLE_AUTH_INDEX for new multi-account structure.
Keep GOOGLE_AUTH_FILE_LEGACY for migration detection.
Co-Authored-By: Claude <noreply@anthropic.com>
When requirements.txt changes trigger pip install, automatically
install the Patchright Chromium browser if patchright package exists.
- Add _ensure_patchright_browser() function
- Use marker file to track installation status
- 5-minute timeout for browser download
- Graceful fallback on failure
Co-Authored-By: Claude <noreply@anthropic.com>
Google blocks personal Gmail sign-in with Playwright's bundled Chromium,
showing "This browser or app may not be secure" error. This commit:
- Add patchright_auth.py module using anti-detection Playwright fork
- Route Google auth through Patchright in auth_manager.py
- Add patchright>=1.50.0 to requirements.txt
- Fall back to agent-browser if Patchright unavailable
Key anti-detection techniques:
- ignore_default_args=["--enable-automation"]
- --disable-blink-features=AutomationControlled
- Uses real Chrome via executable_path
- Persistent context for session management
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: AI agents run scripts non-interactively, so input() gets EOF.
Solution: Add explicit CLI flags for notebook selection:
- --use-active: Upload to currently active notebook
- --create-new: Create new notebook named after the file
- --notebook-id: Upload to specific notebook (existing)
The AI agent should ask the user BEFORE running the script, then
pass the appropriate flag. Script now exits with clear error
message if no target is specified.
Updated SKILL.md to instruct agents to ask user first.
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed all prints from stderr to stdout for consistent buffering
- Added sys.stdout.flush() before input() to ensure proper ordering
- Fixes issue where input prompt appeared before options were displayed
Co-Authored-By: Claude <noreply@anthropic.com>
- Use npm.cmd instead of npm on Windows (os.name == 'nt')
- Add Windows shell names to IGNORED_PROCESS_NAMES (cmd, powershell, pwsh)
- Add Windows executable extensions (.exe, .cmd) to ignored list
- Apply fix to both run.py and setup_environment.py
Co-Authored-By: Claude <noreply@anthropic.com>
When no --notebook-id is specified, prompt user to choose:
- Upload to active notebook (if one exists)
- Create a new notebook
- Select from existing notebooks (if no active)
Adds interactive parameter to disable prompting for programmatic use.
Co-Authored-By: Claude <noreply@anthropic.com>
Compare SHA256 hash of requirements.txt against stored hash in
.venv/.requirements.hash. Automatically runs pip install when
requirements change or hash file is missing.
Co-Authored-By: Claude <noreply@anthropic.com>
Move pre-flight checks from SKILL.md instructions into run.py itself,
providing automatic dependency and authentication validation on every
command. Auth is checked for freshness (10-day TTL) and auto-prompts
browser login when missing or expired.
Co-Authored-By: Claude <noreply@anthropic.com>
- Add generate_slide_deck() and generate_infographic() to notebooklm_wrapper.py
- Add download_slide_deck() and download_infographic() methods
- Add generate-slides and generate-infographic CLI commands to artifact_manager.py
- Update SKILL.md with correct command routing and new options documentation
- Support slide formats: DETAILED_DECK, PRESENTER_SLIDES
- Support infographic orientations: LANDSCAPE, PORTRAIT, SQUARE
- Support detail levels: CONCISE, STANDARD, DETAILED
Co-Authored-By: Claude <noreply@anthropic.com>
The notebooklm-py library's download methods use Playwright internally and
look for storage at NOTEBOOKLM_HOME/storage_state.json. This fix:
- Adds _ensure_storage_state_symlink() to create symlink after Google auth
- Symlink points storage_state.json -> google.json in auth directory
- Falls back to file copy on Windows if symlink creation fails
- NOTEBOOKLM_HOME env var already set in config.py to data/auth
Co-Authored-By: Claude <noreply@anthropic.com>
- Complete README rewrite with new nblm identity
- Add npx add-skill installation via vercel-labs/add-skill
- Position as multi-agent skill (Claude Code, Cursor, OpenCode)
- Add tiered command docs (Quick Start + collapsible reference)
- Add acknowledgments for upstream projects
- Update package.json name to nblm
- Replace all "NotebookLM Skill" references across codebase
- Update .gitignore for .claude-plugin/ and docs/plans/
Co-Authored-By: Claude <noreply@anthropic.com>
The --url argument now auto-detects input type:
- Full URL (https://...): Used as-is
- Partial URL (notebooklm.google.com/...): Adds https://
- Notebook ID (abc123...): Converts to full URL
Users can now simply pass either format to --url without thinking.
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Manual AuthTokens creation doesn't work with notebooklm-py.
The library expects from_storage() which handles token extraction internally.
- Remove AuthTokens import and _create_auth_tokens method
- Use from_storage(auth_file) in __aenter__ and _refresh_tokens
- All API calls (list, create, upload) now work correctly
Co-Authored-By: Claude <noreply@anthropic.com>
- Add _fallback_create_notebook() method
- API failures now fall back to browser for both create_notebook and add_file
- Extract notebook ID from URL after browser creation
Co-Authored-By: Claude <noreply@anthropic.com>
- Add _extract_and_save_tokens() helper that navigates to NotebookLM before extraction
- Token extraction now happens reliably after successful login (no re-auth needed)
- Fix browser fallback upload to use refs instead of CSS selectors
- Add _find_file_input_ref() to locate upload input in snapshot
- Improve upload flow with better error messages and progress logging
Co-Authored-By: Claude <noreply@anthropic.com>